From 3c12f50b6f5152420eb49d72b816afde04136b9b Mon Sep 17 00:00:00 2001 From: ctsk <9384305+ctsk@users.noreply.github.com> Date: Tue, 13 Sep 2022 17:31:30 +0200 Subject: [PATCH] [tlox] Initial Expression parsing and evaluation --- flake.nix | 2 + jlox/lox/pom.xml | 4 +- jlox/pom.xml | 3 +- jlox/tlox/pom.xml | 57 ++++++++++++ .../tlox/src/main/java/xyz/ctsk/lox/Main.java | 38 ++++++++ .../xyz/ctsk/lox/nodes/LoxExpressionNode.java | 7 ++ .../main/java/xyz/ctsk/lox/nodes/LoxNode.java | 6 ++ .../java/xyz/ctsk/lox/nodes/LoxRootNode.java | 20 ++++ .../xyz/ctsk/lox/nodes/expr/LoxAddNode.java | 24 +++++ .../xyz/ctsk/lox/nodes/expr/LoxDivNode.java | 22 +++++ .../xyz/ctsk/lox/nodes/expr/LoxMulNode.java | 22 +++++ .../lox/nodes/expr/LoxNumberLiteralNode.java | 16 ++++ .../xyz/ctsk/lox/nodes/expr/LoxSubNode.java | 22 +++++ .../xyz/ctsk/lox/parser/LoxNodeFactory.java | 22 +++++ .../main/resources/xyz/ctsk/lox/parser/Lox.g4 | 92 +++++++++++++++++++ jlox/tools/pom.xml | 2 +- 16 files changed, 355 insertions(+), 4 deletions(-) create mode 100644 jlox/tlox/pom.xml create mode 100644 jlox/tlox/src/main/java/xyz/ctsk/lox/Main.java create mode 100644 jlox/tlox/src/main/java/xyz/ctsk/lox/nodes/LoxExpressionNode.java create mode 100644 jlox/tlox/src/main/java/xyz/ctsk/lox/nodes/LoxNode.java create mode 100644 jlox/tlox/src/main/java/xyz/ctsk/lox/nodes/LoxRootNode.java create mode 100644 jlox/tlox/src/main/java/xyz/ctsk/lox/nodes/expr/LoxAddNode.java create mode 100644 jlox/tlox/src/main/java/xyz/ctsk/lox/nodes/expr/LoxDivNode.java create mode 100644 jlox/tlox/src/main/java/xyz/ctsk/lox/nodes/expr/LoxMulNode.java create mode 100644 jlox/tlox/src/main/java/xyz/ctsk/lox/nodes/expr/LoxNumberLiteralNode.java create mode 100644 jlox/tlox/src/main/java/xyz/ctsk/lox/nodes/expr/LoxSubNode.java create mode 100644 jlox/tlox/src/main/java/xyz/ctsk/lox/parser/LoxNodeFactory.java create mode 100644 jlox/tlox/src/main/resources/xyz/ctsk/lox/parser/Lox.g4 diff --git a/flake.nix b/flake.nix index d6a67e9..b9c7f63 100644 --- a/flake.nix +++ b/flake.nix @@ -15,6 +15,7 @@ gcc = pkgs.gcc; openjdk = pkgs.openjdk; maven = pkgs.maven; + antlr = pkgs.antlr; in { devShell = pkgs.mkShell { @@ -22,6 +23,7 @@ gcc openjdk maven + antlr ]; }; } diff --git a/jlox/lox/pom.xml b/jlox/lox/pom.xml index 1cdd51d..c547c2f 100644 --- a/jlox/lox/pom.xml +++ b/jlox/lox/pom.xml @@ -6,11 +6,11 @@ xyz.ctsk.lox - lox + jlox 1.0-SNAPSHOT - interpreter + lox jar diff --git a/jlox/pom.xml b/jlox/pom.xml index 0927d0a..922fe39 100644 --- a/jlox/pom.xml +++ b/jlox/pom.xml @@ -2,7 +2,7 @@ 4.0.0 xyz.ctsk.lox - lox + jlox 1.0-SNAPSHOT pom @@ -10,6 +10,7 @@ tools lox + tlox diff --git a/jlox/tlox/pom.xml b/jlox/tlox/pom.xml new file mode 100644 index 0000000..a96ad22 --- /dev/null +++ b/jlox/tlox/pom.xml @@ -0,0 +1,57 @@ + + + + 4.0.0 + + + xyz.ctsk.lox + jlox + 1.0-SNAPSHOT + + + tlox + jar + + + 17 + 17 + UTF-8 + + + + + org.graalvm.truffle + truffle-api + 22.2.0 + + + org.antlr + antlr4 + 4.11.1 + + + + + + + org.antlr + antlr4-maven-plugin + 4.11.1 + + src/main/resources + + + + antlr + + antlr4 + + + + + + + + \ No newline at end of file diff --git a/jlox/tlox/src/main/java/xyz/ctsk/lox/Main.java b/jlox/tlox/src/main/java/xyz/ctsk/lox/Main.java new file mode 100644 index 0000000..96f26af --- /dev/null +++ b/jlox/tlox/src/main/java/xyz/ctsk/lox/Main.java @@ -0,0 +1,38 @@ +package xyz.ctsk.lox; + +import com.oracle.truffle.api.Truffle; +import xyz.ctsk.lox.nodes.LoxRootNode; +import xyz.ctsk.lox.parser.LoxParser; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; + +public class Main { + + public static void repl() throws IOException { + var input = new InputStreamReader(System.in); + var reader = new BufferedReader(input); + + while (true) { + System.out.print(" >> "); + var line = reader.readLine(); + if (line == null) break; + run(line); + } + } + + public static void run(String program) { + var parsed = LoxParser.parseLox(program); + + if (parsed instanceof LoxParser.ExpressionContext expr) { + var root = new LoxRootNode(expr.result); + var callTarget = root.getCallTarget(); + System.out.println(callTarget.call()); + } + } + + public static void main(String[] args) throws IOException { + repl(); + } +} diff --git a/jlox/tlox/src/main/java/xyz/ctsk/lox/nodes/LoxExpressionNode.java b/jlox/tlox/src/main/java/xyz/ctsk/lox/nodes/LoxExpressionNode.java new file mode 100644 index 0000000..75de126 --- /dev/null +++ b/jlox/tlox/src/main/java/xyz/ctsk/lox/nodes/LoxExpressionNode.java @@ -0,0 +1,7 @@ +package xyz.ctsk.lox.nodes; + +import com.oracle.truffle.api.frame.VirtualFrame; + +public abstract class LoxExpressionNode extends LoxNode { + public abstract double executeDouble(VirtualFrame frame); +} diff --git a/jlox/tlox/src/main/java/xyz/ctsk/lox/nodes/LoxNode.java b/jlox/tlox/src/main/java/xyz/ctsk/lox/nodes/LoxNode.java new file mode 100644 index 0000000..d186c3e --- /dev/null +++ b/jlox/tlox/src/main/java/xyz/ctsk/lox/nodes/LoxNode.java @@ -0,0 +1,6 @@ +package xyz.ctsk.lox.nodes; + +import com.oracle.truffle.api.nodes.Node; + +public class LoxNode extends Node { +} diff --git a/jlox/tlox/src/main/java/xyz/ctsk/lox/nodes/LoxRootNode.java b/jlox/tlox/src/main/java/xyz/ctsk/lox/nodes/LoxRootNode.java new file mode 100644 index 0000000..c83bcd9 --- /dev/null +++ b/jlox/tlox/src/main/java/xyz/ctsk/lox/nodes/LoxRootNode.java @@ -0,0 +1,20 @@ +package xyz.ctsk.lox.nodes; + +import com.oracle.truffle.api.frame.VirtualFrame; +import com.oracle.truffle.api.nodes.RootNode; + +public class LoxRootNode extends RootNode { + @SuppressWarnings("FieldMayBeFinal") + @Child + private LoxExpressionNode exprNode; + + public LoxRootNode(LoxExpressionNode exprNode) { + super(null); + this.exprNode = exprNode; + } + + @Override + public Object execute(VirtualFrame frame) { + return this.exprNode.executeDouble(frame); + } +} diff --git a/jlox/tlox/src/main/java/xyz/ctsk/lox/nodes/expr/LoxAddNode.java b/jlox/tlox/src/main/java/xyz/ctsk/lox/nodes/expr/LoxAddNode.java new file mode 100644 index 0000000..3b0c25c --- /dev/null +++ b/jlox/tlox/src/main/java/xyz/ctsk/lox/nodes/expr/LoxAddNode.java @@ -0,0 +1,24 @@ +package xyz.ctsk.lox.nodes.expr; + +import com.oracle.truffle.api.frame.VirtualFrame; +import com.oracle.truffle.api.nodes.Node; +import xyz.ctsk.lox.nodes.LoxExpressionNode; + +public class LoxAddNode extends LoxExpressionNode { + @SuppressWarnings("FieldMayBeFinal") + @Node.Child + private LoxExpressionNode leftNode, rightNode; + + public LoxAddNode(LoxExpressionNode leftNode, LoxExpressionNode rightNode) { + this.leftNode = leftNode; + this.rightNode = rightNode; + } + + + @Override + public double executeDouble(VirtualFrame frame) { + var leftValue = leftNode.executeDouble(frame); + var rightValue = rightNode.executeDouble(frame); + return leftValue + rightValue; + } +} diff --git a/jlox/tlox/src/main/java/xyz/ctsk/lox/nodes/expr/LoxDivNode.java b/jlox/tlox/src/main/java/xyz/ctsk/lox/nodes/expr/LoxDivNode.java new file mode 100644 index 0000000..93e5dfa --- /dev/null +++ b/jlox/tlox/src/main/java/xyz/ctsk/lox/nodes/expr/LoxDivNode.java @@ -0,0 +1,22 @@ +package xyz.ctsk.lox.nodes.expr; + +import com.oracle.truffle.api.frame.VirtualFrame; +import xyz.ctsk.lox.nodes.LoxExpressionNode; + +public class LoxDivNode extends LoxExpressionNode { + @SuppressWarnings("FieldMayBeFinal") + @Child + private LoxExpressionNode leftNode, rightNode; + + public LoxDivNode(LoxExpressionNode leftNode, LoxExpressionNode rightNode) { + this.leftNode = leftNode; + this.rightNode = rightNode; + } + + @Override + public double executeDouble(VirtualFrame frame) { + var leftValue = leftNode.executeDouble(frame); + var rightValue = rightNode.executeDouble(frame); + return leftValue / rightValue; + } +} diff --git a/jlox/tlox/src/main/java/xyz/ctsk/lox/nodes/expr/LoxMulNode.java b/jlox/tlox/src/main/java/xyz/ctsk/lox/nodes/expr/LoxMulNode.java new file mode 100644 index 0000000..948bf36 --- /dev/null +++ b/jlox/tlox/src/main/java/xyz/ctsk/lox/nodes/expr/LoxMulNode.java @@ -0,0 +1,22 @@ +package xyz.ctsk.lox.nodes.expr; + +import com.oracle.truffle.api.frame.VirtualFrame; +import xyz.ctsk.lox.nodes.LoxExpressionNode; + +public class LoxMulNode extends LoxExpressionNode { + @SuppressWarnings("FieldMayBeFinal") + @Child + private LoxExpressionNode leftNode, rightNode; + + public LoxMulNode(LoxExpressionNode leftNode, LoxExpressionNode rightNode) { + this.leftNode = leftNode; + this.rightNode = rightNode; + } + + @Override + public double executeDouble(VirtualFrame frame) { + var leftValue = leftNode.executeDouble(frame); + var rightValue = rightNode.executeDouble(frame); + return leftValue * rightValue; + } +} diff --git a/jlox/tlox/src/main/java/xyz/ctsk/lox/nodes/expr/LoxNumberLiteralNode.java b/jlox/tlox/src/main/java/xyz/ctsk/lox/nodes/expr/LoxNumberLiteralNode.java new file mode 100644 index 0000000..c90e3fd --- /dev/null +++ b/jlox/tlox/src/main/java/xyz/ctsk/lox/nodes/expr/LoxNumberLiteralNode.java @@ -0,0 +1,16 @@ +package xyz.ctsk.lox.nodes.expr; + +import com.oracle.truffle.api.frame.VirtualFrame; +import xyz.ctsk.lox.nodes.LoxExpressionNode; + +public class LoxNumberLiteralNode extends LoxExpressionNode { + private final double value; + + public LoxNumberLiteralNode(double value) { + this.value = value; + } + + public double executeDouble(VirtualFrame frame) { + return this.value; + } +} diff --git a/jlox/tlox/src/main/java/xyz/ctsk/lox/nodes/expr/LoxSubNode.java b/jlox/tlox/src/main/java/xyz/ctsk/lox/nodes/expr/LoxSubNode.java new file mode 100644 index 0000000..d6917b3 --- /dev/null +++ b/jlox/tlox/src/main/java/xyz/ctsk/lox/nodes/expr/LoxSubNode.java @@ -0,0 +1,22 @@ +package xyz.ctsk.lox.nodes.expr; + +import com.oracle.truffle.api.frame.VirtualFrame; +import xyz.ctsk.lox.nodes.LoxExpressionNode; + +public class LoxSubNode extends LoxExpressionNode { + @SuppressWarnings("FieldMayBeFinal") + @Child + private LoxExpressionNode leftNode, rightNode; + + public LoxSubNode(LoxExpressionNode leftNode, LoxExpressionNode rightNode) { + this.leftNode = leftNode; + this.rightNode = rightNode; + } + + @Override + public double executeDouble(VirtualFrame frame) { + var leftValue = leftNode.executeDouble(frame); + var rightValue = rightNode.executeDouble(frame); + return leftValue - rightValue; + } +} diff --git a/jlox/tlox/src/main/java/xyz/ctsk/lox/parser/LoxNodeFactory.java b/jlox/tlox/src/main/java/xyz/ctsk/lox/parser/LoxNodeFactory.java new file mode 100644 index 0000000..263cdf1 --- /dev/null +++ b/jlox/tlox/src/main/java/xyz/ctsk/lox/parser/LoxNodeFactory.java @@ -0,0 +1,22 @@ +package xyz.ctsk.lox.parser; + +import org.antlr.v4.runtime.Token; +import xyz.ctsk.lox.nodes.LoxExpressionNode; +import xyz.ctsk.lox.nodes.expr.*; + +public class LoxNodeFactory { + public static LoxNumberLiteralNode createNumberLiteral(Token literalToken) { + var value = Double.parseDouble(literalToken.getText()); + return new LoxNumberLiteralNode(value); + } + + public static LoxExpressionNode createBinaryNode(Token op, LoxExpressionNode left, LoxExpressionNode right) { + return switch (op.getText()) { + case "+" -> new LoxAddNode(left, right); + case "-" -> new LoxSubNode(left, right); + case "*" -> new LoxMulNode(left, right); + case "/" -> new LoxDivNode(left, right); + default -> null; + }; + } +} diff --git a/jlox/tlox/src/main/resources/xyz/ctsk/lox/parser/Lox.g4 b/jlox/tlox/src/main/resources/xyz/ctsk/lox/parser/Lox.g4 new file mode 100644 index 0000000..d5b2e85 --- /dev/null +++ b/jlox/tlox/src/main/resources/xyz/ctsk/lox/parser/Lox.g4 @@ -0,0 +1,92 @@ +grammar Lox; + + +@parser::header { + +import com.oracle.truffle.api.source.Source; + +import xyz.ctsk.lox.nodes.LoxExpressionNode; +import xyz.ctsk.lox.parser.*; + +} + +@parser::members { + private LoxNodeFactory factory; + + public static Object parseLox(String source) { + LoxLexer lexer = new LoxLexer(CharStreams.fromString(source)); + LoxParser parser = new LoxParser(new CommonTokenStream(lexer)); + + parser.factory = new LoxNodeFactory(); + + return parser.expression(); + } +} + +file returns [LoxExpressionNode result] + : expression EOF { $result = $expression.result; } + ; + +expression returns [LoxExpressionNode result] + : literal { $result = $literal.result; } + | left=expression op=( '*' | '/' ) right=expression { $result = factory.createBinaryNode($op, $left.result, $right.result); } + | left=expression op=( '+' | '-' ) right=expression { $result = factory.createBinaryNode($op, $left.result, $right.result); } + ; + +literal returns [LoxExpressionNode result] + : NUMBER { $result = factory.createNumberLiteral($NUMBER); } + ; + +AND: 'and' ; +CLASS: 'class' ; +ELSE: 'else' ; +FALSE: 'false' ; +FOR: 'for' ; +FUN: 'fun' ; +IF: 'if' ; +NIL: 'nil' ; +OR: 'or' ; +PRINT: 'print' ; +RETURN: 'return' ; +SUPER: 'super' ; +THIS: 'this' ; +TRUE: 'true' ; +VAR: 'var' ; +WHILE: 'while'; + +LESS: '<' ; +LESS_EQUAL: '<='; +GREATER: '>' ; +GREATER_EQUAL: '>=' ; +BANG: '!' ; +BANG_EQUAL: '!=' ; +EQUAL: '=' ; +EQUAL_EQUAL: '==' ; + +NUMBER + : DIGIT+ ('.' DIGIT+)? + ; + +STRING + : '"' ~["]* '"' + ; + +IDENTIFIER + : LETTER ( LETTER | DIGIT | UNDERSCORE )* + ; + +fragment LETTER + : [a-zA-Z] + ; + +fragment DIGIT + : [0-9] + ; + +fragment UNDERSCORE + : '_' + ; + +WS + : [ \t\r\n]+ -> skip + ; \ No newline at end of file diff --git a/jlox/tools/pom.xml b/jlox/tools/pom.xml index 60f7dcf..0cee615 100644 --- a/jlox/tools/pom.xml +++ b/jlox/tools/pom.xml @@ -6,7 +6,7 @@ xyz.ctsk.lox - lox + jlox 1.0-SNAPSHOT