From 3d565cd5157ef132300747ff3e35c338b6b721be Mon Sep 17 00:00:00 2001 From: Vivian Ding Date: Tue, 28 Nov 2023 16:28:52 -0500 Subject: [PATCH 1/2] Add test files --- examples/inputs/circuit/aby/Interesting-alice.txt | 0 examples/inputs/circuit/aby/Interesting-bob.txt | 0 examples/inputs/circuit/aby/Simple-alice.txt | 0 examples/inputs/circuit/aby/Simple-bob.txt | 0 examples/inputs/circuit/tmp-alice.txt | 1 + examples/inputs/circuit/tmp-bob.txt | 0 examples/outputs/circuit/aby/Interesting-alice.txt | 0 examples/outputs/circuit/aby/Interesting-bob.txt | 0 examples/outputs/circuit/aby/Simple-alice.txt | 0 examples/outputs/circuit/aby/Simple-bob.txt | 0 examples/outputs/circuit/tmp-alice.txt | 3 +++ examples/outputs/circuit/tmp-bob.txt | 0 12 files changed, 4 insertions(+) create mode 100755 examples/inputs/circuit/aby/Interesting-alice.txt create mode 100755 examples/inputs/circuit/aby/Interesting-bob.txt create mode 100755 examples/inputs/circuit/aby/Simple-alice.txt create mode 100755 examples/inputs/circuit/aby/Simple-bob.txt create mode 100755 examples/inputs/circuit/tmp-alice.txt create mode 100755 examples/inputs/circuit/tmp-bob.txt create mode 100644 examples/outputs/circuit/aby/Interesting-alice.txt create mode 100644 examples/outputs/circuit/aby/Interesting-bob.txt create mode 100755 examples/outputs/circuit/aby/Simple-alice.txt create mode 100755 examples/outputs/circuit/aby/Simple-bob.txt create mode 100755 examples/outputs/circuit/tmp-alice.txt create mode 100755 examples/outputs/circuit/tmp-bob.txt diff --git a/examples/inputs/circuit/aby/Interesting-alice.txt b/examples/inputs/circuit/aby/Interesting-alice.txt new file mode 100755 index 0000000000..e69de29bb2 diff --git a/examples/inputs/circuit/aby/Interesting-bob.txt b/examples/inputs/circuit/aby/Interesting-bob.txt new file mode 100755 index 0000000000..e69de29bb2 diff --git a/examples/inputs/circuit/aby/Simple-alice.txt b/examples/inputs/circuit/aby/Simple-alice.txt new file mode 100755 index 0000000000..e69de29bb2 diff --git a/examples/inputs/circuit/aby/Simple-bob.txt b/examples/inputs/circuit/aby/Simple-bob.txt new file mode 100755 index 0000000000..e69de29bb2 diff --git a/examples/inputs/circuit/tmp-alice.txt b/examples/inputs/circuit/tmp-alice.txt new file mode 100755 index 0000000000..00750edc07 --- /dev/null +++ b/examples/inputs/circuit/tmp-alice.txt @@ -0,0 +1 @@ +3 diff --git a/examples/inputs/circuit/tmp-bob.txt b/examples/inputs/circuit/tmp-bob.txt new file mode 100755 index 0000000000..e69de29bb2 diff --git a/examples/outputs/circuit/aby/Interesting-alice.txt b/examples/outputs/circuit/aby/Interesting-alice.txt new file mode 100644 index 0000000000..e69de29bb2 diff --git a/examples/outputs/circuit/aby/Interesting-bob.txt b/examples/outputs/circuit/aby/Interesting-bob.txt new file mode 100644 index 0000000000..e69de29bb2 diff --git a/examples/outputs/circuit/aby/Simple-alice.txt b/examples/outputs/circuit/aby/Simple-alice.txt new file mode 100755 index 0000000000..e69de29bb2 diff --git a/examples/outputs/circuit/aby/Simple-bob.txt b/examples/outputs/circuit/aby/Simple-bob.txt new file mode 100755 index 0000000000..e69de29bb2 diff --git a/examples/outputs/circuit/tmp-alice.txt b/examples/outputs/circuit/tmp-alice.txt new file mode 100755 index 0000000000..bb0b1cf658 --- /dev/null +++ b/examples/outputs/circuit/tmp-alice.txt @@ -0,0 +1,3 @@ +0 +0 +0 diff --git a/examples/outputs/circuit/tmp-bob.txt b/examples/outputs/circuit/tmp-bob.txt new file mode 100755 index 0000000000..e69de29bb2 From 625cf7a8099df25b88a25cae37a2d44021b26595 Mon Sep 17 00:00:00 2001 From: Vivian Ding Date: Tue, 28 Nov 2023 17:55:13 -0500 Subject: [PATCH 2/2] Precircuit init --- .../syntax/precircuit/PrecircuitParser.cup | 775 ++++++++++++++++++ .../syntax/precircuit/PrecircuitLexer.jflex | 205 +++++ .../syntax/precircuit/ArrayTypeNode.kt | 19 + .../viaduct/syntax/precircuit/Expressions.kt | 89 ++ .../syntax/precircuit/IndexParameterNode.kt | 16 + .../viaduct/syntax/precircuit/Node.kt | 7 + .../viaduct/syntax/precircuit/Parsing.kt | 27 + .../viaduct/syntax/precircuit/ProgramNode.kt | 52 ++ .../viaduct/syntax/precircuit/Statements.kt | 235 ++++++ .../syntax/precircuit/TopLevelDeclarations.kt | 70 ++ .../viaduct/syntax/precircuit/Variable.kt | 17 + .../precircuit/VariableDeclarationNode.kt | 7 + 12 files changed, 1519 insertions(+) create mode 100755 compiler/src/main/cup/io/github/aplcornell/viaduct/syntax/precircuit/PrecircuitParser.cup create mode 100644 compiler/src/main/jflex/io/github/aplcornell/viaduct/syntax/precircuit/PrecircuitLexer.jflex create mode 100644 compiler/src/main/kotlin/io/github/aplcornell/viaduct/syntax/precircuit/ArrayTypeNode.kt create mode 100644 compiler/src/main/kotlin/io/github/aplcornell/viaduct/syntax/precircuit/Expressions.kt create mode 100644 compiler/src/main/kotlin/io/github/aplcornell/viaduct/syntax/precircuit/IndexParameterNode.kt create mode 100644 compiler/src/main/kotlin/io/github/aplcornell/viaduct/syntax/precircuit/Node.kt create mode 100644 compiler/src/main/kotlin/io/github/aplcornell/viaduct/syntax/precircuit/Parsing.kt create mode 100644 compiler/src/main/kotlin/io/github/aplcornell/viaduct/syntax/precircuit/ProgramNode.kt create mode 100755 compiler/src/main/kotlin/io/github/aplcornell/viaduct/syntax/precircuit/Statements.kt create mode 100644 compiler/src/main/kotlin/io/github/aplcornell/viaduct/syntax/precircuit/TopLevelDeclarations.kt create mode 100644 compiler/src/main/kotlin/io/github/aplcornell/viaduct/syntax/precircuit/Variable.kt create mode 100644 compiler/src/main/kotlin/io/github/aplcornell/viaduct/syntax/precircuit/VariableDeclarationNode.kt diff --git a/compiler/src/main/cup/io/github/aplcornell/viaduct/syntax/precircuit/PrecircuitParser.cup b/compiler/src/main/cup/io/github/aplcornell/viaduct/syntax/precircuit/PrecircuitParser.cup new file mode 100755 index 0000000000..6d52c0a184 --- /dev/null +++ b/compiler/src/main/cup/io/github/aplcornell/viaduct/syntax/precircuit/PrecircuitParser.cup @@ -0,0 +1,775 @@ +import io.github.aplcornell.viaduct.errors.NoMainError; +import io.github.aplcornell.viaduct.errors.ParsingError; +import io.github.aplcornell.viaduct.security.*; +import io.github.aplcornell.viaduct.syntax.*; +import io.github.aplcornell.viaduct.syntax.operators.*; +import io.github.aplcornell.viaduct.syntax.types.*; +import io.github.aplcornell.viaduct.syntax.values.*; +import io.github.aplcornell.viaduct.parsing.*; + +import java.util.List; +import java.util.LinkedList; +import java.util.Map; +import kotlin.collections.CollectionsKt; +import kotlin.Pair; + +import java_cup.runtime.ComplexSymbolFactory; +import java_cup.runtime.ComplexSymbolFactory.Location; +import java_cup.runtime.Symbol; + + +action code {: + /** Constructs a binary expression node. Source location is inferred from the arguments. */ + private OperatorApplicationNode binaryExpression(BinaryOperator op, ExpressionNode lhs, ExpressionNode rhs) { + Arguments arguments = Arguments.from(lhs, rhs); + SourceRange sourceLocation = lhs.getSourceLocation().merge(rhs.getSourceLocation()); + SourceRange opLocation = new SourceRange(lhs.getSourceLocation().getEnd(), rhs.getSourceLocation().getStart()); + return new OperatorApplicationNode(new OperatorNode(op, opLocation), arguments, sourceLocation); + } +:} + +parser code {: + Map> protocolParsers; + + /** Generates source location information for a node given left and right character offsets. */ + private SourceRange location(int left, int right) { + return ((PrecircuitLexer) getScanner()).location(left, right); + } + + @Override + public void report_error(String message, Object info) { + report_fatal_error(message, info); + } + + @Override + public void report_fatal_error(String message, Object info) { + done_parsing(); + + final Symbol infoSymbol = (Symbol) info; + final SourceRange errorLocation = location(infoSymbol.left, infoSymbol.right); + final String actualToken = symbl_name_from_id(infoSymbol.sym); + final List expectedTokens = new LinkedList<>(); + for (Integer id : expected_token_ids()) { + expectedTokens.add(symbl_name_from_id(id)); + } + throw new ParsingError(errorLocation, actualToken, expectedTokens); + } + + + // Used to support multiple start symbols. + private Symbol current_symbol; + + private void set_current_symbol(int symbol) { + final Location location = new Location(0, 0, 0); + current_symbol = + ((ComplexSymbolFactory) getSymbolFactory()) + .newSymbol(symbl_name_from_id(symbol), symbol, location, location); + } + + /** Parses the input as a program. */ + public ProgramNode parseProgram() throws Exception { + set_current_symbol(sym.PARSE_PROGRAM); + return (ProgramNode) parse().value; + } +:} + +scan with {: + if (current_symbol != null) { + final Symbol result = current_symbol; + current_symbol = null; + return result; + } + return getScanner().next_token(); +:}; + +nonterminal ProgramNode program; +nonterminal List declaration_list; +nonterminal TopLevelDeclarationNode declaration; + +nonterminal ReturnNode return; +nonterminal BlockNode block; +nonterminal List stmt_list; +nonterminal StatementNode stmt; +nonterminal CommandNode command; +nonterminal BlockNode circ_block; +nonterminal List circ_stmt_list; +nonterminal CircuitStatementNode circ_stmt; + +nonterminal ExpressionNode expr; +nonterminal IndexExpressionNode index_expr; +nonterminal List index_expr_list, nonempty_index_expr_list; +nonterminal ReferenceNode reference; +nonterminal List reference_list, nonempty_reference_list; +nonterminal LiteralNode literal; +nonterminal Located variable; +nonterminal SizeParameterNode size; +nonterminal List size_list, nonempty_size_list; +nonterminal List expr_list, nonempty_expr_list; +nonterminal Located argument_label; +nonterminal IndexParameterNode index_param; +nonterminal List index_param_list, nonempty_index_param_list; +nonterminal Operator operator; + +nonterminal Value value; +nonterminal Located value_type; +nonterminal ArrayTypeNode array_type; + +nonterminal Located host; +nonterminal List host_list, nonempty_host_list; +nonterminal HostSetValue host_set; +nonterminal Located label, optional_from_label, optional_to_label; +nonterminal LabelExpression label_expr; + +nonterminal List parameter_list, nonempty_parameter_list; +nonterminal ParameterNode parameter; + +nonterminal List var_binding_list, nonempty_var_binding_list; +nonterminal VariableBindingNode var_binding; + +nonterminal Located protocol; +nonterminal Located protocol_annot; +nonterminal Located protocol_name; +nonterminal Pair, Located> protocol_labelled_argument; +nonterminal List protocol_argument_list, nonempty_protocol_argument_list; +nonterminal Value protocol_argument; + +terminal String IDENT, CAP_IDENT; +terminal int INT_LIT; +terminal TRUE, FALSE; + +terminal HOST; +terminal FUNCTION; +terminal CIRCUIT; +terminal RETURN; +terminal COLON; +terminal COLONCOLON; + +terminal PERIOD; + +terminal IF, ELSE, LOOP, BREAK; +terminal INPUT, FROM, OUTPUT, TO; +terminal DECLASSIFY, ENDORSE; +terminal TOP, BOTTOM, OR, AND, JOIN, MEET, RARROW, LARROW; + +terminal REDUCE; + +terminal EQ; + +terminal VAL; +terminal NOT, ANDAND, OROR; +terminal EQEQ, NEQ, LT, LEQ, GT, GEQ; +terminal PLUS, MINUS, TIMES, DIVIDE; +terminal MIN, MAX, MUX; + +terminal INT, BOOL, UNIT; +terminal AT; + +terminal OPEN_BRACE, CLOSE_BRACE, SEMICOLON; +terminal OPEN_SQBRACE, CLOSE_SQBRACE; +terminal OPEN_PAREN, CLOSE_PAREN, COMMA; + +// Used to support multiple start symbols +nonterminal entry_point; +terminal Symbol PARSE_PROGRAM; + +precedence left ANDAND, OROR, AND, OR, JOIN, MEET; +precedence nonassoc EQEQ, NEQ, LT, LEQ, GT, GEQ; +precedence left PLUS, MINUS; +precedence left TIMES, DIVIDE; +precedence right NOT, MIN; +precedence left LARROW, RARROW; +precedence nonassoc OPEN_PAREN, CLOSE_PAREN; + +start with entry_point; + +entry_point ::= + PARSE_PROGRAM program:s {: RESULT = s; :} + ; + +program ::= + declaration_list:declarations {: + if (declarations.isEmpty()) { + /* Empty program */ + throw new NoMainError(((PrecircuitLexer) getScanner()).sourceFile.getPath()); + } + RESULT = new ProgramNode(declarations, location(declarationsleft, declarationsright)); + :} + ; + +declaration_list ::= + declaration_list:declarations declaration:declaration {: + declarations.add(declaration); + RESULT = declarations; + :} + | /* empty */ {: + RESULT = CollectionsKt.mutableListOf(); + :} + ; + +declaration ::= + HOST:begin host:host {: + RESULT = new HostDeclarationNode(host, location(beginleft, hostright)); + :} + | FUNCTION:begin LT:sizesopen size_list:sizes GT:sizesclose IDENT:funcname + OPEN_PAREN:paramsbegin parameter_list:inparams CLOSE_PAREN:paramsclose RARROW:arrow parameter_list:outparams block:body {: + RESULT = new FunctionDeclarationNode( + new Located(new FunctionName(funcname), location(funcnameleft, funcnameright)), + new Arguments(sizes, location(sizesopenleft, sizescloseright)), + new Arguments(inparams, location(paramsbeginleft, paramsbeginright)), + new Arguments(outparams, location(arrowleft, arrowright)), + body, + location(beginleft, bodyright) + ); + :} + ; + +size_list ::= + nonempty_size_list:bs {: + RESULT = bs; + :} + | /* empty */ {: + RESULT = CollectionsKt.mutableListOf(); + :} + ; + +nonempty_size_list ::= + nonempty_size_list:bs COMMA size:b {: + bs.add(b); + RESULT = bs; + :} + | size:b {: + RESULT = CollectionsKt.mutableListOf(b); + :} + ; + +size ::= + variable:var {: RESULT = new SizeParameterNode(var, location(varleft, varright)); :}; + +parameter_list ::= + nonempty_parameter_list:params {: + RESULT = params; + :} + | /* empty */ {: + RESULT = CollectionsKt.mutableListOf(); + :} + ; + +nonempty_parameter_list ::= + nonempty_parameter_list:params COMMA parameter:param {: + params.add(param); + RESULT = params; + :} + | parameter:param {: + RESULT = CollectionsKt.mutableListOf(param); + :} + ; + +parameter ::= + variable:name COLON array_type:type {: + RESULT = new ParameterNode( + name, + type, + location(nameleft, typeright) + ); + :} + ; + +return ::= + RETURN:begin reference_list:values {: + SourceRange valuesLoc = (values.size() == 0) ? location(beginleft, valuesright) : location(valuesleft, valuesright); + RESULT = new ReturnNode( + new Arguments(values, valuesLoc), + location(beginleft, valuesright) + ); + :} + ; + +var_binding_list ::= + nonempty_var_binding_list:var_bindings {: + RESULT = var_bindings; + :} + | /* empty */ {: + RESULT = CollectionsKt.mutableListOf(); + :} + ; + +nonempty_var_binding_list ::= + nonempty_var_binding_list:var_bindings COMMA var_binding:r {: + var_bindings.add(r); + RESULT = var_bindings; + :} + | var_binding:r {: + RESULT = CollectionsKt.mutableListOf(r); + :} + ; + +var_binding ::= + variable:name AT protocol:p {: + RESULT = new VariableBindingNode( + name, + p, + location(nameleft, pright) + ); + :} + ; + +block ::= + OPEN_BRACE:begin stmt_list:statements return:ret CLOSE_BRACE:end {: + RESULT = new BlockNode(statements, ret, location(beginleft, endright)); + :} + ; + +stmt_list ::= + stmt_list:slist stmt:s {: + slist.add(s); + RESULT = slist; + :} + | /* empty */ {: + RESULT = CollectionsKt.mutableListOf(); + :} + ; + +stmt ::= + VAL:begin var_binding_list:varbindings EQ:e command:rhs {: + RESULT = new LetNode( + new Arguments(varbindings, location(varbindingsleft, eright)), + rhs, + location(beginleft, rhsright) + ); + :} + | VAL:begin variable:var OPEN_SQBRACE:open index_param_list:indices CLOSE_SQBRACE:close COLON array_type:type AT protocol:p EQ expr:e {: + RESULT = new ComputeLetNode( + var, + new Arguments(indices, location(openleft, closeright)), + type, + p, + e, + location(beginleft, eright) + ); + :} +; + +command ::= + IDENT:funcname LT:ibegin index_expr_list:inds GT:iend + OPEN_PAREN:abegin reference_list:args CLOSE_PAREN:aend {: + RESULT = new CallNode( + new Located(new FunctionName(funcname), location(funcnameleft, funcnameright)), + new Arguments(inds, location(ibeginleft, iendright)), + new Arguments(args, location(abeginleft, aendright)), + location(funcnameleft, aendright) + ); + :} + | host:sender PERIOD INPUT LT array_type:type GT OPEN_PAREN CLOSE_PAREN:end {: + RESULT = new InputNode(type, sender, location(senderleft, endright)); + :} + | host:recipient PERIOD OUTPUT LT array_type:type GT OPEN_PAREN reference:message CLOSE_PAREN:end {: + RESULT = new OutputNode(type, message, recipient, location(recipientleft, endright)); + :} + | IF:begin OPEN_PAREN index_expr:guard CLOSE_PAREN block:thenbranch ELSE block:elsebranch {: + RESULT = new IfNode(guard, thenbranch, elsebranch, location(beginleft, elsebranchright)); + :} + | LOOP:begin block:body {: + RESULT = new LoopNode(body, location(beginleft, bodyright)); + :} + | BREAK:b {: RESULT = new BreakNode(location(bleft,bright)); :} + | DECLASSIFY:begin reference:e optional_from_label:from TO label:to {: + RESULT = new DeclassificationNode(e, from, to, location(beginleft, toright)); + :} + // invert FROM and TO labels to prevent shift-reduce conflict + | ENDORSE:begin reference:e optional_to_label:to FROM label:from {: + RESULT = new EndorsementNode(e, from, to, location(beginleft, fromright)); + :} +; + +optional_from_label ::= + FROM label:l {: RESULT = l; :} + | /* empty */ {: RESULT = null; :} + ; + +optional_to_label ::= + TO label:l {: RESULT = l; :} + | /* empty */ {: RESULT = null; :} + ; + +label ::= + OPEN_BRACE:begin label_expr:l CLOSE_BRACE:end {: + RESULT = new Located(l, location(beginleft, endright)); + :} + ; + +label_expr ::= + IDENT:id {: RESULT = new LabelLiteral(new Host(id)); :} + + | CAP_IDENT:id {: RESULT = new LabelParameter(new LabelVariable(id)); :} + + | TOP {: RESULT = LabelTop.INSTANCE; :} + + | BOTTOM {: RESULT = LabelBottom.INSTANCE; :} + + | label_expr:l1 OR label_expr:l2 {: + RESULT = new LabelOr(l1, l2); + :} + + | label_expr:l1 AND label_expr:l2 {: + RESULT = new LabelAnd(l1, l2); + :} + + | label_expr:l1 JOIN label_expr:l2 {: + RESULT = new LabelJoin(l1, l2); + :} + + | label_expr:l1 MEET label_expr:l2 {: + RESULT = new LabelMeet(l1, l2); + :} + + | label_expr:l RARROW {: + RESULT = new LabelConfidentiality(l); + :} + + | label_expr:l LARROW {: + RESULT = new LabelIntegrity(l); + :} + + | OPEN_PAREN label_expr:l CLOSE_PAREN {: + RESULT = l; + :} + ; + +variable ::= + IDENT:id {: + RESULT = new Located(new Variable(id), location(idleft, idright)); + :} + ; + +index_param_list ::= + nonempty_index_param_list:plist {: RESULT = plist; :} + | /* empty */ {: + RESULT = CollectionsKt.mutableListOf(); + :} + ; + +nonempty_index_param_list ::= + nonempty_index_param_list:elist COMMA index_param:e {: + elist.add(e); + RESULT = elist; + :} + | index_param:e {: + RESULT = CollectionsKt.mutableListOf(e); + :} + ; + +index_param ::= + variable:var LT index_expr:bound {: + RESULT = new IndexParameterNode(var, bound, location(varleft, boundright)); + :} + ; + +array_type ::= + value_type:elementtype OPEN_SQBRACE:open index_expr_list:shape CLOSE_SQBRACE:close {: + RESULT = new ArrayTypeNode( + elementtype, + new Arguments(shape, location(openleft, closeright)), + location(elementtypeleft, closeright) + ); + :} + ; + +value_type ::= + INT:type {: + RESULT = new Located(IntegerType.INSTANCE, location(typeleft, typeright)); + :} + | BOOL:type {: + RESULT = new Located(BooleanType.INSTANCE, location(typeleft, typeright)); + :} + | UNIT:type {: + RESULT = new Located(UnitType.INSTANCE, location(typeleft, typeright)); + :} + ; + +index_expr_list ::= + nonempty_index_expr_list:elist {: RESULT = elist; :} + | /* empty */ {: + RESULT = CollectionsKt.mutableListOf(); + :} + ; + +nonempty_index_expr_list ::= + nonempty_index_expr_list:elist COMMA index_expr:e {: + elist.add(e); + RESULT = elist; + :} + | index_expr:e {: + RESULT = CollectionsKt.mutableListOf(e); + :} + ; + +reference_list ::= + nonempty_reference_list:rlist {: RESULT = rlist; :} + | /* empty */ {: + RESULT = CollectionsKt.mutableListOf(); + :} + ; + +nonempty_reference_list ::= + nonempty_reference_list:rlist COMMA reference:r {: + rlist.add(r); + RESULT = rlist; + :} + | reference:r {: + RESULT = CollectionsKt.mutableListOf(r); + :} + ; + +index_expr ::= + literal:lit {: RESULT = lit; :} + | reference:var {: RESULT = var; :}; + +literal ::= + value:value {: + RESULT = new LiteralNode(value, location(valueleft, valueright)); + :} + ; + +reference ::= variable:var {: RESULT = new ReferenceNode(var, location(varleft, varright)); :}; + +expr ::= + index_expr:exp {: RESULT = exp; :} + | OPEN_PAREN expr:e CLOSE_PAREN {: + RESULT = e; + :} + /* variable/array lookup */ + | variable:var OPEN_SQBRACE:open index_expr_list:indices CLOSE_SQBRACE:close {: + RESULT = new LookupNode( + var, + new Arguments(indices, location(openleft, closeright)), + location(varleft, closeright) + ); + :} + | REDUCE:begin OPEN_PAREN COLONCOLON operator:op COMMA expr:defaultvalue CLOSE_PAREN OPEN_BRACE:bodybegin + index_param:params RARROW:arrow expr:expression CLOSE_BRACE:bodyend {: + RESULT = new ReduceNode( + new OperatorNode(op, location(opleft, opright)), + defaultvalue, + params, + expression, + location(beginleft, bodyendright) + ); + :} + | NOT:begin expr:e {: + RESULT = new OperatorApplicationNode( + new OperatorNode(Not.INSTANCE, location(beginleft, beginright)), + Arguments.from(e), + location(beginleft, eright)); + :} + | MINUS:begin expr:e {: + RESULT = new OperatorApplicationNode( + new OperatorNode(Negation.INSTANCE, location(beginleft, beginright)), + Arguments.from(e), + location(beginleft, eright)); + :} + | expr:e1 ANDAND expr:e2 {: + RESULT = binaryExpression(And.INSTANCE, e1, e2); + :} + | expr:e1 OROR expr:e2 {: + RESULT = binaryExpression(Or.INSTANCE, e1, e2); + :} + | expr:e1 AND expr:e2 {: + RESULT = binaryExpression(ExclusiveOr.INSTANCE, e1, e2); + :} + | expr:e1 EQEQ expr:e2 {: + RESULT = binaryExpression(EqualTo.INSTANCE, e1, e2); + :} + | expr:e1 NEQ expr:e2 {: + RESULT = binaryExpression(ExclusiveOr.INSTANCE, e1, e2); + :} + | expr:e1 LT expr:e2 {: + RESULT = binaryExpression(LessThan.INSTANCE, e1, e2); + :} + | expr:e1 LEQ expr:e2 {: + RESULT = binaryExpression(LessThanOrEqualTo.INSTANCE, e1, e2); + :} + | expr:e1 GT expr:e2 {: + RESULT = binaryExpression(GreaterThan.INSTANCE, e1, e2); + :} + | expr:e1 GEQ expr:e2 {: + RESULT = binaryExpression(GreaterThanOrEqualTo.INSTANCE, e1, e2); + :} + | expr:e1 PLUS expr:e2 {: + RESULT = binaryExpression(Addition.INSTANCE, e1, e2); + :} + | expr:e1 MINUS expr:e2 {: + RESULT = binaryExpression(Subtraction.INSTANCE, e1, e2); + :} + | expr:e1 TIMES expr:e2 {: + RESULT = binaryExpression(Multiplication.INSTANCE, e1, e2); + :} + | expr:e1 DIVIDE expr:e2 {: + RESULT = binaryExpression(Division.INSTANCE, e1, e2); + :} + | MIN:begin OPEN_PAREN expr:e1 COMMA expr:e2 CLOSE_PAREN:end {: + RESULT = binaryExpression(Minimum.INSTANCE, e1, e2); + :} + | MAX:begin OPEN_PAREN expr:e1 COMMA expr:e2 CLOSE_PAREN:end {: + RESULT = binaryExpression(Maximum.INSTANCE, e1, e2); + :} + | MUX:begin OPEN_PAREN expr:e1 COMMA expr:e2 COMMA expr:e3 CLOSE_PAREN:end {: + Arguments arguments = Arguments.from(e1, e2, e3); + RESULT = new OperatorApplicationNode( + new OperatorNode(Mux.INSTANCE, location(beginleft, beginright)), + arguments, + location(beginleft, endright) + ); + :} + ; + +operator ::= + NOT {: RESULT = Not.INSTANCE; :} + | ANDAND {: RESULT = And.INSTANCE; :} + | OROR {: RESULT = Or.INSTANCE; :} + | AND {: RESULT = ExclusiveOr.INSTANCE; :} + | EQEQ {: RESULT = EqualTo.INSTANCE; :} + | NEQ {: RESULT = ExclusiveOr.INSTANCE; :} + | LT {: RESULT = LessThan.INSTANCE; :} + | LEQ {: RESULT = LessThanOrEqualTo.INSTANCE; :} + | GT {: RESULT = GreaterThan.INSTANCE; :} + | GEQ {: RESULT = GreaterThanOrEqualTo.INSTANCE; :} + | PLUS {: RESULT = Addition.INSTANCE; :} + | MINUS {: RESULT = Subtraction.INSTANCE; :} + | TIMES {: RESULT = Multiplication.INSTANCE; :} + | DIVIDE {: RESULT = Division.INSTANCE; :} + | MIN {: RESULT = Minimum.INSTANCE; :} + | MAX {: RESULT = Maximum.INSTANCE; :} + | MUX {: RESULT = Mux.INSTANCE; :} +; + +expr_list ::= + nonempty_expr_list:elist {: + RESULT = elist; + :} + | /* empty */ {: + RESULT = CollectionsKt.listOf(); + :} + ; + +nonempty_expr_list ::= + nonempty_expr_list:elist COMMA expr:e {: + elist.add(e); + RESULT = elist; + :} + | expr:e {: + RESULT = CollectionsKt.mutableListOf(e); + :} + ; + +value ::= + TRUE {: + RESULT = new BooleanValue(true); + :} + | FALSE {: + RESULT = new BooleanValue(false); + :} + | INT_LIT:literal {: + RESULT = new IntegerValue(literal); + :} + | UNIT {: + RESULT = UnitValue.INSTANCE; + :} + ; + +host ::= + IDENT:id {: + RESULT = new Located(new Host(id), location(idleft, idright)); + :} + ; + +host_list ::= + nonempty_host_list:hosts {: + RESULT = hosts; + :} + | /* empty */ {: + RESULT = CollectionsKt.mutableListOf(); + :} + ; + +nonempty_host_list ::= + nonempty_host_list:hosts COMMA host:host {: + hosts.add(host); + RESULT = hosts; + :} + | host:host {: + RESULT = CollectionsKt.mutableListOf(host); + :} + ; + +host_set ::= + OPEN_BRACE host_list:hosts CLOSE_BRACE {: + RESULT = HostSetValue.invoke(hosts); + :} + ; + +protocol_annot ::= + AT protocol:p {: RESULT = p; :} + | /* empty */ {: RESULT = null; :} + ; + +protocol ::= + protocol_name:protocolName OPEN_PAREN:open protocol_argument_list:arguments CLOSE_PAREN:close {: + RESULT = new Located( + ProtocolParserKt.parseProtocol( + protocolParsers, + protocolName, + NamedArguments.invoke(arguments, location(openleft, closeright))), + location(protocolNameleft, closeright)); + :} + ; + +protocol_name ::= + CAP_IDENT:protocolName {: + RESULT = new Located(new ProtocolName(protocolName), location(protocolNameleft, protocolNameright)); + :} + ; + +protocol_argument_list ::= + nonempty_protocol_argument_list:arguments {: + RESULT = arguments; + :} + | /* empty */ {: + RESULT = CollectionsKt.listOf(); + :} + ; + +nonempty_protocol_argument_list ::= + nonempty_protocol_argument_list:arguments COMMA protocol_labelled_argument:argument {: + arguments.add(argument); + RESULT = arguments; + :} + | protocol_labelled_argument:argument {: + RESULT = CollectionsKt.mutableListOf(argument); + :} + ; + +protocol_labelled_argument ::= + argument_label:label EQ protocol_argument:value {: + RESULT = new Pair( + label, + new Located(value, location(valueleft, valueright)) + ); + :} + ; + +argument_label ::= + IDENT:label {: + RESULT = new Located(new ArgumentLabel(label), location(labelleft, labelright)); + :} + /* Parse the "host" keyword as a label also. */ + // TODO: find a more generic solution (lexer states?) + | HOST:label {: + RESULT = new Located(new ArgumentLabel("host"), location(labelleft, labelright)); + :} + ; + +protocol_argument ::= + host:host {: RESULT = new HostValue(host.getValue()); :} + | host_set:hostSet {: RESULT = hostSet; :} + | value:value {: RESULT = value; :} + ; diff --git a/compiler/src/main/jflex/io/github/aplcornell/viaduct/syntax/precircuit/PrecircuitLexer.jflex b/compiler/src/main/jflex/io/github/aplcornell/viaduct/syntax/precircuit/PrecircuitLexer.jflex new file mode 100644 index 0000000000..f82d63a57a --- /dev/null +++ b/compiler/src/main/jflex/io/github/aplcornell/viaduct/syntax/precircuit/PrecircuitLexer.jflex @@ -0,0 +1,205 @@ +package io.github.aplcornell.viaduct.syntax.precircuit; + +import io.github.aplcornell.viaduct.errors.IllegalCharacterError; + +import java.io.Reader; + +import java_cup.runtime.ComplexSymbolFactory; +import java_cup.runtime.ComplexSymbolFactory.Location; +import java_cup.runtime.Symbol; + +import io.github.aplcornell.viaduct.parsing.*; + + +%% + +%class PrecircuitLexer +%unicode +%cup + +/* Turn on character counting to get access to source locations. */ +%char + +%{ + final SourceFile sourceFile; + private final ComplexSymbolFactory symbolFactory; + private int commentLevel = 0; + + /** + * Constructs a new lexer. + * + * @param sourceFile input file + * @param symbolFactory generates symbols with source location information + */ + public PrecircuitLexer(SourceFile sourceFile, ComplexSymbolFactory symbolFactory) { + this(sourceFile.createReader(), sourceFile, symbolFactory); + } + + /** Generates a source location given left and right character offsets. */ + public SourceRange location(int left, int right) { + final SourcePosition start = new SourcePosition(sourceFile, left); + final SourcePosition end = new SourcePosition(sourceFile, right); + return new SourceRange(start, end); + } + + /** + * Constructs a Symbol with information about source location. + */ + private Symbol symbol(int code) { + return symbol(code, null); + } + + /** + * Constructs a Symbol with information about source location. + * Additionally stores a value. + */ + private Symbol symbol(int code, Object value) { + final int leftOffset = (int) yychar; + final int rightOffset = (int) yychar + yylength(); + + // TODO: make sure line and column numbers are never used, or give them nice values. + final Location left = new Location(-1, -1, leftOffset); + final Location right = new Location(-1, -1, rightOffset); + + return symbolFactory.newSymbol(sym.terminalNames[code], code, left, right, value); + } +%} + + +/* Add these as arguments to the constructor. */ +%ctorarg SourceFile sourceFile +%ctorarg ComplexSymbolFactory symbolFactory + +%init{ + this.sourceFile = sourceFile; + this.symbolFactory = symbolFactory; +%init} + + +/* White space is a line terminator, space, or tab. */ +Whitespace = \R | [ \t\f] + +ALPHANUM = [a-z]([A-Za-z0-9_])* +CAPALPHANUM = [A-Z]([A-Za-z0-9_])* +NUM = ((-)?[1-9][0-9]*) | 0 + + +/* Lexer states in addition to the default. */ +%state COMMENT + + +%% + + { + /* Top-level declarations */ + "host" { return symbol(sym.HOST); } + "fun" { return symbol(sym.FUNCTION); } + "circuit" { return symbol(sym.CIRCUIT); } + "return" { return symbol(sym.RETURN); } + + /* Types */ + "int" { return symbol(sym.INT); } + "bool" { return symbol(sym.BOOL); } + "unit" { return symbol(sym.UNIT); } + + /* Labels */ + "⊤" { return symbol(sym.TOP); } + "⊥" { return symbol(sym.BOTTOM); } + "|" | "∨" { return symbol(sym.OR); } + "&" | "∧" { return symbol(sym.AND); } + "⊔" { return symbol(sym.JOIN); } + "⊓" { return symbol(sym.MEET); } + "<-" | "←" { return symbol(sym.LARROW); } + "->" | "→" { return symbol(sym.RARROW); } + + /* Statements */ + "val" { return symbol(sym.VAL); } + ":" { return symbol(sym.COLON); } + "::" { return symbol(sym.COLONCOLON); } + + "input" { return symbol(sym.INPUT); } + "output" { return symbol(sym.OUTPUT); } + + "declassify" { return symbol(sym.DECLASSIFY); } + "endorse" { return symbol(sym.ENDORSE); } + + "if" { return symbol(sym.IF); } + "else" { return symbol(sym.ELSE); } + "loop" { return symbol(sym.LOOP); } + "break" { return symbol(sym.BREAK); } + + /* Expressions */ + "." { return symbol(sym.PERIOD); } + + "reduce" { return symbol(sym.REDUCE); } + + "true" { return symbol(sym.TRUE); } + "false" { return symbol(sym.FALSE); } + + {NUM} { return symbol(sym.INT_LIT, Integer.valueOf(yytext())); } + + "!" { return symbol(sym.NOT); } + "&&" { return symbol(sym.ANDAND); } + "||" { return symbol(sym.OROR); } + + "=" { return symbol(sym.EQ); } + + "+" { return symbol(sym.PLUS); } + "-" { return symbol(sym.MINUS); } + "*" { return symbol(sym.TIMES); } + "/" { return symbol(sym.DIVIDE); } + "min" { return symbol(sym.MIN); } + "max" { return symbol(sym.MAX); } + "mux" { return symbol(sym.MUX); } + + "==" { return symbol(sym.EQEQ); } + "!=" { return symbol(sym.NEQ); } + "<" { return symbol(sym.LT); } + "<=" { return symbol(sym.LEQ); } + ">" { return symbol(sym.GT); } + ">=" { return symbol(sym.GEQ); } + + /* Labels */ + "<-" | "←" { return symbol(sym.LARROW); } + "->" | "→" { return symbol(sym.RARROW); } + + /* Demarcate protocol annotation */ + "@" { return symbol(sym.AT); } + +/* Grouping */ + "{" { return symbol(sym.OPEN_BRACE); } + "}" { return symbol(sym.CLOSE_BRACE); } + ";" { return symbol(sym.SEMICOLON); } + "[" { return symbol(sym.OPEN_SQBRACE); } + "]" { return symbol(sym.CLOSE_SQBRACE); } + "(" { return symbol(sym.OPEN_PAREN); } + ")" { return symbol(sym.CLOSE_PAREN); } + "," { return symbol(sym.COMMA); } + + /* Identifiers */ + {CAPALPHANUM} { return symbol(sym.CAP_IDENT, yytext()); } + {ALPHANUM} { return symbol(sym.IDENT, yytext()); } + + /* Comments and Whitespace */ + "/*" { commentLevel++; yybegin(COMMENT); } + {Whitespace} { /* do nothing */ } +} + + { + "/*" { commentLevel++; } + + "*/" { + commentLevel--; + if (commentLevel == 0) { + yybegin(YYINITIAL); + } + } + + . { /* do nothing */ } + + \R { /* do nothing */ } +} + +<> { return symbol(sym.EOF); } + +[^] { throw new IllegalCharacterError(location((int) yychar, (int) yychar + yylength())); } diff --git a/compiler/src/main/kotlin/io/github/aplcornell/viaduct/syntax/precircuit/ArrayTypeNode.kt b/compiler/src/main/kotlin/io/github/aplcornell/viaduct/syntax/precircuit/ArrayTypeNode.kt new file mode 100644 index 0000000000..bc62b48293 --- /dev/null +++ b/compiler/src/main/kotlin/io/github/aplcornell/viaduct/syntax/precircuit/ArrayTypeNode.kt @@ -0,0 +1,19 @@ +package io.github.aplcornell.viaduct.syntax.precircuit + +import io.github.aplcornell.viaduct.prettyprinting.Document +import io.github.aplcornell.viaduct.prettyprinting.bracketed +import io.github.aplcornell.viaduct.prettyprinting.plus +import io.github.aplcornell.viaduct.syntax.Arguments +import io.github.aplcornell.viaduct.syntax.SourceLocation +import io.github.aplcornell.viaduct.syntax.ValueTypeNode + +class ArrayTypeNode( + val elementType: ValueTypeNode, + val shape: Arguments, + override val sourceLocation: SourceLocation, +) : Node() { + override val children: Iterable + get() = shape + + override fun toDocument(): Document = elementType + shape.bracketed() +} diff --git a/compiler/src/main/kotlin/io/github/aplcornell/viaduct/syntax/precircuit/Expressions.kt b/compiler/src/main/kotlin/io/github/aplcornell/viaduct/syntax/precircuit/Expressions.kt new file mode 100644 index 0000000000..f979a147ca --- /dev/null +++ b/compiler/src/main/kotlin/io/github/aplcornell/viaduct/syntax/precircuit/Expressions.kt @@ -0,0 +1,89 @@ +package io.github.aplcornell.viaduct.syntax.precircuit + +import io.github.aplcornell.viaduct.prettyprinting.Document +import io.github.aplcornell.viaduct.prettyprinting.bracketed +import io.github.aplcornell.viaduct.prettyprinting.plus +import io.github.aplcornell.viaduct.prettyprinting.times +import io.github.aplcornell.viaduct.prettyprinting.tupled +import io.github.aplcornell.viaduct.syntax.Arguments +import io.github.aplcornell.viaduct.syntax.Operator +import io.github.aplcornell.viaduct.syntax.SourceLocation +import io.github.aplcornell.viaduct.syntax.surface.keyword +import io.github.aplcornell.viaduct.syntax.values.Value + +/** A computation that produces a result. */ +sealed class ExpressionNode : Node() +sealed class IndexExpressionNode : ExpressionNode() + +/** A literal constant. */ +class LiteralNode( + val value: Value, + override val sourceLocation: SourceLocation, +) : IndexExpressionNode() { + override val children: Iterable + get() = listOf() + + override fun toDocument(): Document = value.toDocument() +} + +class ReferenceNode( + val name: VariableNode, + override val sourceLocation: SourceLocation, +) : IndexExpressionNode() { + override val children: Iterable + get() = listOf() + + override fun toDocument(): Document = name.toDocument() +} + +class LookupNode( + val variable: VariableNode, + val indices: Arguments, + override val sourceLocation: SourceLocation, +) : ExpressionNode() { + override val children: Iterable + get() = indices + + override fun toDocument(): Document = variable + indices.bracketed() +} + +/** An n-ary operator applied to n arguments. */ +class OperatorApplicationNode( + val operator: OperatorNode, + val arguments: Arguments, + override val sourceLocation: SourceLocation, +) : ExpressionNode() { + override val children: Iterable + get() = listOf(operator) + arguments + + override fun toDocument(): Document = Document("(") + operator.operator.toDocument(arguments) + ")" +} + +class OperatorNode( + val operator: Operator, + override val sourceLocation: SourceLocation, +) : Node() { + override val children: Iterable + get() = listOf() + + override fun toDocument(): Document = Document("::$operator") +} + +/** + * @param defaultValue to be used when the list is empty + * @param operator must be associative + */ +class ReduceNode( + val operator: OperatorNode, + val defaultValue: ExpressionNode, + val indices: IndexParameterNode, + val body: ExpressionNode, + override val sourceLocation: SourceLocation, +) : ExpressionNode() { + override val children: Iterable + get() = listOf(operator, defaultValue, indices, body) + + override fun toDocument(): Document { + return keyword("reduce") + listOf(operator, defaultValue).tupled() * "{" * indices * "->" * body * " }" + } +} diff --git a/compiler/src/main/kotlin/io/github/aplcornell/viaduct/syntax/precircuit/IndexParameterNode.kt b/compiler/src/main/kotlin/io/github/aplcornell/viaduct/syntax/precircuit/IndexParameterNode.kt new file mode 100644 index 0000000000..3ec35092f2 --- /dev/null +++ b/compiler/src/main/kotlin/io/github/aplcornell/viaduct/syntax/precircuit/IndexParameterNode.kt @@ -0,0 +1,16 @@ +package io.github.aplcornell.viaduct.syntax.precircuit + +import io.github.aplcornell.viaduct.prettyprinting.Document +import io.github.aplcornell.viaduct.prettyprinting.times +import io.github.aplcornell.viaduct.syntax.SourceLocation + +class IndexParameterNode( + override val name: VariableNode, + val bound: IndexExpressionNode, + override val sourceLocation: SourceLocation, +) : Node(), VariableDeclarationNode { + override val children: Iterable + get() = listOf(bound) + + override fun toDocument(): Document = name.toDocument() * "<" * bound +} diff --git a/compiler/src/main/kotlin/io/github/aplcornell/viaduct/syntax/precircuit/Node.kt b/compiler/src/main/kotlin/io/github/aplcornell/viaduct/syntax/precircuit/Node.kt new file mode 100644 index 0000000000..bf32d107eb --- /dev/null +++ b/compiler/src/main/kotlin/io/github/aplcornell/viaduct/syntax/precircuit/Node.kt @@ -0,0 +1,7 @@ +package io.github.aplcornell.viaduct.syntax.precircuit + +import io.github.aplcornell.viaduct.attributes.TreeNode +import io.github.aplcornell.viaduct.prettyprinting.PrettyPrintable +import io.github.aplcornell.viaduct.syntax.HasSourceLocation + +sealed class Node : TreeNode, HasSourceLocation, PrettyPrintable diff --git a/compiler/src/main/kotlin/io/github/aplcornell/viaduct/syntax/precircuit/Parsing.kt b/compiler/src/main/kotlin/io/github/aplcornell/viaduct/syntax/precircuit/Parsing.kt new file mode 100644 index 0000000000..d52bf4a761 --- /dev/null +++ b/compiler/src/main/kotlin/io/github/aplcornell/viaduct/syntax/precircuit/Parsing.kt @@ -0,0 +1,27 @@ +package io.github.aplcornell.viaduct.syntax.precircuit + +import io.github.aplcornell.viaduct.parsing.ProtocolParser +import io.github.aplcornell.viaduct.parsing.SourceFile +import io.github.aplcornell.viaduct.parsing.defaultProtocolParsers +import io.github.aplcornell.viaduct.syntax.Protocol +import io.github.aplcornell.viaduct.syntax.ProtocolName +import java_cup.runtime.ComplexSymbolFactory + +/** Parses [this] string and returns the AST. */ +fun String.parse( + path: String = "", + protocolParsers: Map> = defaultProtocolParsers, +): ProgramNode { + return SourceFile.from(path, this).parse(protocolParsers) +} + +/** Parses [this] source file to IR and returns the IR. */ +fun SourceFile.parse( + protocolParsers: Map> = defaultProtocolParsers, +): ProgramNode { + val symbolFactory = ComplexSymbolFactory() + val scanner = PrecircuitLexer(this, symbolFactory) + val parser = PrecircuitParser(scanner, symbolFactory) + parser.protocolParsers = protocolParsers + return parser.parseProgram() +} diff --git a/compiler/src/main/kotlin/io/github/aplcornell/viaduct/syntax/precircuit/ProgramNode.kt b/compiler/src/main/kotlin/io/github/aplcornell/viaduct/syntax/precircuit/ProgramNode.kt new file mode 100644 index 0000000000..07a91d39fa --- /dev/null +++ b/compiler/src/main/kotlin/io/github/aplcornell/viaduct/syntax/precircuit/ProgramNode.kt @@ -0,0 +1,52 @@ +package io.github.aplcornell.viaduct.syntax.precircuit + +import io.github.aplcornell.viaduct.attributes.Attribute +import io.github.aplcornell.viaduct.attributes.Tree +import io.github.aplcornell.viaduct.attributes.attribute +import io.github.aplcornell.viaduct.prettyprinting.Document +import io.github.aplcornell.viaduct.prettyprinting.concatenated +import io.github.aplcornell.viaduct.prettyprinting.plus +import io.github.aplcornell.viaduct.syntax.Host +import io.github.aplcornell.viaduct.syntax.SourceLocation +import kotlinx.collections.immutable.PersistentList +import kotlinx.collections.immutable.toPersistentList + +/** + * The circuit representation of a program. + */ +class ProgramNode( + val declarations: PersistentList, + override val sourceLocation: SourceLocation, +) : Node(), List by declarations { + constructor(declarations: List, sourceLocation: SourceLocation) : + this(declarations.toPersistentList(), sourceLocation) + + override val children: Iterable + get() = declarations + + /** A lazily constructed [Tree] instance for the program. */ + val tree: Tree by lazy { Tree(this) } + + private val functionCache: Attribute<(ProgramNode) -> Any?, Any?> = attribute { + this.invoke(this@ProgramNode) + } + + /** + * Applies [function] to this program and returns the results. + * The result is cached, so future calls with the same function do not evaluate [function]. + */ + @Suppress("UNCHECKED_CAST") + fun cached(function: (ProgramNode) -> T): T = + functionCache(function) as T + + val hostDeclarations: Iterable = + declarations.filterIsInstance() + + val hosts: Set = hostDeclarations.map { it.name.value }.toSet() + + val functions: Iterable = + declarations.filterIsInstance() + + override fun toDocument(): Document = + declarations.concatenated(Document.forcedLineBreak + Document.forcedLineBreak) +} diff --git a/compiler/src/main/kotlin/io/github/aplcornell/viaduct/syntax/precircuit/Statements.kt b/compiler/src/main/kotlin/io/github/aplcornell/viaduct/syntax/precircuit/Statements.kt new file mode 100755 index 0000000000..88d514bbd1 --- /dev/null +++ b/compiler/src/main/kotlin/io/github/aplcornell/viaduct/syntax/precircuit/Statements.kt @@ -0,0 +1,235 @@ +package io.github.aplcornell.viaduct.syntax.precircuit + +import io.github.aplcornell.viaduct.prettyprinting.Document +import io.github.aplcornell.viaduct.prettyprinting.braced +import io.github.aplcornell.viaduct.prettyprinting.bracketed +import io.github.aplcornell.viaduct.prettyprinting.concatenated +import io.github.aplcornell.viaduct.prettyprinting.joined +import io.github.aplcornell.viaduct.prettyprinting.nested +import io.github.aplcornell.viaduct.prettyprinting.plus +import io.github.aplcornell.viaduct.prettyprinting.times +import io.github.aplcornell.viaduct.prettyprinting.tupled +import io.github.aplcornell.viaduct.syntax.Arguments +import io.github.aplcornell.viaduct.syntax.FunctionNameNode +import io.github.aplcornell.viaduct.syntax.HostNode +import io.github.aplcornell.viaduct.syntax.LabelNode +import io.github.aplcornell.viaduct.syntax.ProtocolNode +import io.github.aplcornell.viaduct.syntax.SourceLocation +import io.github.aplcornell.viaduct.syntax.surface.keyword +import kotlinx.collections.immutable.PersistentList +import kotlinx.collections.immutable.toPersistentList + +/** A computation with side effects. */ +sealed class StatementNode : Node() + +sealed class CommandNode : Node() + +/** A sequence of statements. */ +class BlockNode +private constructor( + val statements: PersistentList, + val returnStatement: ReturnNode, + override val sourceLocation: SourceLocation, +) : Node(), List by statements { + constructor(statements: List, returnStatement: ReturnNode, sourceLocation: SourceLocation) : + this(statements.toPersistentList(), returnStatement, sourceLocation) + + override val children: Iterable + get() = statements + listOf(returnStatement) + + override fun toDocument(): Document { + val statements: MutableList = (statements.map { it.toDocument() } as MutableList) + statements.add(returnStatement.toDocument()) + val body: Document = statements.concatenated(separator = Document.forcedLineBreak) + return listOf((Document.forcedLineBreak + body).nested() + Document.forcedLineBreak).braced() + } +} + +class ReturnNode( + val values: Arguments, + override val sourceLocation: SourceLocation, +) : StatementNode() { + override val children: Iterable + get() = values + + override fun toDocument(): Document = keyword("return") * values.joined() +} + +/** + * Binding the result of an expression to a variable. + * Note that scalars are represented as arrays of dimension zero: + * val x = 5 ===> val x[] = 5 + */ +class ComputeLetNode( + override val name: VariableNode, + val indices: Arguments, + val type: ArrayTypeNode, + val protocol: ProtocolNode, + val value: ExpressionNode, + override val sourceLocation: SourceLocation, +) : StatementNode(), VariableDeclarationNode { + override val children: Iterable + get() = indices + listOf(type, value) + + override fun toDocument(): Document = + (keyword("val") * name + indices.bracketed() + ":") * type * "=" * value +} + +class LetNode( + val bindings: Arguments, + val command: CommandNode, + override val sourceLocation: SourceLocation, +) : StatementNode() { + override val children: Iterable + get() = bindings + listOf(command) + + override fun toDocument(): Document = + keyword("val") * bindings.joined() * "=" * command +} + +class VariableBindingNode( + override val name: VariableNode, + val protocol: ProtocolNode, + override val sourceLocation: SourceLocation, +) : Node(), VariableDeclarationNode { + override val children: Iterable + get() = listOf() + + override fun toDocument(): Document = + name + "@" + protocol +} + +class CallNode( + val name: FunctionNameNode, + val bounds: Arguments, + val inputs: Arguments, + override val sourceLocation: SourceLocation, +) : CommandNode() { + override val children: Iterable + get() = bounds + inputs + + override fun toDocument(): Document { + return name + bounds.joined( + prefix = Document("<"), + postfix = Document(">"), + ) + inputs.tupled() + } +} + +/** + * An external input. + * @param type Type of the value to receive. + */ +class InputNode( + val type: ArrayTypeNode, + val host: HostNode, + override val sourceLocation: SourceLocation, +) : CommandNode() { + override val children: Iterable + get() = listOf(type) + + override fun toDocument(): Document = host + "." + keyword("input") + "<" + type + ">()" +} + +/** An external output. */ +class OutputNode( + val type: ArrayTypeNode, + val message: ReferenceNode, + val host: HostNode, + override val sourceLocation: SourceLocation, +) : CommandNode() { + override val children: Iterable + get() = listOf(message) + + override fun toDocument(): Document = host + "." + keyword("output") + listOf(message).tupled() +} + +/** A command that affects control flow. */ +sealed class ControlNode : CommandNode() + +/** + * Executing statements conditionally. + * + * @param thenBranch Statements to execute if the guard is true. + * @param elseBranch Statements to execute if the guard is false. + */ +class IfNode( + val guard: IndexExpressionNode, + val thenBranch: BlockNode, + val elseBranch: BlockNode, + override val sourceLocation: SourceLocation, +) : ControlNode() { + override val children: Iterable + get() = listOf(guard, thenBranch, elseBranch) + + override fun toDocument(): Document = + keyword("if") + listOf(guard).tupled() + thenBranch.toDocument() + keyword("else") + elseBranch.toDocument() +} + +/** + * A loop that is executed until a break statement is encountered. + * + * @param jumpLabel A label for the loop that break nodes can refer to. + */ +class LoopNode( + val body: BlockNode, +// val jumpLabel: JumpLabelNode?, + override val sourceLocation: SourceLocation, +) : ControlNode() { + override val children: Iterable + get() = listOf(body) + + override fun toDocument(): Document = keyword("loop") + body.toDocument() +} + +/** + * Breaking out of a loop. + * + * @param jumpLabel Label of the loop to break out of. A null value refers to the innermost loop. + */ +class BreakNode( +// val jumpLabel: JumpLabelNode, + override val sourceLocation: SourceLocation, +) : ControlNode() { + override val children: Iterable + get() = listOf() + + override fun toDocument(): Document = keyword("break") +} + +/** Reducing the confidentiality or increasing the integrity of the result of an expression. */ +sealed class DowngradeNode : CommandNode() { + /** Expression whose label is being downgraded. */ + abstract val expression: ReferenceNode + + /** The label [expression] must have before the downgrade. */ + abstract val fromLabel: LabelNode? + + /** The label after the downgrade. */ + abstract val toLabel: LabelNode? + + final override val children: Iterable + get() = listOf(expression) +} + +/** Revealing the result of an expression (reducing confidentiality). */ +class DeclassificationNode( + override val expression: ReferenceNode, + override val fromLabel: LabelNode?, + override val toLabel: LabelNode, + override val sourceLocation: SourceLocation, +) : DowngradeNode() { + override fun toDocument(): Document = + keyword("declassify") + expression.toDocument() + keyword("to") + toLabel.toDocument() +} + +/** Trusting the result of an expression (increasing integrity). */ +class EndorsementNode( + override val expression: ReferenceNode, + override val fromLabel: LabelNode, + override val toLabel: LabelNode?, + override val sourceLocation: SourceLocation, +) : DowngradeNode() { + override fun toDocument(): Document = + keyword("endorse") + expression.toDocument() + keyword("from") + fromLabel.toDocument() +} diff --git a/compiler/src/main/kotlin/io/github/aplcornell/viaduct/syntax/precircuit/TopLevelDeclarations.kt b/compiler/src/main/kotlin/io/github/aplcornell/viaduct/syntax/precircuit/TopLevelDeclarations.kt new file mode 100644 index 0000000000..a1964cbb43 --- /dev/null +++ b/compiler/src/main/kotlin/io/github/aplcornell/viaduct/syntax/precircuit/TopLevelDeclarations.kt @@ -0,0 +1,70 @@ +package io.github.aplcornell.viaduct.syntax.precircuit + +import io.github.aplcornell.viaduct.prettyprinting.Document +import io.github.aplcornell.viaduct.prettyprinting.joined +import io.github.aplcornell.viaduct.prettyprinting.plus +import io.github.aplcornell.viaduct.prettyprinting.times +import io.github.aplcornell.viaduct.prettyprinting.tupled +import io.github.aplcornell.viaduct.syntax.Arguments +import io.github.aplcornell.viaduct.syntax.FunctionNameNode +import io.github.aplcornell.viaduct.syntax.HostNode +import io.github.aplcornell.viaduct.syntax.SourceLocation +import io.github.aplcornell.viaduct.syntax.surface.keyword + +/** A declaration at the top level of a file. */ +sealed class TopLevelDeclarationNode : Node() + +/** + * Declaration of a participant and their authority. + * + * @param name Host name. + */ +class HostDeclarationNode( + val name: HostNode, + override val sourceLocation: SourceLocation, +) : TopLevelDeclarationNode() { + override val children: Iterable + get() = listOf() + + override fun toDocument(): Document = keyword("host") * name +} + +class SizeParameterNode( + override val name: VariableNode, + override val sourceLocation: SourceLocation, +) : Node(), VariableDeclarationNode { + override val children: Iterable + get() = listOf() + + override fun toDocument(): Document = name.toDocument() +} + +/** + * A parameter to a function declaration. + */ +class ParameterNode( + override val name: VariableNode, + val type: ArrayTypeNode, + override val sourceLocation: SourceLocation, +) : Node(), VariableDeclarationNode { + override val children: Iterable + get() = listOf(type) + + override fun toDocument(): Document = + name + Document(":") * type.toDocument() +} + +class FunctionDeclarationNode( + val name: FunctionNameNode, + val sizes: Arguments, + val inputs: Arguments, + val outputs: Arguments, + val body: BlockNode, + override val sourceLocation: SourceLocation, +) : TopLevelDeclarationNode() { + override val children: Iterable + get() = sizes + inputs + outputs + listOf(body) + + override fun toDocument(): Document = + (keyword("fun") * "<" + sizes.joined() + ">") * name + inputs.tupled() * "->" * outputs.joined() * body +} diff --git a/compiler/src/main/kotlin/io/github/aplcornell/viaduct/syntax/precircuit/Variable.kt b/compiler/src/main/kotlin/io/github/aplcornell/viaduct/syntax/precircuit/Variable.kt new file mode 100644 index 0000000000..09fa737d43 --- /dev/null +++ b/compiler/src/main/kotlin/io/github/aplcornell/viaduct/syntax/precircuit/Variable.kt @@ -0,0 +1,17 @@ +package io.github.aplcornell.viaduct.syntax.precircuit + +import io.github.aplcornell.viaduct.prettyprinting.Document +import io.github.aplcornell.viaduct.prettyprinting.styled +import io.github.aplcornell.viaduct.syntax.Located +import io.github.aplcornell.viaduct.syntax.Name +import io.github.aplcornell.viaduct.syntax.VariableStyle + +/** A variable is a name that stands for a value or an object instance. */ +data class Variable(override val name: String) : Name { + override val nameCategory: String + get() = "variable" + + override fun toDocument(): Document = Document(name).styled(VariableStyle) +} + +typealias VariableNode = Located diff --git a/compiler/src/main/kotlin/io/github/aplcornell/viaduct/syntax/precircuit/VariableDeclarationNode.kt b/compiler/src/main/kotlin/io/github/aplcornell/viaduct/syntax/precircuit/VariableDeclarationNode.kt new file mode 100644 index 0000000000..413bf29958 --- /dev/null +++ b/compiler/src/main/kotlin/io/github/aplcornell/viaduct/syntax/precircuit/VariableDeclarationNode.kt @@ -0,0 +1,7 @@ +package io.github.aplcornell.viaduct.syntax.precircuit + +/** A node that declares a [Variable]. */ +sealed interface VariableDeclarationNode { + /** The variable declared by this node. */ + val name: VariableNode +}