From 6346f82edf36fa35b9c6913244bfebc6ba2ee209 Mon Sep 17 00:00:00 2001 From: NeeEoo Date: Tue, 23 Apr 2024 15:02:15 +0200 Subject: [PATCH] Turned stuffs to classes to reduce reflection --- hscript/Expr.hx | 43 +++++++--- hscript/Interp.hx | 2 + hscript/Parser.hx | 44 ++++++----- hscript/Tools.hx | 13 ++- tests/src/HScriptRunner.hx | 13 +++ tests/src/Util.hx | 11 ++- tests/src/benchmark/TestBenchmark.hx | 4 +- tests/src/tests/MiscCase.hx | 114 +++++++++++++++++++++++++++ tests/src/tests/SwitchCase.hx | 1 + tests/src/tests/TestCase.hx | 16 +++- 10 files changed, 217 insertions(+), 44 deletions(-) diff --git a/hscript/Expr.hx b/hscript/Expr.hx index b8c43dc..00384ad 100644 --- a/hscript/Expr.hx +++ b/hscript/Expr.hx @@ -31,18 +31,26 @@ enum Const { } #if hscriptPos -typedef Expr = { - var e : ExprDef; - var pmin : Int; - var pmax : Int; - var origin : String; - var line : Int; +class Expr { + public var e : ExprDef; + public var pmin : Int; + public var pmax : Int; + public var origin : String; + public var line : Int; + public function new(e, pmin, pmax, origin, line) { + this.e = e; + this.pmin = pmin; + this.pmax = pmax; + this.origin = origin; + this.line = line; + } } -enum ExprDef { +enum ExprDef #else typedef ExprDef = Expr; -enum Expr { +enum Expr #end +{ EConst( c : Const ); EIdent( v : String ); EVar( n : String, ?t : CType, ?e : Expr, ?isPublic : Bool, ?isStatic : Bool ); @@ -75,7 +83,19 @@ enum Expr { EClass( name:String, fields:Array, ?extend:String, interfaces:Array ); } -typedef Argument = { name : String, ?t : CType, ?opt : Bool, ?value : Expr }; +//typedef Argument = { name : String, ?t : CType, ?opt : Bool, ?value : Expr }; +class Argument { + public var name : String; + public var t : Null; + public var opt : Bool; + public var value : Null; + public function new(name, ?t, ?opt, ?value) { + this.name = name; + this.t = t; + this.opt = opt; + this.value = value; + } +} typedef Metadata = Array<{ name : String, params : Array }>; @@ -106,10 +126,11 @@ class Error { return Printer.errorToString(this); } } -enum ErrorDef { +enum ErrorDef #else -enum Error { +enum Error #end +{ EInvalidChar( c : Int ); EUnexpected( s : String ); EUnterminatedString; diff --git a/hscript/Interp.hx b/hscript/Interp.hx index 3bcf83e..bd3fa52 100644 --- a/hscript/Interp.hx +++ b/hscript/Interp.hx @@ -631,8 +631,10 @@ class Interp { 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); case EUnop(op, prefix, e): switch (op) { diff --git a/hscript/Parser.hx b/hscript/Parser.hx index 0655977..c34c93a 100644 --- a/hscript/Parser.hx +++ b/hscript/Parser.hx @@ -269,7 +269,7 @@ class Parser { if( e == null ) return null; if( pmin == null ) pmin = tokenMin; if( pmax == null ) pmax = tokenMax; - return { e : e, pmin : pmin, pmax : pmax, origin : origin, line : line }; + return new Expr(e, pmin, pmax, origin, line); #else return e; #end @@ -389,14 +389,14 @@ class Parser { return parseExprNext(mk(ECheckType(e,t),p1,tokenMax)); case TComma: switch( expr(e) ) { - case EIdent(v): return parseLambda([{ name : v, t : t }], pmin(e)); + case EIdent(v): return parseLambda([new Argument(v, t)], pmin(e)); default: } default: } case TComma: switch( expr(e) ) { - case EIdent(v): return parseLambda([{name:v}], pmin(e)); + case EIdent(v): return parseLambda([new Argument(v)], pmin(e)); default: } default: @@ -504,7 +504,7 @@ class Parser { while( true ) { var id = getIdent(); var t = maybe(TDoubleDot) ? parseType() : null; - args.push({ name : id, t : t }); + args.push(new Argument(id, t)); var tk = token(); switch( tk ) { case TComma: @@ -569,28 +569,30 @@ class Parser { if( e == null && resumeErrors ) return null; return switch( 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)); - default: mk(EUnop(op,true,e),pmin(e),pmax(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)); + default: mk(EUnop(op,true,e),pmin(e),pmax(e)); } } function makeBinop( op, e1, e ) { + if(!Tools.isValidBinOp(op)) + error(EInvalidOp(op),pmin(e1),pmax(e1)); if( e == null && resumeErrors ) return mk(EBinop(op,e1,e),pmin(e1),pmax(e1)); return switch( 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)); - else - mk(EBinop(op, e1, e), pmin(e1), pmax(e)); - case ETernary(e2,e3,e4): - if( opRightAssoc.exists(op) ) + 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)); + else + mk(EBinop(op, e1, e), pmin(e1), pmax(e)); + case ETernary(e2,e3,e4): + if( opRightAssoc.exists(op) ) + mk(EBinop(op,e1,e),pmin(e1),pmax(e)); + else + mk(ETernary(makeBinop(op, e1, e2), e3, e4), pmin(e1), pmax(e)); + default: mk(EBinop(op,e1,e),pmin(e1),pmax(e)); - else - mk(ETernary(makeBinop(op, e1, e2), e3, e4), pmin(e1), pmax(e)); - default: - mk(EBinop(op,e1,e),pmin(e1),pmax(e)); } } @@ -1045,10 +1047,10 @@ class Parser { switch( expr(e1) ) { case EIdent(i), EParent(expr(_) => EIdent(i)): var eret = parseExpr(); - return mk(EFunction([{ name : i }], mk(EReturn(eret),pmin(eret))), pmin(e1)); + return mk(EFunction([new Argument(i)], mk(EReturn(eret),pmin(eret))), pmin(e1)); case ECheckType(expr(_) => EIdent(i), t): var eret = parseExpr(); - return mk(EFunction([{ name : i, t : t }], mk(EReturn(eret),pmin(eret))), pmin(e1)); + return mk(EFunction([new Argument(i, t)], mk(EReturn(eret),pmin(eret))), pmin(e1)); default: } unexpected(tk); @@ -1106,7 +1108,7 @@ class Parser { unexpected(tk); break; } - var arg : Argument = { name : name }; + var arg : Argument = new Argument(name); args.push(arg); if( opt ) arg.opt = true; if( allowTypes ) { diff --git a/hscript/Tools.hx b/hscript/Tools.hx index e2fd52b..92c03fe 100644 --- a/hscript/Tools.hx +++ b/hscript/Tools.hx @@ -103,12 +103,21 @@ class Tools { #end } - public static inline function mk( e : ExprDef, p : Expr ) { + public static inline function mk( e : ExprDef, p : Expr ):Expr { #if hscriptPos - return { e : e, pmin : p.pmin, pmax : p.pmax, origin : p.origin, line : p.line }; + return new Expr(e, p.pmin, p.pmax, p.origin, p.line); #else return e; #end } + public static function isValidBinOp(op:String):Bool { + if(op == ("??"+"=")) return true; + return switch(op) { + case "+" | "-" | "*" | "/" | "%" | "&" | "|" | "^" | "<<" | ">>" | ">>>" | "==" | "!=" | ">=" | "<=" | ">" | "<" | "||" | "&&" | "is" | "=" | "??" | "..." | "+=" | "-=" | "*=" | "/=" | "%=" | "&=" | "|=" | "^=" | "<<=" | ">>=" | ">>>=": true; + case "=>": true; + default: false; + } + } + } \ No newline at end of file diff --git a/tests/src/HScriptRunner.hx b/tests/src/HScriptRunner.hx index 1056615..1672169 100644 --- a/tests/src/HScriptRunner.hx +++ b/tests/src/HScriptRunner.hx @@ -33,4 +33,17 @@ class HScriptRunner { else return interp.exprReturn(expr); } + + public function executeWithVars(script:String, vars:Dynamic):Dynamic { + var interp = clearPrevious ? getNewInterp() : this.interp; + var expr = Util.parse(headerCode + script + tailCode); + if(expr == null) + return "ERROR"; + for(v in Reflect.fields(vars)) + interp.variables.set(v, Reflect.field(vars, v)); + if (clearPrevious) + return interp.execute(expr); + else + return interp.exprReturn(expr); + } } \ No newline at end of file diff --git a/tests/src/Util.hx b/tests/src/Util.hx index 69847a9..d70ce8f 100644 --- a/tests/src/Util.hx +++ b/tests/src/Util.hx @@ -3,16 +3,17 @@ import hscript.Expr.Error; using StringTools; class Util { - public static function assert(value:Bool, message:String) { + public static function assert(value:Bool, message:String, ?pos:haxe.PosInfos) { if (value) { passedTestUnits++; } else { Sys.println("Assertion failed: " + message); + Sys.println("> At " + pos.fileName + ":" + pos.lineNumber); failedTestUnits++; } } - public static function assertEq(value:Dynamic, expected:Dynamic, message:String) { + public static function assertEq(value:Dynamic, expected:Dynamic, message:String, ?pos:haxe.PosInfos) { var passed = value == expected; if (Std.isOfType(value, Array) && Std.isOfType(expected, Array)) { if (deepCompareArrays(value, expected)) @@ -27,11 +28,12 @@ class Util { passedTestUnits++; } else { Sys.println("Assertion failed: " + message + " Expected: " + expected + " Got: " + value); + Sys.println("> At " + pos.fileName + ":" + pos.lineNumber); failedTestUnits++; } } - public static function assertNeq(value:Dynamic, expected:Dynamic, message:String) { + public static function assertNeq(value:Dynamic, expected:Dynamic, message:String, ?pos:haxe.PosInfos) { var passed = value != expected; if (Std.isOfType(value, Array) && Std.isOfType(expected, Array)) { if (!deepCompareArrays(value, expected)) @@ -44,6 +46,7 @@ class Util { passedTestUnits++; } else { Sys.println("Assertion failed: " + message + " Expected: " + expected + " Got: " + value); + Sys.println("> At " + pos.fileName + ":" + pos.lineNumber); failedTestUnits++; } } @@ -154,7 +157,7 @@ class Util { var err = error.toString(); if (err.startsWith(fn)) err = err.substr(fn.length); - Sys.println(fn + err); + Sys.println("ERROR: " + fn + err); //Logs.traceColored([ // Logs.logText(fn, GREEN), // Logs.logText(err, RED) diff --git a/tests/src/benchmark/TestBenchmark.hx b/tests/src/benchmark/TestBenchmark.hx index 5b20092..8d2853f 100644 --- a/tests/src/benchmark/TestBenchmark.hx +++ b/tests/src/benchmark/TestBenchmark.hx @@ -15,10 +15,10 @@ class TestBenchmark extends Benchmark { public var a:Array = []; public override function haxeBenchmark() { - for (i in 0...1000) a.push(i); + for (i in 0...1000) a.push(i * 2 + 1 / 6); } public override function hscriptBenchmark() { - execute("var a:Array = []; for (i in 0...1000) a.push(i);"); + execute("var a:Array = []; for (i in 0...1000) a.push(i * 2 + 1 / 6);"); } } \ No newline at end of file diff --git a/tests/src/tests/MiscCase.hx b/tests/src/tests/MiscCase.hx index 4b94246..0c55ed9 100644 --- a/tests/src/tests/MiscCase.hx +++ b/tests/src/tests/MiscCase.hx @@ -5,6 +5,18 @@ class MiscCase extends TestCase { super.setup(); } + override function getNewInterp() { + var interp = super.getNewInterp(); + interp.variables.set("Std", Std); + interp.variables.set("String", String); + interp.variables.set("Bool", Bool); + interp.variables.set("Float", Float); + interp.variables.set("Array", Array); + interp.variables.set("Int", Int); + interp.variables.set("IntIterator", IntIterator); + return interp; + } + override function run() { assertEq("", null); assertEq("true", true); @@ -19,6 +31,108 @@ class MiscCase extends TestCase { var a = 1; var a = 2; assertEq("var a = 1; var a = 2; a", a); + + + + assertEq("0",0); + assertEq("0xFF", 255); + assertEq("0xFF_FF", 0xFFFF); + assertEq("0b101", 5); //assertEq("0b101", 0b101); + #if !(php || python) + #if haxe3 + assertEq("0xBFFFFFFF", 0xBFFFFFFF); + assertEq("0x7FFFFFFF", 0x7FFFFFFF); + #elseif !neko + assertEq("n(0xBFFFFFFF)", 0xBFFFFFFF, { n : haxe.Int32.toNativeInt }); + assertEq("n(0x7FFFFFFF)", 0x7FFFFFFF, { n : haxe.Int32.toNativeInt } ); + #end + #end + assertEq("-123",-123); + assertEq("- 123",-123); + assertEq("1.546",1.546); + assertEq(".545",.545); + assertEq("'bla'","bla"); + assertEq("null",null); + assertEq("true",true); + assertEq("false",false); + assertEq("1 == 2",false); + assertEq("1.3 == 1.3",true); + assertEq("5 > 3",true); + assertEq("0 < 0",false); + assertEq("-1 <= -1",true); + assertEq("1 + 2",3); + assertEq("~545",-546); + assertEq("'abc' + 55","abc55"); + assertEq("'abc' + 'de'","abcde"); + assertEq("-1 + 2",1); + assertEq("1 / 5",0.2); + assertEq("3 * 2 + 5",11); + assertEq("3 * (2 + 5)",21); + assertEq("3 * 2 // + 5 \n + 6",12); + assertEq("3 /* 2\n */ + 5",8); + assertEq("[55,66,77][1]",66); + assertEq("var a = [55]; a[0] *= 2; a[0]",110); + assertEq("x",55,{ x : 55 }); + assertEq("var y = 33; y",33); + assertEq("{ 1; 2; 3; }",3); + assertEq("{ var x = 0; } x",55,{ x : 55 }); + assertEq("o.val",55,{ o : { val : 55 } }); + assertEq("o.val",null,{ o : {} }); + assertEq("var a = 1; a++",1); + assertEq("var a = 1; a++; a",2); + assertEq("var a = 1; ++a",2); + assertEq("var a = 1; a *= 3",3); + assertEq("a = b = 3; a + b",6); + assertEq("add(1,2)",3,{ add : function(x,y) return x + y }); + assertEq("a.push(5); a.pop() + a.pop()",{var a = [3]; a.push(5); a.pop() + a.pop();},{ a : [3] }); + assertEq("if( true ) 1 else 2",1); + assertEq("if( false ) 1 else 2",2); + assertEq("var t = 0; for( x in [1,2,3] ) t += x; t",6); + assertEq("var a = new Array(); for( x in 0...5 ) a[x] = x; a.join('-')","0-1-2-3-4"); + assertEq("(function(a,b) return a + b)(4,5)",9); + assertEq("var y = 0; var add = function(a) y += a; add(5); add(3); y", {var y = 0; var add = function(a) y += a; add(5); add(3); y;}); + assertEq("var a = [1,[2,[3,[4,null]]]]; var t = 0; while( a != null ) { t += a[0]; a = a[1]; }; t",10); + assertEq("var a = false; do { a = true; } while (!a); a;",true); + assertEq("var t = 0; for( x in 1...10 ) t += x; t", 45); + #if haxe3 + assertEq("var t = 0; for( x in new IntIterator(1,10) ) t +=x; t", 45); + #else + assertEq("var t = 0; for( x in new IntIter(1,10) ) t +=x; t", 45); + #end + assertEq("var x = 1; try { var x = 66; throw 789; } catch( e : Dynamic ) e + x",{var x = 1; try { var x = 66; throw 789; } catch( e : Dynamic ) e + x;}); + assertEq("var x = 1; var f = function(x) throw x; try f(55) catch( e : Dynamic ) e + x",{ + var x = 1; + var f:Dynamic = function(x) throw x; + try + f(55) + catch( e:Dynamic ) + e + x; + }); + assertEq("var i=2; if( true ) --i; i",1); + assertEq("var i=0; if( i++ > 0 ) i=3; i",1); + assertEq("var a = 5/2; a",2.5); + assertEq("{ x = 3; x; }", 3); + assertEq("{ x : 3, y : {} }.x", 3); + assertEq("function bug(){ \n }\nbug().x", null); + assertEq("1 + 2 == 3", true); + assertEq("-2 == 3 - 5", true); + assertEq("var x=-3; x", -3); + assertEq("var a:Array=[1,2,4]; a[2]", 4); + assertEq("/**/0", 0); + assertEq("x=1;x*=-2", -2); + assertEq("var f = x -> x + 1; f(3)", 4); + assertEq("var f = () -> 55; f()", 55); + assertEq("var f = (x) -> x + 1; f(3)", 4); + assertEq("var f = (x:Int) -> x + 1; f(3)", 4); + assertEq("var f = (x,y) -> x + y; f(3,1)", 4); + assertEq("var f = (x,y:Int) -> x + y; f(3,1)", 4); + assertEq("var f = (x:Int,y:Int) -> x + y; f(3,1)", 4); + assertEq("var f:Int->Int->Int = (x:Int,y:Int) -> x + y; f(3,1)", {var f:Int->Int->Int = (x:Int,y:Int) -> x + y; f(3,1);}); + assertEq("var f:(x:Int, y:Int)->Int = (x:Int,y:Int) -> x + y; f(3,1)", {var f:(x:Int, y:Int)->Int = (x:Int,y:Int) -> x + y; f(3,1);}); + //assertEq("var f:(x:Int)->(y:Int, z:Int)->Int = (x:Int) -> (y:Int, z:Int) -> x + y + z; f(3)(1, 2)", {var f:(x:Int)->(y:Int, z:Int)->Int = (x:Int) -> (y:Int, z:Int) -> x + y + z; f(3)(1, 2);}); + //assertEq("var f:(x:Int)->(Int, Int)->Int = (x:Int) -> (y:Int, z:Int) -> x + y + z; f(3)(1, 2)", {var f:(x:Int)->(Int, Int)->Int = (x:Int) -> (y:Int, z:Int) -> x + y + z; f(3)(1, 2);}); + assertEq("var a = 10; var b = 5; a - -b", 15); + assertEq("var a = 10; var b = 5; a - b / 2", 7.5); } override function teardown() { diff --git a/tests/src/tests/SwitchCase.hx b/tests/src/tests/SwitchCase.hx index 6851c8f..3080027 100644 --- a/tests/src/tests/SwitchCase.hx +++ b/tests/src/tests/SwitchCase.hx @@ -24,6 +24,7 @@ class SwitchCase extends TestCase { assertEq("switch(5) { case 1|4: 'error'; case 5: 0; default: -1; }", switch(5) { case 1|4: 'error'; case 5: 0; default: -1; }); assertEq("switch(5) { case 1|4: 'error'; default: -1; }", switch(5) { case 1|4: 'error'; default: -1; }); + assertEq("switch(5) { case (1|4): 'error'; default: -1; }", switch(5) { case (1|4): 'error'; default: -1; }); assertEq("switch(5) { case 1,4: 'error'; case 5: 0; default: -1; }", switch(5) { case 1,4: 'error'; case 5: 0; default: -1; }); } diff --git a/tests/src/tests/TestCase.hx b/tests/src/tests/TestCase.hx index 5f34042..7089c1b 100644 --- a/tests/src/tests/TestCase.hx +++ b/tests/src/tests/TestCase.hx @@ -3,15 +3,23 @@ package tests; @:access(hscript.Interp) @:access(hscript.Parser) class TestCase extends HScriptRunner { - public function assertEq(script:String, expected:Dynamic, ?message:String) { + public function assertEq(script:String, expected:Dynamic, ?message:String, ?vars:Dynamic, ?pos:haxe.PosInfos) { if(message == null) message = script; - Util.assertEq(execute(headerCode + script + tailCode), expected, message); + var result = if(vars != null) + executeWithVars(headerCode + script + tailCode, vars); + else + execute(headerCode + script + tailCode); + Util.assertEq(result, expected, message, pos); } - public function assertNeq(script:String, expected:Dynamic, ?message:String) { + public function assertNeq(script:String, expected:Dynamic, ?message:String, ?vars:Dynamic, ?pos:haxe.PosInfos) { if(message == null) message = script; - Util.assertNeq(execute(headerCode + script + tailCode), expected, message); + var result = if(vars != null) + executeWithVars(headerCode + script + tailCode, vars); + else + execute(headerCode + script + tailCode); + Util.assertNeq(result, expected, message, pos); } } \ No newline at end of file