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;