From c8f5edc49411fbca29daf5ed8d5d0e0dcaecd10d Mon Sep 17 00:00:00 2001 From: ctsk <9384305+ctsk@users.noreply.github.com> Date: Thu, 15 Sep 2022 17:20:23 +0200 Subject: [PATCH] [tlox] global vars --- .../tlox/src/main/java/xyz/ctsk/lox/Main.java | 12 ++-- .../java/xyz/ctsk/lox/nodes/LoxRootNode.java | 5 +- .../lox/nodes/expr/LoxReadVariableNode.java | 26 +++++++++ .../lox/nodes/expr/LoxWriteVariableNode.java | 45 ++++++++++++++ .../xyz/ctsk/lox/parser/LoxNodeFactory.java | 58 +++++++++++++++++-- .../java/xyz/ctsk/lox/parser/ParseResult.java | 6 ++ .../main/resources/xyz/ctsk/lox/parser/Lox.g4 | 30 ++++++---- 7 files changed, 155 insertions(+), 27 deletions(-) create mode 100644 jlox/tlox/src/main/java/xyz/ctsk/lox/nodes/expr/LoxReadVariableNode.java create mode 100644 jlox/tlox/src/main/java/xyz/ctsk/lox/nodes/expr/LoxWriteVariableNode.java create mode 100644 jlox/tlox/src/main/java/xyz/ctsk/lox/parser/ParseResult.java diff --git a/jlox/tlox/src/main/java/xyz/ctsk/lox/Main.java b/jlox/tlox/src/main/java/xyz/ctsk/lox/Main.java index 34cb2da..5a1bb79 100644 --- a/jlox/tlox/src/main/java/xyz/ctsk/lox/Main.java +++ b/jlox/tlox/src/main/java/xyz/ctsk/lox/Main.java @@ -1,5 +1,6 @@ package xyz.ctsk.lox; +import com.oracle.truffle.api.frame.FrameDescriptor; import xyz.ctsk.lox.nodes.LoxRootNode; import xyz.ctsk.lox.parser.LoxParser; @@ -22,13 +23,10 @@ public class Main { } 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()); - } + var parseResult = LoxParser.parseLox(program); + var root = new LoxRootNode(parseResult.rootNode(), parseResult.frame()); + var callTarget = root.getCallTarget(); + System.out.println(callTarget.call()); } public static void main(String[] args) throws IOException { 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 index e9c395f..3c83e16 100644 --- a/jlox/tlox/src/main/java/xyz/ctsk/lox/nodes/LoxRootNode.java +++ b/jlox/tlox/src/main/java/xyz/ctsk/lox/nodes/LoxRootNode.java @@ -1,5 +1,6 @@ package xyz.ctsk.lox.nodes; +import com.oracle.truffle.api.frame.FrameDescriptor; import com.oracle.truffle.api.frame.VirtualFrame; import com.oracle.truffle.api.nodes.RootNode; @@ -8,8 +9,8 @@ public class LoxRootNode extends RootNode { @Child private LoxExpressionNode exprNode; - public LoxRootNode(LoxExpressionNode exprNode) { - super(null); + public LoxRootNode(LoxExpressionNode exprNode, FrameDescriptor frameDescriptor) { + super(null, frameDescriptor); this.exprNode = exprNode; } diff --git a/jlox/tlox/src/main/java/xyz/ctsk/lox/nodes/expr/LoxReadVariableNode.java b/jlox/tlox/src/main/java/xyz/ctsk/lox/nodes/expr/LoxReadVariableNode.java new file mode 100644 index 0000000..6c6d7cc --- /dev/null +++ b/jlox/tlox/src/main/java/xyz/ctsk/lox/nodes/expr/LoxReadVariableNode.java @@ -0,0 +1,26 @@ +package xyz.ctsk.lox.nodes.expr; + +import com.oracle.truffle.api.dsl.NodeField; +import com.oracle.truffle.api.dsl.Specialization; +import com.oracle.truffle.api.frame.VirtualFrame; +import xyz.ctsk.lox.nodes.LoxExpressionNode; + +@NodeField(name = "slot", type = int.class) +public abstract class LoxReadVariableNode extends LoxExpressionNode { + protected abstract int getSlot(); + + @Specialization(guards = "frame.isDouble(getSlot())") + protected double readDouble(VirtualFrame frame) { + return frame.getLong(getSlot()); + } + + @Specialization(guards = "frame.isBoolean(getSlot())") + protected boolean readBoolean(VirtualFrame frame) { + return frame.getBoolean(getSlot()); + } + + @Specialization(replaces = { "readDouble", "readBoolean"}) + protected Object readObject(VirtualFrame frame) { + return frame.getObject(getSlot()); + } +} diff --git a/jlox/tlox/src/main/java/xyz/ctsk/lox/nodes/expr/LoxWriteVariableNode.java b/jlox/tlox/src/main/java/xyz/ctsk/lox/nodes/expr/LoxWriteVariableNode.java new file mode 100644 index 0000000..8a93459 --- /dev/null +++ b/jlox/tlox/src/main/java/xyz/ctsk/lox/nodes/expr/LoxWriteVariableNode.java @@ -0,0 +1,45 @@ +package xyz.ctsk.lox.nodes.expr; + +import com.oracle.truffle.api.dsl.NodeChild; +import com.oracle.truffle.api.dsl.NodeField; +import com.oracle.truffle.api.dsl.Specialization; +import com.oracle.truffle.api.frame.FrameSlotKind; +import com.oracle.truffle.api.frame.VirtualFrame; +import xyz.ctsk.lox.nodes.LoxExpressionNode; + +@NodeChild("valueNode") +@NodeField(name = "slot", type = int.class) +public abstract class LoxWriteVariableNode extends LoxExpressionNode { + protected abstract int getSlot(); + + @Specialization(guards = "isDoubleOrIllegal(frame)") + protected double writeDouble(VirtualFrame frame, double value) { + frame.getFrameDescriptor().setSlotKind(getSlot(), FrameSlotKind.Double); + frame.setDouble(getSlot(), value); + return value; + } + + @Specialization(guards = "isBooleanOrIllegal(frame)") + protected boolean writeBoolean(VirtualFrame frame, boolean value) { + frame.getFrameDescriptor().setSlotKind(getSlot(), FrameSlotKind.Boolean); + frame.setBoolean(getSlot(), value); + return value; + } + + @Specialization(replaces = { "writeDouble", "writeBoolean"}) + protected Object write(VirtualFrame frame, Object value) { + frame.getFrameDescriptor().setSlotKind(getSlot(), FrameSlotKind.Object); + frame.setObject(getSlot(), value); + return value; + } + + protected boolean isDoubleOrIllegal(VirtualFrame frame) { + final FrameSlotKind kind = frame.getFrameDescriptor().getSlotKind(getSlot()); + return kind == FrameSlotKind.Double || kind == FrameSlotKind.Illegal; + } + + protected boolean isBooleanOrIllegal(VirtualFrame frame) { + final FrameSlotKind kind = frame.getFrameDescriptor().getSlotKind(getSlot()); + return kind == FrameSlotKind.Boolean || kind == FrameSlotKind.Illegal; + } +} 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 index 5537277..06f975a 100644 --- a/jlox/tlox/src/main/java/xyz/ctsk/lox/parser/LoxNodeFactory.java +++ b/jlox/tlox/src/main/java/xyz/ctsk/lox/parser/LoxNodeFactory.java @@ -1,31 +1,61 @@ package xyz.ctsk.lox.parser; +import com.oracle.truffle.api.frame.FrameDescriptor; +import com.oracle.truffle.api.frame.FrameSlotKind; import com.oracle.truffle.api.strings.TruffleString; import org.antlr.v4.runtime.Token; import xyz.ctsk.lox.nodes.LoxExpressionNode; import xyz.ctsk.lox.nodes.expr.*; +import java.util.HashMap; +import java.util.Map; +import java.util.Optional; + public class LoxNodeFactory { - public static LoxNumberLiteralNode createNumberLiteral(Token literalToken) { + private static class GlobalScope { + private final Map values = new HashMap<>(); + private final FrameDescriptor.Builder builder = FrameDescriptor.newBuilder(); + + Integer find(TruffleString name) { + return values.get(name); + } + + int add(TruffleString name) { + var slot = builder.addSlot(FrameSlotKind.Illegal, name, null); + values.put(name, slot); + return slot; + } + + Integer findOrAdd(TruffleString name) { + return Optional.ofNullable(find(name)).orElseGet(() -> add(name)); + } + + FrameDescriptor getFrame() { + return builder.build(); + } + } + + private final GlobalScope globalScope = new GlobalScope(); + public LoxNumberLiteralNode createNumberLiteral(Token literalToken) { var value = Double.parseDouble(literalToken.getText()); return new LoxNumberLiteralNode(value); } - public static LoxStringLiteralNode createStringLiteral(Token literalToken) { + public LoxStringLiteralNode createStringLiteral(Token literalToken) { var value = TruffleString.fromJavaStringUncached(literalToken.getText(), TruffleString.Encoding.UTF_16); return new LoxStringLiteralNode(value); } - public static LoxBooleanLiteralNode createBooleanLiteral(Token literalToken) { + public LoxBooleanLiteralNode createBooleanLiteral(Token literalToken) { var value = Boolean.parseBoolean(literalToken.getText()); return new LoxBooleanLiteralNode(value); } - public static LoxNilLiteralNode createNilLiteral() { + public LoxNilLiteralNode createNilLiteral() { return new LoxNilLiteralNode(); } - public static LoxExpressionNode createUnary(Token op, LoxExpressionNode value) { + public LoxExpressionNode createUnary(Token op, LoxExpressionNode value) { return switch (op.getText()) { case "-" -> LoxNegNodeGen.create(value); case "!" -> LoxLogicalNotNodeGen.create(value); @@ -33,7 +63,7 @@ public class LoxNodeFactory { }; } - public static LoxExpressionNode createBinary(Token op, LoxExpressionNode left, LoxExpressionNode right) { + public LoxExpressionNode createBinary(Token op, LoxExpressionNode left, LoxExpressionNode right) { return switch (op.getText()) { case "+" -> LoxAddNodeGen.create(left, right); case "-" -> LoxSubNodeGen.create(left, right); @@ -48,4 +78,20 @@ public class LoxNodeFactory { default -> null; }; } + + public LoxWriteVariableNode createAssignment(Token identifier, LoxExpressionNode value) { + var name = TruffleString.fromJavaStringUncached(identifier.getText(), TruffleString.Encoding.US_ASCII); + var slot = globalScope.findOrAdd(name); + return LoxWriteVariableNodeGen.create(value, slot); + } + + public LoxReadVariableNode createRead(Token identifier) { + var name = TruffleString.fromJavaStringUncached(identifier.getText(), TruffleString.Encoding.US_ASCII); + var slot = globalScope.find(name); + return LoxReadVariableNodeGen.create(slot); + } + + public FrameDescriptor getFrameDescriptor() { + return globalScope.getFrame(); + } } diff --git a/jlox/tlox/src/main/java/xyz/ctsk/lox/parser/ParseResult.java b/jlox/tlox/src/main/java/xyz/ctsk/lox/parser/ParseResult.java new file mode 100644 index 0000000..b94fef9 --- /dev/null +++ b/jlox/tlox/src/main/java/xyz/ctsk/lox/parser/ParseResult.java @@ -0,0 +1,6 @@ +package xyz.ctsk.lox.parser; + +import com.oracle.truffle.api.frame.FrameDescriptor; +import xyz.ctsk.lox.nodes.LoxExpressionNode; + +public record ParseResult(LoxExpressionNode rootNode, FrameDescriptor frame) { } 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 index a98c4c8..c5fe4e8 100644 --- a/jlox/tlox/src/main/resources/xyz/ctsk/lox/parser/Lox.g4 +++ b/jlox/tlox/src/main/resources/xyz/ctsk/lox/parser/Lox.g4 @@ -3,6 +3,7 @@ grammar Lox; @parser::header { +import com.oracle.truffle.api.frame.FrameDescriptor; import com.oracle.truffle.api.source.Source; import xyz.ctsk.lox.nodes.LoxExpressionNode; @@ -11,16 +12,18 @@ 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)); +private LoxNodeFactory factory; - parser.factory = new LoxNodeFactory(); +public static ParseResult parseLox(String source) { + LoxLexer lexer = new LoxLexer(CharStreams.fromString(source)); + LoxParser parser = new LoxParser(new CommonTokenStream(lexer)); + + parser.factory = new LoxNodeFactory(); + var root = parser.expression().result; + return new ParseResult(root, parser.factory.getFrameDescriptor()); +} - return parser.expression(); - } } file returns [LoxExpressionNode result] @@ -41,14 +44,17 @@ expression returns [LoxExpressionNode result] { $result = factory.createBinary($op, $left.result, $right.result); } | left=expression op=( EQUAL_EQUAL | BANG_EQUAL ) right=expression { $result = factory.createBinary($op, $left.result, $right.result); } + | IDENTIFIER EQUAL expression + { $result = factory.createAssignment($IDENTIFIER, $expression.result); } ; literal returns [LoxExpressionNode result] - : NUMBER { $result = factory.createNumberLiteral($NUMBER); } - | STRING { $result = factory.createStringLiteral($STRING); } - | TRUE { $result = factory.createBooleanLiteral($TRUE); } - | FALSE { $result = factory.createBooleanLiteral($FALSE); } - | NIL { $result = factory.createNilLiteral(); } + : NUMBER { $result = factory.createNumberLiteral($NUMBER); } + | STRING { $result = factory.createStringLiteral($STRING); } + | TRUE { $result = factory.createBooleanLiteral($TRUE); } + | FALSE { $result = factory.createBooleanLiteral($FALSE); } + | NIL { $result = factory.createNilLiteral(); } + | IDENTIFIER { $result = factory.createRead($IDENTIFIER); } ; AND: 'and' ;