From 9b9f6f34328feb2687dd4eeb37b7410ea34412a6 Mon Sep 17 00:00:00 2001 From: Nicolas Cannasse Date: Sun, 7 Sep 2008 15:01:41 +0000 Subject: [PATCH] Added new, throw, try/catch, hex numbers and ... operator support --- Test.hx | 91 +++++++++++++++++++-------------- haxelib.xml | 2 +- hscript/Expr.hx | 3 ++ hscript/Interp.hx | 78 +++++++++++++++++++++-------- hscript/Parser.hx | 125 +++++++++++++++++++++++++++++++++++++++------- 5 files changed, 221 insertions(+), 78 deletions(-) diff --git a/Test.hx b/Test.hx index ddba781..8d89c76 100644 --- a/Test.hx +++ b/Test.hx @@ -1,46 +1,61 @@ class Test { - static function main() { - var script = " - var sum = 0; - // blablabla - for( a in angles ) - sum += Math.cos(a); - sum; - "; + static function test(x,v:Dynamic,?vars : Dynamic) { var p = new hscript.Parser(); - var program = p.parseString(script); + var program = p.parseString(x); var interp = new hscript.Interp(); - interp.variables.set("Math",Math); // share the Math class - interp.variables.set("angles",[0,1,2,3]); // set the angles list - trace( interp.execute(program) ); + if( vars != null ) + for( v in Reflect.fields(vars) ) + interp.variables.set(v,Reflect.field(vars,v)); + var ret : Dynamic = interp.execute(program); + if( v != ret ) throw ret+" returned while "+v+" expected"; + } - var script = " - var sum = 0; - function foo(a) { - return Math.cos(a); - } - function bar(x) { - sum += foo(x); - } - for( x in angles ) - bar(x); - sum; - "; - var program = p.parseString(script); - trace( interp.execute(program) ); - - var script = " - var angles = [0,1,2,3]; - var i = 0; - var sum = 0; - /* comment */ - while( i < angles.length ) - sum += Math.cos(angles[i++]); - sum; - "; - var program = p.parseString(script); - trace( interp.execute(program) ); + static function main() { + test("0",0); + test("0xFF",255); + test("-123",-123); + test("- 123",-123); + test("1.546",1.546); + test("'bla'","bla"); + test("null",null); + test("true",true); + test("false",false); + test("1 == 2",false); + test("1.3 == 1.3",true); + test("5 > 3",true); + test("0 < 0",false); + test("-1 <= -1",true); + test("1 + 2",3); + test("'abc' + 55","abc55"); + test("'abc' + 'de'","abcde"); + test("-1 + 2",1); + test("1 / 5",0.2); + test("3 * 2 + 5",11); + test("3 * (2 + 5)",21); + test("3 * 2 // + 5 \n + 6",12); + test("3 /* 2\n */ + 5",8); + test("x",55,{ x : 55 }); + test("var y = 33; y",33); + test("{ 1; 2; 3; }",3); + test("{ var x = 0; } x",55,{ x : 55 }); + test("o.val",55,{ o : { val : 55 } }); + test("o.val",null,{ o : {} }); + test("var a = 1; a++",1); + test("var a = 1; a++; a",2); + test("var a = 1; ++a",2); + test("var a = 1; a *= 3",3); + test("add(1,2)",3,{ add : function(x,y) return x + y }); + test("a.push(5); a.pop() + a.pop()",8,{ a : [3] }); + test("if( true ) 1 else 2",1); + test("if( false ) 1 else 2",2); + test("var t = 0; for( x in [1,2,3] ) t += x; t",6); + test("(function(a,b) return a + b)(4,5)",9); + test("var y = 0; var add = function(a) y += a; add(5); add(3); y", 8); + test("var a = [1,[2,[3,[4,null]]]]; var t = 0; while( a != null ) { t += a[0]; a = a[1]; }; t",10); + test("var t = 0; for( x in 1...10 ) t += x; t",45); + test("var t = 0; for( x in new IntIter(1,10) ) t +=x; t",45,{ IntIter : IntIter }); + test("var x = 1; try { var x = 66; throw 789; } catch( e : Dynamic ) e + x",790); } } \ No newline at end of file diff --git a/haxelib.xml b/haxelib.xml index 4800f16..cf6866c 100644 --- a/haxelib.xml +++ b/haxelib.xml @@ -1,5 +1,5 @@ haXe Script is a scripting engine for a subset of the haXe language - Fixed bugs in flash9 + Added new, throw, try/catch, hex numbers and ... operator support \ No newline at end of file diff --git a/hscript/Expr.hx b/hscript/Expr.hx index 96bd173..164d2e6 100644 --- a/hscript/Expr.hx +++ b/hscript/Expr.hx @@ -49,6 +49,9 @@ enum Expr { EReturn( ?e : Expr ); EArray( e : Expr, index : Expr ); EArrayDecl( e : Array ); + ENew( cl : String, params : Array ); + EThrow( e : Expr ); + ETry( e : Expr, v : String, ecatch : Expr ); } enum Error { diff --git a/hscript/Interp.hx b/hscript/Interp.hx index b1a4043..aaef612 100644 --- a/hscript/Interp.hx +++ b/hscript/Interp.hx @@ -40,6 +40,7 @@ class Interp { public function new() { locals = new Hash(); + declared = new Array(); variables = new Hash(); variables.set("null",null); variables.set("true",true); @@ -70,6 +71,7 @@ class Interp { binops.set("||",function(e1,e2) return me.expr(e1) == true || me.expr(e2) == true); binops.set("&&",function(e1,e2) return me.expr(e1) == true && me.expr(e2) == true); binops.set("=",assign); + binops.set("...",function(e1,e2) return new IntIter(me.expr(e1),me.expr(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); @@ -191,16 +193,11 @@ class Interp { return h2; } - function block( exprs : Array ) : Dynamic { - var old = declared; - declared = new Array(); - var v = null; - for( e in exprs ) - v = expr(e); - for( d in declared ) + function restore( old : Int ) { + while( declared.length > old ) { + var d = declared.pop(); locals.set(d.n,d.old); - declared = old; - return v; + } } public function expr( e : Expr ) : Dynamic { @@ -220,13 +217,18 @@ class Interp { throw Error.EUnknownVariable(id); return v; case EVar(n,e): - declared.unshift({ n : n, old : locals.get(n) }); + declared.push({ n : n, old : locals.get(n) }); locals.set(n,{ r : (e == null)?null:expr(e) }); return null; case EParent(e): return expr(e); case EBlock(exprs): - return block(exprs); + var old = declared.length; + var v = null; + for( e in exprs ) + v = expr(e); + restore(old); + return v; case EField(e,f): return get(expr(e),f); case EBinop(op,e1,e2): @@ -296,14 +298,40 @@ class Interp { return a; case EArray(e,index): return expr(e)[expr(index)]; + case ENew(cl,params): + var a = new Array(); + for( e in params ) + a.push(expr(e)); + return cnew(cl,a); + case EThrow(e): + throw expr(e); + case ETry(e,n,ecatch): + var old = declared.length; + try { + var v : Dynamic = expr(e); + restore(old); + return v; + } catch( err : Stop ) { + throw err; + } catch( err : Dynamic ) { + // restore vars + restore(old); + // declare 'v' + declared.push({ n : n, old : locals.get(n) }); + locals.set(n,{ r : err }); + var v : Dynamic = expr(ecatch); + restore(old); + return v; + } } return null; } function whileLoop(econd,e) { + var old = declared.length; while( expr(econd) == true ) { try { - block([e]); + expr(e); } catch( err : Stop ) { switch(err) { case SContinue: @@ -312,17 +340,23 @@ class Interp { } } } + restore(old); } - function forLoop(v,it,e) { - var old = locals.get(v); - var it : Dynamic = expr(it); - if( it.iterator != null ) it = it.iterator(); - if( it.hasNext == null || it.next == null ) throw Error.EInvalidIterator(v); + function makeIterator( v : Dynamic ) : Iterator { + try v = v.iterator() catch( e : Dynamic ) {}; + if( v.hasNext == null || v.next == null ) throw Error.EInvalidIterator(v); + return v; + } + + function forLoop(n,it,e) { + var old = declared.length; + declared.push({ n : n, old : locals.get(n) }); + var it = makeIterator(expr(it)); while( it.hasNext() ) { - locals.set(v,{ r : it.next() }); + locals.set(n,{ r : it.next() }); try { - block([e]); + expr(e); } catch( err : Stop ) { switch( err ) { case SContinue: @@ -331,7 +365,7 @@ class Interp { } } } - locals.set(v,old); + restore(old); } function get( o : Dynamic, f : String ) : Dynamic { @@ -349,4 +383,8 @@ class Interp { return Reflect.callMethod(o,f,args); } + function cnew( cl : String, args : Array ) : Dynamic { + return Type.createInstance(Type.resolveClass(cl),args); + } + } \ No newline at end of file diff --git a/hscript/Parser.hx b/hscript/Parser.hx index 9879da2..eb1ca7b 100644 --- a/hscript/Parser.hx +++ b/hscript/Parser.hx @@ -64,6 +64,7 @@ class Parser { opChars = "+*/-=!><&|^%"; identChars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_"; opPriority = [ + "...", "=", "||","&&", "==","!=",">","<",">=","<=", @@ -162,7 +163,7 @@ class Parser { var found; for( x in unopsPrefix ) if( x == op ) - return EUnop(op,true,parseExpr(s)); + return makeUnop(op,parseExpr(s)); return unexpected(tk); case TBkOpen: return parseExprNext(s,EArrayDecl(parseExprList(s,TBkClose))); @@ -171,6 +172,29 @@ class Parser { } } + function priority(op) { + for( i in 0...opPriority.length ) + if( opPriority[i] == op ) + return i; + return -1; + } + + function makeUnop( op, e ) { + return switch( e ) { + case EBinop(bop,e1,e2): EBinop(bop,makeUnop(op,e1),e2); + default: EUnop(op,true,e); + } + } + + function makeBinop( op, e1, e2 ) { + return switch( e2 ) { + case EBinop(op2,e2,e3): + if( priority(op) > priority(op2) ) + return EBinop(op2,makeBinop(op,e1,e2),e3); + default: EBinop(op,e1,e2); + } + } + function parseStructure( s, id ) { return switch( id ) { case "if": @@ -256,17 +280,54 @@ class Parser { var tk = token(s); tokens.add(tk); EReturn(if( tk == TSemicolon ) null else parseExpr(s)); - default: null; + case "new": + var a = new Array(); + var tk = token(s); + switch( tk ) { + case TId(id): a.push(id); + default: unexpected(tk); + } + while( true ) { + tk = token(s); + switch( tk ) { + case TDot: + tk = token(s); + switch(tk) { + case TId(id): a.push(id); + default: unexpected(tk); + } + case TPOpen: + break; + default: + unexpected(tk); + } + } + ENew(a.join("."),parseExprList(s,TPClose)); + case "throw": + EThrow( parseExpr(s) ); + case "try": + var e = parseExpr(s); + var tk = token(s); + if( !Type.enumEq(tk,TId("catch")) ) unexpected(tk); + tk = token(s); + if( tk != TPOpen ) unexpected(tk); + tk = token(s); + var vname = switch( tk ) { + case TId(id): id; + default: unexpected(tk); + } + tk = token(s); + if( tk != TDoubleDot ) unexpected(tk); + tk = token(s); + if( !Type.enumEq(tk,TId("Dynamic")) ) unexpected(tk); + tk = token(s); + if( tk != TPClose ) unexpected(tk); + ETry(e,vname,parseExpr(s)); + default: + null; } } - function priority(op) { - for( i in 0...opPriority.length ) - if( opPriority[i] == op ) - return i; - return -1; - } - function parseExprNext( s : haxe.io.Input, e1 : Expr ) { var tk = token(s); switch( tk ) { @@ -274,14 +335,7 @@ class Parser { for( x in unopsSuffix ) if( x == op ) return EUnop(op,false,e1); - var e2 = parseExpr(s); - switch( e2 ) { - case EBinop(op2,e2,e3): - if( priority(op) > priority(op2) ) - return EBinop(op2,EBinop(op,e1,e2),e3); - default: - } - return EBinop(op,e1,e2); + return makeBinop(op,e1,parseExpr(s)); case TDot: tk = token(s); var field = null; @@ -392,9 +446,33 @@ class Parser { case 48,49,50,51,52,53,54,55,56,57: n = n * 10 + (char - 48); case 46: - if( exp > 0 ) + if( exp > 0 ) { + // in case of '...' + if( exp == 10 && readChar(s) == 46 ) { + tokens.add(TOp("...")); + return TConst( CInt(n) ); + } throw Error.EInvalidChar(char); + } exp = 1; + case 120: // x + if( n > 0 || exp > 0 ) + throw Error.EInvalidChar(char); + // read hexa + while( true ) { + char = readChar(s); + switch( char ) { + case 48,49,50,51,52,53,54,55,56,57: // 0-9 + n = (n << 4) + (char - 48); + case 65,66,67,68,69,70: // A-F + n = (n << 4) + (char - 55); + case 97,98,99,100,101,102: // a-f + n = (n << 4) + (char - 87); + default: + this.char = char; + return TConst( CInt(n) ); + } + } default: this.char = char; return TConst( (exp > 0) ? CFloat(n * 10 / exp) : CInt(n) ); @@ -404,7 +482,16 @@ class Parser { case 40: return TPOpen; case 41: return TPClose; case 44: return TComma; - case 46: return TDot; + case 46: + char = readChar(s); + if( char != 46 ) { + this.char = char; + return TDot; + } + char = readChar(s); + if( char != 46 ) + throw Error.EInvalidChar(char); + return TOp("..."); case 123: return TBrOpen; case 125: return TBrClose; case 91: return TBkOpen;