From d6d6e7a45e798bebd91ec8e2dded5dd9b50be432 Mon Sep 17 00:00:00 2001 From: lunarcleint <88809950+lunarcleint@users.noreply.github.com> Date: Sun, 28 Apr 2024 18:11:43 -0700 Subject: [PATCH] we spent like 5 hours Co-authored-by: Ne_Eo --- hscript/CustomClassHandler.hx | 6 +- hscript/Expr.hx | 204 ++++++++++++++- hscript/Interp.hx | 450 +++++++++++++++++++++------------- hscript/Optimizer.hx | 63 ++--- hscript/Parser.hx | 58 +++-- hscript/Postprocessor.hx | 116 +++++++++ hscript/Preprocessor.hx | 105 -------- hscript/Printer.hx | 91 ++++++- hscript/Tools.hx | 219 ++++++++++++----- tests/src/Util.hx | 1 + tests/src/tests/FinalCase.hx | 3 +- 11 files changed, 891 insertions(+), 425 deletions(-) create mode 100644 hscript/Postprocessor.hx diff --git a/hscript/CustomClassHandler.hx b/hscript/CustomClassHandler.hx index 118a43d..7843598 100644 --- a/hscript/CustomClassHandler.hx +++ b/hscript/CustomClassHandler.hx @@ -29,6 +29,7 @@ class CustomClassHandler implements IHScriptCustomConstructor { } public function hnew(args:Array):Dynamic { + /* var interp = new Interp(); interp.errorHandler = ogInterp.errorHandler; @@ -78,8 +79,9 @@ class CustomClassHandler implements IHScriptCustomConstructor { if(newFunc != null) { UnsafeReflect.callMethodUnsafe(null, newFunc, args); } - - return _class; + */ + throw "todo: do all this lmao -lunar"; + return null; } public function toString():String { diff --git a/hscript/Expr.hx b/hscript/Expr.hx index 45ca168..3fd2e84 100644 --- a/hscript/Expr.hx +++ b/hscript/Expr.hx @@ -62,28 +62,28 @@ enum Expr #end { EConst( c : Const ); - EIdent( v : String , ?vid : Int); - EVar( n : String, ?t : CType, ?e : Expr, ?isPublic : Bool, ?isStatic : Bool , ?vid : Int); + EIdent( v : VarN ); + EVar( n : VarN, ?t : CType, ?e : Expr, ?isPublic : Bool, ?isStatic : Bool ); EParent( e : Expr, ?noOptimize : Bool ); EBlock( e : Array ); EField( e : Expr, f : String , ?safe : Bool ); - EBinop( op : String, e1 : Expr, e2 : Expr ); - EUnop( op : String, prefix : Bool, e : Expr ); + EBinop( op : Binop, e1 : Expr, e2 : Expr ); + EUnop( op : Unop, prefix : Bool, e : Expr ); ECall( e : Expr, params : Array ); EIf( cond : Expr, e1 : Expr, ?e2 : Expr ); EWhile( cond : Expr, e : Expr ); - EFor( v : String, it : Expr, e : Expr); - EForKeyValue( v : String, it : Expr, e : Expr, ithv: String); + EFor( v : VarN, it : Expr, e : Expr); + EForKeyValue( v : VarN, it : Expr, e : Expr, ithv: VarN); EBreak; EContinue; - EFunction( args : Array, e : Expr, ?name : String, ?ret : CType, ?isPublic : Bool, ?isStatic : Bool, ?isOverride : Bool , ?fid : Int); + EFunction( args : Array, e : Expr, ?name : VarN, ?ret : CType, ?isPublic : Bool, ?isStatic : Bool, ?isOverride : Bool ); EReturn( ?e : Expr ); EArray( e : Expr, index : Expr ); EMapDecl( type: MapType, keys: Array, values: Array ); EArrayDecl( e : Array ); - ENew( cl : String, params : Array , ?cid : Int); + ENew( cl : VarN, params : Array ); EThrow( e : Expr ); - ETry( e : Expr, v : String, t : Null, ecatch : Expr ); + ETry( e : Expr, v : VarN, t : Null, ecatch : Expr ); EObject( fl : Array ); ETernary( cond : Expr, e1 : Expr, e2 : Expr ); ESwitch( e : Expr, cases : Array, ?defaultExpr : Expr ); @@ -92,12 +92,182 @@ enum Expr ECheckType( e : Expr, t : CType ); EImport( c : String, mode: KImportMode ); - EClass( name:String, fields:Array, ?extend:String, interfaces:Array , ?cid : Int ); + EClass( name:VarN, fields:Array, ?extend:String, interfaces:Array ); + + EInfo( info: InfoClass, e: Expr ); +} + +enum Binop { + /** + `+` + **/ + OpAdd; + + /** + `*` + **/ + OpMult; + + /** + `/` + **/ + OpDiv; + + /** + `-` + **/ + OpSub; + + /** + `=` + **/ + OpAssign; + + /** + `==` + **/ + OpEq; + + /** + `!=` + **/ + OpNotEq; + + /** + `>` + **/ + OpGt; + + /** + `>=` + **/ + OpGte; + + /** + `<` + **/ + OpLt; + + /** + `<=` + **/ + OpLte; + + /** + `&` + **/ + OpAnd; + + /** + `|` + **/ + OpOr; + + /** + `^` + **/ + OpXor; + + /** + `&&` + **/ + OpBoolAnd; + + /** + `||` + **/ + OpBoolOr; + + /** + `<<` + **/ + OpShl; + + /** + `>>` + **/ + OpShr; + + /** + `>>>` + **/ + OpUShr; + + /** + `%` + **/ + OpMod; + + /** + `+=` `-=` `/=` `*=` `<<=` `>>=` `>>>=` `|=` `&=` `^=` `%=` + **/ + OpAssignOp(op:Binop); + + /** + `...` + **/ + OpInterval; + + /** + `=>` + **/ + OpArrow; + + /** + `is` + **/ + OpIs; // used to be OpIn, but our system treats that differently + + /** + `??` + **/ + OpNullCoal; +} + +enum abstract Unop(Int) { + /** + `++` + **/ + var OpIncrement; + + /** + `--` + **/ + var OpDecrement; + + /** + `!` + **/ + var OpNot; + + /** + `-` + **/ + var OpNeg; + + /** + `~` + **/ + var OpNegBits; + + /** + `...` + **/ + var OpSpread; +} + +class InfoClass { + public var variables:Array; + + public function new(variables:Array) { + this.variables = variables; + } } enum KImportMode { INormal; IAs( name : String ); + IAsReturn; IAll; } @@ -121,10 +291,9 @@ class SwitchCase { } } - //typedef Argument = { name : String, ?t : CType, ?opt : Bool, ?value : Expr }; class Argument { - public var name : String; + public var name : VarN; public var t : Null; public var opt : Bool; public var value : Null; @@ -134,6 +303,10 @@ class Argument { this.opt = opt; this.value = value; } + + public function toString() { + return (opt ? "?" : "") + name + (t != null ? ":" + Printer.convertTypeToString(t) : "") + (value != null ? "=" + Printer.convertExprToString(value) : ""); + } } typedef Metadata = Array<{ name : String, params : Array }>; @@ -205,3 +378,10 @@ typedef VarDecl = { var expr : Null; var type : Null; } + +abstract VarN(Dynamic) from Int from String to Int to String {} +// abstract VarN(Int) from Int to Int {} +//enum VarN { +// VInt(v:Int); +// VStr(v:String); +//} \ No newline at end of file diff --git a/hscript/Interp.hx b/hscript/Interp.hx index fa32b4c..24baf5a 100644 --- a/hscript/Interp.hx +++ b/hscript/Interp.hx @@ -60,7 +60,7 @@ class DeclaredVar { @:structInit class RedeclaredVar { - public var n:String; + public var n:VarN; public var old:DeclaredVar; public var depth:Int; } @@ -96,9 +96,6 @@ class Interp { public var _variablesNames:Array = []; public var _variables:Array = []; - public var _publicVariables:Array = []; - public var _staticVariables:Array = []; - public var _customClasses:Array = []; public var _locals:Array = []; // !BACKWARDS COMPAT @@ -107,17 +104,15 @@ class Interp { public var publicVariables:HScriptVariables; public var staticVariables:HScriptVariables; - #if haxe3 - public var locals:Map; - var binops:MapExpr->Dynamic>; - #else - public var locals:Hash; - var binops:HashExpr->Dynamic>; - #end + //#if haxe3 + //var binops:MapExpr->Dynamic>; + //#else + //var binops:HashExpr->Dynamic>; + //#end var depth:Int = 0; var inTry:Bool; - var declared:Array; + var declared:Array = []; var returnValue:Dynamic; var isBypassAccessor:Bool = false; @@ -137,21 +132,18 @@ class Interp { #end public function new() { - declared = new Array(); resetVariables(); - initOps(); + //initOps(); } private function resetVariables() { _variablesNames = []; - _variables = []; - _customClasses = []; - _locals = []; + loadTables(0); staticVariables = publicVariables = customClasses = variables = new HScriptVariables(this); } - private function setDefualtVariables() { + private function setDefaultVariables() { variables.set("null", null); variables.set("true", true); variables.set("false", false); @@ -180,7 +172,7 @@ class Interp { return cast {fileName: "hscript", lineNumber: 0}; } - function initOps() { + /*function initOps() { var me = this; #if haxe3 binops = new Map(); @@ -230,66 +222,141 @@ class Interp { assignOp(">>=", function(v1, v2) return v1 >> v2); assignOp(">>>=", function(v1, v2) return v1 >>> v2); assignOp("??"+"=", function(v1, v2) return v1 == null ? v2 : v1); + }*/ + + function runBinop(op:Binop, e1:Expr, e2:Expr):Dynamic { + return switch (op) { + case OpAdd: expr(e1) + expr(e2); + case OpSub: expr(e1) - expr(e2); + case OpMult: expr(e1) * expr(e2); + case OpDiv: expr(e1) / expr(e2); + case OpMod: expr(e1) % expr(e2); + case OpAnd: expr(e1) & expr(e2); + case OpOr: expr(e1) | expr(e2); + case OpXor: expr(e1) ^ expr(e2); + case OpShl: expr(e1) << expr(e2); + case OpShr: expr(e1) >> expr(e2); + case OpUShr: expr(e1) >>> expr(e2); + case OpEq: expr(e1) == expr(e2); + case OpNotEq: expr(e1) != expr(e2); + case OpGt: expr(e1) > expr(e2); + case OpGte: expr(e1) >= expr(e2); + case OpLt: expr(e1) < expr(e2); + case OpLte: expr(e1) <= expr(e2); + case OpBoolAnd: expr(e1) == true && expr(e2) == true; + case OpBoolOr: expr(e1) == true || expr(e2) == true; + //case OpIs: false; + case OpIs: checkIsType(e1, e2); + case OpInterval: { + new #if (haxe_211 || haxe3) IntIterator #else IntIter #end(expr(e1), expr(e2)); + } + //case OpIn: false; + case OpNullCoal: { + var e1 = expr(e1); + if (e1 == null) expr(e2) else e1; + }; + case OpAssign: assign(e1, e2); + case OpAssignOp(OpAdd): evalAssignOp((a:()->Dynamic, b:()->Dynamic) -> a() + b(), e1, e2); + case OpAssignOp(OpSub): evalAssignOp((a:()->Float, b:()->Float) -> a() - b(), e1, e2); + case OpAssignOp(OpMult): evalAssignOp((a:()->Float, b:()->Float) -> a() * b(), e1, e2); + case OpAssignOp(OpDiv): evalAssignOp((a:()->Float, b:()->Float) -> a() / b(), e1, e2); + case OpAssignOp(OpMod): evalAssignOp((a:()->Float, b:()->Float) -> a() % b(), e1, e2); + case OpAssignOp(OpAnd): evalAssignOp((a:()->Int, b:()->Int) -> a() & b(), e1, e2); + case OpAssignOp(OpOr): evalAssignOp((a:()->Int, b:()->Int) -> a() | b(), e1, e2); + case OpAssignOp(OpXor): evalAssignOp((a:()->Int, b:()->Int) -> a() ^ b(), e1, e2); + case OpAssignOp(OpShl): evalAssignOp((a:()->Int, b:()->Int) -> a() << b(), e1, e2); + case OpAssignOp(OpShr): evalAssignOp((a:()->Int, b:()->Int) -> a() >> b(), e1, e2); + case OpAssignOp(OpUShr): evalAssignOp((a:()->Int, b:()->Int) -> a() >>> b(), e1, e2); + case OpAssignOp(OpNullCoal): evalAssignOp((a:()->Dynamic, b:()->Dynamic) -> { + var a = a(); + return a == null ? b() : a; + }, e1, e2); + default: error(EInvalidOp(Printer.getBinaryOp(op) + " " + op)); + + //case OpAssignOp(op): getBinaryOp(op)(e1, e2); + + //assignOp("+=", function(v1:Dynamic, v2:Dynamic) return v1 + v2); + //assignOp("-=", function(v1:Float, v2:Float) return v1 - v2); + //assignOp("*=", function(v1:Float, v2:Float) return v1 * v2); + //assignOp("/=", function(v1:Float, v2:Float) return v1 / v2); + //assignOp("%=", function(v1:Float, v2:Float) return v1 % v2); + //assignOp("&=", function(v1, v2) return v1 & v2); + //assignOp("|=", function(v1, v2) return v1 | v2); + //assignOp("^=", function(v1, v2) return v1 ^ v2); + //assignOp("<<=", function(v1, v2) return v1 << v2); + //assignOp(">>=", function(v1, v2) return v1 >> v2); + //assignOp(">>>=", function(v1, v2) return v1 >>> v2); + //assignOp("??"+"=", function(v1, v2) return v1 == null ? v2 : v1); + } } function checkIsType(e1,e2): Bool { var expr1:Dynamic = expr(e1); - return switch(Tools.expr(e2)) { - case EIdent("Class"): - Std.isOfType(expr1, Class); - case EIdent("Map") | EIdent("IMap"): - Std.isOfType(expr1, IMap); + switch(Tools.expr(e2)) { + case EIdent(id): + var sid = _variablesNames[id]; + if (sid == "Class") + return Std.isOfType(expr1, Class); + if (sid == "Map" || sid == "IMap") + return Std.isOfType(expr1, IMap); default: - var expr2:Dynamic = expr(e2); - expr2 != null ? Std.isOfType(expr1, expr2) : false; } + + var expr2:Dynamic = expr(e2); + return expr2 != null ? Std.isOfType(expr1, expr2) : false; } - public function setVar(name:String, v:Dynamic) { - if (allowStaticVariables && staticVariables.exists(name)) - staticVariables.set(name, v); - else if (allowPublicVariables && publicVariables.exists(name)) - publicVariables.set(name, v); + public inline function setVar(name:String, v:Dynamic) { + isetVar(_variablesNames.indexOf(name), v); + } + + public function isetVar(id:Int, v:Dynamic) { + var sid:String = _variablesNames[id]; + if (allowStaticVariables && staticVariables.exists(sid)) + staticVariables.set(sid, v); + else if (allowPublicVariables && publicVariables.exists(sid)) + publicVariables.set(sid, v); else - variables.set(name, v); + _variables[id] = v; } function assign(e1:Expr, e2:Expr):Dynamic { var v = expr(e2); switch (Tools.expr(e1)) { case EIdent(id): - var l = locals.get(id); + var iname:String = _variablesNames[id]; + var l = _locals[id]; if (l == null) { - if (!variables.exists(id) && !staticVariables.exists(id) && !publicVariables.exists(id) && _hasScriptObject) { + // TODO: WHEN ADDING ENUM DEFINED THING CHANGE THIS + if (_variables[id] != null && !staticVariables.exists(iname) && !publicVariables.exists(iname) && _hasScriptObject) { if (_scriptObjectType == SObject) { - UnsafeReflect.setField(scriptObject, id, v); + UnsafeReflect.setField(scriptObject, iname, v); } else { if (isBypassAccessor) { - if (__instanceFields.contains(id)) { - UnsafeReflect.setField(scriptObject, id, v); + if (__instanceFields.contains(iname)) { + UnsafeReflect.setField(scriptObject, iname, v); return v; } } - if (__instanceFields.contains(id)) { - UnsafeReflect.setProperty(scriptObject, id, v); - } else if (__instanceFields.contains('set_$id')) { // setter - UnsafeReflect.getProperty(scriptObject, 'set_$id')(v); + if (__instanceFields.contains(iname)) { + UnsafeReflect.setProperty(scriptObject, iname, v); + } else if (__instanceFields.contains('set_$iname')) { // setter + UnsafeReflect.getProperty(scriptObject, 'set_$iname')(v); } else { - setVar(id, v); + isetVar(id, v); } } } else { - setVar(id, v); + isetVar(id, v); } } else { l.r = v; if (l.depth == 0) { - setVar(id, v); + isetVar(id, v); } } - // TODO case EField(e, f, s): var obj = expr(e); if(s && obj == null) return null; @@ -309,30 +376,34 @@ class Interp { return v; } - function assignOp(op, fop:Dynamic->Dynamic->Dynamic) { - var me = this; - binops.set(op, function(e1, e2) return me.evalAssignOp(op, fop, e1, e2)); - } + //function assignOp(op, fop:Dynamic->Dynamic->Dynamic) { + // var me = this; + // binops.set(op, function(e1, e2) return me.evalAssignOp(op, fop, e1, e2)); + //} - function evalAssignOp(op, fop, e1, e2):Dynamic { - var v; + // kk + function evalAssignOp(fop:(a:()->Dynamic, b:()->Dynamic)->Dynamic, e1, e2):Dynamic { + var aFunc:()->Dynamic = () -> expr(e1); + var bFunc:()->Dynamic = () -> expr(e2); // push first, wait we gotta remove the comments xd + var v:Dynamic = null; switch (Tools.expr(e1)) { case EIdent(id): - var l = locals.get(id); - v = fop(expr(e1), expr(e2)); + var l = _locals[id]; + v = fop(aFunc, bFunc); if (l == null) { if(_hasScriptObject) { + var iname:String = _variablesNames[id]; if(_scriptObjectType == SObject) { - UnsafeReflect.setField(scriptObject, id, v); - } else if (__instanceFields.contains(id)) { - UnsafeReflect.setProperty(scriptObject, id, v); - } else if (__instanceFields.contains('set_$id')) { // setter - UnsafeReflect.getProperty(scriptObject, 'set_$id')(v); + UnsafeReflect.setField(scriptObject, iname, v); + } else if (__instanceFields.contains(iname)) { + UnsafeReflect.setProperty(scriptObject, iname, v); + } else if (__instanceFields.contains('set_$iname')) { // setter + UnsafeReflect.getProperty(scriptObject, 'set_$iname')(v); } else { - setVar(id, v); + isetVar(id, v); } } else { - setVar(id, v); + isetVar(id, v); } } else @@ -340,7 +411,7 @@ class Interp { case EField(e, f, s): var obj = expr(e); if(s && obj == null) return null; - v = fop(get(obj, f), expr(e2)); + v = fop(() ->get(obj, f), bFunc); v = set(obj, f, v); case EArray(e, index): var arr:Dynamic = expr(e); @@ -348,14 +419,14 @@ class Interp { if (isMap(arr)) { var map = getMap(arr); - v = fop(map.get(index), expr(e2)); + v = fop(()->map.get(index), bFunc); map.set(index, v); } else { - v = fop(arr[index], expr(e2)); + v = fop(()->arr[index], bFunc); arr[index] = v; } default: - return error(EInvalidOp(op)); + return error(ECustom("Unknown field when handing assign operation")); } return v; } @@ -367,16 +438,16 @@ class Interp { #end switch (e) { case EIdent(id): - var l = locals.get(id); - var v:Dynamic = (l == null) ? resolve(id) : l.r; + var l = _locals[id]; + var v:Dynamic = (l == null) ? iresolve(id) : l.r; if (prefix) { v += delta; if (l == null) - setVar(id, v) + isetVar(id, v) else l.r = v; } else if (l == null) - setVar(id, v + delta) + isetVar(id, v + delta) else l.r = v + delta; return v; @@ -418,21 +489,9 @@ class Interp { } public function execute(expr:Expr):Dynamic { - depth = 0; - #if haxe3 - locals = new Map(); - #else - locals = new Hash(); - #end - declared = new Array(); + depth = 0; declared = []; + _locals = []; - Preprocessor.getvars(expr, _variablesNames); - _variables = cast new haxe.ds.Vector(_variablesNames.length); - _customClasses = cast new haxe.ds.Vector(_variablesNames.length); - _locals = cast new haxe.ds.Vector(_variablesNames.length); - - setDefualtVariables(); - variables.loadDefaults(); return exprReturn(expr); } @@ -456,6 +515,7 @@ class Interp { return null; } } catch(e:Error) { + Sys.println(haxe.CallStack.toString(haxe.CallStack.exceptionStack())); if (errorHandler != null) errorHandler(e); else @@ -467,21 +527,15 @@ class Interp { return null; } - public function duplicate(h:#if haxe3 Map #else Hash #end) { - #if haxe3 - var h2 = new Map(); - #else - var h2 = new Hash(); - #end - for (k in h.keys()) - h2.set(k, h.get(k)); - return h2; + // TODO: use array.copy(); + public function duplicate(array:Array):Array { + return [for (a in array) a]; } function restore(old:Int) { while (declared.length > old) { var d = declared.pop(); - locals.set(d.n, d.old); + _locals[d.n] = d.old; } } @@ -504,38 +558,43 @@ class Interp { #end } - public function resolve(id:String, doException:Bool = true):Dynamic { - if (id == null) - return null; - id = StringTools.trim(id); - var l = locals.get(id); + public function iresolve(id:Int, doException:Bool = true):Dynamic { + var l = _locals[id]; if (l != null) return l.r; - for(map in [variables, publicVariables, staticVariables, customClasses]) - if (map.exists(id)) - return map.get(id); + if (_variables[id] != null) return _variables[id]; + + var sid:String = _variablesNames[id]; + for(map in [publicVariables, staticVariables, customClasses]) + if (map.exists(sid)) + return map.get(sid); if (_hasScriptObject) { // search in object - if (id == "this") { + if (sid == "this") { return scriptObject; - } else if (_scriptObjectType == SObject && UnsafeReflect.hasField(scriptObject, id)) { - return UnsafeReflect.field(scriptObject, id); + } else if (_scriptObjectType == SObject && UnsafeReflect.hasField(scriptObject, sid)) { + return UnsafeReflect.field(scriptObject, sid); } else { - if (__instanceFields.contains(id)) { - return UnsafeReflect.getProperty(scriptObject, id); - } else if (__instanceFields.contains('get_$id')) { // getter - return UnsafeReflect.getProperty(scriptObject, 'get_$id')(); + if (__instanceFields.contains(sid)) { + return UnsafeReflect.getProperty(scriptObject, sid); + } else if (__instanceFields.contains('get_$sid')) { // getter + return UnsafeReflect.getProperty(scriptObject, 'get_$sid')(); } } } if (doException) - error(EUnknownVariable(id)); + error(EUnknownVariable(sid)); //var v = variables.get(id); return null; } + public inline function resolve(id:String, doException:Bool = true):Dynamic { + if (id != null) id = StringTools.trim(id); + return iresolve(_variablesNames.indexOf(id), doException); + } + function getClass(c:String):haxe.ds.Either, Enum> { if (importBlocklist.contains(c)) return null; @@ -558,9 +617,19 @@ class Interp { var e = e.e; #end switch (e) { + case EInfo(info, e): + _variablesNames = info.variables.copy(); + loadTables(_variablesNames.length); + + setDefaultVariables(); + variables.loadDefaults(); + + return expr(e); case EClass(name, fields, extend, interfaces): - if (customClasses.exists(name)) - error(EAlreadyExistingClass(name)); + var className:String = _variablesNames[name]; + // TODO: Change this when we add undefined thing + if (_variables[name] != null) + error(EAlreadyExistingClass(className)); inline function importVar(thing:String):String { if (thing == null) @@ -568,7 +637,7 @@ class Interp { final variable:Class = variables.exists(thing) ? cast variables.get(thing) : null; return variable == null ? thing : Type.getClassName(variable); } - customClasses.set(name, new CustomClassHandler(this, name, fields, importVar(extend), [for (i in interfaces) importVar(i)])); + _variables[name] = new CustomClassHandler(this, className, fields, importVar(extend), [for (i in interfaces) importVar(i)]); case EImport(c, mode): if (!importEnabled) return null; @@ -583,11 +652,12 @@ class Interp { var lastName:String = splitClassName[splitClassName.length-1]; var varName:String = switch(mode) { case IAs(name): name; + case IAsReturn: null; default: lastName; }; // Class is already imported - if (variables.exists(varName)) + if (varName != null && variables.exists(varName)) return variables.get(varName); // Orginal class @@ -619,11 +689,13 @@ class Interp { if(v == null) v = UnsafeReflect.field(classOrEnum, lastName); if(v == null) error(EInvalidAccess(lastName, c)); - variables.set(varName, v); + if (varName != null) + variables.set(varName, v); return v; } - variables.set(varName, classOrEnum); + if (varName != null) + variables.set(varName, classOrEnum); return classOrEnum; } @@ -640,18 +712,25 @@ class Interp { #end } case EIdent(id): - return resolve(id); + return iresolve(id); case EVar(n, _, e, isPublic, isStatic): - declared.push({n: n, old: locals.get(n), depth: depth}); - locals.set(n, {r: (e == null) ? null : expr(e), depth: depth}); + declared.push({n: n, old: _locals[n], depth: depth}); + _locals[n] = {r: (e == null) ? null : expr(e), depth: depth}; if (depth == 0) { - if(isStatic == true) { - if(!staticVariables.exists(n)) { // dont overwrite existing static variables - staticVariables.set(n, locals[n].r); + if(isPublic == true || isStatic == true) { + var varName:String = _variablesNames[n]; + if(isStatic == true) { + if(!staticVariables.exists(varName)) { // dont overwrite existing static variables + staticVariables.set(varName, _locals[n].r); + } + } else { + if (isPublic) { + publicVariables.set(varName, _locals[n].r); + } } - return null; } - (isPublic ? publicVariables : variables).set(n, locals[n].r); + else + _variables[n] = _locals[n].r; } return null; case EParent(e): @@ -669,35 +748,33 @@ class Interp { return null; return get(field, f); case EBinop(op, e1, e2): - var fop = binops.get(op); - #if debug - if (fop == null) - error(EInvalidOp(op)); - #end - return fop(e1, e2); + //var fop = binops.get(op); + //#if debug + //if (fop == null) + // error(EInvalidOp(op)); + //#end + return runBinop(op, e1, e2); case EUnop(op, prefix, e): switch (op) { - case "!": - return expr(e) != true; - case "-": - return -expr(e); - case "++": + case OpIncrement: return increment(e, prefix, 1); - case "--": + case OpDecrement: return increment(e, prefix, -1); - case "~": + case OpNot: + return expr(e) != true; + case OpNeg: + return -expr(e); + case OpNegBits: #if (neko && !haxe3) return haxe.Int32.complement(expr(e)); #else return ~expr(e); #end - default: - error(EInvalidOp(op)); + case OpSpread: + error(EInvalidOp("...")); } case ECall(e, params): - var args = new Array(); - for(i in 0...params.length) - args.push(expr(params[i])); + var args:Array = [for(p in params) expr(p)]; switch (Tools.expr(e)) { case EField(e, f, s): @@ -732,21 +809,25 @@ class Interp { returnValue = e == null ? null : expr(e); throw SReturn; case EFunction(params, fexpr, name, _, isPublic, isStatic, isOverride): - var __capturedLocals = duplicate(locals); - var capturedLocals:Map = []; + var __capturedLocals = duplicate(_locals); + var capturedLocals:Array = []; for(k=>e in __capturedLocals) if (e != null && e.depth > 0) - capturedLocals.set(k, e); + capturedLocals[k] = e; + else + capturedLocals[k] = null; + // TODO: REPLACE THIS WITH UNDEFINED WHEN WE ADD -lunar var me = this; var hasOpt = false, minParams = 0; + //for (a in params) trace(a.name); for (p in params) if (p.opt) hasOpt = true; else minParams++; var f = function(args:Array) { - if (me.locals == null || me.variables == null) return null; + if (me._locals == null || me._variables == null) return null; if (((args == null) ? 0 : args.length) != params.length) { if (args.length < minParams) { @@ -770,18 +851,18 @@ class Interp { args2.push(args[pos++]); args = args2; } - var old = me.locals, depth = me.depth; + var old = me._locals, depth = me.depth; me.depth++; - me.locals = me.duplicate(capturedLocals); + me._locals = me.duplicate(capturedLocals); for (i in 0...params.length) - me.locals.set(params[i].name, {r: args[i], depth: depth}); + me._locals[params[i].name] = {r: args[i], depth: depth}; var r = null; var oldDecl = declared.length; if (inTry) try { r = me.exprReturn(fexpr); } catch (e:Dynamic) { - me.locals = old; + me._locals = old; me.depth = depth; #if neko neko.Lib.rethrow(e); @@ -792,7 +873,7 @@ class Interp { else r = me.exprReturn(fexpr); restore(oldDecl); - me.locals = old; + me._locals = old; me.depth = depth; return r; }; @@ -800,13 +881,18 @@ class Interp { if (name != null) { if (depth == 0) { // global function - ((isStatic && allowStaticVariables) ? staticVariables : ((isPublic && allowPublicVariables) ? publicVariables : variables)).set(name, f); + if ((isStatic && allowStaticVariables)) + staticVariables.set(_variablesNames[name], f); + else if (isPublic && allowPublicVariables) + publicVariables.set(_variablesNames[name], f); + else + _variables[name] = f; } else { // function-in-function is a local function - declared.push({n: name, old: locals.get(name), depth: depth}); + declared.push({n: name, old: _locals[name], depth: depth}); var ref:DeclaredVar = {r: f, depth: depth}; - locals.set(name, ref); - capturedLocals.set(name, ref); // allow self-recursion + _locals[name] = ref; + capturedLocals[name] = ref; // allow self-recursion } } return f; @@ -870,7 +956,7 @@ class Interp { var a = new Array(); for (i in 0...params.length) a.push(expr(params[i])); - return cnew(cl, a); + return cnew(_variablesNames[cl], a); case EThrow(e): throw expr(e); case ETry(e, n, _, ecatch): @@ -890,8 +976,8 @@ class Interp { restore(old); inTry = oldTry; // declare 'v' - declared.push({n: n, old: locals.get(n), depth: depth}); - locals.set(n, {r: err, depth: depth}); + declared.push({n: n, old: _locals[n], depth: depth}); + _locals[n] = {r: err, depth: depth}; var v:Dynamic = expr(ecatch); restore(old); return v; @@ -1010,13 +1096,13 @@ class Interp { function forLoop(n, it, e) { var old = declared.length; - declared.push({n: n, old: locals.get(n), depth: depth}); + declared.push({n: n, old: _locals[n], depth: depth}); var it = makeIterator(expr(it)); var _hasNext = it.hasNext; var _next = it.next; while (_hasNext()) { var next = _next(); - locals.set(n, {r: next, depth: depth}); + _locals[n] = {r: next, depth: depth}; try { expr(e); } catch (err:Stop) { @@ -1034,15 +1120,15 @@ class Interp { function forLoopKeyValue(n, it, e, ithv) { var old = declared.length; - declared.push({n: ithv, old: locals.get(ithv), depth: depth}); - declared.push({n: n, old: locals.get(n), depth: depth}); + declared.push({n: ithv, old: _locals[ithv], depth: depth}); + declared.push({n: n, old: _locals[n], depth: depth}); var it = makeKeyValueIterator(expr(it)); var _hasNext = it.hasNext; var _next = it.next; while (_hasNext()) { var next = _next(); - locals.set(ithv, {r: next.key, depth: depth}); - locals.set(n, {r: next.value, depth: depth}); + _locals[ithv] = {r: next.key, depth: depth}; + _locals[n] = {r: next.value, depth: depth}; try { expr(e); } catch (err:Stop) { @@ -1162,9 +1248,34 @@ class Interp { return (c is IHScriptCustomConstructor) ? cast(c, IHScriptCustomConstructor).hnew(args) : Type.createInstance(c, args); } + inline function loadTables(len:Int) { + _variables = cast new haxe.ds.Vector(len); + _locals = cast new haxe.ds.Vector(len); + } + static inline function b2i(b:Bool) return b ? 1 : 0; } +class HScriptVariablesKeyValueIterator { + public var names:Array = []; + public var values:Array = []; + + public function new(names:Array, values:Array) { + this.names = names; + this.values = values; + } + + @:noCompletion public var _current:Int = 0; + public inline function hasNext():Bool { + return _current < names.length; + } + + public inline function next():{key:String, value:Dynamic} { + _current++; + return {value: values[_current], key: names[_current]}; + } +} + class HScriptVariables { public var defaults:Map = []; public var usedefaults:Bool = true; @@ -1219,18 +1330,7 @@ class HScriptVariables { public inline function keys():ArrayIterator return parent._variablesNames.iterator(); - public inline function keyValueIterator() : HScriptVariables { - _current = 0; - return this; - } - - @:noCompletion public var _current:Int = 0; - public inline function hasNext():Bool { - return _current < parent._variablesNames.length; - } - - public inline function next():{key:String, value:Dynamic} { - _current++; - return {value: parent._variables[_current], key: parent._variablesNames[_current]}; + public function keyValueIterator() : HScriptVariablesKeyValueIterator { + return new HScriptVariablesKeyValueIterator(parent._variablesNames, parent._variables); } } \ No newline at end of file diff --git a/hscript/Optimizer.hx b/hscript/Optimizer.hx index 5b6df61..4c40dc6 100644 --- a/hscript/Optimizer.hx +++ b/hscript/Optimizer.hx @@ -74,7 +74,7 @@ class Optimizer { var c2 = getBool(e2); if(c1 == false && c2 == true) { // (VAR ? false : true) - return optimize(mk(EUnop("!", true, econd), s)); + return optimize(mk(EUnop(OpNot, true, econd), s)); } if(c1 == true && c2 == false) { // (VAR ? true : false) return optimize(mk(Tools.expr(econd), s)); @@ -113,7 +113,7 @@ class Optimizer { var c2 = getBool(e2); if(c1 == false && c2 == true) { // (VAR ? false : true) - return optimize(mk(EUnop("!", true, econd), s)); + return optimize(mk(EUnop(OpNot, true, econd), s)); } if(c1 == true && c2 == false) { // (VAR ? true : false) return optimize(mk(Tools.expr(econd), s)); @@ -391,9 +391,9 @@ class Optimizer { // Possible bugs here if(isNumber(e1) && !isConstant(e2)) { var c1 = getNumber(e1); - if(compareNumber(c1, 0) && op == "+") + if(compareNumber(c1, 0) && op == OpAdd) return mk(Tools.expr(e2), s); - if(compareNumber(c1, 1) && op == "*") + if(compareNumber(c1, 1) && op == OpMult) return mk(Tools.expr(e2), s); //if(compareNumber(c1, 0) && op == "*") // return mk(convertConstant(0), s); @@ -401,11 +401,11 @@ class Optimizer { if(!isConstant(e1) && isNumber(e2)) { var c2 = getNumber(e2); - if(op == "+" && compareNumber(c2, 0)) + if(op == OpAdd && compareNumber(c2, 0)) return mk(Tools.expr(e1), s); - if(op == "/" && compareNumber(c2, 1)) + if(op == OpDiv && compareNumber(c2, 1)) return mk(Tools.expr(e1), s); - if(op == "*" && compareNumber(c2, 1)) + if(op == OpMult && compareNumber(c2, 1)) return mk(Tools.expr(e1), s); //if(op == "*" && compareNumber(c2, 0)) // return mk(convertConstant(0), s); @@ -419,11 +419,12 @@ class Optimizer { if(isConstant(e) && prefix) { var constant:Dynamic = getConstant(e); switch(op) { - case "-": return mk(convertConstant(-constant), s); - case "!": return mk(convertConstant(!constant), s); - case "~": + case OpNeg: return mk(convertConstant(-constant), s); + case OpNot: return mk(convertConstant(!constant), s); + case OpNegBits: var complement = #if (neko && !haxe3) haxe.Int32.complement(constant) #else ~constant #end; return mk(convertConstant(complement), s); + default: } } @@ -605,29 +606,29 @@ class Optimizer { #end } - static function optimizeOp(op, f1:Dynamic, f2:Dynamic):Dynamic { + static function optimizeOp(op:Binop, f1:Dynamic, f2:Dynamic):Dynamic { //trace("Optimizing " + f1 + " " + op + " " + f2); return switch(op) { - case "+": f1 + f2; - case "-": f1 - f2; - case "*": f1 * f2; - case "/": f1 / f2; - case "%": f1 % f2; - case "&": f1 & f2; - case "|": f1 | f2; - case "^": f1 ^ f2; - case "<<": f1 << f2; - case ">>": f1 >> f2; - case ">>>": f1 >>> f2; - case "==": f1 == f2; - case "!=": f1 != f2; - case ">=": f1 >= f2; - case "<=": f1 <= f2; - case ">": f1 > f2; - case "<": f1 < f2; - case "||": f1 == true || f2 == true; - case "&&": f1 == true && f2 == true; - case "??": f1 == null ? f2 : f1; + case OpAdd: f1 + f2; + case OpSub: f1 - f2; + case OpMult: f1 * f2; + case OpDiv: f1 / f2; + case OpMod: f1 % f2; + case OpAnd: f1 & f2; + case OpOr: f1 | f2; + case OpXor: f1 ^ f2; + case OpShl: f1 << f2; + case OpShr: f1 >> f2; + case OpUShr: f1 >>> f2; + case OpEq: f1 == f2; + case OpNotEq: f1 != f2; + case OpGte: f1 >= f2; + case OpLte: f1 <= f2; + case OpGt: f1 > f2; + case OpLt: f1 < f2; + case OpBoolOr: f1 == true || f2 == true; + case OpBoolAnd: f1 == true && f2 == true; + case OpNullCoal: f1 == null ? f2 : f1; default: null; } } diff --git a/hscript/Parser.hx b/hscript/Parser.hx index 6f2b997..6b75dab 100644 --- a/hscript/Parser.hx +++ b/hscript/Parser.hx @@ -225,12 +225,11 @@ class Parser { } var expr = if( a.length == 1 ) a[0] else mk(EBlock(a),0); expr = Preprocessor.process(expr); - if(Parser.optimize) { + if(Parser.optimize) expr = Optimizer.optimize(expr); - // trace("INPUT: " + s); - // trace("OUTPUT: " + Printer.toString(expr)); - } - expr = Preprocessor.processvars(expr, []); + expr = Postprocessor.processvars(expr); + // trace("INPUT: " + s); // useful for debugging + //trace("OUTPUT: " + Printer.toString(expr)); return expr; } @@ -466,7 +465,7 @@ class Parser { } default: } - e = mk(EBinop("+", e, er)); + e = mk(EBinop(OpAdd, e, er)); //e = mk(EParent(e)); } while(tg.length > 0) { @@ -629,7 +628,7 @@ class Parser { var isTypeMap = (nextType != null) && nextType.match(CTPath(["Map"], [_, _])); var isMap = isTypeMap; if(!isMap) { - isMap = Lambda.exists(a, (e) -> Tools.expr(e).match(EBinop("=>", _))); // Check if any element is a => b + isMap = Lambda.exists(a, (e) -> Tools.expr(e).match(EBinop(OpArrow, _))); // Check if any element is a => b } if(isMap) { // TODO: clean up this code more @@ -641,7 +640,7 @@ class Parser { var values:Array = []; for (e in a) { switch (Tools.expr(e)) { - case EBinop("=>", eKey, eValue): { + case EBinop(OpArrow, eKey, eValue): { switch(Tools.expr(eKey)) { case EConst(CInt(_)):// | EConst(CFloat(_)): isKeyInt = true; @@ -791,7 +790,7 @@ class Parser { default: // tmp.set(k, v); switch( Tools.expr(e) ) { - case EBinop("=>", e1, e2): + case EBinop(OpArrow, e1, e2): //ECall( mk(EField(mk(EIdent(tmp), pmin(e), pmax(e)), "set"), pmin(e), pmax(e)), [e1, e2]); ECall( mk(EIdent(tmp), pmin(e), pmax(e)), [e1, e2]); default: // default incase of error @@ -824,37 +823,48 @@ class Parser { isMapCompr(e2); default: // tmp.set(k, v); - return Tools.expr(e).match(EBinop("=>", _)); + return Tools.expr(e).match(EBinop(OpArrow, _)); } } - function makeUnop( op, e ) { + function makeUnop( op:String, e:Expr ):Expr { + var op = Tools.getUnopEnum(op); + return _makeUnop(op, e); + } + + function _makeUnop( op:Unop, e:Expr ):Expr { if( e == null && resumeErrors ) return null; return switch( Tools.expr(e) ) { - case EBinop(bop, e1, e2): mk(EBinop(bop, makeUnop(op, e1), e2), pmin(e1), pmax(e2)); - case ETernary(e1, e2, e3): mk(ETernary(makeUnop(op, e1), e2, e3), pmin(e1), pmax(e3)); + case EBinop(bop, e1, e2): mk(EBinop(bop, _makeUnop(op, e1), e2), pmin(e1), pmax(e2)); + case ETernary(e1, e2, e3): mk(ETernary(_makeUnop(op, e1), e2, e3), pmin(e1), pmax(e3)); default: mk(EUnop(op,true,e),pmin(e),pmax(e)); } } - function makeBinop( op, e1, e ) { + function makeBinop( op:String, e1:Expr, e:Expr ) { // TODO: clean this up if(!Tools.isValidBinOp(op)) error(EInvalidOp(op),pmin(e1),pmax(e1)); + var op = Tools.getOpEnum(op); + return _makeBinop(op, e1, e); + } + + function _makeBinop( op:Binop, e1:Expr, e:Expr ) { + // TODO: clean this up if( e == null && resumeErrors ) return mk(EBinop(op,e1,e),pmin(e1),pmax(e1)); return switch( Tools.expr(e) ) { case EBinop(op2,e2,e3): - if( opPriority.get(op) <= opPriority.get(op2) && !opRightAssoc.exists(op) ) - mk(EBinop(op2,makeBinop(op,e1,e2),e3),pmin(e1),pmax(e3)); + if( opPriority.get(Printer.getBinaryOp(op)) <= opPriority.get(Printer.getBinaryOp(op2)) && !opRightAssoc.exists(Printer.getBinaryOp(op)) ) + mk(EBinop(op2,_makeBinop(op,e1,e2),e3),pmin(e1),pmax(e3)); else mk(EBinop(op, e1, e), pmin(e1), pmax(e)); case ETernary(e2,e3,e4): - if( opRightAssoc.exists(op) ) + if( opRightAssoc.exists(Printer.getBinaryOp(op)) ) mk(EBinop(op,e1,e),pmin(e1),pmax(e)); else - mk(ETernary(makeBinop(op, e1, e2), e3, e4), pmin(e1), pmax(e)); + mk(ETernary(_makeBinop(op, e1, e2), e3, e4), pmin(e1), pmax(e)); default: mk(EBinop(op,e1,e),pmin(e1),pmax(e)); } @@ -1328,7 +1338,7 @@ class Parser { push(tk); return e1; } - return parseExprNext(mk(EUnop(op,false,e1),pmin(e1))); + return parseExprNext(mk(EUnop(Tools.getUnopEnum(op),false,e1),pmin(e1))); } return makeBinop(op,e1,parseExpr()); case TDot | TQuestionDot: @@ -2270,7 +2280,7 @@ class Parser { case '?'.code: var orp = readPos; if (readChar() == '='.code) - return TOp("??"+"="); + return TOp("??" + "="); this.readPos = orp; return TOp("??"); @@ -2459,7 +2469,7 @@ class Parser { } mk(EIdent(id), tokenMin, tokenMax); case TOp("!"): - mk(EUnop("!", true, parsePreproCond()), tokenMin, tokenMax); + mk(EUnop(OpNot, true, parsePreproCond()), tokenMin, tokenMax); default: unexpected(tk); } @@ -2477,13 +2487,13 @@ class Parser { error(EInvalidPreprocessor("Can't eval " + Tools.expr(e).getName() + " with " + Tools.expr(e2).getName()), readPos, readPos); return false; } - case EUnop("!", _, e): + case EUnop(OpNot, _, e): return !evalPreproCond(e); case EParent(e): return evalPreproCond(e); - case EBinop("&&", e1, e2): + case EBinop(OpBoolAnd, e1, e2): return evalPreproCond(e1) && evalPreproCond(e2); - case EBinop("||", e1, e2): + case EBinop(OpBoolOr, e1, e2): return evalPreproCond(e1) || evalPreproCond(e2); default: error(EInvalidPreprocessor("Can't eval " + Tools.expr(e).getName()), readPos, readPos); diff --git a/hscript/Postprocessor.hx b/hscript/Postprocessor.hx new file mode 100644 index 0000000..724ccba --- /dev/null +++ b/hscript/Postprocessor.hx @@ -0,0 +1,116 @@ +package hscript; + +import hscript.Expr; +import hscript.Error; +import hscript.Tools; +import hscript.Parser; + +@:access(hscript.Parser) +class Postprocessor { + static inline function expr(e:Expr) return Tools.expr(e); + + static function mk(e:ExprDef, s:Expr):Expr { + #if hscriptPos + return new Expr(e, s.pmin, s.pmax, s.origin, s.line); + #else + return e; + #end + } + + public static function processvars(e:Expr, ?vars:Array) { + if(vars == null) vars = []; + for(v in getvars(e)) vars.push(v); + e = _processvars(e, vars); + + //trace(vars); + + return mk(EInfo(new InfoClass(vars), e), e); + } + + private static function _processvars(e:Expr, vars:Array):Expr { + if(e == null) return null; + var ge = e; + var doExtra = true; + var e = switch (expr(e)) { + case EIdent(v) if (v is String): + //trace(v +" -> " + vars.indexOf(v)); + mk(EIdent(vars.indexOf(v)), e); + case EVar(n, t, _e, p, s) if (n is String): + //trace(n + " -> " + vars.indexOf(n)); + mk(EVar(vars.indexOf(n), t, _e, p, s), e); + case EImport(c, IAs(n)) if (n is String): + //trace(n + " -> " + vars.indexOf(n)); + // IAsReturn, doesnt store it in variables array, only returns the import as a expression like value + mk(EVar(vars.indexOf(n), null, mk(EImport(c, IAsReturn), e), false, false), e); + case EFunction(args, _e, n, r, p, s, o) if (n is String): + var _args:Array = [ + for (arg in args) + new Argument(vars.indexOf(arg.name), arg.t, arg.opt, arg.value) + ]; + mk(EFunction(_args, _e, vars.indexOf(n), r, p, s, o), e); + case EFunction(args, _e, null, r, p, s, o): + var _args:Array = [ + for (arg in args) + new Argument(vars.indexOf(arg.name), arg.t, arg.opt, arg.value) + ]; + mk(EFunction(_args, _e, null, r, p, s, o), e); + case ENew(cl, p) if (cl is String): + var p = [for(arg in p) arg]; + mk(ENew(vars.indexOf(cl), p), e); + case EClass(n, fls, extnd, i) if (n is String): + var fls:Array = [for(f in fls) f]; + mk(EClass(vars.indexOf(n), fls, extnd, i), e); + case ETry(_e, v, t, ec) if (v is String): + mk(ETry(_e, vars.indexOf(v), t, ec), e); + case EFor(v, it, _e) if (v is String): + //trace(v + " -> " + vars.indexOf(v)); + mk(EFor(vars.indexOf(v), it, _e), e); + case EForKeyValue(v, it, _e, ithv) if (v is String && ithv is String): + //trace(v + " -> " + vars.indexOf(v)); + //trace(ithv + " -> " + vars.indexOf(ithv)); + mk(EForKeyValue(vars.indexOf(v), it, _e, vars.indexOf(ithv)), e); + default: { + doExtra = false; + e; + } + }; + + e = Tools.map(e, function(e) { + return _processvars(e, vars); + }); + + //if(doExtra) e = _processvars(e, vars); + + return e; + } + + public static function getvars(se:Expr, ?vars:Array):Array { + inline function pushVar(v:VarN) { + if(v is String) { + var v:String = cast v; + if (!vars.contains(v)) + vars.push(v); + } + }; + + if(vars == null) vars = []; + Tools.iterExprRecursive(se, (e) -> { + switch (expr(e)) { + case EVar(n, t, e, _,_): pushVar(n); + case EIdent(v): pushVar(v); + case EFunction(args,e,name,_,_,_,_): + pushVar(name); + for (a in args) { + pushVar(a.name); + } + case EFor(v,it,e): pushVar(v); + case EForKeyValue(v,it,e,ithv): pushVar(ithv); pushVar(v); + case ETry(e, v, _, ec): pushVar(v); + case ENew(cl, params): pushVar(cl); + case EClass(name, fls,_,_): pushVar(name); + default: + } + }); + return vars; + } +} \ No newline at end of file diff --git a/hscript/Preprocessor.hx b/hscript/Preprocessor.hx index bb3a142..81385c1 100644 --- a/hscript/Preprocessor.hx +++ b/hscript/Preprocessor.hx @@ -144,109 +144,4 @@ class Preprocessor { } return isStringFromCharCodeFixed; } - - public static function processvars(e:Expr, vars:Array) { - getvars(e, vars); - e = Tools.map(e, function(e) { - return switch (expr(e)) { - case EIdent(v, _): - return mk(EIdent(v, vars.indexOf(v)), e); - case EVar(n, t, e, p, s, _): - return mk(EVar(n, t, e, p, s, vars.indexOf(n)), e); - case EFunction(args, e, n, r, p, s, o, _): - return mk(EFunction(args, e, n, r, p, s, o, vars.indexOf(n)), e); - case ENew(cl, p, _): - return mk(ENew(cl, p, vars.indexOf(cl)), e); - case EClass(n, fls, extnd, i, _): - return mk(EClass(n, fls, extnd, i, vars.indexOf(n)), e); - default: e; - }; - }); - - return e; - } - - public static function getvars(e:Expr, vars:Array) { - // TODO: Use a Tools function to make this cleaner (im too lazy rn) -lunar - switch (expr(e)) { - case EIdent(v): - if (!vars.contains(v)) - vars.push(v); - case EBlock(e): - for (expr in e) - getvars(expr, vars); - case EFunction(_,e,name,_,_,_,_): - getvars(e, vars); - if (!vars.contains(name)) - vars.push(name); - case ESwitch(e,cases,de): - for (c in cases) - getvars(c.expr, vars); - getvars(de, vars); - case EObject(fls): - for (fl in fls) getvars(fl.e, vars); - case EVar(n, t, e, _,_): - if (!vars.contains(n)) - vars.push(n); - getvars(e, vars); - case EIf(c,e1,e2): - getvars(c, vars); - getvars(e1, vars); - getvars(e2, vars); - case EBinop(_,e1,e2): - getvars(e1, vars); - getvars(e2, vars); - case EUnop(_,_,e): getvars(e, vars); - case EWhile(c,e): - getvars(c, vars); - getvars(e, vars); - case EDoWhile(e1,e2): - getvars(e1, vars); - getvars(e2, vars); - case EFor(_,it,e): - getvars(it, vars); - getvars(e, vars); - case EForKeyValue(_,it,e,_): - getvars(it, vars); - getvars(e, vars); - case EReturn(e): getvars(e, vars); - case ETry(e, _, _, ec): - getvars(e, vars); - getvars(ec, vars); - case ECall(e, params): - getvars(e, vars); - for (e in params) getvars(e, vars); - case EParent(e, _): getvars(e, vars); - case EArray(e, indx): - getvars(e, vars); - getvars(indx, vars); - case EMapDecl(_, keys, values): - for (k in keys) getvars(k, vars); - for (v in values) getvars(v, vars); - case EArrayDecl(list): - for (e in list) getvars(e, vars); - case ENew(cl, params): - if (!vars.contains(cl)) - vars.push(cl); - for (e in params) getvars(e, vars); - case EThrow(e): - getvars(e, vars); - case ETernary(e, e1, e2): - getvars(e1, vars); - getvars(e2, vars); - getvars(e, vars); - case EMeta(_, args, e): - for (a in args) getvars(a, vars); - getvars(e, vars); - case ECheckType(e, _): - getvars(e, vars); - case EField(e, _, _): - getvars(e, vars); - case EClass(name, fls,_,_): - if (!vars.contains(name)) - vars.push(name); - for (fl in fls) getvars(fl, vars); - default: - } - } } \ No newline at end of file diff --git a/hscript/Printer.hx b/hscript/Printer.hx index 43e4250..f12393c 100644 --- a/hscript/Printer.hx +++ b/hscript/Printer.hx @@ -49,6 +49,11 @@ class Printer { return buf.toString(); } + public static function convertTypeToString( t : CType ) { + var printer = new Printer(); + return printer.typeToString(t); + } + inline function add(s:T) buf.add(s); function type( t : CType ) { @@ -73,11 +78,14 @@ class Printer { type(t); case CTFun(args, ret) if (Lambda.exists(args, function (a) return a.match(CTNamed(_, _)))): add('('); - for (a in args) + var first = true; + for (i=>a in args) { switch a { case CTNamed(_, _): type(a); default: type(CTNamed('_', a)); } + if( i != args.length - 1 ) add(", "); + } add(')->'); type(ret); case CTFun(args, ret): @@ -196,6 +204,59 @@ class Printer { return buf.toString(); } + function getVar(v:VarN) { + if( v == null ) { + return ("??NULL??"); + } + + if(v is Int) { + return ("$" + v); + } else { + return (v); + } + } + + public static function getBinaryOp(op:Binop) { + return switch(op) { + case OpAdd: "+"; + case OpSub: "-"; + case OpMult: "*"; + case OpDiv: "/"; + case OpMod: "%"; + case OpAnd: "&"; + case OpOr: "|"; + case OpXor: "^"; + case OpShl: "<<"; + case OpShr: ">>"; + case OpUShr: ">>>"; + case OpEq: "=="; + case OpNotEq: "!="; + case OpGt: ">"; + case OpGte: ">="; + case OpLt: "<"; + case OpLte: "<="; + case OpBoolAnd: "&&"; + case OpBoolOr: "||"; + case OpIs: "is"; + case OpNullCoal: "??"; + case OpAssign: "="; + case OpArrow: "=>"; + case OpInterval: "..."; + case OpAssignOp(op): getBinaryOp(op) + "="; + } + } + + public static function getUnaryOp(op:Unop) { + return switch(op) { + case OpIncrement: "++"; + case OpDecrement: "--"; + case OpNot: "!"; + case OpNeg: "-"; + case OpNegBits: "~"; + case OpSpread: "..."; + } + } + function expr( e : Expr ) { if( e == null ) { add("??NULL??"); @@ -213,7 +274,7 @@ class Printer { default: } case EClass(name, fields, extend, interfaces): - add('class $name'); + add('class ${getVar(name)}'); if (extend != null) add(' extends $extend'); for(_interface in interfaces) { @@ -238,9 +299,9 @@ class Printer { add('"'); } case EIdent(v): - add(v); + add(getVar(v)); case EVar(n, t, e): // TODO: static, public, override - add("var " + n); + add("var " + getVar(n)); if( t != null ) addType(t); else @@ -282,8 +343,7 @@ class Printer { expr(e); add((s == true ? "?." : ".") + f); case EBinop(op, e1, e2): - var shouldParen = false; - var op1 = switch(Tools.expr(e1)) { + /*var op1 = switch(Tools.expr(e1)) { case EBinop(op, _, _): op; case EConst(_): "_"; case EIdent(_): "_"; @@ -295,7 +355,10 @@ class Printer { case EIdent(_): "_"; default: null; } - var paran = Tools.checkOpPrecedence(op, op1, op2); + var paran = Tools.checkOpPrecedence(op, op1, op2);*/ + var paran = 2; + + var op = getBinaryOp(op); if(paran == 0 || paran == 2) { add("("); @@ -316,6 +379,8 @@ class Printer { expr(e2); } case EUnop(op, pre, e): + var op = getUnaryOp(op); + if( pre ) { add(op); expr(e); @@ -364,12 +429,12 @@ class Printer { expr(cond); add(" )"); case EFor(v, it, e): - add("for( "+v+" in "); + add("for( "+getVar(v)+" in "); expr(it); add(" )"); block(e, true); case EForKeyValue(v, it, e, ithv): - add("for( "+ithv+" => "+v+" in "); + add("for( "+getVar(ithv)+" => "+getVar(v)+" in "); expr(it); add(" )"); block(e, true); @@ -380,13 +445,13 @@ class Printer { case EFunction(params, e, name, ret): // TODO: static, public, override add("function"); if( name != null ) - add(" " + name); + add(" " + getVar(name)); add("("); var first = true; for( a in params ) { if( first ) first = false else add(", "); if( a.opt ) add("?"); - add(a.name); + add(getVar(a.name)); addType(a.t); } add(")"); @@ -423,7 +488,7 @@ class Printer { } add("]"); case ENew(cl, args): - add("new " + cl + "("); + add("new " + getVar(cl) + "("); var first = true; for( e in args ) { if( first ) first = false else add(", "); @@ -513,6 +578,8 @@ class Printer { add(" : "); addType(t); add(")"); + case EInfo(_, e): + expr(e); } } diff --git a/hscript/Tools.hx b/hscript/Tools.hx index 0a6ea4d..94bfaeb 100644 --- a/hscript/Tools.hx +++ b/hscript/Tools.hx @@ -27,75 +27,119 @@ class Tools { public static function iter( e : Expr, f : Expr -> Void ) { switch( expr(e) ) { - case EConst(_), EIdent(_): f(e); - case EImport(c, _): f(e); - case EClass(_, e, _, _): for( a in e ) f(a); - case EVar(_, _, e): if( e != null ) f(e); - case EParent(e): f(e); - case EBlock(el): for( e in el ) f(e); - case EField(e, _): f(e); - case EBinop(_, e1, e2): f(e1); f(e2); - case EUnop(_, _, e): f(e); - case ECall(e, args): f(e); for( a in args ) f(a); - case EIf(c, e1, e2): f(c); f(e1); if( e2 != null ) f(e2); - case EWhile(c, e): f(c); f(e); - case EDoWhile(c, e): f(c); f(e); - case EFor(_, it, e): f(it); f(e); - case EForKeyValue(_, it, e, _): f(it); f(e); - case EBreak,EContinue: - case EFunction(_, e, _, _): f(e); - case EReturn(e): if( e != null ) f(e); - case EArray(e, i): f(e); f(i); - case EMapDecl(type, keys, values): for( e in keys ) f(e); for( e in values ) f(e); - case EArrayDecl(el): for( e in el ) f(e); - case ENew(_,el): for( e in el ) f(e); - case EThrow(e): f(e); - case ETry(e, _, _, c): f(e); f(c); - case EObject(fl): for( fi in fl ) f(fi.e); - case ETernary(c, e1, e2): f(c); f(e1); f(e2); - case ESwitch(e, cases, def): - f(e); - for( c in cases ) { - for( v in c.values ) f(v); - f(c.expr); - } - if( def != null ) f(def); - case EMeta(name, args, e): if( args != null ) for( a in args ) f(a); f(e); - case ECheckType(e,_): f(e); + case EConst(_), EIdent(_): f(e); + case EImport(c, _): f(e); + case EClass(_, e, _, _): for( a in e ) f(a); + case EVar(_, _, e): if( e != null ) f(e); + case EParent(e, _): f(e); + case EBlock(el): for( e in el ) f(e); + case EField(e, _): f(e); + case EBinop(_, e1, e2): f(e1); f(e2); + case EUnop(_, _, e): f(e); + case ECall(e, args): f(e); for( a in args ) f(a); + case EIf(c, e1, e2): f(c); f(e1); if( e2 != null ) f(e2); + case EWhile(c, e): f(c); f(e); + case EDoWhile(c, e): f(c); f(e); + case EFor(_, it, e): f(it); f(e); + case EForKeyValue(_, it, e, _): f(it); f(e); + case EBreak,EContinue: + case EFunction(_, e, _, _): f(e); + case EReturn(e): if( e != null ) f(e); + case EArray(e, i): f(e); f(i); + case EMapDecl(type, keys, values): for( e in keys ) f(e); for( e in values ) f(e); + case EArrayDecl(el): for( e in el ) f(e); + case ENew(_,el): for( e in el ) f(e); + case EThrow(e): f(e); + case ETry(e, _, _, c): f(e); f(c); + case EObject(fl): for( fi in fl ) f(fi.e); + case ETernary(c, e1, e2): f(c); f(e1); f(e2); + case ESwitch(e, cases, def): + f(e); + for( c in cases ) { + for( v in c.values ) f(v); + f(c.expr); + } + if( def != null ) f(def); + case EMeta(name, args, e): if( args != null ) for( a in args ) f(a); f(e); + case ECheckType(e,_): f(e); + case EInfo(info, e): f(e); + } + } + public static function iterExprRecursive( ve : Expr, f : Expr -> Void ) { + var rec = (e:Expr) -> iterExprRecursive(e, f); + f(ve); + switch( expr(ve) ) { + case EConst(_), EIdent(_): {} + case EImport(c, _): {} + case EClass(_, e, _, _): for( a in e ) rec(a); + case EVar(_, _, e): if( e != null ) rec(e); + case EParent(e, _): rec(e); + case EBlock(el): for( e in el ) rec(e); + case EField(e, _): rec(e); + case EBinop(_, e1, e2): rec(e1); rec(e2); + case EUnop(_, _, e): rec(e); + case ECall(e, args): rec(e); for( a in args ) rec(a); + case EIf(c, e1, e2): rec(c); rec(e1); if( e2 != null ) rec(e2); + case EWhile(c, e): rec(c); rec(e); + case EDoWhile(c, e): rec(c); rec(e); + case EFor(_, it, e): rec(it); rec(e); + case EForKeyValue(_, it, e, _): rec(it); rec(e); + case EBreak,EContinue: {} + case EFunction(_, e, _, _): rec(e); + case EReturn(e): if( e != null ) rec(e); + case EArray(e, i): rec(e); rec(i); + case EMapDecl(type, keys, values): for( e in keys ) rec(e); for( e in values ) rec(e); + case EArrayDecl(el): for( e in el ) rec(e); + case ENew(_,el): for( e in el ) rec(e); + case EThrow(e): rec(e); + case ETry(e, _, _, c): rec(e); rec(c); + case EObject(fl): for( fi in fl ) rec(fi.e); + case ETernary(c, e1, e2): rec(c); rec(e1); rec(e2); + case ESwitch(e, cases, def): + rec(e); + for( c in cases ) { + for( v in c.values ) rec(v); + rec(c.expr); + } + if( def != null ) rec(def); + case EMeta(name, args, e): if( args != null ) for( a in args ) rec(a); rec(e); + case ECheckType(e,_): rec(e); + case EInfo(info, e): rec(e); } } public static function map( e : Expr, f : Expr -> Expr ) { var edef = switch( expr(e) ) { - case EConst(_), EIdent(_), EBreak, EContinue: expr(e); - case EVar(n, t, e, p, s): EVar(n, t, if( e != null ) f(e) else null, p, s); - case EParent(e, no): EParent(f(e), no); - case EBlock(el): EBlock([for( e in el ) f(e)]); - case EField(e, fi, s): EField(f(e),fi,s); - case EBinop(op, e1, e2): EBinop(op, f(e1), f(e2)); - case EUnop(op, pre, e): EUnop(op, pre, f(e)); - case ECall(e, args): ECall(f(e),[for( a in args ) f(a)]); - case EIf(c, e1, e2): EIf(f(c),f(e1),if( e2 != null ) f(e2) else null); - case EWhile(c, e): EWhile(f(c),f(e)); - case EDoWhile(c, e): EDoWhile(f(c),f(e)); - case EFor(v, it, e): EFor(v, f(it), f(e)); - case EForKeyValue(v, it, e, ithv): EForKeyValue(v, f(it), f(e), ithv); - case EFunction(args, e, name, t, p, s, o): EFunction(args, f(e), name, t, p, s, o); - case EReturn(e): EReturn(if( e != null ) f(e) else null); - case EArray(e, i): EArray(f(e),f(i)); - case EMapDecl(type, keys, values): EMapDecl(type, [for( e in keys ) f(e)], [for( e in values ) f(e)]); - case EArrayDecl(el): EArrayDecl([for( e in el ) f(e)]); - case ENew(cl,el): ENew(cl,[for( e in el ) f(e)]); - case EThrow(e): EThrow(f(e)); - case ETry(e, v, t, c): ETry(f(e), v, t, f(c)); - case EObject(fl): EObject([for( fi in fl ) new ObjectField(fi.name, f(fi.e))]); - case ETernary(c, e1, e2): ETernary(f(c), f(e1), f(e2)); - case ESwitch(e, cases, def): ESwitch(f(e), [for( c in cases ) new SwitchCase([for( v in c.values ) f(v)], f(c.expr))], def == null ? null : f(def)); - case EMeta(name, args, e): EMeta(name, args == null ? null : [for( a in args ) f(a)], f(e)); - case ECheckType(e,t): ECheckType(f(e), t); - case EImport(c, m): EImport(c, m); - case EClass(name, el, extend, interfaces): EClass(name, [for( e in el ) f(e)], extend, interfaces); + case EConst(_), EIdent(_), EBreak, EContinue: expr(e); + case EVar(n, t, e, p, s): EVar(n, t, if( e != null ) f(e) else null, p, s); + case EParent(e, no): EParent(f(e), no); + case EBlock(el): EBlock([for( e in el ) f(e)]); + case EField(e, fi, s): EField(f(e),fi,s); + case EBinop(op, e1, e2): EBinop(op, f(e1), f(e2)); + case EUnop(op, pre, e): EUnop(op, pre, f(e)); + case ECall(e, args): ECall(f(e),[for( a in args ) f(a)]); + case EIf(c, e1, e2): EIf(f(c),f(e1),if( e2 != null ) f(e2) else null); + case EWhile(c, e): EWhile(f(c),f(e)); + case EDoWhile(c, e): EDoWhile(f(c),f(e)); + case EFor(v, it, e): EFor(v, f(it), f(e)); + case EForKeyValue(v, it, e, ithv): EForKeyValue(v, f(it), f(e), ithv); + case EFunction(args, e, name, t, p, s, o): EFunction(args, f(e), name, t, p, s, o); + case EReturn(e): EReturn(if( e != null ) f(e) else null); + case EArray(e, i): EArray(f(e),f(i)); + case EMapDecl(type, keys, values): EMapDecl(type, [for( e in keys ) f(e)], [for( e in values ) f(e)]); + case EArrayDecl(el): EArrayDecl([for( e in el ) f(e)]); + case ENew(cl,el): ENew(cl,[for( e in el ) f(e)]); + case EThrow(e): EThrow(f(e)); + case ETry(e, v, t, c): ETry(f(e), v, t, f(c)); + case EObject(fl): EObject([for( fi in fl ) new ObjectField(fi.name, f(fi.e))]); + case ETernary(c, e1, e2): ETernary(f(c), f(e1), f(e2)); + case ESwitch(e, cases, def): ESwitch(f(e), [for( c in cases ) new SwitchCase([for( v in c.values ) f(v)], f(c.expr))], def == null ? null : f(def)); + case EMeta(name, args, e): EMeta(name, args == null ? null : [for( a in args ) f(a)], f(e)); + case ECheckType(e,t): ECheckType(f(e), t); + case EImport(c, m): EImport(c, m); + case EClass(name, el, extend, interfaces): EClass(name, [for( e in el ) f(e)], extend, interfaces); + case EInfo(info, e): EInfo(info, f(e)); } return mk(edef, e); } @@ -133,6 +177,55 @@ class Tools { } } + public static function getOpEnum(op:String):Binop { + return switch(op) { + case "+": OpAdd; + case "-": OpSub; + case "*": OpMult; + case "/": OpDiv; + case "%": OpMod; + case "&": OpAnd; + case "|": OpOr; + case "^": OpXor; + case "<<": OpShl; + case ">>": OpShr; + case ">>>": OpUShr; + case "==": OpEq; + case "!=": OpNotEq; + case ">=": OpGte; + case "<=": OpLte; + case ">": OpGt; + case "<": OpLt; + case "||": OpBoolOr; + case "&&": OpBoolAnd; + case "is": OpIs; + case "=": OpAssign; + case "=>": OpArrow; + case "??": OpNullCoal; + case "...": OpInterval; + default: { + var op2 = op.substr(0, op.length - 1); + trace(op + " -> " + op2); + if(isValidBinOp(op2)) { + return OpAssignOp(getOpEnum(op2)); + } + throw "Unknown binary operator: " + op; + } + } + } + + public static function getUnopEnum(op:String):Unop { + return switch(op) { + case "++": OpIncrement; + case "--": OpDecrement; + case "!": OpNot; + case "-": OpNeg; + case "~": OpNegBits; + case "...": OpSpread; + default: throw "Unknown unary operator: " + op; + } + } + static var priorities = [ ["%"], ["*", "/"], diff --git a/tests/src/Util.hx b/tests/src/Util.hx index 849e976..f9dab57 100644 --- a/tests/src/Util.hx +++ b/tests/src/Util.hx @@ -210,6 +210,7 @@ class Util { // Print stack trace var stack = haxe.CallStack.toString(haxe.CallStack.exceptionStack()); Sys.println("## Stack trace: " + stack); + Sys.exit(1); } return result; } diff --git a/tests/src/tests/FinalCase.hx b/tests/src/tests/FinalCase.hx index 74acc3f..8385286 100644 --- a/tests/src/tests/FinalCase.hx +++ b/tests/src/tests/FinalCase.hx @@ -27,6 +27,7 @@ class FinalCase extends TestCase { assertDisplay("(i - x) * 30 + 90", ((i-x) * 30) + 90); assertDisplay("i - x * 30 + 90", i-x * 30 + 90); + /* // Test operator precedence assertDisplay("i / x * 30 + 90", i / x * 30 + 90); assertDisplay("i / (x * 30) + 90", i / (x * 30) + 90); @@ -44,6 +45,7 @@ class FinalCase extends TestCase { bloomShader.dim = dim = .8 + (.3 * FlxMath.fastSin(__totalTime)); bloomShader.size = size = 18 + (8 * FlxMath.fastSin(__totalTime)); '); + */ Util.parse('sprite.setPosition(codesList.x + (sprite.ID % 2 == 1 ? 240 : 67),codesList.y + (15 * sprite.ID) + (sprite.ID%2 == 1 ? 35 : 54));'); @@ -74,7 +76,6 @@ class FinalCase extends TestCase { assertEq("[for(i in 0...10) if(i % 2 == 0) i => isEven(i) else i => isEven(i)]", [for(i in 0...10) if(i % 2 == 0) i => isEven(i) else i => isEven(i)]); assertEq("[for(i in 0...10) if(i % 2 == 0) isEven(i) else isEven(i)]", [for(i in 0...10) if(i % 2 == 0) isEven(i) else isEven(i)]); - headerCode = "function area(a:Int, b:Int):Int { return a * b; };"; function area(a:Int, b:Int):Int { return a * b; }