From e27ac35fab09e95f684e1c080d2931e669b7402a Mon Sep 17 00:00:00 2001 From: Nik Klassen Date: Fri, 20 May 2016 13:54:34 -0400 Subject: [PATCH 1/5] Allow for simple import of other files --- src/main/antlr4/Manifold.g4 | 11 +- .../compiler/front/ExpressionGraph.java | 186 +++++++++++------- .../compiler/front/FunctionValueVertex.java | 8 +- .../org/manifold/compiler/front/Main.java | 65 +++++- .../compiler/front/NodeValueVertex.java | 4 +- .../compiler/front/PrimitivePortVertex.java | 14 +- tests/acceptance/import.manifold | 7 + tests/acceptance/import.manifold.schematic | 53 +++++ tests/input_pin.manifold | 9 + 9 files changed, 256 insertions(+), 101 deletions(-) create mode 100644 tests/acceptance/import.manifold create mode 100644 tests/acceptance/import.manifold.schematic create mode 100644 tests/input_pin.manifold diff --git a/src/main/antlr4/Manifold.g4 b/src/main/antlr4/Manifold.g4 index ff9a8ff..4fae02a 100644 --- a/src/main/antlr4/Manifold.g4 +++ b/src/main/antlr4/Manifold.g4 @@ -17,6 +17,7 @@ LINE_COMMENT: '//' ~[\r\n]* -> skip; INTEGER_VALUE: [0-9]+; BOOLEAN_VALUE: 'false' | 'true'; TYPE_KEYWORD: 'Type'; +STRING_VALUE: '"' ( '\"' | ~["] )*? '"'; tupleTypeValueEntry: (IDENTIFIER ':')? typevalue ('=' expression)?; tupleTypeValue: '(' tupleTypeValueEntry (',' tupleTypeValueEntry)* ')'; @@ -76,7 +77,7 @@ rvalue: | INTEGER_VALUE # Integer | functionValue # Function | reference rvalue # FunctionInvocationExpression // TODO: function invocation needs to be 'reference arglist' - | reference # RValueExpression + | reference # ReferenceExpression | lvalue '=' rvalue # AssignmentExpression | 'primitive' 'port' typevalue (':' tupleTypeValue)? # PrimitivePortDefinitionExpression | 'primitive' 'node' functionTypeValue # PrimitiveNodeDefinitionExpression @@ -88,8 +89,14 @@ lvalue: reference # LValueExpression ; +importExpr: 'import' STRING_VALUE; + // TODO: declarations as expressions -expression: /* declaration | */ rvalue; +expression: + importExpr #ImportExpression + | rvalue #RValueExpression + // | declaration + ; EXPRESSION_TERMINATOR: ';'; diff --git a/src/main/java/org/manifold/compiler/front/ExpressionGraph.java b/src/main/java/org/manifold/compiler/front/ExpressionGraph.java index f48d66e..b3a9975 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 { @@ -29,17 +21,61 @@ public class ExpressionGraph { Set allVertices = new HashSet<>(); private Map variableVertices = - new HashMap<>(); + new HashMap<>(); public Map getVariableVertices() { return ImmutableMap.copyOf(variableVertices); } + public void addSubGraph(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); + }); + + // 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) { + VariableIdentifier ref = ((VariableReferenceVertex) v).getId(); + try { + this.addVertex(ref); + newVertex = this.getVariableVertex(ref); + } catch (MultipleDefinitionException | VariableNotDefinedException e) { + throw Throwables.propagate(e); + } + } 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 +84,7 @@ public VariableReferenceVertex getVariableVertex(VariableIdentifier vID) } public void addVertex(VariableIdentifier vID) - throws MultipleDefinitionException { + throws MultipleDefinitionException { addVertex(new VariableReferenceVertex(this, vID)); } @@ -90,9 +126,9 @@ public void removeVertex(ExpressionVertex v) { private List edges = new ArrayList<>(); public void addEdge(ExpressionEdge e) { Preconditions.checkArgument( - (e.getSource() == null || getVertices().contains(e.getSource())) - && (e.getTarget() == null || getVertices().contains(e.getTarget())), - "Edge had unexpected vertices " + e.toString()); + (e.getSource() == null || getVertices().contains(e.getSource())) + && (e.getTarget() == null || getVertices().contains(e.getTarget())), + "Edge had unexpected vertices " + e.toString()); edges.add(e); } public void removeEdge(ExpressionEdge e) { @@ -158,9 +194,9 @@ public void addFunctionExpressionGraph(ExpressionGraph subGraph, // Sanity checks // input/output vertices exist in mainGraph and subGraph Preconditions.checkArgument(subGraph.getVertices().containsAll( - ImmutableList.of(subGraphInput, subGraphOutput))); + ImmutableList.of(subGraphInput, subGraphOutput))); Preconditions.checkArgument(this.edges.containsAll( - ImmutableList.of(mainGraphInput, mainGraphOutput))); + ImmutableList.of(mainGraphInput, mainGraphOutput))); // input edge and output edge should be connected through the same node (the function invocation) Preconditions.checkArgument(mainGraphInput.getTarget() == mainGraphOutput.getSource()); @@ -172,38 +208,38 @@ 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<>(); // do not add the input/output vertices since they are being replaced by the main graph's vertices 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); - } - } - } else { - newVertex = v.copy(this, exprEdgeMap); - this.addVertex(newVertex); + .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); } - 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 +251,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()); } @@ -247,9 +283,9 @@ public void writeDOTFile(File file) throws IOException { // write all edges for (ExpressionEdge e : edges) { String source = Integer.toString(System.identityHashCode( - e.getSource())); + e.getSource())); String target = Integer.toString(System.identityHashCode( - e.getTarget())); + e.getTarget())); // for now writer.write(source + " -> " + target); if (!e.getName().equals("")) { @@ -269,25 +305,25 @@ 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("}")); + errors.add(error.toString()); + } + }); if (!errors.isEmpty()) { throw new RuntimeException(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..aa74e45 100644 --- a/src/main/java/org/manifold/compiler/front/FunctionValueVertex.java +++ b/src/main/java/org/manifold/compiler/front/FunctionValueVertex.java @@ -150,9 +150,11 @@ 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); + return new FunctionValueVertex(g, edgeMap.get(functionTypeEdge), newBody); } - } diff --git a/src/main/java/org/manifold/compiler/front/Main.java b/src/main/java/org/manifold/compiler/front/Main.java index 0e1dbcd..6d4594f 100644 --- a/src/main/java/org/manifold/compiler/front/Main.java +++ b/src/main/java/org/manifold/compiler/front/Main.java @@ -21,6 +21,7 @@ import java.io.File; import java.io.FileInputStream; +import java.io.IOException; import java.nio.file.Paths; import java.util.*; @@ -163,15 +164,11 @@ public static void elaborateNodes(ExpressionGraph g, Schematic s) } - @Override - public Schematic invokeFrontend(CommandLine cmd) throws Exception { - - File inputFile = Paths.get(cmd.getArgs()[0]).toFile(); - + 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 +181,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 +211,35 @@ public void reportContextSensitivity(@NotNull Parser recognizer, @NotNull DFA df throw new FrontendBuildException(errors.toString()); } - ExpressionContextVisitor graphBuilder = new ExpressionContextVisitor(); + ExpressionGraph exprGraph = new ExpressionGraph(); + + ImportVisitor importVisitor = new ImportVisitor(); + importVisitor.visit(context); + for (String filePath : importVisitor.getImports()) { + File importedFile = new File(inputFile.getParent(), filePath); + ExpressionGraph g = parseFile(importedFile.getCanonicalFile()); + exprGraph.addSubGraph(g); + } + + ExpressionContextVisitor graphBuilder = new ExpressionContextVisitor(exprGraph); List expressionContexts = context.expression(); for (ExpressionContext expressionContext : expressionContexts) { graphBuilder.visit(expressionContext); } - 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(); + ExpressionGraph exprGraph = parseFile(inputFile); exprGraph.verifyVariablesSingleAssignment(); Schematic schematic = new Schematic(inputFile.getName()); @@ -241,6 +257,26 @@ public void reportContextSensitivity(@NotNull Parser recognizer, @NotNull DFA df } } +class ImportVisitor extends ManifoldBaseVisitor { + + private List imports; + public List getImports() { + return this.imports; + } + + public ImportVisitor() { + this.imports = new ArrayList<>(); + } + + @Override + public Void visitImportExpression(ManifoldParser.ImportExpressionContext context) { + String filePath = context.importExpr().STRING_VALUE().getText(); + filePath = filePath.replaceAll("\\\"", "\""); + this.imports.add(filePath.substring(1, filePath.length() - 1)); + return null; + } +} + class ExpressionContextVisitor extends ManifoldBaseVisitor { private ExpressionGraph exprGraph; @@ -251,7 +287,11 @@ public ExpressionGraph getExpressionGraph() { private boolean isLHS = true; private int nextTmpVar = 0; public ExpressionContextVisitor() { - this.exprGraph = new ExpressionGraph(); + this(new ExpressionGraph()); + } + + public ExpressionContextVisitor(ExpressionGraph exprGraph) { + this.exprGraph = exprGraph; } @Override @@ -522,6 +562,11 @@ private ExpressionVertex createVariableVertex(VariableIdentifier id) { } } + @Override + public ExpressionVertex visitImportExpression(ImportExpressionContext context) { + return null; + } + @Override public ExpressionVertex visitTerminal(TerminalNode node) { if (node.getSymbol().getType() == ManifoldLexer.INTEGER_VALUE) { 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/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..4b7f897 --- /dev/null +++ b/tests/acceptance/import.manifold.schematic @@ -0,0 +1,53 @@ +{ + "name": "import.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/input_pin.manifold b/tests/input_pin.manifold new file mode 100644 index 0000000..1a38a31 --- /dev/null +++ b/tests/input_pin.manifold @@ -0,0 +1,9 @@ +digitalOut = primitive port Bool; +inputPin = primitive node (Nil) -> (out: digitalOut); + +in = inputPin(); + +myfunc = (a: Bool, b: Bool) -> (out: Bool) { + out = a; +}; + From 2403a8557d05a407509578169f9add1d79b24b1e Mon Sep 17 00:00:00 2001 From: Nik Klassen Date: Fri, 27 May 2016 14:50:49 -0400 Subject: [PATCH 2/5] Error messages for missing includes and some more tests --- build.gradle | 2 +- .../front/ExpressionContextVisitor.java | 383 +++++++++++++++++ .../compiler/front/ExpressionGraph.java | 88 ++-- .../compiler/front/ImportVisitor.java | 27 ++ .../org/manifold/compiler/front/Main.java | 394 +----------------- .../compiler/front/TupleTypeValueVertex.java | 12 +- .../buildErrors/importFileWithError.manifold | 1 + .../importFileWithError.manifold.error | 1 + tests/buildErrors/unknownImport.manifold | 1 + .../buildErrors/unknownImport.manifold.error | 1 + 10 files changed, 475 insertions(+), 435 deletions(-) create mode 100644 src/main/java/org/manifold/compiler/front/ExpressionContextVisitor.java create mode 100644 src/main/java/org/manifold/compiler/front/ImportVisitor.java create mode 100644 tests/buildErrors/importFileWithError.manifold create mode 100644 tests/buildErrors/importFileWithError.manifold.error create mode 100644 tests/buildErrors/unknownImport.manifold create mode 100644 tests/buildErrors/unknownImport.manifold.error 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/java/org/manifold/compiler/front/ExpressionContextVisitor.java b/src/main/java/org/manifold/compiler/front/ExpressionContextVisitor.java new file mode 100644 index 0000000..977a864 --- /dev/null +++ b/src/main/java/org/manifold/compiler/front/ExpressionContextVisitor.java @@ -0,0 +1,383 @@ +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; + } + + private boolean isLHS = true; + 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; + 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( + 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); + 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 visitImportExpression(ImportExpressionContext 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 b3a9975..c2e18e1 100644 --- a/src/main/java/org/manifold/compiler/front/ExpressionGraph.java +++ b/src/main/java/org/manifold/compiler/front/ExpressionGraph.java @@ -21,7 +21,7 @@ public class ExpressionGraph { Set allVertices = new HashSet<>(); private Map variableVertices = - new HashMap<>(); + new HashMap<>(); public Map getVariableVertices() { return ImmutableMap.copyOf(variableVertices); } @@ -40,22 +40,22 @@ public void addSubGraph(ExpressionGraph subGraph) { // 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) { - VariableIdentifier ref = ((VariableReferenceVertex) v).getId(); - try { - this.addVertex(ref); - newVertex = this.getVariableVertex(ref); - } catch (MultipleDefinitionException | VariableNotDefinedException e) { - throw Throwables.propagate(e); + .forEach(v -> { + ExpressionVertex newVertex; + if (v instanceof VariableReferenceVertex) { + VariableIdentifier ref = ((VariableReferenceVertex) v).getId(); + try { + this.addVertex(ref); + newVertex = this.getVariableVertex(ref); + } catch (MultipleDefinitionException | VariableNotDefinedException e) { + throw Throwables.propagate(e); + } + } else { + newVertex = v.copy(this, exprEdgeMap); + this.addVertex(newVertex); } - } else { - newVertex = v.copy(this, exprEdgeMap); - this.addVertex(newVertex); - } - exprVertexMap.put(v, newVertex); - }); + exprVertexMap.put(v, newVertex); + }); // each edge in subgraph -> edge in main graph should refer to the same source/target subGraph.getEdges().forEach(edge -> { @@ -126,9 +126,9 @@ public void removeVertex(ExpressionVertex v) { private List edges = new ArrayList<>(); public void addEdge(ExpressionEdge e) { Preconditions.checkArgument( - (e.getSource() == null || getVertices().contains(e.getSource())) - && (e.getTarget() == null || getVertices().contains(e.getTarget())), - "Edge had unexpected vertices " + e.toString()); + (e.getSource() == null || getVertices().contains(e.getSource())) + && (e.getTarget() == null || getVertices().contains(e.getTarget())), + "Edge had unexpected vertices " + e.toString()); edges.add(e); } public void removeEdge(ExpressionEdge e) { @@ -194,9 +194,9 @@ public void addFunctionExpressionGraph(ExpressionGraph subGraph, // Sanity checks // input/output vertices exist in mainGraph and subGraph Preconditions.checkArgument(subGraph.getVertices().containsAll( - ImmutableList.of(subGraphInput, subGraphOutput))); + ImmutableList.of(subGraphInput, subGraphOutput))); Preconditions.checkArgument(this.edges.containsAll( - ImmutableList.of(mainGraphInput, mainGraphOutput))); + ImmutableList.of(mainGraphInput, mainGraphOutput))); // input edge and output edge should be connected through the same node (the function invocation) Preconditions.checkArgument(mainGraphInput.getTarget() == mainGraphOutput.getSource()); @@ -218,28 +218,28 @@ public void addFunctionExpressionGraph(ExpressionGraph subGraph, // do not add the input/output vertices since they are being replaced by the main graph's vertices 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); + .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); + } } + } else { + newVertex = v.copy(this, exprEdgeMap); + this.addVertex(newVertex); } - } else { - newVertex = v.copy(this, exprEdgeMap); - this.addVertex(newVertex); - } - exprVertexMap.put(v, newVertex); - }); + exprVertexMap.put(v, newVertex); + }); // Replace the function input vertex with the vertices from the main graph ExpressionVertex inputVertex = mainGraphInput.getSource(); @@ -283,9 +283,9 @@ public void writeDOTFile(File file) throws IOException { // write all edges for (ExpressionEdge e : edges) { String source = Integer.toString(System.identityHashCode( - e.getSource())); + e.getSource())); String target = Integer.toString(System.identityHashCode( - e.getTarget())); + e.getTarget())); // for now writer.write(source + " -> " + target); if (!e.getName().equals("")) { @@ -317,7 +317,7 @@ public void verifyVariablesSingleAssignment() { if (edges.size() != 1) { StringBuilder error = new StringBuilder(); error.append(String.format("Vertex %s has %d incoming edges:", - vertex.toString(), edges.size())); + vertex.toString(), edges.size())); edges.forEach(edge -> error.append(" {") .append(edge.toString()) .append("}")); 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..3fb9d8e --- /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.ImportExpressionContext; + +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 visitImportExpression(ImportExpressionContext context) { + String filePath = context.importExpr().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 6d4594f..b777b48 100644 --- a/src/main/java/org/manifold/compiler/front/Main.java +++ b/src/main/java/org/manifold/compiler/front/Main.java @@ -217,7 +217,13 @@ public void reportContextSensitivity(@NotNull Parser recognizer, @NotNull DFA df importVisitor.visit(context); for (String filePath : importVisitor.getImports()) { File importedFile = new File(inputFile.getParent(), filePath); - ExpressionGraph g = parseFile(importedFile.getCanonicalFile()); + if (!importedFile.exists()) { + importedFile = new File(inputFile.getParent(), filePath + ".manifold"); + if (!importedFile.exists()) { + throw new FrontendBuildException("Import " + filePath + " not found"); + } + } + ExpressionGraph g = parseFile(importedFile); exprGraph.addSubGraph(g); } @@ -238,6 +244,9 @@ public void reportContextSensitivity(@NotNull Parser recognizer, @NotNull DFA df 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(); @@ -256,386 +265,3 @@ public Schematic invokeFrontend(CommandLine cmd) throws Exception { } } - -class ImportVisitor extends ManifoldBaseVisitor { - - private List imports; - public List getImports() { - return this.imports; - } - - public ImportVisitor() { - this.imports = new ArrayList<>(); - } - - @Override - public Void visitImportExpression(ManifoldParser.ImportExpressionContext context) { - String filePath = context.importExpr().STRING_VALUE().getText(); - filePath = filePath.replaceAll("\\\"", "\""); - this.imports.add(filePath.substring(1, filePath.length() - 1)); - return null; - } -} - -class ExpressionContextVisitor extends ManifoldBaseVisitor { - - private ExpressionGraph exprGraph; - public ExpressionGraph getExpressionGraph() { - return this.exprGraph; - } - - private boolean isLHS = true; - private int nextTmpVar = 0; - public ExpressionContextVisitor() { - this(new ExpressionGraph()); - } - - public ExpressionContextVisitor(ExpressionGraph exprGraph) { - this.exprGraph = exprGraph; - } - - @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 visitImportExpression(ImportExpressionContext 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 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/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/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/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 From bf5b0e6cbca586f3a588021c3fc191102f8e9f98 Mon Sep 17 00:00:00 2001 From: Nik Klassen Date: Sun, 29 May 2016 15:07:30 -0400 Subject: [PATCH 3/5] Create new statement rule in the grammar --- src/main/antlr4/Manifold.g4 | 17 ++++++++++------- .../front/ExpressionContextVisitor.java | 2 +- .../manifold/compiler/front/ImportVisitor.java | 6 +++--- .../java/org/manifold/compiler/front/Main.java | 6 +++--- 4 files changed, 17 insertions(+), 14 deletions(-) diff --git a/src/main/antlr4/Manifold.g4 b/src/main/antlr4/Manifold.g4 index 4fae02a..98b7b52 100644 --- a/src/main/antlr4/Manifold.g4 +++ b/src/main/antlr4/Manifold.g4 @@ -28,7 +28,7 @@ tupleValue: '(' ')'; functionTypeValue: tupleTypeValue '->' tupleTypeValue; -functionValue: functionTypeValue '{' (expression EXPRESSION_TERMINATOR)* '}'; +functionValue: functionTypeValue '{' (expression STATEMENT_TERMINATOR)* '}'; //////////////////////////////////////////////////////// // // @@ -89,16 +89,19 @@ lvalue: reference # LValueExpression ; -importExpr: 'import' STRING_VALUE; - // TODO: declarations as expressions expression: - importExpr #ImportExpression - | rvalue #RValueExpression + rvalue // | declaration ; -EXPRESSION_TERMINATOR: ';'; +statement: + expression #ExpressionStatment + | 'import' STRING_VALUE #ImportStatement + ; + +STATEMENT_TERMINATOR: ';'; + //////////////////////////////////////////////////////// // // @@ -106,4 +109,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 index 977a864..2676e5b 100644 --- a/src/main/java/org/manifold/compiler/front/ExpressionContextVisitor.java +++ b/src/main/java/org/manifold/compiler/front/ExpressionContextVisitor.java @@ -304,7 +304,7 @@ private ExpressionVertex createVariableVertex(VariableIdentifier id) { } @Override - public ExpressionVertex visitImportExpression(ImportExpressionContext context) { + public ExpressionVertex visitImportStatement(ImportStatementContext context) { return null; } diff --git a/src/main/java/org/manifold/compiler/front/ImportVisitor.java b/src/main/java/org/manifold/compiler/front/ImportVisitor.java index 3fb9d8e..75935dc 100644 --- a/src/main/java/org/manifold/compiler/front/ImportVisitor.java +++ b/src/main/java/org/manifold/compiler/front/ImportVisitor.java @@ -1,7 +1,7 @@ package org.manifold.compiler.front; import org.manifold.parser.ManifoldBaseVisitor; -import org.manifold.parser.ManifoldParser.ImportExpressionContext; +import org.manifold.parser.ManifoldParser.ImportStatementContext; import java.util.ArrayList; import java.util.List; @@ -18,8 +18,8 @@ public ImportVisitor() { } @Override - public Void visitImportExpression(ImportExpressionContext context) { - String filePath = context.importExpr().STRING_VALUE().getText(); + 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 b777b48..330fa2e 100644 --- a/src/main/java/org/manifold/compiler/front/Main.java +++ b/src/main/java/org/manifold/compiler/front/Main.java @@ -228,9 +228,9 @@ public void reportContextSensitivity(@NotNull Parser recognizer, @NotNull DFA df } ExpressionContextVisitor graphBuilder = new ExpressionContextVisitor(exprGraph); - List expressionContexts = context.expression(); - for (ExpressionContext expressionContext : expressionContexts) { - graphBuilder.visit(expressionContext); + List statementContexts = context.statement(); + for (StatementContext statementContext : statementContexts) { + graphBuilder.visit(statementContext); } exprGraph = graphBuilder.getExpressionGraph(); From 785e31bd2725e4da58e65077ed2b44f733ef3bd9 Mon Sep 17 00:00:00 2001 From: Nik Klassen Date: Sat, 4 Jun 2016 11:32:37 -0400 Subject: [PATCH 4/5] Support public and private variables in manifold file --- src/main/antlr4/Manifold.g4 | 4 +- .../front/ExpressionContextVisitor.java | 13 +++- .../compiler/front/ExpressionGraph.java | 32 ++++++--- .../compiler/front/FunctionValueVertex.java | 3 +- .../org/manifold/compiler/front/Main.java | 2 +- .../front/VariableNotDefinedException.java | 9 +-- .../front/VariableReferenceVertex.java | 31 ++++++--- tests/acceptance/import.manifold.schematic | 26 +++++-- tests/acceptance/importTuple.manifold | 6 ++ .../acceptance/importTuple.manifold.schematic | 67 +++++++++++++++++++ tests/buildErrors/importRedefineVar.manifold | 3 + .../importRedefineVar.manifold.error | 1 + .../importReferencePrivate.manifold | 6 ++ .../importReferencePrivate.manifold.error | 1 + tests/input_pin.manifold | 12 ++-- 15 files changed, 177 insertions(+), 39 deletions(-) create mode 100644 tests/acceptance/importTuple.manifold create mode 100644 tests/acceptance/importTuple.manifold.schematic create mode 100644 tests/buildErrors/importRedefineVar.manifold create mode 100644 tests/buildErrors/importRedefineVar.manifold.error create mode 100644 tests/buildErrors/importReferencePrivate.manifold create mode 100644 tests/buildErrors/importReferencePrivate.manifold.error diff --git a/src/main/antlr4/Manifold.g4 b/src/main/antlr4/Manifold.g4 index 98b7b52..f1f4594 100644 --- a/src/main/antlr4/Manifold.g4 +++ b/src/main/antlr4/Manifold.g4 @@ -19,6 +19,8 @@ BOOLEAN_VALUE: 'false' | 'true'; TYPE_KEYWORD: 'Type'; STRING_VALUE: '"' ( '\"' | ~["] )*? '"'; +VISIBILITY_PUBLIC: 'public'; + tupleTypeValueEntry: (IDENTIFIER ':')? typevalue ('=' expression)?; tupleTypeValue: '(' tupleTypeValueEntry (',' tupleTypeValueEntry)* ')'; @@ -78,7 +80,7 @@ rvalue: | functionValue # Function | reference rvalue # FunctionInvocationExpression // TODO: function invocation needs to be 'reference arglist' | reference # ReferenceExpression - | lvalue '=' rvalue # AssignmentExpression + | VISIBILITY_PUBLIC? lvalue '=' rvalue # AssignmentExpression | 'primitive' 'port' typevalue (':' tupleTypeValue)? # PrimitivePortDefinitionExpression | 'primitive' 'node' functionTypeValue # PrimitiveNodeDefinitionExpression ; diff --git a/src/main/java/org/manifold/compiler/front/ExpressionContextVisitor.java b/src/main/java/org/manifold/compiler/front/ExpressionContextVisitor.java index 2676e5b..fb5c782 100644 --- a/src/main/java/org/manifold/compiler/front/ExpressionContextVisitor.java +++ b/src/main/java/org/manifold/compiler/front/ExpressionContextVisitor.java @@ -25,7 +25,12 @@ 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()); @@ -41,10 +46,12 @@ public ExpressionVertex visitAssignmentExpression( // 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); @@ -262,7 +269,11 @@ public ExpressionVertex visitNamespacedIdentifier( NamespacedIdentifierContext context) { // keeping in mind that we may have constructed this variable already... VariableIdentifier id = getVariableIdentifier(context); - return createVariableVertex(id); + ExpressionVertex v = createVariableVertex(id); + if (v instanceof VariableReferenceVertex && isLHS) { + ((VariableReferenceVertex) v).setExported(isPublic); + } + return v; } private ExpressionVertex createVariableVertex(VariableIdentifier id) { diff --git a/src/main/java/org/manifold/compiler/front/ExpressionGraph.java b/src/main/java/org/manifold/compiler/front/ExpressionGraph.java index c2e18e1..c40079a 100644 --- a/src/main/java/org/manifold/compiler/front/ExpressionGraph.java +++ b/src/main/java/org/manifold/compiler/front/ExpressionGraph.java @@ -26,7 +26,12 @@ public Map getVariableVertices() { return ImmutableMap.copyOf(variableVertices); } - public void addSubGraph(ExpressionGraph subGraph) { + /** + * 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 -> { @@ -43,12 +48,20 @@ public void addSubGraph(ExpressionGraph subGraph) { .forEach(v -> { ExpressionVertex newVertex; if (v instanceof VariableReferenceVertex) { - VariableIdentifier ref = ((VariableReferenceVertex) v).getId(); - try { - this.addVertex(ref); - newVertex = this.getVariableVertex(ref); - } catch (MultipleDefinitionException | VariableNotDefinedException e) { - throw Throwables.propagate(e); + 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); @@ -321,12 +334,13 @@ public void verifyVariablesSingleAssignment() { edges.forEach(edge -> error.append(" {") .append(edge.toString()) .append("}")); - errors.add(error.toString()); + 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 aa74e45..852dc0f 100644 --- a/src/main/java/org/manifold/compiler/front/FunctionValueVertex.java +++ b/src/main/java/org/manifold/compiler/front/FunctionValueVertex.java @@ -153,7 +153,8 @@ public ExpressionVertex copy(ExpressionGraph g, // 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); + newBody.addSubGraph(functionBody, false); + return new FunctionValueVertex(g, edgeMap.get(functionTypeEdge), newBody); } } diff --git a/src/main/java/org/manifold/compiler/front/Main.java b/src/main/java/org/manifold/compiler/front/Main.java index 330fa2e..7ac9beb 100644 --- a/src/main/java/org/manifold/compiler/front/Main.java +++ b/src/main/java/org/manifold/compiler/front/Main.java @@ -224,7 +224,7 @@ public void reportContextSensitivity(@NotNull Parser recognizer, @NotNull DFA df } } ExpressionGraph g = parseFile(importedFile); - exprGraph.addSubGraph(g); + exprGraph.addSubGraph(g, true); } ExpressionContextVisitor graphBuilder = new ExpressionContextVisitor(exprGraph); 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/tests/acceptance/import.manifold.schematic b/tests/acceptance/import.manifold.schematic index 4b7f897..ce4c775 100644 --- a/tests/acceptance/import.manifold.schematic +++ b/tests/acceptance/import.manifold.schematic @@ -12,16 +12,16 @@ } }, "nodeTypes": { - "outputPin": { + "inputPin": { "attributes": {}, "ports": { - "in": "digitalIn" + "out": "digitalOut" } }, - "inputPin": { + "outputPin": { "attributes": {}, "ports": { - "out": "digitalOut" + "in": "digitalIn" } } }, @@ -35,6 +35,20 @@ } }, "n2": { + "type": "inputPin", + "attributes": {}, + "portAttrs": { + "out": {} + } + }, + "n3": { + "type": "inputPin", + "attributes": {}, + "portAttrs": { + "out": {} + } + }, + "n4": { "type": "outputPin", "attributes": {}, "portAttrs": { @@ -45,8 +59,8 @@ "connections": { "c1": { "attributes": {}, - "from": "n1:out", - "to": "n2:in" + "from": "n2:out", + "to": "n4:in" } }, "constraints": {} 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/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/input_pin.manifold b/tests/input_pin.manifold index 1a38a31..9d06aaa 100644 --- a/tests/input_pin.manifold +++ b/tests/input_pin.manifold @@ -1,9 +1,13 @@ -digitalOut = primitive port Bool; -inputPin = primitive node (Nil) -> (out: digitalOut); +public digitalOut = primitive port Bool; +public inputPin = primitive node (Nil) -> (out: digitalOut); -in = inputPin(); +public in = inputPin(); -myfunc = (a: Bool, b: Bool) -> (out: Bool) { +public myfunc = (a: Bool, b: Bool) -> (out: Bool) { out = a; }; +public (x, y) = (inputPin(), inputPin()); + +hiddenVar = 2; + From 9f54df6a6c3ff2cfa8a89d50660e252f2337d618 Mon Sep 17 00:00:00 2001 From: Nik Klassen Date: Sat, 4 Jun 2016 17:39:46 -0400 Subject: [PATCH 5/5] Add system libraries --- .../org/manifold/compiler/front/Main.java | 32 ++++++++--- .../resources/libraries/circuits.manifold | 5 ++ tests/acceptance/importSystem.manifold | 4 ++ .../importSystem.manifold.schematic | 53 +++++++++++++++++++ 4 files changed, 87 insertions(+), 7 deletions(-) create mode 100644 src/main/resources/libraries/circuits.manifold create mode 100644 tests/acceptance/importSystem.manifold create mode 100644 tests/acceptance/importSystem.manifold.schematic diff --git a/src/main/java/org/manifold/compiler/front/Main.java b/src/main/java/org/manifold/compiler/front/Main.java index 7ac9beb..102a8ff 100644 --- a/src/main/java/org/manifold/compiler/front/Main.java +++ b/src/main/java/org/manifold/compiler/front/Main.java @@ -22,8 +22,10 @@ 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 { @@ -164,6 +166,14 @@ public static void elaborateNodes(ExpressionGraph g, Schematic s) } + 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))); @@ -216,13 +226,21 @@ public void reportContextSensitivity(@NotNull Parser recognizer, @NotNull DFA df ImportVisitor importVisitor = new ImportVisitor(); importVisitor.visit(context); for (String filePath : importVisitor.getImports()) { - File importedFile = new File(inputFile.getParent(), filePath); - if (!importedFile.exists()) { - importedFile = new File(inputFile.getParent(), filePath + ".manifold"); - if (!importedFile.exists()) { - throw new FrontendBuildException("Import " + filePath + " not found"); - } - } + 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); } 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/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