diff --git a/.gitignore b/.gitignore index ace37e65..9e5d9983 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,4 @@ /hscript.swf /release.zip + +bk/** \ No newline at end of file diff --git a/hscript/Async.hx b/hscript/Async.hx index 4ea58ab2..4a444f87 100644 --- a/hscript/Async.hx +++ b/hscript/Async.hx @@ -96,15 +96,15 @@ class Async { function lookupFunctions( el : Array ) { 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); + case EFunction(Tools.getFunctionName(_) => name, _) if( name != null ): defineVar(name, Defined); + case EMeta("sync",_,expr(_) => EFunction(Tools.getFunctionName(_) => name, _)) if( name != null ): defineVar(name, ForceSync); default: } } function buildSync( e : Expr, exit : Expr ) : Expr { switch( expr(e) ) { - case EFunction(_,_,name,_): + case EFunction(Tools.getFunctionName(_) => name,_): if( name != null ) return toCps(e, null, null); return e; @@ -116,8 +116,8 @@ class Async { 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 EMeta("sync", args, ef = expr(_) => EFunction(Tools.getFunctionName(_) => name, {args: fargs, expr: body, ret: ret })): + return mk(EMeta("sync",args,mk(EFunction(Tools.getFunctionType(name), {args: fargs, expr: buildSync(body,null), ret: ret}),ef)),e); case EBreak if( currentBreak != null ): return currentBreak(e); case EContinue if( currentLoop != null ): @@ -152,11 +152,11 @@ class Async { } inline function fun(arg:String, e, ?name) { - return mk(EFunction([{ name : arg, t : null }], e, name), e); + return mk(EFunction(FNamed(name, false), {args: [{ name : arg, t : null }], expr: e}), e); } inline function funs(arg:Array, e, ?name) { - return mk(EFunction([for( a in arg ) { name : a, t : null }], e, name), e); + return mk(EFunction(FNamed(name, false), {expr: e, args: [for( a in arg ) { name : a, t : null }]} ), e); } inline function block(arr:Array, e) { @@ -179,7 +179,7 @@ class Async { function retNull(e:Expr,?pos) : Expr { switch( expr(e) ) { - case EFunction([{name:"_"}], e, _, _): return e; + case EFunction(FNamed('_', false), {expr: e}): return e; default: } return call(e, [nullId], pos == null ? e : pos); @@ -213,7 +213,7 @@ class Async { switch( expr(e) ) { case ECall(_): syncFlag = false; - case EFunction(_,_,name,_) if( name != null ): + case EFunction(Tools.getFunctionName(_) => name, _) if( name != null ): syncFlag = false; case EMeta("sync" | "async", _, _): // isolated from the sync part @@ -247,7 +247,7 @@ class Async { } restoreVars(vold); return retNull(rest); - case EFunction(args, body, name, t): + case EFunction(Tools.getFunctionName(_) => name, {args: args, expr: body, ret: t}): var vold = saveVars(); if( name != null ) defineVar(name, Defined); @@ -258,7 +258,7 @@ class Async { var oldFun = currentFun; currentFun = name; var body = toCps(body, frest, frest); - var f = mk(EFunction(args, body, name, t),e); + var f = mk(EFunction(FNamed(name, false), {args: args, expr: body, ret: t}),e); restoreVars(vold); return rest == null ? f : call(rest, [f],e); case EParent(e): @@ -411,9 +411,9 @@ class Async { 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 ); + return toCps(v, mk(EFunction (FAnonymous, {expr: mk(ESwitch(ident("_c",v), cases, def == null ? retNull(rest) : toCps(def, rest, exit)),e), args: [ { name : "_c", t : null } ]} ) ,e), exit ); case EThrow(v): - return toCps(v, mk(EFunction([ { name : "_v", t : null } ], mk(EThrow(v),v)), v), exit); + return toCps(v, mk(EFunction(FAnonymous, {args: [ { name : "_v", t : null } ], expr: 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(_): diff --git a/hscript/Bytes.hx b/hscript/Bytes.hx index 066c12f5..9672a80b 100644 --- a/hscript/Bytes.hx +++ b/hscript/Bytes.hx @@ -190,7 +190,7 @@ class Bytes { doEncode(it); doEncode(e); case EBreak, EContinue: - case EFunction(params,e,name,_): + case EFunction(Tools.getFunctionName(_) => name, {args: params,expr: e}): bout.addByte(params.length); for( p in params ) doEncodeString(p.name); @@ -316,7 +316,7 @@ class Bytes { params.push({ name : doDecodeString() }); var e = doDecode(); var name = doDecodeString(); - EFunction(params,e,(name == "") ? null: name); + EFunction(Tools.getFunctionType(name), {args:params,expr:e}); case 15: EReturn(doDecode()); case 16: diff --git a/hscript/Checker.hx b/hscript/Checker.hx index 800f7555..7d86d86f 100644 --- a/hscript/Checker.hx +++ b/hscript/Checker.hx @@ -1,132 +1,155 @@ package hscript; import hscript.Expr; - +using hscript.Tools; +using hscript.Checker; +using Lambda; /** - This is a special type that can be used in API. - It will be type-checked as `Script` but will compile/execute as `Real` + This is a special type that can be used in API. + It will be type-checked as `Script` but will compile/execute as `Real` **/ typedef TypeCheck = Real; enum TType { - TMono( r : { r : TType } ); - TVoid; - TInt; - TFloat; - TBool; - TDynamic; - TParam( name : String ); - TUnresolved( name : String ); - TNull( t : TType ); - TInst( c : CClass, args : Array ); - TEnum( e : CEnum, args : Array ); - TType( t : CTypedef, args : Array ); - TAbstract( a : CAbstract, args : Array ); - TFun( args : Array<{ name : String, opt : Bool, t : TType }>, ret : TType ); - TAnon( fields : Array<{ name : String, opt : Bool, t : TType }> ); - TLazy( f : Void -> TType ); + TMono( r : { r : TType } ); + TVoid; + TInt; + TFloat; + TBool; + TDynamic; + TParam( name : String, index : Int ); + TUnresolved( name : String ); + TNull( t : TType ); + TInst( c : CClass, args : Array ); + TEnum( e : CEnum, args : Array ); + TType( t : CTypedef, args : Array ); + TAbstract( a : CAbstract, args : Array ); + TFun( args : Array<{ name : String, opt : Bool, t : TType }>, ret : TType); + TAnon( fields : Array<{ name : String, opt : Bool, t : TType }> ); + TLazy( f : Void -> TType ); } -private enum WithType { - NoValue; - Value; - WithType( t : TType ); +enum WithType { + NoValue; + Value; + WithType( t : TType ); } enum CTypedecl { - CTClass( c : CClass ); - CTEnum( e : CEnum ); - CTTypedef( t : CTypedef ); - CTAlias( t : TType ); - CTAbstract( a : CAbstract ); + CTClass( c : CClass ); + CTEnum( e : CEnum ); + CTTypedef( t : CTypedef ); + CTAlias( t : TType ); + CTAbstract( a : CAbstract ); } typedef CNamedType = { - var name : String; - var params : Array; + var name : String; + var params : Array; } typedef CClass = {> CNamedType, - @:optional var superClass : TType; - @:optional var constructor : CField; - @:optional var interfaces : Array; + @:optional var superClass : TType; + @:optional var constructor : CField; + @:optional var interfaces : Array; @:optional var isInterface : Bool; - var fields : Map; - var statics : Map; + var fields : Array; + var statics : Array; } typedef CField = { - var isPublic : Bool; - var canWrite : Bool; - var complete : Bool; - var params : Array; - var name : String; - var t : TType; + var isPublic : Bool; + var canWrite : Bool; + var complete : Bool; + var params : Array; + var name : String; + var t : TType; } typedef CEnum = {> CNamedType, - var constructors : Array<{ name : String, ?args : Array<{ name : String, opt : Bool, t : TType }> }>; + var constructors : Array<{ name : String, ?args : Array<{ name : String, opt : Bool, t : TType }> }>; } typedef CTypedef = {> CNamedType, - var t : TType; + var t : TType; } - +typedef Conversion = {t: TType, field:CField}; +typedef Binop = {op:String, field:CField}; +typedef Unop = {>Binop, postFix:Bool}; typedef CAbstract = {> CNamedType, - var t : TType; + var t : TType; + @:optional var constructor : CField; + var fields : Array; + var statics : Array; + + var to:Array; + var from:Array; + var resolveAssignment:CField; + var resolveAccess:CField; + @:optional var impl:TType; + var unops:Array; + var binops:Array; + var array:Array; } class Completion { - public var expr : Expr; - public var t : TType; - public function new(expr,t) { - this.expr = expr; - this.t = t; - } + public var expr : Expr; + public var t : TType; + public function new(expr,t) { + this.expr = expr; + this.t = t; + } +} + +// @:allow(hscript.Checker) +interface CheckerTypes { + function resolve( name : String, ?args : Array ) : TType; + function getType( name : String, ?args : Array ) : TType; + var t_string : TType; } -@:allow(hscript.Checker) -class CheckerTypes { +class XmlCheckerTypes implements CheckerTypes { - var types : Map = new Map(); - var t_string : TType; - var localParams : Map; + var types : Map = new Map(); + public var t_string : TType; + var localParams : Map; - public function new() { - types = new Map(); - types.set("Void",CTAlias(TVoid)); - types.set("Int",CTAlias(TInt)); - types.set("Float",CTAlias(TFloat)); - types.set("Bool",CTAlias(TBool)); - types.set("Dynamic",CTAlias(TDynamic)); - } + public function new() { + types = new Map(); + types.set("Void",CTAlias(TVoid)); + types.set("Int",CTAlias(TInt)); + types.set("Float",CTAlias(TFloat)); + types.set("Bool",CTAlias(TBool)); + types.set("Dynamic",CTAlias(TDynamic)); + } - public function addXmlApi( api : Xml ) { - var types = new haxe.rtti.XmlParser(); - types.process(api, ""); - var todo = []; - for( v in types.root ) - addXmlType(v,todo); - for( f in todo ) - f(); - t_string = getType("String"); - } + public function addXmlApi( api : Xml ) { + var types = new haxe.rtti.XmlParser(); + types.process(api, ""); + var todo = []; + for( v in types.root ) + addXmlType(v,todo); + for( f in todo ) + f(); + t_string = getType("String"); + } - function addXmlType(x:haxe.rtti.CType.TypeTree,todo:ArrayVoid>) { - switch (x) { - case TPackage(name, full, subs): - for( s in subs ) addXmlType(s,todo); - case TClassdecl(c): - if( types.exists(c.path) ) return; - var cl : CClass = { - name : c.path, - params : [], - fields : new Map(), - statics : new Map(), - }; - if( c.isInterface ) + function addXmlType(x:haxe.rtti.CType.TypeTree,todo:ArrayVoid>) { + switch (x) { + case TPackage(name, full, subs): + for( s in subs ) addXmlType(s,todo); + case TClassdecl(c): + if( types.exists(c.path) ) return; + var cl : CClass = { + name : c.path, + params : [], + fields : [], + statics : [], + }; + if( c.isInterface ) cl.isInterface = true; + var i = 0; for( p in c.params ) - cl.params.push(TParam(p)); + cl.params.push(TParam(p, i++)); todo.push(function() { localParams = [for( t in cl.params ) c.path+"."+Checker.typeStr(t) => t]; if( c.superClass != null ) @@ -137,174 +160,195 @@ class CheckerTypes { cl.interfaces.push(getType(i.path, [for( t in i.params ) makeXmlType(t)])); } var pkeys = []; - for( f in c.fields ) { - if( f.isOverride || f.name.substr(0,4) == "get_" || f.name.substr(0,4) == "set_" ) continue; - var skip = false; - var complete = !StringTools.startsWith(f.name,"__"); // __uid, etc. (no metadata in such fields) - for( m in f.meta ) { - if( m.name == ":noScript" ) { - skip = true; - break; - } - if( m.name == ":noCompletion" ) - complete = false; - } - if( skip ) continue; - var fl : CField = { isPublic : f.isPublic, canWrite : f.set.match(RNormal | RCall(_) | RDynamic), complete : complete, params : [], name : f.name, t : null }; - for( p in f.params ) { - var pt = TParam(p); - var key = f.name+"."+p; - pkeys.push(key); - fl.params.push(pt); - localParams.set(key, pt); - } - fl.t = makeXmlType(f.type); - while( pkeys.length > 0 ) - localParams.remove(pkeys.pop()); - if( fl.name == "new" ) - cl.constructor = fl; - else - cl.fields.set(f.name, fl); - } - localParams = null; - }); - types.set(cl.name, CTClass(cl)); - case TEnumdecl(e): - if( types.exists(e.path) ) return; - var en : CEnum = { - name : e.path, - params : [], - constructors: [], - }; - for( p in e.params ) - en.params.push(TParam(p)); - todo.push(function() { - localParams = [for( t in en.params ) e.path+"."+Checker.typeStr(t) => t]; - for( c in e.constructors ) - en.constructors.push({ name : c.name, args : c.args == null ? null : [for( a in c.args ) { name : a.name, opt : a.opt, t : makeXmlType(a.t) }] }); - localParams = null; - }); - types.set(en.name, CTEnum(en)); - case TTypedecl(t): - if( types.exists(t.path) ) return; - var td : CTypedef = { - name : t.path, - params : [], - t : null, - }; - for( p in t.params ) - td.params.push(TParam(p)); - if( t.path == "hscript.TypeCheck" ) - td.params.reverse(); - todo.push(function() { - localParams = [for( pt in td.params ) t.path+"."+Checker.typeStr(pt) => pt]; - td.t = makeXmlType(t.type); - localParams = null; - }); - types.set(t.path, CTTypedef(td)); - case TAbstractdecl(a): - if( types.exists(a.path) ) return; - var ta : CAbstract = { - name : a.path, - params : [], - t : null, - }; - for( p in a.params ) - ta.params.push(TParam(p)); - todo.push(function() { - localParams = [for( t in ta.params ) a.path+"."+Checker.typeStr(t) => t]; - ta.t = makeXmlType(a.athis); - localParams = null; - }); - types.set(a.path, CTAbstract(ta)); - } - } + for( f in c.fields ) { + if( f.isOverride || f.name.substr(0,4) == "get_" || f.name.substr(0,4) == "set_" ) continue; + var skip = false; + var complete = !StringTools.startsWith(f.name,"__"); // __uid, etc. (no metadata in such fields) + for( m in f.meta ) { + if( m.name == ":noScript" ) { + skip = true; + break; + } + if( m.name == ":noCompletion" ) + complete = false; + } + if( skip ) continue; + var fl : CField = { isPublic : f.isPublic, canWrite : f.set.match(RNormal | RCall(_) | RDynamic), complete : complete, params : [], name : f.name, t : null }; + var i = 0; + for( p in f.params ) { + var pt = TParam(p, i++); + var key = f.name+"."+p; + pkeys.push(key); + fl.params.push(pt); + localParams.set(key, pt); + } + fl.t = makeXmlType(f.type); + while( pkeys.length > 0 ) + localParams.remove(pkeys.pop()); + if( fl.name == "new" ) + cl.constructor = fl; + else + cl.fields.push(fl); + } + localParams = null; + }); + types.set(cl.name, CTClass(cl)); + case TEnumdecl(e): + if( types.exists(e.path) ) return; + var en : CEnum = { + name : e.path, + params : [], + constructors: [], + }; + var i = 0; + for( p in e.params ) + en.params.push(TParam(p, i)); + todo.push(function() { + localParams = [for( t in en.params ) e.path+"."+Checker.typeStr(t) => t]; + for( c in e.constructors ) + en.constructors.push({ name : c.name, args : c.args == null ? null : [for( a in c.args ) { name : a.name, opt : a.opt, t : makeXmlType(a.t) }] }); + localParams = null; + }); + types.set(en.name, CTEnum(en)); + case TTypedecl(t): + if( types.exists(t.path) ) return; + var td : CTypedef = { + name : t.path, + params : [], + t : null, + }; + var i = 0; + for( p in t.params ) + td.params.push(TParam(p, i++)); + if( t.path == "hscript.TypeCheck" ) + td.params.reverse(); + todo.push(function() { + localParams = [for( pt in td.params ) t.path+"."+Checker.typeStr(pt) => pt]; + td.t = makeXmlType(t.type); + localParams = null; + }); + types.set(t.path, CTTypedef(td)); + case TAbstractdecl(a): + if( types.exists(a.path) ) return; + var ta : CAbstract = { + name : a.path, + params : [], + t : null, + fields: [for(f in a.impl.fields) { + var complete = !StringTools.startsWith(f.name,"__"); // __uid, etc. (no metadata in such fields) + var fl : CField = { isPublic : f.isPublic, canWrite : f.set.match(RNormal | RCall(_) | RDynamic), complete : complete, params : [], name : f.name, t : null }; + fl; + }], + statics: [for(f in a.impl.statics) { + var complete = !StringTools.startsWith(f.name,"__"); // __uid, etc. (no metadata in such fields) + var fl : CField = { isPublic : f.isPublic, canWrite : f.set.match(RNormal | RCall(_) | RDynamic), complete : complete, params : [], name : f.name, t : null }; + fl; + }], + unops: [], + binops: [], + to: [], + from: [], + array: [], + resolveAssignment: null, + resolveAccess: null + }; + var i = 0; + for( p in a.params ) + ta.params.push(TParam(p, i++)); + todo.push(function() { + localParams = [for( t in ta.params ) a.path+"."+Checker.typeStr(t) => t]; + ta.t = makeXmlType(a.athis); + localParams = null; + }); + types.set(a.path, CTAbstract(ta)); + } + } - function makeXmlType( t : haxe.rtti.CType.CType ) : TType { - return switch (t) { - case CUnknown: TUnresolved("Unknown"); - case CEnum(name, params): getType(name,[for( t in params ) makeXmlType(t)]); - case CClass(name, params): getType(name,[for( t in params ) makeXmlType(t)]); - case CTypedef(name, params): getType(name,[for( t in params ) makeXmlType(t)]); - case CFunction(args, ret): TFun([for( a in args ) { name : a.name, opt : a.opt, t : makeXmlType(a.t) }], makeXmlType(ret)); - case CAnonymous(fields): - inline function isOpt(m:haxe.rtti.CType.MetaData) { - if( m == null ) return false; - var b = false; - for( m in m ) if( m.name == ":optional" ) { b = true; break; } - return b; - } - TAnon([for( f in fields ) { name : f.name, t : makeXmlType(f.type), opt : isOpt(f.meta) }]); - case CDynamic(t): TDynamic; - case CAbstract(name, params): - switch( name ) { - default: - getType(name,[for( t in params ) makeXmlType(t)]); - } - } - } + function makeXmlType( t : haxe.rtti.CType.CType ) : TType { + return switch (t) { + case CUnknown: TUnresolved("Unknown"); + case CEnum(name, params): getType(name,[for( t in params ) makeXmlType(t)]); + case CClass(name, params): getType(name,[for( t in params ) makeXmlType(t)]); + case CTypedef(name, params): getType(name,[for( t in params ) makeXmlType(t)]); + case CFunction(args, ret): TFun([for( a in args ) { name : a.name, opt : a.opt, t : makeXmlType(a.t) }], makeXmlType(ret)); + case CAnonymous(fields): + inline function isOpt(m:haxe.rtti.CType.MetaData) { + if( m == null ) return false; + var b = false; + for( m in m ) if( m.name == ":optional" ) { b = true; break; } + return b; + } + TAnon([for( f in fields ) { name : f.name, t : makeXmlType(f.type), opt : isOpt(f.meta) }]); + case CDynamic(t): TDynamic; + case CAbstract(name, params): + switch( name ) { + default: + getType(name,[for( t in params ) makeXmlType(t)]); + } + } + } - function getType( name : String, ?args : Array ) : TType { - if( localParams != null ) { - var t = localParams.get(name); - if( t != null ) return t; - } - var t = resolve(name,args); - if( t == null ) { - var pack = name.split("."); - if( pack.length > 1 ) { - // bugfix for some args reported as pack._Name.Name while they are not private - var priv = pack[pack.length-2]; - if( priv.charCodeAt(0) == "_".code ) { - pack.remove(priv); - return getType(pack.join("."), args); - } - } - return TUnresolved(name); // most likely private class - } - return t; - } + public function getType( name : String, ?args : Array ) : TType { + if( localParams != null ) { + var t = localParams.get(name); + if( t != null ) return t; + } + var t = resolve(name,args); + if( t == null ) { + var pack = name.split("."); + if( pack.length > 1 ) { + // bugfix for some args reported as pack._Name.Name while they are not private + var priv = pack[pack.length-2]; + if( priv.charCodeAt(0) == "_".code ) { + pack.remove(priv); + return getType(pack.join("."), args); + } + } + return TUnresolved(name); // most likely private class + } + return t; + } - public function resolve( name : String, ?args : Array ) : TType { - if( name == "Null" ) { - if( args == null || args.length != 1 ) throw "Missing Null parameter"; - return TNull(args[0]); - } - var t = types.get(name); - if( t == null ) return null; - if( args == null ) args = []; - return switch( t ) { - case CTClass(c): TInst(c,args); - case CTEnum(e): TEnum(e,args); - case CTTypedef(t): TType(t,args); - case CTAbstract(a): TAbstract(a, args); - case CTAlias(t): t; - } - } + public function resolve( name : String, ?args : Array ) : TType { + if( name == "Null" ) { + if( args == null || args.length != 1 ) throw "Missing Null parameter"; + return TNull(args[0]); + } + var t = types.get(name); + if( t == null ) return null; + if( args == null ) args = []; + return switch( t ) { + case CTClass(c): TInst(c,args); + case CTEnum(e): TEnum(e,args); + case CTTypedef(t): TType(t,args); + case CTAbstract(a): TAbstract(a, args); + case CTAlias(t): t; + } + } } class Checker { - public var types : CheckerTypes; - var locals : Map; - var globals : Map = new Map(); - var events : Map = new Map(); - var currentFunType : TType; - var isCompletion : Bool; - var allowDefine : Bool; - public var allowAsync : Bool; - public var allowReturn : Null; - public var allowGlobalsDefine : Bool; - public var allowUntypedMeta : Bool; + public var types : CheckerTypes; + var locals : Map; + var globals : Map = new Map(); + var events : Map = new Map(); + var currentFunType : TType; + var isCompletion : Bool; + var allowDefine : Bool; + public var allowAsync : Bool; + public var allowReturn : Null; + public var allowGlobalsDefine : Bool; + public var allowUntypedMeta : Bool; - public function new( ?types ) { - if( types == null ) types = new CheckerTypes(); - this.types = types; - } + public function new( ?types:CheckerTypes ) { + if( types == null ) types = new XmlCheckerTypes(); + this.types = types; + } - public function setGlobals( cl : CClass ) { - while( true ) { + public function setGlobals( cl : CClass ) { + while( true ) { for( f in cl.fields ) if( f.isPublic ) setGlobal(f.name, f.params.length == 0 ? f.t : TLazy(function() return apply(f.t,f.params,[for( i in 0...f.params.length) makeMono()]))); @@ -315,306 +359,331 @@ class Checker { default: throw "assert"; } } - } + } - public function removeGlobal( name : String ) { - globals.remove(name); - } + public function removeGlobal( name : String ) { + globals.remove(name); + } - public function setGlobal( name : String, type : TType ) { - globals.set(name, type); - } + public function setGlobal( name : String, type : TType ) { + globals.set(name, type); + } - public function setEvent( name : String, type : TType ) { - events.set(name, type); - } + public function setEvent( name : String, type : TType ) { + events.set(name, type); + } - public function getGlobals() { - return globals; - } + public function getGlobals() { + return globals; + } - function typeArgs( args : Array, pos : Expr ) { - return [for( i in 0...args.length ) { - var a = args[i]; - var at = a.t == null ? makeMono() : makeType(a.t, pos); - { name : a.name, opt : a.opt, t : at }; - }]; - } + function typeArgs( args : Array, pos : Expr ) { + return [for( i in 0...args.length ) { + var a = args[i]; + var at = a.t == null ? makeMono() : makeType(a.t, pos); + locals.set(a.name, at); + { name : a.name, opt : a.opt, t : at }; + }]; + } + + public function check( expr : Expr, ?withType : WithType, ?isCompletion = false ) { + + if( withType == null ) withType = NoValue; + locals = new Map(); + allowDefine = allowGlobalsDefine; + this.isCompletion = isCompletion; + switch( edef(expr) ) { + + + case EBlock(el): + var delayed = []; + var last = TVoid; + for( e in el ) { + while( true ) { + switch( edef(e) ) { + case EMeta(_,_,e2): e = e2; + default: break; + } + } + inline function declareFunc(name, args, ret) { + var tret = ret == null ? makeMono() : makeType(ret, e); + var ft = TFun(typeArgs(args,e),tret); + locals.set(name, ft); + delayed.push(function() { + currentFunType = ft; + typeExpr(e, NoValue); + return ft; + }); + } + switch( edef(e) ) { + case EFunction(Tools.getFunctionName(_) => name, {args:args,ret:ret}): + declareFunc(name, args, ret); + case v: + switch v { + case EFunction(Tools.getFunctionName(_) => name, {args:args,ret:ret}): + declareFunc(name, args, ret); + default: + } + for( f in delayed ) f(); + delayed = []; + if( el[el.length-1] == e ) + last = typeExpr(e, withType); + else + typeExpr(e, NoValue); + } + } + for( f in delayed ) + last = f(); + return last; + default: + } + return typeExpr(expr,withType); + } - public function check( expr : Expr, ?withType : WithType, ?isCompletion = false ) { - if( withType == null ) withType = NoValue; - locals = new Map(); - allowDefine = allowGlobalsDefine; - this.isCompletion = isCompletion; - switch( edef(expr) ) { - case EBlock(el): - var delayed = []; - var last = TVoid; - for( e in el ) { - while( true ) { - switch( edef(e) ) { - case EMeta(_,_,e2): e = e2; - default: break; - } - } - switch( edef(e) ) { - case EFunction(args,_,name,ret) if( name != null ): - var tret = ret == null ? makeMono() : makeType(ret, e); - var ft = TFun(typeArgs(args,e),tret); - locals.set(name, ft); - delayed.push(function() { - currentFunType = ft; - typeExpr(e, NoValue); - return ft; - }); - default: - for( f in delayed ) f(); - delayed = []; - if( el[el.length-1] == e ) - last = typeExpr(e, withType); - else - typeExpr(e, NoValue); - } - } - for( f in delayed ) - last = f(); - return last; - default: - } - return typeExpr(expr,withType); - } + inline function edef( e : Expr ) { + #if hscriptPos + return e.e; + #else + return e; + #end + } - inline function edef( e : Expr ) { - #if hscriptPos - return e.e; - #else - return e; - #end - } + inline function error( msg : String, curExpr : Expr ) { + var e = ECustom(msg); + #if hscriptPos var e = new Error(e, curExpr.pmin, curExpr.pmax, curExpr.origin, curExpr.line); #end + if( !isCompletion ) throw e; + } - inline function error( msg : String, curExpr : Expr ) { - var e = ECustom(msg); - #if hscriptPos var e = new Error(e, curExpr.pmin, curExpr.pmax, curExpr.origin, curExpr.line); #end - if( !isCompletion ) throw e; - } + function saveLocals() { + return [for( k in locals.keys() ) k => locals.get(k)]; + } - function saveLocals() { - return [for( k in locals.keys() ) k => locals.get(k)]; - } + public function makeType( t : CType, ?e : Expr,?pos:haxe.PosInfos) : TType { + return if(t == null) null else switch (t) { + case CTParam(p, i): TParam(p, i); + case CTPath(path, params): + var ct = types.resolve(path.join("."),params == null ? [] : [for( p in params ) makeType(p,e)]); + if( ct == null ) { + error("Unknown type "+path, if(e != null) e else Tools.mk(EIdent('null'), e)); + ct = TDynamic; + } + return ct; + case CTFun(args, ret): + var i = 0; + return TFun([for( a in args ) { name : "p"+(i++), opt : false, t : makeType(a,e) }], makeType(ret,e)); + case CTAnon(fields): + return TAnon([for( f in fields ) { name : f.name, opt : false, t : makeType(f.t,e) }]); + case CTParent(t): + return makeType(t,e); + case CTNamed(n, t): + return makeType(t,e); + case CTOpt(t): + return makeType(t,e); + } + } - function makeType( t : CType, e : Expr ) : TType { - return switch (t) { - case CTPath(path, params): - var ct = types.resolve(path.join("."),params == null ? [] : [for( p in params ) makeType(p,e)]); - if( ct == null ) { - error("Unknown type "+path, e); - ct = TDynamic; - } - return ct; - case CTFun(args, ret): - var i = 0; - return TFun([for( a in args ) { name : "p"+(i++), opt : false, t : makeType(a,e) }], makeType(ret,e)); - case CTAnon(fields): - return TAnon([for( f in fields ) { name : f.name, opt : false, t : makeType(f.t,e) }]); - case CTParent(t): - return makeType(t,e); - case CTNamed(n, t): - return makeType(t,e); - case CTOpt(t): - return makeType(t,e); - } - } + public static function typeStr( t : TType ) { + if(t == null) return "Unknown"; + inline function makeArgs(args:Array) return args.length==0 ? "" : "<"+[for( t in args ) typeStr(t)].join(",")+">"; + return switch (t) { + case TMono(r): r.r == null ? "Unknown" : typeStr(r.r); + case TInst(c, args): c.name + makeArgs(args); + case TEnum(e, args): e.name + makeArgs(args); + case TType(t, args): + if( t.name == "hscript.TypeCheck" ) + typeStr(args[1]); + else + t.name + makeArgs(args); + case TAbstract(a, args): a.name + makeArgs(args); + case TFun(args, ret): "(" + [for( a in args ) (a.opt?"?":"")+(a.name == "" ? "" : a.name+":")+typeStr(a.t)].join(", ")+") -> "+typeStr(ret); + case TAnon(fields): "{" + [for( f in fields ) (f.opt?"?":"")+f.name+":"+typeStr(f.t)].join(", ")+"}"; + case TParam(name, _): name; + case TNull(t): "Null<"+typeStr(t)+">"; + case TUnresolved(name): "?"+name; + default: t.getName().substr(1); + } + } - public static function typeStr( t : TType ) { - inline function makeArgs(args:Array) return args.length==0 ? "" : "<"+[for( t in args ) typeStr(t)].join(",")+">"; - return switch (t) { - case TMono(r): r.r == null ? "Unknown" : typeStr(r.r); - case TInst(c, args): c.name + makeArgs(args); - case TEnum(e, args): e.name + makeArgs(args); - case TType(t, args): - if( t.name == "hscript.TypeCheck" ) - typeStr(args[1]); - else - t.name + makeArgs(args); - case TAbstract(a, args): a.name + makeArgs(args); - case TFun(args, ret): "(" + [for( a in args ) (a.opt?"?":"")+(a.name == "" ? "" : a.name+":")+typeStr(a.t)].join(", ")+") -> "+typeStr(ret); - case TAnon(fields): "{" + [for( f in fields ) (f.opt?"?":"")+f.name+":"+typeStr(f.t)].join(", ")+"}"; - case TParam(name): name; - case TNull(t): "Null<"+typeStr(t)+">"; - case TUnresolved(name): "?"+name; - default: t.getName().substr(1); - } - } + function linkLoop( a : TType, t : TType ) { + if( t == a ) return true; + switch( t ) { + case TMono(r): + if( r.r == null ) return false; + return linkLoop(a,r.r); + case TEnum(_,tl), TInst(_,tl), TType(_,tl), TAbstract(_,tl): + for( t in tl ) + if( linkLoop(a,t) ) + return true; + return false; + case TFun(args,ret): + for( arg in args ) + if( linkLoop(a,arg.t) ) + return true; + return linkLoop(a,ret); + case TDynamic: + if( t == TDynamic ) + return false; + return linkLoop(a,TDynamic); + case TAnon(fl): + for( f in fl ) + if( linkLoop(a, f.t) ) + return true; + return false; + default: + return false; + } + } - function linkLoop( a : TType, t : TType ) { - if( t == a ) return true; - switch( t ) { - case TMono(r): - if( r.r == null ) return false; - return linkLoop(a,r.r); - case TEnum(_,tl), TInst(_,tl), TType(_,tl), TAbstract(_,tl): - for( t in tl ) - if( linkLoop(a,t) ) - return true; - return false; - case TFun(args,ret): - for( arg in args ) - if( linkLoop(a,arg.t) ) - return true; - return linkLoop(a,ret); - case TDynamic: - if( t == TDynamic ) - return false; - return linkLoop(a,TDynamic); - case TAnon(fl): - for( f in fl ) - if( linkLoop(a, f.t) ) - return true; - return false; - default: - return false; - } - } - - function link( a : TType, b : TType, r : { r : TType } ) { - if( linkLoop(a,b) ) - return follow(b) == a; - if( b == TDynamic ) - return true; - r.r = b; - return true; - } + function link( a : TType, b : TType, r : { r : TType } ) { + if( linkLoop(a,b) ) + return follow(b) == a; + if( b == TDynamic ) + return true; + r.r = b; + return true; + } - function typeEq( t1 : TType, t2 : TType ) { - if( t1 == t2 ) - return true; - switch( [t1,t2] ) { - case [TMono(r), _]: - if( r.r == null ) { - if( !link(t1,t2,r) ) - return false; - r.r = t2; - return true; - } - return typeEq(r.r, t2); - case [_, TMono(r)]: - if( r.r == null ) { - if( !link(t2,t1,r) ) - return false; - r.r = t1; - return true; - } - return typeEq(t1, r.r); - case [TType(t1,pl1),TType(t2,pl2)] if( t1 == t2 ): - for( i in 0...pl1.length ) - if( !typeEq(pl1[i],pl2[i]) ) - return false; - return true; - case [TType(t1,pl1), _]: - return typeEq(apply(t1.t, t1.params, pl1), t2); - case [_,TType(t2,pl2)]: - return typeEq(t1, apply(t2.t, t2.params, pl2)); - case [TInst(cl1,pl1), TInst(cl2,pl2)] if( cl1 == cl2 ): - for( i in 0...pl1.length ) - if( !typeEq(pl1[i],pl2[i]) ) - return false; - return true; - case [TEnum(e1,pl1), TEnum(e2,pl2)] if( e1 == e2 ): - for( i in 0...pl1.length ) - if( !typeEq(pl1[i],pl2[i]) ) - return false; - return true; - case [TAbstract(a1,pl1), TAbstract(a2,pl2)] if( a1 == a2 ): - for( i in 0...pl1.length ) - if( !typeEq(pl1[i],pl2[i]) ) - return false; - return true; - case [TNull(t1), TNull(t2)]: - return typeEq(t1,t2); - case [TNull(t1), _]: - return typeEq(t1,t2); - case [_, TNull(t2)]: - return typeEq(t1,t2); - case [TFun(args1,r1), TFun(args2,r2)] if( args1.length == args2.length ): - for( i in 0...args1.length ) - if( !typeEq(args1[i].t, args2[i].t) ) - return false; - return typeEq(r1, r2); - case [TAnon(a1),TAnon(a2)] if( a1.length == a2.length ): - var m = new Map(); - for( f in a2 ) - m.set(f.name, f); - for( f1 in a1 ) { - var f2 = m.get(f1.name); - if( f2 == null ) return false; - if( !typeEq(f1.t,f2.t) ) - return false; - } - return true; - default: - } - return false; - } + function typeEq( t1 : TType, t2 : TType ) { + if( t1 == t2 ) + return true; + switch( [t1,t2] ) { + case [TMono(r), _]: + if( r.r == null ) { + if( !link(t1,t2,r) ) + return false; + r.r = t2; + return true; + } + return typeEq(r.r, t2); + case [_, TMono(r)]: + if( r.r == null ) { + if( !link(t2,t1,r) ) + return false; + r.r = t1; + return true; + } + return typeEq(t1, r.r); + case [TType(t1,pl1),TType(t2,pl2)] if( t1 == t2 ): + for( i in 0...pl1.length ) + if( !typeEq(pl1[i],pl2[i]) ) + return false; + return true; + case [TType(t1,pl1), _]: + return typeEq(apply(t1.t, t1.params, pl1), t2); + case [_,TType(t2,pl2)]: + return typeEq(t1, apply(t2.t, t2.params, pl2)); + case [TInst(cl1,pl1), TInst(cl2,pl2)] if( cl1 == cl2 ): + for( i in 0...pl1.length ) + if( !typeEq(pl1[i],pl2[i]) ) + return false; + return true; + case [TEnum(e1,pl1), TEnum(e2,pl2)] if( e1 == e2 ): + for( i in 0...pl1.length ) + if( !typeEq(pl1[i],pl2[i]) ) + return false; + return true; + case [TAbstract(a1,pl1), TAbstract(a2,pl2)] if( a1 == a2 ): + for( i in 0...pl1.length ) + if( !typeEq(pl1[i],pl2[i]) ) + return false; + return true; + case [TNull(t1), TNull(t2)]: + return typeEq(t1,t2); + case [TNull(t1), _]: + return typeEq(t1,t2); + case [_, TNull(t2)]: + return typeEq(t1,t2); + case [TFun(args1,r1), TFun(args2,r2)] if( args1.length == args2.length ): + for( i in 0...args1.length ) + if( !typeEq(args1[i].t, args2[i].t) ) + return false; + return typeEq(r1, r2); + case [TAnon(a1),TAnon(a2)] if( a1.length == a2.length ): + var m = new Map(); + for( f in a2 ) + m.set(f.name, f); + for( f1 in a1 ) { + var f2 = m.get(f1.name); + if( f2 == null ) return false; + if( !typeEq(f1.t,f2.t) ) + return false; + } + return true; + default: + } + return false; + } - function tryUnify( t1 : TType, t2 : TType ) { - if( t1 == t2 ) - return true; - switch( [t1,t2] ) { - case [TMono(r), _]: - if( r.r == null ) { - if( !link(t1,t2,r) ) - return false; - r.r = t2; + public function tryUnify( t1 : TType, t2 : TType ) { + if(t1 == null) t1 = TVoid; + if(t2 == null) t2 = TVoid; + if( t1 == t2 ) + return true; + switch( [t1,t2] ) { + // deferred resolution + case [TUnresolved(unresolved), _]: + var resolved = types.resolve(unresolved); + if(resolved == null) return false; + return if(resolved.match(TUnresolved(_))) false else tryUnify(resolved, t2); + case [_, TUnresolved(unresolved)]: + var resolved = types.resolve(unresolved); + if(resolved == null) return false; + return if(resolved.match(TUnresolved(_))) false else tryUnify(t1, resolved); + case [TMono(r), _]: + if( r.r == null ) { + if( !link(t1,t2,r) ) + return false; + r.r = t2; + return true; + } + return tryUnify(r.r, t2); + case [_, TMono(r)]: + if( r.r == null ) { + if( !link(t2,t1,r) ) + return false; + r.r = t1; + return true; + } + return tryUnify(t1, r.r); + case [TType(t1,pl1), _]: + return tryUnify(apply(t1.t, t1.params, pl1), t2); + case [_,TType(t2,pl2)]: + return tryUnify(t1, apply(t2.t, t2.params, pl2)); + case [TNull(t1), _]: + return tryUnify(t1,t2); + case [_, TNull(t2)]: + return tryUnify(t1,t2); + case [TFun(args1,r1),TFun(args2,r2)] if( args1.length == args2.length ): + for( i in 0...args1.length ) { + var a1 = args1[i]; + var a2 = args2[i]; + if( a2.opt && !a1.opt ) return false; + if( !tryUnify(a2.t, a1.t) ) return false; + } + return tryUnify(r1,r2); + case [_, TDynamic]: + return true; + case [TDynamic, _]: + return true; + case [TAnon(a1),TAnon(a2)]: + if( a2.length == 0 ) // always unify with {} return true; - } - return tryUnify(r.r, t2); - case [_, TMono(r)]: - if( r.r == null ) { - if( !link(t2,t1,r) ) - return false; - r.r = t1; - return true; - } - return tryUnify(t1, r.r); - case [TType(t1,pl1), _]: - return tryUnify(apply(t1.t, t1.params, pl1), t2); - case [_,TType(t2,pl2)]: - return tryUnify(t1, apply(t2.t, t2.params, pl2)); - case [TNull(t1), _]: - return tryUnify(t1,t2); - case [_, TNull(t2)]: - return tryUnify(t1,t2); - case [TFun(args1,r1),TFun(args2,r2)] if( args1.length == args2.length ): - for( i in 0...args1.length ) { - var a1 = args1[i]; - var a2 = args2[i]; - if( a2.opt && !a1.opt ) return false; - if( !tryUnify(a2.t, a1.t) ) return false; - } - return tryUnify(r1,r2); - case [_, TDynamic]: - return true; - case [TDynamic, _]: - return true; - case [TAnon(a1),TAnon(a2)]: - if( a2.length == 0 ) // always unify with {} - return true; - var m = new Map(); - for( f in a1 ) - m.set(f.name, f); - for( f2 in a2 ) { - var f1 = m.get(f2.name); - if( f1 == null ) { - if( f2.opt ) continue; - return false; - } - if( !typeEq(f1.t,f2.t) ) - return false; - } - return true; - case [TInst(cl1,pl1), TInst(cl2,pl2)]: - while( cl1 != cl2 ) { - if( cl1.interfaces != null ) { + var m = new Map(); + for( f in a1 ) + m.set(f.name, f); + for( f2 in a2 ) { + var f1 = m.get(f2.name); + if( f1 == null ) { + if( f2.opt ) continue; + return false; + } + if( !typeEq(f1.t,f2.t) ) + return false; + } + return true; + case [TInst(cl1,pl1), TInst(cl2,pl2)]: + while( cl1 != cl2 ) { + if( cl1.interfaces != null ) { for( i in cl1.interfaces ) { switch( i ) { case TInst(cli, args): @@ -626,96 +695,103 @@ class Checker { } } } - switch( cl1.superClass ) { - case null: return false; - case TInst(c, args): - pl1 = [for( a in args ) apply(a,cl1.params,pl1)]; - cl1 = c; - default: throw "assert"; - } - } - for( i in 0...pl1.length ) - if( !typeEq(pl1[i],pl2[i]) ) - return false; - return true; - case [TInst(cl1,pl1),TAnon(fl)]: - for( i in 0...fl.length ) { - var f2 = fl[i]; - var f1 = null; - var cl = cl1; - while( true ) { - f1 = cl.fields.get(f2.name); - if( f1 != null ) break; - if( cl.superClass == null ) - return false; - cl = switch( cl.superClass ) { - case TInst(c,_): c; - default: throw "assert"; - } - } - if( !typeEq(f1.t,f2.t) ) - return false; - } - return true; - case [TInt, TFloat]: - return true; - case [TFun(_), TAbstract({ name : "haxe.Function" },_)]: - return true; - default: - } - return typeEq(t1,t2); - } + + // trace(cl1.superClass); + switch( follow(cl1.superClass, true) ) { + case TVoid|null: return false; + case TInst(c, args): + // trace('args? ${args.length} ${cl1.name} ${cl2.name} ${cl1 == cl2}'); + pl1 = [for( a in args ) apply(a,cl1.params,pl1)]; + cl1 = c; + default: throw "assert"; + } + } + for( i in 0...pl1.length ) + if( !typeEq(pl1[i],pl2[i]) ) + return false; + return true; + case [TInst(cl1,pl1),TAnon(fl)]: + for( i in 0...fl.length ) { + var f2 = fl[i]; + var f1 = null; + var cl = cl1; + while( true ) { + f1 = cl.fields.find(f -> f.name == f2.name && tryUnify(f2.t, f.t)); + if( f1 != null ) break; + if( cl.superClass == null ) + return false; + cl = switch( cl.superClass ) { + case TInst(c,_): c; + default: throw "assert"; + } + } + if( !typeEq(f1.t,f2.t) ) + return false; + } + return true; + case [TInt, TFloat]: + return true; + case [TFun(_), TAbstract({ name : "haxe.Function" },_)]: + return true; + default: + } + return typeEq(t1,t2); + } - public function unify( t1 : TType, t2 : TType, e : Expr ) { - if( !tryUnify(t1,t2) ) - error(typeStr(t1)+" should be "+typeStr(t2),e); - } + public function unify( t1 : TType, t2 : TType, e : Expr ) { + if( !tryUnify(t1,t2) ) + error(typeStr(t1)+" should be "+typeStr(t2),e); + } - public function apply( t : TType, params : Array, args : Array ) { - if( args.length != params.length ) throw "Invalid number of type parameters"; - if( args.length == 0 ) - return t; - var subst = new Map(); - for( i in 0...params.length ) - subst.set(params[i], args[i]); - function map(t:TType) { - var st = subst.get(t); - if( st != null ) return st; - return mapType(t,map); - } - return map(t); - } + public function apply( t : TType, params : Array, args : Array ) { + if( args.length != params.length ) throw "Invalid number of type parameters"; + if( args.length == 0 ) + return t; + var subst = new Map(); + for( i in 0...params.length ) + subst.set(params[i], args[i]); + function map(t:TType) { + var st = subst.get(t); + if( st != null ) return st; + return mapType(t,map); + } + return map(t); + } - public function mapType( t : TType, f : TType -> TType ) { - switch (t) { - case TMono(r): - if( r.r == null ) return t; - return f(t); - case TVoid, TInt, TFloat,TBool,TDynamic,TParam(_), TUnresolved(_): - return t; - case TEnum(_,[]), TInst(_,[]), TAbstract(_,[]), TType(_,[]): - return t; - case TNull(t): - return TNull(f(t)); - case TInst(c, args): - return TInst(c, [for( t in args ) f(t)]); - case TEnum(e, args): - return TEnum(e, [for( t in args ) f(t)]); - case TType(t, args): - return TType(t, [for( t in args ) f(t)]); - case TAbstract(a, args): - return TAbstract(a, [for( t in args ) f(t)]); - case TFun(args, ret): - return TFun([for( a in args ) { name : a.name, opt : a.opt, t : f(a.t) }], f(ret)); - case TAnon(fields): - return TAnon([for( af in fields ) { name : af.name, opt : af.opt, t : f(af.t) }]); - case TLazy(l): - return f(l()); - } - } + public function mapType( t : TType, f : TType -> TType ) { + switch (t) { + case TMono(r): + if( r.r == null ) return t; + return f(t); + case TVoid, TInt, TFloat,TBool,TDynamic,TParam(_), TUnresolved(_): + return t; + case TEnum(_,[]), TInst(_,[]), TAbstract(_,[]), TType(_,[]): + return t; + case TNull(t): + return TNull(f(t)); + case TInst(c, args): + return TInst(c, [for( t in args ) f(t)]); + case TEnum(e, args): + return TEnum(e, [for( t in args ) f(t)]); + case TType(t, args): + return TType(t, [for( t in args ) f(t)]); + case TAbstract(a, args): + return TAbstract(a, [for( t in args ) f(t)]); + case TFun(args, ret): + return TFun([for( a in args ) { name : a.name, opt : a.opt, t : f(a.t) }], f(ret)); + case TAnon(fields): + return TAnon([for( af in fields ) { name : af.name, opt : af.opt, t : f(af.t) }]); + case TLazy(l): + return f(l()); + } + } - public function follow( t : TType ) { - return switch( t ) { + public function follow( t : TType, ?once = false, ?pos:haxe.PosInfos ) { + + return if(t == null) TVoid else switch( t ) { + case TUnresolved(name): + var ret = types.resolve(name); + if(once && ret.match(TUnresolved(_))) null else ret; case TMono(r): if( r.r != null ) follow(r.r) else t; case TType(t,args): follow(apply(t.t, t.params, args)); case TNull(t): follow(t); @@ -726,12 +802,13 @@ class Checker { public function getFields( t : TType ) : Array<{ name : String, t : TType }> { var fields = []; - switch( follow(t) ) { - case TInst(c, args): + + switch( t ) { + case follow(_) => TInst(c, args): var map = (t) -> apply(t,c.params,args); while( c != null ) { - for( fname in c.fields.keys() ) { - var f = c.fields.get(fname); + for( f in c.fields ) { + // var f = c.fields.get(fname); if( !f.isPublic || !f.complete ) continue; var name = f.name, t = map(f.t); if( allowAsync && StringTools.startsWith(name,"a_") ) { @@ -756,20 +833,106 @@ class Checker { break; } } - case TAnon(fl): + case follow(_) =>TAnon(fl): for( f in fl ) fields.push({ name : f.name, t : f.t }); default: } return fields; } + /** + * Test if a given field matches the desired characteristics + * @param f - The name of the field (if this is null, matching is done only on args and ret) + * @param field - The field to test + * @param args - The argument types to match against + * @param ret - The return type to match against + * @param contravariant - Whether to use contravariance (I think, or covariance.. always mix those bad boys up) + */ + // probably will use this for abstract casts + inline public function matchesMethod(?f:String, field:{name:String, t:TType}, args:Array, ?ret:TType, ?contravariant:Bool, ?pos:haxe.PosInfos) { + return (f == null || field.name == f) && ( + args == null || + // if there were arguments (e.g. from an ECall expression), do method overload resolution.. + switch field.t { + case TFun(fArgs, fRet): + var desiredSigType = TFun([for(arg in args) {name: 'arg', opt: false, t: arg}], follow(ret)); + trace('sig of $f has ret ${fRet.typeStr()} and ${fArgs.length} args (vs. ${args.length} args) ${desiredSigType.typeStr()} != ${field.t.typeStr()}\r\n$pos'); + if(fArgs.length != args.length) { + trace('next'); + false; + } + else { - function getField( t : TType, f : String, e : Expr, forWrite = false ) { - switch( follow(t) ) { - case TInst(c, args): - var cf = c.fields.get(f); + trace('checking ${field.t.typeStr()}'); + var match = true; + var reason:String = null; + for(i in 0...args.length) { + var arg = args[i]; + var fArg = fArgs[i]; + if(fArg == null) { + match = false; + reason = 'because there arent enough args'; + break; + } + var t1 = arg; + var t2 = fArg.t; + if(contravariant) { + var t = t1; + t1 = t2; + t2 = t; + } + if(!tryUnify(follow(t1), follow(t2))) { + if(!fArg.opt) { + reason = 'because arg#$i\'s type ${follow(arg).typeStr()} and ${follow(fArg.t).typeStr()} dont unify'; + match = false; + break; + } + } + } + match = match && (ret == null || tryUnify(follow(ret), follow(fRet))); + if(match == false && reason == null) reason = 'because return type ${follow(ret).typeStr()} didnt match ${follow(fRet).typeStr()}'; + if(!match) trace('${typeStr(desiredSigType)} didnt unify with ${typeStr(field.t)} $reason'); + else trace('matched!'); + match; + } + default: false; + }); + } + public function isFieldStatic(t :TType, f:String, ?args:Array, ?ret:TType) { + var found = false; + switch follow(t) { + case TInst(c, args): + found = c.statics.exists(matchesMethod.bind(f, _, args, ret, true)); + case TAbstract(a, args): + found = a.statics.exists(matchesMethod.bind(f, _, args, ret, true)); + case TEnum(e, args): + found = e.constructors.exists(c -> c.name == f); + default: + } + return found; + + } + public function getField( t : TType, f : String, e : Expr, forWrite = false, ?args:Array, ?ret:TType) { + function resolveMember(field:CField) return matchesMethod(f, field, args, ret, true); + switch( t ) { + case TType(_ => {name: typeName, params: params, t: follow(_) => instType},args ) if(instType.match(TInst(_))): + return switch instType { + default: null; + case TInst(c, _): + var resolved = c.statics.find(resolveMember); + if(resolved != null) resolved.t; + else { + var resolved = c.statics.find(field -> field.name == f); + if(resolved == null) null; + else resolved.t; + } + // in case overload resolution fails, fallback to default behavior.. name-based resolution + } + case follow(_) => TInst(c, args): + var cf = c.fields.find(resolveMember); + if(cf == null) cf = c.fields.find(field -> field.name == f); if( cf == null && allowAsync ) { - cf = c.fields.get("a_"+f); + cf = c.fields.find(field -> field.name == "a_"+f); if( cf != null ) { var isPublic = true; // consider a_ prefixed as script specific cf = { isPublic : isPublic, canWrite : false, params : cf.params, name : cf.name, t : unasync(cf.t), complete : cf.complete }; @@ -778,14 +941,14 @@ class Checker { } if( cf == null && c.isInterface && c.interfaces != null ) { for( i in c.interfaces ) { - var ft = getField(i, f, e, forWrite); + var ft = getField(i, f, e, forWrite, args, ret); if( ft != null ) return apply(ft, c.params, args); } } if( cf == null ) { if( c.superClass == null ) return null; - var ft = getField(c.superClass, f, e, forWrite); + var ft = getField(c.superClass, f, e, forWrite, args, ret); if( ft != null ) ft = apply(ft, c.params, args); return ft; } @@ -796,9 +959,9 @@ class Checker { var t = cf.t; if( cf.params != null ) t = apply(t, cf.params, [for( i in 0...cf.params.length ) makeMono()]); return apply(t, c.params, args); - case TDynamic: + case follow(_) => TDynamic: return makeMono(); - case TAnon(fields): + case follow(_) => TAnon(fields): for( af in fields ) if( af.name == f ) return af.t; @@ -808,434 +971,489 @@ class Checker { } } - public function unasync( t : TType ) : TType { - switch( follow(t) ) { - case TFun(args, ret) if( args.length > 0 ): - var rargs = args.copy(); - switch( follow(rargs.shift().t) ) { - case TFun([r],_): return TFun(rargs,r.t); - default: - } - default: - } - return null; - } + public function unasync( t : TType ) : TType { + switch( follow(t) ) { + case TFun(args, ret) if( args.length > 0 ): + var rargs = args.copy(); + switch( follow(rargs.shift().t) ) { + case TFun([r],_): return TFun(rargs,r.t); + default: + } + default: + } + return null; + } - function typeExprWith( expr : Expr, t : TType ) { - var et = typeExpr(expr, WithType(t)); - unify(et, t, expr); - return t; - } + function typeExprWith( expr : Expr, t : TType ) { + var et = typeExpr(expr, WithType(t)); + unify(et, t, expr); + return t; + } - function makeMono() { - return TMono({r:null}); - } + function makeMono() { + return TMono({r:null}); + } - function makeIterator(t) : TType { - return TAnon([{ name : "next", opt : false, t : TFun([],t) }, { name : "hasNext", opt : false, t : TFun([],TBool) }]); - } + function makeIterator(t) : TType { + return TAnon([{ name : "next", opt : false, t : TFun([],t) }, { name : "hasNext", opt : false, t : TFun([],TBool) }]); + } - function mk(e,p) : Expr { - #if hscriptPos - return { e : e, pmin : p.pmin, pmax : p.pmax, origin : p.origin, line : p.line }; - #else - return e; - #end - } + function mk(e,p) : Expr { + #if hscriptPos + return { e : e, pmin : p.pmin, pmax : p.pmax, origin : p.origin, line : p.line }; + #else + return e; + #end + } - function isString( t : TType ) { - t = follow(t); - return t.match(TInst({name:"String"},_)); - } + function isString( t : TType ) { + t = follow(t); + return t.match(TInst({name:"String"},_)) || t.match(TUnresolved("String")); + } - function onCompletion( expr : Expr, t : TType ) { - if( isCompletion ) throw new Completion(expr, t); - } + function onCompletion( expr : Expr, t : TType ) { + if( isCompletion ) throw new Completion(expr, t); + } + function ensure(t:TType) return switch t { + case TUnresolved(name): follow(t); + default: t; + } + function typeField( o : Expr, f : String, expr : Expr, forWrite : Bool, ?args:Array, ?ret:TType = TDynamic) { + var ot = ensure(typeExpr(o, Value)); + trace('type of ${new Printer().exprToString(o)}? ${ot.getName()} of ${ot.typeStr()}'); + trace([for(field in getFields(ot)) {name: field.name, t: field.t.typeStr()}]); + if( f == null ) + onCompletion(expr, ot); + var ft = getField(ot, f, expr, forWrite, args, ret); + if(args == null) args = []; + trace('got ${follow(ft).typeStr()} from looking for $f with ${args.map(arg -> follow(arg).typeStr())}, ret: ${follow(ret).typeStr()}'); + if( ft == null ) { + error(typeStr(ot)+" has no field "+f, expr); + ft = TDynamic; + } + return ft; + } - function typeField( o : Expr, f : String, expr : Expr, forWrite : Bool ) { - var ot = typeExpr(o, Value); - if( f == null ) - onCompletion(expr, ot); - var ft = getField(ot, f, expr, forWrite); - if( ft == null ) { - error(typeStr(ot)+" has no field "+f, expr); - ft = TDynamic; - } - return ft; - } + public function typeExpr( expr : Expr, withType : WithType = NoValue, ?args:Array, ?ret:TType, ?contravariant:Bool, ?pos:haxe.PosInfos) : TType { + trace('from $pos'); + trace('checking ${edef(expr).getName()} ${new Printer().exprToString(expr)} with ${args == null ? null : [for(arg in args) arg.typeStr()]}\r\nret: ${ret == null ? null : ret.typeStr()}'); + + if( expr == null && isCompletion ) + return switch( withType ) { + case WithType(t): t; + default: TDynamic; + } + switch( edef(expr) ) { + + case EConst(c): + return switch (c) { + case CInt(_): TInt; + case CFloat(_): TFloat; + case CString(_): types.t_string; + } + case EIdent(v): + var l = locals.get(v); + if( l != null ) return l; + var g = globals.get(v); + if( g != null ) { + return switch( g ) { + case TLazy(f): f(); + default: g; + } + } + if( allowAsync ) { + g = globals.get("a_"+v); + if( g != null ) g = unasync(g); + if( g != null ) return g; + } + switch( v ) { + case "null": + return makeMono(); + case "true", "false": + return TBool; + case "trace": + return TDynamic; + default: + if( isCompletion) return TDynamic; + trace(({iterator: locals.keys}).array()); + trace(({iterator: globals.keys}).array()); + error("Unknown identifier "+v+' $expr', expr); + } + case EBlock(el): + var t = TVoid; + var locals = saveLocals(); + for( e in el ) + t = typeExpr(e, e == el[el.length-1] ? withType : NoValue); + this.locals = locals; + return t; + case EVar(n, t, init): + var vt = t == null ? makeMono() : makeType(t, expr); + var ret = null; + var args = switch vt { + case TFun(args, r): + ret = r; + [for(arg in args) arg.t]; + default: null; + }; + if( init != null ) { + trace('writing init ${new Printer().exprToString(init)}'); + var et = typeExpr(init, t == null ? Value : WithType(vt), args, ret); + if( t == null ) vt = et else unify(et,vt, init); + } + locals.set(n, vt); + return TVoid; + case EParent(e): + return typeExpr(e,withType); + case ECall(e, params): + var argTypes = [for(param in params) {name: '', opt: false, t: typeExpr(param, withType)}]; + + var ft = typeExpr(e, WithType(TFun(argTypes, TDynamic)), [for(argType in argTypes) argType.t], TDynamic); + trace('checking ${new Printer().exprToString(expr)} with ${ft.typeStr()}'); + switch( follow(ft) ) { + case TFun(args, ret): + for( i in 0...params.length ) { + var a = args[i]; + if( a == null ) { + error("Too many arguments", params[i]); + break; + } + var t = typeExpr(params[i], a == null ? Value : WithType(a.t)); + unify(t, a.t, params[i]); + } + for( i in params.length...args.length ) + if( !args[i].opt ) + error("Missing argument "+args[i].name+":"+typeStr(args[i].t)/* +"\r\n"+typeStr(ft) */, expr); + return ret; + case TDynamic: + for( p in params ) typeExpr(p,Value); + return makeMono(); + default: + error(typeStr(ft)+" cannot be called", e); + return makeMono(); + } + case EField(o, f): + var typeName = new Printer().exprToString(o); + trace('TYPE NAME IS $typeName'); + trace('IS IT GLOBAL? ${globals.exists(typeName)} ${globals.list().length}'); + if(args == null) args = []; + trace('args? ${args.map(arg -> arg.typeStr())}'); + trace('ret? ${follow(ret).typeStr()}'); + var args = args; + var retType = if(ret != null) ret else switch withType { + case NoValue: TVoid; + case Value: TDynamic; + case WithType(t): switch t { + case TFun(a, ret): + args = if(args != null) args else [for(arg in a) arg.t]; + ret; + default: t; + } + }; + if(globals.exists(typeName)) return typeField(EIdent(typeName).mk(expr),f, expr, false, args, retType); + else return typeField(o,f,expr,false, args, retType); + case ECheckType(v, t): + var ct = makeType(t, expr); + var args = null; + var ret = switch ct { + case TFun(a, ret): + args = [for(arg in a) arg.t]; + ret; + default: null; + }; + var vt = typeExpr(v, WithType(ct), args, ret); + unify(vt, ct, v); + return if(contravariant) vt else ct; + case EMeta(m, _, e): + if( m == ":untyped" && allowUntypedMeta ) + return makeMono(); + return typeExpr(e, withType); + case EIf(cond, e1, e2), ETernary(cond, e1, e2): + typeExprWith(cond, TBool); + var t1 = typeExpr(e1, withType); + if( e2 == null ) + return t1; + var t2 = typeExpr(e2, withType); + if( withType == NoValue ) + return TVoid; + if( tryUnify(t2,t1) ) + return t1; + if( tryUnify(t1,t2) ) + return t2; + unify(t2,t1,e2); // error + case EWhile(cond, e), EDoWhile(cond, e): + typeExprWith(cond,TBool); + typeExpr(e, NoValue); + return TVoid; + case EObject(fl): + switch( withType ) { + case WithType(follow(_) => TAnon(tfields)) if( tfields.length > 0 ): + var map = [for( f in tfields ) f.name => f]; + return TAnon([for( f in fl ) { + var ft = map.get(f.name); + var ft = if( ft == null ) { + error("Extra field "+f.name, f.e); + TDynamic; + } else ft.t; + { t : typeExprWith(f.e, ft), opt : false, name : f.name } + }]); + default: + return TAnon([for( f in fl ) { t : typeExpr(f.e, Value), opt : false, name : f.name }]); + } + case EBreak, EContinue: + return TVoid; + case EReturn(v): + var et = v == null ? TVoid : typeExpr(v, allowReturn == null ? Value : WithType(allowReturn)); + if( allowReturn == null ) + error("Return not allowed here", expr); + else + unify(et, allowReturn, v == null ? expr : v); + return makeMono(); + case EArrayDecl(el): + var et = null; + for( v in el ) { + var t = typeExpr(v, et == null ? Value : WithType(et)); + if( et == null ) et = t else if( !tryUnify(t,et) ) { + if( tryUnify(et,t) ) et = t else unify(t,et,v); + } + } + if( et == null ) et = makeMono(); + return types.getType("Array",[et]); + case EArray(a, index): + typeExprWith(index, TInt); + var at = typeExpr(a, Value); + switch( follow(at) ) { + case TInst({ name : "Array"},[et]): return et; + default: error(typeStr(at)+" is not an Array", a); + } + case EThrow(e): + typeExpr(e, Value); + return makeMono(); + case EFunction(Tools.getFunctionName(_) => name, {args:args, expr:body, ret:ret}): + var ft = null, tret = null, targs = null; + if( currentFunType != null ) { + switch( currentFunType ) { + case TFun(args,ret): + ft = currentFunType; + tret = ret; targs = args; + default: + throw "assert"; + } + currentFunType = null; + } else { + tret = ret == null ? makeMono() : makeType(ret, expr); + } + var locals = saveLocals(); + var oldRet = allowReturn; + var oldGDef = allowDefine; + allowReturn = tret; + allowDefine = false; + var withArgs = null; + if( name != null && !withType.match(WithType(follow(_) => TFun(_))) ) { + var ev = events.get(name); + if( ev != null ) withType = WithType(ev); + } + switch( withType ) { + case WithType(follow(_) => TFun(args,ret)): withArgs = args; unify(tret,ret,expr); + default: + } + if( targs == null ) + targs = typeArgs(args,expr); + for( i in 0...targs.length ) { + var a = targs[i]; + if( withArgs != null ) { + if( i < withArgs.length ) + unify(withArgs[i].t, a.t, expr); + else + error("Extra argument "+a.name, expr); + } + this.locals.set(a.name, a.t); + } + if( withArgs != null && targs.length < withArgs.length ) + error("Missing "+(withArgs.length - targs.length)+" arguments ("+[for( i in targs.length...withArgs.length ) typeStr(withArgs[i].t)].join(",")+")", expr); + typeExpr(body,NoValue); + allowDefine = oldGDef; + allowReturn = oldRet; + this.locals = locals; + if( ft == null ) { + ft = TFun(targs, tret); + locals.set(name, ft); + } + return ft; + case EUnop(op, _, e): + var et = typeExpr(e, Value); + switch( op ) { + case "++", "--", "-", "~": + unify(et,TInt,e); + return et; + case "!": + unify(et,TBool,e); + return et; + default: + } + case EFor(v, it, e): + var locals = saveLocals(); + var itt = typeExpr(it, Value); + var vt = switch( follow(itt) ) { + case TInst({name:"Array"},[t]): + t; + default: + var ft = getField(itt,"iterator", it); + if( ft == null ) + switch( itt ) { + case TAbstract(a, args): + // special case : we allow unconditional access + // to an abstract iterator() underlying value (eg: ArrayProxy) + ft = getField(apply(a.t,a.params,args),"iterator",it); + default: + } + if( ft != null ) + switch( ft ) { + case TFun([],ret): ft = ret; + default: ft = null; + } + var t = makeMono(); + var iter = makeIterator(t); + unify(ft != null ? ft : itt,iter,it); + t; + } + this.locals.set(v, vt); + typeExpr(e, NoValue); + this.locals = locals; + return TVoid; + case EBinop(op, e1, e2): + switch( op ) { + case "&", "|", "^", ">>", ">>>", "<<": + // typeExprWith(e1,TInt); + typeExprWith(e2,getVarType(e1)); + return TInt; + case "=": + if( allowDefine ) { + switch( edef(e1) ) { + case EIdent(i) if( !locals.exists(i) && !globals.exists(i) ): + var vt = typeExpr(e2,Value); + locals.set(i, vt); + return vt; + default: + } + } + var vt = getVarType(e1); + typeExprWith(e2,vt); + return vt; + case "+": + var t1 = getVarType(e1); + var t2 = typeExpr(e2,WithType(t1)); + tryUnify(t1,t2); + switch( [follow(t1), follow(t2)]) { + case [TInt, TInt]: + return TInt; + case [TFloat, TInt], [TInt, TFloat], [TFloat, TFloat]: + return TFloat; + case [TDynamic, _], [_, TDynamic]: + return TDynamic; + case [t1,t2]: + if( isString(t1) || isString(t2) ) + return types.t_string; + unify(t1, TFloat, e1); + unify(t2, TFloat, e2); + } + case "-", "*", "/", "%": + var t1 = getVarType(e1); + var t2 = typeExpr(e2,WithType(t1)); + if( !tryUnify(t1,t2) ) + unify(t2,t1,e2); + switch( [follow(t1), follow(t2)]) { + case [TInt, TInt]: + if( op == "/" ) return TFloat; + return TInt; + case [TFloat|TDynamic, TInt|TDynamic], [TInt|TDynamic, TFloat|TDynamic], [TFloat, TFloat]: + return TFloat; + default: + unify(t1, TFloat, e1); + unify(t2, TFloat, e2); + } + case "&&", "||": + typeExprWith(e1,TBool); + typeExprWith(e2,TBool); + return TBool; + case "...": + typeExprWith(e1,TInt); + typeExprWith(e2,TInt); + return makeIterator(TInt); + case "==", "!=": + var t1 = typeExpr(e1,Value); + var t2 = typeExpr(e2,WithType(t1)); + if( !tryUnify(t1,t2) ) + unify(t2,t1,e2); + return TBool; + case ">", "<", ">=", "<=": + var t1 = typeExpr(e1,Value); + var t2 = typeExpr(e2,WithType(t1)); + if( !tryUnify(t1,t2) ) + unify(t2,t1,e2); + switch( follow(t1) ) { + case TInt, TFloat, TBool, TInst({name:"String"},_): + default: + error("Cannot compare "+typeStr(t1), expr); + } + return TBool; + default: + if( op.charCodeAt(op.length-1) == "=".code ) { + var t = typeExpr(mk(EBinop(op.substr(0,op.length-1),e1,e2),expr),withType); + return typeExpr(mk(EBinop("=",e1,e2),expr), withType); + } + error("Unsupported operation "+op, expr); + } + case ETry(etry, v, et, ecatch): + var vt = typeExpr(etry, withType); - function typeExpr( expr : Expr, withType : WithType ) : TType { - if( expr == null && isCompletion ) - return switch( withType ) { - case WithType(t): t; - default: TDynamic; - } - switch( edef(expr) ) { - case EConst(c): - return switch (c) { - case CInt(_): TInt; - case CFloat(_): TFloat; - case CString(_): types.t_string; - } - case EIdent(v): - var l = locals.get(v); - if( l != null ) return l; - var g = globals.get(v); - if( g != null ) { - return switch( g ) { - case TLazy(f): f(); - default: g; - } - } - if( allowAsync ) { - g = globals.get("a_"+v); - if( g != null ) g = unasync(g); - if( g != null ) return g; - } - switch( v ) { - case "null": - return makeMono(); - case "true", "false": - return TBool; - case "trace": - return TDynamic; - default: - if( isCompletion) return TDynamic; - error("Unknown identifier "+v, expr); - } - case EBlock(el): - var t = TVoid; - var locals = saveLocals(); - for( e in el ) - t = typeExpr(e, e == el[el.length-1] ? withType : NoValue); - this.locals = locals; - return t; - case EVar(n, t, init): - var vt = t == null ? makeMono() : makeType(t, expr); - if( init != null ) { - var et = typeExpr(init, t == null ? Value : WithType(vt)); - if( t == null ) vt = et else unify(et,vt, init); - } - locals.set(n, vt); - return TVoid; - case EParent(e): - return typeExpr(e,withType); - case ECall(e, params): - var ft = typeExpr(e, Value); - switch( follow(ft) ) { - case TFun(args, ret): - for( i in 0...params.length ) { - var a = args[i]; - if( a == null ) { - error("Too many arguments", params[i]); - break; - } - var t = typeExpr(params[i], a == null ? Value : WithType(a.t)); - unify(t, a.t, params[i]); - } - for( i in params.length...args.length ) - if( !args[i].opt ) - error("Missing argument "+args[i].name+":"+typeStr(args[i].t), expr); - return ret; - case TDynamic: - for( p in params ) typeExpr(p,Value); - return makeMono(); - default: - error(typeStr(ft)+" cannot be called", e); - return makeMono(); - } - case EField(o, f): - return typeField(o,f,expr,false); - case ECheckType(v, t): - var ct = makeType(t, expr); - var vt = typeExpr(v, WithType(ct)); - unify(vt, ct, v); - return ct; - case EMeta(m, _, e): - if( m == ":untyped" && allowUntypedMeta ) - return makeMono(); - return typeExpr(e, withType); - case EIf(cond, e1, e2), ETernary(cond, e1, e2): - typeExprWith(cond, TBool); - var t1 = typeExpr(e1, withType); - if( e2 == null ) - return t1; - var t2 = typeExpr(e2, withType); - if( withType == NoValue ) - return TVoid; - if( tryUnify(t2,t1) ) - return t1; - if( tryUnify(t1,t2) ) - return t2; - unify(t2,t1,e2); // error - case EWhile(cond, e), EDoWhile(cond, e): - typeExprWith(cond,TBool); - typeExpr(e, NoValue); - return TVoid; - case EObject(fl): - switch( withType ) { - case WithType(follow(_) => TAnon(tfields)) if( tfields.length > 0 ): - var map = [for( f in tfields ) f.name => f]; - return TAnon([for( f in fl ) { - var ft = map.get(f.name); - var ft = if( ft == null ) { - error("Extra field "+f.name, f.e); - TDynamic; - } else ft.t; - { t : typeExprWith(f.e, ft), opt : false, name : f.name } - }]); - default: - return TAnon([for( f in fl ) { t : typeExpr(f.e, Value), opt : false, name : f.name }]); - } - case EBreak, EContinue: - return TVoid; - case EReturn(v): - var et = v == null ? TVoid : typeExpr(v, allowReturn == null ? Value : WithType(allowReturn)); - if( allowReturn == null ) - error("Return not allowed here", expr); - else - unify(et, allowReturn, v == null ? expr : v); - return makeMono(); - case EArrayDecl(el): - var et = null; - for( v in el ) { - var t = typeExpr(v, et == null ? Value : WithType(et)); - if( et == null ) et = t else if( !tryUnify(t,et) ) { - if( tryUnify(et,t) ) et = t else unify(t,et,v); - } - } - if( et == null ) et = makeMono(); - return types.getType("Array",[et]); - case EArray(a, index): - typeExprWith(index, TInt); - var at = typeExpr(a, Value); - switch( follow(at) ) { - case TInst({ name : "Array"},[et]): return et; - default: error(typeStr(at)+" is not an Array", a); - } - case EThrow(e): - typeExpr(e, Value); - return makeMono(); - case EFunction(args, body, name, ret): - var ft = null, tret = null, targs = null; - if( currentFunType != null ) { - switch( currentFunType ) { - case TFun(args,ret): - ft = currentFunType; - tret = ret; targs = args; - default: - throw "assert"; - } - currentFunType = null; - } else { - tret = ret == null ? makeMono() : makeType(ret, expr); - } - var locals = saveLocals(); - var oldRet = allowReturn; - var oldGDef = allowDefine; - allowReturn = tret; - allowDefine = false; - var withArgs = null; - if( name != null && !withType.match(WithType(follow(_) => TFun(_))) ) { - var ev = events.get(name); - if( ev != null ) withType = WithType(ev); - } - switch( withType ) { - case WithType(follow(_) => TFun(args,ret)): withArgs = args; unify(tret,ret,expr); - default: - } - if( targs == null ) - targs = typeArgs(args,expr); - for( i in 0...targs.length ) { - var a = targs[i]; - if( withArgs != null ) { - if( i < withArgs.length ) - unify(withArgs[i].t, a.t, expr); - else - error("Extra argument "+a.name, expr); - } - this.locals.set(a.name, a.t); - } - if( withArgs != null && targs.length < withArgs.length ) - error("Missing "+(withArgs.length - targs.length)+" arguments ("+[for( i in targs.length...withArgs.length ) typeStr(withArgs[i].t)].join(",")+")", expr); - typeExpr(body,NoValue); - allowDefine = oldGDef; - allowReturn = oldRet; - this.locals = locals; - if( ft == null ) { - ft = TFun(targs, tret); - locals.set(name, ft); - } - return ft; - case EUnop(op, _, e): - var et = typeExpr(e, Value); - switch( op ) { - case "++", "--", "-": - unify(et,TInt,e); - return et; - case "!": - unify(et,TBool,e); - return et; - default: - } - case EFor(v, it, e): - var locals = saveLocals(); - var itt = typeExpr(it, Value); - var vt = switch( follow(itt) ) { - case TInst({name:"Array"},[t]): - t; - default: - var ft = getField(itt,"iterator", it); - if( ft == null ) - switch( itt ) { - case TAbstract(a, args): - // special case : we allow unconditional access - // to an abstract iterator() underlying value (eg: ArrayProxy) - ft = getField(apply(a.t,a.params,args),"iterator",it); - default: - } - if( ft != null ) - switch( ft ) { - case TFun([],ret): ft = ret; - default: ft = null; - } - var t = makeMono(); - var iter = makeIterator(t); - unify(ft != null ? ft : itt,iter,it); - t; - } - this.locals.set(v, vt); - typeExpr(e, NoValue); - this.locals = locals; - return TVoid; - case EBinop(op, e1, e2): - switch( op ) { - case "&", "|", "^", ">>", ">>>", "<<": - typeExprWith(e1,TInt); - typeExprWith(e2,TInt); - return TInt; - case "=": - if( allowDefine ) { - switch( edef(e1) ) { - case EIdent(i) if( !locals.exists(i) && !globals.exists(i) ): - var vt = typeExpr(e2,Value); - locals.set(i, vt); - return vt; - default: - } - } - var vt = switch( edef(e1) ) { - case EField(o,f): typeField(o, f, e1, true); - default: typeExpr(e1,Value); - } - typeExprWith(e2,vt); - return vt; - case "+": - var t1 = typeExpr(e1,WithType(TInt)); - var t2 = typeExpr(e2,WithType(t1)); - tryUnify(t1,t2); - switch( [follow(t1), follow(t2)]) { - case [TInt, TInt]: - return TInt; - case [TFloat, TInt], [TInt, TFloat], [TFloat, TFloat]: - return TFloat; - case [TDynamic, _], [_, TDynamic]: - return TDynamic; - case [t1,t2]: - if( isString(t1) || isString(t2) ) - return types.t_string; - unify(t1, TFloat, e1); - unify(t2, TFloat, e2); - } - case "-", "*", "/", "%": - var t1 = typeExpr(e1,WithType(TInt)); - var t2 = typeExpr(e2,WithType(t1)); - if( !tryUnify(t1,t2) ) - unify(t2,t1,e2); - switch( [follow(t1), follow(t2)]) { - case [TInt, TInt]: - if( op == "/" ) return TFloat; - return TInt; - case [TFloat|TDynamic, TInt|TDynamic], [TInt|TDynamic, TFloat|TDynamic], [TFloat, TFloat]: - return TFloat; - default: - unify(t1, TFloat, e1); - unify(t2, TFloat, e2); - } - case "&&", "||": - typeExprWith(e1,TBool); - typeExprWith(e2,TBool); - return TBool; - case "...": - typeExprWith(e1,TInt); - typeExprWith(e2,TInt); - return makeIterator(TInt); - case "==", "!=": - var t1 = typeExpr(e1,Value); - var t2 = typeExpr(e2,WithType(t1)); - if( !tryUnify(t1,t2) ) - unify(t2,t1,e2); - return TBool; - case ">", "<", ">=", "<=": - var t1 = typeExpr(e1,Value); - var t2 = typeExpr(e2,WithType(t1)); - if( !tryUnify(t1,t2) ) - unify(t2,t1,e2); - switch( follow(t1) ) { - case TInt, TFloat, TBool, TInst({name:"String"},_): - default: - error("Cannot compare "+typeStr(t1), expr); - } - return TBool; - default: - if( op.charCodeAt(op.length-1) == "=".code ) { - var t = typeExpr(mk(EBinop(op.substr(0,op.length-1),e1,e2),expr),withType); - return typeExpr(mk(EBinop("=",e1,e2),expr), withType); - } - error("Unsupported operation "+op, expr); - } - case ETry(etry, v, et, ecatch): - var vt = typeExpr(etry, withType); + var old = locals.get(v); + locals.set(v, makeType(et, ecatch)); + var ct = typeExpr(ecatch, withType); + if( old != null ) locals.set(v,old) else locals.remove(v); - var old = locals.get(v); - locals.set(v, makeType(et, ecatch)); - var ct = typeExpr(ecatch, withType); - if( old != null ) locals.set(v,old) else locals.remove(v); + if( withType == NoValue ) + return TVoid; + if( tryUnify(vt,ct) ) + return ct; + unify(ct,vt,ecatch); + return vt; + case ESwitch(value, cases, defaultExpr): + var tmin = null; + var vt = typeExpr(value, Value); + inline function mergeType(t,p) { + if( withType != NoValue ) { + if( tmin == null ) + tmin = t; + else if( !tryUnify(t,tmin) ) { + unify(tmin,t, p); + tmin = t; + } + } + } + for( c in cases ) { + for( v in c.values ) { + var ct = typeExpr(v, WithType(vt)); + unify(ct, vt, v); + } + var et = typeExpr(c.expr, withType); + mergeType(et, c.expr); + } + if( defaultExpr != null ) + mergeType( typeExpr(defaultExpr, withType), defaultExpr); + return withType == NoValue ? TVoid : tmin == null ? makeMono() : tmin; + case ENew(cl, params): return types.resolve(cl, [for(param in params) typeExpr(param, withType) ] ) ; + } + error("Don't know how to type "+edef(expr).getName(), expr); + return TDynamic; + } - if( withType == NoValue ) - return TVoid; - if( tryUnify(vt,ct) ) - return ct; - unify(ct,vt,ecatch); - return vt; - case ESwitch(value, cases, defaultExpr): - var tmin = null; - var vt = typeExpr(value, Value); - inline function mergeType(t,p) { - if( withType != NoValue ) { - if( tmin == null ) - tmin = t; - else if( !tryUnify(t,tmin) ) { - unify(tmin,t, p); - tmin = t; - } - } - } - for( c in cases ) { - for( v in c.values ) { - var ct = typeExpr(v, WithType(vt)); - unify(ct, vt, v); - } - var et = typeExpr(c.expr, withType); - mergeType(et, c.expr); - } - if( defaultExpr != null ) - mergeType( typeExpr(defaultExpr, withType), defaultExpr); - return withType == NoValue ? TVoid : tmin == null ? makeMono() : tmin; - case ENew(cl, params): - } - error("Don't know how to type "+edef(expr).getName(), expr); - return TDynamic; - } + + + function getVarType(e1:Expr) + return switch( edef(e1) ) { + case EField(o,f): typeField(o, f, e1, true); + default: typeExpr(e1,Value); + } + } \ No newline at end of file diff --git a/hscript/Expr.hx b/hscript/Expr.hx index bc020421..8877b453 100644 --- a/hscript/Expr.hx +++ b/hscript/Expr.hx @@ -57,7 +57,7 @@ enum Expr { EFor( v : String, it : Expr, e : Expr ); EBreak; EContinue; - EFunction( args : Array, e : Expr, ?name : String, ?ret : CType ); + EFunction( k:FunctionKind, f:FunctionDecl ); EReturn( ?e : Expr ); EArray( e : Expr, index : Expr ); EArrayDecl( e : Array ); @@ -72,7 +72,7 @@ enum Expr { ECheckType( e : Expr, t : CType ); } -typedef Argument = { name : String, ?t : CType, ?opt : Bool, ?value : Expr }; +typedef Argument = { name : String, ?t : CType, ?opt : Bool, ?value : Expr, ?meta:Metadata }; typedef Metadata = Array<{ name : String, params : Array }>; @@ -83,6 +83,8 @@ enum CType { CTParent( t : CType ); CTOpt( t : CType ); CTNamed( n : String, t : CType ); + + CTParam(p:String, index:Int); } #if hscriptPos @@ -125,25 +127,42 @@ enum ModuleDecl { DImport( path : Array, ?everything : Bool ); DClass( c : ClassDecl ); DTypedef( c : TypeDecl ); -} + DAbstract(a:AbstractDecl); + DEnum(e:EnumDecl); + DInterface(i:InterfaceDecl); +} +typedef Params = Array; typedef ModuleType = { var name : String; - var params : {}; // TODO : not yet parsed + var params : Params; // TODO : not yet parsed var meta : Metadata; var isPrivate : Bool; + @:optional var doc : String; +} +typedef InstancedType = {>ModuleType, + var fields : Array; + var isExtern : Bool; } -typedef ClassDecl = {> ModuleType, +typedef ClassDecl = {> InstancedType, var extend : Null; var implement : Array; - var fields : Array; - var isExtern : Bool; +} +typedef InterfaceDecl = {>InstancedType, + var extend : Array; } typedef TypeDecl = {> ModuleType, var t : CType; } +typedef AbstractDecl = {>InstancedType, + var t:CType; + var to:Array; + var from:Array; +} +typedef EnumDecl = InstancedType; + typedef FieldDecl = { var name : String; @@ -155,8 +174,8 @@ typedef FieldDecl = { enum FieldAccess { APublic; APrivate; - AInline; AOverride; + AInline; AStatic; AMacro; } @@ -166,12 +185,24 @@ enum FieldKind { KVar( v : VarDecl ); } +typedef TypeParamDecl = { + @:optional var params : Params; // what even.. + var name:String; + @:optional var meta:Metadata; + @:optional var constraints:Array; // probably not right +} +enum FunctionKind { + FAnonymous; + FNamed(name:String, inlined:Bool); + FArrow; +} + typedef FunctionDecl = { var args : Array; + @:optional var params : Params; var expr : Expr; - var ret : Null; + @:optional var ret : Null; } - typedef VarDecl = { var get : Null; var set : Null; diff --git a/hscript/Interp.hx b/hscript/Interp.hx index 27c0e512..39adcff9 100644 --- a/hscript/Interp.hx +++ b/hscript/Interp.hx @@ -403,7 +403,7 @@ class Interp { case EReturn(e): returnValue = e == null ? null : expr(e); throw SReturn; - case EFunction(params,fexpr,name,_): + case EFunction(Tools.getFunctionName(_) => name,{args: params,expr: fexpr}): var capturedLocals = duplicate(locals); var me = this; var hasOpt = false, minParams = 0; diff --git a/hscript/Macro.hx b/hscript/Macro.hx index 17cb6b76..1e5d240e 100644 --- a/hscript/Macro.hx +++ b/hscript/Macro.hx @@ -116,6 +116,7 @@ class Macro { function convertType( t : Expr.CType ) : ComplexType { return switch( t ) { + case CTParam(p, _): TPath({name: p, pack: []}); case CTOpt(t): TOptional(convertType(t)); case CTPath(pack, args): var params = []; @@ -208,7 +209,7 @@ class Macro { EBreak; case EContinue: EContinue; - case EFunction(args, e, name, ret): + case EFunction(Tools.getFunctionName(_) => name, {args: args, expr: e, ret: ret}): var targs = []; for( a in args ) targs.push( { diff --git a/hscript/Parser.hx b/hscript/Parser.hx index 4d35c1d2..0e624ac3 100644 --- a/hscript/Parser.hx +++ b/hscript/Parser.hx @@ -276,7 +276,7 @@ class Parser { if( e == null ) return false; return switch( expr(e) ) { case EBlock(_), EObject(_), ESwitch(_): true; - case EFunction(_,e,_,_): isBlock(e); + case EFunction(_, {expr: e}): isBlock(e); case EVar(_, t, e): e != null ? isBlock(e) : t != null ? t.match(CTAnon(_)) : false; case EIf(_,e1,e2): if( e2 != null ) isBlock(e2) else isBlock(e1); case EBinop(_,_,e): isBlock(e); @@ -364,7 +364,7 @@ class Parser { if( tk == TPClose ) { ensureToken(TOp("->")); var eret = parseExpr(); - return mk(EFunction([], mk(EReturn(eret),p1)), p1); + return mk(EFunction(FArrow, {args: [], expr: mk(EReturn(eret),p1) }), p1); } push(tk); var e = parseExpr(); @@ -502,7 +502,7 @@ class Parser { } ensureToken(TOp("->")); var eret = parseExpr(); - return mk(EFunction(args, mk(EReturn(eret),pmin)), pmin); + return mk(EFunction(FArrow, {args: args, expr: mk(EReturn(eret),pmin)}), pmin); } function parseMetaArgs() { @@ -653,7 +653,7 @@ class Parser { default: push(tk); } var inf = parseFunctionDecl(); - mk(EFunction(inf.args, inf.body, name, inf.ret),p1,pmax(inf.body)); + mk(EFunction(Tools.getFunctionType(name),inf),p1,pmax(inf.expr)); case "return": var tk = token(); push(tk); @@ -781,10 +781,10 @@ class Parser { switch( expr(e1) ) { case EIdent(i), EParent(expr(_) => EIdent(i)): var eret = parseExpr(); - return mk(EFunction([{ name : i }], mk(EReturn(eret),pmin(eret))), pmin(e1)); + return mk(EFunction(FArrow, {args: [{ name : i }], expr: mk(EReturn(eret),pmin(eret))}), pmin(e1)); case ECheckType(expr(_) => EIdent(i), t): var eret = parseExpr(); - return mk(EFunction([{ name : i, t : t }], mk(EReturn(eret),pmin(eret))), pmin(e1)); + return mk(EFunction(FArrow, {args: [{ name : i, t : t }], expr: mk(EReturn(eret),pmin(eret))} ), pmin(e1) ); default: } unexpected(tk); @@ -860,7 +860,8 @@ class Parser { return args; } - function parseFunctionDecl() { + function parseFunctionDecl():FunctionDecl { + var params = parseParams(); ensure(TPOpen); var args = parseFunctionArgs(); var ret = null; @@ -871,7 +872,8 @@ class Parser { else ret = parseType(); } - return { args : args, ret : ret, body : parseExpr() }; + var noBody = maybe(TSemicolon); + return { args : args, ret : ret, params: params, expr : if(noBody) null else parseExpr() }; } function parsePath() { @@ -1084,16 +1086,75 @@ class Parser { return meta; } - function parseParams() { - if( maybe(TOp("<")) ) - error(EInvalidOp("Unsupported class type parameters"), readPos, readPos); - return {}; + function parseParams():Params { + var ret:Params = []; + if( maybe(TOp("<")) ) { + ret.push(parseParam()); + while(!maybe(TOp(">"))) { + ensure(TComma); + ret.push(parseParam()); + } + } + return ret; } - + inline function parseParam():TypeParamDecl { + var params = []; + var meta = parseMetadata(); + var name = getIdent(); + var constraints = []; + if(maybe(TDoubleDot)) { + do { + constraints.push(parseType()); + }while(maybe(TOp('&'))); + } + return { + params: params, + meta: meta, + name: name, + constraints: constraints + }; + } + function parseModuleDecl() : ModuleDecl { var meta = parseMetadata(); var ident = getIdent(); var isPrivate = false, isExtern = false; + function parseAbstract() { + var name = getIdent(); + var params = parseParams(); + var underlying = null; + if(maybe(TPOpen)) { + underlying = parseType(); + ensure(TPClose); + } + + var to = []; + var from = []; + while ( true ) { + var ident = getIdent(); + switch ident { + case "from": + from.push(parseType()); + case "to": + to.push(parseType()); + } + } + var fields = []; + ensure(TBrOpen); + while( !maybe(TBrClose) ) + fields.push(parseField()); + return DAbstract({ + name: name, + t: null, + params: params, + meta: meta, + isPrivate: isPrivate, + isExtern: isExtern, + from: from, + to: to, + fields: fields + }); + } while( true ) { switch( ident ) { case "private": @@ -1131,6 +1192,84 @@ class Parser { } ensure(TSemicolon); return DImport(path, star); + case "abstract": + return parseAbstract(); + case "interface": + var name = getIdent(); + var params = parseParams(); + var extend = []; + while( true ) { + var t = token(); + switch( t ) { + case TId("extends"): + extend.push(parseType()); + default: + push(t); + break; + } + } + + var fields = []; + ensure(TBrOpen); + while( !maybe(TBrClose) ) + fields.push(parseField(true)); + + return DInterface({ + name : name, + meta : meta, + params : params, + extend : extend, + fields : fields, + isPrivate : isPrivate, + isExtern : isExtern, + }); + case "enum": + if(maybe(TId("abstract"))) { + return switch parseAbstract() { + case DAbstract(a): + var counter = 0; + inline function fallbackInit(fieldName):Expr return switch a.t { // positions will be wrong + case CTPath(['String'], _): mk(EConst(CString(fieldName)), readPos, readPos+1); + case CTPath(['Int'], _): mk(EConst(CInt(counter++)), readPos, readPos+1); + default: error(ECustom('expected variable initializer for $fieldName'), readPos, readPos+1); null; + } + a.meta = [{name: ':enum', params: []}].concat(a.meta); + a.fields = [for(f in a.fields) switch f.kind { + case KVar(v) if(f.access.length == 0): + { + name: f.name, + meta: f.meta, + kind: KVar({ + type: CTPath(a.name.split('.'), [for(i in 0...a.params.length) CTParam(a.params[i].name, i)]), + set: null, + get: null, + expr: if(v.expr == null) fallbackInit(f.name) else v.expr + }), + access: [APublic,AStatic,AInline] + }; + default: f; + }]; + DAbstract(a); + default:error(ECustom('assert'), readPos, readPos+1); null; + } + + } else { + var name = getIdent(); + var params = parseParams(); + var fields = []; + ensure(TBrOpen); + var i = 0; + while( !maybe(TBrClose) ) + fields.push(parseEnumCtor(name, params, i++)); + return DEnum({ + name: name, + params: params, + fields: fields, + meta: meta, + isPrivate: isPrivate, + isExtern: isExtern + }); + } case "class": var name = getIdent(); var params = parseParams(); @@ -1183,36 +1322,43 @@ class Parser { return null; } - function parseField() : FieldDecl { + function parseField(?isInterface = false) : FieldDecl { var meta = parseMetadata(); var access = []; + var inlined = false; + inline function unexpectedModifier(modifier:String) error(EUnexpected('access modifier $modifier on interface member'), readPos, readPos+1); + inline function addAccess(modifier:FieldAccess) { + if(isInterface) { + unexpectedModifier('$modifier'); + } else access.push(modifier); + } while( true ) { var id = getIdent(); switch( id ) { case "override": - access.push(AOverride); + addAccess(AOverride); case "public": - access.push(APublic); + addAccess(APublic); case "private": - access.push(APrivate); + addAccess(APrivate); case "inline": - access.push(AInline); + addAccess(AInline); case "static": - access.push(AStatic); + addAccess(AStatic); case "macro": - access.push(AMacro); + addAccess(AMacro); case "function": var name = getIdent(); var inf = parseFunctionDecl(); + if(isInterface) + + if(inf.expr != null) error(EUnexpected("method body in interface"), readPos, readPos+1); + return { name : name, meta : meta, access : access, - kind : KFunction({ - args : inf.args, - expr : inf.body, - ret : inf.ret, - }), + kind : KFunction(inf), }; case "var": var name = getIdent(); @@ -1225,9 +1371,10 @@ class Parser { } var type = maybe(TDoubleDot) ? parseType() : null; var expr = maybe(TOp("=")) ? parseExpr() : null; - + if( expr != null ) { - if( isBlock(expr) ) + if(isInterface) error(EUnexpected("field initializer"), readPos, readPos+1); + else if( isBlock(expr) ) maybe(TSemicolon); else ensure(TSemicolon); @@ -1235,7 +1382,6 @@ class Parser { maybe(TSemicolon); } else ensure(TSemicolon); - return { name : name, meta : meta, @@ -1254,7 +1400,32 @@ class Parser { } return null; } - + function parseEnumCtor(enumName:String, params:Params, index:Int) { + var meta = parseMetadata(); + var name = getIdent(); + var i = 0; + var params = [for(param in params) CTParam(param.name, i++)]; + var kind = if(maybe(TOp('='))) { + var varDecl:VarDecl = { + type: CTPath(enumName.split('.'), params), + expr: mk(EConst(CInt(index))), + get: null, + set: null + }; + KVar(varDecl); + } else { + + var inf = parseFunctionDecl(); + if(inf.expr != null) error(EUnexpected("method body in interface"), readPos, readPos+1); + KFunction(inf); + } + return { + name : name, + meta : meta, + access : [APublic], + kind :kind + }; + } // ------------------------ lexing ------------------------------- inline function readChar() { @@ -1723,4 +1894,5 @@ class Parser { } } + } diff --git a/hscript/Printer.hx b/hscript/Printer.hx index 3c5b61cc..ed274788 100644 --- a/hscript/Printer.hx +++ b/hscript/Printer.hx @@ -48,6 +48,9 @@ class Printer { function type( t : CType ) { switch( t ) { + case null: 'Void'; + case CTParam(p, _): + add(p); case CTOpt(t): add('?'); type(t); @@ -207,7 +210,7 @@ class Printer { add("break"); case EContinue: add("continue"); - case EFunction(params, e, name, ret): + case EFunction(Tools.getFunctionName(_) => name, {args: params, expr: e, ret:ret}): add("function"); if( name != null ) add(" " + name); diff --git a/hscript/Tools.hx b/hscript/Tools.hx index e0dc2359..73cfc738 100644 --- a/hscript/Tools.hx +++ b/hscript/Tools.hx @@ -39,7 +39,7 @@ class Tools { case EDoWhile(c, e): f(c); f(e); case EFor(_, it, e): f(it); f(e); case EBreak,EContinue: - case EFunction(_, e, _, _): f(e); + case EFunction(_, {expr: 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); @@ -74,7 +74,7 @@ class Tools { 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 EFunction(args, e, name, t): EFunction(args, f(e), name, t); + case EFunction(Tools.getFunctionName(_) => name, {args:args, expr: e, ret: t}): EFunction(getFunctionType(name), {args:args, expr: f(e), ret: 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)]); @@ -100,10 +100,28 @@ class Tools { public static inline function mk( e : ExprDef, p : Expr ) { #if hscriptPos + if(p == null) p = {pmin: 0, pmax: 0, origin: null, line: 0, e: null}; return { e : e, pmin : p.pmin, pmax : p.pmax, origin : p.origin, line : p.line }; #else return e; #end } + static var ANON = '_$$_anon_$$_'; + static var ARROW = '_$$_arrow_$$_'; + public static function getFunctionName(k:FunctionKind) { + return switch k { + case FAnonymous: ANON; + case FArrow: ARROW; + case FNamed(name, _): name; + } + } + + public static function getFunctionType(name:String):FunctionKind { + return switch name { + case '$ANON'|null|'': FAnonymous; + case '$ARROW': FArrow; + case v: FNamed(v, false); + } + } } \ No newline at end of file