Checkpoint ongoing work
authorNot Zed <notzed@gmail.com>
Sun, 9 Apr 2023 04:11:21 +0000 (13:41 +0930)
committerNot Zed <notzed@gmail.com>
Sun, 9 Apr 2023 04:50:23 +0000 (14:20 +0930)
 - added double and long as variable types
 - improved function resolution logic for type matching
 - coerce variables as necessary for function calls
 - simplify coercion code

src/notzed.scripta/classes/au/notzed/scripta/AST.java
src/notzed.scripta/classes/au/notzed/scripta/ASTBuilder.java
src/notzed.scripta/classes/au/notzed/scripta/ASTPrinter.java
src/notzed.scripta/classes/au/notzed/scripta/ASTVisitor.java
src/notzed.scripta/classes/au/notzed/scripta/Compiler.java
src/notzed.scripta/classes/au/notzed/scripta/Generator.java
src/notzed.scripta/classes/au/notzed/scripta/Imports.java
src/notzed.scripta/gen/ScriptA.g4

index 2e34f87..264c782 100644 (file)
@@ -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;
                }
index 2b9641d..2ccc03b 100644 (file)
@@ -78,8 +78,12 @@ public class ASTBuilder extends ScriptABaseVisitor<AST> {
                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<AST> {
                        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<AST> {
                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<AST> {
                }
                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:
index 26f6d09..d983fce 100644 (file)
@@ -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);
index 03394cb..f238a51 100644 (file)
@@ -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);
        }
 
index 77a8bfe..e5705e9 100644 (file)
@@ -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<String> 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 {
index 1ccab0e..bd35e73 100644 (file)
@@ -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<Block> 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<Value> 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<String> 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));
        }
 
index bcd1b10..122344f 100644 (file)
@@ -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<Import> matchArguments(Type type) {
-               Type[] arga = type.getArgumentTypes();
-               return im -> {
-                       Type[] argb = im.type.getArgumentTypes();
+       static Type[] toArgumentTypes(List<Generator.Value> 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<Generator.Value> 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<Match> {
 
+               @Override
+               public int compareTo(Match o) {
+                       return Integer.compare(o.score, score);
+               }
        }
 
-       static Predicate<Import> matchArguments(Type arga[]) {
+       Function<Import, Match> 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<Generator.Value> arga) {
-               return arga.stream().map(Generator.Value::type).toArray(Type[]::new);
-       }
-
-       static Predicate<Import> matchType(Type type) {
-               return im -> {
-                       return im.type().equals(type);
+                       System.out.printf(" -> score %d\n", score);
+                       return new Match(im, score);
                };
        }
 
-       public Optional<Generator.Value> 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<Generator.Value> findImport(String name, List<Generator.Value> 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<Import> 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<Generator.Value> findImport(Type recv, String name, List<Generator.Value> 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<Import> 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() {
index aaaeb72..cd0e702 100644 (file)
@@ -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            : '~';