Checkpoint ongoing work. master
authorNot Zed <notzed@gmail.com>
Mon, 17 Apr 2023 08:56:40 +0000 (18:26 +0930)
committerNot Zed <notzed@gmail.com>
Mon, 17 Apr 2023 08:56:40 +0000 (18:26 +0930)
 - Implement 'var' declarations
 - Implement 'new' operator
 - Rejig the way fields are resolved to support chained operations
 - Implement null
 - Completely revamped type matching code
 - Import constructors
 - Various fixes

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/classes/au/notzed/scripta/Script.java
src/notzed.scripta/gen/ScriptA.g4

index 8c07d50..e3af07a 100644 (file)
@@ -48,7 +48,8 @@ public abstract class AST {
                FLOAT,
                DOUBLE,
                STRING,
-               OBJECT
+               OBJECT,
+               ANY
        }
 
        public enum XUnaryOp {
@@ -464,12 +465,12 @@ public abstract class AST {
                        return String.join(".", part);
                }
 
-               public String prefix(int stripPrefix) {
-                       return String.join(".", Arrays.copyOfRange(part, 0, part.length - stripPrefix));
+               public String prefix(int addPrefix) {
+                       return String.join(".", Arrays.copyOf(part, addPrefix));
                }
 
-               public String suffix(int stripSuffix) {
-                       return String.join(".", Arrays.copyOf(part, stripSuffix));
+               public String suffix(int addSuffix) {
+                       return String.join(".", Arrays.copyOfRange(part, 0, part.length - addSuffix));
                }
 
                public String part(int stripPrefix, int stripSuffix) {
@@ -482,6 +483,24 @@ public abstract class AST {
                }
        }
 
+       public static class XID extends Expression {
+               String id;
+
+               public XID(int lineNo, String id) {
+                       super(lineNo);
+                       this.id = id;
+               }
+
+               @Override
+               public void accept(ASTVisitor av) {
+                       av.visit(this);
+               }
+
+               @Override
+               public void visitChildren(ASTVisitor av) {
+               }
+       }
+
        public static class XField extends Expression {
                Expression ref;
                String field;
@@ -503,15 +522,44 @@ public abstract class AST {
                }
        }
 
+       public static class XNew extends Expression {
+               String name;
+               List<Expression> args;
+
+               public XNew(int lineNo, String name, List<Expression> args) {
+                       super(lineNo);
+                       this.name = name;
+                       this.args = args;
+               }
+
+               @Override
+               public void accept(ASTVisitor av) {
+                       av.visit(this);
+               }
+
+               @Override
+               public void visitChildren(ASTVisitor av) {
+                       accept(args, av);
+               }
+
+               @Override
+               public String toString() {
+                       StringBuilder sb = new StringBuilder("[new ");
+                       sb.append(name);
+                       sb.append("(");
+                       sb.append(String.join(", ", args.stream().map(x -> x.toString()).toArray(String[]::new)));
+                       sb.append(")]");
+                       return sb.toString();
+               }
+       }
+
        public static class XCall extends Expression {
-               boolean constructor;
                Optional<Expression> ref;
                String name;
                List<Expression> args;
 
-               public XCall(int lineNo, boolean constructor, Optional<Expression> ref, String name, List<Expression> args) {
+               public XCall(int lineNo, Optional<Expression> ref, String name, List<Expression> args) {
                        super(lineNo);
-                       this.constructor = constructor;
                        this.ref = ref;
                        this.name = name;
                        this.args = args;
@@ -646,4 +694,17 @@ public abstract class AST {
                        av.visit(this);
                }
        }
+
+       public static class XNull extends Expression {
+
+               public XNull(int lineNo) {
+                       super(lineNo);
+               }
+
+               @Override
+               public void accept(ASTVisitor av) {
+                       av.visit(this);
+               }
+       }
+
 }
index 41590cc..7229dd6 100644 (file)
@@ -96,6 +96,8 @@ public class ASTBuilder extends ScriptABaseVisitor<AST> {
                        return XType.BOOLEAN;
                case ID:
                        return XType.OBJECT;
+               case VAR:
+                       return XType.ANY;
                default:
                        throw new UnsupportedOperationException(tok.getLine() + ": " + VOCABULARY.getDisplayName(tok.getType()));
                }
@@ -137,7 +139,6 @@ public class ASTBuilder extends ScriptABaseVisitor<AST> {
                return new SCall(ctx.start.getLine(),
                        new XCall(
                                ctx.start.getLine(),
-                               false,
                                expression(ctx.left),
                                ctx.ID().getText(),
                                ctx.args.valueExpr().stream().map(v -> (AST.Expression)v.accept(this)).toList()));
@@ -205,19 +206,16 @@ public class ASTBuilder extends ScriptABaseVisitor<AST> {
        public AST visitValueCallExpr(ValueCallExprContext ctx) {
                return new XCall(
                        ctx.start.getLine(),
-                       false,
                        expression(ctx.left),
                        ctx.ID().getText(),
                        ctx.args.valueExpr().stream().map(v -> (AST.Expression)v.accept(this)).toList());
        }
 
        @Override
-       public AST visitValueNewExpr(ValueNewExprContext ctx) {
-               return new XCall(
+       public AST.XNew visitValueNewExpr(ValueNewExprContext ctx) {
+               return new XNew(
                        ctx.start.getLine(),
-                       true,
-                       Optional.of(visitReference(ctx.ref)),
-                       "<init>",
+                       visitReference(ctx.ref).name(),
                        ctx.args.valueExpr().stream().map(v -> (AST.Expression)v.accept(this)).toList());
        }
 
@@ -337,11 +335,18 @@ public class ASTBuilder extends ScriptABaseVisitor<AST> {
                        return new XBool(ctx.start.getLine(), true);
                case FALSE:
                        return new XBool(ctx.start.getLine(), false);
+               case NULL:
+                       return new XNull(ctx.start.getLine());
                default:
                        throw new UnsupportedOperationException(ctx.start.getLine() + ": " + VOCABULARY.getDisplayName(ctx.start.getType()));
                }
        }
 
+       @Override
+       public AST visitValueIDExpr(ValueIDExprContext ctx) {
+               return new AST.XID(ctx.id.getLine(), ctx.id.getText());
+       }
+
        @Override
        public AST.XReference visitReference(ReferenceContext ctx) {
                return new XReference(ctx.start.getLine(), ctx.ID().stream().map(t -> t.getText()).toArray(String[]::new));
index aa13f6c..6740301 100644 (file)
@@ -159,6 +159,21 @@ public class ASTPrinter implements ASTVisitor {
                System.out.print("]");
        }
 
+       @Override
+       public void visit(AST.XNew e) {
+               System.out.print("new ");
+               System.out.print(e.name);
+               System.out.print("(");
+               Iterator<AST.Expression> it = e.args.iterator();
+               if (it.hasNext())
+                       it.next().accept(this);
+               while (it.hasNext()) {
+                       System.out.print(", ");
+                       it.next().accept(this);
+               }
+               System.out.print(")");
+       }
+
        @Override
        public void visit(AST.XCall e) {
                e.ref.ifPresent(x -> {
@@ -202,6 +217,13 @@ public class ASTPrinter implements ASTVisitor {
                System.out.printf("%s}\n", d);
        }
 
+       @Override
+       public void visit(AST.XID e) {
+               System.out.print("<");
+               System.out.print(e.id);
+               System.out.print(">");
+       }
+
        @Override
        public void visit(AST.XReference e) {
                System.out.print("<");
index b097da1..f2afee8 100644 (file)
@@ -91,10 +91,18 @@ public interface ASTVisitor {
                e.visitChildren(this);
        }
 
+       public default void visit(XID e) {
+               e.visitChildren(this);
+       }
+
        public default void visit(XField e) {
                e.visitChildren(this);
        }
 
+       public default void visit(XNew e) {
+               e.visitChildren(this);
+       }
+
        public default void visit(XCall e) {
                e.visitChildren(this);
        }
@@ -127,4 +135,8 @@ public interface ASTVisitor {
                e.visitChildren(this);
        }
 
+       public default void visit(XNull e) {
+               e.visitChildren(this);
+       }
+
 }
index 1c7e6bc..f83f06c 100644 (file)
@@ -144,7 +144,9 @@ public class Compiler {
                        System.out.println(x);
                        Script s = (Script)x;
 
-                       s.eval();
+                       s.run();
+
+                       System.out.println("name: " + s.name);
                } catch (IOException ex) {
                        ex.printStackTrace();
                } catch (IllegalAccessException ex) {
index 0201287..92b1cca 100644 (file)
 package au.notzed.scripta;
 
 import java.io.PrintStream;
+import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.Iterator;
 import java.util.LinkedList;
 import java.util.List;
 import java.util.Map;
-import java.util.NoSuchElementException;
 import java.util.Optional;
-import java.util.function.Consumer;
+import java.util.function.BiFunction;
 import org.objectweb.asm.ClassWriter;
 import org.objectweb.asm.Label;
 import org.objectweb.asm.MethodVisitor;
@@ -41,6 +41,8 @@ import org.objectweb.asm.Type;
       field (imported)
       method() (imported)
 
+  - values need lvalue and rvalue inserters!
+
   - syntax
       can it be simpler?
          arrays at least would be nice
@@ -50,15 +52,18 @@ public class Generator implements ASTVisitor {
        boolean valid = false;
        ClassWriter cw;
        MethodVisitor mw;
-       int variableID = 1;
+       int variableID;
        Map<String, Variable> variables = new HashMap<>();
        LinkedList<Value> stack = new LinkedList<>();
+       StringBuilder refname = new StringBuilder();
        ClassLoader classLoader = new ClassLoader() {
        };
        Imports imports = new Imports();
 
        static final Type STRING_TYPE = Type.getType(String.class);
        static final Type BOOLEAN_OBJECT_TYPE = Type.getType(Boolean.class);
+       static final Type OBJECT_OBJECT_TYPE = Type.getType(Object.class);
+       static final Type NUMBER_OBJECT_TYPE = Type.getType(Number.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);
@@ -66,195 +71,428 @@ public class Generator implements ASTVisitor {
        static final Type DOUBLE_OBJECT_TYPE = Type.getType(Double.class);
 
        record Variable(String name, int id, Type type) {
+               public Value value() {
+                       return new Value(
+                               type,
+                               Value.promoteFrom(type),
+                               (mv, o) -> {
+                                       mv.visitVarInsn(type.getOpcode(ISTORE), id);
+                               }, (mv, o) -> {
+                                       mv.visitVarInsn(type.getOpcode(ILOAD), id);
+                               });
+               }
        }
 
-       record Value(Type type, Consumer<MethodVisitor> insert) {
-               public Value then(Consumer<MethodVisitor> then) {
-                       return new Value(type, insert.andThen(then));
-               }
+       public interface InsertFunction {
+               void accept(MethodVisitor mv, Value recv);
 
-               static public Value constant(float v) {
-                       return new Value(Type.FLOAT_TYPE, mv -> mv.visitLdcInsn(v));
+               default void accept(MethodVisitor mv) {
+                       accept(mv, null);
                }
+       }
 
-               static public Value constant(double v) {
-                       return new Value(Type.DOUBLE_TYPE, mv -> mv.visitLdcInsn(v));
-               }
+       record Value(Type type, BiFunction<Value, Type, Value> promote, InsertFunction lval, InsertFunction rval) {
 
-               static public Value constant(byte v) {
-                       return new Value(Type.BYTE_TYPE, mv -> mv.visitLdcInsn(v));
+               public Value(Type type, BiFunction<Value, Type, Value> promote, InsertFunction rval) {
+                       this(type, promote, null, rval);
                }
 
-               static public Value constant(short v) {
-                       return new Value(Type.SHORT_TYPE, mv -> mv.visitLdcInsn(v));
+               public Value(Type type, InsertFunction lval, InsertFunction rval) {
+                       this(type, promoteFrom(type), lval, rval);
                }
 
-               static public Value constant(int v) {
-                       return new Value(Type.INT_TYPE, mv -> mv.visitLdcInsn(v));
+               public Value(Type type, InsertFunction rval) {
+                       this(type, promoteFrom(type), null, rval);
                }
 
-               static public Value constant(long v) {
-                       return new Value(Type.LONG_TYPE, mv -> mv.visitLdcInsn(v));
+               public Value rval(Type newtype, BiFunction<Value, Type, Value> promote, InsertFunction then) {
+                       return new Value(newtype, promote, lval, (mv, o) -> {
+                               rval.accept(mv, o);
+                               then.accept(mv, o);
+                       });
                }
 
-               static public Value constant(boolean v) {
-                       return new Value(Type.BOOLEAN_TYPE, mv -> mv.visitLdcInsn(v));
+               public Value rval(Type newtype, InsertFunction then) {
+                       return new Value(newtype, lval, (mv, o) -> {
+                               rval.accept(mv, o);
+                               then.accept(mv, o);
+                       });
                }
 
-               static public Value constant(String v) {
-                       return new Value(STRING_TYPE, mv -> mv.visitLdcInsn(v));
+               public Value promote(Type to) {
+                       return promote.apply(this, to);
                }
 
-               static public Value constant(Object v) {
-                       return new Value(Type.getType(v.getClass()), mv -> mv.visitLdcInsn(v));
-               }
+               static public final Value NULL = new Value(OBJECT_OBJECT_TYPE,
+                       (self, type) -> {
+                               if (type.getSort() == Type.OBJECT)
+                                       return self;
+                               else
+                                       throw new UnsupportedOperationException("null is only Object");
+                       }, (mv, recv) -> {
+                               mv.visitInsn(ACONST_NULL);
+                       });
 
-               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);
+               static BiFunction<Value, Type, Value> promoteFrom(Type from) {
+                       switch (from.getSort()) {
+                       case Type.FLOAT:
+                               return Value::floatTo;
+                       case Type.DOUBLE:
+                               return Value::doubleTo;
+                       case Type.INT:
+                               return Value::intTo;
+                       case Type.LONG:
+                               return Value::longTo;
+                       case Type.BOOLEAN:
+                               return Value::booleanTo;
+                       case Type.OBJECT:
+                               if (from.equals(STRING_TYPE)) {
+                                       return Value::stringTo;
+                               } else if (from.equals(FLOAT_OBJECT_TYPE)) {
+                                       return Value::FloatTo;
+                               } else if (from.equals(DOUBLE_OBJECT_TYPE)) {
+                                       return Value::DoubleTo;
+                               } else if (from.equals(INTEGER_OBJECT_TYPE)) {
+                                       return Value::IntegerTo;
+                               } else if (from.equals(LONG_OBJECT_TYPE)) {
+                                       return Value::LongTo;
+                               } else if (from.equals(BOOLEAN_OBJECT_TYPE)) {
+                                       return Value::BooleanTo;
+                               } else if (from.equals(OBJECT_OBJECT_TYPE)) {
+                                       return (v, t) -> v;
+                               }
+                       default:
+                               return (v, t) -> {
+                                       throw new UnsupportedOperationException();
+                               };
+                       }
                }
 
-               public boolean isBoolean() {
-                       return type.equals(BOOLEAN_OBJECT_TYPE);
+               static Value floatTo(Value self, Type type) {
+                       switch (type.getSort()) {
+                       case Type.FLOAT:
+                               return self;
+                       case Type.DOUBLE:
+                               return self.rval(type, Value::doubleTo, (mv, o) -> mv.visitInsn(F2D));
+                       case Type.INT:
+                               return self.rval(type, Value::intTo, (mv, o) -> mv.visitInsn(F2I));
+                       case Type.LONG:
+                               return self.rval(type, Value::longTo, (mv, o) -> mv.visitInsn(F2L));
+                       case Type.OBJECT:
+                               if (type.equals(STRING_TYPE)) {
+                                       return self.rval(type, Value::stringTo, (mv, o) -> mv.visitMethodInsn(INVOKESTATIC, "java/lang/Float", "toString", "(F)Ljava/lang/String;", false));
+                               } else if (type.equals(FLOAT_OBJECT_TYPE) || type.equals(NUMBER_OBJECT_TYPE) || type.equals(OBJECT_OBJECT_TYPE)) {
+                                       return self.rval(type, Value::FloatTo, (mv, o) -> mv.visitMethodInsn(INVOKESTATIC, "java/lang/Float", "valueOf", "(F)Ljava/lang/Float;", false));
+                               } else if (type.equals(DOUBLE_OBJECT_TYPE)) {
+                                       return self.rval(type, Value::DoubleTo, (mv, o) -> {
+                                               mv.visitInsn(F2D);
+                                               mv.visitMethodInsn(INVOKESTATIC, "java/lang/Double", "valueOf", "(D)Ljava/lang/Double;", false);
+                                       });
+                               } else if (type.equals(INTEGER_OBJECT_TYPE)) {
+                                       return self.rval(type, Value::IntegerTo, (mv, o) -> {
+                                               mv.visitInsn(F2I);
+                                               mv.visitMethodInsn(INVOKESTATIC, "java/lang/Integer", "valueOf", "(I)Ljava/lang/Integer;", false);
+                                       });
+                               } else if (type.equals(LONG_OBJECT_TYPE)) {
+                                       return self.rval(type, Value::LongTo, (mv, o) -> {
+                                               mv.visitInsn(F2L);
+                                               mv.visitMethodInsn(INVOKESTATIC, "java/lang/Long", "valueOf", "(L)Ljava/lang/Long;", false);
+                                       });
+                               }
+                       default:
+                               throw new UnsupportedOperationException();
+                       }
                }
 
-               public Value promote(Type to) {
-                       switch (to.getSort()) {
-                       case Type.BOOLEAN:
-                               return promoteBoolean();
+               static Value doubleTo(Value self, Type type) {
+                       switch (type.getSort()) {
+                       case Type.FLOAT:
+                               return self.rval(type, Value::floatTo, (mv, o) -> mv.visitInsn(D2F));
+                       case Type.DOUBLE:
+                               return self;
                        case Type.INT:
-                               return promoteInt();
+                               return self.rval(type, Value::intTo, (mv, o) -> mv.visitInsn(D2I));
                        case Type.LONG:
-                               return promoteLong();
+                               return self.rval(type, Value::longTo, (mv, o) -> mv.visitInsn(D2L));
+                       case Type.OBJECT:
+                               if (type.equals(STRING_TYPE)) {
+                                       return self.rval(type, Value::stringTo, (mv, o) -> mv.visitMethodInsn(INVOKESTATIC, "java/lang/Double", "toString", "(D)Ljava/lang/String;", false));
+                               } else if (type.equals(FLOAT_OBJECT_TYPE)) {
+                                       return self.rval(type, Value::FloatTo, (mv, o) -> {
+                                               mv.visitInsn(D2F);
+                                               mv.visitMethodInsn(INVOKESTATIC, "java/lang/Float", "valueOf", "(F)Ljava/lang/Float;", false);
+                                       });
+                               } else if (type.equals(DOUBLE_OBJECT_TYPE) || type.equals(NUMBER_OBJECT_TYPE) || type.equals(OBJECT_OBJECT_TYPE)) {
+                                       return self.rval(type, Value::DoubleTo, (mv, o) -> mv.visitMethodInsn(INVOKESTATIC, "java/lang/Double", "valueOf", "(D)Ljava/lang/Double;", false));
+                               } else if (type.equals(INTEGER_OBJECT_TYPE)) {
+                                       return self.rval(type, Value::IntegerTo, (mv, o) -> {
+                                               mv.visitInsn(D2I);
+                                               mv.visitMethodInsn(INVOKESTATIC, "java/lang/Integer", "valueOf", "(I)Ljava/lang/Integer;", false);
+                                       });
+                               } else if (type.equals(LONG_OBJECT_TYPE)) {
+                                       return self.rval(type, Value::LongTo, (mv, o) -> {
+                                               mv.visitInsn(D2L);
+                                               mv.visitMethodInsn(INVOKESTATIC, "java/lang/Long", "valueOf", "(L)Ljava/lang/Long;", false);
+                                       });
+                               }
+                       default:
+                               throw new UnsupportedOperationException();
+                       }
+               }
+
+               static Value intTo(Value self, Type type) {
+                       switch (type.getSort()) {
                        case Type.FLOAT:
-                               return promoteFloat();
+                               return self.rval(type, Value::floatTo, (mv, o) -> mv.visitInsn(I2F));
                        case Type.DOUBLE:
-                               return promoteDouble();
+                               return self.rval(type, Value::doubleTo, (mv, o) -> mv.visitInsn(I2D));
+                       case Type.INT:
+                               return self;
+                       case Type.LONG:
+                               return self.rval(type, Value::longTo, (mv, o) -> mv.visitInsn(I2L));
                        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));
+                               if (type.equals(STRING_TYPE)) {
+                                       return self.rval(type, Value::stringTo, (mv, o) -> mv.visitMethodInsn(INVOKESTATIC, "java/lang/Integer", "toString", "(I)Ljava/lang/String;", false));
+                               } else if (type.equals(FLOAT_OBJECT_TYPE)) {
+                                       return self.rval(type, Value::FloatTo, (mv, o) -> {
+                                               mv.visitInsn(I2F);
+                                               mv.visitMethodInsn(INVOKESTATIC, "java/lang/Float", "valueOf", "(F)Ljava/lang/Float;", false);
+                                       });
+                               } else if (type.equals(DOUBLE_OBJECT_TYPE)) {
+                                       return self.rval(type, Value::DoubleTo, (mv, o) -> {
+                                               mv.visitInsn(I2D);
+                                               mv.visitMethodInsn(INVOKESTATIC, "java/lang/Double", "valueOf", "(D)Ljava/lang/Double;", false);
+                                       });
+                               } else if (type.equals(INTEGER_OBJECT_TYPE) || type.equals(NUMBER_OBJECT_TYPE) || type.equals(OBJECT_OBJECT_TYPE)) {
+                                       return self.rval(type, Value::IntegerTo, (mv, o) -> {
+                                               mv.visitMethodInsn(INVOKESTATIC, "java/lang/Integer", "valueOf", "(I)Ljava/lang/Integer;", false);
+                                       });
+                               } else if (type.equals(LONG_OBJECT_TYPE)) {
+                                       return self.rval(type, Value::LongTo, (mv, o) -> {
+                                               mv.visitInsn(D2L);
+                                               mv.visitMethodInsn(INVOKESTATIC, "java/lang/Long", "valueOf", "(L)Ljava/lang/Long;", false);
+                                       });
+                               }
                        default:
-                               throw new IllegalArgumentException("expecting " + to + ": " + this);
+                               throw new UnsupportedOperationException();
                        }
                }
 
-               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);
+               static Value longTo(Value self, Type type) {
+                       switch (type.getSort()) {
+                       case Type.FLOAT:
+                               return self.rval(type, Value::floatTo, (mv, o) -> mv.visitInsn(L2F));
+                       case Type.DOUBLE:
+                               return self.rval(type, Value::doubleTo, (mv, o) -> mv.visitInsn(L2D));
+                       case Type.INT:
+                               return self.rval(type, Value::longTo, (mv, o) -> mv.visitInsn(L2I));
+                       case Type.LONG:
+                               return self;
+                       case Type.OBJECT:
+                               if (type.equals(STRING_TYPE)) {
+                                       return self.rval(type, Value::stringTo, (mv, o) -> mv.visitMethodInsn(INVOKESTATIC, "java/lang/Long", "toString", "(J)Ljava/lang/String;", false));
+                               } else if (type.equals(FLOAT_OBJECT_TYPE)) {
+                                       return self.rval(type, Value::FloatTo, (mv, o) -> {
+                                               mv.visitInsn(L2F);
+                                               mv.visitMethodInsn(INVOKESTATIC, "java/lang/Float", "valueOf", "(F)Ljava/lang/Float;", false);
+                                       });
+                               } else if (type.equals(DOUBLE_OBJECT_TYPE)) {
+                                       return self.rval(type, Value::DoubleTo, (mv, o) -> {
+                                               mv.visitInsn(L2D);
+                                               mv.visitMethodInsn(INVOKESTATIC, "java/lang/Double", "valueOf", "(D)Ljava/lang/Double;", false);
+                                       });
+                               } else if (type.equals(INTEGER_OBJECT_TYPE)) {
+                                       return self.rval(type, Value::IntegerTo, (mv, o) -> {
+                                               mv.visitInsn(L2I);
+                                               mv.visitMethodInsn(INVOKESTATIC, "java/lang/Integer", "valueOf", "(I)Ljava/lang/Integer;", false);
+                                       });
+                               } else if (type.equals(LONG_OBJECT_TYPE) || type.equals(NUMBER_OBJECT_TYPE) || type.equals(OBJECT_OBJECT_TYPE)) {
+                                       return self.rval(type, Value::LongTo, (mv, o) -> {
+                                               mv.visitMethodInsn(INVOKESTATIC, "java/lang/Long", "valueOf", "(J)Ljava/lang/Long;", false);
+                                       });
+                               }
+                       default:
+                               throw new UnsupportedOperationException();
                        }
                }
 
-               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);
-                       }
+               static Value booleanTo(Value self, Type type) {
+                       if (type.getSort() == Type.BOOLEAN)
+                               return self;
+                       else if (type.equals(Type.BOOLEAN_TYPE))
+                               return self.rval(type, Value::BooleanTo, (mv, o) -> mv.visitMethodInsn(INVOKESTATIC, "java/lang/Boolean", "valueOf", "(Z)Ljava/lang/Boolean;", false));
+                       else if (type.equals(STRING_TYPE))
+                               return self.rval(type, Value::stringTo, (mv, o) -> mv.visitMethodInsn(INVOKESTATIC, "java/lang/Boolean", "toString", "(Z)Ljava/lang/String;", false));
+                       else
+                               throw new UnsupportedOperationException();
                }
 
-               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");
+               static Value stringTo(Value self, Type type) {
+                       switch (type.getSort()) {
+                       case Type.FLOAT:
+                               return self.rval(type, Value::floatTo, (mv, o) -> mv.visitMethodInsn(INVOKESTATIC, "java/lang/Float", "parseFloat", "(Ljava/lang/String;)F", false));
+                       case Type.DOUBLE:
+                               return self.rval(type, Value::doubleTo, (mv, o) -> mv.visitMethodInsn(INVOKESTATIC, "java/lang/Float", "parseFloat", "(Ljava/lang/String;)F", false));
+                       case Type.INT:
+                               return self.rval(type, Value::intTo, (mv, o) -> mv.visitMethodInsn(INVOKESTATIC, "java/lang/Integer", "parseInt", "(Ljava/lang/String;)I", false));
+                       case Type.LONG:
+                               return self.rval(type, Value::longTo, (mv, o) -> mv.visitMethodInsn(INVOKESTATIC, "java/lang/Long", "parseLong", "(Ljava/lang/String;)J", false));
+                       case Type.BOOLEAN:
+                               return self.rval(type, Value::booleanTo, (mv, o) -> mv.visitMethodInsn(INVOKESTATIC, "java/lang/Boolean", "parseBoolean", "(Ljava/lang/String;)Z", false));
+                       case Type.OBJECT:
+                               if (type.equals(STRING_TYPE)) {
+                                       return self;
+                               } else if (type.equals(FLOAT_OBJECT_TYPE)) {
+                                       return self.rval(type, Value::FloatTo, (mv, o) -> {
+                                               mv.visitMethodInsn(INVOKESTATIC, "java/lang/Float", "valueOf", "(Ljava/lang/String;)Ljava/lang/Float;", false);
+                                       });
+                               } else if (type.equals(DOUBLE_OBJECT_TYPE)) {
+                                       return self.rval(type, Value::DoubleTo, (mv, o) -> {
+                                               mv.visitMethodInsn(INVOKESTATIC, "java/lang/Double", "valueOf", "(Ljava/lang/String;)Ljava/lang/Double;", false);
+                                       });
+                               } else if (type.equals(INTEGER_OBJECT_TYPE)) {
+                                       return self.rval(type, Value::IntegerTo, (mv, o) -> {
+                                               mv.visitMethodInsn(INVOKESTATIC, "java/lang/Integer", "valueOf", "(Ljava/lang/String;)Ljava/lang/Integer;", false);
+                                       });
+                               } else if (type.equals(LONG_OBJECT_TYPE)) {
+                                       return self.rval(type, Value::LongTo, (mv, o) -> {
+                                               mv.visitMethodInsn(INVOKESTATIC, "java/lang/Long", "valueOf", "(Ljava/lang/String;)Ljava/lang/Long;", false);
+                                       });
+                               } else if (type.equals(BOOLEAN_OBJECT_TYPE)) {
+                                       return self.rval(type, Value::BooleanTo, (mv, o) -> {
+                                               mv.visitMethodInsn(INVOKESTATIC, "java/lang/Boolean", "valueOf", "(Ljava/lang/String;)Ljava/lang/Boolean;", false);
+                                       });
+                               }
+                       default:
+                               throw new UnsupportedOperationException();
                        }
                }
 
-               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);
+               static Value NumberTo(Value self, Type type) {
+                       switch (type.getSort()) {
+                       case Type.FLOAT:
+                               return self.rval(type, Value::floatTo, (mv, o) -> mv.visitMethodInsn(INVOKEINTERFACE, "java/lang/Number", "floatValue", "()F", true));
+                       case Type.DOUBLE:
+                               return self.rval(type, Value::doubleTo, (mv, o) -> mv.visitMethodInsn(INVOKEINTERFACE, "java/lang/Number", "doubleValue", "()D", true));
+                       case Type.INT:
+                               return self.rval(type, Value::intTo, (mv, o) -> mv.visitMethodInsn(INVOKEINTERFACE, "java/lang/Number", "intValue", "()I", true));
+                       case Type.LONG:
+                               return self.rval(type, Value::longTo, (mv, o) -> mv.visitMethodInsn(INVOKEINTERFACE, "java/lang/Number", "longValue", "()J", true));
+                       case Type.OBJECT:
+                               if (type.equals(STRING_TYPE)) {
+                                       return self.rval(type, Value::stringTo, (mv, o) -> mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Object", "toString", "()Ljava/lang/String;", false));
+                               } else if (type.equals(FLOAT_OBJECT_TYPE)) {
+                                       return self.rval(type, Value::FloatTo, (mv, o) -> {
+                                               mv.visitMethodInsn(INVOKEINTERFACE, "java/lang/Number", "floatValue", "()F", true);
+                                               mv.visitMethodInsn(INVOKESTATIC, "java/lang/Float", "valueOf", "(Ljava/lang/String;)Ljava/lang/Float;", false);
+                                       });
+                               } else if (type.equals(DOUBLE_OBJECT_TYPE)) {
+                                       return self.rval(type, Value::DoubleTo, (mv, o) -> {
+                                               mv.visitMethodInsn(INVOKEINTERFACE, "java/lang/Number", "doubleValue", "()D", true);
+                                               mv.visitMethodInsn(INVOKESTATIC, "java/lang/Double", "valueOf", "(Ljava/lang/String;)Ljava/lang/Double;", false);
+                                       });
+                               } else if (type.equals(INTEGER_OBJECT_TYPE)) {
+                                       return self.rval(type, Value::IntegerTo, (mv, o) -> {
+                                               mv.visitMethodInsn(INVOKEINTERFACE, "java/lang/Number", "intValue", "()I", true);
+                                               mv.visitMethodInsn(INVOKESTATIC, "java/lang/Integer", "valueOf", "(Ljava/lang/String;)Ljava/lang/Integer;", false);
+                                       });
+                               } else if (type.equals(LONG_OBJECT_TYPE)) {
+                                       return self.rval(type, Value::LongTo, (mv, o) -> {
+                                               mv.visitMethodInsn(INVOKEINTERFACE, "java/lang/Number", "longValue", "()J", true);
+                                               mv.visitMethodInsn(INVOKESTATIC, "java/lang/Long", "valueOf", "(Ljava/lang/String;)Ljava/lang/Long;", false);
+                                       });
+                               }
+                       default:
+                               throw new UnsupportedOperationException();
                        }
                }
 
-               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");
-                       }
+               static Value FloatTo(Value self, Type type) {
+                       if (type.equals(FLOAT_OBJECT_TYPE))
+                               return self;
+                       else
+                               return NumberTo(self, type);
                }
 
-               Value promoteString() {
-                       if (type.equals(STRING_TYPE)) {
-                               return this;
-                       } else {
-                               switch (type.getSort()) {
-                               case Type.BOOLEAN:
-                                       return then(mv -> mv.visitMethodInsn(INVOKESTATIC, "java/lang/Boolean", "toString", "(Z)Ljava/lang/String;", false));
-                               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/Double", "toString", "(D)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));
-                               }
-                       }
+               static Value DoubleTo(Value self, Type type) {
+                       if (type.equals(DOUBLE_OBJECT_TYPE))
+                               return self;
+                       else
+                               return NumberTo(self, type);
+               }
+
+               static Value IntegerTo(Value self, Type type) {
+                       if (type.equals(INTEGER_OBJECT_TYPE))
+                               return self;
+                       else
+                               return NumberTo(self, type);
+               }
+
+               static Value LongTo(Value self, Type type) {
+                       if (type.equals(LONG_OBJECT_TYPE))
+                               return self;
+                       else
+                               return NumberTo(self, type);
+               }
+
+               static Value BooleanTo(Value self, Type type) {
+                       if (type.getSort() == Type.BOOLEAN)
+                               return self.rval(type, Value::booleanTo, (mv, o) -> mv.visitMethodInsn(INVOKEINTERFACE, "java/lang/Boolean", "booleanValue", "()Z", true));
+                       else if (type.equals(Type.BOOLEAN_TYPE))
+                               return self;
+                       else if (type.equals(STRING_TYPE))
+                               return self.rval(type, Value::stringTo, (mv, o) -> mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Object", "toString", "()Ljava/lang/String;", false));
+                       else
+                               throw new UnsupportedOperationException();
+               }
+
+               static public Value constant(float v) {
+                       return new Value(Type.FLOAT_TYPE, Value::floatTo, (mv, o) -> mv.visitLdcInsn(v));
+               }
+
+               static public Value constant(double v) {
+                       return new Value(Type.DOUBLE_TYPE, Value::doubleTo, (mv, o) -> mv.visitLdcInsn(v));
+               }
+
+               /*
+               static public Value constant(byte v) {
+                       return new Value(Type.BYTE_TYPE, Value::byteTo, (mv, o) -> mv.visitLdcInsn(v));
+               }
+
+               static public Value constant(short v) {
+                       return new Value(Type.SHORT_TYPE, Value::shortTo, (mv, o) -> mv.visitLdcInsn(v));
+               }
+                */
+               static public Value constant(int v) {
+                       return new Value(Type.INT_TYPE, Value::intTo, (mv, o) -> mv.visitLdcInsn(v));
+               }
+
+               static public Value constant(long v) {
+                       return new Value(Type.LONG_TYPE, Value::longTo, (mv, o) -> mv.visitLdcInsn(v));
+               }
+
+               static public Value constant(boolean v) {
+                       return new Value(Type.BOOLEAN_TYPE, Value::booleanTo, (mv, o) -> mv.visitLdcInsn(v));
+               }
+
+               static public Value constant(String v) {
+                       return new Value(STRING_TYPE, Value::stringTo, (mv, o) -> mv.visitLdcInsn(v));
+               }
+
+               /*
+               static public Value constant(Object v) {
+                       return new Value(Type.getType(v.getClass()), (mv, o) -> 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);
                }
        }
 
@@ -264,6 +502,10 @@ public class Generator implements ASTVisitor {
 
        LinkedList<Block> blocks = new LinkedList<>();
 
+       public Optional<Variable> findVariable(String name) {
+               return Optional.ofNullable(variables.get(name));
+       }
+
        public Variable addVariable(String name, Type type) {
                Variable var;
                if (type == Type.DOUBLE_TYPE || type == Type.LONG_TYPE) {
@@ -290,25 +532,38 @@ public class Generator implements ASTVisitor {
        public Generator(String file) {
                this.file = file;
 
-               imports.importClass(String.class, f -> false, m -> true);
-               imports.importClass(System.class, f -> f.getName().matches("out|error"), m -> false);
-               imports.importClass(PrintStream.class, f -> false, m -> m.getName().matches("^(print|append).*"));
-               imports.importClass(Math.class, f -> true, m -> true);
+               variables.put("this", new Variable("this", variableID++, Type.getType(Script.class)));
+
+               // testing constructors
+               for (Class<?> c: new Class[]{Boolean.class, Number.class, Integer.class, Long.class, Float.class, Double.class}) {
+                       imports.importClass(Double.class, false, f -> true, m -> true);
+               }
+
+               imports.importClass(List.class, false, f -> false, m -> true);
+               imports.importClass(ArrayList.class, false, f -> false, m -> true);
+
+               //
+               imports.importClass(Script.class, false, f -> true, m -> !m.getName().equals("run"));
+               imports.importClass(Object.class, false, f -> false, m -> m.getName().matches("toString|equals"));
+               imports.importClass(String.class, false, f -> false, m -> true);
+               imports.importClass(System.class, false, f -> f.getName().matches("out|error"), m -> false);
+               imports.importClass(PrintStream.class, true, f -> false, m -> m.getName().matches("^(print|append).*"));
+               imports.importClass(Math.class, false, f -> true, m -> true);
                List<Imports.Import> list = imports.importInstance[Imports.IMPORT_METHODS].getOrDefault(Type.getType(PrintStream.class), Map.of()).entrySet().stream()
                        .filter(e -> e.getKey().matches("^print.*"))
                        .flatMap(e -> e.getValue().stream())
                        .toList();
                for (Imports.Import im: list) {
-                       //System.out.println(im);
-                       imports.importStatic(new Imports.Import(im.recv(), im.name(), im.type(), (m, mv, params) -> {
-                               mv.visitFieldInsn(GETSTATIC, "java/lang/System", "out", m.recv().getDescriptor());
-                               // TODO: promote/cast arguments
-                               //System.out.println(m.name() + " INSERT params " + params);
-                               params.forEach(a -> a.insert().accept(mv));
-                               mv.visitMethodInsn(INVOKEVIRTUAL, m.recv().getInternalName(), m.name(), m.type().getDescriptor(), false);
-                       }));
+                       imports.importStatic(new Imports.Import(im.recv(), im.name(), im.type(),
+                               (m, ro, params) -> new Value(im.type().getReturnType(), (mv, o) -> {
+                                       mv.visitFieldInsn(GETSTATIC, "java/lang/System", "out", m.recv().getDescriptor());
+                                       // TODO: promote/cast arguments
+                                       //System.out.println(m.name() + " INSERT params " + params);
+                                       params.forEach(a -> a.rval().accept(mv));
+                                       mv.visitMethodInsn(INVOKEVIRTUAL, m.recv().getInternalName(), m.name(), m.type().getDescriptor(), false);
+                               })));
                }
-
+               //imports.dump();
        }
 
        @Override
@@ -321,8 +576,8 @@ public class Generator implements ASTVisitor {
                cw.visit(V1_8, ACC_PUBLIC,
                        "au/notzed/scripta/script",
                        null,
-                       "java/lang/Object",
-                       new String[]{"au/notzed/scripta/Script"});
+                       "au/notzed/scripta/Script",
+                       new String[]{});
 
                // TODO: work out debuginfo (jsr45)
                cw.visitSource(file, null);
@@ -335,14 +590,14 @@ public class Generator implements ASTVisitor {
 
                        mw.visitCode();
                        mw.visitVarInsn(ALOAD, 0);
-                       mw.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "<init>", "()V", false);
+                       mw.visitMethodInsn(INVOKESPECIAL, "au/notzed/scripta/Script", "<init>", "()V", false);
                        mw.visitInsn(RETURN);
                        mw.visitMaxs(1, 1);
                        mw.visitEnd();
                }
 
                mw = cw.visitMethod(ACC_PUBLIC,
-                       "eval", "()V",
+                       "run", "()V",
                        null, null);
 
                Label start = new Label();
@@ -369,7 +624,7 @@ public class Generator implements ASTVisitor {
 
                        blocks.push(new Block(s.label, loop, exit));
                        mw.visitLabel(loop);
-                       visitExpression(s.test).insert.accept(mw);
+                       visitExpression(s.test).rval.accept(mw);
                        s.then.ifPresentOrElse(r -> {
                                if (s.rest.isPresent()) {
                                        mw.visitJumpInsn(IFEQ, then);
@@ -409,7 +664,7 @@ public class Generator implements ASTVisitor {
                mw.visitLabel(test);
                Value testX = visitExpression(s.test);
 
-               testX.promoteBoolean().insert.accept(mw);
+               testX.promote(Type.BOOLEAN_TYPE).rval.accept(mw);
                mw.visitJumpInsn(IFNE, loop);
                mw.visitLabel(exit);
 
@@ -445,20 +700,34 @@ public class Generator implements ASTVisitor {
 
                        val = i.value.map(x -> visitExpression(x));
 
-                       if (s.type.type == AST.XType.OBJECT) {
-                               var = addVariable(i.name, Type.getObjectType(s.type.name.get().name()));
+                       switch (s.type.type) {
+                       case OBJECT:
+                               var = addVariable(i.name, Type.getObjectType(s.type.name.get().name().replace('.', '/')));
                                val.ifPresent(v -> {
-                                       v.insert.accept(mw);
+                                       v.rval.accept(mw);
                                        mw.visitVarInsn(ASTORE, var.id);
                                });
-                       } else {
+                               break;
+                       case ANY:
+                               if (val.isPresent()) {
+                                       var v = val.get();
+                                       System.out.println("any = " + v.type);
+                                       var = addVariable(i.name, v.type());
+                                       v.rval.accept(mw);
+                                       mw.visitVarInsn(v.type().getOpcode(ISTORE), var.id);
+                               } else {
+                                       throw new UnsupportedOperationException("var must have assignment");
+                               }
+                               break;
+                       default:
                                var = addVariable(i.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);
+                                       v.rval.accept(mw);
                                        mw.visitVarInsn(var.type().getOpcode(ISTORE), var.id);
                                });
+                               break;
                        }
                        variables.put(var.name, var);
                }
@@ -466,22 +735,22 @@ public class Generator implements ASTVisitor {
 
        @Override
        public void visitAssign(AST.SAssign s) {
-               //System.out.printf("%s%s = ", d, s.ref.name());
-
                Value var = visitExpression(s.ref);
-               //      Variable var = variables.get(s.ref.name());
                Value val = visitExpression(s.value);
 
+               System.out.print("** ");
+               System.out.print(var);
+               System.out.print(" = ");
+               System.out.println(val);
                val = val.promote(var.type);
-               val.insert.accept(mw);
-               //mw.visitVarInsn(var.type.getOpcode(ISTORE), var.id);
+               var.lval.accept(mw, val);
        }
 
        @Override
        public void visitCall(AST.SCall s) {
                Value val = visitExpression(s.call);
 
-               val.insert.accept(mw);
+               val.rval.accept(mw);
 
                System.out.printf("call result %s\n", val.type);
                switch (val.type.getSort()) {
@@ -533,7 +802,7 @@ public class Generator implements ASTVisitor {
                Optional<Value> res = s.res.map(r -> visitExpression(r));
 
                res.ifPresentOrElse(r -> {
-                       r.insert.accept(mw);
+                       r.rval.accept(mw);
                        mw.visitInsn(r.type.getOpcode(IRETURN));
                }, () -> {
                        mw.visitInsn(RETURN);
@@ -554,20 +823,21 @@ public class Generator implements ASTVisitor {
 
                switch (e.op) {
                case NEG:
-                       stack.push(a.then(mv -> mv.visitInsn(a.type.getOpcode(INEG))));
+                       stack.push(a.rval(a.type(), (mv, o) -> mv.visitInsn(a.type.getOpcode(INEG))));
                        break;
                case NOT:
-                       Value b = a.promoteBoolean();
-                       stack.push(new Value(Type.BOOLEAN_TYPE, mv -> {
-                               b.insert.accept(mv);
+                       Value b = a.promote(Type.BOOLEAN_TYPE);
+                       stack.push(new Value(Type.BOOLEAN_TYPE, Value::booleanTo, (mv, o) -> {
+                               b.rval.accept(mv);
                                mw.visitInsn(ICONST_1);
                                mw.visitInsn(IXOR);
                        }));
                        break;
                case INV:
-                       Value i = a.promoteInt();
-                       stack.push(new Value(Type.INT_TYPE, mv -> {
-                               i.insert.accept(mv);
+                       // What about long!
+                       Value i = a.promote(Type.INT_TYPE);
+                       stack.push(new Value(Type.INT_TYPE, (mv, o) -> {
+                               i.rval.accept(mv);
                                mw.visitInsn(ICONST_M1);
                                mw.visitInsn(IXOR);
                        }));
@@ -596,12 +866,12 @@ public class Generator implements ASTVisitor {
        };
 
        static Value compareFloat(Value c, Value d, int fcmp, int cmp) {
-               return new Value(Type.BOOLEAN_TYPE, mv -> {
+               return new Value(Type.BOOLEAN_TYPE, Value::booleanTo, (mv, o) -> {
                        Label t = new Label();
                        Label f = new Label();
 
-                       c.insert.accept(mv);
-                       d.insert.accept(mv);
+                       c.rval.accept(mv);
+                       d.rval.accept(mv);
                        mv.visitInsn(fcmp);
                        mv.visitJumpInsn(cmp, t);
 
@@ -614,12 +884,12 @@ public class Generator implements ASTVisitor {
        }
 
        static Value compareInt(Value c, Value d, int lcmp, int cmp) {
-               return new Value(Type.BOOLEAN_TYPE, mv -> {
+               return new Value(Type.BOOLEAN_TYPE, Value::booleanTo, (mv, o) -> {
                        Label t = new Label();
                        Label f = new Label();
 
-                       c.insert.accept(mv);
-                       d.insert.accept(mv);
+                       c.rval.accept(mv);
+                       d.rval.accept(mv);
                        if (lcmp != 0)
                                mv.visitInsn(lcmp);
                        mv.visitJumpInsn(cmp, t);
@@ -645,12 +915,14 @@ public class Generator implements ASTVisitor {
                Type type;
                int opcode;
 
+               System.out.printf("* %s %s %s\n", a, e.op, b);
+
                switch (e.op) {
                case ADD:
-                       if (a.type == STRING_TYPE || b.type == STRING_TYPE) {
-                               stack.addFirst(new Value(STRING_TYPE, mv -> {
-                                       a.promoteString().insert.accept(mv);
-                                       b.promoteString().insert.accept(mv);
+                       if (a.type.equals(STRING_TYPE) || b.type.equals(STRING_TYPE)) {
+                               stack.addFirst(new Value(STRING_TYPE, Value::stringTo, (mv, o) -> {
+                                       a.promote(STRING_TYPE).rval.accept(mv);
+                                       b.promote(STRING_TYPE).rval.accept(mv);
                                        mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/String", "concat", "(Ljava/lang/String;)Ljava/lang/String;", false);
                                }));
                                return;
@@ -658,51 +930,39 @@ 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 = a.promoteDouble();
-                               d = b.promoteDouble();
                        } else if (a.type == Type.FLOAT_TYPE || b.type == Type.FLOAT_TYPE) {
                                type = Type.FLOAT_TYPE;
-                               c = a.promoteFloat();
-                               d = b.promoteFloat();
                        } else if (a.type == Type.LONG_TYPE || b.type == Type.LONG_TYPE) {
                                type = Type.LONG_TYPE;
-                               c = a.promoteLong();
-                               d = b.promoteLong();
                        } else if (a.type == Type.INT_TYPE || b.type == Type.INT_TYPE) {
                                type = Type.INT_TYPE;
-                               c = a.promoteInt();
-                               d = b.promoteInt();
                        } else {
                                type = Type.FLOAT_TYPE;
-                               c = a.promoteFloat();
-                               d = b.promoteFloat();
                        }
+                       c = a.promote(type);
+                       d = b.promote(type);
                        opcode = type.getOpcode(intOp[e.op.ordinal()]);
                        System.out.printf("%s: %d -> %d\n", e.op, intOp[e.op.ordinal()], opcode);
-                       stack.addFirst(new Value(type, mv -> {
-                               c.insert.accept(mv);
-                               d.insert.accept(mv);
+                       stack.addFirst(new Value(type, (mv, o) -> {
+                               c.rval.accept(mv);
+                               d.rval.accept(mv);
                                mv.visitInsn(opcode);
                        }));
                        break;
                case LSR, LSL, ASR, AND, ORR, XOR:
                        if (a.type == Type.LONG_TYPE) {
                                type = a.type;
-                               c = a.promoteLong();
-                               d = b.promoteInt();
                        } else if (a.type == Type.INT_TYPE) {
                                type = a.type;
-                               c = a.promoteInt();
-                               d = b.promoteInt();
                        } else {
                                type = Type.LONG_TYPE;
-                               c = a.promoteLong();
-                               d = b.promoteInt();
                        }
+                       c = a.promote(type);
+                       d = b.promote(Type.INT_TYPE);
                        opcode = type.getOpcode(intOp[e.op.ordinal()]);
-                       stack.addFirst(new Value(type, mv -> {
-                               c.insert.accept(mv);
-                               d.insert.accept(mv);
+                       stack.addFirst(new Value(type, (mv, o) -> {
+                               c.rval.accept(mv);
+                               d.rval.accept(mv);
                                mv.visitInsn(opcode);
                        }));
                        break;
@@ -710,24 +970,25 @@ 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(a.promoteDouble(), b.promoteDouble(), cmpFloat[index] + 2, cmpOp[index]));
+                               stack.addFirst(compareFloat(a.promote(Type.DOUBLE_TYPE), b.promote(Type.DOUBLE_TYPE), cmpFloat[index] + 2, cmpOp[index]));
                        } else if (a.type == Type.FLOAT_TYPE || b.type == Type.FLOAT_TYPE) {
-                               stack.addFirst(compareFloat(a.promoteFloat(), b.promoteFloat(), cmpFloat[index], cmpOp[index]));
+                               stack.addFirst(compareFloat(a.promote(Type.FLOAT_TYPE), b.promote(Type.FLOAT_TYPE), cmpFloat[index], cmpOp[index]));
                        } else if (a.type == Type.LONG_TYPE || b.type == Type.LONG_TYPE) {
-                               stack.addFirst(compareInt(a.promoteLong(), b.promoteLong(), LCMP, cmpJump[index]));
+                               stack.addFirst(compareInt(a.promote(Type.LONG_TYPE), b.promote(Type.LONG_TYPE), LCMP, cmpJump[index]));
                        } else if (a.type == Type.INT_TYPE || b.type == Type.INT_TYPE) {
-                               stack.addFirst(compareInt(a.promoteInt(), b.promoteInt(), 0, cmpJump[index]));
+                               stack.addFirst(compareInt(a.promote(Type.INT_TYPE), b.promote(Type.INT_TYPE), 0, cmpJump[index]));
                        } else {
-                               stack.addFirst(compareInt(a.promoteInt(), b.promoteInt(), 0, cmpJump[index]));
+                               stack.addFirst(compareInt(a.promote(Type.INT_TYPE), b.promote(Type.INT_TYPE), 0, cmpJump[index]));
                        }
                        break;
                case AAND, OOR, XXOR:
-                       c = a.promoteBoolean();
-                       d = b.promoteBoolean();
+                       type = Type.BOOLEAN_TYPE;
+                       c = a.promote(type);
+                       d = b.promote(type);
                        opcode = boolOp[e.op.ordinal() - AST.XBinaryOp.AAND.ordinal()];
-                       stack.addFirst(new Value(Type.BOOLEAN_TYPE, mv -> {
-                               c.insert.accept(mv);
-                               d.insert.accept(mv);
+                       stack.addFirst(new Value(type, Value::booleanTo, (mv, o) -> {
+                               c.rval.accept(mv);
+                               d.rval.accept(mv);
 
                                mv.visitInsn(opcode);
 
@@ -736,28 +997,28 @@ public class Generator implements ASTVisitor {
                }
        }
 
+       @Override
+       public void visit(AST.XNew e) {
+               List<Value> args = e.args.stream().map(this::visitExpression).toList();
+               stack.push(imports.findConstructor(e.name, args).orElseThrow().value(null, args));
+       }
+
        @Override
        public void visit(AST.XCall e) {
                List<Value> args = e.args.stream().map(this::visitExpression).toList();
 
-               //imports.dump();
                if (e.ref.isPresent()) {
                        Value recv = visitExpression(e.ref.get());
 
-                       System.out.printf("receiver: %s\n", recv);
-                       Optional<Value> im = imports.findImport(recv.type(), e.name, args);
-
-                       if (im.isPresent()) {
-                               Value call = im.get();
-                               stack.push(new Value(call.type(), mv -> {
-                                       recv.insert.accept(mv);
-                                       call.insert.accept(mv);
-                               }));
+                       if (recv.type() != null) {
+                               System.out.printf("receiver: %s\n", recv);
+                               stack.push(imports.findImport(e.name, recv, args).orElseThrow().value(recv, args));
                        } else {
-                               throw new NoSuchElementException();
+                               Type type = Type.getType("L" + refname.toString().replace('.', '/') + ";");
+                               stack.push(imports.findImport(e.name, type, args).orElseThrow().value(null, args));
                        }
                } else {
-                       stack.push(imports.findImport(e.name, args).orElseThrow());
+                       stack.push(imports.findImport(e.name, args).orElseThrow().value(null, args));
                }
        }
 
@@ -781,57 +1042,78 @@ public class Generator implements ASTVisitor {
 
        @Override
        public void visit(AST.XField e) {
-               System.exit(2);
+               System.out.println("field: " + e.ref + " . " + e.field);
+               Value recv = visitExpression(e.ref);
+
+               // an incomplete reference, see if it resolved to anything yet
+               // TODO: long variable names?
+               if (recv.type() == null) {
+                       Type type = Type.getType("L" + refname.toString().replace('.', '/') + ";");
+
+                       System.out.println(" $ " + type);
+                       imports.findField(e.field, type)
+                               .ifPresentOrElse(f -> {
+                                       refname.setLength(0);
+                                       stack.push(f.value(recv, stack));
+                               }, () -> {
+                                       refname.append('.');
+                                       refname.append(e.field);
+                                       stack.push(new Value(null, null));
+                               });
+               } else {
+                       stack.push(imports.findField(e.field, recv).orElseThrow());
+               }
        }
 
        @Override
-       public void visit(AST.XReference e) {
-               Variable var = variables.get(e.name());
-
-               if (var != null) {
-                       stack.push(new Value(var.type, mv -> {
-                               mv.visitVarInsn(var.type.getOpcode(ILOAD), var.id);
-                       }));
-                       return;
+       public void visit(AST.XID e) {
+               Variable self = variables.get("this");
+               var val = findVariable(e.id).map(Variable::value)
+                       .or(() -> imports.findField(e.id, self.value()))
+                       .or(() -> imports.findField(e.id));
+
+               if (val.isPresent()) {
+                       stack.push(val.get());
+               } else {
+                       refname.setLength(0);
+                       refname.append(e.id);
+                       stack.push(new Value(null, null));
                }
-
-               stack.push(imports.findStaticField(e.name()).orElseThrow());
        }
 
        @Override
        public void visit(AST.XBool e) {
-               System.out.printf("%s", e.value);
                stack.push(Value.constant(e.value));
        }
 
        @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.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));
        }
 
        @Override
        public void visit(AST.XString e) {
-               System.out.printf("`%s`", e.value);
                stack.push(Value.constant(e.value));
        }
 
+       @Override
+       public void visit(AST.XNull e) {
+               stack.push(Value.NULL);
+       }
+
 }
index eccc21e..7db9e26 100644 (file)
@@ -16,6 +16,7 @@
  */
 package au.notzed.scripta;
 
+import java.lang.reflect.Constructor;
 import java.lang.reflect.Field;
 import java.lang.reflect.Method;
 import java.lang.reflect.Modifier;
@@ -29,13 +30,32 @@ import java.util.function.Function;
 import java.util.function.Predicate;
 import java.util.stream.IntStream;
 import java.util.stream.Stream;
-import org.objectweb.asm.MethodVisitor;
 import static org.objectweb.asm.Opcodes.*;
 import org.objectweb.asm.Type;
 
 /**
  * TODO: might need a Lookup
  * TODO: namespaces
+ * <p>
+ * method
+ * invoke args
+ * field
+ * get
+ * set val
+ * constant
+ * get
+ * variable
+ * get
+ * set val
+ * indexed?
+ * get index
+ * set index val
+ * <p>
+ * Value
+ * set(recv, val)
+ * get(recv)
+ * Call
+ * invoke(args)
  */
 public class Imports {
        /*
@@ -47,29 +67,16 @@ public class Imports {
 
        Some order of precedence needs to be enforced.  e.g. specificity rules.
         */
-       record Import(Type recv, String name, Type type, InsertFunction insert) {
-               // insert routine also needs arguments
-
-               public Generator.Value value() {
-                       return new Generator.Value(type.getSort() == Type.METHOD ? type.getReturnType() : type, mv -> {
-                               insert(mv, List.of());
-                       });
-               }
-
-               public Generator.Value value(List<Generator.Value> args) {
-                       return new Generator.Value(type.getSort() == Type.METHOD ? type.getReturnType() : type, mv -> {
-                               insert(mv, args);
-                       });
-               }
-
-               public void insert(MethodVisitor mw, List<Generator.Value> args) {
-                       insert.insert(this, mw, args);
+       record Import(Type recv, String name, Type type, InsertFunction value) {
+               public Generator.Value value(Generator.Value recv, List<Generator.Value> args) {
+                       return value.accept(this, recv, args);
                }
        }
 
        public interface InsertFunction {
-               void insert(Import im, MethodVisitor mv, List<Generator.Value> args);
+               Generator.Value accept(Import im, Generator.Value recv, List<Generator.Value> args);
        }
+
        // static?
        // fields?  methods?  constructors?
        static final int IMPORT_FIELDS = 0;
@@ -78,9 +85,10 @@ public class Imports {
        /**
         * Link types to their parents.
         */
-       Map<Type, Type> parentClass = new HashMap<>();
+       Map<Type, Type[]> superClass = new HashMap<>();
        Map<Type, Map<String, List<Import>>> importInstance[] = IntStream.range(0, 3).mapToObj(i -> new HashMap<>()).toArray(Map[]::new);
        Map<Type, Map<String, List<Import>>> importStatic[] = IntStream.range(0, 3).mapToObj(i -> new LinkedHashMap<>()).toArray(Map[]::new);
+       Map<String, List<Import>> importConstructors = new HashMap<>();
 
        public void importInstance(Import im) {
                //System.out.println("add new instance " + im);
@@ -106,87 +114,156 @@ public class Imports {
         *
         * @param itype
         */
-       public void importClass(Class<?> itype) {
-               importClass(itype, a -> true, b -> true);
+       public void importClass(Class<?> itype, boolean deep) {
+               importClass(itype, deep, a -> true, b -> true);
        }
 
-       // TODO: primitive constants are fully resolved at compile time in java, without the GETSTATIC
-       public void importClass(Class<?> itype, Predicate<Field> fields, Predicate<Method> methods) {
-               Type irecv = Type.getType(itype);
+       // TODO: final fields shouldn't have rvalue
+       public void importClass(Class<?> itype, boolean deep, Predicate<Field> fields, Predicate<Method> methods) {
+               Class<?> rtype = itype;
+               List<Type> superList = new ArrayList<>();
 
-               Class<?> ptype = itype;
-               while (ptype != Object.class) {
-                       Class<?> ntype = ptype.getSuperclass();
-                       parentClass.put(Type.getType(ptype), Type.getType(ntype));
-                       ptype = ntype;
-               }
+               superList.add(Type.getType(rtype));
+               while (itype != null) {
+                       if (itype == rtype || deep) {
+                               for (Field f: itype.getDeclaredFields()) {
+                                       if (!Modifier.isPublic(f.getModifiers()) || !fields.test(f))
+                                               continue;
 
-               for (Field f: itype.getFields()) {
-                       if (fields.test(f)) {
-                               Type recv = Type.getType(f.getDeclaringClass());
-                               Type type = Type.getType(f.getType());
-                               String name = f.getName();
-
-                               if (Modifier.isStatic(f.getModifiers())) {
-                                       if (type.getSort() == Type.OBJECT) {
-                                               importStatic(new Import(recv, name, type, (im, mv, args) -> {
-                                                       mv.visitFieldInsn(GETSTATIC, irecv.getInternalName(), im.name(), im.type().getDescriptor());
-                                               }));
-                                       } else {
-                                               try {
-                                                       Object value = f.get(null);
-                                                       importStatic(new Import(recv, name, type, (im, mv, args) -> {
-                                                               mv.visitLdcInsn(value);
-                                                       }));
-                                               } catch (IllegalArgumentException | IllegalAccessException ex) {
-                                                       System.out.println("field: " + f + ": " + ex.getMessage());
+                                       Type recv = Type.getType(f.getDeclaringClass());
+                                       Type type = Type.getType(f.getType());
+                                       String name = f.getName();
+
+                                       if (Modifier.isStatic(f.getModifiers())) {
+                                               if (type.getSort() == Type.OBJECT || !Modifier.isFinal(f.getModifiers())) {
+                                                       importStatic(new Import(recv, name, type,
+                                                               (im, ro, args) -> new Generator.Value(im.type(),
+                                                                       (mv, o) -> {
+                                                                               mv.visitFieldInsn(PUTSTATIC, im.recv.getInternalName(), im.name(), im.type().getDescriptor());
+                                                                       }, (mv, o) -> {
+                                                                               mv.visitFieldInsn(GETSTATIC, im.recv.getInternalName(), im.name(), im.type().getDescriptor());
+                                                                       })));
+                                               } else {
+                                                       try {
+                                                               Object value = f.get(null);
+                                                               importStatic(new Import(recv, name, type,
+                                                                       (im, ro, args) -> new Generator.Value(im.type(),
+                                                                               (mv, o) -> {
+                                                                                       mv.visitLdcInsn(value);
+                                                                               })));
+                                                       } catch (IllegalArgumentException | IllegalAccessException ex) {
+                                                               System.out.println("field: " + f + ": " + ex.getMessage());
+                                                       }
                                                }
+                                       } else {
+                                               // TODO: final primitives?
+                                               importInstance(new Import(recv, name, type,
+                                                       (im, ro, args) -> new Generator.Value(type,
+                                                               (mv, o) -> {
+                                                                       ro.rval().accept(mv);
+                                                                       o.rval().accept(mv);
+                                                                       mv.visitFieldInsn(PUTFIELD, im.recv.getInternalName(), im.name(), im.type().getDescriptor());
+                                                               },
+                                                               (mv, o) -> {
+                                                                       ro.rval().accept(mv);
+                                                                       mv.visitFieldInsn(GETFIELD, im.recv.getInternalName(), im.name(), im.type().getDescriptor());
+                                                               })));
                                        }
-                               } else {
-                                       importInstance(new Import(recv, name, type, (im, mv, args) -> {
-                                               mv.visitFieldInsn(GETFIELD, irecv.getInternalName(), im.name(), im.type().getDescriptor());
-                                       }));
                                }
-                       }
-               }
-               for (Method m: itype.getMethods()) {
-                       if (methods.test(m)) {
-                               Type recv = Type.getType(m.getDeclaringClass());
-                               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) -> {
-                                               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) -> {
-                                               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);
-                                       }));
+                               for (Method m: itype.getDeclaredMethods()) {
+                                       if (!Modifier.isPublic(m.getModifiers()) || !methods.test(m))
+                                               continue;
+
+                                       Type recv = Type.getType(m.getDeclaringClass());
+                                       Type type = Type.getType(m);
+                                       String name = m.getName();
+
+                                       // TODO: interface
+                                       if (Modifier.isStatic(m.getModifiers())) {
+                                               importStatic(new Import(recv, name, type,
+                                                       (im, ro, args) -> new Generator.Value(im.type.getReturnType(),
+                                                               (mv, o) -> {
+                                                                       Type[] argb = im.type.getArgumentTypes();
+                                                                       for (int i = 0; i < argb.length; i++) {
+                                                                               args.get(i).promote(argb[i]).rval().accept(mv);
+                                                                       }
+                                                                       mv.visitMethodInsn(INVOKESTATIC, im.recv.getInternalName(), im.name, im.type.getDescriptor(), false);
+                                                               })));
+                                       } else {
+                                               boolean isInterface = itype.isInterface();
+                                               importInstance(new Import(recv, name, type,
+                                                       (im, ro, args) -> new Generator.Value(type.getReturnType(),
+                                                               (mv, o) -> {
+                                                                       Type[] argb = im.type.getArgumentTypes();
+                                                                       ro.rval().accept(mv, ro);
+                                                                       for (int i = 0; i < argb.length; i++) {
+                                                                               args.get(i).promote(argb[i]).rval().accept(mv);
+                                                                       }
+                                                                       mv.visitMethodInsn(isInterface ? INVOKEINTERFACE : INVOKEVIRTUAL, im.recv.getInternalName(), im.name, im.type.getDescriptor(), isInterface);
+                                                               })));
+                                       }
+                               }
+                               if (!Modifier.isAbstract(itype.getModifiers())) {
+                                       for (Constructor<?> c: itype.getDeclaredConstructors()) {
+                                               if (!Modifier.isPublic(c.getModifiers()))
+                                                       continue;
+                                               String name = c.getName();
+                                               Type recv = Type.getType(c.getDeclaringClass());
+                                               Type type = Type.getType(c);
+
+                                               importConstructors.computeIfAbsent(name, k -> new ArrayList<>())
+                                                       .add(new Import(recv, name, type,
+                                                               (im, ro, args) -> new Generator.Value(im.recv,
+                                                                       (mv, o) -> {
+                                                                               Type[] argb = im.type.getArgumentTypes();
+
+                                                                               System.out.println("** " + im.type.getInternalName());
+                                                                               mv.visitTypeInsn(NEW, im.name().replace('.', '/'));
+                                                                               mv.visitInsn(DUP);
+                                                                               for (int i = 0; i < argb.length; i++) {
+                                                                                       args.get(i).promote(argb[i]).rval().accept(mv);
+                                                                               }
+                                                                               mv.visitMethodInsn(INVOKESPECIAL, im.recv.getInternalName(), "<init>", im.type.getDescriptor(), false);
+                                                                       })));
+                                       }
                                }
                        }
+
+                       Class<?> ptype = itype.getSuperclass();
+                       if (ptype != null) {
+                               superList.add(Type.getType(ptype));
+                       }
+                       itype = ptype;
                }
+
+               superClass.put(superList.get(0), superList.toArray(Type[]::new));
        }
 
        static Type[] toArgumentTypes(List<Generator.Value> arga) {
                return arga.stream().map(Generator.Value::type).toArray(Type[]::new);
        }
 
-       public Optional<Generator.Value> findStaticField(String name) {
+       public Optional<Generator.Value> findField(String name) {
                return importStatic[IMPORT_FIELDS].values().stream()
                        .flatMap(m -> m.getOrDefault(name, List.of()).stream())
                        .findFirst()
-                       .map(m -> m.value());
+                       .map(im -> im.value(null, null));
+       }
+
+       public Optional<Generator.Value> findField(String name, Generator.Value recv) {
+               return Stream.of(superClass.getOrDefault(recv.type(), new Type[0]))
+                       .map(m -> importInstance[IMPORT_FIELDS].getOrDefault(m, Map.of()))
+                       .flatMap(m -> m.getOrDefault(name, List.of()).stream())
+                       .findFirst()
+                       .map(im -> im.value(recv, null));
+       }
+
+       // find a static field
+       public Optional<Import> findField(String name, Type recv) {
+               return Stream.of(superClass.getOrDefault(recv, new Type[0]))
+                       .flatMap(m -> Stream.ofNullable(importStatic[IMPORT_FIELDS].get(m)))
+                       .flatMap(m -> m.getOrDefault(name, List.of()).stream())
+                       .findFirst();
        }
 
        record Match(Import im, int score) implements Comparable<Match> {
@@ -202,10 +279,9 @@ public class Imports {
                        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)),
-                       //      String.join(", ", Stream.of(argb).map(Object::toString).toArray(String[]::new)));
-
+                       System.out.printf("match arguments:\n %s\n %s\n",
+                               String.join(", ", Stream.of(arga).map(Object::toString).toArray(String[]::new)),
+                               String.join(", ", Stream.of(argb).map(Object::toString).toArray(String[]::new)));
                        if (arga.length == argb.length) {
                                for (int i = 0; i < arga.length; i++) {
                                        Type a = arga[i], b = argb[i];
@@ -222,19 +298,24 @@ public class Imports {
                                        } else if (b.equals(Generator.STRING_TYPE)) {
                                                // anything can be converted to string
                                                score += 6;
+                                       } else if (b.equals(Type.getType(Object.class))) {
+                                               // anything can be converted to object
+                                               score += 8;
                                        } else {
-                                               //System.out.printf(" -> no match\n");
+                                               System.out.printf(" -> no match\n");
                                                return new Match(im, -1);
                                        }
                                }
+                               if (arga.length == 0)
+                                       score = 10;
                        }
-                       //System.out.printf(" -> score %d\n", score);
+                       System.out.printf(" -> score %d\n", score);
                        return new Match(im, score);
                };
        }
 
        // Find a static method.
-       public Optional<Generator.Value> findImport(String name, List<Generator.Value> args) {
+       public Optional<Import> findImport(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()));
@@ -257,34 +338,39 @@ public class Imports {
                                System.out.println("> " + m);
                                return m;
                        })
-                       .map(m -> m.im.value(args));
+                       .map(m -> m.im);
        }
 
-       // find non-static method
-       // ?? may have to search fields
-       public Optional<Generator.Value> findImport(Type recv, String name, List<Generator.Value> args) {
+       public Optional<Import> findConstructor(String name, List<Generator.Value> args) {
+               Type[] arga = toArgumentTypes(args);
+               System.out.printf("find new: %s", name);
+               args.stream().forEach(a -> System.out.printf(" %s", a.type()));
+               System.out.println();
+
+               return importConstructors.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);
+       }
+
+       public Optional<Import> findImport(String name, Type recv, 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: importInstance[IMPORT_METHODS].entrySet()) {
-       //              System.out.println(map.getKey());
-       //              for (var im: map.getValue().getOrDefault(name, List.of())) {
-       //                      System.out.printf(" ? %s\n", im);
-       //              }
-       //      }
-
-               var table = importInstance[IMPORT_METHODS];
-               List<Type> list = new ArrayList<>();
-               Type ptype = recv;
-               do {
-                       list.add(ptype);
-                       ptype = parentClass.get(ptype);
-               } while (ptype != null);
-
-               return list.stream()
-                       .peek(m-> System.out.println("c " + m))
-                       .map(m -> table.getOrDefault(m, Map.of()))
+
+               return Stream.of(superClass.getOrDefault(recv, new Type[0]))
+                       .flatMap(m
+                               -> Stream.concat(
+                               Stream.ofNullable(importInstance[IMPORT_METHODS].get(m)),
+                               Stream.ofNullable(importStatic[IMPORT_METHODS].get(m))))
                        .flatMap(m -> m.getOrDefault(name, List.of()).stream())
                        .peek(m -> System.out.println("? " + m))
                        .map(match(arga))
@@ -295,9 +381,21 @@ public class Imports {
                                System.out.println("> " + m);
                                return m;
                        })
-                       .map(m -> m.im.value(args));
-               /*
-               return importInstance[IMPORT_METHODS].values().stream()
+                       .map(m -> m.im);
+       }
+
+       // find non-static method
+       // ?? may have to search fields
+       // TODO: should just take recv and arg *types*
+       public Optional<Import> findImport(String name, Generator.Value recv, 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();
+
+               return Stream.of(superClass.getOrDefault(recv.type(), new Type[0]))
+                       .peek(m -> System.out.println("c " + m))
+                       .map(m -> importInstance[IMPORT_METHODS].getOrDefault(m, Map.of()))
                        .flatMap(m -> m.getOrDefault(name, List.of()).stream())
                        .peek(m -> System.out.println("? " + m))
                        .map(match(arga))
@@ -308,11 +406,16 @@ public class Imports {
                                System.out.println("> " + m);
                                return m;
                        })
-                       .map(m -> m.im.value(args));
-*/
+                       .map(m -> m.im);
        }
 
        void dump() {
+               for (var rmap: importConstructors.entrySet()) {
+                       for (var im: rmap.getValue()) {
+                               System.out.printf(" new %s\n", im);
+                       }
+               }
+
                for (var rmap: importInstance) {
                        for (var rme: rmap.entrySet()) {
                                for (var vme: rme.getValue().entrySet()) {
index 56258a4..bf41efd 100644 (file)
  */
 package au.notzed.scripta;
 
-public interface Script {
+public abstract class Script implements Runnable {
 
-       public void eval();
+       public String name;
+       public String[] list = new String[1];
+
+       @Override
+       public abstract void run();
 }
index d6735f6..13a345a 100644 (file)
@@ -34,6 +34,7 @@ statement
 valueExpr
        : '(' valueExpr ')'                                                     # valueGroupExpr
        | func=funcExpr                                                         # valueFunctionExpr
+//     | left=valueExpr '[' index=valueExpr ']'                                # valueArray
        | left=valueExpr '.' ID args=argsExpr                                   # valueCallExpr
        | ID args=argsExpr                                                      # valueCallExpr
        | NEW ref=reference args=argsExpr                                       # valueNewExpr
@@ -46,7 +47,7 @@ valueExpr
        | left=valueExpr op=(LT|LE|GT|GE|EQ|NE) right=valueExpr                 # valueBinaryExpr
        | left=valueExpr op=('&&'|'||'|'^^') right=valueExpr                    # valueBinaryExpr
        | lit=literal                                                           # valueLiteralExpr
-       | ref=reference                                                         # valueReferenceExpr
+       | id=ID # valueIDExpr
        ;
 
 funcExpr       : FUNC rval=type '(' paramExpr? ')' '{' statements? '}';
@@ -83,13 +84,15 @@ STR         : 'string';
 BLN            : 'boolean';
 DBL            : 'double';
 LNG            : 'long';
+VAR            : 'var';
 
-type           : INT | LNG | FLT | DBL | STR | BLN | reference;
-literal                : INTEGER | FLOAT | STRING | TRUE | FALSE;
+type           : INT | LNG | FLT | DBL | STR | BLN | VAR | reference;
+literal                : INTEGER | FLOAT | STRING | TRUE | FALSE | NULL;
 reference      : ID ('.' ID)*;
 
 TRUE           : 'true';
 FALSE          : 'false';
+NULL           : 'null';
 
 LT             : '<';
 LE             : '<=';