From: Not Zed Date: Mon, 17 Apr 2023 08:56:40 +0000 (+0930) Subject: Checkpoint ongoing work. X-Git-Url: https://code.zedzone.au/cvs?a=commitdiff_plain;h=27af7aa9ef51198f4bd9d15e96a96cc5d041e2d9;p=compilerz Checkpoint ongoing work. - 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 --- diff --git a/src/notzed.scripta/classes/au/notzed/scripta/AST.java b/src/notzed.scripta/classes/au/notzed/scripta/AST.java index 8c07d50..e3af07a 100644 --- a/src/notzed.scripta/classes/au/notzed/scripta/AST.java +++ b/src/notzed.scripta/classes/au/notzed/scripta/AST.java @@ -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 args; + + public XNew(int lineNo, String name, List 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 ref; String name; List args; - public XCall(int lineNo, boolean constructor, Optional ref, String name, List args) { + public XCall(int lineNo, Optional ref, String name, List 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); + } + } + } diff --git a/src/notzed.scripta/classes/au/notzed/scripta/ASTBuilder.java b/src/notzed.scripta/classes/au/notzed/scripta/ASTBuilder.java index 41590cc..7229dd6 100644 --- a/src/notzed.scripta/classes/au/notzed/scripta/ASTBuilder.java +++ b/src/notzed.scripta/classes/au/notzed/scripta/ASTBuilder.java @@ -96,6 +96,8 @@ public class ASTBuilder extends ScriptABaseVisitor { 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 { 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 { 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)), - "", + 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 { 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)); diff --git a/src/notzed.scripta/classes/au/notzed/scripta/ASTPrinter.java b/src/notzed.scripta/classes/au/notzed/scripta/ASTPrinter.java index aa13f6c..6740301 100644 --- a/src/notzed.scripta/classes/au/notzed/scripta/ASTPrinter.java +++ b/src/notzed.scripta/classes/au/notzed/scripta/ASTPrinter.java @@ -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 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("<"); diff --git a/src/notzed.scripta/classes/au/notzed/scripta/ASTVisitor.java b/src/notzed.scripta/classes/au/notzed/scripta/ASTVisitor.java index b097da1..f2afee8 100644 --- a/src/notzed.scripta/classes/au/notzed/scripta/ASTVisitor.java +++ b/src/notzed.scripta/classes/au/notzed/scripta/ASTVisitor.java @@ -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); + } + } diff --git a/src/notzed.scripta/classes/au/notzed/scripta/Compiler.java b/src/notzed.scripta/classes/au/notzed/scripta/Compiler.java index 1c7e6bc..f83f06c 100644 --- a/src/notzed.scripta/classes/au/notzed/scripta/Compiler.java +++ b/src/notzed.scripta/classes/au/notzed/scripta/Compiler.java @@ -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) { diff --git a/src/notzed.scripta/classes/au/notzed/scripta/Generator.java b/src/notzed.scripta/classes/au/notzed/scripta/Generator.java index 0201287..92b1cca 100644 --- a/src/notzed.scripta/classes/au/notzed/scripta/Generator.java +++ b/src/notzed.scripta/classes/au/notzed/scripta/Generator.java @@ -17,14 +17,14 @@ 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 variables = new HashMap<>(); LinkedList 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 insert) { - public Value then(Consumer 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 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 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 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 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 blocks = new LinkedList<>(); + public Optional 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 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", "", "()V", false); + mw.visitMethodInsn(INVOKESPECIAL, "au/notzed/scripta/Script", "", "()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 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 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 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 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); + } + } diff --git a/src/notzed.scripta/classes/au/notzed/scripta/Imports.java b/src/notzed.scripta/classes/au/notzed/scripta/Imports.java index eccc21e..7db9e26 100644 --- a/src/notzed.scripta/classes/au/notzed/scripta/Imports.java +++ b/src/notzed.scripta/classes/au/notzed/scripta/Imports.java @@ -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 + *

+ * method + * invoke args + * field + * get + * set val + * constant + * get + * variable + * get + * set val + * indexed? + * get index + * set index val + *

+ * 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 args) { - return new Generator.Value(type.getSort() == Type.METHOD ? type.getReturnType() : type, mv -> { - insert(mv, args); - }); - } - - public void insert(MethodVisitor mw, List args) { - insert.insert(this, mw, args); + record Import(Type recv, String name, Type type, InsertFunction value) { + public Generator.Value value(Generator.Value recv, List args) { + return value.accept(this, recv, args); } } public interface InsertFunction { - void insert(Import im, MethodVisitor mv, List args); + Generator.Value accept(Import im, Generator.Value recv, List args); } + // static? // fields? methods? constructors? static final int IMPORT_FIELDS = 0; @@ -78,9 +85,10 @@ public class Imports { /** * Link types to their parents. */ - Map parentClass = new HashMap<>(); + Map superClass = new HashMap<>(); Map>> importInstance[] = IntStream.range(0, 3).mapToObj(i -> new HashMap<>()).toArray(Map[]::new); Map>> importStatic[] = IntStream.range(0, 3).mapToObj(i -> new LinkedHashMap<>()).toArray(Map[]::new); + Map> 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 fields, Predicate methods) { - Type irecv = Type.getType(itype); + // TODO: final fields shouldn't have rvalue + public void importClass(Class itype, boolean deep, Predicate fields, Predicate methods) { + Class rtype = itype; + List 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(), "", 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 arga) { return arga.stream().map(Generator.Value::type).toArray(Type[]::new); } - public Optional findStaticField(String name) { + public Optional 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 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 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 { @@ -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 findImport(String name, List args) { + public Optional findImport(String name, List args) { Type[] arga = toArgumentTypes(args); System.out.printf("find method: %s", name); args.stream().forEach(a -> System.out.printf(" %s", a.type())); @@ -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 findImport(Type recv, String name, List args) { + public Optional findConstructor(String name, List 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 findImport(String name, Type recv, List args) { Type[] arga = toArgumentTypes(args); System.out.printf("find method: %s", name); args.stream().forEach(a -> System.out.printf(" %s", a.type())); System.out.println(); - // for (var map: 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 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 findImport(String name, Generator.Value recv, List args) { + Type[] arga = toArgumentTypes(args); + System.out.printf("find method: %s", name); + args.stream().forEach(a -> System.out.printf(" %s", a.type())); + System.out.println(); + + 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()) { diff --git a/src/notzed.scripta/classes/au/notzed/scripta/Script.java b/src/notzed.scripta/classes/au/notzed/scripta/Script.java index 56258a4..bf41efd 100644 --- a/src/notzed.scripta/classes/au/notzed/scripta/Script.java +++ b/src/notzed.scripta/classes/au/notzed/scripta/Script.java @@ -16,7 +16,11 @@ */ 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(); } diff --git a/src/notzed.scripta/gen/ScriptA.g4 b/src/notzed.scripta/gen/ScriptA.g4 index d6735f6..13a345a 100644 --- a/src/notzed.scripta/gen/ScriptA.g4 +++ b/src/notzed.scripta/gen/ScriptA.g4 @@ -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 : '<=';