package org.reldb.rel.v0.generator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import org.reldb.rel.exceptions.*;
import org.reldb.rel.v0.debuginfo.DebugInfo;
import org.reldb.rel.v0.storage.*;
import org.reldb.rel.v0.storage.relvars.RelvarHeading;
import org.reldb.rel.v0.types.*;
import org.reldb.rel.v0.vm.Context;
import org.reldb.rel.v0.vm.Instruction;
import org.reldb.rel.v0.vm.Operator;
import org.reldb.rel.v0.vm.instructions.core.OpInvoke;
/** This class captures information about the Rel operator currently being defined,
* including its generated code.
*
* @author dave
*
*/
public class OperatorDefinitionRel extends OperatorDefinitionAbstract {
private OperatorDefinition parent;
private Operator operator;
private HashMap<OperatorSignature, OperatorDefinition> operators;
private HashMap<String, Slot> slots;
private int startLine;
/** Ctor for operator definition. */
public OperatorDefinitionRel(int startLine, String operatorName, OperatorDefinition parentDefinition) {
super(operatorName);
this.startLine = startLine;
parent = parentDefinition;
operator = new Operator((parent == null) ? 0 : parent.getDepth() + 1);
operators = new HashMap<OperatorSignature, OperatorDefinition>();
slots = new HashMap<String, Slot>();
}
/** Get primary language. */
public String getLanguage() {
return "Rel";
}
/** Get the line number this definition starts on. */
public int getStartLine() {
return startLine;
}
/** Get static depth. */
public int getDepth() {
return operator.getDepth();
}
/** Get parent operator definition. Null if this is the root operator. */
public OperatorDefinition getParentOperatorDefinition() {
return parent;
}
/** Compile an Instruction. */
public void compile(DebugInfo errorContext, Instruction o) {
o.setDebugInfo(errorContext);
operator.compile(o);
}
/** Compile an Instruction at a given address. */
public void compileAt(DebugInfo errorContext, Instruction o, int address) {
o.setDebugInfo(errorContext);
operator.compileAt(address, o);
}
/** Return current compilation address. */
public int getCP() {
return operator.size();
}
/** Get executable code. */
public Operator getOperator() {
return operator;
}
/** Get size of executable code. */
public int getExecutableSize() {
return operator.size();
}
/** Return true if a variable, parameter, or slot exists. */
public boolean isDefined(String name) {
return slots.containsKey(name);
}
private void checkDefined(String name) {
if (isDefined(name))
throw new ExceptionSemantic("RS0095: " + name + " is already defined in operator " + getSignature());
}
/** Define a slot. */
public void defineSlot(String name, SlotScoped slot) {
checkDefined(name);
slots.put(name, slot);
}
private void defineVariable(String name, SlotScoped slot) {
defineSlot(name, slot);
operator.setVariableCount(operator.getVariableCount() + 1);
}
/** Define a variable. */
public void defineVariable(String name, Type type) {
defineVariable(name, new Variable(operator.getDepth(), operator.getVariableCount(), type));
}
/** Define a constant. */
public void defineConstant(String name, Type type) {
defineVariable(name, new Constant(operator.getDepth(), operator.getVariableCount(), type));
}
/** Define a parameter. */
public void defineParameter(String name, Type type) {
defineSlot(name, new Parameter(operator.getDepth(), operator.getParameterCount(), type));
getSignature().addParameter(name, type);
operator.setParameterCount(operator.getParameterCount() + 1);
}
/** Define a private relvar. */
public void defineRelvarPrivate(RelDatabase database, String name, RelvarHeading keydef) {
defineVariable(name, database.createPrivateRelvar(operator.getDepth(), operator.getVariableCount(), keydef));
}
/** Return a reference at this nesting level only. */
public Slot findReference(String name) {
return slots.get(name);
}
/** Get the location of a variable, parameter or other identifier. Return null if it doesn't exist. */
public Slot getReference(String name) {
OperatorDefinition definition = this;
while (definition != null) {
Slot slot = definition.findReference(name);
if (slot != null)
return slot;
definition = definition.getParentOperatorDefinition();
}
return null;
}
/** Return true if an operator exists. */
private boolean isOperatorDefined(OperatorSignature signature) {
return operators.containsKey(signature);
}
/** Define an operator. */
public void defineOperator(OperatorDefinition definition) {
OperatorSignature signature = definition.getSignature();
if (signature.isAnonymous())
return;
if (isOperatorDefined(signature))
throw new ExceptionSemantic("RS0096: Operator " + signature + " is already defined.");
operators.put(signature, definition);
}
/** Remove an operator. */
public void removeOperator(OperatorSignature signature) {
operators.remove(signature);
}
/** Return an operator at this nesting level only. */
public OperatorDefinition findOperator(OperatorSignature signature) {
return operators.get(signature);
}
/** Return an operator. */
public OperatorDefinition getOperator(OperatorSignature signature) {
OperatorDefinition definition = this;
while (definition != null) {
OperatorDefinition operator = definition.findOperator(signature);
if (operator != null)
return operator;
definition = definition.getParentOperatorDefinition();
}
return null;
}
public Iterator<OperatorDefinition> getOperators() {
return operators.values().iterator();
}
public void getPossibleTargetOperators(HashSet<OperatorSignature> targets, OperatorSignature invocationSignature) {
OperatorDefinition definition = this;
while (definition != null) {
Iterator<OperatorDefinition> i = definition.getOperators();
if (i != null)
while (i.hasNext()) {
OperatorDefinition operator = i.next();
OperatorSignature signature = operator.getSignature();
if (signature.canBeInvokedBy(invocationSignature))
targets.add(signature);
}
definition = definition.getParentOperatorDefinition();
}
}
public void compileCall(Generator generator) {
generator.compileInstruction(new OpInvoke(getOperator()));
if (hasReturnDeclaration())
generator.compilePop();
}
private void checkHasReturn() {
if (!hasReturnDeclaration())
throw new ExceptionSemantic("RS0097: Attempt to evaluate " + getSignature() + " which does not have a return value.");
}
public Type compileEvaluate(Generator generator) {
generator.compileInstruction(new OpInvoke(getOperator()));
checkHasReturn();
return getDeclaredReturnType();
}
public void call(Context context) {
(new OpInvoke(getOperator())).execute(context);
if (hasReturnDeclaration())
context.pop();
}
public void evaluate(Context context) {
checkHasReturn();
(new OpInvoke(getOperator())).execute(context);
}
}