diff --git a/build.gradle b/build.gradle index 78f7505..c1c60e2 100644 --- a/build.gradle +++ b/build.gradle @@ -102,7 +102,7 @@ checkstyle { configFile = new File(rootDir, "checkstyle.xml") ignoreFailures = false showViolations = true - toolVersion = "5.9" + toolVersion = "6.18" } antlr4 { diff --git a/src/main/antlr4/Manifold.g4 b/src/main/antlr4/Manifold.g4 index ff9a8ff..f1f4594 100644 --- a/src/main/antlr4/Manifold.g4 +++ b/src/main/antlr4/Manifold.g4 @@ -17,6 +17,9 @@ LINE_COMMENT: '//' ~[\r\n]* -> skip; INTEGER_VALUE: [0-9]+; BOOLEAN_VALUE: 'false' | 'true'; TYPE_KEYWORD: 'Type'; +STRING_VALUE: '"' ( '\"' | ~["] )*? '"'; + +VISIBILITY_PUBLIC: 'public'; tupleTypeValueEntry: (IDENTIFIER ':')? typevalue ('=' expression)?; tupleTypeValue: '(' tupleTypeValueEntry (',' tupleTypeValueEntry)* ')'; @@ -27,7 +30,7 @@ tupleValue: '(' ')'; functionTypeValue: tupleTypeValue '->' tupleTypeValue; -functionValue: functionTypeValue '{' (expression EXPRESSION_TERMINATOR)* '}'; +functionValue: functionTypeValue '{' (expression STATEMENT_TERMINATOR)* '}'; //////////////////////////////////////////////////////// // // @@ -76,8 +79,8 @@ rvalue: | INTEGER_VALUE # Integer | functionValue # Function | reference rvalue # FunctionInvocationExpression // TODO: function invocation needs to be 'reference arglist' - | reference # RValueExpression - | lvalue '=' rvalue # AssignmentExpression + | reference # ReferenceExpression + | VISIBILITY_PUBLIC? lvalue '=' rvalue # AssignmentExpression | 'primitive' 'port' typevalue (':' tupleTypeValue)? # PrimitivePortDefinitionExpression | 'primitive' 'node' functionTypeValue # PrimitiveNodeDefinitionExpression ; @@ -89,9 +92,18 @@ lvalue: ; // TODO: declarations as expressions -expression: /* declaration | */ rvalue; +expression: + rvalue + // | declaration + ; + +statement: + expression #ExpressionStatment + | 'import' STRING_VALUE #ImportStatement + ; + +STATEMENT_TERMINATOR: ';'; -EXPRESSION_TERMINATOR: ';'; //////////////////////////////////////////////////////// // // @@ -99,4 +111,4 @@ EXPRESSION_TERMINATOR: ';'; // // //////////////////////////////////////////////////////// -schematic: (expression EXPRESSION_TERMINATOR)*; +schematic: (statement STATEMENT_TERMINATOR)*; diff --git a/src/main/java/org/manifold/compiler/front/ExpressionContextVisitor.java b/src/main/java/org/manifold/compiler/front/ExpressionContextVisitor.java new file mode 100644 index 0000000..fb5c782 --- /dev/null +++ b/src/main/java/org/manifold/compiler/front/ExpressionContextVisitor.java @@ -0,0 +1,394 @@ +package org.manifold.compiler.front; + +import com.google.common.base.Throwables; +import org.antlr.v4.runtime.ParserRuleContext; +import org.antlr.v4.runtime.Token; +import org.antlr.v4.runtime.misc.NotNull; +import org.antlr.v4.runtime.misc.ParseCancellationException; +import org.antlr.v4.runtime.tree.TerminalNode; +import org.manifold.compiler.BooleanValue; +import org.manifold.compiler.IntegerValue; +import org.manifold.compiler.NilTypeValue; +import org.manifold.compiler.UndefinedBehaviourError; +import org.manifold.parser.ManifoldBaseVisitor; +import org.manifold.parser.ManifoldLexer; +import org.manifold.parser.ManifoldParser.*; + +import java.util.Arrays; +import java.util.LinkedList; +import java.util.List; + +class ExpressionContextVisitor extends ManifoldBaseVisitor { + + private ExpressionGraph exprGraph; + public ExpressionGraph getExpressionGraph() { + return this.exprGraph; + } + + // Used to distinguish between unpacking and defining tuples + private boolean isLHS = true; + + // Should tuples and variables be exported + private boolean isPublic = false; + + private int nextTmpVar = 0; + public ExpressionContextVisitor() { + this(new ExpressionGraph()); + } + + public ExpressionContextVisitor(ExpressionGraph exprGraph) { + this.exprGraph = exprGraph; + } + + @Override + public ExpressionVertex visitAssignmentExpression( + AssignmentExpressionContext context) { + + // get the vertex corresponding to the lvalue + isLHS = true; + isPublic = context.VISIBILITY_PUBLIC() != null; + ExpressionVertex vLeft = context.lvalue().accept(this); + + // then get the rvalue... + isLHS = false; + isPublic = false; + ExpressionVertex vRight = context.rvalue().accept(this); + + ExpressionEdge e = new ExpressionEdge(vRight, vLeft); + exprGraph.addEdge(e); + return vRight; + } + + @Override + public ExpressionVertex visitFunctionInvocationExpression( + FunctionInvocationExpressionContext context) { + // get the vertex corresponding to the function being called + ExpressionVertex vFunction = context.reference().accept(this); + ExpressionEdge eFunction = new ExpressionEdge(vFunction, null); + // then get the input vertex + ExpressionVertex vInput = context.rvalue().accept(this); + ExpressionEdge eInput = new ExpressionEdge(vInput, null); + + FunctionInvocationVertex vInvocation = new FunctionInvocationVertex( + exprGraph, eFunction, eInput); + exprGraph.addVertex(vInvocation); + exprGraph.addEdge(eFunction); + exprGraph.addEdge(eInput); + return vInvocation; + } + + @Override + public ExpressionVertex visitFunctionValue( + FunctionValueContext ctx) { + ExpressionContextVisitor functionGraphBuilder = + new ExpressionContextVisitor(); + ctx.expression().forEach(functionGraphBuilder::visit); + ExpressionGraph fSubGraph = functionGraphBuilder.getExpressionGraph(); + + ExpressionVertex fTypeVertex = visitFunctionTypeValue( + ctx.functionTypeValue()); + ExpressionEdge fTypeEdge = new ExpressionEdge(fTypeVertex, null); + + FunctionValueVertex fValueVertex = new FunctionValueVertex( + exprGraph, fTypeEdge, fSubGraph); + exprGraph.addVertex(fValueVertex); + exprGraph.addEdge(fTypeEdge); + + return fValueVertex; + } + + // KEY INSIGHT: combine the port type/port attributes and + // node attributes in a single FunctionTypeValue signature. + // As an example, if we have port types xIn(a: Int) and xOut(b: Int) + // and want a node type xDev whose attributes are p,q: Bool, + // input port u: xIn, output port v: xOut, we can declare it like + // + // xDev = primitive node (u: xIn, p: Bool, q: Bool) -> (v: xOut); + // + // and instantiate it like + // + // vResult = xDev(u: (0: uVal, 1: (a: 3)), p: True, q: False, v: (b: 4)) + // + @Override + public ExpressionVertex visitPrimitiveNodeDefinitionExpression( + PrimitiveNodeDefinitionExpressionContext context) { + ExpressionVertex vSignature = context.functionTypeValue().accept(this); + ExpressionEdge eSignature = new ExpressionEdge(vSignature, null); + exprGraph.addEdge(eSignature); + PrimitiveNodeVertex vNode = new PrimitiveNodeVertex(exprGraph, eSignature); + exprGraph.addVertex(vNode); + return vNode; + } + + @Override + public ExpressionVertex visitPrimitivePortDefinitionExpression( + PrimitivePortDefinitionExpressionContext context) { + + ExpressionVertex vSignalType = context.typevalue().accept(this); + ExpressionEdge eSignalType = new ExpressionEdge(vSignalType, null); + exprGraph.addEdge(eSignalType); + + ExpressionVertex vAttributes; + if (context.tupleTypeValue() != null) { + vAttributes = context.tupleTypeValue().accept(this); + } else { + vAttributes = new ConstantValueVertex(exprGraph, + NilTypeValue.getInstance()); + } + exprGraph.addVertex(vAttributes); + ExpressionEdge eAttributes = new ExpressionEdge(vAttributes, null); + exprGraph.addEdge(eAttributes); + PrimitivePortVertex vPort = new PrimitivePortVertex(exprGraph, + eSignalType, eAttributes); + exprGraph.addVertex(vPort); + return vPort; + } + + @Override + public ExpressionVertex visitTupleTypeValue(TupleTypeValueContext context) { + List entries = context.tupleTypeValueEntry(); + MappedArray typeValueEdges = new MappedArray<>(); + MappedArray defaultValueEdges = new MappedArray<>(); + Integer nextAnonymousID = 0; + for (TupleTypeValueEntryContext entryCtx : entries) { + // each child has a typevalue, and may have + // an identifier (named field) + // and an expression (default value) + String identifier; + if (entryCtx.IDENTIFIER() != null) { + identifier = entryCtx.IDENTIFIER().getText(); + } else { + identifier = nextAnonymousID.toString(); + nextAnonymousID += 1; + } + ExpressionVertex vxTypeValue = entryCtx.typevalue().accept(this); + ExpressionEdge eTypeValue = new ExpressionEdge(vxTypeValue, null); + typeValueEdges.put(identifier, eTypeValue); + exprGraph.addEdge(eTypeValue); + if (entryCtx.expression() != null) { + ExpressionVertex vxDefaultValue = entryCtx.expression().accept(this); + ExpressionEdge eDefaultValue = new ExpressionEdge(vxDefaultValue, null); + defaultValueEdges.put(identifier, eDefaultValue); + exprGraph.addEdge(eDefaultValue); + } + } + TupleTypeValueVertex vTuple = new TupleTypeValueVertex(exprGraph, + typeValueEdges, defaultValueEdges); + exprGraph.addVertex(vTuple); + return vTuple; + } + + @Override + public ExpressionVertex visitTupleValue(TupleValueContext context) { + List entries = context.tupleValueEntry(); + + // Desugar tuple unpacking to assignment to a temporary variable, then accessing the attributes of that variable + if (isLHS) { + return unpackTuple(entries); + } + + Integer nextAnonymousID = 0; + MappedArray valueEdges = new MappedArray<>(); + for (TupleValueEntryContext entryCtx : entries) { + // each child has a value, and may have an identifier (named field) + ExpressionVertex vxValue = entryCtx.expression().accept(this); + String identifier; + if (entryCtx.IDENTIFIER() != null) { + identifier = entryCtx.IDENTIFIER().getText(); + } else { + // TODO verify this against the specification + identifier = nextAnonymousID.toString(); + nextAnonymousID += 1; + } + ExpressionEdge eValue = new ExpressionEdge(vxValue, null); + valueEdges.put(identifier, eValue); + exprGraph.addEdge(eValue); + } + TupleValueVertex vTuple = new TupleValueVertex(exprGraph, valueEdges); + exprGraph.addVertex(vTuple); + return vTuple; + } + + private ExpressionVertex unpackTuple(List entries) { + VariableIdentifier tmpTupleId = new VariableIdentifier(Arrays.asList("tmp_" + nextTmpVar)); + nextTmpVar += 1; + ExpressionVertex tmpTuple = createVariableVertex(tmpTupleId); + + int entryNum = 0; + boolean containsNamedEntry = false; + for (TupleValueEntryContext entryCtx : entries) { + ExpressionVertex entryVertex = entryCtx.expression().accept(this); + + if (!(entryVertex instanceof VariableReferenceVertex)) { + String err = createLineError(entryCtx, "Unpacking target must be a variable"); + throw new FrontendBuildException(err); + } + + ExpressionVertex attrVertex; + TerminalNode entryId = entryCtx.IDENTIFIER(); + if (entryId != null) { + + attrVertex = createStaticAttributeAccessExpression(entryId.getText(), tmpTuple); + containsNamedEntry = true; + + } else if (containsNamedEntry) { + + String err = createLineError(entryCtx, "Index-based entries must be unpacked before namespaced identifiers"); + throw new FrontendBuildException(err); + + } else { + attrVertex = createStaticAttributeAccessExpression(entryNum, tmpTuple); + } + exprGraph.addEdge(new ExpressionEdge(attrVertex, entryVertex)); + + entryNum += 1; + } + return tmpTuple; + } + + @Override + public ExpressionVertex visitFunctionTypeValue( + FunctionTypeValueContext context) { + // get the vertex corresponding to the input type + ExpressionVertex vIn = context.tupleTypeValue(0).accept(this); + ExpressionEdge eIn = new ExpressionEdge(vIn, null); + // then get the output type vertex + ExpressionVertex vOut = context.tupleTypeValue(1).accept(this); + ExpressionEdge eOut = new ExpressionEdge(vOut, null); + + FunctionTypeValueVertex vFunctionType = new FunctionTypeValueVertex( + exprGraph, eIn, eOut); + exprGraph.addVertex(vFunctionType); + exprGraph.addEdge(eIn); + exprGraph.addEdge(eOut); + return vFunctionType; + } + + @Override + public ExpressionVertex visitNamespacedIdentifier( + NamespacedIdentifierContext context) { + // keeping in mind that we may have constructed this variable already... + VariableIdentifier id = getVariableIdentifier(context); + ExpressionVertex v = createVariableVertex(id); + if (v instanceof VariableReferenceVertex && isLHS) { + ((VariableReferenceVertex) v).setExported(isPublic); + } + return v; + } + + private ExpressionVertex createVariableVertex(VariableIdentifier id) { + if (ReservedIdentifiers.getInstance() + .isReservedIdentifier(id)) { + // construct a constant value vertex with the identifier's value + ConstantValueVertex vReserved = new ConstantValueVertex(exprGraph, + ReservedIdentifiers.getInstance().getValue(id)); + exprGraph.addVertex(vReserved); + return vReserved; + } else { + // this is a variable + // TODO scope + if (exprGraph.containsVariable(id)) { + try { + VariableReferenceVertex v = exprGraph.getVariableVertex(id); + return v; + } catch (VariableNotDefinedException e) { + // cannot actually happen + throw Throwables.propagate(e); + } + } else { + // doesn't exist yet + try { + exprGraph.addVertex(id); + } catch (MultipleDefinitionException e2) { + System.err.println("multiple definitions of variable " + id); + throw new ParseCancellationException(); + } + try { + VariableReferenceVertex v = exprGraph.getVariableVertex(id); + return v; + } catch (VariableNotDefinedException e2) { + throw new UndefinedBehaviourError("failed to define variable " + + id); + } + } + } + } + + @Override + public ExpressionVertex visitImportStatement(ImportStatementContext context) { + return null; + } + + @Override + public ExpressionVertex visitTerminal(TerminalNode node) { + if (node.getSymbol().getType() == ManifoldLexer.INTEGER_VALUE) { + ConstantValueVertex v = new ConstantValueVertex(exprGraph, + new IntegerValue(Integer.valueOf(node.getText()))); + exprGraph.addVertex(v); + return v; + } else if (node.getSymbol().getType() == ManifoldLexer.BOOLEAN_VALUE) { + ConstantValueVertex v = new ConstantValueVertex(exprGraph, + BooleanValue.getInstance(Boolean.parseBoolean(node.getText()))); + exprGraph.addVertex(v); + return v; + } else { + throw new UndefinedBehaviourError( + "unknown terminal node '" + node.getSymbol().getText() + "'"); + } + } + + private VariableIdentifier getVariableIdentifier(NamespacedIdentifierContext context) { + List identifierNodes = context.IDENTIFIER(); + List identifierStrings = new LinkedList<>(); + for (TerminalNode node : identifierNodes) { + identifierStrings.add(node.getText()); + } + + return new VariableIdentifier(identifierStrings); + } + + @Override + public StaticAttributeAccessVertex visitStaticAttributeAccessExpression( + @NotNull StaticAttributeAccessExpressionContext ctx) { + ExpressionVertex vRef = ctx.reference().accept(this); + if (ctx.INTEGER_VALUE() != null) { + return createStaticAttributeAccessExpression(Integer.parseInt(ctx.INTEGER_VALUE().toString()), vRef); + } + return createStaticAttributeAccessExpression(ctx.IDENTIFIER().getText(), vRef); + } + + private StaticAttributeAccessVertex createStaticAttributeAccessExpression(int entryIdx, ExpressionVertex vRef) { + ExpressionEdge e = new ExpressionEdge(vRef, null); + exprGraph.addEdge(e); + + StaticAttributeAccessVertex attributeVertex = new StaticNumberAttributeAccessVertex( + exprGraph, e, entryIdx); + + exprGraph.addVertex(attributeVertex); + return attributeVertex; + } + + private StaticAttributeAccessVertex createStaticAttributeAccessExpression(String attrName, ExpressionVertex vRef) { + ExpressionEdge e = new ExpressionEdge(vRef, null); + exprGraph.addEdge(e); + + StaticAttributeAccessVertex attributeVertex = new StaticStringAttributeAccessVertex( + exprGraph, e, attrName); + + exprGraph.addVertex(attributeVertex); + return attributeVertex; + } + + private String createLineError(ParserRuleContext ctx, String reason) { + Token start = ctx.getStart(); + StringBuilder sb = new StringBuilder() + .append("Error at line ") + .append(start.getLine()) + .append(", char ") + .append(start.getCharPositionInLine() + 1) + .append(": ") + .append(reason); + return sb.toString(); + } +} + diff --git a/src/main/java/org/manifold/compiler/front/ExpressionGraph.java b/src/main/java/org/manifold/compiler/front/ExpressionGraph.java index f48d66e..c40079a 100644 --- a/src/main/java/org/manifold/compiler/front/ExpressionGraph.java +++ b/src/main/java/org/manifold/compiler/front/ExpressionGraph.java @@ -1,26 +1,18 @@ package org.manifold.compiler.front; -import java.io.BufferedWriter; -import java.io.File; -import java.io.FileWriter; -import java.io.IOException; -import java.util.ArrayList; -import java.util.Collection; -import java.util.HashMap; -import java.util.HashSet; -import java.util.LinkedList; -import java.util.List; -import java.util.Map; -import java.util.Set; - -import org.apache.log4j.LogManager; -import org.apache.log4j.Logger; -import org.manifold.compiler.UndefinedBehaviourError; - import com.google.common.base.Preconditions; import com.google.common.base.Throwables; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; +import org.apache.log4j.LogManager; +import org.apache.log4j.Logger; +import org.manifold.compiler.UndefinedBehaviourError; + +import java.io.BufferedWriter; +import java.io.File; +import java.io.FileWriter; +import java.io.IOException; +import java.util.*; public class ExpressionGraph { @@ -34,12 +26,69 @@ public Map getVariableVertices() { return ImmutableMap.copyOf(variableVertices); } + /** + * Copy everything from subGraph into this graph + * @param subGraph the graph to copy + * @param importVars should the visibility modifiers in subGraph are respected + */ + public void addSubGraph(ExpressionGraph subGraph, boolean importVars) { + // map of subgraph edge -> new edge to be inserted + Map exprEdgeMap = new HashMap<>(); + subGraph.getEdges().forEach(e -> { + // replace these with the correct vertices later + ExpressionEdge newEdge = new ExpressionEdge(null, null); + exprEdgeMap.put(e, newEdge); + }); + + // map of subgraph vertex -> new vertex + Map exprVertexMap = new HashMap<>(); + + // do not add the input/output vertices since they are being replaced by the main graph's vertices + subGraph.getVertices().stream() + .forEach(v -> { + ExpressionVertex newVertex; + if (v instanceof VariableReferenceVertex) { + VariableReferenceVertex var = (VariableReferenceVertex) v; + // Allow for ignoring the state of import, this is the case when this function + // is used to copy the body of a FunctionValueVertex + if (var.getExported() || !importVars) { + VariableIdentifier ref = var.getId(); + try { + this.addVertex(ref); + newVertex = this.getVariableVertex(ref); + } catch (MultipleDefinitionException | VariableNotDefinedException e) { + throw Throwables.propagate(e); + } + } else { + newVertex = new VariableReferenceVertex(this, var.getId()); + this.addVertex(newVertex); + } + } else { + newVertex = v.copy(this, exprEdgeMap); + this.addVertex(newVertex); + } + exprVertexMap.put(v, newVertex); + }); + + // each edge in subgraph -> edge in main graph should refer to the same source/target + subGraph.getEdges().forEach(edge -> { + if (edge.getSource() != null) { + exprEdgeMap.get(edge).setSource(exprVertexMap.get(edge.getSource())); + } + if (edge.getTarget() != null) { + exprEdgeMap.get(edge).setTarget(exprVertexMap.get(edge.getTarget())); + } + }); + + this.edges.addAll(exprEdgeMap.values()); + } + public boolean containsVariable(VariableIdentifier vID) { return variableVertices.containsKey(vID); } public VariableReferenceVertex getVariableVertex(VariableIdentifier vID) - throws VariableNotDefinedException { + throws VariableNotDefinedException { if (variableVertices.containsKey(vID)) { return variableVertices.get(vID); } else { @@ -48,7 +97,7 @@ public VariableReferenceVertex getVariableVertex(VariableIdentifier vID) } public void addVertex(VariableIdentifier vID) - throws MultipleDefinitionException { + throws MultipleDefinitionException { addVertex(new VariableReferenceVertex(this, vID)); } @@ -172,10 +221,10 @@ public void addFunctionExpressionGraph(ExpressionGraph subGraph, // map of subgraph edge -> new edge to be inserted Map exprEdgeMap = new HashMap<>(); subGraph.getEdges().forEach(e -> { - // replace these with the correct vertices later - ExpressionEdge newEdge = new ExpressionEdge(null, null); - exprEdgeMap.put(e, newEdge); - }); + // replace these with the correct vertices later + ExpressionEdge newEdge = new ExpressionEdge(null, null); + exprEdgeMap.put(e, newEdge); + }); // map of subgraph vertex -> new vertex Map exprVertexMap = new HashMap<>(); @@ -184,26 +233,26 @@ public void addFunctionExpressionGraph(ExpressionGraph subGraph, subGraph.getVertices().stream() .filter(vertex -> vertex != subGraphInput) .forEach(v -> { - ExpressionVertex newVertex; - if (v instanceof VariableReferenceVertex) { - // special case; handle renaming here - if (variableRenamingMap.containsKey(v)) { - newVertex = variableRenamingMap.get(v); - } else { - VariableIdentifier ref = ((VariableReferenceVertex) v).getId(); - try { - this.addVertex(ref); - newVertex = this.getVariableVertex(ref); - } catch (MultipleDefinitionException | VariableNotDefinedException e) { - throw Throwables.propagate(e); - } - } + ExpressionVertex newVertex; + if (v instanceof VariableReferenceVertex) { + // special case; handle renaming here + if (variableRenamingMap.containsKey(v)) { + newVertex = variableRenamingMap.get(v); } else { - newVertex = v.copy(this, exprEdgeMap); - this.addVertex(newVertex); + VariableIdentifier ref = ((VariableReferenceVertex) v).getId(); + try { + this.addVertex(ref); + newVertex = this.getVariableVertex(ref); + } catch (MultipleDefinitionException | VariableNotDefinedException e) { + throw Throwables.propagate(e); + } } - exprVertexMap.put(v, newVertex); - }); + } else { + newVertex = v.copy(this, exprEdgeMap); + this.addVertex(newVertex); + } + exprVertexMap.put(v, newVertex); + }); // Replace the function input vertex with the vertices from the main graph ExpressionVertex inputVertex = mainGraphInput.getSource(); @@ -215,13 +264,13 @@ public void addFunctionExpressionGraph(ExpressionGraph subGraph, // each edge in subgraph -> edge in main graph should refer to the same source/target subGraph.getEdges().forEach(edge -> { - if (edge.getSource() != null) { - exprEdgeMap.get(edge).setSource(exprVertexMap.get(edge.getSource())); - } - if (edge.getTarget() != null) { - exprEdgeMap.get(edge).setTarget(exprVertexMap.get(edge.getTarget())); - } - }); + if (edge.getSource() != null) { + exprEdgeMap.get(edge).setSource(exprVertexMap.get(edge.getSource())); + } + if (edge.getTarget() != null) { + exprEdgeMap.get(edge).setTarget(exprVertexMap.get(edge.getTarget())); + } + }); this.edges.addAll(exprEdgeMap.values()); } @@ -269,28 +318,29 @@ public void writeDOTFile(File file) throws IOException { public void verifyVariablesSingleAssignment() { Map> inboundEdges = new HashMap<>(); getEdges().forEach(exprEdge -> { - ExpressionVertex v = exprEdge.getTarget(); - if (v instanceof VariableReferenceVertex) { - inboundEdges.putIfAbsent(v, new ArrayList<>()); - inboundEdges.get(v).add(exprEdge); - } - }); + ExpressionVertex v = exprEdge.getTarget(); + if (v instanceof VariableReferenceVertex) { + inboundEdges.putIfAbsent(v, new ArrayList<>()); + inboundEdges.get(v).add(exprEdge); + } + }); List errors = new ArrayList<>(); inboundEdges.forEach((vertex, edges) -> { - if (edges.size() != 1) { - StringBuilder error = new StringBuilder(); - error.append(String.format("Vertex %s has %d incoming edges:", - vertex.toString(), edges.size())); - edges.forEach(edge -> error.append(" {") - .append(edge.toString()) - .append("}")); - errors.add(error.toString()); - } - }); + if (edges.size() != 1) { + StringBuilder error = new StringBuilder(); + error.append(String.format("Vertex %s has %d incoming edges:", + vertex.toString(), edges.size())); + edges.forEach(edge -> error.append(" {") + .append(edge.toString()) + .append("}")); + log.error(error.toString()); + errors.add(((VariableReferenceVertex) vertex).getId() + " is defined multiple times"); + } + }); if (!errors.isEmpty()) { - throw new RuntimeException(errors.toString()); + throw new FrontendBuildException(errors.toString()); } } diff --git a/src/main/java/org/manifold/compiler/front/FunctionValueVertex.java b/src/main/java/org/manifold/compiler/front/FunctionValueVertex.java index 12d227c..852dc0f 100644 --- a/src/main/java/org/manifold/compiler/front/FunctionValueVertex.java +++ b/src/main/java/org/manifold/compiler/front/FunctionValueVertex.java @@ -150,9 +150,12 @@ public void writeToDOTFile(BufferedWriter writer) throws IOException { @Override public ExpressionVertex copy(ExpressionGraph g, Map edgeMap) { - // TODO Auto-generated method stub - return null; - } + // Use a new expression graph for the body, just like when constructing a function initially. + // This maintains proper scoping + ExpressionGraph newBody = new ExpressionGraph(); + newBody.addSubGraph(functionBody, false); + return new FunctionValueVertex(g, edgeMap.get(functionTypeEdge), newBody); + } } diff --git a/src/main/java/org/manifold/compiler/front/ImportVisitor.java b/src/main/java/org/manifold/compiler/front/ImportVisitor.java new file mode 100644 index 0000000..75935dc --- /dev/null +++ b/src/main/java/org/manifold/compiler/front/ImportVisitor.java @@ -0,0 +1,27 @@ +package org.manifold.compiler.front; + +import org.manifold.parser.ManifoldBaseVisitor; +import org.manifold.parser.ManifoldParser.ImportStatementContext; + +import java.util.ArrayList; +import java.util.List; + +class ImportVisitor extends ManifoldBaseVisitor { + + private List imports; + public List getImports() { + return this.imports; + } + + public ImportVisitor() { + this.imports = new ArrayList<>(); + } + + @Override + public Void visitImportStatement(ImportStatementContext context) { + String filePath = context.STRING_VALUE().getText(); + filePath = filePath.replaceAll("\\\"", "\""); + this.imports.add(filePath.substring(1, filePath.length() - 1)); + return null; + } +} diff --git a/src/main/java/org/manifold/compiler/front/Main.java b/src/main/java/org/manifold/compiler/front/Main.java index 0e1dbcd..102a8ff 100644 --- a/src/main/java/org/manifold/compiler/front/Main.java +++ b/src/main/java/org/manifold/compiler/front/Main.java @@ -21,8 +21,11 @@ import java.io.File; import java.io.FileInputStream; +import java.io.IOException; +import java.net.URL; import java.nio.file.Paths; import java.util.*; +import java.util.stream.Stream; public class Main implements Frontend { @@ -163,15 +166,19 @@ public static void elaborateNodes(ExpressionGraph g, Schematic s) } - @Override - public Schematic invokeFrontend(CommandLine cmd) throws Exception { - - File inputFile = Paths.get(cmd.getArgs()[0]).toFile(); + private File getLib(String libPath) { + URL url = this.getClass().getClassLoader().getResource("libraries/" + libPath); + if (url == null) { + return null; + } + return new File(url.getFile()); + } + public ExpressionGraph parseFile(File inputFile) throws IOException { ManifoldLexer lexer = new ManifoldLexer(new ANTLRInputStream( new FileInputStream(inputFile))); - // Get a list of matched tokens + // Get a list of matched tokens CommonTokenStream tokens = new CommonTokenStream(lexer); // Pass the tokens to the parser @@ -184,7 +191,7 @@ public Schematic invokeFrontend(CommandLine cmd) throws Exception { public void syntaxError(@NotNull Recognizer recognizer, @Nullable Object offendingSymbol, int line, int charPositionInLine, @NotNull String msg, @Nullable RecognitionException e) { errors.append("Error at line ").append(line).append(", char ") - .append(charPositionInLine).append(": ").append(msg).append("\n"); + .append(charPositionInLine).append(": ").append(msg).append("\n"); } @Override @@ -214,16 +221,52 @@ public void reportContextSensitivity(@NotNull Parser recognizer, @NotNull DFA df throw new FrontendBuildException(errors.toString()); } - ExpressionContextVisitor graphBuilder = new ExpressionContextVisitor(); - List expressionContexts = context.expression(); - for (ExpressionContext expressionContext : expressionContexts) { - graphBuilder.visit(expressionContext); + ExpressionGraph exprGraph = new ExpressionGraph(); + + ImportVisitor importVisitor = new ImportVisitor(); + importVisitor.visit(context); + for (String filePath : importVisitor.getImports()) { + File importedFile = Stream.of( + new File(inputFile.getParent(), filePath), + new File(inputFile.getParent(), filePath + ".manifold") + ).filter(f -> f.exists()) + .findFirst() + .orElseGet(() -> { + File lib = getLib(filePath); + if (lib == null) { + lib = getLib(filePath + ".manifold"); + } + if (lib != null) { + return lib; + } + throw new FrontendBuildException("Import " + filePath + " not found"); + }); + ExpressionGraph g = parseFile(importedFile); + exprGraph.addSubGraph(g, true); + } + + ExpressionContextVisitor graphBuilder = new ExpressionContextVisitor(exprGraph); + List statementContexts = context.statement(); + for (StatementContext statementContext : statementContexts) { + graphBuilder.visit(statementContext); } - ExpressionGraph exprGraph = graphBuilder.getExpressionGraph(); + exprGraph = graphBuilder.getExpressionGraph(); + log.debug("writing out initial expression graph"); File exprGraphDot = new File(inputFile.getName() + ".exprs.dot"); exprGraph.writeDOTFile(exprGraphDot); + return exprGraph; + } + + @Override + public Schematic invokeFrontend(CommandLine cmd) throws Exception { + + File inputFile = Paths.get(cmd.getArgs()[0]).toFile(); + if (!inputFile.exists()) { + throw new FrontendBuildException(inputFile.getName() + " not found."); + } + ExpressionGraph exprGraph = parseFile(inputFile); exprGraph.verifyVariablesSingleAssignment(); Schematic schematic = new Schematic(inputFile.getName()); @@ -240,357 +283,3 @@ public void reportContextSensitivity(@NotNull Parser recognizer, @NotNull DFA df } } - -class ExpressionContextVisitor extends ManifoldBaseVisitor { - - private ExpressionGraph exprGraph; - public ExpressionGraph getExpressionGraph() { - return this.exprGraph; - } - - private boolean isLHS = true; - private int nextTmpVar = 0; - public ExpressionContextVisitor() { - this.exprGraph = new ExpressionGraph(); - } - - @Override - public ExpressionVertex visitAssignmentExpression( - ManifoldParser.AssignmentExpressionContext context) { - - // get the vertex corresponding to the lvalue - isLHS = true; - ExpressionVertex vLeft = context.lvalue().accept(this); - - // then get the rvalue... - isLHS = false; - ExpressionVertex vRight = context.rvalue().accept(this); - - ExpressionEdge e = new ExpressionEdge(vRight, vLeft); - exprGraph.addEdge(e); - return vRight; - } - - @Override - public ExpressionVertex visitFunctionInvocationExpression( - ManifoldParser.FunctionInvocationExpressionContext context) { - // get the vertex corresponding to the function being called - ExpressionVertex vFunction = context.reference().accept(this); - ExpressionEdge eFunction = new ExpressionEdge(vFunction, null); - // then get the input vertex - ExpressionVertex vInput = context.rvalue().accept(this); - ExpressionEdge eInput = new ExpressionEdge(vInput, null); - - FunctionInvocationVertex vInvocation = new FunctionInvocationVertex( - exprGraph, eFunction, eInput); - exprGraph.addVertex(vInvocation); - exprGraph.addEdge(eFunction); - exprGraph.addEdge(eInput); - return vInvocation; - } - - @Override - public ExpressionVertex visitFunctionValue( - ManifoldParser.FunctionValueContext ctx) { - ExpressionContextVisitor functionGraphBuilder = - new ExpressionContextVisitor(); - ctx.expression().forEach(functionGraphBuilder::visit); - ExpressionGraph fSubGraph = functionGraphBuilder.getExpressionGraph(); - - ExpressionVertex fTypeVertex = visitFunctionTypeValue( - ctx.functionTypeValue()); - ExpressionEdge fTypeEdge = new ExpressionEdge(fTypeVertex, null); - - FunctionValueVertex fValueVertex = new FunctionValueVertex( - exprGraph, fTypeEdge, fSubGraph); - exprGraph.addVertex(fValueVertex); - exprGraph.addEdge(fTypeEdge); - - return fValueVertex; - } - - // KEY INSIGHT: combine the port type/port attributes and - // node attributes in a single FunctionTypeValue signature. - // As an example, if we have port types xIn(a: Int) and xOut(b: Int) - // and want a node type xDev whose attributes are p,q: Bool, - // input port u: xIn, output port v: xOut, we can declare it like - // - // xDev = primitive node (u: xIn, p: Bool, q: Bool) -> (v: xOut); - // - // and instantiate it like - // - // vResult = xDev(u: (0: uVal, 1: (a: 3)), p: True, q: False, v: (b: 4)) - // - @Override - public ExpressionVertex visitPrimitiveNodeDefinitionExpression( - ManifoldParser.PrimitiveNodeDefinitionExpressionContext context) { - ExpressionVertex vSignature = context.functionTypeValue().accept(this); - ExpressionEdge eSignature = new ExpressionEdge(vSignature, null); - exprGraph.addEdge(eSignature); - PrimitiveNodeVertex vNode = new PrimitiveNodeVertex(exprGraph, eSignature); - exprGraph.addVertex(vNode); - return vNode; - } - - @Override - public ExpressionVertex visitPrimitivePortDefinitionExpression( - ManifoldParser.PrimitivePortDefinitionExpressionContext context) { - - ExpressionVertex vSignalType = context.typevalue().accept(this); - ExpressionEdge eSignalType = new ExpressionEdge(vSignalType, null); - exprGraph.addEdge(eSignalType); - - ExpressionVertex vAttributes; - if (context.tupleTypeValue() != null) { - vAttributes = context.tupleTypeValue().accept(this); - } else { - vAttributes = new ConstantValueVertex(exprGraph, - NilTypeValue.getInstance()); - } - exprGraph.addVertex(vAttributes); - ExpressionEdge eAttributes = new ExpressionEdge(vAttributes, null); - exprGraph.addEdge(eAttributes); - PrimitivePortVertex vPort = new PrimitivePortVertex(exprGraph, - eSignalType, eAttributes); - exprGraph.addVertex(vPort); - return vPort; - } - - @Override - public ExpressionVertex visitTupleTypeValue(TupleTypeValueContext context) { - List entries = context.tupleTypeValueEntry(); - MappedArray typeValueEdges = new MappedArray<>(); - MappedArray defaultValueEdges = new MappedArray<>(); - Integer nextAnonymousID = 0; - for (TupleTypeValueEntryContext entryCtx : entries) { - // each child has a typevalue, and may have - // an identifier (named field) - // and an expression (default value) - String identifier; - if (entryCtx.IDENTIFIER() != null) { - identifier = entryCtx.IDENTIFIER().getText(); - } else { - identifier = nextAnonymousID.toString(); - nextAnonymousID += 1; - } - ExpressionVertex vxTypeValue = entryCtx.typevalue().accept(this); - ExpressionEdge eTypeValue = new ExpressionEdge(vxTypeValue, null); - typeValueEdges.put(identifier, eTypeValue); - exprGraph.addEdge(eTypeValue); - if (entryCtx.expression() != null) { - ExpressionVertex vxDefaultValue = entryCtx.expression().accept(this); - ExpressionEdge eDefaultValue = new ExpressionEdge(vxDefaultValue, null); - defaultValueEdges.put(identifier, eDefaultValue); - exprGraph.addEdge(eDefaultValue); - } - } - TupleTypeValueVertex vTuple = new TupleTypeValueVertex(exprGraph, - typeValueEdges, defaultValueEdges); - exprGraph.addVertex(vTuple); - return vTuple; - } - - @Override - public ExpressionVertex visitTupleValue(TupleValueContext context) { - List entries = context.tupleValueEntry(); - - // Desugar tuple unpacking to assignment to a temporary variable, then accessing the attributes of that variable - if (isLHS) { - return unpackTuple(entries); - } - - Integer nextAnonymousID = 0; - MappedArray valueEdges = new MappedArray<>(); - for (TupleValueEntryContext entryCtx : entries) { - // each child has a value, and may have an identifier (named field) - ExpressionVertex vxValue = entryCtx.expression().accept(this); - String identifier; - if (entryCtx.IDENTIFIER() != null) { - identifier = entryCtx.IDENTIFIER().getText(); - } else { - // TODO verify this against the specification - identifier = nextAnonymousID.toString(); - nextAnonymousID += 1; - } - ExpressionEdge eValue = new ExpressionEdge(vxValue, null); - valueEdges.put(identifier, eValue); - exprGraph.addEdge(eValue); - } - TupleValueVertex vTuple = new TupleValueVertex(exprGraph, valueEdges); - exprGraph.addVertex(vTuple); - return vTuple; - } - - private ExpressionVertex unpackTuple(List entries) { - VariableIdentifier tmpTupleId = new VariableIdentifier(Arrays.asList("tmp_" + nextTmpVar)); - nextTmpVar += 1; - ExpressionVertex tmpTuple = createVariableVertex(tmpTupleId); - - int entryNum = 0; - boolean containsNamedEntry = false; - for (TupleValueEntryContext entryCtx : entries) { - ExpressionVertex entryVertex = entryCtx.expression().accept(this); - - if (!(entryVertex instanceof VariableReferenceVertex)) { - String err = createLineError(entryCtx, "Unpacking target must be a variable"); - throw new FrontendBuildException(err); - } - - ExpressionVertex attrVertex; - TerminalNode entryId = entryCtx.IDENTIFIER(); - if (entryId != null) { - - attrVertex = createStaticAttributeAccessExpression(entryId.getText(), tmpTuple); - containsNamedEntry = true; - - } else if (containsNamedEntry) { - - String err = createLineError(entryCtx, "Index-based entries must be unpacked before namespaced identifiers"); - throw new FrontendBuildException(err); - - } else { - attrVertex = createStaticAttributeAccessExpression(entryNum, tmpTuple); - } - exprGraph.addEdge(new ExpressionEdge(attrVertex, entryVertex)); - - entryNum += 1; - } - return tmpTuple; - } - - @Override - public ExpressionVertex visitFunctionTypeValue( - FunctionTypeValueContext context) { - // get the vertex corresponding to the input type - ExpressionVertex vIn = context.tupleTypeValue(0).accept(this); - ExpressionEdge eIn = new ExpressionEdge(vIn, null); - // then get the output type vertex - ExpressionVertex vOut = context.tupleTypeValue(1).accept(this); - ExpressionEdge eOut = new ExpressionEdge(vOut, null); - - FunctionTypeValueVertex vFunctionType = new FunctionTypeValueVertex( - exprGraph, eIn, eOut); - exprGraph.addVertex(vFunctionType); - exprGraph.addEdge(eIn); - exprGraph.addEdge(eOut); - return vFunctionType; - } - - @Override - public ExpressionVertex visitNamespacedIdentifier( - NamespacedIdentifierContext context) { - // keeping in mind that we may have constructed this variable already... - VariableIdentifier id = getVariableIdentifier(context); - return createVariableVertex(id); - } - - private ExpressionVertex createVariableVertex(VariableIdentifier id) { - if (ReservedIdentifiers.getInstance() - .isReservedIdentifier(id)) { - // construct a constant value vertex with the identifier's value - ConstantValueVertex vReserved = new ConstantValueVertex(exprGraph, - ReservedIdentifiers.getInstance().getValue(id)); - exprGraph.addVertex(vReserved); - return vReserved; - } else { - // this is a variable - // TODO scope - if (exprGraph.containsVariable(id)) { - try { - VariableReferenceVertex v = exprGraph.getVariableVertex(id); - return v; - } catch (VariableNotDefinedException e) { - // cannot actually happen - throw Throwables.propagate(e); - } - } else { - // doesn't exist yet - try { - exprGraph.addVertex(id); - } catch (MultipleDefinitionException e2) { - System.err.println("multiple definitions of variable " + id); - throw new ParseCancellationException(); - } - try { - VariableReferenceVertex v = exprGraph.getVariableVertex(id); - return v; - } catch (VariableNotDefinedException e2) { - throw new UndefinedBehaviourError("failed to define variable " - + id); - } - } - } - } - - @Override - public ExpressionVertex visitTerminal(TerminalNode node) { - if (node.getSymbol().getType() == ManifoldLexer.INTEGER_VALUE) { - ConstantValueVertex v = new ConstantValueVertex(exprGraph, - new IntegerValue(Integer.valueOf(node.getText()))); - exprGraph.addVertex(v); - return v; - } else if (node.getSymbol().getType() == ManifoldLexer.BOOLEAN_VALUE) { - ConstantValueVertex v = new ConstantValueVertex(exprGraph, - BooleanValue.getInstance(Boolean.parseBoolean(node.getText()))); - exprGraph.addVertex(v); - return v; - } else { - throw new UndefinedBehaviourError( - "unknown terminal node '" + node.getSymbol().getText() + "'"); - } - } - - private VariableIdentifier getVariableIdentifier(NamespacedIdentifierContext context) { - List identifierNodes = context.IDENTIFIER(); - List identifierStrings = new LinkedList<>(); - for (TerminalNode node : identifierNodes) { - identifierStrings.add(node.getText()); - } - - return new VariableIdentifier(identifierStrings); - } - - @Override - public StaticAttributeAccessVertex visitStaticAttributeAccessExpression( - @NotNull ManifoldParser.StaticAttributeAccessExpressionContext ctx) { - ExpressionVertex vRef = ctx.reference().accept(this); - if (ctx.INTEGER_VALUE() != null) { - return createStaticAttributeAccessExpression(Integer.parseInt(ctx.INTEGER_VALUE().toString()), vRef); - } - return createStaticAttributeAccessExpression(ctx.IDENTIFIER().getText(), vRef); - } - - private StaticAttributeAccessVertex createStaticAttributeAccessExpression(int entryIdx, ExpressionVertex vRef) { - ExpressionEdge e = new ExpressionEdge(vRef, null); - exprGraph.addEdge(e); - - StaticAttributeAccessVertex attributeVertex = new StaticNumberAttributeAccessVertex( - exprGraph, e, entryIdx); - - exprGraph.addVertex(attributeVertex); - return attributeVertex; - } - - private StaticAttributeAccessVertex createStaticAttributeAccessExpression(String attrName, ExpressionVertex vRef) { - ExpressionEdge e = new ExpressionEdge(vRef, null); - exprGraph.addEdge(e); - - StaticAttributeAccessVertex attributeVertex = new StaticStringAttributeAccessVertex( - exprGraph, e, attrName); - - exprGraph.addVertex(attributeVertex); - return attributeVertex; - } - - private String createLineError(ParserRuleContext ctx, String reason) { - Token start = ctx.getStart(); - StringBuilder sb = new StringBuilder() - .append("Error at line ") - .append(start.getLine()) - .append(", char ") - .append(start.getCharPositionInLine() + 1) - .append(": ") - .append(reason); - return sb.toString(); - } -} diff --git a/src/main/java/org/manifold/compiler/front/NodeValueVertex.java b/src/main/java/org/manifold/compiler/front/NodeValueVertex.java index 1aaf2d5..bacc918 100644 --- a/src/main/java/org/manifold/compiler/front/NodeValueVertex.java +++ b/src/main/java/org/manifold/compiler/front/NodeValueVertex.java @@ -213,7 +213,7 @@ public void writeToDOTFile(BufferedWriter writer) throws IOException { @Override public ExpressionVertex copy(ExpressionGraph g, Map edgeMap) { - Preconditions.checkArgument(edgeMap.containsKey(signature)); - return new NodeValueVertex(g, nodeType, signature, edgeMap.get(signature)); + Preconditions.checkArgument(edgeMap.containsKey(inputEdge)); + return new NodeValueVertex(g, nodeType, signature, edgeMap.get(inputEdge)); } } diff --git a/src/main/java/org/manifold/compiler/front/PrimitivePortVertex.java b/src/main/java/org/manifold/compiler/front/PrimitivePortVertex.java index 3fef80f..3ed3101 100644 --- a/src/main/java/org/manifold/compiler/front/PrimitivePortVertex.java +++ b/src/main/java/org/manifold/compiler/front/PrimitivePortVertex.java @@ -1,17 +1,13 @@ package org.manifold.compiler.front; +import com.google.common.base.Preconditions; +import org.manifold.compiler.*; + import java.io.BufferedWriter; import java.io.IOException; import java.util.HashMap; import java.util.Map; -import com.google.common.base.Preconditions; -import org.manifold.compiler.NilTypeValue; -import org.manifold.compiler.PortTypeValue; -import org.manifold.compiler.TypeTypeValue; -import org.manifold.compiler.TypeValue; -import org.manifold.compiler.Value; - public class PrimitivePortVertex extends ExpressionVertex { private PortTypeValue port = null; @@ -98,8 +94,8 @@ public void writeToDOTFile(BufferedWriter writer) throws IOException { @Override public ExpressionVertex copy(ExpressionGraph g, Map edgeMap) { - Preconditions.checkArgument(edgeMap.containsKey(signalTypeEdge) && edgeMap.containsKey(signalTypeEdge)); - return new PrimitivePortVertex(g, edgeMap.get(signalTypeEdge), edgeMap.get(signalTypeEdge)); + Preconditions.checkArgument(edgeMap.containsKey(signalTypeEdge) && edgeMap.containsKey(attributesEdge)); + return new PrimitivePortVertex(g, edgeMap.get(signalTypeEdge), edgeMap.get(attributesEdge)); } @Override diff --git a/src/main/java/org/manifold/compiler/front/TupleTypeValueVertex.java b/src/main/java/org/manifold/compiler/front/TupleTypeValueVertex.java index 6cbf11b..d0f3707 100644 --- a/src/main/java/org/manifold/compiler/front/TupleTypeValueVertex.java +++ b/src/main/java/org/manifold/compiler/front/TupleTypeValueVertex.java @@ -73,13 +73,13 @@ public ExpressionVertex copy(ExpressionGraph g, Map newTypeValueEdges = new MappedArray<>(); MappedArray newDefaultValueEdges = new MappedArray<>(); typeValueEdges.forEach(entry -> { - Preconditions.checkArgument(edgeMap.containsKey(entry.getValue())); - newTypeValueEdges.put(entry.getKey(), edgeMap.get(entry.getValue())); - }); + Preconditions.checkArgument(edgeMap.containsKey(entry.getValue())); + newTypeValueEdges.put(entry.getKey(), edgeMap.get(entry.getValue())); + }); defaultValueEdges.forEach(entry -> { - Preconditions.checkArgument(edgeMap.containsKey(entry.getValue())); - newDefaultValueEdges.put(entry.getKey(), edgeMap.get(entry.getValue())); - }); + Preconditions.checkArgument(edgeMap.containsKey(entry.getValue())); + newDefaultValueEdges.put(entry.getKey(), edgeMap.get(entry.getValue())); + }); return new TupleTypeValueVertex(g, newTypeValueEdges, newDefaultValueEdges); } diff --git a/src/main/java/org/manifold/compiler/front/VariableNotDefinedException.java b/src/main/java/org/manifold/compiler/front/VariableNotDefinedException.java index 9627c10..9876629 100644 --- a/src/main/java/org/manifold/compiler/front/VariableNotDefinedException.java +++ b/src/main/java/org/manifold/compiler/front/VariableNotDefinedException.java @@ -1,14 +1,9 @@ package org.manifold.compiler.front; -public class VariableNotDefinedException extends Exception { +public class VariableNotDefinedException extends FrontendBuildException { private static final long serialVersionUID = 1L; - private VariableIdentifier identifier; public VariableNotDefinedException(VariableIdentifier id){ - this.identifier = id; - } - @Override - public String getMessage(){ - return "variable '" + identifier + "' not defined in this scope"; + super("variable '" + id + "' not defined in this scope"); } } diff --git a/src/main/java/org/manifold/compiler/front/VariableReferenceVertex.java b/src/main/java/org/manifold/compiler/front/VariableReferenceVertex.java index 545b1a3..6a9f2b8 100644 --- a/src/main/java/org/manifold/compiler/front/VariableReferenceVertex.java +++ b/src/main/java/org/manifold/compiler/front/VariableReferenceVertex.java @@ -1,30 +1,43 @@ package org.manifold.compiler.front; +import org.manifold.compiler.TypeValue; +import org.manifold.compiler.UndefinedBehaviourError; +import org.manifold.compiler.Value; + import java.io.BufferedWriter; -import java.io.File; import java.io.IOException; import java.util.List; import java.util.Map; -import org.manifold.compiler.TypeValue; -import org.manifold.compiler.UndefinedBehaviourError; -import org.manifold.compiler.Value; - public class VariableReferenceVertex extends ExpressionVertex { - private VariableIdentifier id; + protected VariableIdentifier id; + protected boolean exported; public VariableIdentifier getIdentifier() { return id; } public VariableReferenceVertex(ExpressionGraph g, VariableIdentifier id) { + this(g, id, false); + } + + public VariableReferenceVertex(ExpressionGraph g, VariableIdentifier id, boolean exported) { super(g); this.id = id; + this.exported = exported; } public VariableIdentifier getId() { return id; } - + + public boolean getExported() { + return exported; + } + + public void setExported(boolean exported) { + this.exported = exported; + } + @Override public String toString() { return "var " + id.toString(); @@ -47,7 +60,7 @@ public void writeToDOTFile(BufferedWriter writer) throws IOException { @Override public ExpressionVertex copy(ExpressionGraph g, Map edgeMap) { - return new VariableReferenceVertex(g, id); + return new VariableReferenceVertex(g, id, exported); } private TypeValue type = null; @@ -60,7 +73,7 @@ private ExpressionEdge findAssigningEdge() { return incoming.get(0); } else if (incoming.size() == 0) { // not assigned - throw new UndefinedBehaviourError("variable '" + id + "' not assigned"); + throw new VariableNotDefinedException(id); } else { throw new UndefinedBehaviourError("variable '" + id + "' multiply assigned"); } diff --git a/src/main/resources/libraries/circuits.manifold b/src/main/resources/libraries/circuits.manifold new file mode 100644 index 0000000..7126572 --- /dev/null +++ b/src/main/resources/libraries/circuits.manifold @@ -0,0 +1,5 @@ +public digitalIn = primitive port Bool; +public digitalOut = primitive port Bool; + +public inputPin = primitive node (Nil) -> (out: digitalOut); +public outputPin = primitive node (in: digitalIn) -> (Nil); diff --git a/tests/acceptance/import.manifold b/tests/acceptance/import.manifold new file mode 100644 index 0000000..6c835cc --- /dev/null +++ b/tests/acceptance/import.manifold @@ -0,0 +1,7 @@ +import "../input_pin.manifold"; + +digitalIn = primitive port Bool; +outputPin = primitive node (in: digitalIn) -> (Nil); + +v = myfunc(a=in, b=in); +outputPin(in=v); diff --git a/tests/acceptance/import.manifold.schematic b/tests/acceptance/import.manifold.schematic new file mode 100644 index 0000000..ce4c775 --- /dev/null +++ b/tests/acceptance/import.manifold.schematic @@ -0,0 +1,67 @@ +{ + "name": "import.manifold", + "userDefinedTypes": {}, + "portTypes": { + "digitalOut": { + "signalType": "Bool", + "attributes": {} + }, + "digitalIn": { + "signalType": "Bool", + "attributes": {} + } + }, + "nodeTypes": { + "inputPin": { + "attributes": {}, + "ports": { + "out": "digitalOut" + } + }, + "outputPin": { + "attributes": {}, + "ports": { + "in": "digitalIn" + } + } + }, + "constraintTypes": {}, + "nodes": { + "n1": { + "type": "inputPin", + "attributes": {}, + "portAttrs": { + "out": {} + } + }, + "n2": { + "type": "inputPin", + "attributes": {}, + "portAttrs": { + "out": {} + } + }, + "n3": { + "type": "inputPin", + "attributes": {}, + "portAttrs": { + "out": {} + } + }, + "n4": { + "type": "outputPin", + "attributes": {}, + "portAttrs": { + "in": {} + } + } + }, + "connections": { + "c1": { + "attributes": {}, + "from": "n2:out", + "to": "n4:in" + } + }, + "constraints": {} +} \ No newline at end of file diff --git a/tests/acceptance/importSystem.manifold b/tests/acceptance/importSystem.manifold new file mode 100644 index 0000000..77e0f98 --- /dev/null +++ b/tests/acceptance/importSystem.manifold @@ -0,0 +1,4 @@ +import "circuits"; + +x = inputPin(); +outputPin(in=x); \ No newline at end of file diff --git a/tests/acceptance/importSystem.manifold.schematic b/tests/acceptance/importSystem.manifold.schematic new file mode 100644 index 0000000..28739b7 --- /dev/null +++ b/tests/acceptance/importSystem.manifold.schematic @@ -0,0 +1,53 @@ +{ + "name": "importSystem.manifold", + "userDefinedTypes": {}, + "portTypes": { + "digitalOut": { + "signalType": "Bool", + "attributes": {} + }, + "digitalIn": { + "signalType": "Bool", + "attributes": {} + } + }, + "nodeTypes": { + "outputPin": { + "attributes": {}, + "ports": { + "in": "digitalIn" + } + }, + "inputPin": { + "attributes": {}, + "ports": { + "out": "digitalOut" + } + } + }, + "constraintTypes": {}, + "nodes": { + "n1": { + "type": "inputPin", + "attributes": {}, + "portAttrs": { + "out": {} + } + }, + "n2": { + "type": "outputPin", + "attributes": {}, + "portAttrs": { + "in": {} + } + } + }, + "connections": { + "c1": { + "attributes": {}, + "from": "n1:out", + "to": "n2:in" + } + }, + "constraints": {} +} \ No newline at end of file diff --git a/tests/acceptance/importTuple.manifold b/tests/acceptance/importTuple.manifold new file mode 100644 index 0000000..9a61c2b --- /dev/null +++ b/tests/acceptance/importTuple.manifold @@ -0,0 +1,6 @@ +import "../input_pin"; + +digitalIn = primitive port Bool; +outputPin = primitive node (in: digitalIn) -> (Nil); + +outputPin(in=x); diff --git a/tests/acceptance/importTuple.manifold.schematic b/tests/acceptance/importTuple.manifold.schematic new file mode 100644 index 0000000..d039177 --- /dev/null +++ b/tests/acceptance/importTuple.manifold.schematic @@ -0,0 +1,67 @@ +{ + "name": "importTuple.manifold", + "userDefinedTypes": {}, + "portTypes": { + "digitalOut": { + "signalType": "Bool", + "attributes": {} + }, + "digitalIn": { + "signalType": "Bool", + "attributes": {} + } + }, + "nodeTypes": { + "inputPin": { + "attributes": {}, + "ports": { + "out": "digitalOut" + } + }, + "outputPin": { + "attributes": {}, + "ports": { + "in": "digitalIn" + } + } + }, + "constraintTypes": {}, + "nodes": { + "n1": { + "type": "inputPin", + "attributes": {}, + "portAttrs": { + "out": {} + } + }, + "n2": { + "type": "inputPin", + "attributes": {}, + "portAttrs": { + "out": {} + } + }, + "n3": { + "type": "inputPin", + "attributes": {}, + "portAttrs": { + "out": {} + } + }, + "n4": { + "type": "outputPin", + "attributes": {}, + "portAttrs": { + "in": {} + } + } + }, + "connections": { + "c1": { + "attributes": {}, + "from": "n3:out", + "to": "n4:in" + } + }, + "constraints": {} +} \ No newline at end of file diff --git a/tests/buildErrors/importFileWithError.manifold b/tests/buildErrors/importFileWithError.manifold new file mode 100644 index 0000000..d4408b7 --- /dev/null +++ b/tests/buildErrors/importFileWithError.manifold @@ -0,0 +1 @@ +import "parseError.manifold"; \ No newline at end of file diff --git a/tests/buildErrors/importFileWithError.manifold.error b/tests/buildErrors/importFileWithError.manifold.error new file mode 100644 index 0000000..abd8f55 --- /dev/null +++ b/tests/buildErrors/importFileWithError.manifold.error @@ -0,0 +1 @@ +Error at line 2, char 0: no viable alternative at input 'compile.' diff --git a/tests/buildErrors/importRedefineVar.manifold b/tests/buildErrors/importRedefineVar.manifold new file mode 100644 index 0000000..6af3a11 --- /dev/null +++ b/tests/buildErrors/importRedefineVar.manifold @@ -0,0 +1,3 @@ +import "../input_pin"; + +in = inputPin(); diff --git a/tests/buildErrors/importRedefineVar.manifold.error b/tests/buildErrors/importRedefineVar.manifold.error new file mode 100644 index 0000000..a2ddc49 --- /dev/null +++ b/tests/buildErrors/importRedefineVar.manifold.error @@ -0,0 +1 @@ +[in is defined multiple times] \ No newline at end of file diff --git a/tests/buildErrors/importReferencePrivate.manifold b/tests/buildErrors/importReferencePrivate.manifold new file mode 100644 index 0000000..b17967f --- /dev/null +++ b/tests/buildErrors/importReferencePrivate.manifold @@ -0,0 +1,6 @@ +import "../input_pin"; + +digitalIn = primitive port Bool; +outputPin = primitive node (in: digitalIn) -> (Nil); + +outputPin(in=hiddenVar); diff --git a/tests/buildErrors/importReferencePrivate.manifold.error b/tests/buildErrors/importReferencePrivate.manifold.error new file mode 100644 index 0000000..96b273a --- /dev/null +++ b/tests/buildErrors/importReferencePrivate.manifold.error @@ -0,0 +1 @@ +variable 'hiddenVar' not defined in this scope \ No newline at end of file diff --git a/tests/buildErrors/unknownImport.manifold b/tests/buildErrors/unknownImport.manifold new file mode 100644 index 0000000..365c233 --- /dev/null +++ b/tests/buildErrors/unknownImport.manifold @@ -0,0 +1 @@ +import "../hello.manifold"; \ No newline at end of file diff --git a/tests/buildErrors/unknownImport.manifold.error b/tests/buildErrors/unknownImport.manifold.error new file mode 100644 index 0000000..ba3e262 --- /dev/null +++ b/tests/buildErrors/unknownImport.manifold.error @@ -0,0 +1 @@ +Import ../hello.manifold not found \ No newline at end of file diff --git a/tests/input_pin.manifold b/tests/input_pin.manifold new file mode 100644 index 0000000..9d06aaa --- /dev/null +++ b/tests/input_pin.manifold @@ -0,0 +1,13 @@ +public digitalOut = primitive port Bool; +public inputPin = primitive node (Nil) -> (out: digitalOut); + +public in = inputPin(); + +public myfunc = (a: Bool, b: Bool) -> (out: Bool) { + out = a; +}; + +public (x, y) = (inputPin(), inputPin()); + +hiddenVar = 2; +