From 841261f1992e5cfac9436a0e7e343bee05bde490 Mon Sep 17 00:00:00 2001 From: YoshiCrafter29 Date: Wed, 8 Jun 2022 20:10:40 +0200 Subject: [PATCH] Added import support --- hscript/Expr.hx | 2 + hscript/Interp.hx | 1380 +++++++++++++++++++++++--------------------- hscript/Parser.hx | 57 +- hscript/Printer.hx | 5 +- hscript/Tools.hx | 3 + 5 files changed, 774 insertions(+), 673 deletions(-) diff --git a/hscript/Expr.hx b/hscript/Expr.hx index bc02042..62862ee 100644 --- a/hscript/Expr.hx +++ b/hscript/Expr.hx @@ -70,6 +70,7 @@ enum Expr { EDoWhile( cond : Expr, e : Expr); EMeta( name : String, args : Array, e : Expr ); ECheckType( e : Expr, t : CType ); + EImport( c : String ); } typedef Argument = { name : String, ?t : CType, ?opt : Bool, ?value : Expr }; @@ -117,6 +118,7 @@ enum Error { EInvalidOp( op : String ); EInvalidAccess( f : String ); ECustom( msg : String ); + EInvalidClass( className : String); } diff --git a/hscript/Interp.hx b/hscript/Interp.hx index dd9fea4..4542ab4 100644 --- a/hscript/Interp.hx +++ b/hscript/Interp.hx @@ -19,682 +19,722 @@ * 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; -} + /* + * YoshiCrafter Engine fixes: + * - Added Error handler + */ + package hscript; + import haxe.PosInfos; + import hscript.Expr; + import haxe.Constraints.IMap; -class Interp { + using StringTools; + + private enum Stop { + SBreak; + SContinue; + SReturn; + } + + class Interp { + + public var errorHandler:Error->Void; + #if haxe3 + public var variables : Map; + var locals : Map; + var binops : Map Expr -> Dynamic >; + #else + public var variables : Hash; + var locals : Hash<{ r : Dynamic }>; + var binops : Hash< Expr -> Expr -> Dynamic >; + #end + + 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() { + #if haxe3 + locals = new Map(); + #else + locals = new Hash(); + #end + declared = new Array(); + resetVariables(); + initOps(); + } + + private function resetVariables(){ + #if haxe3 + variables = new Map(); + #else + variables = new Hash(); + #end + + 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; + #if haxe3 + binops = new Map(); + #else + binops = new Hash(); + #end + 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 #if (haxe_211 || haxe3) IntIterator #else IntIter #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); + } + + 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; + #if haxe3 + locals = new Map(); + #else + locals = new Hash(); + #end + 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( h : #if haxe3 Map < String, T > #else Hash #end ) { + #if haxe3 + var h2 = new Map(); + #else + var h2 = new Hash(); + #end + 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 (errorHandler != null) + errorHandler(e); + else 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 l = locals.get(id); + if( l != null ) + return l.r; + 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 EImport(c): + var splitClassName = [for (e in c.split(".")) e.trim()]; + var realClassName = splitClassName.join("."); + var cl = Type.resolveClass(realClassName); + var en = Type.resolveEnum(realClassName); - #if haxe3 - public var variables : Map; - var locals : Map; - var binops : Map Expr -> Dynamic >; - #else - public var variables : Hash; - var locals : Hash<{ r : Dynamic }>; - var binops : Hash< Expr -> Expr -> Dynamic >; - #end - - 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() { - #if haxe3 - locals = new Map(); - #else - locals = new Hash(); - #end - declared = new Array(); - resetVariables(); - initOps(); - } - - private function resetVariables(){ - #if haxe3 - variables = new Map(); - #else - variables = new Hash(); - #end - - 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; - #if haxe3 - binops = new Map(); - #else - binops = new Hash(); - #end - 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 #if (haxe_211 || haxe3) IntIterator #else IntIter #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); - } - - 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; - #if haxe3 - locals = new Map(); - #else - locals = new Hash(); - #end - 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( h : #if haxe3 Map < String, T > #else Hash #end ) { - #if haxe3 - var h2 = new Map(); - #else - var h2 = new Hash(); - #end - 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 l = locals.get(id); - if( l != null ) - return l.r; - 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; - #if !haxe3 - case CInt32(v): return v; - #end - } - case EIdent(id): - 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 "~": - #if (neko && !haxe3) - return haxe.Int32.complement(expr(e)); - #else - return ~expr(e); - #end - 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 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) { - 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 ) { - 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); + if (cl == null && en == null) { + //LogsOverlay.trace('Class / Enum at $realClassName does not exist.'); + error(EInvalidClass(realClassName)); } 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 isAllString:Bool = true; - var isAllInt:Bool = true; - var isAllObject:Bool = true; - var isAllEnum:Bool = true; - var keys:Array = []; - var values:Array = []; - for (e in arr) { - switch(Tools.expr(e)) { - case EBinop("=>", eKey, eValue): { - var key:Dynamic = expr(eKey); - var value:Dynamic = expr(eValue); - isAllString = isAllString && (key is String); - isAllInt = isAllInt && (key is Int); - isAllObject = isAllObject && Reflect.isObject(key); - isAllEnum = isAllEnum && Reflect.isEnumValue(key); - keys.push(key); - values.push(value); + if (en != null) { + // ENUM!!!! + var enumThingy = {}; + for(c in en.getConstructors()) { + Reflect.setField(enumThingy, c, en.createByName(c)); } - default: throw("=> expected"); + variables.set(splitClassName[splitClassName.length - 1], enumThingy); + } else { + variables.set(splitClassName[splitClassName.length - 1], cl); } } - var map:Dynamic = { - if (isAllInt) new haxe.ds.IntMap(); - else if (isAllString) new haxe.ds.StringMap(); - else if (isAllEnum) new haxe.ds.EnumValueMap(); - else if (isAllObject) new haxe.ds.ObjectMap(); - else throw 'Inconsistent key types'; - } - for (n in 0...keys.length) { - setMapValue(map, keys[n], values[n]); - } - return map; - } - 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); - } - else { - 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(_, _, e): - return expr(e); - case ECheckType(e,_): - return expr(e); - } - return null; - } - function doWhileLoop(econd,e) { - var old = declared.length; - do { - try { - expr(e); - } catch( err : Stop ) { - switch(err) { - case SContinue: - case SBreak: break; - case SReturn: throw err; - } - } - } - while( expr(econd) == true ); - restore(old); - } + return null; - function whileLoop(econd,e) { - var old = declared.length; - while( expr(econd) == true ) { - try { - expr(e); - } catch( err : Stop ) { - switch(err) { - case SContinue: - case SBreak: break; - case SReturn: throw err; - } - } - } - restore(old); - } - - function makeIterator( v : Dynamic ) : Iterator { - #if ((flash && !flash9) || (php && !php7 && haxe_ver < '4.0.0')) - if ( v.iterator != null ) v = v.iterator(); - #else - try v = v.iterator() 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() }); - try { - expr(e); - } catch( err : Stop ) { - switch( err ) { - case SContinue: - case SBreak: break; - case SReturn: throw err; - } - } - } - restore(old); - } - - inline function isMap(o:Dynamic):Bool { - return (o is IMap); - } - - inline function getMapValue(map:Dynamic, key:Dynamic):Dynamic { - return cast(map, haxe.Constraints.IMap).get(key); - } - - inline function setMapValue(map:Dynamic, key:Dynamic, value:Dynamic):Void { - cast(map, haxe.Constraints.IMap).set(key, value); - } - - 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 { - return call(o, get(o, f), args); - } - - function call( o : Dynamic, f : Dynamic, args : Array ) : Dynamic { - return Reflect.callMethod(o,f,args); - } - - function cnew( cl : String, args : Array ) : Dynamic { - var c = Type.resolveClass(cl); - if( c == null ) c = resolve(cl); - return Type.createInstance(c,args); - } - -} + case EConst(c): + switch( c ) { + case CInt(v): return v; + case CFloat(f): return f; + case CString(s): return s; + #if !haxe3 + case CInt32(v): return v; + #end + } + case EIdent(id): + 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 "~": + #if (neko && !haxe3) + return haxe.Int32.complement(expr(e)); + #else + return ~expr(e); + #end + 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 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) { + 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 ) { + 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 isAllString:Bool = true; + var isAllInt:Bool = true; + var isAllObject:Bool = true; + var isAllEnum:Bool = true; + var keys:Array = []; + var values:Array = []; + for (e in arr) { + switch(Tools.expr(e)) { + case EBinop("=>", eKey, eValue): { + var key:Dynamic = expr(eKey); + var value:Dynamic = expr(eValue); + isAllString = isAllString && (key is String); + isAllInt = isAllInt && (key is Int); + isAllObject = isAllObject && Reflect.isObject(key); + isAllEnum = isAllEnum && Reflect.isEnumValue(key); + keys.push(key); + values.push(value); + } + default: throw("=> expected"); + } + } + var map:Dynamic = { + if (isAllInt) new haxe.ds.IntMap(); + else if (isAllString) new haxe.ds.StringMap(); + else if (isAllEnum) new haxe.ds.EnumValueMap(); + else if (isAllObject) new haxe.ds.ObjectMap(); + else throw 'Inconsistent key types'; + } + for (n in 0...keys.length) { + setMapValue(map, keys[n], values[n]); + } + return map; + } + 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); + } + else { + 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(_, _, e): + return expr(e); + case ECheckType(e,_): + return expr(e); + } + return null; + } + + function doWhileLoop(econd,e) { + var old = declared.length; + do { + try { + expr(e); + } catch( err : Stop ) { + switch(err) { + case SContinue: + case SBreak: break; + case SReturn: throw err; + } + } + } + while( expr(econd) == true ); + restore(old); + } + + function whileLoop(econd,e) { + var old = declared.length; + while( expr(econd) == true ) { + try { + expr(e); + } catch( err : Stop ) { + switch(err) { + case SContinue: + case SBreak: break; + case SReturn: throw err; + } + } + } + restore(old); + } + + function makeIterator( v : Dynamic ) : Iterator { + #if ((flash && !flash9) || (php && !php7 && haxe_ver < '4.0.0')) + if ( v.iterator != null ) v = v.iterator(); + #else + try v = v.iterator() 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() }); + try { + expr(e); + } catch( err : Stop ) { + switch( err ) { + case SContinue: + case SBreak: break; + case SReturn: throw err; + } + } + } + restore(old); + } + + inline function isMap(o:Dynamic):Bool { + return (o is IMap); + } + + inline function getMapValue(map:Dynamic, key:Dynamic):Dynamic { + return cast(map, haxe.Constraints.IMap).get(key); + } + + inline function setMapValue(map:Dynamic, key:Dynamic, value:Dynamic):Void { + cast(map, haxe.Constraints.IMap).set(key, value); + } + + 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 { + return call(o, get(o, f), args); + } + + function call( o : Dynamic, f : Dynamic, args : Array ) : Dynamic { + return Reflect.callMethod(o,f,args); + } + + function cnew( cl : String, args : Array ) : Dynamic { + var c = Type.resolveClass(cl); + if( c == null ) c = resolve(cl); + return Type.createInstance(c,args); + } + + } + \ No newline at end of file diff --git a/hscript/Parser.hx b/hscript/Parser.hx index 4d35c1d..d6cd0e7 100644 --- a/hscript/Parser.hx +++ b/hscript/Parser.hx @@ -347,13 +347,15 @@ class Parser { } function parseExpr() { + + var oldPos = readPos; var tk = token(); #if hscriptPos var p1 = tokenMin; #end switch( tk ) { case TId(id): - var e = parseStructure(id); + var e = parseStructure(id, oldPos); if( e == null ) e = mk(EIdent(id)); return parseExprNext(e); @@ -579,7 +581,7 @@ class Parser { } } - function parseStructure(id) { + function parseStructure(id, ?oldPos:Int) { #if hscriptPos var p1 = tokenMin; #end @@ -654,6 +656,57 @@ class Parser { } var inf = parseFunctionDecl(); mk(EFunction(inf.args, inf.body, name, inf.ret),p1,pmax(inf.body)); + case "import": + var oldReadPos = readPos; + var tk = token(); + switch( tk ) { + case TPOpen: + trace("using base thing shit"); + var tok = token(); + switch(tok) { + case TConst(c): + switch(c) { + case CString(s): + token(); + ensure(TSemicolon); + push(TSemicolon); + mk(EImport(s), p1); + default: + unexpected(tok); + null; + } + default: + unexpected(tok); + null; + } + case TId(id): + var path = [id]; + var t = null; + while( true ) { + t = token(); + if( t != TDot ) { + push(t); + break; + } + t = token(); + switch( t ) { + case TId(id): + path.push(id); + // case TOp("*"): + // star = true; + default: + unexpected(t); + } + } + ensure(TSemicolon); + push(TSemicolon); + var p = path.join("."); + mk(EImport(p),p1); + default: + unexpected(tk); + null; + } + case "return": var tk = token(); push(tk); diff --git a/hscript/Printer.hx b/hscript/Printer.hx index 3c5b61c..d9972d1 100644 --- a/hscript/Printer.hx +++ b/hscript/Printer.hx @@ -114,6 +114,8 @@ class Printer { return; } switch( #if hscriptPos e.e #else e #end ) { + case EImport(c): + add("import " + c); case EConst(c): switch( c ) { case CInt(i): add(i); @@ -331,7 +333,7 @@ class Printer { 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 EInvalidChar(c): "Invalid character: '"+(StringTools.isEof(c) ? "EOF (End Of File)" : String.fromCharCode(c))+"' ("+c+")"; case EUnexpected(s): "Unexpected token: \""+s+"\""; case EUnterminatedString: "Unterminated string"; case EUnterminatedComment: "Unterminated comment"; @@ -341,6 +343,7 @@ class Printer { case EInvalidOp(op): "Invalid operator: "+op; case EInvalidAccess(f): "Invalid access to field " + f; case ECustom(msg): msg; + case EInvalidClass(cla): "Invalid class: " + cla + " was not found."; }; #if hscriptPos return e.origin + ":" + e.line + ": " + message; diff --git a/hscript/Tools.hx b/hscript/Tools.hx index e0dc235..64562f2 100644 --- a/hscript/Tools.hx +++ b/hscript/Tools.hx @@ -27,6 +27,7 @@ class Tools { public static function iter( e : Expr, f : Expr -> Void ) { switch( expr(e) ) { case EConst(_), EIdent(_): + case EImport(c): f(e); case EVar(_, _, e): if( e != null ) f(e); case EParent(e): f(e); case EBlock(el): for( e in el ) f(e); @@ -57,6 +58,7 @@ class Tools { 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); + } } @@ -86,6 +88,7 @@ class Tools { 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 EImport(c): EImport(c); } return mk(edef, e); }