From 0b18c1a4ed93c5373ae2e12ad11ad11306c56d2e Mon Sep 17 00:00:00 2001 From: Not Zed Date: Sun, 9 Apr 2023 13:41:21 +0930 Subject: [PATCH] Checkpoint ongoing work - added double and long as variable types - improved function resolution logic for type matching - coerce variables as necessary for function calls - simplify coercion code --- .../classes/au/notzed/scripta/AST.java | 34 +- .../classes/au/notzed/scripta/ASTBuilder.java | 22 +- .../classes/au/notzed/scripta/ASTPrinter.java | 12 +- .../classes/au/notzed/scripta/ASTVisitor.java | 10 +- .../classes/au/notzed/scripta/Compiler.java | 38 +- .../classes/au/notzed/scripta/Generator.java | 410 ++++++++++-------- .../classes/au/notzed/scripta/Imports.java | 180 ++++---- src/notzed.scripta/gen/ScriptA.g4 | 16 +- 8 files changed, 425 insertions(+), 297 deletions(-) diff --git a/src/notzed.scripta/classes/au/notzed/scripta/AST.java b/src/notzed.scripta/classes/au/notzed/scripta/AST.java index 2e34f87..264c782 100644 --- a/src/notzed.scripta/classes/au/notzed/scripta/AST.java +++ b/src/notzed.scripta/classes/au/notzed/scripta/AST.java @@ -44,7 +44,9 @@ public abstract class AST { public enum XType { BOOLEAN, INTEGER, + LONG, FLOAT, + DOUBLE, STRING, OBJECT } @@ -507,10 +509,38 @@ public abstract class AST { } } - public static class XReal extends Expression { + public static class XLong extends Expression { + long value; + + public XLong(int lineNo, long value) { + super(lineNo); + this.value = value; + } + + @Override + public void accept(ASTVisitor av) { + av.visit(this); + } + } + + public static class XFloat extends Expression { float value; - public XReal(int lineNo, float value) { + public XFloat(int lineNo, float value) { + super(lineNo); + this.value = value; + } + + @Override + public void accept(ASTVisitor av) { + av.visit(this); + } + } + + public static class XDouble extends Expression { + double value; + + public XDouble(int lineNo, double value) { super(lineNo); this.value = value; } diff --git a/src/notzed.scripta/classes/au/notzed/scripta/ASTBuilder.java b/src/notzed.scripta/classes/au/notzed/scripta/ASTBuilder.java index 2b9641d..2ccc03b 100644 --- a/src/notzed.scripta/classes/au/notzed/scripta/ASTBuilder.java +++ b/src/notzed.scripta/classes/au/notzed/scripta/ASTBuilder.java @@ -78,8 +78,12 @@ public class ASTBuilder extends ScriptABaseVisitor { switch (tok.getType()) { case INT: return XType.INTEGER; + case LNG: + return XType.LONG; case FLT: return XType.FLOAT; + case DBL: + return XType.DOUBLE; case STR: return XType.STRING; case BLN: @@ -213,6 +217,12 @@ public class ASTBuilder extends ScriptABaseVisitor { return XBinaryOp.ADD; case SUB: return XBinaryOp.SUB; + case LSL: + return XBinaryOp.LSL; + case LSR: + return XBinaryOp.LSR; + case ASR: + return XBinaryOp.ASR; case MUL: return XBinaryOp.MUL; case DIV: @@ -262,7 +272,10 @@ public class ASTBuilder extends ScriptABaseVisitor { switch (ctx.start.getType()) { case INTEGER: { try { - return new XInteger(ctx.start.getLine(), Integer.parseInt(ctx.start.getText())); + if (ctx.size != null && ctx.size.getText().equals("L")) + return new XLong(ctx.start.getLine(), Long.parseLong(ctx.start.getText())); + else + return new XInteger(ctx.start.getLine(), Integer.parseInt(ctx.start.getText())); } catch (NumberFormatException x) { errors.add("Invalid INTEGER: " + ctx.start.getText()); return new XInteger(ctx.start.getLine(), Integer.MAX_VALUE); @@ -270,10 +283,13 @@ public class ASTBuilder extends ScriptABaseVisitor { } case FLOAT: { try { - return new XReal(ctx.start.getLine(), Float.parseFloat(ctx.start.getText())); + if (ctx.size != null && ctx.size.getText().equals("f")) + return new XFloat(ctx.start.getLine(), Float.parseFloat(ctx.start.getText())); + else + return new XDouble(ctx.start.getLine(), Double.parseDouble(ctx.start.getText())); } catch (NumberFormatException x) { errors.add("Invalid REAL: " + ctx.start.getText()); - return new XReal(ctx.start.getLine(), Float.NaN); + return new XFloat(ctx.start.getLine(), Float.NaN); } } case STRING: diff --git a/src/notzed.scripta/classes/au/notzed/scripta/ASTPrinter.java b/src/notzed.scripta/classes/au/notzed/scripta/ASTPrinter.java index 26f6d09..d983fce 100644 --- a/src/notzed.scripta/classes/au/notzed/scripta/ASTPrinter.java +++ b/src/notzed.scripta/classes/au/notzed/scripta/ASTPrinter.java @@ -193,10 +193,20 @@ public class ASTPrinter implements ASTVisitor { } @Override - public void visit(AST.XReal e) { + public void visit(AST.XLong e) { + System.out.printf("%dL", e.value); + } + + @Override + public void visit(AST.XFloat e) { System.out.printf("%ff", e.value); } + @Override + public void visit(AST.XDouble e) { + System.out.printf("%f", e.value); + } + @Override public void visit(AST.XString e) { System.out.printf("`%s`", e.value); diff --git a/src/notzed.scripta/classes/au/notzed/scripta/ASTVisitor.java b/src/notzed.scripta/classes/au/notzed/scripta/ASTVisitor.java index 03394cb..f238a51 100644 --- a/src/notzed.scripta/classes/au/notzed/scripta/ASTVisitor.java +++ b/src/notzed.scripta/classes/au/notzed/scripta/ASTVisitor.java @@ -101,7 +101,15 @@ public interface ASTVisitor { e.visitChildren(this); } - public default void visit(XReal e) { + public default void visit(XLong e) { + e.visitChildren(this); + } + + public default void visit(XFloat e) { + e.visitChildren(this); + } + + public default void visit(XDouble e) { e.visitChildren(this); } diff --git a/src/notzed.scripta/classes/au/notzed/scripta/Compiler.java b/src/notzed.scripta/classes/au/notzed/scripta/Compiler.java index 77a8bfe..e5705e9 100644 --- a/src/notzed.scripta/classes/au/notzed/scripta/Compiler.java +++ b/src/notzed.scripta/classes/au/notzed/scripta/Compiler.java @@ -23,12 +23,21 @@ import java.lang.reflect.InvocationTargetException; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.StandardOpenOption; +import java.util.ArrayList; +import java.util.BitSet; +import java.util.List; import java.util.stream.Stream; +import org.antlr.v4.runtime.ANTLRErrorListener; import org.antlr.v4.runtime.CharStream; import org.antlr.v4.runtime.CharStreams; import org.antlr.v4.runtime.CommonTokenStream; +import org.antlr.v4.runtime.Parser; import org.antlr.v4.runtime.ParserRuleContext; +import org.antlr.v4.runtime.RecognitionException; +import org.antlr.v4.runtime.Recognizer; import org.antlr.v4.runtime.Token; +import org.antlr.v4.runtime.atn.ATNConfigSet; +import org.antlr.v4.runtime.dfa.DFA; import org.antlr.v4.runtime.tree.ParseTree; public class Compiler { @@ -71,7 +80,8 @@ public class Compiler { while (a < 1) { a = a + 0.1 } """; - src = Files.readString(Path.of("script.sa")); + String name = "vars.sa"; + src = Files.readString(Path.of(name)); CharStream inputStream = CharStreams.fromString(src); var lexer = new ScriptALexer(inputStream); var tokenStream = new CommonTokenStream(lexer); @@ -87,14 +97,38 @@ public class Compiler { } var parser = new ScriptAParser(tokenStream); + + List log = new ArrayList<>(); + parser.addErrorListener(new ANTLRErrorListener() { + @Override + public void syntaxError(Recognizer recognizer, Object offendingSymbol, int line, int charPositionInLine, String msg, RecognitionException e) { + log.add(String.format("Syntax error:%d:%d: %s", line, charPositionInLine, msg)); + } + + @Override + public void reportAmbiguity(Parser recognizer, DFA dfa, int startIndex, int stopIndex, boolean exact, BitSet ambigAlts, ATNConfigSet configs) { + } + + @Override + public void reportAttemptingFullContext(Parser recognizer, DFA dfa, int startIndex, int stopIndex, BitSet conflictingAlts, ATNConfigSet configs) { + } + + @Override + public void reportContextSensitivity(Parser recognizer, DFA dfa, int startIndex, int stopIndex, int prediction, ATNConfigSet configs) { + } + }); + var cst = parser.script(); dump(" ", cst); + if (!log.isEmpty()) + return; + AST.ScriptA script = new ASTBuilder().visitScript(cst); new ASTPrinter().visitScript(script); - Generator gen = new Generator("script.sa"); + Generator gen = new Generator(name); gen.visitScript(script); try { diff --git a/src/notzed.scripta/classes/au/notzed/scripta/Generator.java b/src/notzed.scripta/classes/au/notzed/scripta/Generator.java index 1ccab0e..bd35e73 100644 --- a/src/notzed.scripta/classes/au/notzed/scripta/Generator.java +++ b/src/notzed.scripta/classes/au/notzed/scripta/Generator.java @@ -46,6 +46,7 @@ import org.objectweb.asm.Type; * */ public class Generator implements ASTVisitor { + boolean valid = true; ClassWriter cw; MethodVisitor mw; int variableID = 1; @@ -57,8 +58,11 @@ public class Generator implements ASTVisitor { static final Type STRING_TYPE = Type.getType(String.class); static final Type BOOLEAN_OBJECT_TYPE = Type.getType(Boolean.class); + static final Type BYTE_OBJECT_TYPE = Type.getType(Byte.class); static final Type INTEGER_OBJECT_TYPE = Type.getType(Integer.class); + static final Type LONG_OBJECT_TYPE = Type.getType(Long.class); static final Type FLOAT_OBJECT_TYPE = Type.getType(Float.class); + static final Type DOUBLE_OBJECT_TYPE = Type.getType(Double.class); record Variable(String name, int id, Type type) { } @@ -72,10 +76,26 @@ public class Generator implements ASTVisitor { return new Value(Type.FLOAT_TYPE, mv -> mv.visitLdcInsn(v)); } + static public Value constant(double v) { + return new Value(Type.DOUBLE_TYPE, mv -> mv.visitLdcInsn(v)); + } + + static public Value constant(byte v) { + return new Value(Type.BYTE_TYPE, mv -> mv.visitLdcInsn(v)); + } + + static public Value constant(short v) { + return new Value(Type.SHORT_TYPE, mv -> mv.visitLdcInsn(v)); + } + static public Value constant(int v) { return new Value(Type.INT_TYPE, mv -> mv.visitLdcInsn(v)); } + static public Value constant(long v) { + return new Value(Type.LONG_TYPE, mv -> mv.visitLdcInsn(v)); + } + static public Value constant(boolean v) { return new Value(Type.BOOLEAN_TYPE, mv -> mv.visitLdcInsn(v)); } @@ -87,6 +107,152 @@ public class Generator implements ASTVisitor { static public Value constant(Object v) { return new Value(Type.getType(v.getClass()), mv -> mv.visitLdcInsn(v)); } + + public boolean isNumber() { + return type.equals(INTEGER_OBJECT_TYPE) + || type.equals(FLOAT_OBJECT_TYPE) + || type.equals(LONG_OBJECT_TYPE) + || type.equals(DOUBLE_OBJECT_TYPE) + || type.equals(BYTE_OBJECT_TYPE); + } + + public boolean isBoolean() { + return type.equals(BOOLEAN_OBJECT_TYPE); + } + + public Value promote(Type to) { + switch (to.getSort()) { + case Type.BOOLEAN: + return promoteBoolean(); + case Type.INT: + return promoteInt(); + case Type.LONG: + return promoteLong(); + case Type.FLOAT: + return promoteFloat(); + case Type.DOUBLE: + return promoteDouble(); + case Type.OBJECT: + if (to.equals(STRING_TYPE)) + return promoteString(); + else if (to.equals(INTEGER_OBJECT_TYPE)) + return promoteInt().then(mv -> mv.visitMethodInsn(INVOKESTATIC, "java/lang/Integer", "valueOf", "(I)Ljava/lang/Integer;", false)); + else if (to.equals(FLOAT_OBJECT_TYPE)) + return promoteFloat().then(mv -> mv.visitMethodInsn(INVOKESTATIC, "java/lang/Float", "valueOf", "(F)Ljava/lang/Float;", false)); + else if (to.equals(BOOLEAN_OBJECT_TYPE)) + return promoteBoolean().then(mv -> mv.visitMethodInsn(INVOKESTATIC, "java/lang/Boolean", "valueOf", "(Z)Ljava/lang/Boolean;", false)); + else if (to.equals(LONG_OBJECT_TYPE)) + return promoteLong().then(mv -> mv.visitMethodInsn(INVOKESTATIC, "java/lang/Long", "valueOf", "(J)Ljava/lang/Long;", false)); + else if (to.equals(DOUBLE_OBJECT_TYPE)) + return promoteDouble().then(mv -> mv.visitMethodInsn(INVOKESTATIC, "java/lang/Double", "valueOf", "(D)Ljava/lang/Double;", false)); + default: + throw new IllegalArgumentException("expecting " + to + ": " + this); + } + } + + Value promoteBoolean() { + if (type == Type.BOOLEAN_TYPE) { + return this; + } else if (isBoolean()) { + return then(mv -> mv.visitMethodInsn(INVOKEINTERFACE, "java/lang/Boolean", "booleanValue", "()Z", false)); + } else if (type.equals(STRING_TYPE)) { + return then(mv -> mv.visitMethodInsn(INVOKESTATIC, "java/lang/Boolean", "parseBoolean", "(Ljava/lang/String;)Z", false)); + } else { + throw new IllegalArgumentException("expecting boolean or boolean string: " + this); + } + } + + Value promoteInt() { + if (type == Type.INT_TYPE) { + return this; + } else if (type == Type.FLOAT_TYPE) { + return then(mv -> mv.visitInsn(F2I)); + } else if (type == Type.LONG_TYPE) { + return then(mv -> mv.visitInsn(L2I)); + } else if (type == Type.DOUBLE_TYPE) { + return then(mv -> mv.visitInsn(D2I)); + } else if (isNumber()) { + return then(mv -> mv.visitMethodInsn(INVOKEINTERFACE, "java/lang/Number", "intValue", "()I", true)); + } else if (type.equals(STRING_TYPE)) { + return then(mv -> mv.visitMethodInsn(INVOKESTATIC, "java/lang/Integer", "parseInt", "(Ljava/lang/String;)I", false)); + } else { + throw new IllegalArgumentException("expecting number or numerical string " + this); + } + } + + Value promoteLong() { + if (type == Type.LONG_TYPE) { + return this; + } else if (type == Type.FLOAT_TYPE) { + return then(mv -> mv.visitInsn(F2L)); + } else if (type == Type.INT_TYPE) { + return then(mv -> mv.visitInsn(I2L)); + } else if (type == Type.DOUBLE_TYPE) { + return then(mv -> mv.visitInsn(D2L)); + } else if (isNumber()) { + return then(mv -> mv.visitMethodInsn(INVOKEINTERFACE, "java/lang/Number", "longValue", "()J", true)); + } else if (type.equals(STRING_TYPE)) { + return then(mv -> mv.visitMethodInsn(INVOKESTATIC, "java/lang/Long", "parseLong", "(Ljava/lang/String;)J", false)); + } else { + throw new IllegalArgumentException("expecting number or numerical string"); + } + } + + public Value promoteFloat() { + if (type == Type.FLOAT_TYPE) { + return this; + } else if (type == Type.INT_TYPE) { + return then(mv -> mv.visitInsn(I2F)); + } else if (type == Type.DOUBLE_TYPE) { + return then(mv -> mv.visitInsn(D2F)); + } else if (type == Type.LONG_TYPE) { + return then(mv -> mv.visitInsn(L2F)); + } else if (isNumber()) { + return then(mv -> mv.visitMethodInsn(INVOKEINTERFACE, "java/lang/Number", "floatValue", "()F", false)); + } else if (type.equals(STRING_TYPE)) { + return then(mv -> mv.visitMethodInsn(INVOKESTATIC, "java/lang/Float", "parseFloat", "(Ljava/lang/String;)F", false)); + } else { + throw new IllegalArgumentException("expecting number or numerical string " + this); + } + } + + Value promoteDouble() { + if (type == Type.DOUBLE_TYPE) { + return this; + } else if (type == Type.INT_TYPE) { + return then(mv -> mv.visitInsn(I2D)); + } else if (type == Type.FLOAT_TYPE) { + return then(mv -> mv.visitInsn(F2D)); + } else if (type == Type.LONG_TYPE) { + return then(mv -> mv.visitInsn(L2D)); + } else if (isNumber()) { + return then(mv -> mv.visitMethodInsn(INVOKEINTERFACE, "java/lang/Number", "doubleValue", "()D", true)); + } else if (type.equals(STRING_TYPE)) { + return then(mv -> mv.visitMethodInsn(INVOKESTATIC, "java/lang/Double", "parseDouble", "(Ljava/lang/String;)D", false)); + } else { + throw new IllegalArgumentException("expecting number or numerical string"); + } + } + + Value promoteString() { + if (type.equals(STRING_TYPE)) { + return this; + } else { + switch (type.getSort()) { + case Type.INT: + return then(mv -> mv.visitMethodInsn(INVOKESTATIC, "java/lang/Integer", "toString", "(I)Ljava/lang/String;", false)); + case Type.LONG: + return then(mv -> mv.visitMethodInsn(INVOKESTATIC, "java/lang/Long", "toString", "(J)Ljava/lang/String;", false)); + case Type.FLOAT: + return then(mv -> mv.visitMethodInsn(INVOKESTATIC, "java/lang/Float", "toString", "(F)Ljava/lang/String;", false)); + case Type.DOUBLE: + return then(mv -> mv.visitMethodInsn(INVOKESTATIC, "java/lang/Boolean", "toString", "(Z)Ljava/lang/String;", false)); + case Type.OBJECT: + default: + return then(mv -> mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Object", "toString", "(Ljava/lang/Object;)Ljava/lang/String;", false)); + } + } + } } // maybe other shit like locals here too @@ -95,7 +261,7 @@ public class Generator implements ASTVisitor { LinkedList blocks = new LinkedList<>(); - Variable addVariable(String name, Type type) { + public Variable addVariable(String name, Type type) { Variable var; if (type == Type.DOUBLE_TYPE || type == Type.LONG_TYPE) { variableID = (variableID + 1) & ~1; @@ -142,8 +308,6 @@ public class Generator implements ASTVisitor { @Override public void visitScript(AST.ScriptA script) { - boolean valid = true; - if (valid) cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES); else @@ -240,7 +404,7 @@ public class Generator implements ASTVisitor { mw.visitLabel(test); Value testX = visitExpression(s.test); - promoteBoolean(testX).insert.accept(mw); + testX.promoteBoolean().insert.accept(mw); mw.visitJumpInsn(IFNE, loop); mw.visitLabel(exit); @@ -261,7 +425,7 @@ public class Generator implements ASTVisitor { blocks.pop(); } - static final Type typeMap[] = {Type.BOOLEAN_TYPE, Type.INT_TYPE, Type.FLOAT_TYPE, STRING_TYPE}; + static final Type typeMap[] = {Type.BOOLEAN_TYPE, Type.INT_TYPE, Type.LONG_TYPE, Type.FLOAT_TYPE, Type.DOUBLE_TYPE, STRING_TYPE}; @Override public void visitDecl(AST.SDeclare s) { @@ -276,17 +440,18 @@ public class Generator implements ASTVisitor { val = s.value.map(x -> visitExpression(x)); if (s.type.type == AST.XType.OBJECT) { - var = new Variable(s.name, variableID++, Type.getObjectType(s.type.typeName.get().name())); + var = addVariable(s.name, Type.getObjectType(s.type.typeName.get().name())); val.ifPresent(v -> { v.insert.accept(mw); mw.visitVarInsn(ASTORE, var.id); }); } else { - var = new Variable(s.name, variableID++, typeMap[s.type.type.ordinal()]); - val = val.map(v -> promote(v, s.type.type)); + var = addVariable(s.name, typeMap[s.type.type.ordinal()]); + val = val.map(v -> v.promote(var.type)); + System.out.printf(" var: %s = %s\n", var, val); val.ifPresent(v -> { v.insert.accept(mw); - mw.visitVarInsn(v.type().getOpcode(ISTORE), var.id); + mw.visitVarInsn(var.type().getOpcode(ISTORE), var.id); }); } variables.put(var.name, var); @@ -299,7 +464,7 @@ public class Generator implements ASTVisitor { Variable var = variables.get(s.ref.name()); Value val = visitExpression(s.value); - val = promote(val, var.type); + val = val.promote(var.type); val.insert.accept(mw); mw.visitVarInsn(var.type.getOpcode(ISTORE), var.id); } @@ -384,7 +549,7 @@ public class Generator implements ASTVisitor { stack.push(a.then(mv -> mv.visitInsn(a.type.getOpcode(INEG)))); break; case NOT: - Value b = promoteBoolean(a); + Value b = a.promoteBoolean(); stack.push(new Value(Type.BOOLEAN_TYPE, mv -> { b.insert.accept(mv); mw.visitInsn(ICONST_1); @@ -392,7 +557,7 @@ public class Generator implements ASTVisitor { })); break; case INV: - Value i = promoteInt(a); + Value i = a.promoteInt(); stack.push(new Value(Type.INT_TYPE, mv -> { i.insert.accept(mv); mw.visitInsn(ICONST_M1); @@ -405,7 +570,7 @@ public class Generator implements ASTVisitor { } static final int intOp[] = { - IADD, ISUB, IMUL, IDIV, IUSHR, ISHL, ISHR, IAND, IOR, IXOR + IADD, ISUB, IMUL, IDIV, 0, ISHR, ISHL, IUSHR, IAND, IOR, IXOR }; // CLT, CLE, CGT, CGE, CEQ, CNE, static final int cmpFloat[] = { @@ -422,147 +587,6 @@ public class Generator implements ASTVisitor { IAND, IOR, IXOR }; - boolean isAssignable(Class target, Type type) { - try { - Class klass = Class.forName(type.getClassName(), false, classLoader); - return target.isAssignableFrom(klass); - } catch (ClassNotFoundException ex) { - return false; - } - } - - Value promote(Value val, AST.XType to) { - switch (to) { - case BOOLEAN: - return promoteBoolean(val); - case INTEGER: - return promoteInt(val); - case FLOAT: - return promoteFloat(val); - case STRING: - return promoteString(val); - default: - return val; - } - } - - Value promote(Value val, Type to) { - switch (to.getSort()) { - case Type.BOOLEAN: - return promoteBoolean(val); - case Type.INT: - return promoteInt(val); - case Type.FLOAT: - return promoteFloat(val); - default: - if (to.equals(STRING_TYPE)) - return promoteString(val); - } - throw new IllegalArgumentException("expecting " + to + ": " + val); - } - - Value promoteBoolean(Value a) { - if (a.type == Type.BOOLEAN_TYPE) { - return a; - } else if (isAssignable(Boolean.class, a.type)) { - return a.then(mv -> mv.visitMethodInsn(INVOKEINTERFACE, "java/lang/Boolean", "booleanValue", "()Z", false)); - } else if (a.type.equals(STRING_TYPE)) { - return a.then(mv -> mv.visitMethodInsn(INVOKESTATIC, "java/lang/Boolean", "parseBoolean", "(Ljava/lang/String;)Z", false)); - } else { - throw new IllegalArgumentException("expecting boolean or boolean string: " + a); - } - } - - Value promoteDouble(Value a) { - if (a.type == Type.DOUBLE_TYPE) { - return a; - } else if (a.type == Type.INT_TYPE) { - return a.then(mv -> mv.visitInsn(I2D)); - } else if (a.type == Type.FLOAT_TYPE) { - return a.then(mv -> mv.visitInsn(F2D)); - } else if (a.type == Type.LONG_TYPE) { - return a.then(mv -> mv.visitInsn(L2D)); - } else if (isAssignable(Number.class, a.type)) { - return a.then(mv -> mv.visitMethodInsn(INVOKEINTERFACE, "java/lang/Number", "doubleValue", "()D", true)); - } else if (a.type.equals(STRING_TYPE)) { - return a.then(mv -> mv.visitMethodInsn(INVOKESTATIC, "java/lang/Double", "parseDouble", "(Ljava/lang/String;)D", false)); - } else { - throw new IllegalArgumentException("expecting number or numerical string"); - } - } - - Value promoteFloat(Value a) { - if (a.type == Type.FLOAT_TYPE) { - return a; - } else if (a.type == Type.INT_TYPE) { - return a.then(mv -> mv.visitInsn(I2F)); - } else if (a.type == Type.DOUBLE_TYPE) { - return a.then(mv -> mv.visitInsn(D2F)); - } else if (a.type == Type.LONG_TYPE) { - return a.then(mv -> mv.visitInsn(L2F)); - } else if (isAssignable(Number.class, a.type)) { - return a.then(mv -> mv.visitMethodInsn(INVOKEINTERFACE, "java/lang/Number", "floatValue", "()F", true)); - } else if (a.type.equals(STRING_TYPE)) { - return a.then(mv -> mv.visitMethodInsn(INVOKESTATIC, "java/lang/Float", "parseFloat", "(Ljava/lang/String;)F", false)); - } else { - throw new IllegalArgumentException("expecting number or numerical string"); - } - } - - Value promoteLong(Value a) { - if (a.type == Type.LONG_TYPE) { - return a; - } else if (a.type == Type.FLOAT_TYPE) { - return a.then(mv -> mv.visitInsn(F2L)); - } else if (a.type == Type.INT_TYPE) { - return a.then(mv -> mv.visitInsn(I2L)); - } else if (a.type == Type.DOUBLE_TYPE) { - return a.then(mv -> mv.visitInsn(D2L)); - } else if (isAssignable(Number.class, a.type)) { - return a.then(mv -> mv.visitMethodInsn(INVOKEINTERFACE, "java/lang/Number", "longValue", "()J", true)); - } else if (a.type.equals(STRING_TYPE)) { - return a.then(mv -> mv.visitMethodInsn(INVOKESTATIC, "java/lang/Long", "parseLong", "(Ljava/lang/String;)J", false)); - } else { - throw new IllegalArgumentException("expecting number or numerical string"); - } - } - - Value promoteInt(Value a) { - if (a.type == Type.INT_TYPE) { - return a; - } else if (a.type == Type.FLOAT_TYPE) { - return a.then(mv -> mv.visitInsn(F2I)); - } else if (a.type == Type.LONG_TYPE) { - return a.then(mv -> mv.visitInsn(L2I)); - } else if (a.type == Type.DOUBLE_TYPE) { - return a.then(mv -> mv.visitInsn(D2I)); - } else if (isAssignable(Number.class, a.type)) { - return a.then(mv -> mv.visitMethodInsn(INVOKEINTERFACE, "java/lang/Number", "intValue", "()I", true)); - } else if (a.type.equals(STRING_TYPE)) { - return a.then(mv -> mv.visitMethodInsn(INVOKESTATIC, "java/lang/Integer", "parseInt", "(Ljava/lang/String;)I", false)); - } else { - throw new IllegalArgumentException("expecting number or numerical string"); - } - } - - Value promoteString(Value a) { - if (a.type.equals(STRING_TYPE)) { - return a; - } else if (a.type == Type.INT_TYPE) { - return a.then(mv -> mv.visitMethodInsn(INVOKESTATIC, "java/lang/Integer", "toString", "(I)Ljava/lang/String;", false)); - } else if (a.type == Type.FLOAT_TYPE) { - return a.then(mv -> mv.visitMethodInsn(INVOKESTATIC, "java/lang/Float", "toString", "(F)Ljava/lang/String;", false)); - } else if (a.type == Type.DOUBLE_TYPE) { - return a.then(mv -> mv.visitMethodInsn(INVOKESTATIC, "java/lang/Double", "toString", "(D)Ljava/lang/String;", false)); - } else if (a.type == Type.LONG_TYPE) { - return a.then(mv -> mv.visitMethodInsn(INVOKESTATIC, "java/lang/Long", "toString", "(J)Ljava/lang/String;", false)); - } else if (a.type == Type.BOOLEAN_TYPE) { - return a.then(mv -> mv.visitMethodInsn(INVOKESTATIC, "java/lang/Boolean", "toString", "(Z)Ljava/lang/String;", false)); - } else { - return a.then(mv -> mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Object", "toString", "(Ljava/lang/Object;)Ljava/lang/String;", false)); - } - } - static Value compareFloat(Value c, Value d, int fcmp, int cmp) { return new Value(Type.BOOLEAN_TYPE, mv -> { Label t = new Label(); @@ -617,8 +641,8 @@ public class Generator implements ASTVisitor { case ADD: if (a.type == STRING_TYPE || b.type == STRING_TYPE) { stack.addFirst(new Value(STRING_TYPE, mv -> { - promoteString(a).insert.accept(mv); - promoteString(b).insert.accept(mv); + a.promoteString().insert.accept(mv); + b.promoteString().insert.accept(mv); mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/String", "concat", "(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;", false); })); return; @@ -626,24 +650,24 @@ public class Generator implements ASTVisitor { case SUB, MUL, DIV: if (a.type == Type.DOUBLE_TYPE || b.type == Type.DOUBLE_TYPE) { type = Type.DOUBLE_TYPE; - c = promoteDouble(a); - d = promoteDouble(b); + c = a.promoteDouble(); + d = b.promoteDouble(); } else if (a.type == Type.FLOAT_TYPE || b.type == Type.FLOAT_TYPE) { type = Type.FLOAT_TYPE; - c = promoteFloat(a); - d = promoteFloat(b); + c = a.promoteFloat(); + d = b.promoteFloat(); } else if (a.type == Type.LONG_TYPE || b.type == Type.LONG_TYPE) { type = Type.LONG_TYPE; - c = promoteLong(a); - d = promoteLong(b); + c = a.promoteLong(); + d = b.promoteLong(); } else if (a.type == Type.INT_TYPE || b.type == Type.INT_TYPE) { type = Type.INT_TYPE; - c = promoteInt(a); - d = promoteInt(b); + c = a.promoteInt(); + d = b.promoteInt(); } else { - type = Type.DOUBLE_TYPE; - c = promoteDouble(a); - d = promoteDouble(b); + type = Type.FLOAT_TYPE; + c = a.promoteFloat(); + d = b.promoteFloat(); } opcode = type.getOpcode(intOp[e.op.ordinal()]); System.out.printf("%s: %d -> %d\n", e.op, intOp[e.op.ordinal()], opcode); @@ -656,16 +680,16 @@ public class Generator implements ASTVisitor { case LSR, LSL, ASR, AND, ORR, XOR: if (a.type == Type.LONG_TYPE) { type = a.type; - c = promoteLong(a); - d = promoteInt(b); + c = a.promoteLong(); + d = b.promoteInt(); } else if (a.type == Type.INT_TYPE) { type = a.type; - c = promoteInt(a); - d = promoteInt(b); + c = a.promoteInt(); + d = b.promoteInt(); } else { type = Type.LONG_TYPE; - c = promoteLong(a); - d = promoteInt(b); + c = a.promoteLong(); + d = b.promoteInt(); } opcode = type.getOpcode(intOp[e.op.ordinal()]); stack.addFirst(new Value(type, mv -> { @@ -678,20 +702,20 @@ public class Generator implements ASTVisitor { int index = e.op.ordinal() - AST.XBinaryOp.CLT.ordinal(); if (a.type == Type.DOUBLE_TYPE || b.type == Type.DOUBLE_TYPE) { - stack.addFirst(compareFloat(promoteDouble(a), promoteDouble(b), cmpFloat[index] + 2, cmpOp[index])); + stack.addFirst(compareFloat(a.promoteDouble(), b.promoteDouble(), cmpFloat[index] + 2, cmpOp[index])); } else if (a.type == Type.FLOAT_TYPE || b.type == Type.FLOAT_TYPE) { - stack.addFirst(compareFloat(promoteFloat(a), promoteFloat(b), cmpFloat[index], cmpOp[index])); + stack.addFirst(compareFloat(a.promoteFloat(), b.promoteFloat(), cmpFloat[index], cmpOp[index])); } else if (a.type == Type.LONG_TYPE || b.type == Type.LONG_TYPE) { - stack.addFirst(compareInt(promoteLong(a), promoteLong(b), LCMP, cmpJump[index])); + stack.addFirst(compareInt(a.promoteLong(), b.promoteLong(), LCMP, cmpJump[index])); } else if (a.type == Type.INT_TYPE || b.type == Type.INT_TYPE) { - stack.addFirst(compareInt(promoteInt(a), promoteInt(b), 0, cmpJump[index])); + stack.addFirst(compareInt(a.promoteInt(), b.promoteInt(), 0, cmpJump[index])); } else { - stack.addFirst(compareInt(promoteLong(a), promoteLong(b), LCMP, cmpJump[index])); + stack.addFirst(compareInt(a.promoteInt(), b.promoteInt(), 0, cmpJump[index])); } break; case AAND, OOR, XXOR: - c = promoteBoolean(a); - d = promoteBoolean(b); + c = a.promoteBoolean(); + d = b.promoteBoolean(); opcode = boolOp[e.op.ordinal() - AST.XBinaryOp.AAND.ordinal()]; stack.addFirst(new Value(Type.BOOLEAN_TYPE, mv -> { c.insert.accept(mv); @@ -708,13 +732,19 @@ public class Generator implements ASTVisitor { public void visit(AST.XCall e) { System.out.println(e); // TODO: how to resolve parts of expression - // TODO: how to promote? List args = e.params.stream().map(this::visitExpression).toList(); - stack.push(imports.findImport(e.ref.name(), args).orElseThrow()); + + if (e.ref.part.length == 1) { + stack.push(imports.findImport(e.ref.name(), args).orElseThrow()); + // or func variable + } else { + throw new UnsupportedOperationException("namespace dereferencing unimplemented"); + } } @Override public void visit(AST.XFunction e) { + // unimplemented System.out.print("function ("); Iterator it = e.params.iterator(); if (it.hasNext()) @@ -752,13 +782,25 @@ public class Generator implements ASTVisitor { @Override public void visit(AST.XInteger e) { + System.out.printf("%d\n", e.value); + stack.push(Value.constant(e.value)); + } + + @Override + public void visit(AST.XLong e) { System.out.printf("%dL\n", e.value); stack.push(Value.constant(e.value)); } @Override - public void visit(AST.XReal e) { - System.out.printf("%fD", e.value); + public void visit(AST.XFloat e) { + System.out.printf("%ff", e.value); + stack.push(Value.constant(e.value)); + } + + @Override + public void visit(AST.XDouble e) { + System.out.printf("%f", e.value); stack.push(Value.constant(e.value)); } diff --git a/src/notzed.scripta/classes/au/notzed/scripta/Imports.java b/src/notzed.scripta/classes/au/notzed/scripta/Imports.java index bcd1b10..122344f 100644 --- a/src/notzed.scripta/classes/au/notzed/scripta/Imports.java +++ b/src/notzed.scripta/classes/au/notzed/scripta/Imports.java @@ -25,6 +25,7 @@ import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.Optional; +import java.util.function.Function; import java.util.function.Predicate; import java.util.stream.IntStream; import java.util.stream.Stream; @@ -32,6 +33,10 @@ import org.objectweb.asm.MethodVisitor; import static org.objectweb.asm.Opcodes.*; import org.objectweb.asm.Type; +/** + * TODO: might need a Lookup + * TODO: namespaces + */ public class Imports { /* Need to be able to find fields and methods based on: @@ -138,20 +143,21 @@ public class Imports { Type type = Type.getType(m); String name = m.getName(); + // TODO: interface if (Modifier.isStatic(m.getModifiers())) { importStatic(new Import(recv, name, type, (im, mv, args) -> { - // receive must already be present - // TODO: interface - // TODO: promote args - for (var a: args) { - a.insert().accept(mv); + Type[] argb = im.type.getArgumentTypes(); + for (int i = 0; i < argb.length; i++) { + args.get(i).promote(argb[i]).insert().accept(mv); } mv.visitMethodInsn(INVOKESTATIC, im.recv.getInternalName(), im.name, im.type.getDescriptor(), false); })); } else { + // receiver must already be present importInstance(new Import(recv, name, type, (im, mv, args) -> { - for (var a: args) { - a.insert().accept(mv); + Type[] argb = im.type.getArgumentTypes(); + for (int i = 0; i < argb.length; i++) { + args.get(i).promote(argb[i]).insert().accept(mv); } mv.visitMethodInsn(INVOKEVIRTUAL, im.recv.getInternalName(), im.name, im.type.getDescriptor(), false); })); @@ -160,36 +166,29 @@ public class Imports { } } - // TODO: match promotable types as well - // MethodType has some stuff but it's private - static Predicate matchArguments(Type type) { - Type[] arga = type.getArgumentTypes(); - return im -> { - Type[] argb = im.type.getArgumentTypes(); + static Type[] toArgumentTypes(List arga) { + return arga.stream().map(Generator.Value::type).toArray(Type[]::new); + } - System.out.printf("match %s %s\n", type, im.type); - if (arga.length == argb.length) { - for (int i = 0; i < arga.length; i++) { - if (!im.type().equals(type)) - return false; - } - return true; - } - return false; - }; + public Optional findStaticField(String name) { + return importStatic[IMPORT_FIELDS].values().stream() + .flatMap(m -> m.getOrDefault(name, List.of()).stream()) + .findFirst() + .map(m -> m.value()); } - // void boolean char byte short int float long double - static boolean matchArgument(Type a, Type b) { - return a.equals(b); - // || (a.getSort() >= Type.CHAR && a.getSort() <= Type.DOUBLE - // && b.getSort() >= Type.CHAR && b.getSort() <= Type.DOUBLE); + record Match(Import im, int score) implements Comparable { + @Override + public int compareTo(Match o) { + return Integer.compare(o.score, score); + } } - static Predicate matchArguments(Type arga[]) { + Function match(Type[] arga) { return im -> { Type[] argb = im.type.getArgumentTypes(); + int score = 0; System.out.printf("match arguments:\n %s\n %s\n", String.join(", ", Stream.of(arga).map(Object::toString).toArray(String[]::new)), @@ -197,37 +196,32 @@ public class Imports { if (arga.length == argb.length) { for (int i = 0; i < arga.length; i++) { - if (!matchArgument(arga[i], argb[i])) { - System.out.println(" -> false"); - return false; + Type a = arga[i], b = argb[i]; + int sa = a.getSort(), sb = b.getSort(); + + if (a.equals(b)) { + score += 10; + } else if ((sa == Type.FLOAT || sa == Type.DOUBLE) && (sb == Type.FLOAT || sb == Type.DOUBLE)) { + score += 8; + } else if ((sa == Type.INT || sa == Type.LONG) && (sb == Type.INT || sb == Type.LONG)) { + score += 8; + } else if (new Generator.Value(a, null).isNumber() && sb >= Type.CHAR && sb <= Type.DOUBLE) { + score += 6; + } else if (b.equals(Generator.STRING_TYPE)) { + // anything can be converted to string + score += 6; + } else { + System.out.printf(" -> no match\n"); + return new Match(im, -1); } } - System.out.println(" -> true"); - return true; } - System.out.println(" -> false"); - return false; - }; - } - - static Type[] toArgumentTypes(List arga) { - return arga.stream().map(Generator.Value::type).toArray(Type[]::new); - } - - static Predicate matchType(Type type) { - return im -> { - return im.type().equals(type); + System.out.printf(" -> score %d\n", score); + return new Match(im, score); }; } - public Optional findStaticField(String name) { - return importStatic[IMPORT_FIELDS].values().stream() - .flatMap(m -> m.getOrDefault(name, List.of()).stream()) - .findFirst() - .map(m -> m.value()); - } - - // Find a method. + // Find a static method. public Optional findImport(String name, List args) { Type[] arga = toArgumentTypes(args); System.out.printf("find method: %s", name); @@ -243,59 +237,43 @@ public class Imports { return importStatic[IMPORT_METHODS].values().stream() .flatMap(m -> m.getOrDefault(name, List.of()).stream()) .peek(m -> System.out.println("? " + m)) - .filter(matchArguments(arga)) + .map(match(arga)) + .filter(m -> m.score >= 0) + .sorted() .findFirst() - .map(m -> m.value(args)); + .map(m -> { + System.out.println("> " + m); + return m; + }) + .map(m -> m.im.value(args)); } - /** - * - * @param name - * @param type - * @return - * @deprecated not finished - */ - @Deprecated - public Optional findImport(String name, Type type) { - if (type.getSort() == Type.METHOD) { - System.out.printf("find method: %s\n", name); - System.out.println(importStatic[IMPORT_METHODS].toString()); - for (var map: importStatic[IMPORT_METHODS].values()) { - System.out.println(map.getClass()); - System.out.println(map); - for (var im: map.get(name)) { - System.out.printf(" ? %s\n", im); - } + // find non-static method + // ?? may have to search fields + public Optional findImport(Type recv, String name, List args) { + Type[] arga = toArgumentTypes(args); + System.out.printf("find method: %s", name); + args.stream().forEach(a -> System.out.printf(" %s", a.type())); + System.out.println(); + for (var map: importStatic[IMPORT_METHODS].entrySet()) { + System.out.println(map.getKey()); + for (var im: map.getValue().getOrDefault(name, List.of())) { + System.out.printf(" ? %s\n", im); } - System.out.println("----"); - return importStatic[IMPORT_METHODS].values().stream() - .peek(x -> System.out.print(" > ")) - .peek(System.out::println) - .flatMap(m -> m.get(name).stream()) - .peek(System.out::println) - .filter(matchArguments(type)) - .findFirst(); - } else { - System.out.printf("find field: %s\n", name); - importStatic[IMPORT_FIELDS].values().stream() - .flatMap(m -> m.get(name).stream()) - .filter(matchType(type)); } - return null; - } - - @Deprecated - public Optional findImport(Type recv, String name, Type type) { - if (type.getSort() == Type.METHOD) { - return importInstance[IMPORT_METHODS].values().stream() - .flatMap(m -> m.get(name).stream()) - .filter(matchArguments(type)) - .findFirst(); - } else { - } - - return null; + return importStatic[IMPORT_METHODS].values().stream() + .flatMap(m -> m.getOrDefault(name, List.of()).stream()) + .peek(m -> System.out.println("? " + m)) + .map(match(arga)) + .filter(m -> m.score >= 0) + .sorted() + .findFirst() + .map(m -> { + System.out.println("> " + m); + return m; + }) + .map(m -> m.im.value(args)); } void dump() { diff --git a/src/notzed.scripta/gen/ScriptA.g4 b/src/notzed.scripta/gen/ScriptA.g4 index aaaeb72..cd0e702 100644 --- a/src/notzed.scripta/gen/ScriptA.g4 +++ b/src/notzed.scripta/gen/ScriptA.g4 @@ -29,11 +29,14 @@ statement | RETURN valueExpr ? # returnStatement ; +// This more or less follows c operator precedence valueExpr : '(' valueExpr ')' # valueGroupExpr | op=('!'|'-'|'+'|'~') rightValue=valueExpr # valueUnaryExpr | leftValue=valueExpr op=('*'|'/'|'%') rightValue=valueExpr # valueBinaryExpr - | leftValue=valueExpr op=('&'|'|'|'+'|'-') rightValue=valueExpr # valueBinaryExpr + | leftValue=valueExpr op=('+'|'-') rightValue=valueExpr # valueBinaryExpr + | leftValue=valueExpr op=('<<'|'<<<'|'>>') rightValue=valueExpr # valueBinaryExpr + | leftValue=valueExpr op=('&'|'|') rightValue=valueExpr # valueBinaryExpr | leftValue=valueExpr op=('&&'|'||'|'^^') rightValue=valueExpr # valueBinaryExpr | leftValue=valueExpr op=(LT|LE|GT|GE|EQ|NE) rightValue=valueExpr # valueBinaryExpr | lit=literal # valueLiteralExpr @@ -61,9 +64,11 @@ INT : 'int'; FLT : 'float'; STR : 'string'; BLN : 'boolean'; +DBL : 'double'; +LNG : 'long'; -type : INT | FLT | STR | BLN | reference; -literal : INTEGER | FLOAT | STRING | TRUE | FALSE; +type : INT | LNG | FLT | DBL | STR | BLN | reference; +literal : INTEGER size='L'? | FLOAT size='f'? | STRING | TRUE | FALSE; reference : ID ('.' ID)*; TRUE : 'true'; @@ -83,6 +88,11 @@ SUB : '-'; MUL : '*'; DIV : '/'; MOD : '%'; + +LSL : '<<'; +LSR : '>>'; +ASR : '>>>'; + NOT : '!'; INV : '~'; -- 2.39.2