diff --git a/Test.hx b/Test.hx index fae6704..8f5c5c5 100644 --- a/Test.hx +++ b/Test.hx @@ -70,6 +70,8 @@ class Test { test("var i=2; if( true ) --i; i",1); test("var i=0; if( i++ > 0 ) i=3; i",1); test("var a = 5/2; a",2.5); + test("{ x = 3; x; }", 3); + test("{ x : 3, y : {} }.x",3); trace("Done"); } diff --git a/haxelib.xml b/haxelib.xml index 85216bb..69d5369 100644 --- a/haxelib.xml +++ b/haxelib.xml @@ -2,5 +2,5 @@ haXe Script is a scripting engine for a subset of the haXe language - Added Int32 hexadecimal support + Added anonymous object support \ No newline at end of file diff --git a/hscript.hxml b/hscript.hxml index 49c996a..33bc917 100644 --- a/hscript.hxml +++ b/hscript.hxml @@ -1,5 +1,5 @@ # HScript --neko hscript.n +-swf9 hscript.swf -main Test -debug diff --git a/hscript.hxp b/hscript.hxp index aa8d369..d20eb74 100644 --- a/hscript.hxp +++ b/hscript.hxp @@ -1,5 +1,5 @@  - + diff --git a/hscript/Bytes.hx b/hscript/Bytes.hx index b68bc21..32ad69a 100644 --- a/hscript/Bytes.hx +++ b/hscript/Bytes.hx @@ -205,6 +205,12 @@ class Bytes { doEncode(e); doEncodeString(v); doEncode(ecatch); + case EObject(fl): + bout.addByte(fl.length); + for( f in fl ) { + doEncodeString(f.name); + doEncode(f.e); + } } } @@ -285,6 +291,14 @@ class Bytes { var e = doDecode(); var v = doDecodeString(); ETry(e,v,doDecode()); + case 21: + var fl = new Array(); + for( i in 0...bin.get(pin++) ) { + var name = doDecodeString(); + var e = doDecode(); + fl.push({ name : name, e : e }); + } + EObject(fl); case 255: null; default: diff --git a/hscript/Expr.hx b/hscript/Expr.hx index 7684070..8f4fd2a 100644 --- a/hscript/Expr.hx +++ b/hscript/Expr.hx @@ -53,6 +53,7 @@ enum Expr { ENew( cl : String, params : Array ); EThrow( e : Expr ); ETry( e : Expr, v : String, ecatch : Expr ); + EObject( fl : Array<{ name : String, e : Expr }> ); } enum Error { diff --git a/hscript/Interp.hx b/hscript/Interp.hx index 4d589ba..a7cec3b 100644 --- a/hscript/Interp.hx +++ b/hscript/Interp.hx @@ -341,6 +341,11 @@ class Interp { restore(old); return v; } + case EObject(fl): + var o = {}; + for( f in fl ) + set(o,f.name,expr(f.e)); + return o; } return null; } diff --git a/hscript/Parser.hx b/hscript/Parser.hx index 26e763b..a67373a 100644 --- a/hscript/Parser.hx +++ b/hscript/Parser.hx @@ -54,6 +54,7 @@ class Parser { public var unopsSuffix : Array; // implementation + var input : haxe.io.Input; var char : Null; var ops : Array; var idents : Array; @@ -85,6 +86,7 @@ class Parser { public function parse( s : haxe.io.Input ) { char = null; + input = s; ops = new Array(); idents = new Array(); tokens = new haxe.FastList(); @@ -94,10 +96,10 @@ class Parser { idents[identChars.charCodeAt(i)] = true; var a = new Array(); while( true ) { - var tk = token(s); + var tk = token(); if( tk == TEof ) break; tokens.add(tk); - a.push(parseFullExpr(s)); + a.push(parseFullExpr()); } return if( a.length == 1 ) a[0] else EBlock(a); } @@ -122,9 +124,9 @@ class Parser { } } - function parseFullExpr(s) { - var e = parseExpr(s); - var tk = token(s); + function parseFullExpr() { + var e = parseExpr(); + var tk = token(); if( tk != TSemicolon && tk != TEof ) { if( isBlock(e) ) tokens.add(tk); @@ -134,39 +136,80 @@ class Parser { return e; } - function parseExpr( s : haxe.io.Input ) { - var tk = token(s); + function parseObject() { + // parse object + var fl = new Array(); + while( true ) { + var tk = token(); + switch( tk ) { + case TId(id): + tk = token(); + if( tk != TDoubleDot ) unexpected(tk); + fl.push({ name : id, e : parseExpr() }); + tk = token(); + switch( tk ) { + case TBrClose: + break; + case TComma: + default: + unexpected(tk); + } + default: + unexpected(tk); + } + } + return parseExprNext(EObject(fl)); + } + + function parseExpr() { + var tk = token(); switch( tk ) { case TId(id): - var e = parseStructure(s,id); + var e = parseStructure(id); if( e == null ) e = EIdent(id); - return parseExprNext(s,e); + return parseExprNext(e); case TConst(c): - return parseExprNext(s,EConst(c)); + return parseExprNext(EConst(c)); case TPOpen: - var e = parseExpr(s); - tk = token(s); + var e = parseExpr(); + tk = token(); if( tk != TPClose ) unexpected(tk); - return parseExprNext(s,EParent(e)); + return parseExprNext(EParent(e)); case TBrOpen: + tk = token(); + switch( tk ) { + case TBrClose: + return parseExprNext(EObject([])); + case TId(id): + var tk2 = token(); + tokens.add(tk2); + tokens.add(tk); + switch( tk2 ) { + case TDoubleDot: + return parseExprNext(parseObject()); + default: + } + default: + tokens.add(tk); + } var a = new Array(); while( true ) { - tk = token(s); + a.push(parseFullExpr()); + tk = token(); if( tk == TBrClose ) break; tokens.add(tk); - a.push(parseFullExpr(s)); } return EBlock(a); case TOp(op): var found; for( x in unopsPrefix ) if( x == op ) - return makeUnop(op,parseExpr(s)); + return makeUnop(op,parseExpr()); return unexpected(tk); case TBkOpen: - return parseExprNext(s,EArrayDecl(parseExprList(s,TBkClose))); + return parseExprNext(EArrayDecl(parseExprList(TBkClose))); default: return unexpected(tk); } @@ -197,103 +240,103 @@ class Parser { } } - function parseStructure( s, id ) { + function parseStructure(id) { return switch( id ) { case "if": - var cond = parseExpr(s); - var e1 = parseExpr(s); + var cond = parseExpr(); + var e1 = parseExpr(); var e2 = null; var semic = false; - var tk = token(s); + var tk = token(); if( tk == TSemicolon ) { semic = true; - tk = token(s); + tk = token(); } if( Type.enumEq(tk,TId("else")) ) - e2 = parseExpr(s); + e2 = parseExpr(); else { tokens.add(tk); if( semic ) tokens.add(TSemicolon); } EIf(cond,e1,e2); case "var": - var tk = token(s); + var tk = token(); var ident = null; switch(tk) { case TId(id): ident = id; default: unexpected(tk); } - tk = token(s); + tk = token(); var e = null; if( Type.enumEq(tk,TOp("=")) ) - e = parseExpr(s); + e = parseExpr(); else tokens.add(tk); EVar(ident,e); case "while": - var econd = parseExpr(s); - var e = parseExpr(s); + var econd = parseExpr(); + var e = parseExpr(); EWhile(econd,e); case "for": - var tk = token(s); + var tk = token(); if( tk != TPOpen ) unexpected(tk); - tk = token(s); + tk = token(); var vname = null; switch( tk ) { case TId(id): vname = id; default: unexpected(tk); } - tk = token(s); + tk = token(); if( !Type.enumEq(tk,TId("in")) ) unexpected(tk); - var eiter = parseExpr(s); - tk = token(s); + var eiter = parseExpr(); + tk = token(); if( tk != TPClose ) unexpected(tk); - EFor(vname,eiter,parseExpr(s)); + EFor(vname,eiter,parseExpr()); case "break": EBreak; case "continue": EContinue; case "else": unexpected(TId(id)); case "function": - var tk = token(s); + var tk = token(); var name = null; switch( tk ) { - case TId(id): name = id; tk = token(s); + case TId(id): name = id; tk = token(); default: } if( tk != TPOpen ) unexpected(tk); var args = new Array(); - tk = token(s); + tk = token(); if( tk != TPClose ) { while( true ) { switch( tk ) { case TId(id): args.push(id); default: unexpected(tk); } - tk = token(s); + tk = token(); switch( tk ) { case TComma: case TPClose: break; default: unexpected(tk); } - tk = token(s); + tk = token(); } } - EFunction(args,parseExpr(s),name); + EFunction(args,parseExpr(),name); case "return": - var tk = token(s); + var tk = token(); tokens.add(tk); - EReturn(if( tk == TSemicolon ) null else parseExpr(s)); + EReturn(if( tk == TSemicolon ) null else parseExpr()); case "new": var a = new Array(); - var tk = token(s); + var tk = token(); switch( tk ) { case TId(id): a.push(id); default: unexpected(tk); } while( true ) { - tk = token(s); + tk = token(); switch( tk ) { case TDot: - tk = token(s); + tk = token(); switch(tk) { case TId(id): a.push(id); default: unexpected(tk); @@ -304,34 +347,34 @@ class Parser { unexpected(tk); } } - ENew(a.join("."),parseExprList(s,TPClose)); + ENew(a.join("."),parseExprList(TPClose)); case "throw": - EThrow( parseExpr(s) ); + EThrow( parseExpr() ); case "try": - var e = parseExpr(s); - var tk = token(s); + var e = parseExpr(); + var tk = token(); if( !Type.enumEq(tk,TId("catch")) ) unexpected(tk); - tk = token(s); + tk = token(); if( tk != TPOpen ) unexpected(tk); - tk = token(s); + tk = token(); var vname = switch( tk ) { case TId(id): id; default: unexpected(tk); } - tk = token(s); + tk = token(); if( tk != TDoubleDot ) unexpected(tk); - tk = token(s); + tk = token(); if( !Type.enumEq(tk,TId("Dynamic")) ) unexpected(tk); - tk = token(s); + tk = token(); if( tk != TPClose ) unexpected(tk); - ETry(e,vname,parseExpr(s)); + ETry(e,vname,parseExpr()); default: null; } } - function parseExprNext( s : haxe.io.Input, e1 : Expr ) { - var tk = token(s); + function parseExprNext( e1 : Expr ) { + var tk = token(); switch( tk ) { case TOp(op): for( x in unopsSuffix ) @@ -340,29 +383,29 @@ class Parser { tokens.add(tk); return e1; } - return parseExprNext(s,EUnop(op,false,e1)); + return parseExprNext(EUnop(op,false,e1)); } - return makeBinop(op,e1,parseExpr(s)); + return makeBinop(op,e1,parseExpr()); case TDot: - tk = token(s); + tk = token(); var field = null; switch(tk) { case TId(id): field = id; default: unexpected(tk); } - return parseExprNext(s,EField(e1,field)); + return parseExprNext(EField(e1,field)); case TPOpen: - return parseExprNext(s,ECall(e1,parseExprList(s,TPClose))); + return parseExprNext(ECall(e1,parseExprList(TPClose))); case TBkOpen: - var e2 = parseExpr(s); - tk = token(s); + var e2 = parseExpr(); + tk = token(); if( tk != TBkClose ) unexpected(tk); - return parseExprNext(s,EArray(e1,e2)); + return parseExprNext(EArray(e1,e2)); case TQuestion: - var e2 = parseExpr(s); - tk = token(s); + var e2 = parseExpr(); + tk = token(); if( tk != TDoubleDot ) unexpected(tk); - var e3 = parseExpr(s); + var e3 = parseExpr(); return EIf(e1,e2,e3); default: tokens.add(tk); @@ -370,15 +413,15 @@ class Parser { } } - function parseExprList( s : haxe.io.Input, etk ) { + function parseExprList( etk ) { var args = new Array(); - var tk = token(s); + var tk = token(); if( tk == etk ) return args; tokens.add(tk); while( true ) { - args.push(parseExpr(s)); - tk = token(s); + args.push(parseExpr()); + tk = token(); switch( tk ) { case TComma: default: @@ -389,15 +432,16 @@ class Parser { return args; } - function readChar( s : haxe.io.Input ) { - return try s.readByte() catch( e : Dynamic ) 0; + function readChar() { + return try input.readByte() catch( e : Dynamic ) 0; } - function readString( s : haxe.io.Input, until ) { + function readString( until ) { var c; var b = new StringBuf(); var esc = false; var old = line; + var s = input; while( true ) { try { c = s.readByte(); @@ -428,12 +472,12 @@ class Parser { return b.toString(); } - function token( s : haxe.io.Input ) { + function token() { if( !tokens.isEmpty() ) return tokens.pop(); var char; if( this.char == null ) - char = readChar(s); + char = readChar(); else { char = this.char; this.char = null; @@ -447,7 +491,7 @@ class Parser { var n = char - 48; var exp = 0; while( true ) { - char = readChar(s); + char = readChar(); exp *= 10; switch( char ) { case 48,49,50,51,52,53,54,55,56,57: @@ -455,7 +499,7 @@ class Parser { case 46: if( exp > 0 ) { // in case of '...' - if( exp == 10 && readChar(s) == 46 ) { + if( exp == 10 && readChar() == 46 ) { tokens.add(TOp("...")); return TConst( CInt(n) ); } @@ -468,7 +512,7 @@ class Parser { // read hexa var n = haxe.Int32.ofInt(0); while( true ) { - char = readChar(s); + char = readChar(); switch( char ) { case 48,49,50,51,52,53,54,55,56,57: // 0-9 n = haxe.Int32.add(haxe.Int32.shl(n,4), cast (char - 48)); @@ -495,13 +539,13 @@ class Parser { case 41: return TPClose; case 44: return TComma; case 46: - char = readChar(s); + char = readChar(); switch( char ) { case 48,49,50,51,52,53,54,55,56,57: var n = char - 48; var exp = 1; while( true ) { - char = readChar(s); + char = readChar(); exp *= 10; switch( char ) { case 48,49,50,51,52,53,54,55,56,57: @@ -512,7 +556,7 @@ class Parser { } } case 46: - char = readChar(s); + char = readChar(); if( char != 46 ) throw Error.EInvalidChar(char); return TOp("..."); @@ -524,18 +568,18 @@ class Parser { case 125: return TBrClose; case 91: return TBkOpen; case 93: return TBkClose; - case 39: return TConst( CString(readString(s,39)) ); - case 34: return TConst( CString(readString(s,34)) ); + case 39: return TConst( CString(readString(39)) ); + case 34: return TConst( CString(readString(34)) ); case 63: return TQuestion; case 58: return TDoubleDot; default: if( ops[char] ) { var op = String.fromCharCode(char); while( true ) { - char = readChar(s); + char = readChar(); if( !ops[char] ) { if( op.charCodeAt(0) == 47 ) - return tokenComment(s,op,char); + return tokenComment(op,char); this.char = char; return TOp(op); } @@ -545,7 +589,7 @@ class Parser { if( idents[char] ) { var id = String.fromCharCode(char); while( true ) { - char = readChar(s); + char = readChar(); if( !idents[char] ) { this.char = char; return TId(id); @@ -555,13 +599,14 @@ class Parser { } throw Error.EInvalidChar(char); } - char = readChar(s); + char = readChar(); } return null; } - function tokenComment( s : haxe.io.Input, op : String, char : Int ) { + function tokenComment( op : String, char : Int ) { var c = op.charCodeAt(1); + var s = input; if( c == 47 ) { // comment try { while( char != 10 && char != 13 ) @@ -569,7 +614,7 @@ class Parser { this.char = char; } catch( e : Dynamic ) { } - return token(s); + return token(); } if( c == 42 ) { /* comment */ var old = line; @@ -587,7 +632,7 @@ class Parser { line = old; throw Error.EUnterminatedComment; } - return token(s); + return token(); } this.char = char; return TOp(op); diff --git a/release.bat b/release.bat new file mode 100644 index 0000000..efc4d64 --- /dev/null +++ b/release.bat @@ -0,0 +1,13 @@ +@echo off +rm -rf release +mkdir release +cp haxelib.xml release +cd release +mkdir hscript +cd .. +cp hscript/*.hx release/hscript +haxe -xml release/haxedoc.xml hscript.Interp hscript.Parser hscript.Bytes +7z a -tzip release.zip release +rm -rf release +haxelib submit release.zip +pause \ No newline at end of file