package org.reldb.rel.v0.generator; import java.io.PrintStream; import java.lang.reflect.InvocationTargetException; import java.util.HashMap; import java.util.HashSet; import org.reldb.rel.exceptions.ExceptionFatal; import org.reldb.rel.exceptions.ExceptionSemantic; import org.reldb.rel.v0.debuginfo.DebugInfo; import org.reldb.rel.v0.external.ForeignCompilerJava; import org.reldb.rel.v0.interpreter.TutorialDParser; import org.reldb.rel.v0.languages.tutoriald.BaseASTNode; import org.reldb.rel.v0.languages.tutoriald.parser.Token; import org.reldb.rel.v0.storage.BuiltinTypeBuilder; import org.reldb.rel.v0.storage.RelDatabase; import org.reldb.rel.v0.storage.relvars.Relvar; import org.reldb.rel.v0.storage.relvars.RelvarCustomMetadata; import org.reldb.rel.v0.storage.relvars.RelvarDefinition; import org.reldb.rel.v0.storage.relvars.RelvarHeading; import org.reldb.rel.v0.storage.relvars.RelvarInProgress; import org.reldb.rel.v0.storage.relvars.RelvarMetadata; import org.reldb.rel.v0.storage.relvars.RelvarPrivate; import org.reldb.rel.v0.storage.relvars.RelvarRealMetadata; import org.reldb.rel.v0.storage.relvars.RelvarVirtualMetadata; import org.reldb.rel.v0.storage.tables.TableExternal.DuplicateHandling; import org.reldb.rel.v0.types.Attribute; import org.reldb.rel.v0.types.AttributeMap; import org.reldb.rel.v0.types.Heading; import org.reldb.rel.v0.types.JoinMap; import org.reldb.rel.v0.types.OrderMap; import org.reldb.rel.v0.types.Type; import org.reldb.rel.v0.types.TypeAlpha; import org.reldb.rel.v0.types.TypeArray; import org.reldb.rel.v0.types.TypeHeading; import org.reldb.rel.v0.types.TypeOperator; import org.reldb.rel.v0.types.TypeRelation; import org.reldb.rel.v0.types.TypeTuple; import org.reldb.rel.v0.types.builtin.TypeBoolean; import org.reldb.rel.v0.types.builtin.TypeCharacter; import org.reldb.rel.v0.types.builtin.TypeInteger; import org.reldb.rel.v0.types.userdefined.DerivedPossrep; import org.reldb.rel.v0.types.userdefined.Possrep; import org.reldb.rel.v0.types.userdefined.PossrepComponent; import org.reldb.rel.v0.values.Value; import org.reldb.rel.v0.values.ValueAlpha; import org.reldb.rel.v0.values.ValueBoolean; import org.reldb.rel.v0.values.ValueCharacter; import org.reldb.rel.v0.values.ValueInteger; import org.reldb.rel.v0.values.ValueRational; import org.reldb.rel.v0.values.ValueRelationLiteral; import org.reldb.rel.v0.values.ValueTuple; import org.reldb.rel.v0.version.Version; import org.reldb.rel.v0.vm.Instruction; import org.reldb.rel.v0.vm.NativeFunction; import org.reldb.rel.v0.vm.Operator; import org.reldb.rel.v0.vm.instructions.array.OpArrayAppend; import org.reldb.rel.v0.vm.instructions.array.OpArrayFor; import org.reldb.rel.v0.vm.instructions.array.OpArrayGet; import org.reldb.rel.v0.vm.instructions.array.OpArrayProject; import org.reldb.rel.v0.vm.instructions.array.OpArraySet; import org.reldb.rel.v0.vm.instructions.array.OpArrayToArray; import org.reldb.rel.v0.vm.instructions.array.OpArrayToRelation; import org.reldb.rel.v0.vm.instructions.core.OpAdd; import org.reldb.rel.v0.vm.instructions.core.OpAverage; import org.reldb.rel.v0.vm.instructions.core.OpBranchIfFalse; import org.reldb.rel.v0.vm.instructions.core.OpConcatenate; import org.reldb.rel.v0.vm.instructions.core.OpDuplicate; import org.reldb.rel.v0.vm.instructions.core.OpDuplicateUnder; import org.reldb.rel.v0.vm.instructions.core.OpExactly; import org.reldb.rel.v0.vm.instructions.core.OpGetTemporarilyUniqueInteger; import org.reldb.rel.v0.vm.instructions.core.OpInvokeAnonymousEvaluate; import org.reldb.rel.v0.vm.instructions.core.OpInvokeDynamicCall; import org.reldb.rel.v0.vm.instructions.core.OpInvokeDynamicEvaluate; import org.reldb.rel.v0.vm.instructions.core.OpJump; import org.reldb.rel.v0.vm.instructions.core.OpOutput; import org.reldb.rel.v0.vm.instructions.core.OpPop; import org.reldb.rel.v0.vm.instructions.core.OpPreserveContextInValueOperator; import org.reldb.rel.v0.vm.instructions.core.OpPushLiteral; import org.reldb.rel.v0.vm.instructions.core.OpReturn; import org.reldb.rel.v0.vm.instructions.core.OpReturnValue; import org.reldb.rel.v0.vm.instructions.core.OpSwap; import org.reldb.rel.v0.vm.instructions.core.OpWrite; import org.reldb.rel.v0.vm.instructions.core.OpWriteRaw; import org.reldb.rel.v0.vm.instructions.core.OpWriteln; import org.reldb.rel.v0.vm.instructions.core.OpWritelnNoExpression; import org.reldb.rel.v0.vm.instructions.ddl.OpAlterVarRealAlterKey; import org.reldb.rel.v0.vm.instructions.ddl.OpAlterVarRealChangeAttributeType; import org.reldb.rel.v0.vm.instructions.ddl.OpAlterVarRealDropAttribute; import org.reldb.rel.v0.vm.instructions.ddl.OpAlterVarRealInsertAttributes; import org.reldb.rel.v0.vm.instructions.ddl.OpAlterVarRealRenameAttribute; import org.reldb.rel.v0.vm.instructions.ddl.OpCreateConstraint; import org.reldb.rel.v0.vm.instructions.ddl.OpCreateExternalRelvar; import org.reldb.rel.v0.vm.instructions.ddl.OpCreateOperator; import org.reldb.rel.v0.vm.instructions.ddl.OpCreateRealRelvar; import org.reldb.rel.v0.vm.instructions.ddl.OpCreateType; import org.reldb.rel.v0.vm.instructions.ddl.OpCreateVirtualRelvar; import org.reldb.rel.v0.vm.instructions.ddl.OpDropConstraint; import org.reldb.rel.v0.vm.instructions.ddl.OpDropOperator; import org.reldb.rel.v0.vm.instructions.ddl.OpDropRelvar; import org.reldb.rel.v0.vm.instructions.ddl.OpDropType; import org.reldb.rel.v0.vm.instructions.possrep.OpPossrepGetComponent; import org.reldb.rel.v0.vm.instructions.possrep.OpPossrepSetComponent; import org.reldb.rel.v0.vm.instructions.relation.OpRelationDUnion; import org.reldb.rel.v0.vm.instructions.relation.OpRelationDupRemove; import org.reldb.rel.v0.vm.instructions.relation.OpRelationGetTuple; import org.reldb.rel.v0.vm.instructions.relation.OpRelationGroup; import org.reldb.rel.v0.vm.instructions.relation.OpRelationIMinus; import org.reldb.rel.v0.vm.instructions.relation.OpRelationIntersect; import org.reldb.rel.v0.vm.instructions.relation.OpRelationJoin; import org.reldb.rel.v0.vm.instructions.relation.OpRelationLiteralInsertTuple; import org.reldb.rel.v0.vm.instructions.relation.OpRelationMinus; import org.reldb.rel.v0.vm.instructions.relation.OpRelationProduct; import org.reldb.rel.v0.vm.instructions.relation.OpRelationPushLiteral; import org.reldb.rel.v0.vm.instructions.relation.OpRelationRank; import org.reldb.rel.v0.vm.instructions.relation.OpRelationTClose; import org.reldb.rel.v0.vm.instructions.relation.OpRelationUngroup; import org.reldb.rel.v0.vm.instructions.relation.OpRelationUnion; import org.reldb.rel.v0.vm.instructions.relation.OpRelationWhere; import org.reldb.rel.v0.vm.instructions.relation.OpRelationWrite; import org.reldb.rel.v0.vm.instructions.relation.OpRelationXunion; import org.reldb.rel.v0.vm.instructions.relation.OpTupleInRelation; import org.reldb.rel.v0.vm.instructions.relvar.OpRelvarDeleteGivenExpression; import org.reldb.rel.v0.vm.instructions.relvar.OpRelvarDeleteWhere; import org.reldb.rel.v0.vm.instructions.relvar.OpRelvarGlobalGet; import org.reldb.rel.v0.vm.instructions.relvar.OpRelvarIDelete; import org.reldb.rel.v0.vm.instructions.relvar.OpRelvarInsert; import org.reldb.rel.v0.vm.instructions.relvar.OpRelvarInsertNoDuplicates; import org.reldb.rel.v0.vm.instructions.relvar.OpRelvarPurge; import org.reldb.rel.v0.vm.instructions.relvar.OpRelvarUpdate; import org.reldb.rel.v0.vm.instructions.relvar.OpRelvarUpdateWhere; import org.reldb.rel.v0.vm.instructions.system.OpBackup; import org.reldb.rel.v0.vm.instructions.system.OpCheckConstraintsAndCommitOrRollback; import org.reldb.rel.v0.vm.instructions.system.OpExecute; import org.reldb.rel.v0.vm.instructions.system.OpTransactionBegin; import org.reldb.rel.v0.vm.instructions.system.OpTransactionCommit; import org.reldb.rel.v0.vm.instructions.system.OpTransactionRollback; import org.reldb.rel.v0.vm.instructions.tuple.OpTupleGetAttribute; import org.reldb.rel.v0.vm.instructions.tuple.OpTupleJoin; import org.reldb.rel.v0.vm.instructions.tuple.OpTupleJoinDisjoint; import org.reldb.rel.v0.vm.instructions.tuple.OpTupleProject; import org.reldb.rel.v0.vm.instructions.tuple.OpTuplePushLiteral; import org.reldb.rel.v0.vm.instructions.tuple.OpTupleSetAttribute; import org.reldb.rel.v0.vm.instructions.tupleIteratable.OpTupleIteratableProject; import org.reldb.rel.v0.vm.instructions.tupleIteratable.OpTupleIteratableOrder; import org.reldb.rel.v0.vm.instructions.tupleIteratable.OpTupleIteratableMap; /** Code generator. */ public class Generator { // Temporary kludge to obtain a relvar owner private String userRelvarOwner = "User"; private boolean compiling = true; private boolean persistentOnly = false; // The database upon which we're running. private RelDatabase database; // The parser being used to build code. private TutorialDParser parser; // Reference to current operator definition. private OperatorDefinition currentOperatorDefinition; // Depth of _main (interactive) operator. private int interactiveOperatorNestingDepth; // Depth of nested assignments private int assignmentNestingLevel = 0; // Depth of nested compiling on/off actuations. private int compilingOffNestingLevel = 0; // Depth of persistent-only reference on/off actuations. private int persistentOnlyNestingLevel = 0; // Output stream private PrintStream printStream; // When set, collects references to global relvars, operators, and user-defined types. // Used to prevent DROPping VIRTUAL relvars, operators, constraints, and types that use these. private References globalReferenceCollector = null; // New relvars at compile-time. private HashMap<String, RelvarDefinition> relvarsInProgress = new HashMap<String, RelvarDefinition>(); // New types at compile-time. private HashMap<String, Type> typesInProgress = new HashMap<String, Type>(); // True if verbose external operator/type generation is enabled private boolean verboseExternalOperatorTypeGeneration = false; // True if verbose reporting of relvar updates is enabled private boolean verboseRelvarUpdates = true; public Generator(RelDatabase database, PrintStream outputStream) { this.database = database; this.parser = null; this.printStream = outputStream; initialise(); } private void initialise() { compiling = true; persistentOnly = false; assignmentNestingLevel = 0; compilingOffNestingLevel = 0; persistentOnlyNestingLevel = 0; currentOperatorDefinition = null; globalReferenceCollector = null; // _root definition does not execute but holds built-in operator definitions operatorDefinition("Root Operator Context"); // make sure it's "special", so we don't really see it currentOperatorDefinition.setSpecial(true); interactiveOperatorNestingDepth = currentOperatorDefinition.getDepth(); } /** Reset the generator after encountering an error. */ public void reset() { database.rollbackTransactionIfThereIsOne(); relvarsInProgress.clear(); typesInProgress.clear(); initialise(); } public RelDatabase getDatabase() { return database; } public void setParser(TutorialDParser parser) { this.parser = parser; } public PrintStream getPrintStream() { return printStream; } public void setOwner(String owner) { userRelvarOwner = owner; } public void beginCompilation() { // Begin main operator definition operatorDefinition("Interactive Session"); // make sure it's "special", so we don't really see it currentOperatorDefinition.setSpecial(true); // Obtain depth of "Interactive Session" interactiveOperatorNestingDepth = currentOperatorDefinition.getDepth(); } public OperatorDefinition endCompilation() { // Capture reference to main operator definition OperatorDefinition mainOperatorDefinition = currentOperatorDefinition; // End main operator definition endOperator(); // Return main operator definition return mainOperatorDefinition; } public void setVerboseExternalCompilation(boolean b) { verboseExternalOperatorTypeGeneration = b; } public void setVerboseRelvarUpdates(boolean b) { verboseRelvarUpdates = b; } public boolean isVerboseRelvarUpdates() { return verboseRelvarUpdates; } /** Turn code generation on or off. Useful for doing type checking without generating code. */ public void setCompilingOn() { if (--compilingOffNestingLevel == 0) compiling = true; } /** Turn code generation off. */ public void setCompilingOff() { if (compilingOffNestingLevel++ == 0) compiling = false; } private DebugInfo getDebugInfo() { if (parser != null) return new DebugInfo(parser.getCurrentNode(), getOperatorDefinitionLineReferenceStack(parser.getCurrentNode().first_token.beginLine)); return new DebugInfo("unknown location"); } public void compileInstruction(Instruction instruction) { if (compiling) currentOperatorDefinition.compile(getDebugInfo(), instruction); } public void compileInstructionAt(Instruction instruction, int address) { if (compiling) currentOperatorDefinition.compileAt(getDebugInfo(), instruction, address); } private void compileCheckConstraintsAndCommitOrRollback() { compileInstruction(new OpCheckConstraintsAndCommitOrRollback()); } /** Invoked prior to a set of possibly-nested assignment statements. */ private void beginAssignments() { compileTransactionBegin(); } /** Invoked after a set of possibly-nested assignment statements. */ private void endAssignments() { compileCheckConstraintsAndCommitOrRollback(); } /** Invoked prior to one or more assignment statements. May be nested. */ public void beginAssignment() { if (assignmentNestingLevel++ == 0) beginAssignments(); } /** Invoked after one or more assignment statements. May be nested. */ public void endAssignment() { if (--assignmentNestingLevel == 0) endAssignments(); } public void defineVariable(String varname, Type type) { currentOperatorDefinition.defineVariable(varname, type); compileVariableInitialise(varname); } public void defineConstant(String constname, Type type) { currentOperatorDefinition.defineConstant(constname, type); compileVariableInitialise(constname); } public void defineRelvarReal(String varname, RelvarHeading keydef, References references) { if (currentOperatorDefinition.getDepth() > interactiveOperatorNestingDepth) throw new ExceptionSemantic("RS0018: REAL relation-valued variables may not be defined inside a user-defined operator."); if (relvarsInProgress.containsKey(varname) || database.isRelvarExists(varname)) throw new ExceptionSemantic("RS0019: " + varname + " already exists."); RelvarDefinition relvar = new RelvarDefinition(varname, new RelvarRealMetadata(database, keydef, userRelvarOwner), references); relvarsInProgress.put(varname, relvar); beginAssignment(); compileInstruction(new OpCreateRealRelvar(relvarsInProgress, relvar)); compileVariableInitialise(varname); endAssignment(); } public void defineRelvarPublic(String varname, RelvarHeading expectedKeydef) { RelvarMetadata metadata = database.getRelvarMetadata(varname); if (metadata == null) throw new ExceptionSemantic("RS0020: Relation-valued variable '" + varname + "' does not exist."); TypeRelation actualRelvarType = new TypeRelation(metadata.getHeadingDefinition(database).getHeading()); // TODO - type check can be less restrictive; if heading of actualRelvarType is a subset of expectedType it should be acceptable if (!expectedKeydef.getHeading().canAccept(actualRelvarType.getHeading())) throw new ExceptionSemantic("RS0021: Expected relation-valued variable '" + varname + "' to be " + new TypeRelation(expectedKeydef.getHeading()) + " but got " + actualRelvarType); // TODO - check that expectedKeydef is a subkey of metadata.getKeyDefinition() here... // Force run-time check of relvar by accessing it compileInstruction(new OpRelvarGlobalGet(varname, expectedKeydef)); compileInstruction(new OpPop()); } public void defineRelvarPrivate(String varname, RelvarHeading keydef) { currentOperatorDefinition.defineRelvarPrivate(database, varname, keydef); compileVariableInitialise(varname); } public void defineRelvarVirtual(String varname, String sourceCode, RelvarHeading keydef, References references) { if (currentOperatorDefinition.getDepth() > interactiveOperatorNestingDepth) throw new ExceptionSemantic("RS0021: VIRTUAL relation-valued variables may not be defined inside a user-defined operator."); if (relvarsInProgress.containsKey(varname) || database.isRelvarExists(varname)) throw new ExceptionSemantic("RS0022: " + varname + " already exists."); RelvarDefinition relvar = new RelvarDefinition(varname, new RelvarVirtualMetadata(database, sourceCode, keydef, userRelvarOwner), references); relvarsInProgress.put(varname, relvar); beginAssignment(); compileInstruction(new OpCreateVirtualRelvar(relvarsInProgress, relvar)); endAssignment(); } public void defineRelvarExternal(String varname, String externalRelvarType, String externalRelvarSpecification, String duplicates) { DuplicateHandling handler = DuplicateHandling.AUTOKEY; if (duplicates.compareToIgnoreCase("DUP_REMOVE") == 0) handler = DuplicateHandling.DUP_REMOVE; else if (duplicates.compareToIgnoreCase("DUP_COUNT") == 0) handler = DuplicateHandling.DUP_COUNT; else if (duplicates.compareToIgnoreCase("AUTOKEY") == 0) handler = DuplicateHandling.AUTOKEY; else throw new ExceptionSemantic("RS0023: Expected DUP_REMOVE, DUP_COUNT or AUTOKEY but got: " + duplicates); if (currentOperatorDefinition.getDepth() > interactiveOperatorNestingDepth) throw new ExceptionSemantic("RS0024: EXTERNAL relation-valued variables may not be defined inside a user-defined operator."); if (relvarsInProgress.containsKey(varname) || database.isRelvarExists(varname)) throw new ExceptionSemantic("RS0025: " + varname + " already exists."); String className = "org.reldb.rel.v" + Version.getDatabaseVersion() + ".storage.relvars.external." + externalRelvarType.toLowerCase() + ".Relvar" + externalRelvarType.toUpperCase() + "Metadata"; Class<?> clazz; try { clazz = Class.forName(className); } catch (ClassNotFoundException e1) { throw new ExceptionSemantic("RS0451: Can't find " + className + " to handle external relvar of type " + externalRelvarType); } RelvarCustomMetadata metadata = null; try { metadata = (RelvarCustomMetadata)clazz.getConstructors()[0].newInstance(database, userRelvarOwner, externalRelvarSpecification, handler); } catch (InvocationTargetException ite) { String msg = "RS0450: EXTERNAL relvar definition failed due to: " + ite.getCause(); throw new ExceptionSemantic(msg); } catch (Exception e) { throw new ExceptionFatal("RS0449: EXTERNAL relvar definition failed due to: " + e); } RelvarDefinition relvar = new RelvarDefinition(varname, metadata, new References()); relvarsInProgress.put(varname, relvar); beginAssignment(); compileInstruction(new OpCreateExternalRelvar(relvarsInProgress, relvar)); endAssignment(); } public void dropRelvar(String varName) { if (!database.isRelvarExists(varName)) throw new ExceptionSemantic("RS0031: " + varName + " is not defined."); beginAssignment(); compileInstruction(new OpDropRelvar(varName)); endAssignment(); } // Return TypeScalar value about heading public Value getTypeOf(Heading heading, String headingIn) { Heading metaHeading = new Heading(); metaHeading.add("AttrName", TypeCharacter.getInstance()); metaHeading.add("AttrType", findType("TypeInfo")); ValueRelationLiteral attributes = new ValueRelationLiteral(this); for (Attribute attribute: heading.getAttributes()) { Value[] values = new Value[] { ValueCharacter.select(this, attribute.getName()), getTypeOf(attribute.getType()) }; ValueTuple metadataTuple = new ValueTuple(this, values); attributes.insert(metadataTuple); } ValueCharacter kind = ValueCharacter.select(this, headingIn); TypeAlpha typeNonScalar = (TypeAlpha)findType("NonScalar"); ValueAlpha value = new ValueAlpha(this, typeNonScalar, new Value[] {kind, attributes}, 0); return value; } // Return TypeInfo value about typeOfExpression public Value getTypeOf(Type typeOfExpression) { if (typeOfExpression instanceof TypeHeading) { if (typeOfExpression instanceof TypeTuple) { Heading heading = ((TypeTuple)typeOfExpression).getHeading(); return getTypeOf(heading, "TUPLE"); } else if (typeOfExpression instanceof TypeRelation) { Heading heading = ((TypeRelation)typeOfExpression).getHeading(); return getTypeOf(heading, "RELATION"); } else if (typeOfExpression instanceof TypeArray) { Heading heading = ((TypeArray)typeOfExpression).getElementType().getHeading(); return getTypeOf(heading, "ARRAY"); } else { // We should never get here, but deal with it sensibly if we do. Heading heading = ((TypeRelation)typeOfExpression).getHeading(); return getTypeOf(heading, typeOfExpression.getSignature()); } } else { ValueCharacter scalarSignature = ValueCharacter.select(this, typeOfExpression.getSignature()); TypeAlpha typeScalar = (TypeAlpha)findType("Scalar"); ValueAlpha value = new ValueAlpha(this, typeScalar, new Value[] {scalarSignature}, 0); return value; } } public void addTypeInProgress(String typeName, Type type) { typesInProgress.put(typeName, type); } public void createTypeExternal(String typeName, String language, String source, References references) { if (database.isTypeExists(this, typeName)) throw new ExceptionSemantic("RS0032: TYPE " + typeName + " already exists."); (new ForeignCompilerJava(this, verboseExternalOperatorTypeGeneration)).compileForeignType(typeName, language, source); source = "TYPE " + typeName + " Java FOREIGN " + source + "\nEND TYPE;"; beginAssignment(); compileInstruction(new OpCreateType(typeName, source, userRelvarOwner, "Java", references, null)); endAssignment(); } private int retrievingTypeCount = 0; private Type lastRetrievedType = null; public void beginTypeRetrieval() { retrievingTypeCount++; } public Type endTypeRetrieval() { retrievingTypeCount--; return lastRetrievedType; } // Needed to support recursive type definitions. public void createTypeInternalForwardReference(TypeAlpha udt) { String typeName = udt.getTypeName(); if (retrievingTypeCount == 0 && database.isTypeExists(this, typeName)) throw new ExceptionSemantic("RS0033: TYPE " + typeName + " already exists."); addTypeInProgress(udt.getTypeName(), udt); } public abstract class OperatorAssociatedWithType { public OperatorAssociatedWithType(String createdByType, String owner, OperatorSignature signature, References references) { NativeFunction fn = new NativeFunction() { public Value evaluate(Value[] arguments) { return OperatorAssociatedWithType.this.evaluate(arguments); } }; OperatorDefinitionNativeFunction typeOperator = new OperatorDefinitionNativeFunction(signature, fn); typeOperator.setCreatedByType(createdByType); typeOperator.setLanguage("System"); typeOperator.setReferences(references); typeOperator.setOwner(owner); if (retrievingTypeCount == 0) persistOperator(typeOperator); database.cacheOperator(typeOperator); } public abstract Value evaluate(Value[] arguments); } private boolean isSbyC(TypeAlpha udt) { if (udt.hasSpecialisationConstraint()) return true; for (TypeAlpha subtype: udt.getSubtypes()) if (isSbyC(subtype)) return true; return false; } public TypeAlpha findMST(TypeAlpha udt, ValueAlpha value) { while (true) try { for (TypeAlpha subtype: udt.getSubtypes()) if (subtype.checkSpecialisationConstraint(this, value, database)) return findMST(subtype, value); return udt; } catch (java.util.ConcurrentModificationException cme) { System.out.println("Generator: Attempting to recover from concurrent modification exception in findMST() caused by type(s) being loaded."); } } private static boolean inSelectValue = false; public ValueAlpha selectValue(TypeAlpha udt, ValueAlpha value) { if (inSelectValue) return value; inSelectValue = true; if (isSbyC(udt)) value.setMST(findMST(udt, value)); inSelectValue = false; return value; } private void createTypeOperators(final TypeAlpha udt, final String owner) { final String typeName = udt.getTypeName(); // create type's operators if (udt.getPossrepCount() == 0) { if (udt.isSubtype() && udt.getSupertype().isBuiltin()) { // create selector per Database Explorations Chapter 21 page 348 "Selectors for System Defined Types". References selectorReferenceToType = new References(); selectorReferenceToType.addReferenceToType(typeName); selectorReferenceToType.addReferenceToType(udt.getSupertype().getTypeName()); final OperatorSignature selectorSignature = new OperatorSignature(typeName); selectorSignature.setReturnType(udt); selectorSignature.addParameterType(udt.getSupertype()); new OperatorAssociatedWithType(typeName, owner, selectorSignature, selectorReferenceToType) { public Value evaluate(Value[] arguments) { // Essentially, this is TREAT_AS_typename if (!((ValueAlpha)arguments[0]).getTypeName().equals(typeName)) throw new ExceptionSemantic("RS0034: Selector failed. Argument is not of type '" + typeName + "'."); return arguments[0]; } }; } } else { for (int possrepAt=0; possrepAt<udt.getPossrepCount(); possrepAt++) { final int possrepNumber = possrepAt; final Possrep possrep = udt.getPossrep(possrepNumber); String selectorName = (possrep.getName() == null) ? typeName : possrep.getName(); final OperatorSignature selectorSignature = new OperatorSignature(selectorName); selectorSignature.setReturnType(udt); References selectorReferenceToType = new References(); selectorReferenceToType.addReferenceToType(typeName); for (int componentAt=0; componentAt<possrep.getComponentCount(); componentAt++) { PossrepComponent component = possrep.getComponent(componentAt); selectorSignature.addParameter(component.getName(), component.getType()); selectorReferenceToType.addReferenceToType(component.getType().getSignature()); // create THE_x operator References theReferenceToType = new References(); theReferenceToType.addReferenceToType(typeName); theReferenceToType.addReferenceToType(component.getType().getSignature()); final String theOperatorName = "THE_" + component.getName(); final OperatorSignature theSignature = new OperatorSignature(theOperatorName); theSignature.addParameter("%p0", udt); theSignature.setReturnType(component.getType()); final int componentIndex = component.getComponentIndex(); new OperatorAssociatedWithType(typeName, owner, theSignature, theReferenceToType) { public Value evaluate(Value[] arguments) { Value value = ((ValueAlpha)arguments[0]).getComponentValue(componentIndex); if (value == null) throw new ExceptionSemantic("RS0036: The value for '" + theOperatorName + "' is undefined."); return value; } }; } // create selector if (!(possrep instanceof DerivedPossrep)) { new OperatorAssociatedWithType(typeName, owner, selectorSignature, selectorReferenceToType) { public Value evaluate(Value[] arguments) { ValueAlpha value; // this can be optimised by removing it from the inside of evaluate() if (typeName.equals("CHARACTER")) value = ValueCharacter.select(Generator.this, arguments[0].stringValue()); else if (typeName.equals("BOOLEAN")) value = ValueBoolean.select(Generator.this, arguments[0].booleanValue()); else if (typeName.equals("INTEGER")) value = ValueInteger.select(Generator.this, arguments[0].longValue()); else if (typeName.equals("RATIONAL")) value = ValueRational.select(Generator.this, arguments[0].doubleValue()); else value = new ValueAlpha(Generator.this, udt, arguments, possrepNumber); if (!possrep.checkConstraint(Generator.this, value, database)) throw new ExceptionSemantic("RS0037: Selector " + selectorSignature.toRelLookupString() + " violates POSSREP constraint in type '" + typeName + "'."); possrep.runInitialiser(Generator.this, value, database); if (isSbyC(udt)) { if (!udt.checkSpecialisationConstraint(Generator.this, value, database)) throw new ExceptionSemantic("RS0038: Selector " + selectorSignature.toRelLookupString() + " violates specialisation constraint in type '" + typeName + "'."); value.setMST(findMST(udt, value)); } return value; } }; } } } // create IS_x operator References isReferenceToType = new References(); isReferenceToType.addReferenceToType(typeName); OperatorSignature isSignature = new OperatorSignature("IS_" + typeName); isSignature.addParameter("%p0", udt.getRootType()); isSignature.setReturnType(TypeBoolean.getInstance()); new OperatorAssociatedWithType(typeName, owner, isSignature, isReferenceToType) { public Value evaluate(Value[] arguments) { if (isSbyC(udt)) { return ValueBoolean.select(Generator.this, ((ValueAlpha)arguments[0]).getTypeName().equals(typeName)); } else { Type type = ((ValueAlpha)arguments[0]).getType(getDatabase()); return ValueBoolean.select(Generator.this, udt.canAccept(type)); } } }; // create TREAT_AS_x operator References treatReferenceToType = new References(); treatReferenceToType.addReferenceToType(typeName); final String treatName = "TREAT_AS_" + typeName; OperatorSignature treatSignature = new OperatorSignature(treatName); treatSignature.addParameter("%p0", udt.getRootType()); treatSignature.setReturnType(udt); new OperatorAssociatedWithType(typeName, owner, treatSignature, treatReferenceToType) { public Value evaluate(Value[] arguments) { if (isSbyC(udt)) { if (!((ValueAlpha)arguments[0]).getTypeName().equals(typeName)) throw new ExceptionSemantic("RS0039: " + treatName + " failed. Argument is not of type '" + typeName + "'."); return arguments[0]; } else { Type type = ((ValueAlpha)arguments[0]).getType(getDatabase()); if (!udt.canAccept(type)) throw new ExceptionSemantic("RS0396: " + treatName + " failed. Argument is not of type '" + typeName + "'."); return arguments[0]; } } }; } public Type createTypeInternal(final TypeAlpha udt, String source, References references) { String typeName = udt.getTypeName(); if (udt.getPossrepCount() == 0 && !udt.isUnion() && !(udt.isSubtype() && udt.getSupertype().isBuiltin())) throw new ExceptionSemantic("RS0040: A non-UNION type must define at least one POSSREP or be an immediate subtype of a built-in type."); if (udt.getPossrepCount() > 1) udt.checkPossrepInitialisation(); // multiple POSSREPs require initialisation if (retrievingTypeCount == 0) { if (database.isTypeExists(this, typeName)) throw new ExceptionSemantic("RS0041: TYPE " + typeName + " already exists."); source = "TYPE " + typeName + " " + source + ";"; beginAssignment(); String superTypeName = (udt.isSubtype()) ? udt.getSupertype().getSignature() : null; compileInstruction(new OpCreateType(typeName, source, userRelvarOwner, "Rel", references, superTypeName)); endAssignment(); } createTypeOperators(udt, userRelvarOwner); // set last retrieved type lastRetrievedType = udt; return udt; } public void createTypeBuiltin(final TypeAlpha udt) { String typeName = udt.getTypeName(); createTypeOperators(udt, "Rel"); if (retrievingTypeCount == 0) { beginAssignment(); compileInstruction(new OpCreateType(typeName, "", "Rel", "System", new References(), null)); endAssignment(); } } public void dropType(String typeName) { if (!database.isTypeExists(this, typeName)) throw new ExceptionSemantic("RS0042: TYPE " + typeName + " does not exist."); beginAssignment(); compileInstruction(new OpDropType(typeName)); endAssignment(); } private void checkRelvarIsGlobalPersistent(String varname) { RelvarMetadata metadata = database.getRelvarMetadata(varname); if (!(metadata instanceof RelvarRealMetadata)) throw new ExceptionSemantic("RS0418: To ALTER VAR " + varname + ", it must be a REAL relvar."); } public RelvarHeading alterVarRealRename(String varname, RelvarHeading relvarHeading, String oldAttributeName, String newAttributeName) { checkRelvarIsGlobalPersistent(varname); relvarHeading.renameAttribute(oldAttributeName, newAttributeName); beginAssignment(); compileInstruction(new OpAlterVarRealRenameAttribute(varname, oldAttributeName, newAttributeName)); endAssignment(); return relvarHeading; } public RelvarHeading alterVarRealChangeType(String varname, RelvarHeading relvarHeading, String attributeName, Type newType) { checkRelvarIsGlobalPersistent(varname); relvarHeading.changeTypeAttribute(attributeName, newType); beginAssignment(); compileInstruction(new OpAlterVarRealChangeAttributeType(varname, attributeName, newType)); endAssignment(); return relvarHeading; } public RelvarHeading alterVarRealInsertAttributes(String varname, RelvarHeading relvarHeading, Heading heading) { checkRelvarIsGlobalPersistent(varname); relvarHeading.insertAttributes(heading); beginAssignment(); compileInstruction(new OpAlterVarRealInsertAttributes(varname, heading)); endAssignment(); return relvarHeading; } public RelvarHeading alterVarRealDropAttribute(String varname, RelvarHeading relvarHeading, String attributeName) { checkRelvarIsGlobalPersistent(varname); relvarHeading.dropAttribute(attributeName); beginAssignment(); compileInstruction(new OpAlterVarRealDropAttribute(varname, attributeName)); endAssignment(); return relvarHeading; } public void alterVarRealAlterKey(String varname, RelvarHeading keydefs) { checkRelvarIsGlobalPersistent(varname); beginAssignment(); compileInstruction(new OpAlterVarRealAlterKey(varname, keydefs)); endAssignment(); } // Define new slots in the given operator definition to expose individual possrep components (where the ValueUserdefined is assumed to be // a parameter in the current operation definition, with a name specified by sourceValueParameterName). private class PossrepComponentExposure { PossrepComponentExposure(TypeAlpha udt, final String sourceValueParameterName, final Possrep possrep) { for (int index=0; index < possrep.getComponentCount(); index++) { final int depth = currentOperatorDefinition.getDepth(); final PossrepComponent component = possrep.getComponent(index); currentOperatorDefinition.defineSlot(component.getName(), new SlotScoped(depth, component.getComponentIndex(), component.getType()) { public void compileGet(Generator generator) { generator.compileGet(sourceValueParameterName); generator.compileInstruction(new OpPossrepGetComponent(component.getName(), getOffset())); } public void compileSet(Generator generator) { throw new ExceptionFatal("RS0294: compileSet invoked on SlotScoped in PossrepComponentExposure."); } public void compileInitialise(Generator generator) { throw new ExceptionFatal("RS0295: compileInitialise invoked on SlotScoped in PossrepComponentExposure."); } }); } } } public class PossrepConstraint { private Possrep possrep; private OperatorDefinition possrepConstraintFn; public PossrepConstraint(Possrep possrep) { this.possrep = possrep; setPersistentOnlyOn(); // Define anonymous operator of the form Fn(ValueUserdefined v) RETURNS BOOLEAN; RETURN <boolexpr>; END; possrepConstraintFn = beginAnonymousOperator(); possrepConstraintFn.setDeclaredReturnType(TypeBoolean.getInstance()); possrepConstraintFn.defineParameter("%p0", possrep.getType()); new PossrepComponentExposure(possrep.getType(), "%p0", possrep); } public void endPossrepConstraint() { // Compile RETURN compileReturnValue(possrepConstraintFn.getDeclaredReturnType()); // End of operator endOperator(); setPersistentOnlyOff(); possrep.setConstraint(possrepConstraintFn.getOperator()); } } public class SpecialisationConstraint { private TypeAlpha udt; private OperatorDefinition specialisationConstraintFn; public SpecialisationConstraint(TypeAlpha udt) { if (!udt.isSubtype()) throw new ExceptionSemantic("RS0043: A specialisation constraint may only be specified for subtypes."); this.udt = udt; setPersistentOnlyOn(); // Define anonymous operator of the form Fn(ValueUserdefined SUPERTYPE_NAME) RETURNS BOOLEAN; RETURN <boolexpr>; END; specialisationConstraintFn = beginAnonymousOperator(); specialisationConstraintFn.setDeclaredReturnType(TypeBoolean.getInstance()); specialisationConstraintFn.defineParameter(udt.getSupertype().getTypeName(), udt.getSupertype()); } public void endSpecialisationConstraint() { // Compile RETURN compileReturnValue(specialisationConstraintFn.getDeclaredReturnType()); // End of operator endOperator(); setPersistentOnlyOff(); udt.setSpecialisationConstraint(specialisationConstraintFn.getOperator()); } } public class PossrepInitialisation { // Define new slots in the given operator definition to expose all of a type's possrep components // (where the ValueUserdefined is assumed to be a parameter in the current operation definition, // with a name specified by sourceValueParameterName). private class PossrepComponentExposureForInitialisation { private HashSet<String> componentsRequiringInitialisation = new HashSet<String>(); PossrepComponentExposureForInitialisation(final TypeAlpha udt, final String sourceValueParameterName, final Possrep possrepToInitialise) { for (int possrepIndex=0; possrepIndex < udt.getPossrepCount(); possrepIndex++) { final Possrep possrep = udt.getPossrep(possrepIndex); for (int componentIndex=0; componentIndex<possrep.getComponentCount(); componentIndex++) { final int depth = currentOperatorDefinition.getDepth(); final String componentName = possrep.getComponent(componentIndex).getName(); if (possrep != possrepToInitialise) componentsRequiringInitialisation.add(componentName); PossrepComponent component = possrep.getComponent(componentIndex); currentOperatorDefinition.defineSlot(component.getName(), new SlotScoped(depth, component.getComponentIndex(), component.getType()) { public void compileGet(Generator generator) { if (componentsRequiringInitialisation.contains(componentName) && possrep != possrepToInitialise) throw new ExceptionSemantic("RS0044: Component '" + componentName + "' has been referenced before being initialised."); generator.compileGet(sourceValueParameterName); generator.compileInstruction(new OpPossrepGetComponent(componentName, getOffset())); } public void compileSet(Generator generator) { if (possrep == possrepToInitialise) throw new ExceptionSemantic("RS0045: Component '" + componentName + "' cannot be assigned because it is set via the selector."); generator.compileGet(sourceValueParameterName); generator.compileInstruction(new OpPossrepSetComponent(getOffset())); componentsRequiringInitialisation.remove(componentName); } public void compileInitialise(Generator generator) { throw new ExceptionFatal("RS0296: compileInitialise invoked on SlotScoped in PossrepComponentExposureForInitialisation."); } }); } } } public HashSet<String> getUninitialisedComponents() { return componentsRequiringInitialisation; } } private PossrepComponentExposureForInitialisation componentExposure; public PossrepInitialisation(TypeAlpha possreps, String possrepName) { if (possreps.getPossrepCount() <= 1) throw new ExceptionSemantic("RS0046: INIT is not required if the number of POSSREPs is <= 1."); Possrep possrep = possreps.locatePossrep(possrepName); if (possrep == null) throw new ExceptionSemantic("RS0047: No POSSREP named " + possrepName + " was found."); setPersistentOnlyOn(); // Define anonymous operator of the form Fn(PossrepValue v); OperatorDefinition possrepInitProc = beginAnonymousOperator(); possrepInitProc.defineParameter("p0", possrep.getType()); componentExposure = new PossrepComponentExposureForInitialisation(possrep.getType(), "p0", possrep); possrep.setInitialiser(possrepInitProc.getOperator()); } public void endPossrepInitialisation() { // End of operator endOperator(); setPersistentOnlyOff(); HashSet<String> uninitialisedComponents = componentExposure.getUninitialisedComponents(); if (uninitialisedComponents.size() > 0) { String s = "The following POSSREP components have not been INITialised:"; for (String componentName: uninitialisedComponents) s += "\n\t" + componentName; throw new ExceptionSemantic("RS0048: " + s); } } } public Operator beginConstraintDefinition() { setPersistentOnlyOn(); OperatorDefinition constraintOperator = beginAnonymousOperator(); setDeclaredReturnType(TypeBoolean.getInstance()); return constraintOperator.getOperator(); } public void endConstraintDefinition() { endOperator(); setPersistentOnlyOff(); } public void createConstraint(String constraintName, String sourceCode, Operator operator, References references) { if (database.isConstraintExists(constraintName)) throw new ExceptionSemantic("RS0049: CONSTRAINT " + constraintName + " already exists."); beginAssignment(); compileInstruction(new OpCreateConstraint(constraintName, sourceCode, operator, userRelvarOwner, references)); endAssignment(); } public void dropConstraint(String constraintName) { if (!database.isConstraintExists(constraintName)) throw new ExceptionSemantic("RS0050: CONSTRAINT " + constraintName + " does not exist."); beginAssignment(); compileInstruction(new OpDropConstraint(constraintName)); endAssignment(); } /** Turn on capture of global references. Pass null to turn off collecting. */ public void setGlobalReferenceCollector(References referenceCollector) { globalReferenceCollector = referenceCollector; } public class ExternalOperator { private OperatorDefinition operator; public ExternalOperator(String fnname) { beginOperator(fnname); operator = getCurrentOperatorDefinition(); if (!isTopLevelOperator(operator)) throw new ExceptionSemantic("RS0051: FOREIGN operators may not be nested."); } public void endExternalOperator(String externalLanguage, String sourcecode) { if (operator.getDeclaredReturnType() != null) operator.setDefinedReturnValue(true); operator = (new ForeignCompilerJava(Generator.this, verboseExternalOperatorTypeGeneration)).compileForeignOperator(getCurrentOperatorDefinition().getSignature(), externalLanguage, sourcecode); endOperator(); // Need to remove and redefine, because operator was initially an OperatorDefinitionRel but is now an OperatorDefinitionNative derivative. currentOperatorDefinition.removeOperator(operator.getSignature()); currentOperatorDefinition.defineOperator(operator); addOperator(operator); } } public Type locateType(String typeName) { Type type = typesInProgress.get(typeName); if (type == null) type = database.loadType(this, typeName); if (type != null && globalReferenceCollector != null) globalReferenceCollector.addReferenceToType(typeName); return type; } public Type findType(String typeName) { Type type = locateType(typeName); if (type == null) throw new ExceptionSemantic("RS0052: Type '" + typeName + "' has not been defined."); return type; } private Type[] possibleTypes(Type type) { if (type instanceof TypeAlpha) { Type[] subTypes = ((TypeAlpha)type).getSubtypes().toArray(new Type[0]); Type[] superTypes = ((TypeAlpha)type).getSupertypes().toArray(new Type[0]); Type[] allTypes = new Type[subTypes.length + superTypes.length + 1]; allTypes[0] = type; System.arraycopy(subTypes, 0, allTypes, 1, subTypes.length); System.arraycopy(superTypes, 0, allTypes, subTypes.length + 1, superTypes.length); return allTypes; } else return new Type[] {type}; } private void addTypeSignatures(HashSet<OperatorSignature> invocations, OperatorSignature invocationSignature) { int parmCount = invocationSignature.getParmCount(); Type[][] parameterTypes = new Type[parmCount][]; Type returnType = invocationSignature.getReturnType(); String name = invocationSignature.getName(); int permuter[] = new int[parmCount]; for (int i=0; i<parmCount; i++) { Type type = invocationSignature.getParameterType(i); parameterTypes[i] = possibleTypes(type); permuter[i] = 0; } while (true) { OperatorSignature signature = new OperatorSignature(name); signature.setReturnType(returnType); for (int j=0; j<parmCount; j++) signature.addParameterType(parameterTypes[j][permuter[j]]); invocations.add(signature); int j = 0; while (true) { if (permuter[j] < parameterTypes[j].length - 1) { permuter[j]++; break; } else { permuter[j] = 0; j++; if (j >= parmCount) return; } } } } public HashSet<OperatorSignature> getPossibleTargetSignatures(OperatorDefinition startingOperator, OperatorSignature invocationSignature) { HashSet<OperatorSignature> invocations = new HashSet<OperatorSignature>(); addTypeSignatures(invocations, invocationSignature); HashSet<OperatorSignature> sigs = new HashSet<OperatorSignature>(); for (OperatorSignature invocation: invocations) { database.getPossibleTargetSignatures(sigs, this, invocation); startingOperator.getPossibleTargetOperators(sigs, invocation); } return sigs; } public OperatorDefinition locateOperator(OperatorDefinition startingOperator, OperatorSignature signature) { OperatorDefinition fn = startingOperator.getOperator(signature); if (fn == null) { fn = database.loadOperator(this, signature); if (fn == null) return null; else if (globalReferenceCollector != null) globalReferenceCollector.addReferenceToOperator(signature.toRelLookupString()); } return fn; } public OperatorDefinition locateOperator(OperatorSignature signature) { return locateOperator(currentOperatorDefinition, signature); } public OperatorInvocation findOperator(OperatorSignature signature) { if (signature.isPossiblyDynamicDispatch()) return new OperatorInvocation(signature); OperatorDefinition fn = locateOperator(signature); if (fn == null) throw new ExceptionSemantic("RS0053: Operator '" + signature + "' has not been defined."); return new OperatorInvocation(fn); } public Type compileEvaluate(OperatorDefinition operator) { return operator.compileEvaluate(this); } private void compileCall(OperatorDefinition operator) { operator.compileCall(this); } public Type compileEvaluate(OperatorSignature signature) { OperatorInvocation invocation = findOperator(signature); if (invocation.useDynamicDispatch()) { Type lastFoundReturnType = null; HashSet<OperatorSignature> possibleInvocations = getPossibleTargetSignatures(currentOperatorDefinition, signature); for (OperatorSignature searchFor: possibleInvocations) { OperatorDefinition operator = locateOperator(searchFor); if (operator != null) { Type returnType = operator.getDeclaredReturnType(); if (returnType != null && lastFoundReturnType != null && !lastFoundReturnType.canAccept(returnType)) throw new ExceptionSemantic("RS0054: Operator " + searchFor + " is a possible invocation target but returns type " + returnType + " which is incompatible with " + lastFoundReturnType); if (returnType != null) lastFoundReturnType = returnType; } } if (lastFoundReturnType == null) throw new ExceptionSemantic("RS0055: Could not find operator " + signature); compileInstruction(new OpInvokeDynamicEvaluate(this, invocation.getOperatorSignature())); return lastFoundReturnType; } else { OperatorDefinition operator = invocation.getStaticOperatorDefinition(); return compileEvaluate(operator); } } // ValueOperator will be topmost on stack. Arguments follow. public void compileEvaluateAnonymous() { compileInstruction(new OpInvokeAnonymousEvaluate()); } public void compileCall(OperatorSignature signature) { OperatorInvocation invocation = findOperator(signature); if (invocation.useDynamicDispatch()) { compileInstruction(new OpInvokeDynamicCall(this, invocation.getOperatorSignature())); } else { OperatorDefinition operator = invocation.getStaticOperatorDefinition(); compileCall(operator); } } /** Limit global references to non-transient relvars. */ public void setPersistentOnlyOn() { if (persistentOnlyNestingLevel++ == 0) persistentOnly = true; } /** Turn off global reference limit. */ public void setPersistentOnlyOff() { if (--persistentOnlyNestingLevel == 0) persistentOnly = false; } public Slot findReference(String refname) { Slot slot = currentOperatorDefinition.getReference(refname); if (slot == null) { // is it a database relvar? slot = database.openGlobalRelvar(refname); if (slot == null) { RelvarDefinition pendingRelvar = relvarsInProgress.get(refname); if (pendingRelvar == null) throw new ExceptionSemantic("RS0056: '" + refname + "' has not been defined."); slot = new RelvarInProgress(refname, database, pendingRelvar.getRelvarMetadata()); } if (globalReferenceCollector != null) globalReferenceCollector.addReferenceToRelvar(refname); } else { if (persistentOnly) { if (slot instanceof SlotScoped) { if (((SlotScoped)slot).getDepth() <= interactiveOperatorNestingDepth) throw new ExceptionSemantic("RS0057: In this context, transient global variables like " + refname + " may not be referenced."); } } } return slot; } public void compileRelvarInsert(Slot slot, String relvarName, TypeRelation expressionType) { if (slot.isParameter()) throw new ExceptionSemantic("RS0397: Parameter is not updateable."); slot.compileGet(this); compileInstruction(new OpRelvarInsert()); } public void compileRelvarInsertNoDuplicates(Slot slot, String relvarName, TypeRelation expressionType) { if (slot.isParameter()) throw new ExceptionSemantic("RS0398: Parameter is not updateable."); slot.compileGet(this); compileInstruction(new OpRelvarInsertNoDuplicates()); } public void compileRelvarPurge(Slot slot, String relvarName) { if (slot.isParameter()) throw new ExceptionSemantic("RS0399: Parameter is not updateable."); slot.compileGet(this); compileInstruction(new OpRelvarPurge()); } public void compileTransactionBegin() { compileInstruction(new OpTransactionBegin()); } public void compileTransactionCommit() { compileInstruction(new OpTransactionCommit()); } public void compileTransactionRollback() { compileInstruction(new OpTransactionRollback()); } // Define new slots in the given operator definition to expose individual tuple attributes (where the tuple is assumed to be // a parameter in the current operation definition, with a name specified by sourceTupleParameterName), and allow them to be set. private abstract class TupleAttributeExposure { TupleAttributeExposure(final String sourceTupleParameterName, Heading heading) { int index = 0; for (final Attribute attribute: heading.getAttributes()) { final int depth = currentOperatorDefinition.getDepth(); currentOperatorDefinition.defineSlot(attribute.getName(), new SlotScoped(depth, index++, attribute.getType()) { public void compileGet(Generator generator) { generator.compileGet(sourceTupleParameterName); generator.compileInstruction(new OpTupleGetAttribute(getOffset())); } public void compileSet(Generator generator) { TupleAttributeExposure.this.compileSet(generator, attribute, getDepth(), depth, getOffset()); } public void compileInitialise(Generator generator) { throw new ExceptionFatal("RS0297: compileInitialise invoked on SlotScoped in TupleAttributeExposure."); } }); } } abstract void compileSet(Generator generator, Attribute attribute, int slotDepth, int operatorDepth, int offset); } private int parmNameSerialNumber = 0; // Define an anonymous tuple operator. private abstract class AnonymousTupleOperator { private OperatorDefinition operator; private String name; AnonymousTupleOperator(TypeTuple sourceType, Type returnType) { name = "%tuple" + parmNameSerialNumber++; operator = beginAnonymousOperator(); beginParameterDefinitions(); defineOperatorParameter(getTupleParameterName(), sourceType); endParameterDefinitions(); setDeclaredReturnType(returnType); // Set up to access each attribute via a Slot in the current scope. // Subsequent dereference operations on original tuple attributes will use the following: new TupleAttributeExposure(getTupleParameterName(), sourceType.getHeading()) { public void compileSet(Generator generator, Attribute attribute, int slotDepth, int operatorDepth, int offset) { AnonymousTupleOperator.this.compileSet(generator, attribute, slotDepth, operatorDepth, offset); } }; } String getTupleParameterName() { return name; } void end() { if (getDeclaredReturnType() != null) compileReturnValue(getDeclaredReturnType()); endOperator(); } Operator getOperator() { return operator.getOperator(); } OperatorDefinition getOperatorDefinition() { return operator; } public abstract void compileSet(Generator generator, Attribute attribute, int slotDepth, int operatorDepth, int offset); } // Anonymous operator of the form OPERATOR(TUPLE x) RETURNS BOOLEAN private class TupleFilterOperator extends AnonymousTupleOperator { private String contextDescription; TupleFilterOperator(TypeTuple source, String contextDescription) { super(source, TypeBoolean.getInstance()); this.contextDescription = contextDescription; } public void compileSet(Generator generator, Attribute attribute, int slotDepth, int operatorDepth, int offset) { throw new ExceptionFatal("RS0298: Attempt to set attribute " + attribute.getName() + " in " + contextDescription + "."); } } // Anonymous operator of the form OPERATOR(TUPLE x) RETURNS TUPLE private class TupleMapOperator extends AnonymousTupleOperator { private String contextDescription; TupleMapOperator(TypeTuple source, String contextDescription) { super(source, source); this.contextDescription = contextDescription; } public void compileSet(Generator generator, Attribute attribute, int slotDepth, int operatorDepth, int offset) { if (slotDepth != operatorDepth) throw new ExceptionSemantic("RS0058: " + contextDescription + " may only assign values to the most local tuple's attributes."); generator.compileGet(getTupleParameterName()); generator.compileInstruction(new OpTupleSetAttribute(offset)); generator.compileSet(getTupleParameterName()); } void end() { compileGet(getTupleParameterName()); super.end(); } } public class DeleteHandler { private TypeRelation operandType; private Slot reference; private AnonymousTupleOperator whereOperator; /** Must precede the expression of a DELETE ... [ WHERE ] ... statement. * * @param relvarName - name of relvar * @param operand - TypeRelation of relation operand * @return - DeleteHandler */ public DeleteHandler(String relvarName, TypeRelation operand) { reference = findReference(relvarName); if (!(reference instanceof Relvar || reference instanceof RelvarPrivate || reference.getType().toString().startsWith("RELATION"))) throw new ExceptionSemantic("RS0059: Expected a relation-valued variable or attribute in DELETE but got " + reference.getType()); if (reference.isParameter()) throw new ExceptionSemantic("RS0400: Parameter is not updateable."); operandType = operand; whereOperator = null; } // Invoked if WHERE is specified. public void doWhere() { // Define an anonymous operator of the type OPERATOR(TUPLE x) RETURNS BOOLEAN whereOperator = new TupleFilterOperator(new TypeTuple(operandType.getHeading()), "DELETE ... WHERE"); } /** Must follow the expression of a DELETE ... [ WHERE ] ... statement. * @param expressionType * * @param where - DeleteWhere * @return - TypeRelation */ public TypeRelation endDeleteHandler(Type expressionType) { if (whereOperator != null) { whereOperator.end(); reference.compileGet(Generator.this); compileInstruction(new OpRelvarDeleteWhere(whereOperator.getOperator())); } else { if (!operandType.canAccept(expressionType)) throw new ExceptionSemantic("RS0060: Expected expression of type " + operandType + " but got " + expressionType); compileReformat(operandType, expressionType); reference.compileGet(Generator.this); compileInstruction(new OpRelvarDeleteGivenExpression()); } return operandType; } } public void compileRelvarIDelete(Slot slot, String identifier) { if (slot.isParameter()) throw new ExceptionSemantic("RS0401: Parameter is not updateable."); slot.compileGet(Generator.this); compileInstruction(new OpRelvarIDelete()); } public class UpdateWhere { private AnonymousTupleOperator tupleUpdateOp = null; private AnonymousTupleOperator tupleFilterOp = null; private TypeTuple sourceTupleType; private Slot reference; /** Begin relvar update. * * @param relvarName * @param sourceType */ public UpdateWhere(String relvarName, TypeRelation sourceType) { reference = findReference(relvarName); if (!(reference instanceof Relvar || reference instanceof RelvarPrivate || reference.getType().toString().startsWith("RELATION"))) throw new ExceptionSemantic("RS0061: Expected a relation-valued variable or attribute in UPDATE ... WHERE but got " + reference.getType()); if (reference.isParameter()) throw new ExceptionSemantic("RS0402: Parameter is not updateable."); sourceTupleType = new TypeTuple(sourceType.getHeading()); } /** To be used immediately prior to a relvar update's optional WHERE clause expression. */ public void beginRelvarUpdateWhere() { // Define an anonymous operator of the type OPERATOR(TUPLE x) RETURNS BOOLEAN tupleFilterOp = new TupleFilterOperator(sourceTupleType, "UPDATE ... WHERE"); } /** To be used immediately after a relvar update's optional WHERE clause expression. */ public void endRelvarUpdateWhere() { tupleFilterOp.end(); } /** Begin update statement */ public void beginRelvarUpdateAssignment() { // Define anonymous function that accepts a tuple as an argument and returns a tuple tupleUpdateOp = new TupleMapOperator(sourceTupleType, "Tuple UPDATE"); } /** * End relvar update. * * This is to be used immediately after an assignment statement. */ public void endUpdateWhere() { tupleUpdateOp.end(); reference.compileGet(Generator.this); if (tupleFilterOp != null) compileInstruction(new OpRelvarUpdateWhere(tupleFilterOp.getOperator(), tupleUpdateOp.getOperator())); else compileInstruction(new OpRelvarUpdate(tupleUpdateOp.getOperator())); } } /** Assuming a Value on the stack of Type sourceType is to be assigned * to a slot of Type destinationType, project the Value to conform to * destinationType. This should only compile a projection if the * source and destination are both of TypeHeading, or are both of TypeArray. * * @param destinationType * @param sourceType */ public Type compileReformat(Type destinationType, Type sourceType) { if (!destinationType.requiresReformatOf(sourceType)) return destinationType; if (destinationType instanceof TypeArray && sourceType instanceof TypeArray) { Type sourceElementType = ((TypeArray)sourceType).getElementType(); Type destinationElementType = ((TypeArray)destinationType).getElementType(); if (!destinationType.requiresReformatOf(sourceType)) return destinationType; AttributeMap reformatMap = new AttributeMap(((TypeHeading)destinationElementType).getHeading(), ((TypeHeading)sourceElementType).getHeading()); compileInstruction(new OpArrayProject(reformatMap)); } else { AttributeMap reformatMap = new AttributeMap(((TypeHeading)destinationType).getHeading(), ((TypeHeading)sourceType).getHeading()); if (destinationType instanceof TypeTuple && sourceType instanceof TypeTuple) { compileInstruction(new OpTupleProject(reformatMap)); } else if (destinationType instanceof TypeRelation && sourceType instanceof TypeRelation) { compileInstruction(new OpTupleIteratableProject(reformatMap)); } else throw new ExceptionFatal("RS0299: compileReformat doesn't know how to deal with a " + destinationType + " and a " + sourceType); } return destinationType; } /** Load an ARRAY variable from an ARRAY (usually generated by ORDER). */ private void compileLoadArrayFromRelation(String identifier, Slot slot, TypeArray expressionType) { TypeArray arrayVarType = (TypeArray)slot.getType(); Type targetContainedType = arrayVarType.getElementType(); if (!(targetContainedType instanceof TypeTuple)) throw new ExceptionSemantic("RS0062: 'LOAD " + identifier + "' should reference an ARRAY of TUPLEs, but references an ARRAY of " + targetContainedType); Type sourceContainedType = expressionType.getElementType(); if (!targetContainedType.canAccept(sourceContainedType)) throw new ExceptionSemantic("RS0063: 'LOAD " + identifier + "' expected an ARRAY of " + targetContainedType + " but got an ARRAY of " + sourceContainedType); compileReformat(arrayVarType, expressionType); compileInstruction(new OpArrayToArray()); compileSet(identifier); } /** Load a relvar from an ARRAY. */ private void compileLoadRelationFromArray(String identifier, Slot slot, TypeArray expressionType) { compileInstruction(new OpArrayToRelation()); Type containedType = expressionType.getElementType(); if (!(containedType instanceof TypeTuple)) throw new ExceptionSemantic("RS0064: 'LOAD " + identifier + "' should reference an ARRAY of TUPLEs, but references an ARRAY of " + containedType); Heading sourceHeading = ((TypeTuple)containedType).getHeading(); TypeRelation source = new TypeRelation(sourceHeading); TypeRelation target = (TypeRelation)slot.getType(); Heading targetHeading = target.getHeading(); if (!target.canAccept(source)) throw new ExceptionSemantic("RS0065: 'LOAD " + identifier + "' expected an ARRAY with TUPLEs of heading " + targetHeading + " but got " + sourceHeading); compileReformat(target, source); compileSet(identifier); } public void compileLoad(String identifier, Type expressionType) { Slot slot = findReference(identifier); if (slot.getType() instanceof TypeRelation) { if (expressionType instanceof TypeArray) compileLoadRelationFromArray(identifier, slot, (TypeArray)expressionType); else throw new ExceptionSemantic("RS0066: 'LOAD " + identifier + " FROM ...' expected an expression of type ARRAY, but got " + expressionType); } else if (slot.getType() instanceof TypeArray) { if (expressionType instanceof TypeArray) compileLoadArrayFromRelation(identifier, slot, (TypeArray)expressionType); else throw new ExceptionSemantic("RS0067: 'LOAD " + identifier + " FROM ...' expected an expression of type ARRAY (usually via ORDER), but got " + expressionType); } else throw new ExceptionSemantic("RS0068: 'LOAD " + identifier + "', should reference a RELATION-valued variable or an ARRAY variable, but references a variable of type " + slot.getType()); } /** Small DIVIDEBY A1 - attributes common to r1 and r3 A2 - attributes common to r2 and r3 r1 {A1} MINUS ((r1 {A1} JOIN r2 {A2}) MINUS r3 {A1, A2}) {A1} **/ public TypeRelation compileSmallDivide(TypeRelation r1, TypeRelation r2, TypeRelation r3) { final String r1Name = "%r1"; final String r2Name = "%r2"; final String r3Name = "%r3"; Heading A1 = r1.getHeading().intersect(r3.getHeading()); Heading A2 = r2.getHeading().intersect(r3.getHeading()); Heading A1A2 = A1.union(A2); // Define anonymous operator OperatorDefinition div = beginAnonymousOperator(); beginParameterDefinitions(); defineOperatorParameter(r1Name, r1); defineOperatorParameter(r2Name, r2); defineOperatorParameter(r3Name, r3); endParameterDefinitions(); // VarGet r1 compileGet(r1Name); // RelationProject A1 TypeRelation r1p = compileRelationProject(r1, new SelectAttributes(A1)); // DUP compileDuplicate(); // VarGet r2 compileGet(r2Name); // RelationProject A2 TypeRelation r2p = compileRelationProject(r2, new SelectAttributes(A2)); // RelationJoin TypeRelation r1r2joined = compileRelationJoin(r1p, r2p); // VarGet r3 compileGet(r3Name); // RelationProject A1, A2 TypeRelation r3p = compileRelationProject(r3, new SelectAttributes(A1A2)); // RelationMinus TypeRelation minusResult = compileRelationMinus(r1r2joined, r3p); // RelationProject A1 TypeRelation rightResult = compileRelationProject(minusResult, new SelectAttributes(A1)); // RelationMinus TypeRelation finalResult = compileRelationMinus(r1p, rightResult); setDeclaredReturnType(finalResult); compileReturnValue(finalResult); endOperator(); return (TypeRelation)compileEvaluate(div); } /** Great DIVIDEBY A1 - attributes common to r1, r3 A2 - attributes common to r2, r4 A3 - attributes common to r3, r4 WITH r1 {A1} as r1p, r4 {A2, A3} as r4p: (r1p JOIN r2 {A2}) MINUS ((r1p JOIN r4p) MINUS (r3 {A1, A3} JOIN r4p)) {A1, A2} **/ public TypeRelation compileGreatDivide(TypeRelation r1, TypeRelation r2, TypeRelation r3, TypeRelation r4) { final String r1Name = "%r1"; final String r2Name = "%r2"; final String r3Name = "%r3"; final String r4Name = "%r4"; final String r1pName = "%r1p"; final String r4pName = "%r4p"; Heading A1 = r1.getHeading().intersect(r3.getHeading()); Heading A2 = r2.getHeading().intersect(r4.getHeading()); Heading A3 = r3.getHeading().intersect(r4.getHeading()); Heading A1A2 = A1.union(A2); Heading A1A3 = A1.union(A3); Heading A2A3 = A2.union(A3); // Define anonymous operator OperatorDefinition div = beginAnonymousOperator(); beginParameterDefinitions(); defineOperatorParameter(r1Name, r1); defineOperatorParameter(r2Name, r2); defineOperatorParameter(r3Name, r3); defineOperatorParameter(r4Name, r4); endParameterDefinitions(); // WITH With with = new With(); // r1 compileGet(r1Name); // {A1} TypeRelation r1pType = this.compileRelationProject(r1, new SelectAttributes(A1)); // as r1p with.addWithItem(r1pType, r1pName); // r4 compileGet(r4Name); // {A2, A3} TypeRelation r4pType = this.compileRelationProject(r4, new SelectAttributes(A2A3)); // as r4p with.addWithItem(r4pType, r4pName); // VarGet r1p compileGet(r1pName); // VarGet r2 compileGet(r2Name); // RelationProject A2 TypeRelation r2Project = compileRelationProject(r2, new SelectAttributes(A2)); // RelationJOIN TypeRelation leftJoin = compileRelationJoin(r1pType, r2Project); // VarGet r1p compileGet(r1pName); // VarGet r4p compileGet(r4pName); // RelationJOIN TypeRelation middleJoin = compileRelationJoin(r1pType, r4pType); // VarGet r3 compileGet(r3Name); // RelationProject A1, A3 TypeRelation r3Project = compileRelationProject(r3, new SelectAttributes(A1A3)); // VarGet r4p compileGet(r4pName); // RelationJoin TypeRelation rightJoin = compileRelationJoin(r3Project, r4pType); // RelationMinus TypeRelation firstMinus = compileRelationMinus(middleJoin, rightJoin); // RelationProject A1, A2 TypeRelation rightProject = compileRelationProject(firstMinus, new SelectAttributes(A1A2)); // RelationMinus TypeRelation finalResult = compileRelationMinus(leftJoin, rightProject); // end of WITH with.endWith(finalResult); // end of DIVIDEBY anonymous operator setDeclaredReturnType(finalResult); compileReturnValue(finalResult); endOperator(); return (TypeRelation)compileEvaluate(div); } private static int summarizeParmNameSerial = 0; private static int summarizeItemNameSerial = 0; public class Summarize { private OperatorDefinition summarizeOperator; private Extend outerExtend; private String r1ParmName; private String r2ParmName; private String yItemName; /** Begin SUMMARIZE */ public Summarize(TypeRelation source, TypeRelation per) { // create parm names r1ParmName = "%R1" + summarizeParmNameSerial; r2ParmName = "%R2" + summarizeParmNameSerial; yItemName = "%Y" + summarizeParmNameSerial; summarizeParmNameSerial++; // Attributes common to r1 (source) and r2 (per) Heading common = source.getHeading().intersect(per.getHeading()); // Begin SUMMARIZE anonymous operator summarizeOperator = beginAnonymousOperator(); beginParameterDefinitions(); defineOperatorParameter(r1ParmName, source); defineOperatorParameter(r2ParmName, per); endParameterDefinitions(); // Get r2 compileGet(r2ParmName); // begin extend of r2 outerExtend = new Extend(per.getHeading()); // get r1 compileGet(r1ParmName); // RELATION { RelationDefinition relDef = new RelationDefinition(common); // TUPLE { TupleDefinition tupleDef = new TupleDefinition(); // put common attributes in tuple -- {a a, b b, ...} etc. for (Attribute attribute : common.getAttributes()) { // get attribute from r1 compileGet(attribute.getName()); // set tuple attribute tupleDef.setTupleAttribute(attribute.getName(), attribute.getType()); } // } TypeTuple tupleType = tupleDef.endTuple(); relDef.addTupleToRelation(tupleType); // } TypeRelation relationType = relDef.endRelation(); Type typeOfY = compileRelationJoin(source, relationType); // Extend AS %Y outerExtend.addExtendItem(yItemName, typeOfY); } /** End a SUMMARIZE definition. */ public TypeRelation endSummarize() { // end EXTEND TypeRelation extendType = endRelationExtend(outerExtend); // {ALL BUT %Y} SelectAttributes eliminateYProjector = new SelectAttributes(); eliminateYProjector.add(yItemName); eliminateYProjector.setAllBut(true); TypeRelation result = compileRelationProject(extendType, eliminateYProjector); // End SUMMARIZE operator setDeclaredReturnType(result); compileReturnValue(result); endOperator(); // Compile invocation of SUMMARIZE operator return (TypeRelation)compileEvaluate(summarizeOperator); } public class SummarizeItem { private Extend innerExtend; private TypeRelation typeOfY; private String extendAttributeName; private boolean isDistinct; /** Begin a SUMMARIZE item definition. */ public SummarizeItem() { extendAttributeName = "%X" + summarizeItemNameSerial; summarizeItemNameSerial++; // PUSH value of '%Y' typeOfY = (TypeRelation)compileGet(yItemName); } public String getExtendAttributeName() { return extendAttributeName; } public TypeRelation getTypeOfY() { return typeOfY; } /** This must appear before a SUMMARIZE expression 'exp' */ public void beginSummarizeItemExpression() { // BEGIN EXTEND innerExtend = new Extend(typeOfY.getHeading()); } /** End a SUMMARIZE aggregate operator's expression. * * This must appear immediately after a SUMMARIZE expression 'exp' * * This should appear before invocation of the aggregate operator. */ public TypeRelation endSummarizeItemExpression(Type exprType, boolean distinct) { // Extend AS %X innerExtend.addExtendItem(extendAttributeName, exprType); TypeRelation forAggregateExpressionType = endRelationExtend(innerExtend); // if distinct, compile {extendAttributeName} if (distinct) { SelectAttributes distinctProjector = new SelectAttributes(); distinctProjector.add(extendAttributeName); forAggregateExpressionType = compileRelationProject(forAggregateExpressionType, distinctProjector); } return forAggregateExpressionType; } /** End a SUMMARIZE item. * * This must appear after invocation of the aggregate operator. */ public void endSummarizeItem(Type aggReturnType, String aggResultName) { // Extend with aggResultName outerExtend.addExtendItem(aggResultName, aggReturnType); } public void setDistinct(boolean b) { isDistinct = b; } public boolean isDistinct() { return isDistinct; } } } private static int extendTupleNameSerial = 0; public class Extend { private OperatorDefinition extendOp; private Heading sourceHeading; private Heading extendedHeading = new Heading(); private String sourceTupleParmName; private String extendTupleParmName; /** * Begin tuple or relation extend. * * This is to be used immediately before any extend expressions, to * introduce the existing tuple's attributes into the current scope so they * can be dereferenced, and to introduce the extend attributes so they can * be assigned. */ public Extend(Heading sourceHeading) { this.sourceHeading = sourceHeading; // create parameter names sourceTupleParmName = "%source_tuple" + extendTupleNameSerial; extendTupleParmName = "%extend_tuple" + extendTupleNameSerial; extendTupleNameSerial++; // Define anonymous function that accepts two tuples as arguments and returns a tuple extendOp = beginAnonymousOperator(); beginParameterDefinitions(); defineOperatorParameter(sourceTupleParmName, new TypeTuple(sourceHeading)); defineOperatorParameter(extendTupleParmName, TypeTuple.getEmptyTupleType()); endParameterDefinitions(); // Set up to access each attribute via a Slot in the current scope. // Subsequent dereference operations on original tuple attributes will use the following: new TupleAttributeExposure(sourceTupleParmName, sourceHeading) { public void compileSet(Generator generator, Attribute attribute, int slotDepth, int operatorDepth, int offset) { throw new ExceptionFatal("RS0300: Attempt to set original attribute '" + attribute.getName() + "' in EXTEND."); } }; } /** This should be called immediately after the compilation of an expression whose * result will be assigned to the given new identifier. */ public void addExtendItem(String identifier, Type expressionType) { // define target as new slot final int depth = currentOperatorDefinition.getDepth(); SlotScoped newSlot = new SlotScoped(depth, extendedHeading.getDegree(), expressionType) { public void compileGet(Generator generator) { generator.compileGet(extendTupleParmName); generator.compileInstruction(new OpTupleGetAttribute(getOffset())); } public void compileSet(Generator generator) { if (getDepth() != depth) throw new ExceptionSemantic("RS0069: EXTEND may only assign values to newly-defined attributes."); generator.compileGet(extendTupleParmName); generator.compileInstruction(new OpTupleSetAttribute(getOffset())); generator.compileSet(extendTupleParmName); } public void compileInitialise(Generator generator) { throw new ExceptionFatal("RS0301: compileInitialise invoked on SlotScoped in addExtendItem()."); } }; currentOperatorDefinition.defineSlot(identifier, newSlot); // compile assign to attribute here compileSet(newSlot); // add to extend heading extendedHeading.add(identifier, expressionType); } /** Compile generation of a unique tuple number, and assign it. */ public void addExtendSerialiser(String attributeName) { compileInstruction(new OpGetTemporarilyUniqueInteger()); addExtendItem(attributeName, TypeInteger.getInstance()); } /** Get extended heading. */ public Heading getExtendedHeading() { return sourceHeading.unionDisjoint(extendedHeading); } /** End extend definition. This is invoked immediately after any extend expressions. */ void endExtendDefinition() { setDeclaredReturnType(new TypeTuple(getExtendedHeading())); // Compile tuple join compileGet(sourceTupleParmName); compileGet(extendTupleParmName); compileInstruction(new OpTupleJoinDisjoint()); compileReturnValue(getDeclaredReturnType()); endOperator(); } } public TypeRelation compileRelationProject(TypeRelation operand, SelectAttributes attributes) { if (attributes.isEverything()) return operand; Heading destination = operand.getHeading().project(attributes); compileInstruction(new OpTupleIteratableProject(new AttributeMap(destination, operand.getHeading()))); return new TypeRelation(destination); } public TypeArray compileArrayProject(TypeArray operand, SelectAttributes attributes) { if (attributes.isEverything()) return operand; Heading destination = operand.getHeading().project(attributes); compileInstruction(new OpTupleIteratableProject(new AttributeMap(destination, operand.getHeading()))); return new TypeArray(destination); } public class Where { private TypeRelation operandType; private AnonymousTupleOperator whereOperator; /** Must precede the boolean expression of a WHERE operator. * * @param operand - TypeRelation of relation operand * @return - Where */ public Where(TypeRelation operand) { operandType = operand; // Define an anonymous operator of the type OPERATOR(TUPLE x) RETURNS BOOLEAN whereOperator = new TupleFilterOperator(new TypeTuple(operand.getHeading()), "WHERE"); } /** Must follow the boolean expression of a WHERE operator. * * @param where - Where * @return - TypeRelation */ public TypeRelation endWhere() { whereOperator.end(); compileInstruction(new OpRelationWhere(whereOperator.getOperator())); return operandType; } } public class With { private OperatorDefinition withWrapper; /** Must precede WITH definition. */ public With() { // Wrap WITH in operator, so as to isolate introduced names to their own scope withWrapper = beginAnonymousOperator(); } /** Must follow WITH item expression. */ public void addWithItem(Type expressionType, String introducedName) { defineConstant(introducedName, expressionType); } /** Must follow WITH definition and final expression. */ public Type endWith(Type expressionType) { setDeclaredReturnType(expressionType); compileReturnValue(expressionType); endOperator(); return compileEvaluate(withWrapper); } } public Type compileTClose(Type expressionType) { if (!(expressionType instanceof TypeRelation)) throw new ExceptionSemantic("RS0070: TCLOSE expected RELATION but got " + expressionType); Heading exprHeading = ((TypeRelation)expressionType).getHeading(); if (exprHeading.getDegree() != 2) throw new ExceptionSemantic("RS0071: TCLOSE expected RELATION of degree 2, but degree is " + exprHeading.getDegree()); if (!exprHeading.getAttributes().get(0).getType().getSignature().equals(exprHeading.getAttributes().get(1).getType().getSignature())) throw new ExceptionSemantic("RS0072: TCLOSE expected both attributes of the RELATION to be the same type, but they aren't."); compileInstruction(new OpRelationTClose()); return expressionType; } private void testLeftCanAcceptRight(Type leftType, Type rightType) { if (!leftType.canAccept(rightType)) throw new ExceptionSemantic("RS0073: " + leftType + " is not compatible with " + rightType); } public TypeRelation compileRelationUnion(TypeRelation leftType, TypeRelation rightType) { testLeftCanAcceptRight(leftType, rightType); compileReformat(leftType, rightType); compileInstruction(new OpRelationUnion()); return leftType; } public TypeRelation compileRelationXunion(TypeRelation leftType, TypeRelation rightType) { testLeftCanAcceptRight(leftType, rightType); compileReformat(leftType, rightType); compileInstruction(new OpRelationXunion()); return leftType; } public TypeRelation compileRelationDUnion(TypeRelation leftType, TypeRelation rightType) { testLeftCanAcceptRight(leftType, rightType); compileReformat(leftType, rightType); compileInstruction(new OpRelationDUnion()); return leftType; } public TypeRelation compileRelationIntersect(TypeRelation leftType, TypeRelation rightType) { testLeftCanAcceptRight(leftType, rightType); compileReformat(leftType, rightType); compileInstruction(new OpRelationIntersect()); return leftType; } public TypeRelation compileRelationMinus(TypeRelation leftType, TypeRelation rightType) { testLeftCanAcceptRight(leftType, rightType); compileReformat(leftType, rightType); compileInstruction(new OpRelationMinus()); return leftType; } public TypeRelation compileRelationIMinus(TypeRelation leftType, TypeRelation rightType) { testLeftCanAcceptRight(leftType, rightType); compileReformat(leftType, rightType); compileInstruction(new OpRelationIMinus()); return leftType; } public TypeRelation compileRelationJoin(TypeRelation leftType, TypeRelation rightType) { Heading left = leftType.getHeading(); Heading right = rightType.getHeading(); Heading intersect = left.intersect(right); Heading result = left.union(right); if (intersect.getDegree() == 0) compileInstruction(new OpRelationProduct()); else compileInstruction(new OpRelationJoin(new JoinMap(result, left, right))); return new TypeRelation(result); } public TypeRelation compileRelationTimes(TypeRelation leftType, TypeRelation rightType) { Heading left = leftType.getHeading(); Heading right = rightType.getHeading(); Heading intersect = left.intersect(right); Heading result = left.union(right); if (intersect.getDegree() == 0) compileInstruction(new OpRelationProduct()); else throw new ExceptionSemantic("RS0074: Attempt to perform TIMES on operands with attributes in common. Perhaps you want to use JOIN or RENAME?"); return new TypeRelation(result); } public TypeRelation compileRelationCompose(TypeRelation leftType, TypeRelation rightType) { Heading intersect = leftType.getHeading().intersect(rightType.getHeading()); if (intersect.getDegree() == 0) { compileInstruction(new OpRelationProduct()); return new TypeRelation(leftType.getHeading().unionDisjoint(rightType.getHeading())); } else { TypeRelation joinType = compileRelationJoin(leftType, rightType); TypeRelation composeType = new TypeRelation(joinType.getHeading().minus(intersect)); compileReformat(composeType, joinType); return composeType; } } public TypeRelation compileRelationSemijoin(TypeRelation leftType, TypeRelation rightType) { compileReformat(leftType, compileRelationJoin(leftType, rightType)); return leftType; } public TypeRelation compileRelationSemiminus(TypeRelation leftType, TypeRelation rightType) { compileDuplicateUnder(); return compileRelationMinus(leftType, compileRelationSemijoin(leftType, rightType)); } /** End TupleIterable extend. */ private Heading endTupleIteratableExtend(Extend extend) { final String sourceParameterName = "%source"; extend.endExtendDefinition(); Heading extendedHeading = extend.sourceHeading.unionDisjoint(extend.extendedHeading); // Define map operator for relational map operation OperatorDefinition mapOp = beginAnonymousOperator(); beginParameterDefinitions(); defineOperatorParameter(sourceParameterName, new TypeTuple(extend.sourceHeading)); endParameterDefinitions(); setDeclaredReturnType(new TypeTuple(extendedHeading)); // Get source tuple compileGet(sourceParameterName); // Initialisation of extend_tuple. compilePush(new ValueTuple(this, new TypeTuple(extend.extendedHeading))); // Invoke anonymous function compileEvaluate(extend.extendOp); compileReturnValue(getDeclaredReturnType()); endOperator(); compileInstruction(new OpTupleIteratableMap(mapOp.getOperator())); return extendedHeading; } /** End relation extend. */ public TypeRelation endRelationExtend(Extend extend) { Heading extendedHeading = endTupleIteratableExtend(extend); return new TypeRelation(extendedHeading); } /** End ARRAY extend. (Used by aggregation.) */ public TypeArray endArrayExtend(Extend extend) { Heading extendedHeading = endTupleIteratableExtend(extend); return new TypeArray(extendedHeading); } public TypeRelation compileRelationWrap(TypeRelation sourceType, SelectAttributes selection, String name) { final String sourceParameterName = "%source"; // Define map operator for relational map operation OperatorDefinition mapOp = beginAnonymousOperator(); beginParameterDefinitions(); defineOperatorParameter(sourceParameterName, new TypeTuple(sourceType.getHeading())); endParameterDefinitions(); setDeclaredReturnType(new TypeTuple(sourceType.getHeading())); // bogus return type, but it's ignored // Get source tuple compileGet(sourceParameterName); TypeTuple tupleType = compileTupleWrap(new TypeTuple(sourceType.getHeading()), selection, name); compileReturnValue(getDeclaredReturnType()); endOperator(); compileInstruction(new OpTupleIteratableMap(mapOp.getOperator())); return new TypeRelation(tupleType.getHeading()); } public TypeRelation compileRelationUnwrap(TypeRelation operand, String attributeName) { final String sourceParameterName = "%source"; // Define map operator for relational map operation OperatorDefinition mapOp = beginAnonymousOperator(); beginParameterDefinitions(); defineOperatorParameter(sourceParameterName, new TypeTuple(operand.getHeading())); endParameterDefinitions(); setDeclaredReturnType(new TypeTuple(operand.getHeading())); // bogus return type, but it's ignored // Get source tuple compileGet(sourceParameterName); TypeTuple tupleType = compileTupleUnwrap(new TypeTuple(operand.getHeading()), attributeName); compileReturnValue(getDeclaredReturnType()); endOperator(); compileInstruction(new OpTupleIteratableMap(mapOp.getOperator())); return new TypeRelation(tupleType.getHeading()); } public TypeRelation compileRelationGroup(TypeRelation sourceType, SelectAttributes itemList, String name) { if (itemList.isAllBut()) itemList.makeNamesExplicit(sourceType.getHeading()); Heading groupedAttributeHeading = new Heading(); Heading sourceHeading = sourceType.getHeading(); Heading sortAttributeHeading = new Heading(sourceHeading); for (String attributeName: itemList.getNames()) { groupedAttributeHeading.add(attributeName, sourceHeading.getAttribute(attributeName).getType()); sortAttributeHeading.remove(attributeName); } Heading resultHeading = new Heading(sortAttributeHeading); resultHeading.add(name, new TypeRelation(groupedAttributeHeading)); compileInstruction(new OpRelationGroup(new AttributeMap(sortAttributeHeading, sourceHeading), new AttributeMap(groupedAttributeHeading, sourceHeading))); return new TypeRelation(resultHeading); } public TypeRelation compileRelationUngroup(TypeRelation sourceType, String attributeName) { Heading sourceHeading = sourceType.getHeading(); Attribute rvAttribute = sourceHeading.getAttribute(attributeName); if (rvAttribute == null) throw new ExceptionSemantic("RS0075: Attribute '" + attributeName + "' not found in " + sourceType); Type rvaType = rvAttribute.getType(); if (!(rvaType instanceof TypeRelation)) throw new ExceptionSemantic("RS0076: Expected attribute '" + attributeName + "' to be a relation, but got " + rvaType); Heading rvaHeading = ((TypeRelation)rvaType).getHeading(); if (sourceHeading.intersect(rvaHeading).getDegree() > 0) throw new ExceptionSemantic("RS0077: Relation-valued attribute '" + attributeName + "' shares attributes with " + sourceType); Heading resultType = sourceHeading.unionDisjoint(rvaHeading); AttributeMap sourceMap = new AttributeMap(sourceHeading, resultType); AttributeMap rvaMap = new AttributeMap(rvaHeading, resultType); int indexofRVA = sourceHeading.getIndexOf(attributeName); int resultDegree = resultType.getDegree(); compileInstruction(new OpRelationUngroup(sourceMap, rvaMap, indexofRVA, resultDegree)); // project out RVA SelectAttributes excludeRVA = new SelectAttributes(); excludeRVA.add(attributeName); excludeRVA.setAllBut(true); sourceType = compileRelationProject(new TypeRelation(resultType), excludeRVA); return sourceType; } public TypeArray compileOrder(TypeHeading sourceType, SelectOrder orderItems) { Heading sourceHeading = sourceType.getHeading(); compileInstruction(new OpTupleIteratableOrder(new OrderMap(sourceHeading, orderItems))); return new TypeArray(sourceHeading); } public TypeRelation compileArrayUnorder(TypeArray sourceType) { Heading sourceHeading = sourceType.getHeading(); compileInstruction(new OpArrayToRelation()); return new TypeRelation(sourceHeading); } public TypeRelation compileRank(TypeRelation sourceRelationType, SelectOrder orderItems, String identifier) { Heading heading = sourceRelationType.getHeading(); if (heading.getIndexOf(identifier) >= 0) throw new ExceptionSemantic("RS0500: " + sourceRelationType + " already has an attribute named " + identifier + "."); heading.add(identifier, TypeInteger.getInstance()); compileInstruction(new OpRelationRank(new OrderMap(heading, orderItems))); return new TypeRelation(heading); } public class RelationSubstitute { private AnonymousTupleOperator updateOp; private TypeRelation sourceType; /** Begin relation substitute. * * This is to be used immediately before an assignment statement, to introduce a tuple's * attributes into the current scope so they can be dereferenced and assigned. * * @param sourceType */ public RelationSubstitute(TypeRelation sourceType) { this.sourceType = sourceType; // Define anonymous function that accepts a tuple as an argument and returns a tuple updateOp = new TupleMapOperator(new TypeTuple(sourceType.getHeading()), "Relation UPDATE"); } /** * End relation update. * * This is to be used immediately after an assignment statement. */ public TypeRelation endRelationSubstitute() { updateOp.end(); compileInstruction(new OpTupleIteratableMap(updateOp.getOperator())); compileInstruction(new OpRelationDupRemove()); return sourceType; } } /** Compile TUPLE FROM relation */ public TypeTuple compileRelationGetTuple(TypeRelation relationExpression) { compileInstruction(new OpRelationGetTuple()); return new TypeTuple(relationExpression.getHeading()); } public class TupleSubstitute { private AnonymousTupleOperator updateOp; /** Begin tuple substitute. * * This is to be used immediately before an assignment statement, to introduce a tuple's * attributes into the current scope so they can be dereferenced and assigned. * * The ValueTuple on the stack will be subject to a non-destructive update via the assignment * statement. A new ValueTuple of the same TypeTuple will be returned on the stack. * * @param sourceType */ public TupleSubstitute(TypeTuple sourceType) { // Define anonymous function that accepts a tuple as an argument and returns a tuple updateOp = new TupleMapOperator(sourceType, "Tuple UPDATE"); } /** * End tuple substitute. * * This is to be used immediately after an assignment statement. */ public TypeTuple endTupleSubstitute() { updateOp.end(); // Invoke anonymous function return (TypeTuple)compileEvaluate(updateOp.getOperatorDefinition()); } } /** Compile FROM TUPLE. Return attribute type. */ public Type compileTupleGetAttribute(TypeTuple sourceType, String attributeName) { Heading source = sourceType.getHeading(); int index = source.getIndexOf(attributeName); if (index == -1) throw new ExceptionSemantic("RS0078: Attribute '" + attributeName + "' not found."); compileInstruction(new OpTupleGetAttribute(index)); return source.getAttributes().get(index).getType(); } /** Compile tuple unwrap. Return new TypeTuple. */ public TypeTuple compileTupleUnwrap(TypeTuple sourceType, String attributeName) { Attribute tupleAttribute = sourceType.getHeading().getAttribute(attributeName); if (tupleAttribute == null) throw new ExceptionSemantic("RS0079: Attribute '" + attributeName + "' not in " + sourceType); if (!(tupleAttribute.getType() instanceof TypeTuple)) throw new ExceptionSemantic("RS0080: Expected attribute '" + tupleAttribute.getName() + "' to be a TUPLE but it is a " + tupleAttribute.getType()); Heading unwrappingAttributeHeading = new Heading(); unwrappingAttributeHeading.add(tupleAttribute.getName(), tupleAttribute.getType()); TypeTuple preservedAttributes = new TypeTuple(sourceType.getHeading().minus(unwrappingAttributeHeading)); compileDuplicate(); compileReformat(preservedAttributes, sourceType); compileSwap(); compileInstruction(new OpTupleGetAttribute(sourceType.getHeading().getIndexOf(tupleAttribute.getName()))); sourceType = compileTupleJoin(preservedAttributes, (TypeTuple)tupleAttribute.getType()); return sourceType; } /** Compile tuple wrap. Return new TypeTuple. */ public TypeTuple compileTupleWrap(TypeTuple sourceType, SelectAttributes selection, String name) { // Create a tuple type consisting of the attributes being wrapped by new attribute TypeTuple wrappingAttributes = new TypeTuple(sourceType.getHeading().project(selection)); // Create an attribute selection to exclude the wrapped attributes SelectAttributes wrappedAttributeRemover = new SelectAttributes(); wrappedAttributeRemover.setAllBut(true); wrappedAttributeRemover.add(wrappingAttributes.getHeading().getAttributes()); // Push a duplicate of the operand onto the stack compileDuplicate(); // Project the original operand without wrapped attributes TypeTuple wrappedAttributesRemoved = compileTupleProject(sourceType, wrappedAttributeRemover); // Put the original operand on top of the stack compileSwap(); // Project the original operand to only have the wrapped attributes compileReformat(wrappingAttributes, sourceType); // Create a new tuple to host the wrapped attributes Generator.TupleDefinition extendTuple = new TupleDefinition(); extendTuple.setTupleAttribute(name, wrappingAttributes); TypeTuple wrappedAttribute = extendTuple.endTuple(); // Extend the operand without wrapped attributes to include the wrapping tuple return compileTupleJoin(wrappedAttributesRemoved, wrappedAttribute); } /** Compile tuple d_union. Return new TypeTuple. */ public TypeTuple compileTupleDUnion(TypeTuple leftType, TypeTuple rightType) { Heading left = leftType.getHeading(); Heading right = rightType.getHeading(); if (left.intersect(right).getDegree() == 0) { compileInstruction(new OpTupleJoinDisjoint()); return new TypeTuple(left.unionDisjoint(right)); } else throw new ExceptionSemantic("RS0081: Attempt to perform disjoint union on tuple types that have attributes in common."); } /** Compile tuple semijoin. Return new TypeTuple. */ public TypeTuple compileTupleSemijoin(TypeTuple leftType, TypeTuple rightType) { return compileTupleIntersect(leftType, rightType); } /** Compile tuple semiminus. Return new TypeTuple. */ public TypeTuple compileTupleSemiminus(TypeTuple leftType, TypeTuple rightType) { compileDuplicateUnder(); return compileTupleMinus(leftType, compileTupleSemijoin(leftType, rightType)); } /** Compile tuple compose. Return new TypeTuple. */ public TypeTuple compileTupleCompose(TypeTuple leftType, TypeTuple rightType) { Heading intersect = leftType.getHeading().intersect(rightType.getHeading()); if (intersect.getDegree() == 0) { compileInstruction(new OpTupleJoinDisjoint()); return new TypeTuple(leftType.getHeading().unionDisjoint(rightType.getHeading())); } else { TypeTuple joinType = compileTupleJoin(leftType, rightType); TypeTuple composeType = new TypeTuple(joinType.getHeading().minus(intersect)); compileReformat(composeType, joinType); return composeType; } } /** Compile tuple minus. Return new TypeTuple. */ public TypeTuple compileTupleMinus(TypeTuple leftType, TypeTuple rightType) { Heading left = leftType.getHeading(); Heading right = rightType.getHeading(); Heading intersect = left.intersect(right); if (intersect.getDegree() == 0) { compilePop(); return leftType; } else { TypeTuple joinType = compileTupleJoin(leftType, rightType); TypeTuple minusType = new TypeTuple(left.minus(right)); compileReformat(minusType, joinType); return minusType; } } public TypeTuple compileTupleIMinus(TypeTuple leftType, TypeTuple rightType) { Heading left = leftType.getHeading(); Heading right = rightType.getHeading(); Heading intersect = left.intersect(right); if (intersect.getDegree() != right.getDegree()) { throw new ExceptionSemantic("RS0082: In I_MINUS, the right operand must be a subset of the left operand."); } else { TypeTuple joinType = compileTupleJoin(leftType, rightType); TypeTuple minusType = new TypeTuple(left.minus(right)); compileReformat(minusType, joinType); return minusType; } } /** Compile tuple intersect. Return new TypeTuple. */ public TypeTuple compileTupleIntersect(TypeTuple leftType, TypeTuple rightType) { TypeTuple intersectType = new TypeTuple(leftType.getHeading().intersect(rightType.getHeading())); if (intersectType.getHeading().getDegree() > 0) { TypeTuple joinType = compileTupleJoin(leftType, rightType); compileReformat(intersectType, joinType); } else { compilePop(); compilePop(); compilePush(intersectType.getDefaultValue(this)); } return intersectType; } /** End tuple extend. This is invoked immediately after any extend expressions. */ public TypeTuple endTupleExtend(Extend extend) { extend.endExtendDefinition(); // Initialisation of extend_tuple. compilePush(new ValueTuple(this, new TypeTuple(extend.extendedHeading))); // Invoke anonymous function return (TypeTuple)compileEvaluate(extend.extendOp); } /** Compile tuple join. Return new TypeTuple. */ public TypeTuple compileTupleJoin(TypeTuple leftType, TypeTuple rightType) { Heading destination; Heading left = leftType.getHeading(); Heading right = rightType.getHeading(); if (left.intersect(right).getDegree() > 0) { destination = left.union(right); compileInstruction(new OpTupleJoin(new JoinMap(destination, left, right))); } else { compileInstruction(new OpTupleJoinDisjoint()); destination = left.unionDisjoint(right); } return new TypeTuple(destination); } /** Compile tuple project. Return new TypeTuple. */ public TypeTuple compileTupleProject(TypeTuple sourceType, SelectAttributes attributes) { if (attributes.isEverything()) return sourceType; Heading destination = sourceType.getHeading().project(attributes); compileInstruction(new OpTupleProject(new AttributeMap(destination, sourceType.getHeading()))); return new TypeTuple(destination); } private void operatorDefinition(String fnname) { int startLine = 0; if (parser != null) { BaseASTNode node = parser.getCurrentNode(); if (node != null) { Token token = node.first_token; if (token != null) startLine = token.beginLine; } } currentOperatorDefinition = new OperatorDefinitionRel(startLine, fnname, currentOperatorDefinition); } public void addOperator(OperatorDefinition op) { parser.addOperator(op); } /** Create an anonymous operator. */ public OperatorDefinition beginAnonymousOperator() { operatorDefinition(null); // make sure it's "special", so we don't really see it currentOperatorDefinition.setSpecial(true); return currentOperatorDefinition; } public void beginOperator(String fnname) { operatorDefinition(fnname); } public OperatorDefinition getCurrentOperatorDefinition() { return currentOperatorDefinition; } public String getOperatorDefinitionLineReferenceStack(int lineNumber) { StringBuffer out = new StringBuffer(); OperatorDefinition current = currentOperatorDefinition; while (true) { if (!current.isSpecial()) out.append("\tIn " + current.getSignature().toString() + " line " + (lineNumber - current.getStartLine() + 1)); current = current.getParentOperatorDefinition(); if (current == null) break; out.append('\n'); } return out.toString(); } public OperatorSignature getCurrentDefinitionSignature() { return currentOperatorDefinition.getSignature(); } public void setDeclaredReturnType(Type type) { currentOperatorDefinition.setDeclaredReturnType(type); } public Type getDeclaredReturnType() { return currentOperatorDefinition.getDeclaredReturnType(); } public void compileReturnValue(Type returnType) { if (!currentOperatorDefinition.hasReturnDeclaration()) throw new ExceptionSemantic("RS0083: Operator " + currentOperatorDefinition.getSignature() + " has not declared a return type but has defined a return expression."); if (returnType instanceof TypeOperator) compileInstruction(new OpPreserveContextInValueOperator()); compileInstruction(new OpReturnValue()); currentOperatorDefinition.setDefinedReturnValue(true); } public void compileReturn() { if (currentOperatorDefinition.hasReturnDeclaration()) throw new ExceptionSemantic("RS0084: Operator " + currentOperatorDefinition.getSignature() + " has declared a return type, but the RETURN statement is missing an expression of that type."); compileInstruction(new OpReturn()); } public void beginParameterDefinitions() { // This is a no-op, but here in case needed in the future. } public void defineOperatorParameter(String name, Type type) { currentOperatorDefinition.defineParameter(name, type); } public void endParameterDefinitions() { currentOperatorDefinition.getParentOperatorDefinition().defineOperator(currentOperatorDefinition); } public void endOperator() { if (currentOperatorDefinition.hasReturnDeclaration()) { if (!currentOperatorDefinition.hasDefinedReturnValue()) throw new ExceptionSemantic("RS0085: Operator " + currentOperatorDefinition.getSignature() + " has declared a return type, but contains no RETURN statement of that type."); compilePush(currentOperatorDefinition.getDeclaredReturnType().getDefaultValue(this)); compileInstruction(new OpReturnValue()); } else compileInstruction(new OpReturn()); // Done. Restore previous definition context. currentOperatorDefinition = currentOperatorDefinition.getParentOperatorDefinition(); } /** Return true if current operator definition is top level, and should therefore be persistent. */ public boolean isTopLevelOperator(OperatorDefinition definition) { return (definition.getDepth() == interactiveOperatorNestingDepth + 1); } /** Persist the given operator definition. */ public void persistOperator(OperatorDefinition operator) { if (database.isOperatorExists(operator.getSignature())) throw new ExceptionSemantic("RS0086: OPERATOR " + operator.getSignature() + " is already in the database."); if (operator.getOwner() == null || operator.getOwner().isEmpty()) operator.setOwner(userRelvarOwner); beginAssignment(); compileInstruction(new OpCreateOperator(operator)); endAssignment(); } public void dropOperator(OperatorSignature signature) { if (!database.isOperatorExists(signature)) throw new ExceptionSemantic("RS0087: OPERATOR " + signature + " does not exist."); String operatorGenerationTypeName = database.getOperatorGenerationTypeName(signature); if (operatorGenerationTypeName != null && operatorGenerationTypeName.length() > 0) throw new ExceptionSemantic("RS0088: OPERATOR " + signature + " was generated by TYPE " + operatorGenerationTypeName + " and may not be dropped directly."); beginAssignment(); compileInstruction(new OpDropOperator(signature)); endAssignment(); } public class IfStatement { private int resolveThisForwardBranch; private boolean conditional; public IfStatement() { // if the expression returns false, branch past the if(true) block resolveThisForwardBranch = currentOperatorDefinition.getCP(); conditional = true; compileInstruction(new OpBranchIfFalse(0)); // needs to be resolved } public void beginElse() { // if we executed the if(true) block, we need to jump past the else block int pendingForwardBranch = resolveThisForwardBranch; resolveThisForwardBranch = currentOperatorDefinition.getCP(); conditional = false; compileInstruction(new OpJump(0)); // needs to be resolved // resolve the pending forward branch from the expression test // if the test returns false, we need to branch to this point compileInstructionAt(new OpBranchIfFalse(currentOperatorDefinition.getCP()), pendingForwardBranch); } public void endIf() { // resolve the pending forward branch if (conditional) compileInstructionAt(new OpBranchIfFalse(currentOperatorDefinition.getCP()), resolveThisForwardBranch); else compileInstructionAt(new OpJump(currentOperatorDefinition.getCP()), resolveThisForwardBranch); } } public class DoLoop { private int head; private int resolveThisForwardBranch; public DoLoop() { head = currentOperatorDefinition.getCP(); } public void testDo() { // compile branch out of loop if loop test returns false resolveThisForwardBranch = currentOperatorDefinition.getCP(); compileInstruction(new OpBranchIfFalse(0)); } public void endDo() { // compile jump back to top of loop compileInstruction(new OpJump(head)); // resolve unresolved branch out of loop compileInstructionAt(new OpBranchIfFalse(currentOperatorDefinition.getCP()), resolveThisForwardBranch); } } // Anonymous operator of the form OPERATOR(TUPLE x) private class TupleProcessOperator extends AnonymousTupleOperator { private String contextDescription; TupleProcessOperator(TypeTuple source, String contextDescription) { super(source, null); this.contextDescription = contextDescription; } public void compileSet(Generator generator, Attribute attribute, int slotDepth, int operatorDepth, int offset) { throw new ExceptionFatal("RS0302: Attempt to set attribute '" + attribute.getName() + "' in " + contextDescription + "."); } } public class ForLoop { private TupleProcessOperator tupleOperator; public void beginForLoop(TypeArray array) { tupleOperator = new TupleProcessOperator((TypeTuple)array.getElementType(), "FOR"); } public void endForLoop() { tupleOperator.end(); compileInstruction(new OpArrayFor(tupleOperator.getOperator())); } } public Type compileGet(Slot slot) { slot.compileGet(this); return slot.getType(); } public Type compileGet(String refname) { return compileGet(findReference(refname)); } public Type compileSet(Slot slot) { slot.compileSet(this); return slot.getType(); } public Type compileSet(String refname) { return compileSet(findReference(refname)); } public void compileVariableInitialise(Slot slot) { slot.compileInitialise(this); } public void compileVariableInitialise(String refname) { compileVariableInitialise(findReference(refname)); } public void compileArrayGet() { compileInstruction(new OpArrayGet()); } public void compileArraySet() { compileInstruction(new OpArraySet()); } public void compileArrayAppend() { compileInstruction(new OpArrayAppend()); } // EXACTLY (n-adic) public void compileExactly(int countOfBooleanExpressions) { compileInstruction(new OpExactly(countOfBooleanExpressions)); } // AVG (n-adic) public void compileNadicAverage(int countOfExpressions) { compileInstruction(new OpAverage(countOfExpressions)); } public Type compileOperatorInvocation(String opName, Type parmType, Type returnType) { OperatorSignature signature = new OperatorSignature(opName); signature.addParameterType(parmType); signature.setReturnType(returnType); compileEvaluate(signature); return returnType; } public Type compileOperatorInvocation(String opName, Type leftParmType, Type rightParmType, Type returnType) { OperatorSignature signature = new OperatorSignature(opName); signature.addParameterType(leftParmType); signature.addParameterType(rightParmType); signature.setReturnType(returnType); compileEvaluate(signature); return returnType; } private Type compileComparisonOperatorInvocation(String opName, Type leftType, Type rightType, Type returnType) { if (!leftType.canAccept(rightType)) throw new ExceptionSemantic("RS0388: " + leftType + " is not the same type as " + rightType + "."); compileReformat(leftType, rightType); return compileOperatorInvocation(opName, leftType, rightType, returnType); } // = public Type compileEQ(Type leftType, Type rightType) { return compileComparisonOperatorInvocation(BuiltinTypeBuilder.EQUALS, leftType, rightType, TypeBoolean.getInstance()); } // != public Type compileNEQ(Type leftType, Type rightType) { return compileComparisonOperatorInvocation(BuiltinTypeBuilder.NOTEQUALS, leftType, rightType, TypeBoolean.getInstance()); } private void checkNotSupportedByTuple(Type leftType, Type rightType) { if (leftType instanceof TypeTuple || rightType instanceof TypeTuple) throw new ExceptionSemantic("RS0089: Operator not supported by TUPLE."); } // >= public Type compileGTE(Type leftType, Type rightType) { checkNotSupportedByTuple(leftType, rightType); return compileComparisonOperatorInvocation(BuiltinTypeBuilder.GREATERTHANOREQUALS, leftType, rightType, TypeBoolean.getInstance()); } // <= public Type compileLTE(Type leftType, Type rightType) { checkNotSupportedByTuple(leftType, rightType); return compileComparisonOperatorInvocation(BuiltinTypeBuilder.LESSTHANOREQUALS, leftType, rightType, TypeBoolean.getInstance()); } // > public Type compileGT(Type leftType, Type rightType) { checkNotSupportedByTuple(leftType, rightType); return compileComparisonOperatorInvocation(BuiltinTypeBuilder.GREATERTHAN, leftType, rightType, TypeBoolean.getInstance()); } // < public Type compileLT(Type leftType, Type rightType) { checkNotSupportedByTuple(leftType, rightType); return compileComparisonOperatorInvocation(BuiltinTypeBuilder.LESSTHAN, leftType, rightType, TypeBoolean.getInstance()); } public Object compileSubset(Type leftType, Type rightType) { checkNotSupportedByTuple(leftType, rightType); return compileComparisonOperatorInvocation(BuiltinTypeBuilder.SUBSET, leftType, rightType, TypeBoolean.getInstance()); } public Object compileSubsetOrEqual(Type leftType, Type rightType) { checkNotSupportedByTuple(leftType, rightType); return compileComparisonOperatorInvocation(BuiltinTypeBuilder.SUBSETOREQUAL, leftType, rightType, TypeBoolean.getInstance()); } public Object compileSuperset(Type leftType, Type rightType) { checkNotSupportedByTuple(leftType, rightType); return compileComparisonOperatorInvocation(BuiltinTypeBuilder.SUPERSET, leftType, rightType, TypeBoolean.getInstance()); } public Object compileSupersetOrEqual(Type leftType, Type rightType) { checkNotSupportedByTuple(leftType, rightType); return compileComparisonOperatorInvocation(BuiltinTypeBuilder.SUPERSETOREQUAL, leftType, rightType, TypeBoolean.getInstance()); } // IN public TypeBoolean compileTupleIn(TypeTuple leftType, TypeRelation rightType) { compileSwap(); if (leftType.requiresReformatOf(new TypeRelation(rightType.getHeading()))) compileInstruction(new OpTupleProject(new AttributeMap(rightType.getHeading(), leftType.getHeading()))); compileInstruction(new OpTupleInRelation()); return TypeBoolean.getInstance(); } // || public void compileConcatenate() { compileInstruction(new OpConcatenate()); } // + public void compilePlus() { compileInstruction(new OpAdd()); } // Pop value from stack public void compilePop() { compileInstruction(new OpPop()); } // Duplicate topmost value on stack public void compileDuplicate() { compileInstruction(new OpDuplicate()); } // Duplicate value under topmost on stack. Topmost is unchanged. public void compileDuplicateUnder() { compileInstruction(new OpDuplicateUnder()); } // Swap top two values on stack public void compileSwap() { compileInstruction(new OpSwap()); } // Push literal Value to stack public void compilePush(Value v) { compileInstruction(new OpPushLiteral(v)); } public class RelationDefinition { private Heading heading; private boolean isHeadingExplicit = false; // Begin relation literal. If heading is null, first tuple added will define the type. public RelationDefinition(Heading heading) { this.heading = heading; if (heading != null) isHeadingExplicit = true; compileInstruction(new OpRelationPushLiteral()); } // Add a tuple to the relation definition public void addTupleToRelation(TypeTuple tupleType) { Heading tupleHeading = tupleType.getHeading(); if (heading == null) heading = tupleHeading; else if (!heading.canAccept(tupleHeading)) if (!isHeadingExplicit && tupleHeading.canAccept(heading)) heading = tupleHeading; else heading = heading.getMostSpecificCommonSupertype(tupleHeading); compileReformat(new TypeTuple(heading), tupleType); compileInstruction(new OpRelationLiteralInsertTuple()); } // End relation literal public TypeRelation endRelation() { return new TypeRelation(heading); } } public class TupleDefinition { private Heading heading; private boolean wildcard = false; // Begin tuple literal public TupleDefinition() { heading = new Heading(); } // Define a tuple attribute. At run-time, the value topmost on the stack will be // assigned to this attribute. public void setTupleAttribute(String name, Type type) { heading.add(name, type); } // End tuple literal. Return the tuple's type. public TypeTuple endTuple() { if (wildcard) { OperatorDefinition currentOperator = getCurrentOperatorDefinition(); while (currentOperator != null) { OperatorSignature currentOperatorSignature = currentOperator.getSignature(); int parmCount = currentOperatorSignature.getParmCount(); for (int parm = 0; parm < parmCount; parm++) { String name = currentOperatorSignature.getParameterName(parm); // Are we in an open expression context? If so, a parm "%tuple<n>" or "%source_tuple<n>" will exist. // Let TUP {*} refer to it. if (name.startsWith("%tuple") || name.startsWith("%source_tuple")) return (TypeTuple)compileGet(name); } currentOperator = currentOperator.getParentOperatorDefinition(); } } compileInstruction(new OpTuplePushLiteral(heading.getDegree())); return new TypeTuple(heading); } // Identify this as a "wildcard" tuple, i.e., TUPLE {*}, which should close over its scope public void setWildcard() { wildcard = true; } } // Push delimited string literal to stack public void compilePushDelimitedString(String value) { compilePush(ValueCharacter.stripDelimited(Generator.this, value)); } // Push string literal to stack public void compilePush(String value) { compilePush(ValueCharacter.select(this, value)); } // Push double literal to stack public void compilePush(double value) { compilePush(ValueRational.select(this, value)); } // Push integer literal to stack public void compilePush(long value) { compilePush(ValueInteger.select(this, value)); } // Push boolean literal to stack public void compilePush(boolean flag) { compilePush(ValueBoolean.select(this, flag)); } // Run-time announcement public void announce(String s) { printStream.println(ValueCharacter.stripDelimitedString(s)); } // Compile: write value on top of stack to output stream. public void compileWrite(Type type) { compileInstruction(new OpWrite(type)); } // Compile: write value on top of stack to output stream, followed by newline. public void compileWriteln(Type type) { compileInstruction(new OpWriteln(type)); } // Compile: write newline to output stream. public void compileWritelnNoExpression() { compileInstruction(new OpWritelnNoExpression()); } // Compile: write value on top of stack to output stream in parsable format. public void compileOutput(Type type) { compileInstruction(new OpOutput(type)); } // Compile: EXECUTE value on top of stack. public void compileExecute() { compileInstruction(new OpExecute()); } // SET <attribute> <value> public void set(String attribute, String value) { EnvironmentSettings.set(this, attribute, value); } // Compile: make a backup public void backup() { compileInstruction(new OpBackup()); } // Compile: write value on top of stack to console. For debugging purposes. public void compileWrite() { compileInstruction(new OpWriteRaw()); } // Compile: write relation on top of stack to console. For debugging purposes. public void compileWriteRelation() { compileInstruction(new OpRelationWrite()); } }