From c5a1ca96fd7e55da87d0871cc5221b9645636746 Mon Sep 17 00:00:00 2001 From: Peter Socha Date: Mon, 16 May 2016 20:17:15 -0400 Subject: [PATCH 01/11] Delete lots of unused classes --- .../front/MultipleAssignmentException.java | 16 -- .../manifold/compiler/front/Namespace.java | 31 ---- .../org/manifold/compiler/front/Scope.java | 81 --------- .../org/manifold/compiler/front/Variable.java | 69 ------- .../front/VariableNotAssignedException.java | 16 -- .../TestMultipleAssignmentException.java | 42 ----- .../compiler/front/TestNamespace.java | 42 ----- .../manifold/compiler/front/TestScope.java | 169 ------------------ .../manifold/compiler/front/TestVariable.java | 117 ------------ .../TestVariableNotAssignedException.java | 41 ----- 10 files changed, 624 deletions(-) delete mode 100644 src/main/java/org/manifold/compiler/front/MultipleAssignmentException.java delete mode 100644 src/main/java/org/manifold/compiler/front/Namespace.java delete mode 100644 src/main/java/org/manifold/compiler/front/Scope.java delete mode 100644 src/main/java/org/manifold/compiler/front/Variable.java delete mode 100644 src/main/java/org/manifold/compiler/front/VariableNotAssignedException.java delete mode 100644 src/test/java/org/manifold/compiler/front/TestMultipleAssignmentException.java delete mode 100644 src/test/java/org/manifold/compiler/front/TestNamespace.java delete mode 100644 src/test/java/org/manifold/compiler/front/TestScope.java delete mode 100644 src/test/java/org/manifold/compiler/front/TestVariable.java delete mode 100644 src/test/java/org/manifold/compiler/front/TestVariableNotAssignedException.java diff --git a/src/main/java/org/manifold/compiler/front/MultipleAssignmentException.java b/src/main/java/org/manifold/compiler/front/MultipleAssignmentException.java deleted file mode 100644 index f0cb7b0..0000000 --- a/src/main/java/org/manifold/compiler/front/MultipleAssignmentException.java +++ /dev/null @@ -1,16 +0,0 @@ -package org.manifold.compiler.front; - -public class MultipleAssignmentException extends Exception { - private static final long serialVersionUID = 1L; - - private final Variable variable; - - public MultipleAssignmentException(Variable var) { - this.variable = var; - } - - @Override - public String getMessage() { - return "multiple assignment to variable '" + variable.getIdentifier() + "'"; - } -} diff --git a/src/main/java/org/manifold/compiler/front/Namespace.java b/src/main/java/org/manifold/compiler/front/Namespace.java deleted file mode 100644 index f37fd81..0000000 --- a/src/main/java/org/manifold/compiler/front/Namespace.java +++ /dev/null @@ -1,31 +0,0 @@ -package org.manifold.compiler.front; - -// TODO dead code - -public class Namespace { - private NamespaceIdentifier absoluteName; - private Scope privateScope = new Scope(); - private Scope publicScope = new Scope(); - - public Namespace(NamespaceIdentifier absoluteName) { - this.absoluteName = absoluteName; - } - - public NamespaceIdentifier getAbsoluteName() { - return absoluteName; - } - - public Scope getPrivateScope() { - return privateScope; - } - - public Scope getPublicScope() { - // TODO we may or may not want to make this immutable in the future, - // depending on where this is populated - return publicScope; - } - - // TODO(murphy) - // public Set getCompilationUnits(); - -} diff --git a/src/main/java/org/manifold/compiler/front/Scope.java b/src/main/java/org/manifold/compiler/front/Scope.java deleted file mode 100644 index 4d20b2b..0000000 --- a/src/main/java/org/manifold/compiler/front/Scope.java +++ /dev/null @@ -1,81 +0,0 @@ -package org.manifold.compiler.front; - -import java.util.HashMap; -import java.util.Map; -import java.util.Set; - -import org.manifold.compiler.TypeValue; -import org.manifold.compiler.Value; - -// TODO dead code - -public class Scope { - private final Scope parentScope; - private final Map symbolTable; - - public Set getSymbolIdentifiers() { - return symbolTable.keySet(); - } - - public Scope(Scope parentScope) { - this.symbolTable = new HashMap<>(); - this.parentScope = parentScope; - } - - public Scope() { - this.symbolTable = new HashMap<>(); - this.parentScope = null; - } - - public Scope getParentScope() { - return parentScope; - } - - // at the time a variable is defined, all we may know about it - // is that a binding for that name "will exist" - public void defineVariable(VariableIdentifier identifier) - throws MultipleDefinitionException { - if (symbolTable.containsKey(identifier)) { - throw new MultipleDefinitionException(identifier); - } - Variable v = new Variable(this, identifier); - symbolTable.put(identifier, v); - } - - public boolean isVariableDefined(VariableIdentifier identifier) { - if (symbolTable.containsKey(identifier)) { - return true; - } else if (parentScope == null) { - return false; - } else { - return parentScope.isVariableDefined(identifier); - } - } - - public TypeValue getVariableType(VariableIdentifier identifier) - throws TypeMismatchException, VariableNotDefinedException { - return getVariable(identifier).getType(); - } - - public Variable getVariable(VariableIdentifier identifier) - throws VariableNotDefinedException { - // TODO this does not handle namespaces correctly - Variable v = symbolTable.get(identifier); - if (v == null) { - // no such variable in this scope; check parent scope - if (parentScope == null) { - throw new VariableNotDefinedException(identifier); - } else { - return parentScope.getVariable(identifier); - } - } else { - return v; - } - } - - public Value getVariableValue(VariableIdentifier identifier) - throws VariableNotAssignedException, VariableNotDefinedException { - return getVariable(identifier).getValue(); - } - -} diff --git a/src/main/java/org/manifold/compiler/front/Variable.java b/src/main/java/org/manifold/compiler/front/Variable.java deleted file mode 100644 index e8c16dc..0000000 --- a/src/main/java/org/manifold/compiler/front/Variable.java +++ /dev/null @@ -1,69 +0,0 @@ -package org.manifold.compiler.front; - -import org.manifold.compiler.TypeValue; -import org.manifold.compiler.Value; - -// TODO dead code -public class Variable { - private final VariableIdentifier identifier; - private TypeValue type; - private final Scope scope; - - private boolean assigned = false; - private Object valueExpression; // was Expression - - public Variable(Scope scope, VariableIdentifier identifier) { - this.scope = scope; - this.identifier = identifier; - this.type = UnknownTypeValue.getInstance(); - } - - public VariableIdentifier getIdentifier() { - return identifier; - } - - public TypeValue getType() { - return this.type; - } - - public void setType(TypeValue type) { - this.type = type; - } - - public Scope getScope() { - return scope; - } - - public boolean isAssigned() { - return assigned; - } - - public Value getValue() { - return null; - } - - public void verify() { - // TODO(murphy) - /* - // Ensure the Type is an actual type - if (typeExpression.getType(scope) != TypeTypeValue.getInstance()) { - // TODO(lucas) We should have a special exception for the case where - // a nontype value is used as a type. - throw new TypeMismatchException( - TypeTypeValue.getInstance(), - typeExpression.getType(scope) - ); - } - - // Ensure the value is of the correct type - // TODO(lucas) - if (assigned && !(valueExpression.getType(scope).isSubtypeOf(getType()))) { - throw new TypeMismatchException( - getType(), - valueExpression.getType(scope) - ); - } - */ - } - -} diff --git a/src/main/java/org/manifold/compiler/front/VariableNotAssignedException.java b/src/main/java/org/manifold/compiler/front/VariableNotAssignedException.java deleted file mode 100644 index 7ac219f..0000000 --- a/src/main/java/org/manifold/compiler/front/VariableNotAssignedException.java +++ /dev/null @@ -1,16 +0,0 @@ -package org.manifold.compiler.front; - -public class VariableNotAssignedException extends Exception { - private static final long serialVersionUID = 1L; - - private final Variable variable; - public VariableNotAssignedException(Variable var){ - this.variable = var; - } - - @Override - public String getMessage(){ - return "value of variable '" + variable.getIdentifier() + - "' used but not assigned"; - } -} diff --git a/src/test/java/org/manifold/compiler/front/TestMultipleAssignmentException.java b/src/test/java/org/manifold/compiler/front/TestMultipleAssignmentException.java deleted file mode 100644 index e4affb1..0000000 --- a/src/test/java/org/manifold/compiler/front/TestMultipleAssignmentException.java +++ /dev/null @@ -1,42 +0,0 @@ -package org.manifold.compiler.front; - -import static org.junit.Assert.assertTrue; - -import java.util.ArrayList; - -import org.junit.Test; - -public class TestMultipleAssignmentException { - - private NamespaceIdentifier getNamespaceIdentifierInstance() { - ArrayList name = new ArrayList<>(1); - name.add("manifold"); - return new NamespaceIdentifier(name); - } - - private VariableIdentifier getVariableIdentifierInstance() { - return new VariableIdentifier(getNamespaceIdentifierInstance(), "foo"); - } - - public Variable getVariableInstance() { - return new Variable( - new Scope(), - getVariableIdentifierInstance() - ); - } - - public MultipleAssignmentException getInstance() { - return new MultipleAssignmentException(getVariableInstance()); - } - - @Test - public void testGetMessage_containsVariableIdentifier() { - MultipleAssignmentException instance = getInstance(); - - String message = instance.getMessage(); - assertTrue(message.contains( - getVariableInstance().getIdentifier().toString() - )); - } - -} diff --git a/src/test/java/org/manifold/compiler/front/TestNamespace.java b/src/test/java/org/manifold/compiler/front/TestNamespace.java deleted file mode 100644 index 3350749..0000000 --- a/src/test/java/org/manifold/compiler/front/TestNamespace.java +++ /dev/null @@ -1,42 +0,0 @@ -package org.manifold.compiler.front; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotSame; - -import org.junit.Test; -import org.manifold.compiler.BooleanTypeValue; - -public class TestNamespace { -/* - private NamespaceIdentifier getNamespaceIdentifier() { - return new NamespaceIdentifier("manifold:is:cool"); - } - - private VariableIdentifier getVariableIdentifier() { - VariableIdentifier id = new VariableIdentifier( - getNamespaceIdentifier(), - "foo" - ); - - return id; - } - - private Expression getTypeExpression() { - return new LiteralExpression(BooleanTypeValue.getInstance()); - } - - @Test - public void testGetAbsoluteName() { - Namespace n = new Namespace(getNamespaceIdentifier()); - assertEquals(getNamespaceIdentifier(), n.getAbsoluteName()); - } - - @Test - public void privateScope_distinctFrom_publicScope() { - Namespace n = new Namespace(getNamespaceIdentifier()); - Scope priv = n.getPrivateScope(); - Scope pub = n.getPublicScope(); - assertNotSame("private scope and public scope not distinct", priv, pub); - } -*/ -} diff --git a/src/test/java/org/manifold/compiler/front/TestScope.java b/src/test/java/org/manifold/compiler/front/TestScope.java deleted file mode 100644 index e6a7720..0000000 --- a/src/test/java/org/manifold/compiler/front/TestScope.java +++ /dev/null @@ -1,169 +0,0 @@ -package org.manifold.compiler.front; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertSame; -import static org.junit.Assert.assertTrue; -import static org.junit.Assert.fail; - -import org.junit.Test; -import org.manifold.compiler.BooleanTypeValue; -import org.manifold.compiler.BooleanValue; - -import com.google.common.collect.ImmutableSet; - -public class TestScope { - - /* - * Most of these tests depend on the correct operation of Variable. Look to - * those tests if errors show up. - */ - - /* - @Test - public void testGetParentScope() { - Scope foo = new Scope(null); - Scope bar = new Scope(foo); - assertSame(foo, bar.getParentScope()); - } - - private NamespaceIdentifier getNamespaceIdentifier() { - return new NamespaceIdentifier("manifold:is:cool"); - } - - private VariableIdentifier getVariableIdentifier() { - VariableIdentifier id = new VariableIdentifier( - getNamespaceIdentifier(), - "foo" - ); - - return id; - } - - private Expression getTypeExpression() { - return new LiteralExpression(BooleanTypeValue.getInstance()); - } - - // TODO(lucas) return an instance of the type - private Expression getValueExpression() { - return new LiteralExpression(BooleanValue.getInstance(true)); - } - - @Test - public void testDefineVariable() throws MultipleDefinitionException { - Scope s = new Scope(); - s.defineVariable(getVariableIdentifier()); - } - - @Test(expected = MultipleDefinitionException.class) - public void testDefineVariableMultiple() throws MultipleDefinitionException { - Scope s = new Scope(); - s.defineVariable(getVariableIdentifier()); - s.defineVariable(getVariableIdentifier()); - } - - @Test - public void testIsVariableDefined_true() throws MultipleDefinitionException { - Scope s = new Scope(); - s.defineVariable(getVariableIdentifier()); - assertTrue(s.isVariableDefined(getVariableIdentifier())); - } - - @Test - public void testIsVariableDefined_false() { - Scope s = new Scope(); - assertFalse(s.isVariableDefined(getVariableIdentifier())); - } - - - @Test - public void testGetVariableType() throws MultipleDefinitionException, - TypeMismatchException, VariableNotDefinedException { - - Scope s = new Scope(); - s.defineVariable(getVariableIdentifier()); - assertEquals(getTypeExpression().getValue(s), - s.getVariableType(getVariableIdentifier())); - } - - - @Test(expected = VariableNotDefinedException.class) - public void testGetVariableNotDefined() throws VariableNotDefinedException { - Scope s = new Scope(); - s.getVariable(getVariableIdentifier()); - } - - @Test - public void testGetVariable() throws MultipleDefinitionException, - VariableNotDefinedException, TypeMismatchException { - Scope s = new Scope(); - s.defineVariable(getVariableIdentifier()); - Variable v = s.getVariable(getVariableIdentifier()); - - assertEquals(v.getIdentifier(), getVariableIdentifier()); - //assertEquals(v.getType(), getTypeExpression().getValue(s)); - } - - @Test - public void testGetVariable_ParentScope() throws MultipleDefinitionException { - Scope s1 = new Scope(); - Scope s2 = new Scope(s1); - s1.defineVariable(getVariableIdentifier()); - try { - Variable v = s2.getVariable(getVariableIdentifier()); - assertTrue(s2.isVariableDefined(getVariableIdentifier())); - } catch (VariableNotDefinedException vnde) { - fail("could not get a variable defined in a parent scope"); - } - } - - @Test - public void testGetVariableValue() throws MultipleDefinitionException, - VariableNotDefinedException, MultipleAssignmentException, - VariableNotAssignedException { - - Scope s = new Scope(); - s.defineVariable(getVariableIdentifier()); - s.assignVariable(getVariableIdentifier(), getValueExpression()); - - assertEquals(getValueExpression().getValue(s), - s.getVariableValue(getVariableIdentifier())); - } - - @Test - public void testGetSymbolIdentifiers() throws MultipleDefinitionException { - Scope s = new Scope(); - s.defineVariable(getVariableIdentifier()); - assertEquals(ImmutableSet.of(getVariableIdentifier()), - s.getSymbolIdentifiers()); - } - - @Test(expected = VariableNotDefinedException.class) - public void testAssignVariableNotDefined() - throws VariableNotDefinedException, MultipleAssignmentException { - Scope s = new Scope(); - s.assignVariable(getVariableIdentifier(), getValueExpression()); - } - - @Test - public void testAssignVariable() throws MultipleDefinitionException, - VariableNotDefinedException, MultipleAssignmentException, - VariableNotAssignedException { - Scope s = new Scope(); - s.defineVariable(getVariableIdentifier()); - s.assignVariable(getVariableIdentifier(), getValueExpression()); - assertEquals(s.getVariableValue(getVariableIdentifier()), - getValueExpression().getValue(s)); - } - - @Test(expected = MultipleAssignmentException.class) - public void testAssignVariableMultiple() throws MultipleDefinitionException, - VariableNotDefinedException, MultipleAssignmentException { - Scope s = new Scope(); - s.defineVariable(getVariableIdentifier()); - s.assignVariable(getVariableIdentifier(), getValueExpression()); - s.assignVariable(getVariableIdentifier(), getValueExpression()); - } - - */ -} diff --git a/src/test/java/org/manifold/compiler/front/TestVariable.java b/src/test/java/org/manifold/compiler/front/TestVariable.java deleted file mode 100644 index 5f06560..0000000 --- a/src/test/java/org/manifold/compiler/front/TestVariable.java +++ /dev/null @@ -1,117 +0,0 @@ -package org.manifold.compiler.front; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNull; - -import org.junit.Test; -import org.manifold.compiler.BooleanTypeValue; -import org.manifold.compiler.BooleanValue; -import org.manifold.compiler.TypeValue; -import org.manifold.compiler.Value; - -public class TestVariable { - /* - private NamespaceIdentifier getNamespaceIdentifier() { - return new NamespaceIdentifier("manifold::is::cool"); - } - - private VariableIdentifier getVariableIdentifier() { - return new VariableIdentifier(getNamespaceIdentifier(), "foo"); - } - - private Scope getScope() { - return new Scope(); - } - - private Variable getVariable() { - // declare "foo" as a variable that stores a Type - return new Variable( - getScope(), - getVariableIdentifier() - ); - } - - private TypeValue getTypeValue() { - return BooleanTypeValue.getInstance(); - } - - private Value getValue() { - return BooleanValue.getInstance(true); - } - - private Expression getTypeExpression(){ - return new LiteralExpression(getTypeValue()); - } - - private Expression getValueExpression(){ - return new LiteralExpression(getValue()); - } - - @Test - public void testGetIdentifier() { - assertEquals(getVariable().getIdentifier(), getVariableIdentifier()); - } - - /* - @Test - public void testGetTypeValue() throws TypeMismatchException { - assertEquals( - getVariable().getType(), - getTypeExpression().getValue(getVariable().getScope()) - ); - } - - - @Test - public void testGetValueUnassigned() throws VariableNotAssignedException { - Variable v = getVariable(); - assertFalse(v.isAssigned()); - assertNull(v.getValue()); - } - - @Test - public void testSetValue() throws - MultipleAssignmentException, - VariableNotAssignedException, - TypeMismatchException { - Variable v = getVariable(); - v.setValueExpression(getValueExpression()); - assertEquals(v.getValue(), getValue()); - v.verify(); // Variable should verify successfully after being set. - } - - @Test(expected = MultipleAssignmentException.class) - public void testSetValueMultiple() throws MultipleAssignmentException { - Variable v = getVariable(); - v.setValueExpression(getValueExpression()); - v.setValueExpression(getValueExpression()); - } - - - @Test(expected = TypeMismatchException.class) - public void verify_nontypeThrow() throws - TypeMismatchException, - MultipleAssignmentException { - - Variable v = new Variable( - getScope(), - getVariableIdentifier() - ); - v.verify(); - } - - @Test(expected = TypeMismatchException.class) - public void verify_typeMismatchThrow() throws - TypeMismatchException, - MultipleAssignmentException { - - Variable v = new Variable( - getScope(), - getVariableIdentifier() - ); - v.setValueExpression(new LiteralExpression(BooleanTypeValue.getInstance())); - v.verify(); - } - */ -} diff --git a/src/test/java/org/manifold/compiler/front/TestVariableNotAssignedException.java b/src/test/java/org/manifold/compiler/front/TestVariableNotAssignedException.java deleted file mode 100644 index d0ad51c..0000000 --- a/src/test/java/org/manifold/compiler/front/TestVariableNotAssignedException.java +++ /dev/null @@ -1,41 +0,0 @@ -package org.manifold.compiler.front; - -import static org.junit.Assert.assertTrue; - -import java.util.ArrayList; - -import org.junit.Test; - -public class TestVariableNotAssignedException { - - private NamespaceIdentifier getNamespaceIdentifierInstance() { - ArrayList name = new ArrayList<>(1); - name.add("manifold"); - return new NamespaceIdentifier(name); - } - - private VariableIdentifier getVariableIdentifierInstance() { - return new VariableIdentifier(getNamespaceIdentifierInstance(), "foo"); - } - - public Variable getVariableInstance(){ - return new Variable( - new Scope(), - getVariableIdentifierInstance() - ); - } - - public VariableNotAssignedException getInstance(){ - return new VariableNotAssignedException(getVariableInstance()); - } - - @Test - public void testGetMessage_containsVariableIdentifier() { - VariableNotAssignedException instance = getInstance(); - String message = instance.getMessage(); - assertTrue(message.contains( - getVariableInstance().getIdentifier().toString()) - ); - } - -} From 8002806ad2fe030794b76de21e5baefe126ea672 Mon Sep 17 00:00:00 2001 From: Peter Socha Date: Mon, 16 May 2016 20:19:36 -0400 Subject: [PATCH 02/11] Handle line ending problems in buildErrors --- .../java/org/manifold/compiler/front/TestBuildErrors.java | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/test/java/org/manifold/compiler/front/TestBuildErrors.java b/src/test/java/org/manifold/compiler/front/TestBuildErrors.java index 358df3f..dbaca59 100644 --- a/src/test/java/org/manifold/compiler/front/TestBuildErrors.java +++ b/src/test/java/org/manifold/compiler/front/TestBuildErrors.java @@ -39,6 +39,7 @@ public void buildFailureTest() throws Exception { if (Files.exists(expectedErrorPath)) { byte[] encoded = Files.readAllBytes(expectedErrorPath); expectedError = new String(encoded, Charset.defaultCharset()); + expectedError = normalizeLineEndings(expectedError); } boolean isCaught = false; @@ -47,6 +48,7 @@ public void buildFailureTest() throws Exception { } catch (FrontendBuildException error) { isCaught = true; String actualError = error.getMessage(); + actualError = normalizeLineEndings(actualError); if (expectedError == null) { FileWriter fileWriter = new FileWriter(expectedErrorPath.toFile()); @@ -82,5 +84,9 @@ public void buildFailureTest() throws Exception { expectedErrorPath.toAbsolutePath()); } } + + private String normalizeLineEndings(String fileContents) { + return fileContents.replaceAll("\\r\\n?", "\n"); + } } From e27ac35fab09e95f684e1c080d2931e669b7402a Mon Sep 17 00:00:00 2001 From: Nik Klassen Date: Fri, 20 May 2016 13:54:34 -0400 Subject: [PATCH 03/11] 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 5ed9ea86573667b966c17d35ceb9757c3d3d35e7 Mon Sep 17 00:00:00 2001 From: Peter Socha Date: Sun, 22 May 2016 10:56:23 -0400 Subject: [PATCH 04/11] Restructuring of the README --- README.md | 441 ++++++++++++++++++++++-------------------------------- 1 file changed, 182 insertions(+), 259 deletions(-) diff --git a/README.md b/README.md index 9cd4096..d26c0f7 100644 --- a/README.md +++ b/README.md @@ -7,7 +7,7 @@ In much the same way that computer programs are written in programming languages, digital circuits is "written" in a **hardware description language**. The two hardware languages in wide use today are **VHDL** and **Verilog**. Both -were designed in the early 1980's and both have remained frozen in time since, +were designed in the early 1980s and both have remained frozen in time since, while our understanding of how to build expressive, powerful, and usable programming languages has grown considerably. These languages are in widespread use because they are the best tool for the job but they aren't nearly as @@ -93,35 +93,7 @@ In order to make Manifold readable and conceptually consistent, the following na # Front-End Language The Manifold front-end language expresses systems in many problem -domains, including digital hardware and microfluidics, as text. It is optimized for conceptual elegance, expressiveness, and human readability. - -## Bools - -The most fundamental type in Manifold is the `Bool` type. A -`Bool` represents a single bit of information: true or false, -represented as `true` or `false`. For example, we might turn -on our time machine by setting - -``` -Bool timeMachineOn = true; -``` - -The compiler can infer the type of the variable so it is equivalent to write - -``` -timeMachineOn = true; -``` - -## Annotations - -Manifold supports a system capable of annotating variables with additional metadata using **annotations**. These annotations are similar to Java's annotations in syntax, being prefixed by a *@* and optionally taking parameters - -``` -@bar Int width = 5; -@foo(10, true) Int height = 200; -``` - -At the moment, annotations are defined by the compiler but user-defined annotations are planned for future versions of the spec. +domains, including digital hardware and microfluidics, as text. It is optimized for conceptual elegance, expressiveness, and human readability. ## Compile-Time vs Run-Time @@ -142,17 +114,24 @@ Manifold is designed so that you don't need to think about the difference between these two types of operations but may take control over them, if desired. -### Explicit Static vs Dynamic +## Bools -Values may be explicitly constrained to being knowable at compile time or run time using annotations. For example, we can require that `width` is known statically (at compile time) by defining it as +The most fundamental type in Manifold is the `Bool` type. A +`Bool` represents a single bit of information: true or false, +represented as `true` or `false`. For example, we might turn +on our time machine by setting ``` -@static Int width; +timeMachineOn = true; ``` -If a variable or property is annotated as `@static` then we guarantee that its value is known at compile-time. Its value is additionally available at run-time. +## Ints + +The `Int` data type is used to represent positive integers. -If a variable or property is annotated as `@dynamic` then we guarantee that its value is *not* known at compile-time but is known at run-time. +``` +year = 5000; +``` ## Tuples @@ -166,52 +145,128 @@ machine that can travel to any year with an optional invisibility shield. You could define a tuple which groups and names these variables, ``` -(year: Int, invisibility: Bool) input; +input = (year=5000, invisibility=true); ``` -create such a tuple, +and access the properties in that tuple, ``` -input = (year: 5000, invisibility: true); +input.invisibility; // => 5000 +input.year; // => true ``` -and access the properties in that tuple +Variables in a tuple can also be unnamed. Unnamed properties are identified by their position and accessed through an integer index. ``` -input.invisibility; // => 500 -input.year; // => true +input = (1, 2, 3); + +input[0]; // => 1 +input[1]; // => 2 +``` + +## Functions + +A function is an entity that, given an input value, uses some logic to produce +an output value. + +Suppose you were to write a function that determined if an input to the time +machine was unsafe (i.e. targeting a year after the robot uprising and without the +invisibility shield). This function takes in some input in the form `(year: +Int, invisible: Bool invisible)` and produces some +output in the form of a `Bool`. + +``` +reversedTuple = (a: Bool, b: Bool) -> (c: Bool, d: Bool) { + c = b; + d = a; +}; +``` + +Note in the above function that there is no `return` statement -- output +variables defined in the function definition are assigned to directly. + +This function could be invoked as, + +``` +(foo, bar) = reversedTuple(a = true, b = false); +// (foo = false, bar = true) +``` + +## Comments + +Manifold supports C++-style comment syntax, with both single-line `//` comments and multiline `/* ... */` comments. + +``` +/* +This is a comment +*/ + +// This is also a comment. +``` + +# Planned or Speculative Features + +The features described in this section are not currently part of the Manifold language. They may be added in future versions depending on need. + +## Typing System + + - `Type` is the "type" of all types in Manifold (including itself) + - `Bool` is a single bit of data, with the value `true` or `false`. + - `Function` is an entity that produces an output value given an input value and potentially some internal state. + - `Tuple` is a structured group of values. + - `Enum` + - `Int` + +Since types are first class objects of type `Type`, a new type can be +defined via variable assignment. For example, the definition of Bit might look +like + +``` +Type Bit = Bool; ``` -### Default Properties +## Enums -The declaration may also include a default value for any property. +An enum allows you to restrict a domain to a fixed set of named values. For example, if you want to represent the states of a traffic light, you might define a TrafficLightState enum as follows ``` -(year: Int = 3000, invisible: Bool = false); +Type TrafficLight = Enum( + (green: Bool, yellow: Bool, red: Bool), + green: (1, 0, 0), + yellow: (0, 1, 0), + red: (0, 0, 1) +); ``` -Any property which does not have a default value is required; any property which -does have a default value is optional. +This enum can then be used as -### Positional Properties +``` +TrafficLight south = TrafficLight.green; +TrafficLight east = (0, 0, 1); +// But 'TrafficLight east = (0, 1, 1)' would be rejected by the compiler +``` -In addition to named properties, tuples may have implicitly named positional -properties; these properties are named by their position in the tuple. For -example, an ordered pair (x,y) might be represented as +If no type or values are specified for an enum, integers are used implicitly ``` -(Int, Int); +Type Color = Enum( + green, + red, + blue +); +Color color1 = Color.red; +Color color2 = 0; ``` -where the first `Int` is implicitly named `0` and the second `1`. +## Package System + +A package and import system makes it possible to combine multiple Manifold files together into one schematic. Imported files contribute their declarations and definitions into the importing file's scope. ``` -(Int, Int) position = (2, 4); -position.0; // => 2 -position.1; // => 4 +import "otherFile.manifold"; ``` -### Repeated Positional Properties (Arrays) +## Arrays Arrays in Manifold are a special case of tuples which have many positional properties of a particular type. For example, an integer array of width 3 could be defined as @@ -248,8 +303,6 @@ array.width; // => 5 ### Subscript Operator -*TODO This is super speculative. Do we even want to include this in the spec right now?* - ``` Type TimeMachineSettings = (year: year, invisible: invisible); TimeMachineSettings settings = (year: 5, invisible: high); @@ -322,76 +375,49 @@ If a tuple with one property of type `A`, `(A)`, is cast to type `A`, then the v Bool winning = (true); ``` -## Enums - -An enum allows you to restrict a domain to a fixed set of named values. For example, if you want to represent the states of a traffic light, you might define a TrafficLightState enum as follows - -``` -Type TrafficLight = Enum( - (green: Bool, yellow: Bool, red: Bool), - green: (1, 0, 0), - yellow: (0, 1, 0), - red: (0, 0, 1) -); -``` +### Parameterized Types -This enum can then be used as +Some types have compile-time parameters (like generics in C++). Such types are +defined via the `=>` syntax. Take, for example, this simple definition of an +`Array` type ``` -TrafficLight south = TrafficLight.green; -TrafficLight east = (0, 0, 1); -// But 'TrafficLight east = (0, 1, 1)' would be rejected by the compiler +Type Array = (Type T, Int width) => (T...width, width: width); ``` -If no type or values are specified for an enum, integers are used implicitly +Using this defined type, a developer could declare an instance of `Array` as ``` -Type Color = Enum( - green, - red, - blue -); -Color color1 = Color.red; -Color color2 = 0; +Array(SpaceshipEngine, 5) engines; ``` -## Functions - -A function is an entity that, given an input value, uses some logic to produce -an output value. +## Function Overloading -Suppose you were to write a function that determined if an input to the time -machine was unsafe (i.e. targeting a year after the robot uprising and without the -invisibility shield). This function takes in some input in the form `(year: -Int, invisible: Bool invisible)` and produces some -output in the form of a `Bool`. +Manifold is single assignment but there is one exception to +this rule: assigning to a function variable multiple times will overload that +function to support different input and output types. For example ``` -TimeMachineInput = (year: Int, invisible: Bool invisible); +travel = Int year -> Bool success { + success = travel (year, false); +}; -Function isDangerous = TimeMachineInput -> Bool dangerous { - dangerous = year > 5000 and !invisible; +travel = (year: Int year, invisibility: Bool invisibility) + -> Bool success { + ... }; ``` -Note in the above function that there is no `return` statement -- output -variables defined in the function definition are assigned to directly. - -This function could be invoked as -``` -settings = (year: 10000, invisibility: false); -dangerous = isDangerous settings; -``` +This example defines a function called `travel` which accepts either our time +machine input tuple or just an input year. Overloaded implementations may freely call each other. -Multiple functions may be chained together as -``` -if (!isDangerous settings) { - travel startFluxCapacitor configureTimeCrystals settings; -} -``` +## Digital Hardware Core Library -where the returned value of `configureTimeCrystals` is passed to -`startFluxCapacitor` and that of `startFluxCapacitor` is passed to `travel`. + - `series ((T -> T)...@static Int width) -> T` + - `recall (T initial, T next) -> T` + - `count (Int hz, Int mod) -> Int` + - `cycle (Int hz, T...) -> T` + - `if ((Bool, Void -> Void)...)` ### Stateful Functions @@ -421,117 +447,6 @@ Note that the output value of `recall` is used as an input value to itself! This might seem strange in the sequential programming paradigm but it is perfectly natural in hardware! -### Overloading - -We said earlier that Manifold is single assignment but there is one exception to -this rule: assigning to a function variable multiple times will overload that -function to support different input and output types. For example - -``` -travel = Int year -> Bool success { - success = travel (year, false); -}; - -travel = (year: Int year, invisibility: Bool invisibility) - -> Bool success { - ... -}; -``` - -This example defines a function called `travel` which accepts either our time -machine input tuple or just an input year. Overloaded implementations may freely call each other. - -## Types - - - `Bool` is a single bit of data, with the value `high` or `false`. - - `Function` is an entity that produces an output value given an input value and potentially some internal state. - - `Tuple` is a structured group of values. - - `Enum` - - `Int` - - `Type` is the "type" of all types in Manifold (including itself) - -Since types are first class objects of type `Type`, a new type can be -defined via variable assignment. For example, the definition of Bit might look -like - -``` -Type Bit = Bool; -``` - -### Parameterized Types - -Some types have compile-time parameters (like generics in C++). Such types are -defined via the `=>` syntax. Take, for example, this simple definition of an -`Array` type - -``` -Type Array = (Type T, Int width) => (T...width, width: width); -``` - -Using this defined type, a developer could declare an instance of `Array` as - -``` -Array(SpaceshipEngine, 5) engines; -``` - -## Constraints - -*TODO* - -## Packages and Namespacing - -# Digital Hardware Core Library - - - `recall` - - `count` - - `cycle` - -``` -parallel = import parallel; -``` - -You may use destructuring assignment with packages - -``` -(parallel) = import Manifold; -``` - -or `*` destructuring assignment to import everything from the package into local scope - -``` -* = import Manifold; -``` - -## Core Library - -### `Bool` - -### `Int` - -### `Tuple` - -### `Void` - -A type describing the lack of a value. Equivalent to `()`. - -### `Type` - -### `parallel ((T -> U)...@static Int width) -> (T...width)` - -### `series ((T -> T)...@static Int width) -> T` - -## Digital Hardware Library - -### `recall (T initial, T next) -> T` - -### `count (Int hz, Int mod) -> Int` - -### `cycle (Int hz, T...) -> T` - -### `if ((Bool, Void -> Void)...)` - -## Microfluidics Library - # Intermediate Language - **port** @@ -551,63 +466,71 @@ Type information goes into **definition files** which are shipped with the back Programs written in the Manifold front-end language compile into **schematic files** which can then be compiled into domain specific artifacts by the appropriate back end. For example, a description of a digital hardware circuit in the Manifold front-end would be compiled to a digital hardware schematic file that, when fed into the digital hardware back-end, would produce a VHDL or Verilog file. -## Definition Files +## Schematic Files -Each back-end compiler includes a definition file which declares all of its supported primitives. +A schematic written in the Manifold front-end language will compile into a intermediate file of this format. ``` { - "node_types": {...} - "connection_types": {...} - "port_types": {...} + "name": "..." + "userDefinedTypes": {...} + "portTypes": {...} + "nodeTypes": {...} + "constraintTypes": {...} + "nodes": {...} + "connections": {...} + "constraints": {...} } ``` - -### Node Definitions +### Port Types ``` -{ - "port_types": { - "name": { - attributes: {...} - } - , ... - } -} +"portTypes": { + "name": { + "signalType": "...", + "attributes": {...} + }, + ... +}, ``` ### Node Types + ``` -{ - "node_types": { - "name": { - ports: ["name"] - attributes: {...} - } - , ... - } -} +"nodeTypes": { + "name": { + "attributes": {...}, + "ports": {...} + }, + ... +}, ``` -### Connection Types - -## Schematic Files - -A schematic written in the Manifold front-end language will compile into a intermediate file of this format. +### Nodes ``` -{ - "nodes": {...} - "connections": {...} - "ports": {...} -} +"nodes": { + "name": { + "type": "...", + "attributes": {...}, + "portAttrs": {...} + }, + ... +}, ``` -### Nodes +### Connections -### Ports +``` +"connections": { + "name": { + "attributes": {...}, + "from": "...", + "to": "..." + }, + ... +}, +``` -### Connections -### Constraints From 2403a8557d05a407509578169f9add1d79b24b1e Mon Sep 17 00:00:00 2001 From: Nik Klassen Date: Fri, 27 May 2016 14:50:49 -0400 Subject: [PATCH 05/11] 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 06/11] 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 07/11] 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 08/11] 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 From 91824e9d9c0fb18b91b9bca629ffe088cf260104 Mon Sep 17 00:00:00 2001 From: Nik Klassen Date: Mon, 13 Jun 2016 11:23:34 -0400 Subject: [PATCH 09/11] Convert imports to expressions --- src/main/antlr4/Manifold.g4 | 8 +- .../front/ExpressionContextVisitor.java | 73 +++++++++++- .../compiler/front/ExpressionGraph.java | 28 ++++- .../compiler/front/ExpressionGraphParser.java | 86 ++++++++++++++ .../compiler/front/ExpressionVertex.java | 1 + .../compiler/front/FunctionValueVertex.java | 2 +- .../compiler/front/ImportTypeValue.java | 29 +++++ .../manifold/compiler/front/ImportValue.java | 37 ++++++ .../manifold/compiler/front/ImportVertex.java | 81 +++++++++++++ .../compiler/front/ImportVisitor.java | 27 ----- .../org/manifold/compiler/front/Main.java | 111 +----------------- .../compiler/front/NamedEntryTypeValue.java | 7 ++ .../compiler/front/NamedEntryValue.java | 7 ++ .../compiler/front/NodeValueVertex.java | 10 +- .../front/StaticAttributeAccessVertex.java | 41 +++++-- .../compiler/front/TupleTypeValue.java | 16 ++- .../manifold/compiler/front/TupleValue.java | 9 +- tests/acceptance/import.manifold | 4 +- tests/acceptance/import.manifold.schematic | 24 ++-- tests/acceptance/importSystem.manifold | 6 +- tests/acceptance/importTuple.manifold | 4 +- .../acceptance/importTuple.manifold.schematic | 16 +-- .../importFileWithError.manifold.error | 1 + tests/buildErrors/importRedefineVar.manifold | 3 - .../importRedefineVar.manifold.error | 1 - 25 files changed, 424 insertions(+), 208 deletions(-) create mode 100644 src/main/java/org/manifold/compiler/front/ExpressionGraphParser.java create mode 100644 src/main/java/org/manifold/compiler/front/ImportTypeValue.java create mode 100644 src/main/java/org/manifold/compiler/front/ImportValue.java create mode 100644 src/main/java/org/manifold/compiler/front/ImportVertex.java delete mode 100644 src/main/java/org/manifold/compiler/front/ImportVisitor.java create mode 100644 src/main/java/org/manifold/compiler/front/NamedEntryTypeValue.java create mode 100644 src/main/java/org/manifold/compiler/front/NamedEntryValue.java delete mode 100644 tests/buildErrors/importRedefineVar.manifold delete mode 100644 tests/buildErrors/importRedefineVar.manifold.error diff --git a/src/main/antlr4/Manifold.g4 b/src/main/antlr4/Manifold.g4 index f1f4594..432d38d 100644 --- a/src/main/antlr4/Manifold.g4 +++ b/src/main/antlr4/Manifold.g4 @@ -83,6 +83,7 @@ rvalue: | VISIBILITY_PUBLIC? lvalue '=' rvalue # AssignmentExpression | 'primitive' 'port' typevalue (':' tupleTypeValue)? # PrimitivePortDefinitionExpression | 'primitive' 'node' functionTypeValue # PrimitiveNodeDefinitionExpression + | 'import' STRING_VALUE #ImportExpr ; lvalue: @@ -97,11 +98,6 @@ expression: // | declaration ; -statement: - expression #ExpressionStatment - | 'import' STRING_VALUE #ImportStatement - ; - STATEMENT_TERMINATOR: ';'; @@ -111,4 +107,4 @@ STATEMENT_TERMINATOR: ';'; // // //////////////////////////////////////////////////////// -schematic: (statement STATEMENT_TERMINATOR)*; +schematic: (expression 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 fb5c782..0704d98 100644 --- a/src/main/java/org/manifold/compiler/front/ExpressionContextVisitor.java +++ b/src/main/java/org/manifold/compiler/front/ExpressionContextVisitor.java @@ -14,13 +14,21 @@ import org.manifold.parser.ManifoldLexer; import org.manifold.parser.ManifoldParser.*; +import java.io.File; +import java.net.URL; +import java.util.ArrayList; import java.util.Arrays; import java.util.LinkedList; import java.util.List; +import java.util.stream.Stream; class ExpressionContextVisitor extends ManifoldBaseVisitor { + private ExpressionGraphParser parser; private ExpressionGraph exprGraph; + private File inputFile; + private List errors; + public ExpressionGraph getExpressionGraph() { return this.exprGraph; } @@ -32,12 +40,27 @@ public ExpressionGraph getExpressionGraph() { private boolean isPublic = false; private int nextTmpVar = 0; - public ExpressionContextVisitor() { - this(new ExpressionGraph()); + public ExpressionContextVisitor(File inputFile) { + this(new ExpressionGraph(), inputFile); } - public ExpressionContextVisitor(ExpressionGraph exprGraph) { + public ExpressionContextVisitor(ExpressionGraph exprGraph, File inputFile) { + this.inputFile = inputFile; + this.parser = new ExpressionGraphParser(); this.exprGraph = exprGraph; + this.errors = new ArrayList<>(); + } + + public List getErrors() { + return errors; + } + + private File getLib(String libPath) { + URL url = this.getClass().getClassLoader().getResource("libraries/" + libPath); + if (url == null) { + return null; + } + return new File(url.getFile()); } @Override @@ -81,7 +104,7 @@ public ExpressionVertex visitFunctionInvocationExpression( public ExpressionVertex visitFunctionValue( FunctionValueContext ctx) { ExpressionContextVisitor functionGraphBuilder = - new ExpressionContextVisitor(); + new ExpressionContextVisitor(inputFile); ctx.expression().forEach(functionGraphBuilder::visit); ExpressionGraph fSubGraph = functionGraphBuilder.getExpressionGraph(); @@ -315,8 +338,46 @@ private ExpressionVertex createVariableVertex(VariableIdentifier id) { } @Override - public ExpressionVertex visitImportStatement(ImportStatementContext context) { - return null; + public ExpressionVertex visitImportExpr(ImportExprContext context) { + String importString = context.STRING_VALUE().getText() + .replaceAll("\\\"", "\""); + importString = importString.substring(1, importString.length() - 1); + + // Reassign to a new variable to satisfy the lambda requirement of "final" variables + String filePath = importString; + + 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; + } + errors.add("Import " + filePath + " not found"); + return null; + }); + if (importedFile == null) { + return null; + } + + ExpressionGraph g; + try { + g = parser.parseFile(importedFile); + } catch (Exception e) { + errors.add(e.getMessage().trim()); + errors.add("Error while parsing included file " + filePath); + return null; + } + + ExpressionVertex v = new ImportVertex(exprGraph, g); + exprGraph.addVertex(v); + return v; } @Override diff --git a/src/main/java/org/manifold/compiler/front/ExpressionGraph.java b/src/main/java/org/manifold/compiler/front/ExpressionGraph.java index c40079a..1e0da83 100644 --- a/src/main/java/org/manifold/compiler/front/ExpressionGraph.java +++ b/src/main/java/org/manifold/compiler/front/ExpressionGraph.java @@ -4,6 +4,7 @@ import com.google.common.base.Throwables; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; +import org.antlr.v4.runtime.misc.Nullable; import org.apache.log4j.LogManager; import org.apache.log4j.Logger; import org.manifold.compiler.UndefinedBehaviourError; @@ -13,6 +14,8 @@ import java.io.FileWriter; import java.io.IOException; import java.util.*; +import java.util.stream.Collectors; +import java.util.stream.Stream; public class ExpressionGraph { @@ -29,9 +32,18 @@ public Map getVariableVertices() { /** * 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) { + public void addSubGraph(ExpressionGraph subGraph) { + addSubGraph(subGraph, null); + } + + /** + * Copy all vertices from subGraph into this graph. + * @param subGraph the graph to copy + * @param namespace The namespace for any exported variables. If this is null all variables will be exported under + * the same name. + */ + public void addSubGraph(ExpressionGraph subGraph, @Nullable NamespaceIdentifier namespace) { // map of subgraph edge -> new edge to be inserted Map exprEdgeMap = new HashMap<>(); subGraph.getEdges().forEach(e -> { @@ -42,6 +54,7 @@ public void addSubGraph(ExpressionGraph subGraph, boolean importVars) { // map of subgraph vertex -> new vertex Map exprVertexMap = new HashMap<>(); + List oldNs = namespace == null ? new ArrayList<>() : namespace.getName(); // do not add the input/output vertices since they are being replaced by the main graph's vertices subGraph.getVertices().stream() @@ -51,8 +64,13 @@ public void addSubGraph(ExpressionGraph subGraph, boolean importVars) { 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(); + if (namespace == null || var.getExported()) { + List newNs = + Stream.concat( + oldNs.stream(), + var.getId().getNamespaceIdentifier().getName().stream()) + .collect(Collectors.toList()); + VariableIdentifier ref = new VariableIdentifier(new NamespaceIdentifier(newNs), var.getId().getName()); try { this.addVertex(ref); newVertex = this.getVariableVertex(ref); @@ -61,6 +79,8 @@ public void addSubGraph(ExpressionGraph subGraph, boolean importVars) { } } else { newVertex = new VariableReferenceVertex(this, var.getId()); + // Uses the ExpressionVertex overload which will add this node to the non-variable vertices, + // instead of variable this.addVertex(newVertex); } } else { diff --git a/src/main/java/org/manifold/compiler/front/ExpressionGraphParser.java b/src/main/java/org/manifold/compiler/front/ExpressionGraphParser.java new file mode 100644 index 0000000..37f7b83 --- /dev/null +++ b/src/main/java/org/manifold/compiler/front/ExpressionGraphParser.java @@ -0,0 +1,86 @@ +package org.manifold.compiler.front; + +import org.antlr.v4.runtime.*; +import org.antlr.v4.runtime.atn.ATNConfigSet; +import org.antlr.v4.runtime.dfa.DFA; +import org.antlr.v4.runtime.misc.NotNull; +import org.antlr.v4.runtime.misc.Nullable; +import org.apache.log4j.LogManager; +import org.apache.log4j.Logger; +import org.manifold.parser.ManifoldLexer; +import org.manifold.parser.ManifoldParser; + +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.util.BitSet; +import java.util.List; + +public class ExpressionGraphParser { + + private static Logger log = LogManager.getLogger("ExpressionGraphParser"); + + public ExpressionGraph parseFile(File inputFile) throws IOException { + ManifoldLexer lexer = new ManifoldLexer(new ANTLRInputStream( + new FileInputStream(inputFile))); + + // Get a list of matched tokens + CommonTokenStream tokens = new CommonTokenStream(lexer); + + // Pass the tokens to the parser + ManifoldParser parser = new ManifoldParser(tokens); + + StringBuilder errors = new StringBuilder(); + parser.addErrorListener(new ANTLRErrorListener() { + + @Override + 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"); + } + + @Override + public void reportAmbiguity(@NotNull Parser recognizer, @NotNull DFA dfa, int startIndex, int stopIndex, + boolean exact, @Nullable BitSet ambigAlts, @NotNull ATNConfigSet configs) { + // Pass + } + + @Override + public void reportAttemptingFullContext(@NotNull Parser recognizer, @NotNull DFA dfa, int startIndex, + int stopIndex, @Nullable BitSet conflictingAlts, + @NotNull ATNConfigSet configs) { + // Pass + } + + @Override + public void reportContextSensitivity(@NotNull Parser recognizer, @NotNull DFA dfa, int startIndex, int stopIndex, + int prediction, @NotNull ATNConfigSet configs) { + // Pass + } + }); + + // Specify our entry point + ManifoldParser.SchematicContext context = parser.schematic(); + + if (errors.length() != 0) { + throw new FrontendBuildException(errors.toString()); + } + + ExpressionContextVisitor graphBuilder = new ExpressionContextVisitor(inputFile); + List expressionContexts = context.expression(); + for (ManifoldParser.ExpressionContext expressionContext : expressionContexts) { + graphBuilder.visit(expressionContext); + } + if (graphBuilder.getErrors().size() > 0) { + throw new FrontendBuildException( + String.join("\n", graphBuilder.getErrors())); + } + ExpressionGraph exprGraph = graphBuilder.getExpressionGraph(); + + log.debug("writing out initial expression graph"); + File exprGraphDot = new File(inputFile.getName() + ".exprs.dot"); + exprGraph.writeDOTFile(exprGraphDot); + return exprGraph; + } +} diff --git a/src/main/java/org/manifold/compiler/front/ExpressionVertex.java b/src/main/java/org/manifold/compiler/front/ExpressionVertex.java index 50f2166..7ac1be1 100644 --- a/src/main/java/org/manifold/compiler/front/ExpressionVertex.java +++ b/src/main/java/org/manifold/compiler/front/ExpressionVertex.java @@ -10,6 +10,7 @@ public abstract class ExpressionVertex { private final ExpressionGraph exprGraph; + protected ExpressionGraph getExpressionGraph() { return this.exprGraph; } diff --git a/src/main/java/org/manifold/compiler/front/FunctionValueVertex.java b/src/main/java/org/manifold/compiler/front/FunctionValueVertex.java index 852dc0f..e77bad6 100644 --- a/src/main/java/org/manifold/compiler/front/FunctionValueVertex.java +++ b/src/main/java/org/manifold/compiler/front/FunctionValueVertex.java @@ -153,7 +153,7 @@ 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, false); + newBody.addSubGraph(functionBody); return new FunctionValueVertex(g, edgeMap.get(functionTypeEdge), newBody); } diff --git a/src/main/java/org/manifold/compiler/front/ImportTypeValue.java b/src/main/java/org/manifold/compiler/front/ImportTypeValue.java new file mode 100644 index 0000000..33214fb --- /dev/null +++ b/src/main/java/org/manifold/compiler/front/ImportTypeValue.java @@ -0,0 +1,29 @@ +package org.manifold.compiler.front; + +import org.manifold.compiler.SchematicValueVisitor; +import org.manifold.compiler.TypeValue; +import org.manifold.compiler.UndefinedBehaviourError; + +public class ImportTypeValue extends TypeValue implements NamedEntryTypeValue { + + private NamespaceIdentifier namespace; + private ExpressionGraph exprGraph; + + public ImportTypeValue(ExpressionGraph exprGraph, NamespaceIdentifier namespace) { + this.exprGraph = exprGraph; + this.namespace = namespace; + } + + public TypeValue getEntry(String key) throws Exception { + VariableIdentifier id = new VariableIdentifier(namespace, key); + VariableReferenceVertex source = exprGraph.getVariableVertex(id); + source.elaborate(); + return source.getType(); + } + + @Override + public void accept(SchematicValueVisitor schematicValueVisitor) { + throw new UndefinedBehaviourError( + "cannot accept non-frontend ValueVisitor into a frontend Value"); + } +} diff --git a/src/main/java/org/manifold/compiler/front/ImportValue.java b/src/main/java/org/manifold/compiler/front/ImportValue.java new file mode 100644 index 0000000..b99883a --- /dev/null +++ b/src/main/java/org/manifold/compiler/front/ImportValue.java @@ -0,0 +1,37 @@ +package org.manifold.compiler.front; + +import org.manifold.compiler.SchematicValueVisitor; +import org.manifold.compiler.Value; + +public class ImportValue extends Value implements NamedEntryValue { + private ExpressionGraph exprGraph; + private NamespaceIdentifier namespace; + + public ImportValue(ExpressionGraph exprGraph, NamespaceIdentifier namespace) { + super(new ImportTypeValue(exprGraph, namespace)); + this.exprGraph = exprGraph; + this.namespace = namespace; + } + @Override + public boolean isElaborationtimeKnowable() { + return false; + } + + @Override + public boolean isRuntimeKnowable() { + return false; + } + + @Override + public void accept(SchematicValueVisitor schematicValueVisitor) { + return; + } + + @Override + public Value getEntry(String key) throws Exception { + VariableIdentifier id = new VariableIdentifier(namespace, key); + VariableReferenceVertex source = exprGraph.getVariableVertex(id); + source.elaborate(); + return source.getValue(); + } +} diff --git a/src/main/java/org/manifold/compiler/front/ImportVertex.java b/src/main/java/org/manifold/compiler/front/ImportVertex.java new file mode 100644 index 0000000..eed42db --- /dev/null +++ b/src/main/java/org/manifold/compiler/front/ImportVertex.java @@ -0,0 +1,81 @@ +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.IOException; +import java.util.List; +import java.util.Map; + +public class ImportVertex extends ExpressionVertex { + + private NamespaceIdentifier namespace; + private ExpressionGraph includedGraph; + private TypeValue type; + + public ImportVertex(ExpressionGraph exprGraph, ExpressionGraph includedGraph) { + super(exprGraph); + this.includedGraph = includedGraph; + } + + @Override + public TypeValue getType() { + return this.type; + } + + @Override + public Value getValue() { + return this.value; + } + + @Override + public void elaborate() throws Exception { + if (namespace != null) { + return; + } + + ExpressionGraph exprGraph = getExpressionGraph(); + List targets = exprGraph.getEdgesFromSource(this); + if (targets.size() != 1) { + throw new UndefinedBehaviourError("Multiple edges from import " + this.toString()); + } + ExpressionVertex target = targets.get(0).getTarget(); + if (!(target instanceof VariableReferenceVertex)) { + throw new UndefinedBehaviourError("Import " + this.toString() + " must be assigned to a variable"); + } + namespace = new NamespaceIdentifier(((VariableReferenceVertex) target).getId().getName()); + + exprGraph.addSubGraph(includedGraph, namespace); + this.value = new ImportValue(getExpressionGraph(), namespace); + this.type = this.value.getType(); + } + + @Override + public void verify() throws Exception { + return; + } + + @Override + public boolean isElaborationtimeKnowable() { + return false; + } + + @Override + public boolean isRuntimeKnowable() { + return false; + } + + @Override + public void writeToDOTFile(BufferedWriter writer) throws IOException { + return; + } + + @Override + public ExpressionVertex copy(ExpressionGraph g, Map edgeMap) { + ImportVertex v = new ImportVertex(g, includedGraph); + v.namespace = namespace; + return v; + } +} diff --git a/src/main/java/org/manifold/compiler/front/ImportVisitor.java b/src/main/java/org/manifold/compiler/front/ImportVisitor.java deleted file mode 100644 index 75935dc..0000000 --- a/src/main/java/org/manifold/compiler/front/ImportVisitor.java +++ /dev/null @@ -1,27 +0,0 @@ -package org.manifold.compiler.front; - -import org.manifold.parser.ManifoldBaseVisitor; -import org.manifold.parser.ManifoldParser.ImportStatementContext; - -import java.util.ArrayList; -import java.util.List; - -class ImportVisitor extends ManifoldBaseVisitor { - - private List imports; - public List getImports() { - return this.imports; - } - - public ImportVisitor() { - this.imports = new ArrayList<>(); - } - - @Override - public Void visitImportStatement(ImportStatementContext context) { - String filePath = context.STRING_VALUE().getText(); - filePath = filePath.replaceAll("\\\"", "\""); - this.imports.add(filePath.substring(1, filePath.length() - 1)); - return null; - } -} diff --git a/src/main/java/org/manifold/compiler/front/Main.java b/src/main/java/org/manifold/compiler/front/Main.java index 102a8ff..0895f37 100644 --- a/src/main/java/org/manifold/compiler/front/Main.java +++ b/src/main/java/org/manifold/compiler/front/Main.java @@ -1,31 +1,15 @@ package org.manifold.compiler.front; -import com.google.common.base.Throwables; -import org.antlr.v4.runtime.*; -import org.antlr.v4.runtime.atn.ATNConfigSet; -import org.antlr.v4.runtime.dfa.DFA; -import org.antlr.v4.runtime.misc.NotNull; -import org.antlr.v4.runtime.misc.Nullable; -import org.antlr.v4.runtime.misc.ParseCancellationException; -import org.antlr.v4.runtime.tree.TerminalNode; import org.apache.commons.cli.CommandLine; import org.apache.commons.cli.Options; import org.apache.log4j.LogManager; import org.apache.log4j.Logger; import org.manifold.compiler.*; import org.manifold.compiler.middle.Schematic; -import org.manifold.parser.ManifoldBaseVisitor; -import org.manifold.parser.ManifoldLexer; -import org.manifold.parser.ManifoldParser; -import org.manifold.parser.ManifoldParser.*; 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 { @@ -166,98 +150,6 @@ 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))); - - // Get a list of matched tokens - CommonTokenStream tokens = new CommonTokenStream(lexer); - - // Pass the tokens to the parser - ManifoldParser parser = new ManifoldParser(tokens); - - StringBuilder errors = new StringBuilder(); - parser.addErrorListener(new ANTLRErrorListener() { - - @Override - 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"); - } - - @Override - public void reportAmbiguity(@NotNull Parser recognizer, @NotNull DFA dfa, int startIndex, int stopIndex, - boolean exact, @Nullable BitSet ambigAlts, @NotNull ATNConfigSet configs) { - // Pass - } - - @Override - public void reportAttemptingFullContext(@NotNull Parser recognizer, @NotNull DFA dfa, int startIndex, - int stopIndex, @Nullable BitSet conflictingAlts, - @NotNull ATNConfigSet configs) { - // Pass - } - - @Override - public void reportContextSensitivity(@NotNull Parser recognizer, @NotNull DFA dfa, int startIndex, int stopIndex, - int prediction, @NotNull ATNConfigSet configs) { - // Pass - } - }); - - // Specify our entry point - ManifoldParser.SchematicContext context = parser.schematic(); - - if (errors.length() != 0) { - throw new FrontendBuildException(errors.toString()); - } - - ExpressionGraph exprGraph = new ExpressionGraph(); - - ImportVisitor importVisitor = new ImportVisitor(); - importVisitor.visit(context); - for (String filePath : importVisitor.getImports()) { - File importedFile = Stream.of( - new File(inputFile.getParent(), filePath), - new File(inputFile.getParent(), filePath + ".manifold") - ).filter(f -> f.exists()) - .findFirst() - .orElseGet(() -> { - File lib = getLib(filePath); - if (lib == null) { - lib = getLib(filePath + ".manifold"); - } - if (lib != null) { - return lib; - } - throw new FrontendBuildException("Import " + filePath + " not found"); - }); - ExpressionGraph g = parseFile(importedFile); - exprGraph.addSubGraph(g, true); - } - - ExpressionContextVisitor graphBuilder = new ExpressionContextVisitor(exprGraph); - List statementContexts = context.statement(); - for (StatementContext statementContext : statementContexts) { - graphBuilder.visit(statementContext); - } - 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 { @@ -266,7 +158,8 @@ public Schematic invokeFrontend(CommandLine cmd) throws Exception { throw new FrontendBuildException(inputFile.getName() + " not found."); } - ExpressionGraph exprGraph = parseFile(inputFile); + ExpressionGraphParser parser = new ExpressionGraphParser(); + ExpressionGraph exprGraph = parser.parseFile(inputFile); exprGraph.verifyVariablesSingleAssignment(); Schematic schematic = new Schematic(inputFile.getName()); diff --git a/src/main/java/org/manifold/compiler/front/NamedEntryTypeValue.java b/src/main/java/org/manifold/compiler/front/NamedEntryTypeValue.java new file mode 100644 index 0000000..20441f0 --- /dev/null +++ b/src/main/java/org/manifold/compiler/front/NamedEntryTypeValue.java @@ -0,0 +1,7 @@ +package org.manifold.compiler.front; + +import org.manifold.compiler.TypeValue; + +public interface NamedEntryTypeValue { + TypeValue getEntry(String key) throws Exception; +} diff --git a/src/main/java/org/manifold/compiler/front/NamedEntryValue.java b/src/main/java/org/manifold/compiler/front/NamedEntryValue.java new file mode 100644 index 0000000..4f2a201 --- /dev/null +++ b/src/main/java/org/manifold/compiler/front/NamedEntryValue.java @@ -0,0 +1,7 @@ +package org.manifold.compiler.front; + +import org.manifold.compiler.Value; + +public interface NamedEntryValue { + Value getEntry(String key) throws Exception; +} diff --git a/src/main/java/org/manifold/compiler/front/NodeValueVertex.java b/src/main/java/org/manifold/compiler/front/NodeValueVertex.java index bacc918..bac7e34 100644 --- a/src/main/java/org/manifold/compiler/front/NodeValueVertex.java +++ b/src/main/java/org/manifold/compiler/front/NodeValueVertex.java @@ -105,7 +105,7 @@ public void elaborate() throws Exception { for (String inputPortName : inputPortNames) { PortTypeValue inputPortType = nodeType.getPorts().get(inputPortName); - Value inputPortValue = input.entry(inputPortName); + Value inputPortValue = input.getEntry(inputPortName); // if the port has attributes, this is a tuple // (0: FuturePortValue, 1: (attributes)) // otherwise, this is just a FuturePortValue @@ -120,8 +120,8 @@ public void elaborate() throws Exception { } else { TupleValue inputPortTuple = (TupleValue) inputPortValue; // same story here about unwrapping the port - futurePort = unwrapPort(inputPortTuple.atIndex(0)); - TupleValue attributesValue = (TupleValue) inputPortTuple.atIndex(1); + futurePort = unwrapPort(inputPortTuple.getEntry(0)); + TupleValue attributesValue = (TupleValue) inputPortTuple.getEntry(1); // TODO this can probably be done better, it assumes that all tuple values are named inputPortAttrs = MappedArray.toMap(attributesValue.getEntries()); } @@ -136,14 +136,14 @@ public void elaborate() throws Exception { if (outputPortType.getAttributes().isEmpty()) { outputPortAttrs = new HashMap<>(); // no attributes } else { - TupleValue attributesValue = (TupleValue) input.entry(outputPortName); + TupleValue attributesValue = (TupleValue) input.getEntry(outputPortName); outputPortAttrs = MappedArray.toMap(attributesValue.getEntries()); } portAttrs.put(outputPortName, outputPortAttrs); } for (String attrName : nodeType.getAttributes().keySet()) { - Value attrValue = input.entry(attrName); + Value attrValue = input.getEntry(attrName); nodeAttrs.put(attrName, attrValue); } diff --git a/src/main/java/org/manifold/compiler/front/StaticAttributeAccessVertex.java b/src/main/java/org/manifold/compiler/front/StaticAttributeAccessVertex.java index 2e51342..a4a7600 100644 --- a/src/main/java/org/manifold/compiler/front/StaticAttributeAccessVertex.java +++ b/src/main/java/org/manifold/compiler/front/StaticAttributeAccessVertex.java @@ -43,7 +43,9 @@ public Value getValue() { return this.value; } - protected abstract Value getVal(TupleValue tupleValue); + protected abstract Value getValEntry(Value v) throws Exception; + + protected abstract TypeValue getTypeEntry(TypeValue t) throws Exception; /** * The string representation of the value used to access this attribute @@ -57,9 +59,10 @@ public void elaborate() throws Exception { vExpr.elaborate(); Value val = vExpr.getValue(); - TupleValue tupleValue = (TupleValue) val; - this.value = getVal(tupleValue); - this.type = this.value.getType(); + this.value = getValEntry(val); + + TypeValue t = vExpr.getType(); + this.type = getTypeEntry(t); } @Override @@ -117,8 +120,19 @@ protected String attributeToString() { } @Override - protected final Value getVal(TupleValue tupleValue) { - return tupleValue.entry(attributeID); + protected final Value getValEntry(Value v) throws Exception { + if (!(v instanceof NamedEntryValue)) { + throw new RuntimeException("Cannot get entry by attribute of " + v.toString()); + } + return ((NamedEntryValue) v).getEntry(attributeID); + } + + @Override + protected final TypeValue getTypeEntry(TypeValue t) throws Exception { + if (!(t instanceof NamedEntryTypeValue)) { + throw new RuntimeException("Cannot get entry by attribute of " + t.toString()); + } + return ((NamedEntryTypeValue) t).getEntry(attributeID); } public StaticStringAttributeAccessVertex(ExpressionGraph exprGraph, @@ -151,8 +165,19 @@ protected String attributeToString() { } @Override - protected final Value getVal(TupleValue tupleValue) { - return tupleValue.atIndex(attributeIDX); + protected final Value getValEntry(Value v) { + if (!(v instanceof TupleValue)) { + throw new RuntimeException("Cannot get entry by index of " + v.toString()); + } + return ((TupleValue) v).getEntry(attributeIDX); + } + + @Override + protected final TypeValue getTypeEntry(TypeValue t) throws Exception { + if (!(t instanceof TupleTypeValue)) { + throw new RuntimeException("Cannot get entry by attribute of " + t.toString()); + } + return ((TupleTypeValue) t).getEntry(attributeIDX); } public StaticNumberAttributeAccessVertex(ExpressionGraph exprGraph, diff --git a/src/main/java/org/manifold/compiler/front/TupleTypeValue.java b/src/main/java/org/manifold/compiler/front/TupleTypeValue.java index 9052db2..0b10f2e 100644 --- a/src/main/java/org/manifold/compiler/front/TupleTypeValue.java +++ b/src/main/java/org/manifold/compiler/front/TupleTypeValue.java @@ -4,7 +4,7 @@ import org.manifold.compiler.TypeValue; import org.manifold.compiler.UndefinedBehaviourError; -public class TupleTypeValue extends TypeValue { +public class TupleTypeValue extends TypeValue implements NamedEntryTypeValue { private final MappedArray subtypes; // TODO default values @@ -17,7 +17,11 @@ public int getSize() { return subtypes.size(); } - public TypeValue entry(int i){ + public TypeValue getEntry(String key) { + return subtypes.get(key); + } + + public TypeValue getEntry(int i){ return subtypes.get(i); } @@ -58,8 +62,8 @@ public boolean equals(Object other) { } // type-check subexpressions for equality for (int i = 0; i < getSize(); ++i) { - TypeValue myType = entry(i).getType(); - TypeValue otherType = oTuple.entry(i).getType(); + TypeValue myType = getEntry(i).getType(); + TypeValue otherType = oTuple.getEntry(i).getType(); if (!myType.equals(otherType)) { return false; } @@ -84,8 +88,8 @@ public boolean isSubtypeOf(TypeValue other) { } // type-check subexpressions for (int i = 0; i < getSize(); ++i) { - TypeValue myType = entry(i).getType(); - TypeValue otherType = oTuple.entry(i).getType(); + TypeValue myType = getEntry(i).getType(); + TypeValue otherType = oTuple.getEntry(i).getType(); if (!myType.isSubtypeOf(otherType)) { return false; } diff --git a/src/main/java/org/manifold/compiler/front/TupleValue.java b/src/main/java/org/manifold/compiler/front/TupleValue.java index 461b2a6..4a7620d 100644 --- a/src/main/java/org/manifold/compiler/front/TupleValue.java +++ b/src/main/java/org/manifold/compiler/front/TupleValue.java @@ -4,7 +4,7 @@ import org.manifold.compiler.UndefinedBehaviourError; import org.manifold.compiler.Value; -public class TupleValue extends Value { +public class TupleValue extends Value implements NamedEntryValue { private final MappedArray entries; @@ -16,15 +16,15 @@ public int getSize() { return entries.size(); } - public Value entry(String key) { + public Value getEntry(String key) { if (!entries.containsKey(key)) { throw new IllegalArgumentException("No value for entry " + key); } return entries.get(key); } - public Value atIndex(int idx) { - return entries.get(idx); + public Value getEntry(int i) { + return entries.get(i); } public TupleValue(TupleTypeValue type, MappedArray entries) { @@ -68,5 +68,4 @@ public void accept(SchematicValueVisitor v) { throw new UndefinedBehaviourError( "cannot accept non-frontend ValueVisitor into a frontend Value"); } - } diff --git a/tests/acceptance/import.manifold b/tests/acceptance/import.manifold index 6c835cc..330e0f9 100644 --- a/tests/acceptance/import.manifold +++ b/tests/acceptance/import.manifold @@ -1,7 +1,7 @@ -import "../input_pin.manifold"; +pin = import "../input_pin.manifold"; digitalIn = primitive port Bool; outputPin = primitive node (in: digitalIn) -> (Nil); -v = myfunc(a=in, b=in); +v = pin.myfunc(a=pin.in, b=pin.in); outputPin(in=v); diff --git a/tests/acceptance/import.manifold.schematic b/tests/acceptance/import.manifold.schematic index ce4c775..e616e3e 100644 --- a/tests/acceptance/import.manifold.schematic +++ b/tests/acceptance/import.manifold.schematic @@ -2,26 +2,26 @@ "name": "import.manifold", "userDefinedTypes": {}, "portTypes": { - "digitalOut": { + "digitalIn": { "signalType": "Bool", "attributes": {} }, - "digitalIn": { + "digitalOut": { "signalType": "Bool", "attributes": {} } }, "nodeTypes": { - "inputPin": { + "outputPin": { "attributes": {}, "ports": { - "out": "digitalOut" + "in": "digitalIn" } }, - "outputPin": { + "inputPin": { "attributes": {}, "ports": { - "in": "digitalIn" + "out": "digitalOut" } } }, @@ -35,10 +35,10 @@ } }, "n2": { - "type": "inputPin", + "type": "outputPin", "attributes": {}, "portAttrs": { - "out": {} + "in": {} } }, "n3": { @@ -49,18 +49,18 @@ } }, "n4": { - "type": "outputPin", + "type": "inputPin", "attributes": {}, "portAttrs": { - "in": {} + "out": {} } } }, "connections": { "c1": { "attributes": {}, - "from": "n2:out", - "to": "n4:in" + "from": "n1:out", + "to": "n2:in" } }, "constraints": {} diff --git a/tests/acceptance/importSystem.manifold b/tests/acceptance/importSystem.manifold index 77e0f98..ec124e1 100644 --- a/tests/acceptance/importSystem.manifold +++ b/tests/acceptance/importSystem.manifold @@ -1,4 +1,4 @@ -import "circuits"; +c = import "circuits"; -x = inputPin(); -outputPin(in=x); \ No newline at end of file +x = c.inputPin(); +c.outputPin(in=x); \ No newline at end of file diff --git a/tests/acceptance/importTuple.manifold b/tests/acceptance/importTuple.manifold index 9a61c2b..d89b207 100644 --- a/tests/acceptance/importTuple.manifold +++ b/tests/acceptance/importTuple.manifold @@ -1,6 +1,6 @@ -import "../input_pin"; +pin = import "../input_pin"; digitalIn = primitive port Bool; outputPin = primitive node (in: digitalIn) -> (Nil); -outputPin(in=x); +outputPin(in=pin.x); diff --git a/tests/acceptance/importTuple.manifold.schematic b/tests/acceptance/importTuple.manifold.schematic index d039177..4f42505 100644 --- a/tests/acceptance/importTuple.manifold.schematic +++ b/tests/acceptance/importTuple.manifold.schematic @@ -2,11 +2,11 @@ "name": "importTuple.manifold", "userDefinedTypes": {}, "portTypes": { - "digitalOut": { + "digitalIn": { "signalType": "Bool", "attributes": {} }, - "digitalIn": { + "digitalOut": { "signalType": "Bool", "attributes": {} } @@ -42,25 +42,25 @@ } }, "n3": { - "type": "inputPin", + "type": "outputPin", "attributes": {}, "portAttrs": { - "out": {} + "in": {} } }, "n4": { - "type": "outputPin", + "type": "inputPin", "attributes": {}, "portAttrs": { - "in": {} + "out": {} } } }, "connections": { "c1": { "attributes": {}, - "from": "n3:out", - "to": "n4:in" + "from": "n1:out", + "to": "n3:in" } }, "constraints": {} diff --git a/tests/buildErrors/importFileWithError.manifold.error b/tests/buildErrors/importFileWithError.manifold.error index abd8f55..1cc9fb7 100644 --- a/tests/buildErrors/importFileWithError.manifold.error +++ b/tests/buildErrors/importFileWithError.manifold.error @@ -1 +1,2 @@ Error at line 2, char 0: no viable alternative at input 'compile.' +Error while parsing included file parseError.manifold \ No newline at end of file diff --git a/tests/buildErrors/importRedefineVar.manifold b/tests/buildErrors/importRedefineVar.manifold deleted file mode 100644 index 6af3a11..0000000 --- a/tests/buildErrors/importRedefineVar.manifold +++ /dev/null @@ -1,3 +0,0 @@ -import "../input_pin"; - -in = inputPin(); diff --git a/tests/buildErrors/importRedefineVar.manifold.error b/tests/buildErrors/importRedefineVar.manifold.error deleted file mode 100644 index a2ddc49..0000000 --- a/tests/buildErrors/importRedefineVar.manifold.error +++ /dev/null @@ -1 +0,0 @@ -[in is defined multiple times] \ No newline at end of file From 0bf3a7ff145d67eed88359acf438b9b3d81d8bee Mon Sep 17 00:00:00 2001 From: Nik Klassen Date: Mon, 13 Jun 2016 13:10:06 -0400 Subject: [PATCH 10/11] Fix build error --- src/main/java/org/manifold/compiler/front/ImportVertex.java | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/java/org/manifold/compiler/front/ImportVertex.java b/src/main/java/org/manifold/compiler/front/ImportVertex.java index eed42db..d3d0f89 100644 --- a/src/main/java/org/manifold/compiler/front/ImportVertex.java +++ b/src/main/java/org/manifold/compiler/front/ImportVertex.java @@ -14,6 +14,7 @@ public class ImportVertex extends ExpressionVertex { private NamespaceIdentifier namespace; private ExpressionGraph includedGraph; private TypeValue type; + private Value value; public ImportVertex(ExpressionGraph exprGraph, ExpressionGraph includedGraph) { super(exprGraph); From cede96a249b8bcb1542f88a38882d621de710c00 Mon Sep 17 00:00:00 2001 From: Nik Klassen Date: Wed, 15 Jun 2016 14:20:23 -0400 Subject: [PATCH 11/11] Make file parsing lazier --- src/main/antlr4/Manifold.g4 | 6 +++--- .../front/ExpressionContextVisitor.java | 13 +----------- .../manifold/compiler/front/ImportVertex.java | 21 ++++++++++++++----- .../buildErrors/importFileWithError.manifold | 8 ++++++- .../importFileWithError.manifold.error | 2 +- 5 files changed, 28 insertions(+), 22 deletions(-) diff --git a/src/main/antlr4/Manifold.g4 b/src/main/antlr4/Manifold.g4 index 432d38d..77ea866 100644 --- a/src/main/antlr4/Manifold.g4 +++ b/src/main/antlr4/Manifold.g4 @@ -30,7 +30,7 @@ tupleValue: '(' ')'; functionTypeValue: tupleTypeValue '->' tupleTypeValue; -functionValue: functionTypeValue '{' (expression STATEMENT_TERMINATOR)* '}'; +functionValue: functionTypeValue '{' (expression EXPRESSION_TERMINATOR)* '}'; //////////////////////////////////////////////////////// // // @@ -98,7 +98,7 @@ expression: // | declaration ; -STATEMENT_TERMINATOR: ';'; +EXPRESSION_TERMINATOR: ';'; //////////////////////////////////////////////////////// @@ -107,4 +107,4 @@ STATEMENT_TERMINATOR: ';'; // // //////////////////////////////////////////////////////// -schematic: (expression STATEMENT_TERMINATOR)*; +schematic: (expression EXPRESSION_TERMINATOR)*; diff --git a/src/main/java/org/manifold/compiler/front/ExpressionContextVisitor.java b/src/main/java/org/manifold/compiler/front/ExpressionContextVisitor.java index 0704d98..a387341 100644 --- a/src/main/java/org/manifold/compiler/front/ExpressionContextVisitor.java +++ b/src/main/java/org/manifold/compiler/front/ExpressionContextVisitor.java @@ -24,7 +24,6 @@ class ExpressionContextVisitor extends ManifoldBaseVisitor { - private ExpressionGraphParser parser; private ExpressionGraph exprGraph; private File inputFile; private List errors; @@ -46,7 +45,6 @@ public ExpressionContextVisitor(File inputFile) { public ExpressionContextVisitor(ExpressionGraph exprGraph, File inputFile) { this.inputFile = inputFile; - this.parser = new ExpressionGraphParser(); this.exprGraph = exprGraph; this.errors = new ArrayList<>(); } @@ -366,16 +364,7 @@ public ExpressionVertex visitImportExpr(ImportExprContext context) { return null; } - ExpressionGraph g; - try { - g = parser.parseFile(importedFile); - } catch (Exception e) { - errors.add(e.getMessage().trim()); - errors.add("Error while parsing included file " + filePath); - return null; - } - - ExpressionVertex v = new ImportVertex(exprGraph, g); + ExpressionVertex v = new ImportVertex(exprGraph, importedFile); exprGraph.addVertex(v); return v; } diff --git a/src/main/java/org/manifold/compiler/front/ImportVertex.java b/src/main/java/org/manifold/compiler/front/ImportVertex.java index d3d0f89..5d39cc0 100644 --- a/src/main/java/org/manifold/compiler/front/ImportVertex.java +++ b/src/main/java/org/manifold/compiler/front/ImportVertex.java @@ -5,20 +5,21 @@ 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; public class ImportVertex extends ExpressionVertex { + private File importedFile; private NamespaceIdentifier namespace; - private ExpressionGraph includedGraph; private TypeValue type; private Value value; - public ImportVertex(ExpressionGraph exprGraph, ExpressionGraph includedGraph) { + public ImportVertex(ExpressionGraph exprGraph, File importedFile) { super(exprGraph); - this.includedGraph = includedGraph; + this.importedFile = importedFile; } @Override @@ -37,6 +38,16 @@ public void elaborate() throws Exception { return; } + ExpressionGraphParser parser = new ExpressionGraphParser(); + ExpressionGraph importedGraph; + try { + importedGraph = parser.parseFile(importedFile); + } catch (FrontendBuildException e) { + throw new FrontendBuildException( + "Could not parse " + importedFile.getName() + "\n" + + e.getMessage()); + } + ExpressionGraph exprGraph = getExpressionGraph(); List targets = exprGraph.getEdgesFromSource(this); if (targets.size() != 1) { @@ -48,7 +59,7 @@ public void elaborate() throws Exception { } namespace = new NamespaceIdentifier(((VariableReferenceVertex) target).getId().getName()); - exprGraph.addSubGraph(includedGraph, namespace); + exprGraph.addSubGraph(importedGraph, namespace); this.value = new ImportValue(getExpressionGraph(), namespace); this.type = this.value.getType(); } @@ -75,7 +86,7 @@ public void writeToDOTFile(BufferedWriter writer) throws IOException { @Override public ExpressionVertex copy(ExpressionGraph g, Map edgeMap) { - ImportVertex v = new ImportVertex(g, includedGraph); + ImportVertex v = new ImportVertex(g, importedFile); v.namespace = namespace; return v; } diff --git a/tests/buildErrors/importFileWithError.manifold b/tests/buildErrors/importFileWithError.manifold index d4408b7..21d133c 100644 --- a/tests/buildErrors/importFileWithError.manifold +++ b/tests/buildErrors/importFileWithError.manifold @@ -1 +1,7 @@ -import "parseError.manifold"; \ No newline at end of file +error = import "parseError.manifold"; + +digitalIn = primitive port Bool; +outputPin = primitive node (in: digitalIn) -> (Nil); + +// error isn't parsed until usage, so force the elaboration here +outputPin(error.foo); diff --git a/tests/buildErrors/importFileWithError.manifold.error b/tests/buildErrors/importFileWithError.manifold.error index 1cc9fb7..57d5dd9 100644 --- a/tests/buildErrors/importFileWithError.manifold.error +++ b/tests/buildErrors/importFileWithError.manifold.error @@ -1,2 +1,2 @@ +Could not parse parseError.manifold Error at line 2, char 0: no viable alternative at input 'compile.' -Error while parsing included file parseError.manifold \ No newline at end of file