Repe [T3DU] Update - 31f26d171bba0355ce2a77031e3aad4c64dbc7e9

This commit is contained in:
2026-09-04 10:46:13 -07:00
parent c915312901
commit 0460b9d587
482 changed files with 27797 additions and 3226 deletions

View File

@ -0,0 +1,536 @@
/*
* Copyright (C)2008-2017 Haxe Foundation
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*/
package hscript;
import hscript.Expr;
enum VarMode {
Defined;
ForceSync;
}
class Async {
var definedVars : Array<{ n : String, prev : Null<VarMode> }>;
var vars : Map<String,VarMode>;
var currentFun : String;
var currentLoop : Expr;
var currentBreak : Expr -> Expr;
var uid = 0;
public var asyncIdents : Map<String,Bool>;
static var nullExpr : Expr = #if hscriptPos { e : null, pmin : 0, pmax : 0, origin : "<null>", line : 0 } #else null #end;
static var nullId = mk(EIdent("null"), nullExpr);
inline static function expr( e : Expr ) {
return #if hscriptPos e.e #else e #end;
}
inline static function mk( e, inf : Expr ) : Expr {
return #if hscriptPos { e : e, pmin : inf.pmin, pmax : inf.pmax, origin : inf.origin, line : inf.line } #else e #end;
}
/**
Convert a script into asynchronous one.
- calls such as foo(a,b,c) are translated to a_foo(function(r) ...rest, a,b,c) where r is the result value
- object access such obj.bar(a,b,c) are translated to obj.a_bar(function(r) ...rest, a, b, c)
- @async expr will execute the expression but continue without waiting for it to finish
- @split [ e1, e2, e3 ] is transformed to split(function(_) ...rest, [e1, e2, e3]) which
should execute asynchronously all expressions - until they return - before continuing the execution
- for(i in v) block; loops are translated to the following:
var _i = makeIterator(v);
function _loop() {
if( !_i.hasNext() ) return;
var v = _i.next();
block(function(_) _loop());
}
_loop()
- while loops are translated similar to for loops
- break and continue are correctly handled
- you can use @sync <expr> to disable async transformation in some code parts (for performance reason)
- a few expressions are still not supported (complex calls, try/catch, and a few others)
In these examples ...rest represents the continuation of execution of the script after the expression
**/
public static function toAsync( e : Expr, topLevelSync = false ) {
var a = new Async();
return a.build(e, topLevelSync);
}
public dynamic function getTopLevelEnd() {
return ignore();
}
public function build( e : Expr, topLevelSync = false ) {
if( topLevelSync ) {
return buildSync(e,null);
} else {
var end = getTopLevelEnd();
return toCps(e, end, end);
}
}
function defineVar( v : String, mode ) {
definedVars.push({ n : v, prev : vars.get(v) });
vars.set(v, mode);
}
function lookupFunctions( el : Array<Expr> ) {
for( e in el )
switch( expr(e) ) {
case EFunction(_, _, name, _) if( name != null ): defineVar(name, Defined);
case EMeta("sync",_,expr(_) => EFunction(_,_,name,_)) if( name != null ): defineVar(name, ForceSync);
default:
}
}
function buildSync( e : Expr, exit : Expr ) : Expr {
switch( expr(e) ) {
case EFunction(_,_,name,_):
if( name != null )
return toCps(e, null, null);
return e;
case EBlock(el):
var v = saveVars();
lookupFunctions(el);
var e = block([for(e in el) buildSync(e,exit)], e);
restoreVars(v);
return e;
case EMeta("async", _, e):
return toCps(e, ignore(), ignore());
case EMeta("sync", args, ef = expr(_) => EFunction(fargs, body, name, ret)):
return mk(EMeta("sync",args,mk(EFunction(fargs, buildSync(body,null), name, ret),ef)),e);
case EBreak if( currentBreak != null ):
return currentBreak(e);
case EContinue if( currentLoop != null ):
return block([retNull(currentLoop, e), mk(EReturn(),e)],e);
case EFor(_), EWhile(_):
var oldLoop = currentLoop, oldBreak = currentBreak;
currentLoop = null;
currentBreak = null;
e = Tools.map(e, buildSync.bind(_, exit));
currentLoop = oldLoop;
currentBreak = oldBreak;
return e;
case EReturn(eret) if( exit != null ):
return block([eret == null ? retNull(exit, e) : call(exit,[eret], e), mk(EReturn(),e)], e);
default:
return Tools.map(e, buildSync.bind(_, exit));
}
}
public function new() {
vars = new Map();
definedVars = [];
}
function ignore(?e) : Expr {
var inf = e == null ? nullExpr : e;
return fun("_", block(e == null ? [] : [e],inf));
}
inline function ident(str, e) {
return mk(EIdent(str), e);
}
inline function fun(arg:String, e, ?name) {
return mk(EFunction([{ name : arg, t : null }], e, name), e);
}
inline function funs(arg:Array<String>, e, ?name) {
return mk(EFunction([for( a in arg ) { name : a, t : null }], e, name), e);
}
inline function block(arr:Array<Expr>, e) {
if( arr.length == 1 && expr(arr[0]).match(EBlock(_)) )
return arr[0];
return mk(EBlock(arr), e);
}
inline function field(e, f, inf) {
return mk(EField(e, f), inf);
}
inline function binop(op, e1, e2, inf) {
return mk(EBinop(op, e1, e2), inf);
}
inline function call(e, args, inf) {
return mk(ECall(e, args), inf);
}
function retNull(e:Expr,?pos) : Expr {
switch( expr(e) ) {
case EFunction([{name:"_"}], e, _, _): return e;
default:
}
return call(e, [nullId], pos == null ? e : pos);
}
function makeCall( ecall, args : Array<Expr>, rest : Expr, exit, sync = false ) {
var names = [for( i in 0...args.length ) "_a"+uid++];
var rargs = [for( i in 0...args.length ) ident(names[i],ecall)];
if( !sync )
rargs.unshift(rest);
var rest = mk(sync ? ECall(rest,[call(ecall, rargs,ecall)]) : ECall(ecall, rargs), ecall);
var i = args.length - 1;
while( i >= 0 ) {
rest = toCps(args[i], fun(names[i], rest), exit);
i--;
}
return rest;
}
var syncFlag : Bool;
function isSync( e : Expr ) {
syncFlag = true;
checkSync(e);
return syncFlag;
}
inline function isAsyncIdent( id : String ) {
return asyncIdents == null || asyncIdents.exists(id);
}
function checkSync( e : Expr ) {
if( !syncFlag )
return;
switch( expr(e) ) {
case ECall(expr(_) => EIdent(i),_) if( isAsyncIdent(i) || vars.get(i) == Defined ):
syncFlag = false;
case ECall(expr(_) => EField(_,i),_) if( isAsyncIdent(i) ):
syncFlag = false;
case EFunction(_,_,name,_) if( name != null ):
syncFlag = false;
case EMeta("sync" | "async", _, _):
// isolated from the sync part
default:
Tools.iter(e, checkSync);
}
}
function saveVars() {
return definedVars.length;
}
function restoreVars(k) {
while( definedVars.length > k ) {
var v = definedVars.pop();
if( v.prev == null ) vars.remove(v.n) else vars.set(v.n, v.prev);
}
}
public function toCps( e : Expr, rest : Expr, exit : Expr ) : Expr {
if( isSync(e) )
return call(rest, [buildSync(e, exit)],e);
switch( expr(e) ) {
case EBlock(el):
var el = el.copy();
var vold = saveVars();
lookupFunctions(el);
while( el.length > 0 ) {
var e = toCps(el.pop(), rest, exit);
rest = ignore(e);
}
restoreVars(vold);
return retNull(rest);
case EFunction(args, body, name, t):
var vold = saveVars();
if( name != null )
defineVar(name, Defined);
for( a in args )
defineVar(a.name, Defined);
args.unshift( { name : "_onEnd", t : null } );
var frest = ident("_onEnd",e);
var oldFun = currentFun;
currentFun = name;
var body = toCps(body, frest, frest);
var f = mk(EFunction(args, body, name, t),e);
restoreVars(vold);
return rest == null ? f : call(rest, [f],e);
case EParent(e):
return mk(EParent(toCps(e, rest, exit)),e);
case EMeta("sync", _, e):
return call(rest,[buildSync(e,exit)],e);
case EMeta("async", _, e):
var nothing = ignore();
return block([toCps(e,nothing,nothing),retNull(rest)],e);
case EMeta("split", _, e):
var args = switch( expr(e) ) { case EArrayDecl(el): el; default: throw "@split expression should be an array"; };
var args = [for( a in args ) fun("_rest", toCps(block([a],a), ident("_rest",a), exit))];
return call(ident("split",e), [rest, mk(EArrayDecl(args),e)],e);
case ECall(expr(_) => EIdent(i), args):
var mode = vars.get(i);
return makeCall( ident( mode != null ? i : "a_" + i,e) , args, rest, exit, mode == ForceSync);
case ECall(expr(_) => EField(e, f), args):
return makeCall(field(e,"a_"+f,e), args, rest, exit);
case EFor(v, eit, eloop):
var id = ++uid;
var it = ident("_i" + id,e);
var oldLoop = currentLoop, oldBreak = currentBreak;
var loop = ident("_loop" + id,e);
currentLoop = loop;
currentBreak = function(inf) return block([retNull(rest, inf), mk(EReturn(),inf)], inf);
var efor = block([
mk(EVar("_i" + id, call(ident("makeIterator",eit),[eit],eit)),eit),
fun("_", block([
mk(EIf(mk(EUnop("!", true, call( field(it, "hasNext", it), [], it)),it), currentBreak(it)),it),
mk(EVar(v, call(field(it, "next",it), [], it)), it),
toCps(eloop, loop, exit),
], it),"_loop" + id),
retNull(loop, e),
], e);
currentLoop = oldLoop;
currentBreak = oldBreak;
return efor;
case EUnop(op = "!", prefix, eop):
return toCps(eop, fun("_r",call(rest, [mk(EUnop(op, prefix, ident("_r",e)),e)], e)), exit);
case EBinop(op, e1, e2):
switch( op ) {
case "=", "+=", "-=", "/=", "*=", "%=", "&=", "|=", "^=":
switch( expr(e1) ) {
case EIdent(_):
var id = "_r" + uid++;
return toCps(e2, fun(id, call(rest, [binop(op, e1, ident(id,e1),e1)], e1)), exit);
case EField(ef1, f):
var id1 = "_r" + uid++;
var id2 = "_r" + uid++;
return toCps(ef1, fun(id1, toCps(e2, fun(id2, call(rest, [binop(op, field(ident(id1, e1), f, ef1), ident(id2, e2), e)], e)), exit)), exit);
case EArray(earr, eindex):
var idArr = "_r" + uid++;
var idIndex = "_r" + uid++;
var idVal = "_r" + uid++;
return toCps(earr,fun(idArr, toCps(eindex, fun(idIndex, toCps(e2,
fun(idVal, call(rest, [binop(op, mk(EArray(ident(idArr,earr), ident(idIndex,eindex)),e1), ident(idVal,e1), e)], e))
, exit)), exit)),exit);
default:
throw "assert " + e1;
}
case "||":
var id1 = "_r" + uid++;
var id2 = "_r" + uid++;
return toCps(e1, fun(id1, mk(EIf(binop("==", ident(id1,e1), ident("true",e1), e1),call(rest,[ident("true",e1)],e1),toCps(e2, rest, exit)),e)), exit);
case "&&":
var id1 = "_r" + uid++;
var id2 = "_r" + uid++;
return toCps(e1, fun(id1, mk(EIf(binop("!=", ident(id1,e1), ident("true",e1), e1),call(rest,[ident("false",e1)],e1),toCps(e2, rest, exit)),e)), exit);
default:
var id1 = "_r" + uid++;
var id2 = "_r" + uid++;
return toCps(e1, fun(id1, toCps(e2, fun(id2, call(rest, [binop(op, ident(id1,e1), ident(id2,e2), e)], e)), exit)), exit);
}
case EIf(cond, e1, e2), ETernary(cond, e1, e2):
return toCps(cond, fun("_c", mk(EIf(ident("_c",cond), toCps(e1, rest, exit), e2 == null ? retNull(rest) : toCps(e2, rest, exit)),e)), exit);
case EWhile(cond, ewh):
var id = ++uid;
var loop = ident("_loop" + id, cond);
var oldLoop = currentLoop, oldBreak = currentBreak;
currentLoop = loop;
currentBreak = function(e) return block([retNull(rest,e), mk(EReturn(),e)],e);
var ewhile = block([
fun("_r",
toCps(cond, fun("_c", mk(EIf(ident("_c", cond), toCps(ewh, loop, exit), retNull(rest,cond)),cond)), exit)
, "_loop"+id),
retNull(loop, cond),
],e);
currentLoop = oldLoop;
currentBreak = oldBreak;
return ewhile;
case EReturn(eret):
return eret == null ? retNull(exit, e) : toCps(eret, exit, exit);
case EObject(fields):
var id = "_o" + uid++;
var rest = call(rest, [ident(id,e)], e);
fields.reverse();
for( f in fields )
rest = toCps(f.e, fun("_r", block([
binop("=", mk(EField(ident(id,f.e), f.name),f.e), ident("_r",f.e), f.e),
rest,
],f.e)),exit);
return block([
mk(EVar(id, mk(EObject([]),e)),e),
rest,
],e);
case EArrayDecl(el):
var id = "_a" + uid++;
var rest = call(rest, [ident(id,e)], e);
var i = el.length - 1;
while( i >= 0 ) {
var e = el[i];
rest = toCps(e, fun("_r", block([
binop("=", mk(EArray(ident(id,e), mk(EConst(CInt(i)),e)),e), ident("_r",e), e),
rest,
],e)), exit);
i--;
}
return block([
mk(EVar(id, mk(EArrayDecl([]),e)),e),
rest,
],e);
case EArray(earr, eindex):
var id1 = "_r" + uid++;
var id2 = "_r" + uid++;
return toCps(earr, fun(id1, toCps(eindex, fun(id2, call(rest, [mk(EArray(ident(id1,e), ident(id2,e)),e)], e)), exit)), exit);
case EVar(v, t, ev):
if( ev == null )
return block([e, retNull(rest, e)], e);
return block([
mk(EVar(v, t),e),
toCps(ev, fun("_r", block([binop("=", ident(v,e), ident("_r",e), e), retNull(rest,e)], e)), exit),
],e);
case EConst(_), EIdent(_), EUnop(_), EField(_):
return call(rest, [e], e);
case ENew(cl, args):
var names = [for( i in 0...args.length ) "_a"+uid++];
var rargs = [for( i in 0...args.length ) ident(names[i], args[i])];
var rest = call(rest,[mk(ENew(cl, rargs),e)],e);
var i = args.length - 1;
while( i >= 0 ) {
rest = toCps(args[i], fun(names[i], rest), exit);
i--;
}
return rest;
case EBreak:
if( currentBreak == null ) throw "Break outside loop";
return currentBreak(e);
case EContinue:
if( currentLoop == null ) throw "Continue outside loop";
return block([retNull(currentLoop, e), mk(EReturn(),e)], e);
case ESwitch(v, cases, def):
var cases = [for( c in cases ) { values : c.values, expr : toCps(c.expr, rest, exit) } ];
return toCps(v, mk(EFunction([ { name : "_c", t : null } ], mk(ESwitch(ident("_c",v), cases, def == null ? retNull(rest) : toCps(def, rest, exit)),e)),e), exit );
case EThrow(v):
return toCps(v, mk(EFunction([ { name : "_v", t : null } ], mk(EThrow(v),v)), v), exit);
case EMeta(name,_,e) if( name.charCodeAt(0) == ":".code ): // ignore custom ":" metadata
return toCps(e, rest, exit);
//case EDoWhile(_), ETry(_), ECall(_):
default:
throw "Unsupported async expression " + Printer.toString(e);
}
}
}
class AsyncInterp extends Interp {
public function setContext( api : Dynamic ) {
var funs = new Array();
for( v in variables.keys() )
if( Reflect.isFunction(variables.get(v)) )
funs.push({ v : v, obj : null });
variables.set("split", split);
variables.set("makeIterator", makeIterator);
var c = Type.getClass(api);
for( f in (c == null ? Reflect.fields(api) : Type.getInstanceFields(c)) ) {
var fv = Reflect.field(api, f);
if( !Reflect.isFunction(fv) ) continue;
if( f.charCodeAt(0) == "_".code ) f = f.substr(1);
variables.set(f, fv);
// create the async wrapper if doesn't exists
if( f.substr(0, 2) != "a_" )
funs.push({ v : f, obj : api });
}
for( v in funs ) {
if( variables.exists("a_" + v.v) ) continue;
var fv : Dynamic = variables.get(v.v);
var obj = v.obj;
variables.set("a_" + v.v, Reflect.makeVarArgs(function(args:Array<Dynamic>) {
var onEnd = args.shift();
onEnd(Reflect.callMethod(obj, fv, args));
}));
}
}
public function hasMethod( name : String ) {
var v = variables.get(name);
return v != null && Reflect.isFunction(v);
}
public function callValue( value : Dynamic, args : Array<Dynamic>, ?onResult : Dynamic -> Void, ?vthis : {} ) {
var oldThis = variables.get("this");
if( vthis != null )
variables.set("this", vthis);
if( onResult == null )
onResult = function(_) {};
args.unshift(onResult);
Reflect.callMethod(null, value, args);
variables.set("this", oldThis);
}
public function callAsync( id : String, args, ?onResult, ?vthis : {} ) {
var v = variables.get(id);
if( v == null )
throw "Missing function " + id + "()";
callValue(v, args, onResult, vthis);
}
function split( rest : Dynamic -> Void, args : Array<Dynamic> ) {
if( args.length == 0 )
rest(null);
else {
var count = args.length;
function next(_) {
if( --count == 0 ) rest(null);
}
for( a in args )
a(next);
}
}
override function fcall( o : Dynamic, f : String, args : Array<Dynamic> ) : Dynamic {
var m = Reflect.field(o, f);
if( m == null ) {
if( f.substr(0, 2) == "a_" ) {
m = Reflect.field(o, f.substr(2));
// fallback on sync version
if( m != null ) {
var onEnd = args.shift();
onEnd(call(o, m, args));
return null;
}
// fallback on generic script
m = Reflect.field(o, "scriptCall");
if( m != null ) {
call(o, m, [args.shift(), f.substr(2), args]);
return null;
}
} else {
// fallback on generic script
m = Reflect.field(o, "scriptCall");
if( m != null ) {
var result : Dynamic = null;
call(o, m, [function(r) result = r, f, args]);
return result;
}
}
error(ECustom(o + " has no method " + f));
}
return call(o, m, args);
}
}

View File

@ -0,0 +1,518 @@
/*
* Copyright (C)2008-2017 Haxe Foundation
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*/
package hscript;
import hscript.Expr;
class Bytes {
var bin : haxe.io.Bytes;
var bout : haxe.io.BytesBuffer;
var pin : Int;
var hstrings : Map<String,Int>;
var strings : Array<String>;
var nstrings : Int;
public var storeTypes : Bool = false;
function new( ?bin ) {
this.bin = bin;
pin = 0;
bout = new haxe.io.BytesBuffer();
hstrings = new Map();
strings = [null];
nstrings = 1;
}
function doEncodeString( v : String ) {
var vid = hstrings.get(v);
if( vid == null ) {
if( nstrings == 256 ) {
hstrings = new Map();
nstrings = 1;
}
hstrings.set(v,nstrings);
bout.addByte(0);
var vb = haxe.io.Bytes.ofString(v);
bout.addByte(vb.length);
bout.add(vb);
nstrings++;
} else
bout.addByte(vid);
}
function doDecodeString() {
var id = bin.get(pin++);
if( id == 0 ) {
var len = bin.get(pin);
var str = #if (haxe_ver < 3.103) bin.readString(pin+1,len); #else bin.getString(pin+1,len); #end
pin += len + 1;
if( strings.length == 255 )
strings = [null];
strings.push(str);
return str;
}
return strings[id];
}
function doEncodeInt(v: Int) {
bout.addInt32(v);
}
function doEncodeConst( c : Const ) {
switch( c ) {
case CInt(v):
if( v >= 0 && v <= 255 ) {
bout.addByte(0);
bout.addByte(v);
} else {
bout.addByte(1);
doEncodeInt(v);
}
case CFloat(f):
bout.addByte(2);
doEncodeString(Std.string(f));
case CString(s):
bout.addByte(3);
doEncodeString(s);
}
}
function doDecodeInt() {
var i = bin.getInt32(pin);
pin += 4;
return i;
}
function doDecodeConst() {
return switch( bin.get(pin++) ) {
case 0:
CInt(bin.get(pin++));
case 1:
var i = doDecodeInt();
CInt(i);
case 2:
CFloat( Std.parseFloat(doDecodeString()) );
case 3:
CString( doDecodeString() );
default:
throw "Invalid code "+bin.get(pin-1);
}
}
function doEncode( e : Expr ) {
#if hscriptPos
doEncodeString(e.origin);
doEncodeInt(e.line);
var e = e.e;
#end
bout.addByte(exprIndex(e));
switch( e ) {
case EConst(c):
doEncodeConst(c);
case EIdent(v):
doEncodeString(v);
case EVar(n,t,e):
doEncodeString(n);
encodeType(t);
if( e == null )
bout.addByte(255);
else
doEncode(e);
case EParent(e):
doEncode(e);
case EBlock(el):
bout.addByte(el.length);
for( e in el )
doEncode(e);
case EField(e,f):
doEncode(e);
doEncodeString(f);
case EBinop(op,e1,e2):
doEncodeString(op);
doEncode(e1);
doEncode(e2);
case EUnop(op,prefix,e):
doEncodeString(op);
bout.addByte(prefix?1:0);
doEncode(e);
case ECall(e,el):
doEncode(e);
bout.addByte(el.length);
for( e in el )
doEncode(e);
case EIf(cond,e1,e2):
doEncode(cond);
doEncode(e1);
if( e2 == null )
bout.addByte(255);
else
doEncode(e2);
case EWhile(cond,e):
doEncode(cond);
doEncode(e);
case EDoWhile(cond,e):
doEncode(cond);
doEncode(e);
case EFor(v,it,e):
doEncodeString(v);
doEncode(it);
doEncode(e);
case EForGen(it,e):
doEncode(it);
doEncode(e);
case EBreak, EContinue:
case EFunction(params,e,name,t):
bout.addByte(params.length);
for( p in params )
doEncodeString(p.name);
doEncode(e);
doEncodeString(name == null?"":name);
encodeType(t);
case EReturn(e):
if( e == null )
bout.addByte(255);
else
doEncode(e);
case EArray(e,index):
doEncode(e);
doEncode(index);
case EArrayDecl(el):
if( el.length >= 255 ) throw "assert";
bout.addByte(el.length);
for( e in el )
doEncode(e);
case ENew(cl,params):
doEncodeString(cl);
bout.addByte(params.length);
for( e in params )
doEncode(e);
case EThrow(e):
doEncode(e);
case ETry(e,v,t,ecatch):
doEncode(e);
doEncodeString(v);
encodeType(t);
doEncode(ecatch);
case EObject(fl):
bout.addByte(fl.length);
for( f in fl ) {
doEncodeString(f.name);
doEncode(f.e);
}
case ETernary(cond, e1, e2):
doEncode(cond);
doEncode(e1);
doEncode(e2);
case ESwitch(e, cases, def):
doEncode(e);
for( c in cases ) {
if( c.values.length == 0 ) throw "assert";
for( v in c.values )
doEncode(v);
bout.addByte(255);
doEncode(c.expr);
}
bout.addByte(255);
if( def == null ) bout.addByte(255) else doEncode(def);
case EMeta(name,args,e):
doEncodeString(name);
bout.addByte(args == null ? 0 : args.length + 1);
if( args != null ) for( e in args ) doEncode(e);
doEncode(e);
case ECheckType(e,t):
doEncode(e);
encodeType(t);
case ECast(e, t):
doEncode(e);
encodeType(t);
}
}
function encodeType( t : CType ) {
if( !storeTypes )
return;
if( t == null ) {
bout.addByte(255);
return;
}
bout.addByte(t.getIndex());
switch( t ) {
case CTPath(path, params):
doEncodeString(path.join("."));
if( params == null )
bout.addByte(255);
else {
bout.addByte(params.length);
for( p in params )
encodeType(p);
}
case CTFun(args, ret):
bout.addByte(args.length);
for( a in args )
encodeType(a);
encodeType(ret);
case CTAnon(fields):
doEncodeInt(fields.length);
for( f in fields ) {
doEncodeString(f.name);
encodeType(f.t);
if( f.meta == null )
bout.addByte(255);
else {
bout.addByte(f.meta.length);
for( m in f.meta ) {
doEncodeString(m.name);
doEncodeInt(m.params.length);
for( p in m.params )
doEncode(p);
}
}
}
case CTParent(t):
encodeType(t);
case CTOpt(t):
encodeType(t);
case CTNamed(n, t):
doEncodeString(n);
encodeType(t);
case CTExpr(e):
doEncode(e);
}
}
function exprIndex(e):Int {
return switch (e) {
case EConst(_): 0;
case EIdent(_): 1;
case EVar(_): 2;
case EParent(_): 3;
case EBlock(_): 4;
case EField(_): 5;
case EBinop(_): 6;
case EUnop(_): 7;
case ECall(_): 8;
case EIf(_): 9;
case EWhile(_): 10;
case EFor(_): 11;
case EBreak: 12;
case EContinue: 13;
case EFunction(_): 14;
case EReturn(_): 15;
case EArray(_): 16;
case EArrayDecl(_): 17;
case ENew(_): 18;
case EThrow(_): 19;
case ETry(_): 20;
case EObject(_): 21;
case ETernary(_): 22;
case ESwitch(_): 23;
case EDoWhile(_): 24;
case EMeta(_): 25;
case ECheckType(_): 26;
case EForGen(_): 27;
case ECast(_): 28;
}
}
function doDecode() : Expr {
#if hscriptPos
if( getByte() == 255 )
return null;
pin--;
var origin = doDecodeString();
var line = doDecodeInt();
return { e : _doDecode(), pmin : 0, pmax : 0, origin : origin, line : line };
}
function _doDecode() : ExprDef {
#end
return switch( getByte() ) {
case 0:
EConst( doDecodeConst() );
case 1:
EIdent( doDecodeString() );
case 2:
var v = doDecodeString();
EVar(v,decodeType(),doDecode());
case 3:
EParent(doDecode());
case 4:
var a = new Array();
for( i in 0...getByte() )
a.push(doDecode());
EBlock(a);
case 5:
var e = doDecode();
EField(e,doDecodeString());
case 6:
var op = doDecodeString();
var e1 = doDecode();
EBinop(op,e1,doDecode());
case 7:
var op = doDecodeString();
var prefix = getByte() != 0;
EUnop(op,prefix,doDecode());
case 8:
var e = doDecode();
var params = new Array();
for( i in 0...getByte() )
params.push(doDecode());
ECall(e,params);
case 9:
var cond = doDecode();
var e1 = doDecode();
EIf(cond,e1,doDecode());
case 10:
var cond = doDecode();
EWhile(cond,doDecode());
case 11:
var v = doDecodeString();
var it = doDecode();
EFor(v,it,doDecode());
case 12:
EBreak;
case 13:
EContinue;
case 14:
var params = new Array<Argument>();
for( i in 0...getByte() )
params.push({ name : doDecodeString() });
var e = doDecode();
var name = doDecodeString();
EFunction(params,e,(name == "") ? null: name,decodeType());
case 15:
EReturn(doDecode());
case 16:
var e = doDecode();
EArray(e,doDecode());
case 17:
var el = new Array();
for( i in 0...getByte() )
el.push(doDecode());
EArrayDecl(el);
case 18:
var cl = doDecodeString();
var el = new Array();
for( i in 0...getByte() )
el.push(doDecode());
ENew(cl,el);
case 19:
EThrow(doDecode());
case 20:
var e = doDecode();
var v = doDecodeString();
ETry(e,v,decodeType(),doDecode());
case 21:
var fl = new Array();
for( i in 0...getByte() ) {
var name = doDecodeString();
var e = doDecode();
fl.push({ name : name, e : e });
}
EObject(fl);
case 22:
var cond = doDecode();
var e1 = doDecode();
var e2 = doDecode();
ETernary(cond, e1, e2);
case 23:
var e = doDecode();
var cases = [];
while( true ) {
var v = doDecode();
if( v == null ) break;
var values = [v];
while( true ) {
v = doDecode();
if( v == null ) break;
values.push(v);
}
cases.push( { values : values, expr : doDecode() } );
}
var def = doDecode();
ESwitch(e, cases, def);
case 24:
var cond = doDecode();
EDoWhile(cond,doDecode());
case 25:
var name = doDecodeString();
var count = getByte();
var args = count == 0 ? null : [for( i in 0...count - 1 ) doDecode()];
EMeta(name, args, doDecode());
case 26:
ECheckType(doDecode(), decodeType());
case 27:
EForGen(doDecode(), doDecode());
case 28:
ECast(doDecode(), decodeType());
case 255:
null;
default:
throw "Invalid code "+bin.get(pin - 1);
}
}
inline function getByte() {
return bin.get(pin++);
}
function decodeType() : CType {
if( !storeTypes )
return null;
return switch( getByte() ) {
case 255: null;
case 0:
var path = doDecodeString().split(".");
var plen = getByte();
var params = plen == 255 ? null : [for( i in 0...plen ) decodeType()];
CTPath(path, params);
case 1:
CTFun([for( v in 0...getByte() ) decodeType()], decodeType());
case 2:
CTAnon([for( v in 0...doDecodeInt() ) { name : doDecodeString(), t : decodeType(), meta : {
var v = getByte();
if( v == 255 ) null else [for( i in 0...v ) { name : doDecodeString(), params : [for( i in 0...getByte() ) doDecode()] }];
}}]);
case 3:
CTParent(decodeType());
case 4:
CTOpt(decodeType());
case 5:
CTNamed(doDecodeString(),decodeType());
case 6:
CTExpr(doDecode());
default:
throw "Invalid code "+bin.get(pin - 1);
}
}
public static function encode( e : Expr ) : haxe.io.Bytes {
var b = new Bytes();
b.doEncode(e);
return b.bout.getBytes();
}
public static function decode( bytes : haxe.io.Bytes ) : Expr {
var b = new Bytes(bytes);
return b.doDecode();
}
}

File diff suppressed because it is too large Load Diff

180
lib/hscript/hscript/Expr.hx Normal file
View File

@ -0,0 +1,180 @@
/*
* Copyright (C)2008-2017 Haxe Foundation
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*/
package hscript;
enum Const {
CInt( v : Int );
CFloat( f : Float );
CString( s : String );
}
#if hscriptPos
typedef Expr = {
var e : ExprDef;
var pmin : Int;
var pmax : Int;
var origin : String;
var line : Int;
}
enum ExprDef {
#else
typedef ExprDef = Expr;
enum Expr {
#end
EConst( c : Const );
EIdent( v : String );
EVar( n : String, ?t : CType, ?e : Expr );
EParent( e : Expr );
EBlock( e : Array<Expr> );
EField( e : Expr, f : String );
EBinop( op : String, e1 : Expr, e2 : Expr );
EUnop( op : String, prefix : Bool, e : Expr );
ECall( e : Expr, params : Array<Expr> );
EIf( cond : Expr, e1 : Expr, ?e2 : Expr );
EWhile( cond : Expr, e : Expr );
EFor( v : String, it : Expr, e : Expr );
EBreak;
EContinue;
EFunction( args : Array<Argument>, e : Expr, ?name : String, ?ret : CType );
EReturn( ?e : Expr );
EArray( e : Expr, index : Expr );
EArrayDecl( e : Array<Expr> );
ENew( cl : String, params : Array<Expr> );
EThrow( e : Expr );
ETry( e : Expr, v : String, t : Null<CType>, ecatch : Expr );
EObject( fl : Array<{ name : String, e : Expr }> );
ETernary( cond : Expr, e1 : Expr, e2 : Expr );
ESwitch( e : Expr, cases : Array<{ values : Array<Expr>, expr : Expr }>, ?defaultExpr : Expr);
EDoWhile( cond : Expr, e : Expr);
EMeta( name : String, args : Array<Expr>, e : Expr );
ECheckType( e : Expr, t : CType );
EForGen( it : Expr, e : Expr );
ECast( e : Expr, ?t : CType );
}
typedef Argument = { name : String, ?t : CType, ?opt : Bool, ?value : Expr };
typedef Metadata = Array<{ name : String, params : Array<Expr> }>;
enum CType {
CTPath( path : Array<String>, ?params : Array<CType> );
CTFun( args : Array<CType>, ret : CType );
CTAnon( fields : Array<{ name : String, t : CType, ?meta : Metadata }> );
CTParent( t : CType );
CTOpt( t : CType );
CTNamed( n : String, t : CType );
CTExpr( e : Expr ); // for type parameters only
}
#if hscriptPos
class Error {
public var e : ErrorDef;
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;
}
public function toString(): String {
return Printer.errorToString(this);
}
}
enum ErrorDef {
#else
enum Error {
#end
EInvalidChar( c : Int );
EUnexpected( s : String );
EUnterminatedString;
EUnterminatedComment;
EInvalidPreprocessor( msg : String );
EUnknownVariable( v : String );
EInvalidIterator( v : String );
EInvalidOp( op : String );
EInvalidAccess( f : String );
ECustom( msg : String );
}
enum ModuleDecl {
DPackage( path : Array<String> );
DImport( path : Array<String>, ?everything : Bool, ?name : String );
DClass( c : ClassDecl );
DTypedef( c : TypeDecl );
}
typedef ModuleType = {
var name : String;
var params : {}; // TODO : not yet parsed
var meta : Metadata;
var isPrivate : Bool;
}
typedef ClassDecl = {> ModuleType,
var extend : Null<CType>;
var implement : Array<CType>;
var fields : Array<FieldDecl>;
var isExtern : Bool;
}
typedef TypeDecl = {> ModuleType,
var t : CType;
}
typedef FieldDecl = {
var name : String;
var meta : Metadata;
var kind : FieldKind;
var access : Array<FieldAccess>;
}
enum FieldAccess {
APublic;
APrivate;
AInline;
AOverride;
AStatic;
AMacro;
}
enum FieldKind {
KFunction( f : FunctionDecl );
KVar( v : VarDecl );
}
typedef FunctionDecl = {
var args : Array<Argument>;
var expr : Expr;
var ret : Null<CType>;
}
typedef VarDecl = {
var get : Null<String>;
var set : Null<String>;
var expr : Null<Expr>;
var type : Null<CType>;
}

View File

@ -0,0 +1,745 @@
/*
* Copyright (C)2008-2017 Haxe Foundation
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*/
package hscript;
import haxe.PosInfos;
import hscript.Expr;
import haxe.Constraints.IMap;
private enum Stop {
SBreak;
SContinue;
SReturn;
}
class Interp {
public var variables : Map<String,Dynamic>;
var locals : Map<String,{ r : Dynamic }>;
var binops : Map<String, Expr -> Expr -> Dynamic >;
var depth : Int;
var inTry : Bool;
var declared : Array<{ n : String, old : { r : Dynamic } }>;
var returnValue : Dynamic;
#if hscriptPos
var curExpr : Expr;
#end
public function new() {
locals = new Map();
declared = new Array();
resetVariables();
initOps();
}
private function resetVariables(){
variables = new Map<String,Dynamic>();
variables.set("null",null);
variables.set("true",true);
variables.set("false",false);
variables.set("trace", Reflect.makeVarArgs(function(el) {
var inf = posInfos();
var v = el.shift();
if( el.length > 0 ) inf.customParams = el;
haxe.Log.trace(Std.string(v), inf);
}));
}
public function posInfos(): PosInfos {
#if hscriptPos
if (curExpr != null)
return cast { fileName : curExpr.origin, lineNumber : curExpr.line };
#end
return cast { fileName : "hscript", lineNumber : 0 };
}
function initOps() {
var me = this;
binops = new Map();
binops.set("+",function(e1,e2) return me.expr(e1) + me.expr(e2));
binops.set("-",function(e1,e2) return me.expr(e1) - me.expr(e2));
binops.set("*",function(e1,e2) return me.expr(e1) * me.expr(e2));
binops.set("/",function(e1,e2) return me.expr(e1) / me.expr(e2));
binops.set("%",function(e1,e2) return me.expr(e1) % me.expr(e2));
binops.set("&",function(e1,e2) return me.expr(e1) & me.expr(e2));
binops.set("|",function(e1,e2) return me.expr(e1) | me.expr(e2));
binops.set("^",function(e1,e2) return me.expr(e1) ^ me.expr(e2));
binops.set("<<",function(e1,e2) return me.expr(e1) << me.expr(e2));
binops.set(">>",function(e1,e2) return me.expr(e1) >> me.expr(e2));
binops.set(">>>",function(e1,e2) return me.expr(e1) >>> me.expr(e2));
binops.set("==",function(e1,e2) return me.expr(e1) == me.expr(e2));
binops.set("!=",function(e1,e2) return me.expr(e1) != me.expr(e2));
binops.set(">=",function(e1,e2) return me.expr(e1) >= me.expr(e2));
binops.set("<=",function(e1,e2) return me.expr(e1) <= me.expr(e2));
binops.set(">",function(e1,e2) return me.expr(e1) > me.expr(e2));
binops.set("<",function(e1,e2) return me.expr(e1) < me.expr(e2));
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 IntIterator(me.expr(e1),me.expr(e2)));
binops.set("is",function(e1,e2) return #if (haxe_ver >= 4.2) Std.isOfType #else Std.is #end (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);
assignOp("/=",function(v1:Float,v2:Float) return v1 / v2);
assignOp("%=",function(v1:Float,v2:Float) return v1 % v2);
assignOp("&=",function(v1,v2) return v1 & v2);
assignOp("|=",function(v1,v2) return v1 | v2);
assignOp("^=",function(v1,v2) return v1 ^ v2);
assignOp("<<=",function(v1,v2) return v1 << v2);
assignOp(">>=",function(v1,v2) return v1 >> v2);
assignOp(">>>=",function(v1,v2) return v1 >>> v2);
}
function setVar( name : String, v : Dynamic ) {
variables.set(name, v);
return v;
}
function assign( e1 : Expr, e2 : Expr ) : Dynamic {
var v = expr(e2);
switch( Tools.expr(e1) ) {
case EIdent(id):
var l = locals.get(id);
if( l == null )
setVar(id,v)
else
l.r = v;
case EField(e,f):
v = set(expr(e),f,v);
case EArray(e, index):
var arr:Dynamic = expr(e);
var index:Dynamic = expr(index);
if (isMap(arr)) {
setMapValue(arr, index, v);
}
else {
arr[index] = v;
}
default:
error(EInvalidOp("="));
}
return v;
}
function assignOp( op, fop : Dynamic -> Dynamic -> Dynamic ) {
var me = this;
binops.set(op,function(e1,e2) return me.evalAssignOp(op,fop,e1,e2));
}
function evalAssignOp(op,fop,e1,e2) : Dynamic {
var v;
switch( Tools.expr(e1) ) {
case EIdent(id):
var l = locals.get(id);
v = fop(expr(e1),expr(e2));
if( l == null )
setVar(id,v)
else
l.r = v;
case EField(e,f):
var obj = expr(e);
v = fop(get(obj,f),expr(e2));
v = set(obj,f,v);
case EArray(e, index):
var arr:Dynamic = expr(e);
var index:Dynamic = expr(index);
if (isMap(arr)) {
v = fop(getMapValue(arr, index), expr(e2));
setMapValue(arr, index, v);
}
else {
v = fop(arr[index],expr(e2));
arr[index] = v;
}
default:
return error(EInvalidOp(op));
}
return v;
}
function increment( e : Expr, prefix : Bool, delta : Int ) : Dynamic {
#if hscriptPos
curExpr = e;
var e = e.e;
#end
switch(e) {
case EIdent(id):
var l = locals.get(id);
var v : Dynamic = (l == null) ? resolve(id) : l.r;
if( prefix ) {
v += delta;
if( l == null ) setVar(id,v) else l.r = v;
} else
if( l == null ) setVar(id,v + delta) else l.r = v + delta;
return v;
case EField(e,f):
var obj = expr(e);
var v : Dynamic = get(obj,f);
if( prefix ) {
v += delta;
set(obj,f,v);
} else
set(obj,f,v + delta);
return v;
case EArray(e, index):
var arr:Dynamic = expr(e);
var index:Dynamic = expr(index);
if (isMap(arr)) {
var v = getMapValue(arr, index);
if (prefix) {
v += delta;
setMapValue(arr, index, v);
}
else {
setMapValue(arr, index, v + delta);
}
return v;
}
else {
var v = arr[index];
if( prefix ) {
v += delta;
arr[index] = v;
} else
arr[index] = v + delta;
return v;
}
default:
return error(EInvalidOp((delta > 0)?"++":"--"));
}
}
public function execute( expr : Expr ) : Dynamic {
depth = 0;
locals = new Map();
declared = new Array();
return exprReturn(expr);
}
function exprReturn(e) : Dynamic {
try {
return expr(e);
} catch( e : Stop ) {
switch( e ) {
case SBreak: throw "Invalid break";
case SContinue: throw "Invalid continue";
case SReturn:
var v = returnValue;
returnValue = null;
return v;
}
}
return null;
}
function duplicate<T>( h : Map<String,T> ) {
var h2 = new Map();
for( k in h.keys() )
h2.set(k,h.get(k));
return h2;
}
function restore( old : Int ) {
while( declared.length > old ) {
var d = declared.pop();
locals.set(d.n,d.old);
}
}
inline function error(e : #if hscriptPos ErrorDef #else Error #end, rethrow=false ) : Dynamic {
#if hscriptPos var e = new Error(e, curExpr.pmin, curExpr.pmax, curExpr.origin, curExpr.line); #end
if( rethrow ) this.rethrow(e) else throw e;
return null;
}
inline function rethrow( e : Dynamic ) {
#if hl
hl.Api.rethrow(e);
#else
throw e;
#end
}
function resolve( id : String ) : Dynamic {
var v = variables.get(id);
if( v == null && !variables.exists(id) )
error(EUnknownVariable(id));
return v;
}
public function expr( e : Expr ) : Dynamic {
#if hscriptPos
curExpr = e;
var e = e.e;
#end
switch( e ) {
case EConst(c):
switch( c ) {
case CInt(v): return v;
case CFloat(f): return f;
case CString(s): return s;
}
case EIdent(id):
var l = locals.get(id);
if( l != null )
return l.r;
return resolve(id);
case EVar(n,_,e):
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):
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):
var fop = binops.get(op);
if( fop == null ) error(EInvalidOp(op));
return fop(e1,e2);
case EUnop(op,prefix,e):
switch(op) {
case "!":
return expr(e) != true;
case "-":
return -expr(e);
case "++":
return increment(e,prefix,1);
case "--":
return increment(e,prefix,-1);
case "~":
return ~expr(e);
default:
error(EInvalidOp(op));
}
case ECall(e,params):
var args = new Array();
for( p in params )
args.push(expr(p));
switch( Tools.expr(e) ) {
case EField(e,f):
var obj = expr(e);
if( obj == null ) error(EInvalidAccess(f));
return fcall(obj,f,args);
default:
return call(null,expr(e),args);
}
case EIf(econd,e1,e2):
return if( expr(econd) == true ) expr(e1) else if( e2 == null ) null else expr(e2);
case EWhile(econd,e):
whileLoop(econd,e);
return null;
case EDoWhile(econd,e):
doWhileLoop(econd,e);
return null;
case EFor(v,it,e):
forLoop(v,it,e);
return null;
case EForGen(it,e):
Tools.getKeyIterator(it, function(vk,vv,it) {
if( vk == null ) {
#if hscriptPos
curExpr = it;
#end
error(ECustom("Invalid for expression"));
return;
}
forKeyValueLoop(vk,vv,it,e);
});
return null;
case EBreak:
throw SBreak;
case EContinue:
throw SContinue;
case EReturn(e):
returnValue = e == null ? null : expr(e);
throw SReturn;
case EFunction(params,fexpr,name,_):
var capturedLocals = duplicate(locals);
var me = this;
var hasOpt = false, minParams = 0;
for( p in params )
if( p.opt )
hasOpt = true;
else
minParams++;
var f = function(args:Array<Dynamic>) {
if( ( (args == null) ? 0 : args.length ) != params.length ) {
if( args.length < minParams ) {
var str = "Invalid number of parameters. Got " + args.length + ", required " + minParams;
if( name != null ) str += " for function '" + name+"'";
error(ECustom(str));
}
// make sure mandatory args are forced
var args2 = [];
var extraParams = args.length - minParams;
var pos = 0;
for( p in params )
if( p.opt ) {
if( extraParams > 0 ) {
args2.push(args[pos++]);
extraParams--;
} else
args2.push(null);
} else
args2.push(args[pos++]);
args = args2;
}
var old = me.locals, depth = me.depth;
me.depth++;
me.locals = me.duplicate(capturedLocals);
for( i in 0...params.length )
me.locals.set(params[i].name,{ r : args[i] });
var r = null;
var oldDecl = declared.length;
if( inTry )
try {
r = me.exprReturn(fexpr);
} catch( e : Dynamic ) {
restore(oldDecl);
me.locals = old;
me.depth = depth;
#if neko
neko.Lib.rethrow(e);
#else
throw e;
#end
}
else
r = me.exprReturn(fexpr);
restore(oldDecl);
me.locals = old;
me.depth = depth;
return r;
};
var f = Reflect.makeVarArgs(f);
if( name != null ) {
if( depth == 0 ) {
// global function
variables.set(name, f);
} else {
// function-in-function is a local function
declared.push( { n : name, old : locals.get(name) } );
var ref = { r : f };
locals.set(name, ref);
capturedLocals.set(name, ref); // allow self-recursion
}
}
return f;
case EArrayDecl(arr):
if( arr.length > 0 && Tools.expr(arr[0]).match(EBinop("=>", _)) ) {
var keys = [];
var values = [];
for( e in arr ) {
switch(Tools.expr(e)) {
case EBinop("=>", eKey, eValue):
keys.push(expr(eKey));
values.push(expr(eValue));
default:
#if hscriptPos
curExpr = e;
#end
error(ECustom("Invalid map key=>value expression"));
}
}
return makeMap(keys,values);
} else {
var a = new Array();
for( e in arr )
a.push(expr(e));
return a;
}
case EArray(e, index):
var arr:Dynamic = expr(e);
var index:Dynamic = expr(index);
if( isMap(arr) )
return getMapValue(arr, index);
return arr[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;
var oldTry = inTry;
try {
inTry = true;
var v : Dynamic = expr(e);
restore(old);
inTry = oldTry;
return v;
} catch( err : Stop ) {
inTry = oldTry;
throw err;
} catch( err : Dynamic ) {
// restore vars
restore(old);
inTry = oldTry;
// declare 'v'
declared.push({ n : n, old : locals.get(n) });
locals.set(n,{ r : err });
var v : Dynamic = expr(ecatch);
restore(old);
return v;
}
case EObject(fl):
var o = {};
for( f in fl )
set(o,f.name,expr(f.e));
return o;
case ETernary(econd,e1,e2):
return if( expr(econd) == true ) expr(e1) else expr(e2);
case ESwitch(e, cases, def):
var val : Dynamic = expr(e);
var match = false;
for( c in cases ) {
for( v in c.values )
if( expr(v) == val ) {
match = true;
break;
}
if( match ) {
val = expr(c.expr);
break;
}
}
if( !match )
val = def == null ? null : expr(def);
return val;
case EMeta(meta, args, e):
return exprMeta(meta, args, e);
case ECheckType(e,_), ECast(e,_):
return expr(e);
}
return null;
}
function exprMeta(meta,args,e) : Dynamic {
return expr(e);
}
function doWhileLoop(econd,e) {
var old = declared.length;
do {
if( !loopRun(() -> expr(e)) )
break;
}
while( expr(econd) == true );
restore(old);
}
function whileLoop(econd,e) {
var old = declared.length;
while( expr(econd) == true ) {
if( !loopRun(() -> expr(e)) )
break;
}
restore(old);
}
function makeIterator( v : Dynamic ) : Iterator<Dynamic> {
#if js
// don't use try/catch (very slow)
if( v is Array )
return (v : Array<Dynamic>).iterator();
if( v.iterator != null ) v = v.iterator();
#else
#if (cpp) if ( v.iterator != null ) #end
try v = v.iterator() catch( e : Dynamic ) {};
#end
if( v.hasNext == null || v.next == null ) error(EInvalidIterator(v));
return v;
}
function makeKeyValueIterator( v : Dynamic ) : KeyValueIterator<Dynamic,Dynamic> {
#if js
// don't use try/catch (very slow)
if( v is Array )
return (v : Array<Dynamic>).keyValueIterator();
if( v.keyValueIterator != null ) v = v.keyValueIterator();
#else
try v = v.keyValueIterator() catch( e : Dynamic ) {};
#end
if( v.hasNext == null || v.next == null ) 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(n,{ r : it.next() });
if( !loopRun(() -> expr(e)) )
break;
}
restore(old);
}
function forKeyValueLoop(vk,vv,it,e) {
var old = declared.length;
declared.push({ n : vk, old : locals.get(vk) });
declared.push({ n : vv, old : locals.get(vv) });
var it = makeKeyValueIterator(expr(it));
while( it.hasNext() ) {
var v = it.next();
locals.set(vk,{ r : v.key });
locals.set(vv,{ r : v.value });
if( !loopRun(() -> expr(e)) )
break;
}
restore(old);
}
inline function loopRun( f : Void -> Void ) {
var cont = true;
try {
f();
} catch( err : Stop ) {
switch( err ) {
case SContinue:
case SBreak:
cont = false;
case SReturn:
throw err;
}
}
return cont;
}
inline function isMap(o:Dynamic):Bool {
return (o is IMap);
}
inline function getMapValue(map:Dynamic, key:Dynamic):Dynamic {
return cast(map, haxe.Constraints.IMap<Dynamic, Dynamic>).get(key);
}
inline function setMapValue(map:Dynamic, key:Dynamic, value:Dynamic):Void {
cast(map, haxe.Constraints.IMap<Dynamic, Dynamic>).set(key, value);
}
function makeMap( keys : Array<Dynamic>, values : Array<Dynamic> ) : Dynamic {
var isAllString:Bool = true;
var isAllInt:Bool = true;
var isAllObject:Bool = true;
var isAllEnum:Bool = true;
for( key in keys ) {
isAllString = isAllString && (key is String);
isAllInt = isAllInt && (key is Int);
isAllObject = isAllObject && Reflect.isObject(key);
isAllEnum = isAllEnum && Reflect.isEnumValue(key);
}
#if (haxe_ver >= 4.1)
if( isAllInt ) {
var m = new Map<Int,Dynamic>();
for( i => key in keys )
m.set(key, values[i]);
return m;
}
if( isAllString ) {
var m = new Map<String,Dynamic>();
for( i => key in keys )
m.set(key, values[i]);
return m;
}
if( isAllEnum ) {
var m = new haxe.ds.EnumValueMap<Dynamic,Dynamic>();
for( i => key in keys )
m.set(key, values[i]);
return m;
}
if( isAllObject ) {
var m = new Map<{},Dynamic>();
for( i => key in keys )
m.set(key, values[i]);
return m;
}
#else
var m:Dynamic = {
if ( isAllInt ) new haxe.ds.IntMap<Dynamic>();
else if ( isAllString ) new haxe.ds.StringMap<Dynamic>();
else if ( isAllEnum ) new haxe.ds.EnumValueMap<Dynamic, Dynamic>();
else if ( isAllObject ) new haxe.ds.ObjectMap<Dynamic, Dynamic>();
else null;
}
if( m != null ) {
for ( n in 0...keys.length )
setMapValue(m, keys[n], values[n]);
return m;
}
#end
error(ECustom("Invalid map keys "+keys));
return null;
}
function get( o : Dynamic, f : String ) : Dynamic {
if ( o == null ) error(EInvalidAccess(f));
return {
#if php
// https://github.com/HaxeFoundation/haxe/issues/4915
try {
Reflect.getProperty(o, f);
} catch (e:Dynamic) {
Reflect.field(o, f);
}
#else
Reflect.getProperty(o, f);
#end
}
}
function set( o : Dynamic, f : String, v : Dynamic ) : Dynamic {
if( o == null ) error(EInvalidAccess(f));
Reflect.setProperty(o,f,v);
return v;
}
function fcall( o : Dynamic, f : String, args : Array<Dynamic> ) : Dynamic {
return call(o, get(o, f), args);
}
function call( o : Dynamic, f : Dynamic, args : Array<Dynamic> ) : Dynamic {
return Reflect.callMethod(o,f,args);
}
function cnew( cl : String, args : Array<Dynamic> ) : Dynamic {
var c = Type.resolveClass(cl);
if( c == null ) c = resolve(cl);
return Type.createInstance(c,args);
}
}

View File

@ -0,0 +1,448 @@
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<String,Bool>;
var localNames : Map<String,String>;
var hasBreakContinue : Int = 0;
var hasReturn : Bool;
override function execute( expr : Expr ) : Dynamic {
depth = 0;
localNames = new Map();
var str = '(($$i) => ${exprValue(expr)})';
var f : Dynamic -> Dynamic = js.Lib.eval(str);
return f(this);
}
public static function defineArrayExtensions() {
var arr : Dynamic = Array;
arr.prototype.copy = function() { var v : Array<Dynamic> = js.Lib.nativeThis; return [for( v in v ) v]; };
arr.prototype.contains = function(i) return js.Lib.nativeThis.indexOf(i) >= 0;
arr.prototype.remove = function(x) return @:privateAccess HxOverrides.remove(js.Lib.nativeThis, x);
arr.prototype.resize = function(len) js.Lib.nativeThis.length = len;
}
function escapeString(s:String) {
return s.split("\\").join("\\\\").split("\r").join("\\r").split("\n").join("\\n").split('"').join('\\"');
}
function handleRBC( e : Expr ) {
function mkRBC(code:Int,?value) {
var fields = [{ name : "$", e : Tools.mk(EConst(CInt(code)),e) }];
if( value != null ) fields.push({name:"$val",e:value});
return Tools.mk(EThrow(Tools.mk(EObject(fields),e)),e);
}
switch( Tools.expr(e) ) {
case EReturn(v):
hasReturn = true;
return mkRBC(1,v);
case EBreak if( hasBreakContinue >= 0 ):
hasBreakContinue |= 1;
return mkRBC(2);
case EContinue if( hasBreakContinue >= 0 ):
hasBreakContinue |= 2;
return mkRBC(3);
case EWhile(_), EFor(_), EDoWhile(_):
var prev = hasBreakContinue;
hasBreakContinue = -1;
e = Tools.map(e, handleRBC);
hasBreakContinue = prev;
return e;
case EFunction(_):
return e;
default:
return Tools.map(e, handleRBC);
}
}
function exprValue( expr : Expr ) {
switch( Tools.expr(expr) ) {
case EBlock([]):
return "null";
case EIf(cond,e1,e2):
return exprJS(Tools.mk(ETernary(cond,e1,e2),expr));
case EBlock(el):
var el = [for( e in el ) handleRBC(e)];
var last = el[el.length-1];
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)})()';
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)} })()';
case EVar(_,_,e):
return e == null ? "null" : '(${exprValue(e)},null)';
case EWhile(_), EFor(_), EDoWhile(_), EThrow(_):
expr = handleRBC(expr);
return '(() => {${exprJS(expr)}})()';
case EMeta(_,_,e), ECheckType(e,_):
return exprValue(e);
case EFunction(_,_,name,_) if( name != null ):
return '(() => {${exprJS(expr)}})()';
default:
return exprJS(expr);
}
}
function exprBlock( expr : Expr ) {
switch( Tools.expr(expr) ) {
case EBlock(_):
return exprJS(expr);
default:
return '{${exprJS(expr)};}';
}
}
function addPos( estr : String ) {
#if hscriptPos
var expr = curExpr;
var p = '{pmin:,pmax:,origin:"",line:}';
return '($$i._p(${expr.pmin},${expr.pmax},"${expr.origin}",${expr.line}),$estr)';
#else
return estr;
#end
}
function isContext(v:String) {
return ctx != null && Reflect.hasField(ctx,v);
}
function isProperty(f:String) {
return properties == null || properties.get(f);
}
function exprCond( e : Expr ) {
return switch( Tools.expr(e) ) {
case EBinop("=="|"!="|">="|">"|"<="|"<"|"&&"|"||",_): exprValue(e);
default: '(${exprOp(e)} == true)';
}
}
function exprOp( e : Expr ) {
return switch( Tools.expr(e) ) {
case EBinop(_), EUnop(_): '(${exprValue(e)})';
default: exprValue(e);
}
}
function declLocal( n : String ) {
if( !localNames.exists(n) ) {
localNames.set(n, n);
return n;
}
var c = 2;
while( localNames.exists(n+c) ) c++;
localNames.set(n, n+c);
return n+c;
}
function exprBreakContinue( e, needBlock=true ) {
var prevBC = hasBreakContinue;
hasBreakContinue = 0;
var estr = needBlock ? exprBlock(e) : exprJS(e);
if( hasBreakContinue != 0 ) {
var checks = [];
if( hasBreakContinue & 1 != 0 )
checks.push("if( $e.$ == 2 ) break;");
if( hasBreakContinue & 2 != 0 )
checks.push("if( $e.$ == 3 ) continue;");
if( !needBlock && !Tools.expr(e).match(EBlock(_)) )
estr = '{$estr;}';
estr = 'try $estr catch( $$e ) { ${checks.join('')} throw $$e; }';
}
hasBreakContinue = prevBC;
return estr;
}
function exprJS( expr : Expr ) : String {
#if hscriptPos
curExpr = expr;
var expr = expr.e;
#end
switch( expr ) {
case EConst(c):
return switch c {
case CInt(v): Std.string(v);
case CFloat(f): Std.string(f);
case CString(s): '"'+escapeString(s)+'"';
}
case EIdent(v):
var v2 = localNames.get(v);
if( v2 != null )
return v2;
if( isContext(v) )
return '$$i.ctx.$v';
switch( v ) {
case "null", "true", "false": return v;
default:
}
return '$$i.resolve("$v")';
case EVar(n, t, e):
n = declLocal(n);
return e == null ? 'let $n' : 'let $n = ${exprValue(e)}';
case EParent(e):
return '(${exprValue(e)})';
case EBlock(el):
var old = localNames.copy();
// pre define name functions
for( e in el )
switch( Tools.expr(e) ) {
case EFunction(_,_,name,_): declLocal(name);
default:
}
var buf = new StringBuf();
buf.add('{');
for( e in el ) {
buf.add(exprJS(e));
buf.add(";");
}
buf.add('}');
localNames = 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):
switch( op ) {
case "+","-","*","/","%","&","|","^",">>","<<",">>>","==","!=",">=","<=",">","<":
return '${exprOp(e1)} $op ${exprOp(e2)}';
case "||","&&":
return '(${exprCond(e1)} $op ${exprCond(e2)})';
case "=":
switch( Tools.expr(e1) ) {
case EIdent(id) if( localNames.exists(id) ):
return localNames.get(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):
return addPos('$$i.set(${exprValue(e)},"$f")');
case EArray(e, index):
return '$$i.setArray(${exprValue(e)},${exprValue(index)},${exprValue(e2)})';
default:
error(EInvalidOp("="));
}
case "+=","-=","*=","/=","%=","|=","&=","^=","<<=",">>=",">>>=":
var aop = op.substr(0, op.length - 1);
switch( Tools.expr(e1) ) {
case EIdent(id) if( localNames.exists(id) ):
return localNames.get(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):
return '(($$o,$$v) => $$i.set($$o,"$f",$$i.get($$o,"$f") $aop $$v)(${exprValue(e)},${exprValue(e2)})';
case EArray(e, index):
return '(($$a,$$idx,$$v) => $$i.setArray($$a,$$idx,$$i.getArray($$a,$$idx) $aop $$v))(${exprValue(e)},${exprValue(index)},${exprValue(e2)})';
default:
error(EInvalidOp(op));
}
case "...":
return '$$i.intIterator(${exprValue(e1)},${exprValue(e2)})';
case "is":
return '$$i.isOfType(${exprValue(e1)},${exprValue(e2)})';
default:
error(EInvalidOp(op));
}
case EUnop(op, prefix, e):
switch( op ) {
case "!":
return '${exprValue(e)} != true';
case "-","~":
return op+exprOp(e);
case "++", "--":
switch( Tools.expr(e) ) {
case EIdent(id) if( localNames.exists(id) ):
id = localNames.get(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));
case EIdent(id):
var op = op.charAt(1);
return '(($$v) => ($$i.setVar("$id",$$v $op 1),$$v))($$i.resolve("$id"))';
case EArray(e, index):
var op = op.charAt(0);
var v = declLocal("$v");
var str = '(($$a,$$idx) => { let $v = $$i.getArray($$a,$$idx); $$i.setArray($$a,$$idx,$v $op 1); return $v; })(${exprValue(e)},${exprValue(index)})';
localNames.remove(v);
return str;
case EField(e, f):
var v = declLocal("$v");
var str = '(($$o) => { let $v = $$i.get($$o,"$f"); $$i.set($$o,"$f",$$v $op 1); return $v; })(${exprValue(e)})';
localNames.remove(v);
return str;
default:
error(EInvalidOp(op));
}
default:
error(EInvalidOp(op));
}
case ECall(e, params):
var args = [for( p in params ) exprValue(p)];
switch( Tools.expr(e) ) {
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( localNames.exists(id) ):
id = localNames.get(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(',')}])';
}
case EIf(cond,e1,e2):
return 'if( ${exprCond(cond)} ) ${exprJS(e1)}'+(e2 == null ? "" : 'else ${exprJS(e2)}');
case ETernary(cond, e1, e2):
return '(${exprCond(cond)} ? ${exprValue(e1)} : ${e2 == null ? 'undefined' : exprValue(e2)})';
case EWhile(cond, e):
return 'while( ${exprValue(cond)} ) ${exprBreakContinue(e)}';
case EDoWhile(cond, e):
return 'do ${exprBreakContinue(e)} while( ${exprCond(cond)} )';
case EFor(v, it, e):
v = declLocal(v);
var block = exprBreakContinue(e,false);
localNames.remove(v);
var iter = '$$i.makeIterator(${exprValue(it)})';
var it = declLocal("$it");
var str = '{ let $it = ${addPos(iter)}; while( $it.hasNext() ) { let $v = $it.next(); $block; } }';
localNames.remove(it);
return str;
case EBreak:
return 'break';
case EContinue:
return 'continue';
case EReturn(null):
return 'return';
case EReturn(e):
return 'return '+exprValue(e);
case EArray(e, index):
return '$$i.getArray(${exprValue(e)},${exprValue(index)})';
case EArrayDecl(arr):
if( arr.length > 0 && Tools.expr(arr[0]).match(EBinop("=>", _)) ) {
var keys = [], values = [];
for( e in arr ) {
switch(Tools.expr(e)) {
case EBinop("=>", eKey, eValue):
keys.push(exprValue(eKey));
values.push(exprValue(eValue));
default:
#if hscriptPos
curExpr = e;
#end
error(ECustom("Invalid map key=>value expression"));
}
}
return '$$i.makeMap([${keys.join(',')}],[${values.join(',')}])';
}
return '['+[for( e in arr ) exprValue(e)].join(',')+']';
case ENew(cl, params):
var args = [for( e in params ) exprValue(e)];
return '$$i.cnew("$cl",[${args.join(',')}])';
case EThrow(e):
return "throw "+exprValue(e);
case ETry(e, v, t, ecatch):
v = declLocal(v);
var ec = exprBlock(ecatch);
localNames.remove(v);
return 'try ${exprBlock(e)} catch( $v ) $ec';
case EObject(fl):
var fields = [for( f in fl ) f.name+":"+exprValue(f.e)];
return '{${fields.join(',')}}'; // do not use 'set' here
case EMeta(_, _, e), ECheckType(e,_):
return exprJS(e);
case EFunction(args, e, name, ret):
var prev = localNames.copy();
if( name != null && !localNames.exists(name) )
declLocal(name);
for( a in args )
localNames.set(a.name, a.name);
var prevReturn = hasReturn;
hasReturn = false;
var bl = exprBlock(e);
if( hasReturn )
bl = '{ try { $bl } catch( $$e ) { if( $$e.$$ == 1 ) return $$e.$$val; throw $$e; }}';
hasReturn = prevReturn;
localNames = prev;
var fstr = 'function(${[for( a in args ) a.name].join(",")}) $bl';
if( name != null )
fstr = 'let $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(handleRBC(v))}'].join(" || ")} ) return ${exprValue(handleRBC(c.expr))};'];
if( defaultExpr != null )
checks.push('return '+exprValue(handleRBC(defaultExpr)));
return '(($$v) => { ${[for( c in checks ) c+";"].join(" ")} })(${exprValue(e)})';
default:
throw "TODO";
}
}
function fcall2( o : Dynamic, f : String, args : Array<Dynamic> ) : Dynamic {
if( o == null ) {
error(EInvalidAccess(f));
return null;
}
return fcall(o,f,args);
}
function getArray( arr : Dynamic, index : Dynamic ) {
return isMap(arr) ? getMapValue(arr,index) : arr[index];
}
function setArray( arr : Dynamic, index : Dynamic, v : Dynamic ) {
if(isMap(arr) )
setMapValue(arr, index, v);
else
arr[index] = v;
return v;
}
function intIterator(v1,v2) {
return new IntIterator(v1,v2);
}
function isOfType(v1,v2) {
return Std.isOfType(v1,v2);
}
#if hscriptPos
function _p( pmin, pmax, origin, line ) {
curExpr = { e : null, pmin : pmin, pmax: pmax, origin:origin, line:line };
}
#end
}

View File

@ -0,0 +1,262 @@
/*
* Copyright (C)2008-2017 Haxe Foundation
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*/
package hscript;
import hscript.Expr.Error;
#if hscriptPos
import hscript.Expr.ErrorDef;
#end
import haxe.macro.Expr;
class Macro {
var p : Position;
var binops : Map<String,Binop>;
var unops : Map<String,Unop>;
public function new(pos) {
p = pos;
binops = new Map();
unops = new Map();
for( c in Type.getEnumConstructs(Binop) ) {
if( c == "OpAssignOp" ) continue;
var op = Type.createEnum(Binop, c);
var assign = false;
var str = switch( op ) {
case OpAdd: assign = true; "+";
case OpMult: assign = true; "*";
case OpDiv: assign = true; "/";
case OpSub: assign = true; "-";
case OpAssign: "=";
case OpEq: "==";
case OpNotEq: "!=";
case OpGt: ">";
case OpGte: ">=";
case OpLt: "<";
case OpLte: "<=";
case OpAnd: assign = true; "&";
case OpOr: assign = true; "|";
case OpXor: assign = true; "^";
case OpBoolAnd: "&&";
case OpBoolOr: "||";
case OpShl: assign = true; "<<";
case OpShr: assign = true; ">>";
case OpUShr: assign = true; ">>>";
case OpMod: assign = true; "%";
case OpAssignOp(_): "";
case OpInterval: "...";
case OpArrow: "=>";
#if (haxe_ver >= 4)
case OpIn: "in";
#end
default:
continue;
};
binops.set(str, op);
if( assign )
binops.set(str + "=", OpAssignOp(op));
}
for( c in Type.getEnumConstructs(Unop) ) {
var op = Type.createEnum(Unop, c);
var str = switch( op ) {
case OpNot: "!";
case OpNeg: "-";
case OpNegBits: "~";
case OpIncrement: "++";
case OpDecrement: "--";
#if (haxe_ver >= 4.2)
case OpSpread: continue;
#end
}
unops.set(str, op);
}
}
function map<T,R>( a : Array<T>, f : T -> R ) : Array<R> {
var b = new Array();
for( x in a )
b.push(f(x));
return b;
}
function convertType( t : Expr.CType ) : ComplexType {
return switch( t ) {
case CTOpt(t): TOptional(convertType(t));
case CTPath(pack, args):
var params = [];
if( args != null ) {
for( t in args )
params.push(switch( t ) {
case CTExpr(e): TPExpr(convert(e));
default: TPType(convertType(t));
});
}
var pack = pack.copy();
TPath({
pack : pack,
name : pack.pop(),
params : params,
sub : null,
});
case CTParent(t): TParent(convertType(t));
case CTFun(args, ret):
TFunction(map(args,convertType), convertType(ret));
case CTNamed(name, convertType(_) => ct):
#if (haxe_ver >= 4)
TNamed(name, ct);
#else
ct;
#end
case CTAnon(fields):
var tf = [];
for( f in fields ) {
var meta = f.meta == null ? [] : [for( m in f.meta ) { name : m.name, params : m.params == null ? [] : [for( e in m.params ) convert(e)], pos : p }];
tf.push( { name : f.name, meta : meta, doc : null, access : [], kind : FVar(convertType(f.t), null), pos : p } );
}
TAnonymous(tf);
case CTExpr(_):
throw "assert";
};
}
public function convert( e : hscript.Expr ) : Expr {
return { expr : switch( #if hscriptPos e.e #else e #end ) {
case EConst(c):
EConst(switch(c) {
case CInt(v): CInt(Std.string(v));
case CFloat(f): CFloat(Std.string(f));
case CString(s): CString(s);
});
case EIdent(v):
EConst(CIdent(v));
case EVar(n, t, e):
EVars([ { name : n, expr : if( e == null ) null else convert(e), type : if( t == null ) null else convertType(t) } ]);
case EParent(e):
EParenthesis(convert(e));
case EBlock(el):
EBlock(map(el,convert));
case EField(e, f):
EField(convert(e), f);
case EBinop(op, e1, e2):
var b = binops.get(op);
if( b == null ) throw EInvalidOp(op);
EBinop(b, convert(e1), convert(e2));
case EUnop(op, prefix, e):
var u = unops.get(op);
if( u == null ) throw EInvalidOp(op);
EUnop(u, !prefix, convert(e));
case ECall(e, params):
ECall(convert(e), map(params, convert));
case EIf(c, e1, e2):
EIf(convert(c), convert(e1), e2 == null ? null : convert(e2));
case EWhile(c, e):
EWhile(convert(c), convert(e), true);
case EDoWhile(c, e):
EWhile(convert(c), convert(e), false);
case EFor(v, it, efor):
var p = #if (!macro && hscriptPos) { file : p.file, min : e.pmin, max : e.pmax } #else p #end;
EFor({ expr : EBinop(OpIn,{ expr : EConst(CIdent(v)), pos : p },convert(it)), pos : p }, convert(efor));
case EForGen(it, efor):
EFor(convert(it), convert(efor));
case EBreak:
EBreak;
case EContinue:
EContinue;
case EFunction(args, e, name, ret):
var targs = [];
for( a in args )
targs.push( {
name : a.name,
type : a.t == null ? null : convertType(a.t),
opt : false,
value : null,
});
EFunction(#if haxe4 name != null ? FNamed(name,false) : FAnonymous #else name #end, {
params : [],
args : targs,
expr : convert(e),
ret : ret == null ? null : convertType(ret),
});
case EReturn(e):
EReturn(e == null ? null : convert(e));
case EArray(e, index):
EArray(convert(e), convert(index));
case EArrayDecl(el):
EArrayDecl(map(el,convert));
case ENew(cl, params):
var pack = cl.split(".");
ENew( { pack : pack, name : pack.pop(), params : [], sub : null }, map(params, convert));
case EThrow(e):
EThrow(convert(e));
case ETry(e, v, t, ec):
ETry(convert(e), [ { type : convertType(t), name : v, expr : convert(ec) } ]);
case EObject(fields):
var tf = [];
for( f in fields )
tf.push( { field : f.name, expr : convert(f.e) } );
EObjectDecl(tf);
case ETernary(cond, e1, e2):
ETernary(convert(cond), convert(e1), convert(e2));
case ESwitch(e, cases, edef):
ESwitch(convert(e), [for( c in cases ) { values : [for( v in c.values ) convert(v)], expr : convert(c.expr) } ], edef == null ? null : convert(edef));
case EMeta(m, params, esub):
var mpos = #if (!macro && hscriptPos) { file : p.file, min : e.pmin, max : e.pmax } #else p #end;
EMeta({ name : m, params : params == null ? [] : [for( p in params ) convert(p)], pos : mpos }, convert(esub));
case ECheckType(e, t):
ECheckType(convert(e), convertType(t));
case ECast(e, t):
ECast(convert(e), t == null ? null : convertType(t));
}, pos : #if (!macro && hscriptPos) { file : p.file, min : e.pmin, max : e.pmax } #else p #end }
}
public function typeEncode( t : ComplexType ) : Expr.CType {
switch( t ) {
case TPath(p):
var path = p.pack.copy();
path.push(p.name);
if( p.sub != null ) path.push(p.sub);
var params : Array<Expr.CType> = null;
if( p.params != null && p.params.length > 0 )
params = [for( p in p.params ) switch( p ) {
case TPType(t): typeEncode(t);
case TPExpr(e): CTExpr(null); // TODO : macro expr to hscript expr
}];
return CTPath(path,params);
case TFunction(args, ret):
return CTFun([for( a in args ) typeEncode(a)], typeEncode(ret));
case TAnonymous(fields):
return CTAnon([for( f in fields ) { name : f.name, t : switch( f.kind ) {
case FVar(t): typeEncode(t);
case FProp(get,set,t,_): typeEncode(t);
case FFun(f): CTFun([for( a in f.args ) typeEncode(a.type)],typeEncode(f.ret));
}}]);
case TParent(t):
return CTParent(typeEncode(t));
case TOptional(t):
return CTOpt(typeEncode(t));
case TNamed(n, t):
return CTNamed(n,typeEncode(t));
case TIntersection(_), TExtend(_):
throw "assert";
}
}
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,374 @@
/*
* Copyright (C)2008-2017 Haxe Foundation
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*/
package hscript;
import hscript.Expr;
class Printer {
var buf : StringBuf;
var tabs : String;
public function new() {
}
public function exprToString( e : Expr ) {
buf = new StringBuf();
tabs = "";
expr(e);
return buf.toString();
}
public function typeToString( t : CType ) {
buf = new StringBuf();
tabs = "";
type(t);
return buf.toString();
}
inline function add<T>(s:T) buf.add(s);
function type( t : CType ) {
switch( t ) {
case CTOpt(t):
add('?');
type(t);
case CTPath(path, params):
add(path.join("."));
if( params != null ) {
add("<");
var first = true;
for( p in params ) {
if( first ) first = false else add(", ");
type(p);
}
add(">");
}
case CTNamed(name, t):
add(name);
add(':');
type(t);
case CTFun(args, ret) if (Lambda.exists(args, function (a) return a.match(CTNamed(_, _)))):
add('(');
for (a in args)
switch a {
case CTNamed(_, _): type(a);
default: type(CTNamed('_', a));
}
add(')->');
type(ret);
case CTFun(args, ret):
if( args.length == 0 )
add("Void -> ");
else {
for( a in args ) {
type(a);
add(" -> ");
}
}
type(ret);
case CTAnon(fields):
add("{");
var first = true;
for( f in fields ) {
if( first ) { first = false; add(" "); } else add(", ");
add(f.name + " : ");
type(f.t);
}
add(first ? "}" : " }");
case CTParent(t):
add("(");
type(t);
add(")");
case CTExpr(e):
expr(e);
}
}
function addType( t : CType ) {
if( t != null ) {
add(" : ");
type(t);
}
}
function addConst( c : Const ) {
switch( c ) {
case CInt(i): add(i);
case CFloat(f): add(f);
case CString(s): add('"'); add(s.split('"').join('\\"').split("\n").join("\\n").split("\r").join("\\r").split("\t").join("\\t")); add('"');
}
}
function expr( e : Expr ) {
if( e == null ) {
add("??NULL??");
return;
}
switch( #if hscriptPos e.e #else e #end ) {
case EConst(c): addConst(c);
case EIdent(v):
add(v);
case EVar(n, t, e):
add("var " + n);
addType(t);
if( e != null ) {
add(" = ");
expr(e);
}
case EParent(e):
add("("); expr(e); add(")");
case EBlock(el):
if( el.length == 0 ) {
add("{}");
} else {
tabs += "\t";
add("{\n");
for( e in el ) {
add(tabs);
expr(e);
add(";\n");
}
tabs = tabs.substr(1);
add("}");
}
case EField(e, f):
expr(e);
add("." + f);
case EBinop(op, e1, e2):
expr(e1);
add(" " + op + " ");
expr(e2);
case EUnop(op, pre, e):
if( pre ) {
add(op);
expr(e);
} else {
expr(e);
add(op);
}
case ECall(e, args):
if( e == null )
expr(e);
else switch( #if hscriptPos e.e #else e #end ) {
case EField(_), EIdent(_), EConst(_):
expr(e);
default:
add("(");
expr(e);
add(")");
}
add("(");
var first = true;
for( a in args ) {
if( first ) first = false else add(", ");
expr(a);
}
add(")");
case EIf(cond,e1,e2):
add("if( ");
expr(cond);
add(" ) ");
expr(e1);
if( e2 != null ) {
add(" else ");
expr(e2);
}
case EWhile(cond,e):
add("while( ");
expr(cond);
add(" ) ");
expr(e);
case EDoWhile(cond,e):
add("do ");
expr(e);
add(" while ( ");
expr(cond);
add(" )");
case EFor(v, it, e):
add("for( "+v+" in ");
expr(it);
add(" ) ");
expr(e);
case EForGen(it, e):
add("for( ");
expr(it);
add(" ) ");
expr(e);
case EBreak:
add("break");
case EContinue:
add("continue");
case EFunction(params, e, name, ret):
add("function");
if( name != null )
add(" " + name);
add("(");
var first = true;
for( a in params ) {
if( first ) first = false else add(", ");
if( a.opt ) add("?");
add(a.name);
addType(a.t);
}
add(")");
addType(ret);
add(" ");
expr(e);
case EReturn(e):
add("return");
if( e != null ) {
add(" ");
expr(e);
}
case EArray(e,index):
expr(e);
add("[");
expr(index);
add("]");
case EArrayDecl(el):
add("[");
var first = true;
for( e in el ) {
if( first ) first = false else add(", ");
expr(e);
}
add("]");
case ENew(cl, args):
add("new " + cl + "(");
var first = true;
for( e in args ) {
if( first ) first = false else add(", ");
expr(e);
}
add(")");
case EThrow(e):
add("throw ");
expr(e);
case ETry(e, v, t, ecatch):
add("try ");
expr(e);
add(" catch( " + v);
addType(t);
add(") ");
expr(ecatch);
case EObject(fl):
if( fl.length == 0 ) {
add("{}");
} else {
tabs += "\t";
add("{\n");
for( f in fl ) {
add(tabs);
add(f.name+" : ");
expr(f.e);
add(",\n");
}
tabs = tabs.substr(1);
add("}");
}
case ETernary(c,e1,e2):
expr(c);
add(" ? ");
expr(e1);
add(" : ");
expr(e2);
case ESwitch(e, cases, def):
add("switch( ");
expr(e);
add(") {");
for( c in cases ) {
add("case ");
var first = true;
for( v in c.values ) {
if( first ) first = false else add(", ");
expr(v);
}
add(": ");
expr(c.expr);
add(";\n");
}
if( def != null ) {
add("default: ");
expr(def);
add(";\n");
}
add("}");
case EMeta(name, args, e):
add("@");
add(name);
if( args != null && args.length > 0 ) {
add("(");
var first = true;
for( a in args ) {
if( first ) first = false else add(", ");
expr(e);
}
add(")");
}
add(" ");
expr(e);
case ECheckType(e, t):
add("(");
expr(e);
add(" : ");
addType(t);
add(")");
case ECast(e,t):
if( t == null ) {
add("cast ");
expr(e);
} else {
add("cast(");
expr(e);
add(",");
addType(t);
add(")");
}
}
}
public static function toString( e : Expr ) {
return new Printer().exprToString(e);
}
public static function errorToString( e : Expr.Error ) {
var message = switch( #if hscriptPos e.e #else e #end ) {
case EInvalidChar(c): "Invalid character: '"+(StringTools.isEof(c) ? "EOF" : String.fromCharCode(c))+"' ("+c+")";
case EUnexpected(s): "Unexpected token: \""+s+"\"";
case EUnterminatedString: "Unterminated string";
case EUnterminatedComment: "Unterminated comment";
case EInvalidPreprocessor(str): "Invalid preprocessor (" + str + ")";
case EUnknownVariable(v): "Unknown variable: "+v;
case EInvalidIterator(v): "Invalid iterator: "+v;
case EInvalidOp(op): "Invalid operator: "+op;
case EInvalidAccess(f): "Invalid access to field " + f;
case ECustom(msg): msg;
};
#if hscriptPos
return e.origin + ":" + e.line + ": " + message;
#else
return message;
#end
}
}

View File

@ -0,0 +1,133 @@
/*
* Copyright (C)2008-2017 Haxe Foundation
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*/
package hscript;
import hscript.Expr;
class Tools {
public static function iter( e : Expr, f : Expr -> Void ) {
switch( expr(e) ) {
case EConst(_), EIdent(_):
case EVar(_, _, e): if( e != null ) f(e);
case EParent(e): f(e);
case EBlock(el): for( e in el ) f(e);
case EField(e, _): f(e);
case EBinop(_, e1, e2): f(e1); f(e2);
case EUnop(_, _, e): f(e);
case ECall(e, args): f(e); for( a in args ) f(a);
case EIf(c, e1, e2): f(c); f(e1); if( e2 != null ) f(e2);
case EWhile(c, e): f(c); f(e);
case EDoWhile(c, e): f(c); f(e);
case EFor(_, it, e): f(it); f(e);
case EForGen(it, e): f(it); f(e);
case EBreak,EContinue:
case EFunction(_, e, _, _): f(e);
case EReturn(e): if( e != null ) f(e);
case EArray(e, i): f(e); f(i);
case EArrayDecl(el): for( e in el ) f(e);
case ENew(_,el): for( e in el ) f(e);
case EThrow(e): f(e);
case ETry(e, _, _, c): f(e); f(c);
case EObject(fl): for( fi in fl ) f(fi.e);
case ETernary(c, e1, e2): f(c); f(e1); f(e2);
case ESwitch(e, cases, def):
f(e);
for( c in cases ) {
for( v in c.values ) f(v);
f(c.expr);
}
if( def != null ) f(def);
case EMeta(name, args, e): if( args != null ) for( a in args ) f(a); f(e);
case ECheckType(e,_): f(e);
case ECast(e,_): f(e);
}
}
public static function map( e : Expr, f : Expr -> Expr ) {
var edef = switch( expr(e) ) {
case EConst(_), EIdent(_), EBreak, EContinue: expr(e);
case EVar(n, t, e): EVar(n, t, if( e != null ) f(e) else null);
case EParent(e): EParent(f(e));
case EBlock(el): EBlock([for( e in el ) f(e)]);
case EField(e, fi): EField(f(e),fi);
case EBinop(op, e1, e2): EBinop(op, f(e1), f(e2));
case EUnop(op, pre, e): EUnop(op, pre, f(e));
case ECall(e, args): ECall(f(e),[for( a in args ) f(a)]);
case EIf(c, e1, e2): EIf(f(c),f(e1),if( e2 != null ) f(e2) else null);
case EWhile(c, e): EWhile(f(c),f(e));
case EDoWhile(c, e): EDoWhile(f(c),f(e));
case EFor(v, it, e): EFor(v, f(it), f(e));
case EForGen(it, e): EForGen(f(it), f(e));
case EFunction(args, e, name, t): EFunction(args, f(e), name, t);
case EReturn(e): EReturn(if( e != null ) f(e) else null);
case EArray(e, i): EArray(f(e),f(i));
case EArrayDecl(el): EArrayDecl([for( e in el ) f(e)]);
case ENew(cl,el): ENew(cl,[for( e in el ) f(e)]);
case EThrow(e): EThrow(f(e));
case ETry(e, v, t, c): ETry(f(e), v, t, f(c));
case EObject(fl): EObject([for( fi in fl ) { name : fi.name, e : f(fi.e) }]);
case ETernary(c, e1, e2): ETernary(f(c), f(e1), f(e2));
case ESwitch(e, cases, def): ESwitch(f(e), [for( c in cases ) { values : [for( v in c.values ) f(v)], expr : f(c.expr) } ], def == null ? null : f(def));
case EMeta(name, args, e): EMeta(name, args == null ? null : [for( a in args ) f(a)], f(e));
case ECheckType(e,t): ECheckType(f(e), t);
case ECast(e,t): ECast(f(e),t);
}
return mk(edef, e);
}
public static inline function expr( e : Expr ) : ExprDef {
#if hscriptPos
return e.e;
#else
return e;
#end
}
public static inline function mk( e : ExprDef, p : Expr ) {
#if hscriptPos
return { e : e, pmin : p.pmin, pmax : p.pmax, origin : p.origin, line : p.line };
#else
return e;
#end
}
public static inline function getKeyIterator<T>( e : Expr, callb : String -> String -> Expr -> T ) {
var key = null, value = null, it = e;
switch( expr(it) ) {
case EBinop("in", ekv, eiter):
switch( expr(ekv) ) {
case EBinop("=>",v1,v2):
switch( [expr(v1),expr(v2)] ) {
case [EIdent(v1), EIdent(v2)]:
key = v1;
value = v2;
it = eiter;
default:
}
default:
}
default:
}
return callb(key,value,it);
}
}