diff --git a/src/main/antlr4/PiccodeScript.g4 b/src/main/antlr4/PiccodeScript.g4 index c29d4eb..845a287 100644 --- a/src/main/antlr4/PiccodeScript.g4 +++ b/src/main/antlr4/PiccodeScript.g4 @@ -28,7 +28,7 @@ symbol_lift symbol_entry : ID (symbol_lift)? ; -declaration: ID CC (module | func); +declaration: ID CC (module | func | import_module); module: MODULE LBRACE module_stmts RBRACE; @@ -102,11 +102,12 @@ closure_decl: BOR arg_list? BOR ARROW expr; unary: EXCLAIM expr | SUB expr + | RETURN expr | TILDE expr | BAND expr; if_expr: - IF expr LBRACE expr* RBRACE (ELSE LBRACE expr* RBRACE)?; + IF expr LBRACE expr RBRACE (ELSE LBRACE expr RBRACE)?; when_expr: WHEN expr LBRACE when_cases else_case? RBRACE; diff --git a/src/main/java/org/piccode/ast/Arg.java b/src/main/java/org/piccode/ast/Arg.java index 211e9f4..07d9f9d 100644 --- a/src/main/java/org/piccode/ast/Arg.java +++ b/src/main/java/org/piccode/ast/Arg.java @@ -10,15 +10,18 @@ public class Arg extends Ast { public String name; public Ast def_val; + public boolean export; public Arg(String name, Ast def_val) { this.name = name; this.def_val = def_val; + this.export = false; } public Arg(String name) { this.name = name; this.def_val = null; + this.export = false; } @Override diff --git a/src/main/java/org/piccode/ast/CCOperationAst.java b/src/main/java/org/piccode/ast/CCOperationAst.java index 416943f..9500023 100644 --- a/src/main/java/org/piccode/ast/CCOperationAst.java +++ b/src/main/java/org/piccode/ast/CCOperationAst.java @@ -1,5 +1,6 @@ package org.piccode.ast; +import com.github.tomaslanger.chalk.Chalk; import java.util.concurrent.ExecutionException; import org.piccode.piccodescript.TargetEnvironment; import org.piccode.rt.Context; @@ -34,18 +35,37 @@ public String toString() { @Override public PiccodeValue execute(Integer frame) { - if (lhs instanceof IdentifierAst id && Context.modules.containsKey(id.text)) { - var mod = Context.modules.get(id.text); - + if (lhs instanceof CCOperationAst op) { + var mod = (PiccodeModule) op.execute(frame); if (!(rhs instanceof CallAst) && !(rhs instanceof IdentifierAst)) { - throw new PiccodeException(file, line, column, "No node " + rhs + " found in module " + id.text); + throw new PiccodeException(file, line, column, "No node " + rhs + " found in module " + mod.name); } - + + var id = new IdentifierAst(mod.name); + id.file = file; + id.line = line; + id.column = column; return process(id, mod, frame); } + + if (lhs instanceof IdentifierAst id && Context.top.getValue(id.text) != null) { + var mod = Context.top.getValue(id.text); + if (!(rhs instanceof CallAst) && !(rhs instanceof IdentifierAst)) { + throw new PiccodeException(file, line, column, "No node " + rhs + " found in module " + id.text); + } + return process(id, (PiccodeModule)mod, frame); + } - var err = new PiccodeException(file, line, column, "Invalid use of `::`. Expected a module on the lhs"); + var err = new PiccodeException(file, line, column, "Invalid use of `::`. Expected a module on the lhs, but found " + Chalk.on(lhs.toString()).red()); err.frame = frame; + + if (lhs instanceof IdentifierAst id) { + var nm = Context.top.getSimilarName(id.text); + if (nm != null && !nm.isEmpty()) { + var note = new PiccodeSimpleNote("Did you mean `" + Chalk.on(nm).green() + "` instead of `" + Chalk.on(id.text).red() + "` ?"); + err.addNote(note); + } + } throw err; } @@ -75,8 +95,7 @@ private PiccodeValue process(IdentifierAst id, PiccodeModule mod, Integer frame) return result; } if (node instanceof ModuleAst _mod && _mod.name.equals(_id.text)) { - node.execute(frame); - return Context.modules.get(_id.text); + return node.execute(frame); } } @@ -105,7 +124,7 @@ private PiccodeValue process(IdentifierAst id, PiccodeModule mod, Integer frame) } if (node instanceof ModuleAst _mod && _mod.name.equals(_id.text)) { node.execute(frame); - return Context.modules.get(_id.text); + return ctx.getValue(_id.text); } } diff --git a/src/main/java/org/piccode/ast/DotOperationAst.java b/src/main/java/org/piccode/ast/DotOperationAst.java index cedafa9..d3b4c6e 100644 --- a/src/main/java/org/piccode/ast/DotOperationAst.java +++ b/src/main/java/org/piccode/ast/DotOperationAst.java @@ -36,7 +36,7 @@ public String toString() { @Override public PiccodeValue execute(Integer frame) { - if (lhs instanceof IdentifierAst id && Context.modules.containsKey(id.text)) { + if (lhs instanceof IdentifierAst id && Context.top.getValue(id.text) != null && Context.top.getValue(id.text) instanceof PiccodeModule) { var err = new PiccodeException(file, line, column, "Cannot access the module `" + id.text + "` using dot. Please use `::` instead"); err.frame = frame; throw err; @@ -56,10 +56,6 @@ public PiccodeValue execute(Integer frame) { return processArrayIndexing(tupl.array(), rhs.execute(frame), frame); } - if (left instanceof PiccodeModule mod) { - return process(new IdentifierAst(mod.name), mod, frame); - } - if (!(left instanceof PiccodeObject)) { var err = new PiccodeException(file, line, column, "Invalid expression on the side of `.` : " + lhs + " has value " + left + " which is not an object"); err.frame = frame; @@ -112,70 +108,6 @@ public PiccodeValue execute(Integer frame) { return value; } - private PiccodeValue process(IdentifierAst id, PiccodeModule mod, Integer frame) { - var ctx = frame == null - ? Context.top - : Context.getContextAt(frame); - - if (rhs instanceof IdentifierAst _id) { - for (var node : mod.nodes) { - if (node instanceof VarDecl vd && vd.name.equals(_id.text)) { - return node.execute(frame); - } - if (node instanceof FunctionAst func && func.name.equals(_id.text)) { - node.execute(frame); - var result = ctx.getValue(_id.text); - if (result == null) { - var err = new PiccodeException(func.file, func.line, func.column, "Function `" + _id.text + "` is not defined"); - err.frame = frame; - var nm = ctx.getSimilarName(_id.text); - if (nm != null && !nm.isEmpty()) { - var note = new PiccodeException(func.file, func.line, func.column, "Maybe you meant `" + nm + "`"); - err.addNote(note); - } - throw err; - } - return result; - } - if (node instanceof ModuleAst _mod && _mod.name.equals(_id.text)) { - node.execute(frame); - return Context.modules.get(_id.text); - } - } - - var err = new PiccodeException(file, line, column, "No function or identifier " + _id.text + " found in module " + id.text); - err.frame = frame; - throw err; - } - - var call = (CallAst) rhs; - - if (!(call.expr instanceof IdentifierAst)) { - var err = new PiccodeException(file, line, column, "Invalid function reference in module access module " + id.text + ": " + call.expr); - err.frame = frame; - throw err; - } - - var _id = (IdentifierAst) call.expr; - for (var node : mod.nodes) { - if (node instanceof VarDecl vd && vd.name.equals(_id.text)) { - return node.execute(frame); - } - if (node instanceof FunctionAst func && func.name.equals(_id.text)) { - node.execute(frame); - //return Context.top.getValue(_id.text); - return call.execute(frame); - } - if (node instanceof ModuleAst _mod && _mod.name.equals(_id.text)) { - node.execute(frame); - return Context.modules.get(_id.text); - } - } - - var err = new PiccodeException(file, line, column, "No function or identifier " + _id.text + " found in module " + id.text); - err.frame = frame; - throw err; - } private PiccodeValue processArrayIndexing(PiccodeValue[] arr, PiccodeValue execute, Integer frame) { if (!(execute instanceof PiccodeNumber)) { diff --git a/src/main/java/org/piccode/ast/FunctionAst.java b/src/main/java/org/piccode/ast/FunctionAst.java index 96071b7..2cc2602 100644 --- a/src/main/java/org/piccode/ast/FunctionAst.java +++ b/src/main/java/org/piccode/ast/FunctionAst.java @@ -53,6 +53,10 @@ private String formatArgs() { @Override public PiccodeValue execute(Integer frame) { + var ctx = frame == null + ? Context.top + : Context.getContextAt(frame); + Map newArgs = new HashMap<>(); var cl = new PiccodeClosure(arg, newArgs, 0, body); cl.creator = this; @@ -62,7 +66,7 @@ public PiccodeValue execute(Integer frame) { cl.file = file; cl.column = column; cl.line = line; - Context.addGlobal(name, cl); + ctx.putLocal(name, cl); return cl; } diff --git a/src/main/java/org/piccode/ast/IdentifierAst.java b/src/main/java/org/piccode/ast/IdentifierAst.java index 4072ec9..b012c59 100644 --- a/src/main/java/org/piccode/ast/IdentifierAst.java +++ b/src/main/java/org/piccode/ast/IdentifierAst.java @@ -64,7 +64,7 @@ public PiccodeValue execute(Integer frame) { var note = new PiccodeSimpleNote("Track size: " + ctx.getFramesCount()); err.addNote(note); - note = new PiccodeSimpleNote("Symbol table dump: " + sb.toString()); + note = new PiccodeSimpleNote("Symbol table dump: \n" + sb.toString()); err.addNote(note); throw err; } diff --git a/src/main/java/org/piccode/ast/ImportAst.java b/src/main/java/org/piccode/ast/ImportAst.java index 9cf0269..99c689f 100644 --- a/src/main/java/org/piccode/ast/ImportAst.java +++ b/src/main/java/org/piccode/ast/ImportAst.java @@ -122,7 +122,7 @@ private void executeLifted(List moduleNodes, List nestedLifted, Intege } } - private List loadModuleFromStdLib(String module, Integer frame) { + public List loadModuleFromStdLib(String module, Integer frame) { var storage = getAppStorage(); var paths = List.of(storage, "./"); var nodes = new ArrayList(); diff --git a/src/main/java/org/piccode/ast/ImportModuleCreateAst.java b/src/main/java/org/piccode/ast/ImportModuleCreateAst.java new file mode 100644 index 0000000..8dd4861 --- /dev/null +++ b/src/main/java/org/piccode/ast/ImportModuleCreateAst.java @@ -0,0 +1,161 @@ +package org.piccode.ast; + +import java.io.File; +import java.io.FileNotFoundException; +import java.net.URISyntaxException; +import java.util.ArrayList; +import java.util.List; +import java.util.Scanner; +import java.util.logging.Level; +import java.util.logging.Logger; +import org.antlr.v4.runtime.CharStreams; +import org.antlr.v4.runtime.CommonTokenStream; +import org.piccode.antlr4.PiccodeScriptLexer; +import org.piccode.antlr4.PiccodeScriptParser; +import org.piccode.backend.Compiler; +import org.piccode.piccodescript.TargetEnvironment; +import org.piccode.rt.Context; +import org.piccode.rt.PiccodeBoolean; +import org.piccode.rt.PiccodeException; +import org.piccode.rt.PiccodeInfo; +import org.piccode.rt.PiccodeModule; +import org.piccode.rt.PiccodeUnit; +import org.piccode.rt.PiccodeValue; + +/** + * + * @author hexaredecimal + */ +public class ImportModuleCreateAst extends Ast { + + public String name; + public ImportAst import_; + private String path; + + public ImportModuleCreateAst(String name, ImportAst import_) { + this.name = name; + this.import_ = import_; + this.path = import_.path; + } + + @Override + public PiccodeValue execute(Integer frame) { + var nodes = import_.loadModuleFromStdLib(path, frame); + + var final_nodes = new ArrayList(); + var lifted = import_.lifted; + + if (lifted.isEmpty()) { + nodes.forEach(node -> { + if (node instanceof ModuleAst mod && mod.name.equals(name)) { + final_nodes.addAll(mod.nodes); + } else { + final_nodes.add(node); + } + }); + + var top = Context.top.getValue(name); + if (top != null && top instanceof PiccodeModule module) { + final_nodes.addAll(module.nodes); + var mod = new ModuleAst(name, final_nodes); + var result = mod.execute(frame); + Context.top.putLocal(name, result); + return result; + } else { + var mod = new ModuleAst(name, final_nodes); + var result = mod.execute(frame); + return result; + } + } + + for (var liftedNode : lifted) { + executeLifted(nodes, liftedNode, frame, final_nodes); + } + + var top = Context.top.getValue(name); + if (top != null && top instanceof PiccodeModule module) { + final_nodes.addAll(module.nodes); + var mod = new ModuleAst(name, final_nodes); + var result = mod.execute(frame); + Context.top.putLocal(name, result); + return result; + } else { + var mod = new ModuleAst(name, final_nodes); + var result = mod.execute(frame); + return result; + } + + } + + private void executeLifted(List moduleNodes, Ast liftedNode, Integer frame, List final_nodes) { + if (liftedNode instanceof ImportId id) { + for (var node : moduleNodes) { + if (node instanceof ImportAst imp) { + imp.execute(frame); + } + if (node instanceof StatementList sts) { + executeLifted(sts.nodes, id, frame, final_nodes); + return; + } + if (node instanceof FunctionAst func && func.name.equals(id.text)) { + final_nodes.add(node); + return; + } + if (node instanceof ModuleAst mod && mod.name.equals(id.text)) { + if (id.text.equals(name)) { + final_nodes.addAll(mod.nodes); + return; + } + + mod.createSymbol = false; + final_nodes.add(node); + return; + } + } + var err = new PiccodeException(file, line, column, "Symbol '" + id.text + "' not found in module: " + path); + err.frame = frame; + throw err; + } + + if (liftedNode instanceof ImportLift lift) { + for (var node : moduleNodes) { + if (node instanceof ImportAst imp) { + imp.execute(frame); + } + if (node instanceof StatementList sts) { + executeLifted(sts.nodes, lift, frame, final_nodes); + return; + } + if (node instanceof ModuleAst mod && mod.name.equals(lift.text)) { + // Recursively execute lifted symbols from inside the module + executeLifted(mod.nodes, lift.nodes, frame, final_nodes); + return; + } + if (node instanceof FunctionAst func && func.name.equals(lift.text)) { + var err = new PiccodeException(lift.file, lift.line, lift.column, + "Invalid import lift. Attempt to lift function as module"); + err.frame = frame; + PiccodeInfo note = new PiccodeException(func.file, func.line, func.column, + "Function is declared here"); + err.addNote(note); + throw err; + } + } + var err = new PiccodeException(file, line, column, + "Module '" + lift.text + "' not found in module: " + path.replaceAll("/", ".")); + err.frame = frame; + throw err; + } + } + + private void executeLifted(List moduleNodes, List nestedLifted, Integer frame, List final_nodes) { + for (var lifted : nestedLifted) { + executeLifted(moduleNodes, lifted, frame, final_nodes); + } + } + + @Override + public String codeGen(TargetEnvironment target) { + throw new UnsupportedOperationException("Not supported yet."); // Generated from nbfs://nbhost/SystemFileSystem/Templates/Classes/Code/GeneratedMethodBody + } +} diff --git a/src/main/java/org/piccode/ast/ModuleAst.java b/src/main/java/org/piccode/ast/ModuleAst.java index f705f4f..bc18723 100644 --- a/src/main/java/org/piccode/ast/ModuleAst.java +++ b/src/main/java/org/piccode/ast/ModuleAst.java @@ -13,10 +13,12 @@ public class ModuleAst extends Ast { public String name; public List nodes; + public boolean createSymbol; public ModuleAst(String name, List nodes) { this.name = name; this.nodes = nodes; + this.createSymbol = true; } @Override @@ -33,15 +35,20 @@ public String toString() { @Override public PiccodeValue execute(Integer frame) { - if (Context.modules.containsKey(name)) { - var mod = Context.modules.get(name); - mod.nodes.addAll(nodes); + var ctx = Context.top; + var module = ctx.getValue(name); + + if ((module == null) || !(module instanceof PiccodeModule)) { + var mod = new PiccodeModule(name, nodes); + if (createSymbol) { + ctx.putLocal(name, mod); + } return mod; - } else { - var module = new PiccodeModule(name, nodes); - Context.modules.put(name, module); - return module; } + + var mod = (PiccodeModule) module; + mod.nodes.addAll(nodes); + return mod; } @Override diff --git a/src/main/java/org/piccode/ast/PiccodeVisitor.java b/src/main/java/org/piccode/ast/PiccodeVisitor.java index d174627..4018238 100644 --- a/src/main/java/org/piccode/ast/PiccodeVisitor.java +++ b/src/main/java/org/piccode/ast/PiccodeVisitor.java @@ -112,45 +112,15 @@ public Ast visitArg(ArgContext ctx) { var tok = ctx.ID().getSymbol(); var name = ctx.ID().getText(); if (ctx.ASSIGN() != null) { - var value = visitLiteral_expr(ctx.literal_expr()); + var value = visitExpr(ctx.expr()); var result = new Arg(name, value); - result.line = tok.getLine(); - result.column = tok.getCharPositionInLine(); - result.file = fileName; - return result; + result.export = ctx.USE() != null; + return finalizeAstNode(result, tok); } var result = new Arg(name); - result.line = tok.getLine(); - result.column = tok.getCharPositionInLine(); - result.file = fileName; - return result; - } - - @Override - public Ast visitLiteral_expr(Literal_exprContext ctx) { - if (ctx.NUMBER() != null) { - return visitNumber(ctx.NUMBER()); - } - - if (ctx.STRING() != null) { - return visitString(ctx.STRING()); - } - - if (ctx.array() != null) { - return visitArray(ctx.array()); - } - if (ctx.tuple() != null) { - return visitTuple(ctx.tuple()); - } - if (ctx.object() != null) { - return visitObject(ctx.object()); - } - - var tok = ctx.getStart(); - var err = new PiccodeException(fileName, tok.getLine(), tok.getCharPositionInLine(), "Invalid literal expression"); - err.frame = null; - throw err; + result.export = ctx.USE() != null; + return finalizeAstNode(result, tok); } @Override diff --git a/src/main/java/org/piccode/ast/ReturnAst.java b/src/main/java/org/piccode/ast/ReturnAst.java new file mode 100644 index 0000000..507b065 --- /dev/null +++ b/src/main/java/org/piccode/ast/ReturnAst.java @@ -0,0 +1,36 @@ +package org.piccode.ast; + +import org.piccode.piccodescript.TargetEnvironment; +import org.piccode.rt.PiccodeReturnException; +import org.piccode.rt.PiccodeValue; + +/** + * + * @author hexaredecimal + */ +public class ReturnAst extends Ast { + public Ast expr; + + public ReturnAst(Ast expr) { + this.expr = expr; + } + + + + @Override + public PiccodeValue execute(Integer frame) { + var result = expr.execute(frame); + throw new PiccodeReturnException(result); + } + + @Override + public String codeGen(TargetEnvironment target) { + return toString(); + } + + @Override + public String toString() { + return "return " + expr; + } + +} diff --git a/src/main/java/org/piccode/ast/StatementList.java b/src/main/java/org/piccode/ast/StatementList.java index 36e0da4..f11fdee 100644 --- a/src/main/java/org/piccode/ast/StatementList.java +++ b/src/main/java/org/piccode/ast/StatementList.java @@ -29,10 +29,11 @@ public String toString() { @Override public PiccodeValue execute(Integer frame) { + PiccodeValue result = null; for (var stmt: nodes) { - stmt.execute(frame); + result = stmt.execute(frame); } - return new PiccodeBoolean("true"); + return result; } @Override diff --git a/src/main/java/org/piccode/ast/StringAst.java b/src/main/java/org/piccode/ast/StringAst.java index 6c29baa..ae41062 100644 --- a/src/main/java/org/piccode/ast/StringAst.java +++ b/src/main/java/org/piccode/ast/StringAst.java @@ -1,6 +1,9 @@ package org.piccode.ast; +import java.util.function.Consumer; +import org.piccode.backend.Compiler; import org.piccode.piccodescript.TargetEnvironment; +import org.piccode.rt.PiccodeException; import org.piccode.rt.PiccodeString; import org.piccode.rt.PiccodeValue; @@ -22,7 +25,14 @@ public String toString() { @Override public PiccodeValue execute(Integer frame) { - return new PiccodeString(unescapeString(text)); + StringBuilder finalString = new StringBuilder(); + parseInterpolatedString(text, expr -> { + var result = Compiler.program(file, expr).execute(frame).toString(); + finalString.append(result); + }, finalString); + + var str = unescapeString(finalString.toString()); + return new PiccodeString(str); } public String unescapeString(String str) { @@ -81,4 +91,55 @@ public String unescapeString(String str) { public String codeGen(TargetEnvironment target) { return String.format("\"%s\"", text); } + public void parseInterpolatedString(String input, Consumer expressionHandler, StringBuilder outputBuilder) { + int length = input.length(); + int i = 0; + + while (i < length) { + char c = input.charAt(i); + if (c == '{') { + if (i + 1 < length && input.charAt(i + 1) == '{') { + outputBuilder.append('{'); + i += 2; + continue; + } + + + int startExpr = i + 1; + int braceDepth = 1; + i++; + + while (i < length && braceDepth > 0) { + if (input.charAt(i) == '{') { + braceDepth++; + } else if (input.charAt(i) == '}') { + braceDepth--; + if (braceDepth == 0) { + break; + } + } + i++; + } + + if (braceDepth == 0) { + String expr = input.substring(startExpr, i).trim(); + expressionHandler.accept(expr); + i++; + } else { + throw new PiccodeException(file, line, column, "Unmatched '{' in input string."); + } + } + else if (c == '}') { + if (i + 1 < length && input.charAt(i + 1) == '}') { + outputBuilder.append('}'); + i += 2; + continue; + } + } else { + outputBuilder.append(c); + i++; + } + } + } + } diff --git a/src/main/java/org/piccode/rt/PiccodeReturnException.java b/src/main/java/org/piccode/rt/PiccodeReturnException.java new file mode 100644 index 0000000..2da348f --- /dev/null +++ b/src/main/java/org/piccode/rt/PiccodeReturnException.java @@ -0,0 +1,14 @@ + +package org.piccode.rt; + +/** + * + * @author hexaredecimal + */ +public class PiccodeReturnException extends RuntimeException { + public PiccodeValue value; + + public PiccodeReturnException(PiccodeValue value) { + this.value = value; + } +}