diff --git a/build.gradle b/build.gradle index 3d5cf4c..75dd82a 100644 --- a/build.gradle +++ b/build.gradle @@ -12,7 +12,7 @@ targetCompatibility = 1.8 group = "org.manifold" // name = "manifold-frontend" -version = '0.3.1-SNAPSHOT' +version = '0.4.0-SNAPSHOT' mainClassName = "org.manifold.compiler.front.Main" diff --git a/src/main/java/org/manifold/compiler/front/ExpressionEdge.java b/src/main/java/org/manifold/compiler/front/ExpressionEdge.java index d2a6748..f4c39b1 100644 --- a/src/main/java/org/manifold/compiler/front/ExpressionEdge.java +++ b/src/main/java/org/manifold/compiler/front/ExpressionEdge.java @@ -1,9 +1,12 @@ package org.manifold.compiler.front; -import org.manifold.compiler.UndefinedBehaviourError; +import org.apache.log4j.LogManager; +import org.apache.log4j.Logger; public class ExpressionEdge { + private static Logger log = LogManager.getLogger(ExpressionEdge.class); + private String name = ""; public String getName() { return name; @@ -30,8 +33,7 @@ public void setTarget(ExpressionVertex newTarget) { public ExpressionEdge(ExpressionVertex from, ExpressionVertex to) { if (from == null && to == null) { - throw new UndefinedBehaviourError( - "attempt to create a totally disconnected ExpressionEdge"); + log.warn("attempt to create a totally disconnected ExpressionEdge"); } source = from; target = to; diff --git a/src/main/java/org/manifold/compiler/front/ExpressionGraph.java b/src/main/java/org/manifold/compiler/front/ExpressionGraph.java index 114d20b..167fe15 100644 --- a/src/main/java/org/manifold/compiler/front/ExpressionGraph.java +++ b/src/main/java/org/manifold/compiler/front/ExpressionGraph.java @@ -13,11 +13,12 @@ import java.util.Map; import java.util.Set; -import com.google.common.base.Throwables; 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; @@ -32,11 +33,11 @@ public class ExpressionGraph { public Map getVariableVertices() { return ImmutableMap.copyOf(variableVertices); } - + public boolean containsVariable(VariableIdentifier vID) { return variableVertices.containsKey(vID); } - + public VariableReferenceVertex getVariableVertex(VariableIdentifier vID) throws VariableNotDefinedException { if (variableVertices.containsKey(vID)) { @@ -62,6 +63,9 @@ public List getNonVariableVertices() { return ImmutableList.copyOf(nonVariableVertices); } public void addVertex(ExpressionVertex v) { + if (v == null) { + throw new UndefinedBehaviourError("attempt to add null vertex to graph"); + } nonVariableVertices.add(v); allVertices.add(v); } @@ -75,12 +79,13 @@ public void removeVertex(ExpressionVertex v) { while (variableVertices.values().remove(v)) { } // simple removal from nonVariableVertices nonVariableVertices.remove(v); + allVertices.remove(v); } - + private List edges = new ArrayList<>(); public void addEdge(ExpressionEdge e) { Preconditions.checkArgument( - getVertices().contains(e.getSource()) + (e.getSource() == null || getVertices().contains(e.getSource())) && (e.getTarget() == null || getVertices().contains(e.getTarget())), "Edge had unexpected vertices " + e.toString()); edges.add(e); @@ -142,7 +147,8 @@ public ExpressionGraph() { */ public void addSubExpressionGraph(ExpressionGraph subGraph, ExpressionEdge mainGraphInput, ExpressionVertex subGraphInput, - ExpressionEdge mainGraphOutput, ExpressionVertex subGraphOutput) { + ExpressionEdge mainGraphOutput, ExpressionVertex subGraphOutput, + Map variableRenamingMap) { // Sanity checks // input/output vertices exist in mainGraph and subGraph @@ -158,15 +164,11 @@ public void addSubExpressionGraph(ExpressionGraph subGraph, this.allVertices.remove(mainGraphInput.getTarget()); this.edges.removeAll(ImmutableList.of(mainGraphInput, mainGraphOutput)); - ExpressionVertex inputVertex = mainGraphInput.getSource(); - ExpressionVertex outputVertex = mainGraphOutput.getTarget(); - // map of subgraph edge -> new edge to be inserted Map exprEdgeMap = new HashMap<>(); subGraph.getEdges().forEach(e -> { - // copy source/target for now since ExpressionEdge doesn't like creating with null on both - // they will be replaced with the correct vertices later - ExpressionEdge newEdge = new ExpressionEdge(e.getSource(), e.getTarget()); + // replace these with the correct vertices later + ExpressionEdge newEdge = new ExpressionEdge(null, null); exprEdgeMap.put(e, newEdge); }); @@ -179,14 +181,17 @@ public void addSubExpressionGraph(ExpressionGraph subGraph, .forEach(v -> { ExpressionVertex newVertex; if (v instanceof VariableReferenceVertex) { - // special case - // TODO: probably can handle renaming here? - VariableIdentifier ref = ((VariableReferenceVertex) v).getId(); - try { - this.addVertex(ref); - newVertex = this.getVariableVertex(ref); - } catch (MultipleDefinitionException | VariableNotDefinedException e) { - throw Throwables.propagate(e); + // 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); @@ -196,13 +201,19 @@ public void addSubExpressionGraph(ExpressionGraph subGraph, }); // input/output vertex + ExpressionVertex inputVertex = mainGraphInput.getSource(); + ExpressionVertex outputVertex = mainGraphOutput.getTarget(); exprVertexMap.put(subGraphInput, inputVertex); exprVertexMap.put(subGraphOutput, outputVertex); // each edge in subgraph -> edge in main graph should refer to the same source/target subGraph.getEdges().forEach(edge -> { - exprEdgeMap.get(edge).setSource(exprVertexMap.get(edge.getSource())); - 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()); @@ -275,5 +286,5 @@ public void verifyVariablesSingleAssignment() { throw new RuntimeException(errors.toString()); } } - + } diff --git a/src/main/java/org/manifold/compiler/front/FunctionInvocationVertex.java b/src/main/java/org/manifold/compiler/front/FunctionInvocationVertex.java index 67a2b99..4437575 100644 --- a/src/main/java/org/manifold/compiler/front/FunctionInvocationVertex.java +++ b/src/main/java/org/manifold/compiler/front/FunctionInvocationVertex.java @@ -2,9 +2,13 @@ import java.io.BufferedWriter; import java.io.IOException; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; import java.util.Map; +import java.util.Map.Entry; +import java.util.Random; -import com.google.common.base.Preconditions; import org.apache.log4j.LogManager; import org.apache.log4j.Logger; import org.manifold.compiler.NodeTypeValue; @@ -12,6 +16,8 @@ import org.manifold.compiler.UndefinedBehaviourError; import org.manifold.compiler.Value; +import com.google.common.base.Preconditions; + public class FunctionInvocationVertex extends ExpressionVertex { private static Logger log = LogManager.getLogger("FunctionInvocationVertex"); @@ -107,9 +113,74 @@ private void elaborateNodeInstantiation(Value function, // now remove this vertex from the graph getExpressionGraph().removeVertex(this); } + + private void elaborateNonPrimitiveFunction(Value f, + ExpressionVertex input) throws Exception { + log.debug("function invocation is non-primitive elaboration"); + FunctionValue function = (FunctionValue) f; + FunctionTypeValue signature = (FunctionTypeValue) f.getType(); + TupleTypeValue inputType = (TupleTypeValue) signature.getInputType(); + TupleTypeValue outputType = (TupleTypeValue) signature.getOutputType(); + // calculate renaming map (body -> this.exprGraph) + Map renamingMap = + new HashMap<>(); + for (Entry entry + : function.getBody().getVariableVertices().entrySet()) { + VariableReferenceVertex varTarget; + if (getExpressionGraph().containsVariable(entry.getKey())) { + // if the main graph contains this variable, don't rename; use existing + log.debug("preserving variable " + entry.getKey()); + varTarget = getExpressionGraph().getVariableVertex(entry.getKey()); + } else { + // otherwise, create a fresh variable in the main graph and map to that + String oldName = entry.getKey().getName(); + Random rng = new Random(); + while (true) { + // choose a random suffix and mangle + Integer i = rng.nextInt(Integer.MAX_VALUE); + String newName = oldName + "_" + i.toString(); + List newNames = new ArrayList<>(); + newNames.add(newName); + VariableIdentifier newID = new VariableIdentifier(newNames); + if (!getExpressionGraph().containsVariable(newID)) { + log.debug("renaming local variable " + entry.getKey() + + " to " + newID); + getExpressionGraph().addVertex(newID); + varTarget = getExpressionGraph().getVariableVertex(newID); + break; + } + } + } + renamingMap.put(entry.getValue(), varTarget); + } + // identify main graph input and output edges + // we assume there's exactly one of each + ExpressionEdge mainGraphInput = inputEdge; + log.debug("main graph input is " + mainGraphInput); + ExpressionEdge mainGraphOutput = null; + for (ExpressionEdge e : getExpressionGraph().getEdgesFromSource(this)) { + mainGraphOutput = e; + break; + } + log.debug("main graph output is " + mainGraphOutput); + // identify subgraph (body) input and output vertices + ExpressionVertex subGraphInput = function.getInputVertex(); + ExpressionVertex subGraphOutput = function.getOutputVertex(); + // perform copy + getExpressionGraph().addSubExpressionGraph(function.getBody(), + mainGraphInput, subGraphInput, mainGraphOutput, subGraphOutput, + renamingMap); + // destroy the function edge + getExpressionGraph().removeEdge(functionEdge); + // now remove this vertex from the graph + getExpressionGraph().removeVertex(this); + } + + private boolean elaborated = false; @Override public void elaborate() throws Exception { + if (elaborated) return; // Elaborate argument ExpressionVertex vInput = inputEdge.getSource(); vInput.elaborate(); @@ -121,10 +192,14 @@ public void elaborate() throws Exception { if (function instanceof NodeTypeValue) { // we're not calling a function; we're instantiating a node! elaborateNodeInstantiation(function, vFunction); + } else if (function instanceof FunctionValue) { + // non-primitive function elaboration + elaborateNonPrimitiveFunction(function, vInput); } else { throw new UndefinedBehaviourError("don't know how to invoke '" + function.toString() + "'"); } + elaborated = true; } } diff --git a/src/main/java/org/manifold/compiler/front/FunctionValue.java b/src/main/java/org/manifold/compiler/front/FunctionValue.java index 8c427b4..ef81882 100644 --- a/src/main/java/org/manifold/compiler/front/FunctionValue.java +++ b/src/main/java/org/manifold/compiler/front/FunctionValue.java @@ -1,20 +1,23 @@ package org.manifold.compiler.front; -import java.util.List; - import org.manifold.compiler.SchematicValueVisitor; import org.manifold.compiler.UndefinedBehaviourError; import org.manifold.compiler.Value; public class FunctionValue extends Value { - + private ExpressionGraph body; - - public FunctionValue(FunctionTypeValue type, ExpressionGraph body) { + private TupleValueVertex vInput; + private TupleValueVertex vOutput; + + public FunctionValue(FunctionTypeValue type, ExpressionGraph body, + TupleValueVertex vInput, TupleValueVertex vOutput) { super(type); this.body = body; + this.vInput = vInput; + this.vOutput = vOutput; } - + @Override public void verify() throws Exception { // TODO introspect body to make sure it treats input and output values @@ -24,21 +27,29 @@ public void verify() throws Exception { public ExpressionGraph getBody() { return body; } - + + public TupleValueVertex getInputVertex() { + return vInput; + } + + public TupleValueVertex getOutputVertex() { + return vOutput; + } + @Override public boolean isRuntimeKnowable() { return false; } - + @Override public boolean isElaborationtimeKnowable() { return true; } - + @Override public void accept(SchematicValueVisitor v) { throw new UndefinedBehaviourError( "cannot accept non-frontend ValueVisitor into a frontend Value"); } - + } diff --git a/src/main/java/org/manifold/compiler/front/FunctionValueVertex.java b/src/main/java/org/manifold/compiler/front/FunctionValueVertex.java new file mode 100644 index 0000000..26a577c --- /dev/null +++ b/src/main/java/org/manifold/compiler/front/FunctionValueVertex.java @@ -0,0 +1,158 @@ +package org.manifold.compiler.front; + +import java.io.BufferedWriter; +import java.io.IOException; +import java.util.HashMap; +import java.util.Map; + +import org.apache.log4j.LogManager; +import org.apache.log4j.Logger; +import org.manifold.compiler.TypeTypeValue; +import org.manifold.compiler.TypeValue; +import org.manifold.compiler.Value; + +public class FunctionValueVertex extends ExpressionVertex { + + private static Logger log = LogManager.getLogger(FunctionValueVertex.class); + + private final ExpressionEdge functionTypeEdge; + public ExpressionEdge getFunctionTypeEdge() { + return this.functionTypeEdge; + } + + private final ExpressionGraph functionBody; + public ExpressionGraph getFunctionBody() { + return this.functionBody; + } + + public FunctionValueVertex(ExpressionGraph exprGraph, + ExpressionEdge functionTypeEdge, ExpressionGraph functionBody) { + super(exprGraph); + this.functionTypeEdge = functionTypeEdge; + this.functionTypeEdge.setTarget(this); + this.functionTypeEdge.setName("type"); + this.functionBody = functionBody; + } + + private FunctionTypeValue type = null; + + @Override + public TypeValue getType() { + // this is a function, so return a function type + return this.type; + } + + private FunctionValue function = null; + @Override + public Value getValue() { + return this.function; + } + + @Override + public void elaborate() throws Exception { + if (function != null) { + return; + } + log.debug("elaborating function definition"); + ExpressionVertex typeVertex = functionTypeEdge.getSource(); + typeVertex.elaborate(); + if (!(typeVertex.getValue() instanceof FunctionTypeValue)) { + TypeValue typeConstructor = new FunctionTypeValue( + TypeTypeValue.getInstance(), TypeTypeValue.getInstance()); + throw new TypeMismatchException( + typeConstructor, typeVertex.getValue()); + } + type = (FunctionTypeValue) typeVertex.getValue(); + // inject input/output TupleValues + TupleTypeValue inputType = (TupleTypeValue) type.getInputType(); + TupleTypeValue outputType = (TupleTypeValue) type.getOutputType(); + Map inputEdges = new HashMap<>(); + Map outputEdges = new HashMap<>(); + for (String argName : inputType.getSubtypes().keySet()) { + ExpressionEdge e = new ExpressionEdge(null, null); // I know what I'm doing + functionBody.addEdge(e); + inputEdges.put(argName, e); + } + for (String argName : outputType.getSubtypes().keySet()) { + // "name resolution": look for a variable reference vertex with this name in the subgraph + for (Map.Entry vRef + : functionBody.getVariableVertices().entrySet()) { + if (vRef.getKey().getName().equals(argName)) { + ExpressionEdge e = new ExpressionEdge(vRef.getValue(), null); + functionBody.addEdge(e); + outputEdges.put(argName, e); + break; + } + } + } + TupleValueVertex vInput = new TupleValueVertex(functionBody, inputEdges); + functionBody.addVertex(vInput); + TupleValueVertex vOutput = new TupleValueVertex(functionBody, outputEdges); + functionBody.addVertex(vOutput); + function = new FunctionValue(type, functionBody, vInput, vOutput); + // inject StaticAttributeAccesses to assign each variable from the input + // TODO we're making a few guesses about "name resolution" here + for (Map.Entry entry + : functionBody.getVariableVertices().entrySet()) { + String varName = entry.getKey().getName(); + if (inputType.getSubtypes().containsKey(varName)) { + ExpressionEdge eInput = new ExpressionEdge(vInput, null); + functionBody.addEdge(eInput); + StaticAttributeAccessVertex vAccess = new StaticAttributeAccessVertex( + functionBody, eInput, varName); + functionBody.addVertex(vAccess); + ExpressionEdge eAssign = new ExpressionEdge(vAccess, entry.getValue()); + functionBody.addEdge(eAssign); + } + } + } + + @Override + public void verify() throws Exception { + // TODO Auto-generated method stub + + } + + @Override + public boolean isElaborationtimeKnowable() { + return true; + } + + @Override + public boolean isRuntimeKnowable() { + return false; + } + + @Override + public String toString() { + if (function == null) { + return "function value (not elaborated)"; + } else { + return "function value " + type.toString(); + } + } + + @Override + public void writeToDOTFile(BufferedWriter writer) throws IOException { + String objectID = Integer.toString(System.identityHashCode(this)); + String label = this.toString(); + writer.write(objectID); + writer.write(" ["); + writer.write("label=\""); + writer.write(objectID); + writer.write("\n"); + writer.write(label); + writer.write("\""); + writer.write("];"); + writer.newLine(); + } + + @Override + public ExpressionVertex copy(ExpressionGraph g, + Map edgeMap) { + // TODO Auto-generated method stub + 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 217e90b..06f5f48 100644 --- a/src/main/java/org/manifold/compiler/front/Main.java +++ b/src/main/java/org/manifold/compiler/front/Main.java @@ -62,7 +62,6 @@ public void registerArguments(Options options) { public static void elaborateFunctions(ExpressionGraph g) throws Exception { // Maintain a set of unelaborated function invocations and // iterate until this set is empty. - Set funcalls = new LinkedHashSet<>(); // Add all function invocations initially present in the graph for (ExpressionVertex v : g.getNonVariableVertices()) { @@ -70,7 +69,7 @@ public static void elaborateFunctions(ExpressionGraph g) throws Exception { funcalls.add((FunctionInvocationVertex) v); } } - + int step = 1; // now proceed while (!(funcalls.isEmpty())) { // get next vertex @@ -80,6 +79,11 @@ public static void elaborateFunctions(ExpressionGraph g) throws Exception { log.debug("elaborating function " + Integer.toString(System.identityHashCode(v))); v.elaborate(); + log.debug("writing out expression graph at function elaboration step " + + step); + File elaboratedDot = new File("tmp.elaborated.step" + step + ".dot"); + g.writeDOTFile(elaboratedDot); + step++; // TODO it would be more efficient for the vertex to tell us whether // any new function invocations were created during elaboration for (ExpressionVertex vNew : g.getNonVariableVertices()) { @@ -204,9 +208,9 @@ public Schematic invokeFrontend(CommandLine cmd) throws Exception { exprGraph.writeDOTFile(exprGraphDot); exprGraph.verifyVariablesSingleAssignment(); - + Schematic schematic = new Schematic(inputFile.getName()); - + elaborateFunctions(exprGraph); log.debug("writing out expression graph after function elaboration"); File elaboratedDot = new File(inputFile.getName() + ".elaborated.dot"); @@ -226,11 +230,11 @@ class ExpressionContextVisitor extends ManifoldBaseVisitor { public ExpressionGraph getExpressionGraph() { return this.exprGraph; } - + public ExpressionContextVisitor() { this.exprGraph = new ExpressionGraph(); } - + @Override public ExpressionVertex visitAssignmentExpression( ManifoldParser.AssignmentExpressionContext context) { @@ -252,7 +256,7 @@ public ExpressionVertex visitFunctionInvocationExpression( // then get the input vertex ExpressionVertex vInput = context.expression(1).accept(this); ExpressionEdge eInput = new ExpressionEdge(vInput, null); - + FunctionInvocationVertex vInvocation = new FunctionInvocationVertex( exprGraph, eFunction, eInput); exprGraph.addVertex(vInvocation); @@ -261,6 +265,26 @@ public ExpressionVertex visitFunctionInvocationExpression( 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) @@ -296,7 +320,7 @@ public ExpressionVertex visitPrimitivePortDefinitionExpression( if (context.tupleTypeValue() != null) { vAttributes = context.tupleTypeValue().accept(this); } else { - vAttributes = new ConstantValueVertex(exprGraph, + vAttributes = new ConstantValueVertex(exprGraph, NilTypeValue.getInstance()); } exprGraph.addVertex(vAttributes); @@ -376,7 +400,7 @@ public ExpressionVertex visitFunctionTypeValue( // 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); diff --git a/src/main/java/org/manifold/compiler/front/StaticAttributeAccessVertex.java b/src/main/java/org/manifold/compiler/front/StaticAttributeAccessVertex.java new file mode 100644 index 0000000..38d6b21 --- /dev/null +++ b/src/main/java/org/manifold/compiler/front/StaticAttributeAccessVertex.java @@ -0,0 +1,111 @@ +package org.manifold.compiler.front; + +import java.io.BufferedWriter; +import java.io.IOException; +import java.util.Map; + +import org.apache.log4j.LogManager; +import org.apache.log4j.Logger; +import org.manifold.compiler.TypeValue; +import org.manifold.compiler.Value; + +import com.google.common.base.Preconditions; + +public class StaticAttributeAccessVertex extends ExpressionVertex { + + private static Logger log = LogManager.getLogger( + "StaticAttributeAccessVertex"); + + private final ExpressionEdge exprEdge; + public ExpressionEdge getExpressionEdge() { + return this.exprEdge; + } + + private final String attributeID; + public String getAttributeID() { + return this.attributeID; + } + + public StaticAttributeAccessVertex(ExpressionGraph exprGraph, + ExpressionEdge exprEdge, String attrID) { + super(exprGraph); + this.exprEdge = exprEdge; + this.exprEdge.setTarget(this); + this.exprEdge.setName("expr"); + this.attributeID = attrID; + } + + private TypeValue type = null; + + @Override + public TypeValue getType() { + return type; + } + + private Value value = null; + + @Override + public Value getValue() { + return this.value; + } + + @Override + public void elaborate() throws Exception { + log.debug("elaborating static attribute access"); + ExpressionVertex vExpr = exprEdge.getSource(); + vExpr.elaborate(); + Value val = vExpr.getValue(); + TupleValue tupleValue = (TupleValue) val; + this.value = tupleValue.entry(attributeID); + this.type = this.value.getType(); + } + + @Override + public void verify() throws Exception { + // TODO Auto-generated method stub + + } + + @Override + public boolean isElaborationtimeKnowable() { + return true; + } + + @Override + public boolean isRuntimeKnowable() { + return true; + } + + @Override + public String toString() { + String retval = "attribute[ " + attributeID + " ]"; + if (this.value == null) { + retval += " (not elaborated)"; + } + return retval; + } + + @Override + public void writeToDOTFile(BufferedWriter writer) throws IOException { + String objectID = Integer.toString(System.identityHashCode(this)); + String label = this.toString(); + writer.write(objectID); + writer.write(" ["); + writer.write("label=\""); + writer.write(objectID); + writer.write("\n"); + writer.write(label); + writer.write("\""); + writer.write("];"); + writer.newLine(); + } + + @Override + public ExpressionVertex copy(ExpressionGraph g, + Map edgeMap) { + Preconditions.checkArgument(edgeMap.containsKey(exprEdge)); + return new StaticAttributeAccessVertex(g, + edgeMap.get(exprEdge), attributeID); + } + +} diff --git a/src/main/java/org/manifold/compiler/front/TupleValueVertex.java b/src/main/java/org/manifold/compiler/front/TupleValueVertex.java index 77fda80..dfad370 100644 --- a/src/main/java/org/manifold/compiler/front/TupleValueVertex.java +++ b/src/main/java/org/manifold/compiler/front/TupleValueVertex.java @@ -5,17 +5,17 @@ import java.util.HashMap; import java.util.Map; -import com.google.common.base.Preconditions; import org.apache.log4j.LogManager; import org.apache.log4j.Logger; import org.manifold.compiler.TypeValue; import org.manifold.compiler.Value; +import com.google.common.base.Preconditions; import com.google.common.collect.ImmutableMap; public class TupleValueVertex extends ExpressionVertex { - private static Logger log = LogManager.getLogger("FunctionInvocationVertex"); + private static Logger log = LogManager.getLogger("TupleValueVertex"); private TupleValue value = null; diff --git a/src/test/java/org/manifold/compiler/front/TestExpressionGraph.java b/src/test/java/org/manifold/compiler/front/TestExpressionGraph.java index 23d3a0b..1297135 100644 --- a/src/test/java/org/manifold/compiler/front/TestExpressionGraph.java +++ b/src/test/java/org/manifold/compiler/front/TestExpressionGraph.java @@ -1,17 +1,39 @@ package org.manifold.compiler.front; -import com.google.common.collect.ImmutableList; +import java.io.File; +import java.util.HashMap; +import java.util.Map; + +import org.apache.log4j.ConsoleAppender; +import org.apache.log4j.LogManager; +import org.apache.log4j.PatternLayout; +import org.junit.AfterClass; import org.junit.Before; +import org.junit.BeforeClass; import org.junit.Test; import org.manifold.compiler.BooleanValue; import org.manifold.compiler.StringTypeValue; import org.manifold.compiler.StringValue; -import java.io.File; +import com.google.common.collect.ImmutableList; public class TestExpressionGraph { ExpressionGraph expressionGraph; + @BeforeClass + public static void setupLogging() { + PatternLayout layout = new PatternLayout( + "%-5p [%t]: %m%n"); + LogManager.getRootLogger().removeAllAppenders(); + LogManager.getRootLogger().addAppender( + new ConsoleAppender(layout, ConsoleAppender.SYSTEM_ERR)); + } + + @AfterClass + public static void afterClass() { + LogManager.getRootLogger().removeAllAppenders(); + } + @Before public void setup() { expressionGraph = new ExpressionGraph(); @@ -92,7 +114,8 @@ public void testAddSubExpressionGraph_correctGraphAdded() throws Exception { mainGraph.writeDOTFile(new File("build/mainGraph.txt")); subGraph.writeDOTFile(new File("build/subGraph.txt")); - mainGraph.addSubExpressionGraph(subGraph, inputEdge, subInputVertex, outputEdge, subOutputVertex); + Map dontRename = new HashMap<>(); + mainGraph.addSubExpressionGraph(subGraph, inputEdge, subInputVertex, outputEdge, subOutputVertex, dontRename); mainGraph.writeDOTFile(new File("build/merged.txt")); } } diff --git a/src/test/java/org/manifold/compiler/front/TestMain.java b/src/test/java/org/manifold/compiler/front/TestMain.java index 3872ee2..a948745 100644 --- a/src/test/java/org/manifold/compiler/front/TestMain.java +++ b/src/test/java/org/manifold/compiler/front/TestMain.java @@ -6,6 +6,8 @@ import java.io.File; import java.io.FileWriter; import java.io.IOException; +import java.nio.file.Path; +import java.nio.file.Paths; import org.apache.commons.cli.CommandLine; import org.apache.commons.cli.CommandLineParser; @@ -17,6 +19,13 @@ import org.junit.BeforeClass; import org.junit.Test; import org.manifold.compiler.middle.Schematic; +import org.manifold.compiler.middle.serialization.SchematicSerializer; + +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import com.google.gson.JsonParser; public class TestMain { @@ -28,7 +37,7 @@ public static void setupLogging() { LogManager.getRootLogger().addAppender( new ConsoleAppender(layout, ConsoleAppender.SYSTEM_ERR)); } - + private File writeTempSchematic(String contents) throws IOException { File input = File.createTempFile("tmp", ".manifold"); input.deleteOnExit(); @@ -38,7 +47,7 @@ private File writeTempSchematic(String contents) throws IOException { writer.close(); return input; } - + private Schematic invokeFrontend(String[] args) throws Exception { Options options = new Options(); Main frontend = new Main(); @@ -47,7 +56,7 @@ private Schematic invokeFrontend(String[] args) throws Exception { CommandLine cli = parser.parse(options, args); return frontend.invokeFrontend(cli); } - + @Test public void testSimpleElaboration() throws Exception { StringBuilder sb = new StringBuilder(); @@ -66,4 +75,38 @@ public void testSimpleElaboration() throws Exception { // TODO check for expected structure } + @Test + public void testNonPrimitiveElaboration() throws Exception { + StringBuilder sb = new StringBuilder(); + sb + .append("xIn = primitive port Bool; xOut = primitive port Bool;") + .append("xNot = primitive node (x: xIn) -> (xbar: xOut);") + .append("xInputPin = primitive node (Nil) -> (x: xOut);") + .append("xOutputPin = primitive node (x: xIn) -> (Nil);") + .append("invert = (p: Bool) -> (q: Bool) { q = xNot(x: p); };") + .append("a = xInputPin();") + .append("b = invert(p:a);") + .append("xOutputPin(x:b);"); + File in = writeTempSchematic(sb.toString()); + String[] args = { in.getAbsolutePath() }; + Schematic actual = invokeFrontend(args); + assertNotNull(actual); + // TODO check for expected structure + // serialize and print, for now + JsonObject schematicJson = SchematicSerializer.serialize(actual); + String schematicFilename = actual.getName() + ".schematic"; + Path schematicPath = Paths.get(schematicFilename); + File schematicFile = schematicPath.toFile(); + FileWriter fileWriter = new FileWriter(schematicFile); + try { + Gson gson = new GsonBuilder().setPrettyPrinting().create(); + JsonParser jp = new JsonParser(); + JsonElement je = jp.parse(schematicJson.toString()); + String prettyJsonString = gson.toJson(je); + fileWriter.write(prettyJsonString); + } finally { + fileWriter.close(); + } + } + }