From 2a18e3abbe4560b5f391fb76be994206246b0833 Mon Sep 17 00:00:00 2001 From: Nicolas Cannasse Date: Sun, 6 Oct 2024 17:01:14 +0200 Subject: [PATCH] added ctx and properties, and a few fixes --- hscript/JsInterp.hx | 97 +++++++++++++++++++++++++++++++++++++-------- 1 file changed, 81 insertions(+), 16 deletions(-) diff --git a/hscript/JsInterp.hx b/hscript/JsInterp.hx index 6dc0df4..cb2b46e 100644 --- a/hscript/JsInterp.hx +++ b/hscript/JsInterp.hx @@ -2,19 +2,34 @@ package hscript; class JsInterp extends Interp { + /** + Variables declared in `ctx` are directly accessed without going through the `variables` map, which is faster + **/ + public var ctx : {}; + + /** + If properties are defined, all calls to get/set are only done for fields accesses which are listed here. + **/ + public var properties : Map; + + override function execute( expr : Expr ) : Dynamic { depth = 0; locals = new Map(); - var str = '({ _exec : ($$i) => ${exprValue(expr)} })'; - trace(str); - var obj : Dynamic = js.Lib.eval(str); - return obj._exec(this); + var str = '(($$i) => ${exprValue(expr)})'; + var f : Dynamic -> Dynamic = js.Lib.eval(str); + return f(this); } function escapeString(s:String) { return s.split("\\").join("\\\\").split("\r").join("\\r").split("\n").join("\\n").split('"').join('\\"'); } + function handleRBC( e : Expr ) { + // todo : return/break/continue inside expr here won't work ! + return e; + } + function exprValue( expr : Expr ) { switch( Tools.expr(expr) ) { case EBlock([]): @@ -22,18 +37,25 @@ class JsInterp extends Interp { case EIf(cond,e1,e2): return exprJS(Tools.mk(ETernary(cond,e1,e2),expr)); case EBlock(el): - var el = el.copy(); + var el = [for( e in el ) handleRBC(e)]; var last = el[el.length-1]; - el[el.length - 1] = Tools.mk(EReturn(last),last); + switch( Tools.expr(last) ) { + case EFunction(_,_,name,_) if( name != null ): // don't return latest named function + default: + el[el.length - 1] = Tools.mk(EReturn(last),last); + } var ebl = Tools.mk(EBlock(el),expr); - return '(() => ${exprJS(ebl)})()'; // todo : return/break/continue inside expr here won't work ! + return '(() => ${exprJS(ebl)})()'; case ETry(e,v,t,ecatch): + e = handleRBC(e); + ecatch = handleRBC(ecatch); var expr = Tools.mk(ETry(Tools.mk(EReturn(e),e),v,t,Tools.mk(EReturn(ecatch),ecatch)),expr); - return '(() => { ${exprJS(expr)} })()'; // todo : return/break/continue inside expr here won't work ! + return '(() => { ${exprJS(expr)} })()'; case EVar(_,_,e): return e == null ? "null" : '(${exprValue(e)},null)'; case EWhile(_), EFor(_), EDoWhile(_), EThrow(_): - return '(() => {${exprJS(expr)}})()'; // todo : return/break/continue inside expr here won't work ! + expr = handleRBC(expr); + return '(() => {${exprJS(expr)}})()'; case EMeta(_,_,e), ECheckType(e,_): return exprValue(e); default: @@ -60,9 +82,17 @@ class JsInterp extends Interp { #end } + function isContext(v:String) { + return ctx != null && Reflect.hasField(ctx,v); + } + + function isProperty(f:String) { + return properties == null || properties.get(f); + } + function exprJS( expr : Expr ) : String { #if hscriptPos - curExpr = e; + curExpr = expr; var expr = expr.e; #end switch( expr ) { @@ -75,6 +105,8 @@ class JsInterp extends Interp { case EIdent(v): if( locals.exists(v) ) return v; + if( isContext(v) ) + return '$$i.ctx.$v'; switch( v ) { case "null", "true", "false": return v; default: @@ -87,6 +119,12 @@ class JsInterp extends Interp { return '(${exprValue(e)})'; case EBlock(el): var old = locals.copy(); + // pre define name functions + for( e in el ) + switch( Tools.expr(e) ) { + case EFunction(_,_,name,_): locals.set(name,null); + default: + } var buf = new StringBuf(); buf.add('{'); for( e in el ) { @@ -96,6 +134,8 @@ class JsInterp extends Interp { buf.add('}'); locals = old; return buf.toString(); + case EField(e,f) if( !isProperty(f) ): + return exprValue(e)+"."+f; case EField(e, f): return '$$i.get(${exprValue(e)},"$f")'; case EBinop(op, e1, e2): @@ -108,6 +148,8 @@ class JsInterp extends Interp { switch( Tools.expr(e1) ) { case EIdent(id) if( locals.exists(id) ): return id+" = "+exprValue(e2); + case EIdent(id) if( isContext(id) ): + return '$$i.ctx.$id = ${exprValue(e2)}'; case EIdent(id): return '$$i.setVar("$id",${exprValue(e2)})'; case EField(e,f): @@ -122,6 +164,8 @@ class JsInterp extends Interp { switch( Tools.expr(e1) ) { case EIdent(id) if( locals.exists(id) ): return id+" "+op+" "+exprValue(e2); + case EIdent(id) if( isContext(id) ): + return '$$i.ctx.$id $op ${exprValue(e2)}'; case EIdent(id): return '$$i.setVar("$id",$$i.resolve("$id") $aop (${exprValue(e2)}))'; case EField(e, f): @@ -148,6 +192,8 @@ class JsInterp extends Interp { switch( Tools.expr(e) ) { case EIdent(id) if( locals.exists(id) ): return prefix ? op + id : id + op; + case EIdent(id) if( isContext(id) ): + return prefix ? op + "$i.ctx."+id : "$i.ctx."+id + op; case _ if( prefix ): var one = Tools.mk(EConst(CInt(1)),e); return exprJS(Tools.mk(EBinop(op.charAt(0)+"=",e,one),e)); @@ -168,8 +214,23 @@ class JsInterp extends Interp { case ECall(e, params): var args = [for( p in params ) exprValue(p)]; switch( Tools.expr(e) ) { - case EField(e,f): - return addPos('$$i.fcall2(${exprValue(e)},"$f",[${args.join(',')}])'); + case EField(eobj,f): + var isCtx = false; + var obj = eobj; + while( true ) { + switch( Tools.expr(obj) ) { + case EField(o,_): obj = o; + case EIdent(i) if( isContext(i) ): isCtx = true; break; + default: break; + } + } + if( isCtx ) + return '${exprValue(e)}(${args.join(',')})'; + return addPos('$$i.fcall2(${exprValue(eobj)},"$f",[${args.join(',')}])'); + case EIdent(id) if( locals.exists(id) ): + return '$id(${args.join(',')})'; + case EIdent(id) if( isContext(id) ): + return '$$i.ctx.$id(${args.join(',')})'; default: return '$$i.call(null,${exprValue(e)},[${args.join(',')}])'; } @@ -233,15 +294,19 @@ class JsInterp extends Interp { case EMeta(_, _, e), ECheckType(e,_): return exprJS(e); case EFunction(args, e, name, ret): + if( name != null ) + locals.set(name, null); var prev = locals.copy(); for( a in args ) locals.set(a.name, null); var bl = exprBlock(e); locals = prev; - var fname = if( name == null ) "" else { locals.set(name,null); " "+name; } - return 'function$fname(${[for( a in args ) a.name].join(",")}) $bl'; + var fstr = 'function(${[for( a in args ) a.name].join(",")}) $bl'; + if( name != null ) + fstr = 'var $name = $$i.setVar("$name",$fstr)'; + return fstr; case ESwitch(e, cases, defaultExpr): - var checks = [for( c in cases ) 'if( ${[for( v in c.values ) '$$v == ${exprValue(v)}'].join(" || ")} ) return ${exprValue(c.expr)};']; + var checks = [for( c in cases ) 'if( ${[for( v in c.values ) '$$v == ${exprValue(v)}'].join(" || ")} ) return ${exprValue(handleRBC(c.expr))};']; if( defaultExpr != null ) checks.push('return '+exprValue(defaultExpr)); return '(($$v) => { ${[for( c in checks ) c+";"].join(" ")} })(${exprValue(e)})'; @@ -280,7 +345,7 @@ class JsInterp extends Interp { #if hscriptPos function _p( pmin, pmax, origin, line ) { - curExpr = { expr : null, pmin : pmin, pmax: pmax, origin:origin, line:line }; + curExpr = { e : null, pmin : pmin, pmax: pmax, origin:origin, line:line }; } #end