From cf8a66ff971c4ca89fe1ddf45cb605a1627cbfa4 Mon Sep 17 00:00:00 2001 From: hexaredecimal Date: Tue, 20 May 2025 00:53:29 +0200 Subject: [PATCH 1/3] rt: Made the fields public --- src/main/java/org/piccode/rt/PiccodeArray.java | 2 +- src/main/java/org/piccode/rt/PiccodeObject.java | 2 +- src/main/java/org/piccode/rt/PiccodeTuple.java | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/java/org/piccode/rt/PiccodeArray.java b/src/main/java/org/piccode/rt/PiccodeArray.java index 4a4823a..f934e38 100644 --- a/src/main/java/org/piccode/rt/PiccodeArray.java +++ b/src/main/java/org/piccode/rt/PiccodeArray.java @@ -9,7 +9,7 @@ * @author hexaredecimal */ public class PiccodeArray implements PiccodeValue { - private List nodes; + public List nodes; public PiccodeArray(List nodes) { this.nodes = nodes; diff --git a/src/main/java/org/piccode/rt/PiccodeObject.java b/src/main/java/org/piccode/rt/PiccodeObject.java index 5cbbe91..2fcee38 100644 --- a/src/main/java/org/piccode/rt/PiccodeObject.java +++ b/src/main/java/org/piccode/rt/PiccodeObject.java @@ -10,7 +10,7 @@ * @author hexaredecimal */ public class PiccodeObject implements PiccodeValue { - private HashMap obj; + public HashMap obj; public PiccodeObject(HashMap obj) { this.obj = obj; diff --git a/src/main/java/org/piccode/rt/PiccodeTuple.java b/src/main/java/org/piccode/rt/PiccodeTuple.java index 18ee9e6..8f779b3 100644 --- a/src/main/java/org/piccode/rt/PiccodeTuple.java +++ b/src/main/java/org/piccode/rt/PiccodeTuple.java @@ -9,7 +9,7 @@ * @author hexaredecimal */ public class PiccodeTuple implements PiccodeValue { - private List nodes; + public List nodes; public PiccodeTuple(List nodes) { this.nodes = nodes; From 0da76a9681e976ff79778d727bd701c82a21090b Mon Sep 17 00:00:00 2001 From: hexaredecimal Date: Tue, 20 May 2025 00:53:53 +0200 Subject: [PATCH 2/3] ast: Implements the when expression --- src/main/java/org/piccode/ast/WhenAst.java | 108 +++++++++++++++++--- src/test/java/org/piccode/ast/TopLevel.java | 95 +++++++++++++++++ src/test/java/org/piccode/rt/Runtime.java | 75 ++++++++++++++ 3 files changed, 261 insertions(+), 17 deletions(-) create mode 100644 src/test/java/org/piccode/ast/TopLevel.java create mode 100644 src/test/java/org/piccode/rt/Runtime.java diff --git a/src/main/java/org/piccode/ast/WhenAst.java b/src/main/java/org/piccode/ast/WhenAst.java index 24f2396..61fe5f3 100644 --- a/src/main/java/org/piccode/ast/WhenAst.java +++ b/src/main/java/org/piccode/ast/WhenAst.java @@ -1,14 +1,17 @@ package org.piccode.ast; import java.util.List; -import org.piccode.rt.PiccodeException; -import org.piccode.rt.PiccodeValue; + +import org.piccode.rt.*; +import java.util.*; + /** * * @author hexaredecimal */ public class WhenAst implements Ast { + public Ast cond; public List cases; public Ast else_case; @@ -22,11 +25,10 @@ public WhenAst(Ast cond, List cases, Ast else_case) { @Override public String toString() { StringBuilder sb = new StringBuilder(); - sb.append("when ").append(cond).append("{\n"); - for (var when_c: cases) { + sb.append("when ").append(cond).append(" {\n"); + for (var when_c : cases) { sb.append(when_c.toString().indent(4)); } - if (else_case != null) { sb.append(String.format("else %s", else_case).indent(4)); } @@ -38,31 +40,103 @@ public String toString() { public PiccodeValue execute() { var cond_value = cond.execute(); - for (var match_case: cases) { - if (isMatching(match_case.match, cond_value)) { - return match_case.value.execute(); + for (var match_case : cases) { + var tempSymtable = new HashMap(); + if (isMatching(match_case.match, cond_value, tempSymtable)) { + Context.top.pushStack(); + for (var entry : tempSymtable.entrySet()) { + Context.top.putLocal(entry.getKey(), entry.getValue()); } + var result = match_case.value.execute(); + Context.top.dropStackFrame(); + return result; + } } if (else_case == null) { - throw new PiccodeException("Inexhaustive when expression has hit an unexpected state where no pattern was matched: when " + cond + " { ..."); + throw new PiccodeException("Inexhaustive when expression: no pattern matched: when " + cond + " { ... }"); } - + return else_case.execute(); } - private boolean isMatching(List match, PiccodeValue cond_value) { - for (var node: match) { - if (node instanceof IdentifierAst id) { - continue; // TODO: Add Ids to the symtable + private boolean isMatching(List patterns, PiccodeValue cond_value, Map temp) { + for (Ast pattern : patterns) { + if (matchPattern(pattern, cond_value, temp)) { + return true; } + } + return false; + } - var value = node.execute(); - if (node.equals(cond_value)) { - return true; + private boolean matchPattern(Ast pattern, PiccodeValue value, Map temp) { + if (pattern instanceof IdentifierAst id) { + temp.put(id.text, value); + return true; + } + + if (pattern instanceof NumberAst lit) { + var litVal = lit.execute(); + return Objects.equals(litVal, value); + } + + if (pattern instanceof StringAst lit) { + var litVal = lit.execute(); + return Objects.equals(litVal, value); + } + + if (pattern instanceof TupleAst tup && value instanceof PiccodeTuple vTup) { + var items = tup.nodes; + var vItems = vTup.nodes; + if (items.size() != vItems.size()) { + return false; } + + for (int i = 0; i < items.size(); i++) { + if (!matchPattern(items.get(i), vItems.get(i), temp)) { + return false; + } + } + return true; } + if (pattern instanceof ArrayAst listPat && value instanceof PiccodeArray listVal) { + var patItems = listPat.nodes; + var valItems = listVal.nodes; + if (patItems.size() != valItems.size()) { + return false; + } + for (int i = 0; i < patItems.size(); i++) { + if (!matchPattern(patItems.get(i), valItems.get(i), temp)) { + return false; + } + } + return true; + } + if (pattern instanceof ListConstAst cons && value instanceof PiccodeArray listVal2) { + if (listVal2.nodes.isEmpty()) { + return false; + } + var head = listVal2.nodes.getFirst(); + var tail = new PiccodeArray(listVal2.nodes.subList(1, listVal2.nodes.size())); + + return matchPattern(cons.lhs, head, temp) && matchPattern(cons.rhs, tail, temp); + } + if (pattern instanceof ObjectAst objPat && value instanceof PiccodeObject objVal) { + for (var entry : objPat.objs.entrySet()) { + if (!objVal.obj.containsKey(entry.getKey())) { + return false; + } + if (!matchPattern(entry.getValue(), objVal.obj.get(entry.getKey()), temp)) { + return false; + } + } + return true; + } + + if (pattern instanceof ArrayAst && value instanceof PiccodeArray vList && vList.nodes.isEmpty()) { + return true; + } return false; } } diff --git a/src/test/java/org/piccode/ast/TopLevel.java b/src/test/java/org/piccode/ast/TopLevel.java new file mode 100644 index 0000000..43160d2 --- /dev/null +++ b/src/test/java/org/piccode/ast/TopLevel.java @@ -0,0 +1,95 @@ +package org.piccode.ast; + +import org.antlr.v4.runtime.CharStreams; +import org.antlr.v4.runtime.CommonTokenStream; +import org.editor.AccessFrame; +import org.editor.errors.IDEErrorListener; +import org.junit.jupiter.api.Assertions; +import static org.junit.jupiter.api.Assertions.*; +import org.junit.jupiter.api.Test; +import org.piccode.antlr4.PiccodeScriptLexer; +import org.piccode.antlr4.PiccodeScriptParser; + +/** + * + * @author hexaredecimal + */ +public class TopLevel { + @Test + public void function() { + var code = "function add(x, y) = x + y"; + var ast = compile(code); + + assertEquals(ast.nodes.size(), 1); + var func = ast.nodes.getFirst(); + + assertFalse(!(func instanceof FunctionAst)); + var node = (FunctionAst) func; + assertEquals(node.name, "add"); + assertEquals(node.arg.size(), 2); + assertTrue(node.body instanceof BinOpAst); + } + + @Test + public void variable() { + var code = "let foo = 1"; + var ast = compile(code); + + assertEquals(ast.nodes.size(), 1); + var let = ast.nodes.getFirst(); + + assertFalse(!(let instanceof VarDecl)); + var node = (VarDecl) let; + assertEquals(node.name, "foo"); + assertTrue(node.value instanceof NumberAst num && num.text.equals("1")); + } + + @Test + public void module() { + var code = + """ + module Foo { + function bar () = () + } + """; + var ast = compile(code); + + assertEquals(ast.nodes.size(), 1); + var mod = ast.nodes.getFirst(); + + assertFalse(!(mod instanceof ModuleAst)); + var node = (ModuleAst) mod; + assertEquals(node.name, "Foo"); + assertEquals(node.nodes.size(), 1); + + var inner = node.nodes.getFirst(); + assertTrue(inner instanceof FunctionAst); + } + + @Test + public void importModule() { + var code = "import pkg:io"; + var ast = compile(code); + assertEquals(ast.nodes.size(), 1); + var import_ = ast.nodes.getFirst(); + assertFalse(!(import_ instanceof ImportAst)); + var node = (ImportAst) import_; + assertEquals(node.pkg, "pkg"); + assertEquals(node.module, "io"); + } + + private static StatementList compile(String code) { + var lexer = new PiccodeScriptLexer(CharStreams.fromString(code)); + var parser = new PiccodeScriptParser(new CommonTokenStream(lexer)); + lexer.removeErrorListeners(); + parser.removeErrorListeners(); + + IDEErrorListener errorListener = new IDEErrorListener(); + lexer.addErrorListener(errorListener); + parser.addErrorListener(errorListener); + + var visitor = new PiccodeVisitor(); + + return (StatementList) visitor.visit(parser.stmts()); + } +} diff --git a/src/test/java/org/piccode/rt/Runtime.java b/src/test/java/org/piccode/rt/Runtime.java new file mode 100644 index 0000000..d922434 --- /dev/null +++ b/src/test/java/org/piccode/rt/Runtime.java @@ -0,0 +1,75 @@ +package org.piccode.rt; + +import org.antlr.v4.runtime.CharStreams; +import org.antlr.v4.runtime.CommonTokenStream; +import org.editor.AccessFrame; +import org.editor.errors.IDEErrorListener; +import org.junit.jupiter.api.Assertions; +import static org.junit.jupiter.api.Assertions.*; +import org.junit.jupiter.api.Test; +import org.piccode.antlr4.PiccodeScriptLexer; +import org.piccode.antlr4.PiccodeScriptParser; +import org.piccode.ast.*; + + +/** + * + * @author hexaredecimal + */ +public class Runtime { + @Test + public void function() { + var code = "function add(x, y) = x + y"; + var ast = compile(code); + + assertEquals(ast.nodes.size(), 1); + var func = ast.nodes.getFirst(); + + assertFalse(!(func instanceof FunctionAst)); + var node = func.execute(); + assertTrue(node instanceof PiccodeClosure); + } + + @Test + public void variable() { + var code = "let foo = 1"; + var ast = compile(code); + + assertEquals(ast.nodes.size(), 1); + var let = ast.nodes.getFirst(); + + assertFalse(!(let instanceof VarDecl)); + + Context.top.pushStack(); + var node = let.execute(); + Context.top.dropStackFrame(); + assertTrue(node instanceof PiccodeNumber num && num.toString().equals("1")); + } + + @Test + public void importModule() { + var code = "import pkg:io"; + var ast = compile(code); + assertEquals(ast.nodes.size(), 1); + var import_ = ast.nodes.getFirst(); + assertFalse(!(import_ instanceof ImportAst)); + var node = import_.execute(); + assertTrue(node instanceof PiccodeBoolean bool && bool.toString().equals("true")); + } + + private static StatementList compile(String code) { + var lexer = new PiccodeScriptLexer(CharStreams.fromString(code)); + var parser = new PiccodeScriptParser(new CommonTokenStream(lexer)); + lexer.removeErrorListeners(); + parser.removeErrorListeners(); + + IDEErrorListener errorListener = new IDEErrorListener(); + lexer.addErrorListener(errorListener); + parser.addErrorListener(errorListener); + + var visitor = new PiccodeVisitor(); + + return (StatementList) visitor.visit(parser.stmts()); + } + +} From 102a7254906cf1fce7eb26a2c3deafc347cac214 Mon Sep 17 00:00:00 2001 From: hexaredecimal Date: Tue, 20 May 2025 14:41:56 +0200 Subject: [PATCH 3/3] editor: Migrate the DockablePanel into the panels package --- src/main/java/org/editor/EditorWindow.java | 1 + src/main/java/org/editor/{ => panels}/DockablePanel.java | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) rename src/main/java/org/editor/{ => panels}/DockablePanel.java (96%) diff --git a/src/main/java/org/editor/EditorWindow.java b/src/main/java/org/editor/EditorWindow.java index 328a666..13d5d76 100644 --- a/src/main/java/org/editor/EditorWindow.java +++ b/src/main/java/org/editor/EditorWindow.java @@ -1,5 +1,6 @@ package org.editor; +import org.editor.panels.DockablePanel; import org.editor.panels.DashboardPanel; import com.formdev.flatlaf.FlatLightLaf; import com.vlsolutions.swing.docking.Dockable; diff --git a/src/main/java/org/editor/DockablePanel.java b/src/main/java/org/editor/panels/DockablePanel.java similarity index 96% rename from src/main/java/org/editor/DockablePanel.java rename to src/main/java/org/editor/panels/DockablePanel.java index e753f1d..0b98b31 100644 --- a/src/main/java/org/editor/DockablePanel.java +++ b/src/main/java/org/editor/panels/DockablePanel.java @@ -1,4 +1,4 @@ -package org.editor; +package org.editor.panels; import com.vlsolutions.swing.docking.DockKey; import com.vlsolutions.swing.docking.Dockable;