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