added Parser.resumeError, added Checker.getCompletion
This commit is contained in:
+50
-12
@@ -53,6 +53,7 @@ typedef CClass = {> CNamedType,
|
||||
|
||||
typedef CField = {
|
||||
var isPublic : Bool;
|
||||
var complete : Bool;
|
||||
var params : Array<TType>;
|
||||
var name : String;
|
||||
var t : TType;
|
||||
@@ -69,6 +70,15 @@ typedef CTypedef = {> CNamedType,
|
||||
typedef CAbstract = {> CNamedType,
|
||||
}
|
||||
|
||||
class Completion {
|
||||
public var expr : Expr;
|
||||
public var t : TType;
|
||||
public function new(expr,t) {
|
||||
this.expr = expr;
|
||||
this.t = t;
|
||||
}
|
||||
}
|
||||
|
||||
@:allow(hscript.Checker)
|
||||
class CheckerTypes {
|
||||
|
||||
@@ -118,13 +128,17 @@ class CheckerTypes {
|
||||
for( f in c.fields ) {
|
||||
if( f.isOverride || f.name.substr(0,4) == "get_" || f.name.substr(0,4) == "set_" ) continue;
|
||||
var skip = false;
|
||||
for( m in f.meta )
|
||||
if( m.name == ":noScript" ) {
|
||||
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, params : [], name : f.name, t : null };
|
||||
var fl : CField = { isPublic : f.isPublic, complete : complete, params : [], name : f.name, t : null };
|
||||
for( p in f.params ) {
|
||||
var pt = TParam(p);
|
||||
var key = f.name+"."+p;
|
||||
@@ -251,6 +265,7 @@ class Checker {
|
||||
var globals : Map<String,TType> = new Map();
|
||||
var events : Map<String,TType> = new Map();
|
||||
var currentFunType : TType;
|
||||
var isCompletion : Bool;
|
||||
public var allowAsync : Bool;
|
||||
public var allowReturn : Null<TType>;
|
||||
|
||||
@@ -283,9 +298,10 @@ class Checker {
|
||||
}];
|
||||
}
|
||||
|
||||
public function check( expr : Expr, ?withType : WithType ) {
|
||||
public function check( expr : Expr, ?withType : WithType, ?isCompletion = false ) {
|
||||
if( withType == null ) withType = NoValue;
|
||||
locals = new Map();
|
||||
this.isCompletion = isCompletion;
|
||||
switch( edef(expr) ) {
|
||||
case EBlock(el):
|
||||
var delayed = [];
|
||||
@@ -328,7 +344,7 @@ class Checker {
|
||||
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
|
||||
throw e;
|
||||
if( !isCompletion ) throw e;
|
||||
}
|
||||
|
||||
function saveLocals() {
|
||||
@@ -339,7 +355,10 @@ class Checker {
|
||||
return switch (t) {
|
||||
case CTPath(path, params):
|
||||
var ct = types.resolve(path.join("."),params == null ? [] : [for( p in params ) makeType(t,e)]);
|
||||
if( ct == null ) error("Unknown type "+path, e);
|
||||
if( ct == null ) {
|
||||
error("Unknown type "+path, e);
|
||||
ct = TDynamic;
|
||||
}
|
||||
return ct;
|
||||
case CTFun(args, ret):
|
||||
var i = 0;
|
||||
@@ -647,7 +666,7 @@ class Checker {
|
||||
cf = c.fields.get("a_"+f);
|
||||
if( cf != null ) {
|
||||
var isPublic = true; // consider a_ prefixed as script specific
|
||||
cf = { isPublic : isPublic, params : cf.params, name : cf.name, t : unasync(cf.t) };
|
||||
cf = { isPublic : isPublic, params : cf.params, name : cf.name, t : unasync(cf.t), complete : cf.complete };
|
||||
if( cf.t == null ) cf = null;
|
||||
}
|
||||
}
|
||||
@@ -712,7 +731,16 @@ class Checker {
|
||||
return t.match(TInst({name:"String"},_));
|
||||
}
|
||||
|
||||
function onCompletion( expr : Expr, t : TType ) {
|
||||
if( isCompletion ) throw new Completion(expr, t);
|
||||
}
|
||||
|
||||
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) {
|
||||
@@ -738,6 +766,7 @@ class Checker {
|
||||
case "trace":
|
||||
return TDynamic;
|
||||
default:
|
||||
if( isCompletion) return TDynamic;
|
||||
error("Unknown identifier "+v, expr);
|
||||
}
|
||||
case EBlock(el):
|
||||
@@ -764,24 +793,29 @@ class Checker {
|
||||
for( i in 0...params.length ) {
|
||||
var a = args[i];
|
||||
if( a == null ) error("Too many arguments", params[i]);
|
||||
var t = typeExpr(params[i], WithType(a.t));
|
||||
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+"'", expr);
|
||||
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):
|
||||
var ot = typeExpr(o, Value);
|
||||
if( f == null )
|
||||
onCompletion(expr, ot);
|
||||
var ft = getField(ot, f, expr);
|
||||
if( ft == null )
|
||||
if( ft == null ) {
|
||||
error(typeStr(ot)+" has no field "+f, expr);
|
||||
ft = TDynamic;
|
||||
}
|
||||
return ft;
|
||||
case ECheckType(v, t):
|
||||
var ct = makeType(t, expr);
|
||||
@@ -813,8 +847,11 @@ class Checker {
|
||||
var map = [for( f in tfields ) f.name => f];
|
||||
return TAnon([for( f in fl ) {
|
||||
var ft = map.get(f.name);
|
||||
if( ft == null ) error("Extra field "+f.name, f.e);
|
||||
{ t : typeExprWith(f.e, ft.t), opt : false, name : 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 }]);
|
||||
@@ -1032,6 +1069,7 @@ class Checker {
|
||||
case ENew(cl, params):
|
||||
}
|
||||
error("Don't know how to type "+edef(expr).getName(), expr);
|
||||
return TDynamic;
|
||||
}
|
||||
|
||||
}
|
||||
+33
-14
@@ -78,6 +78,11 @@ class Parser {
|
||||
**/
|
||||
public var allowMetadata : Bool;
|
||||
|
||||
/**
|
||||
resume from parsing errors (when parsing incomplete code, during completion for example)
|
||||
**/
|
||||
public var resumeErrors : Bool;
|
||||
|
||||
// implementation
|
||||
var input : haxe.io.Input;
|
||||
var char : Int;
|
||||
@@ -142,6 +147,7 @@ class Parser {
|
||||
}
|
||||
|
||||
public inline function error( err, pmin, pmax ) {
|
||||
if( !resumeErrors )
|
||||
#if hscriptPos
|
||||
throw new Error(err, pmin, pmax, origin, line);
|
||||
#else
|
||||
@@ -329,6 +335,7 @@ class Parser {
|
||||
break;
|
||||
default:
|
||||
unexpected(tk);
|
||||
break;
|
||||
}
|
||||
ensure(TDoubleDot);
|
||||
fl.push({ name : id, e : parseExpr() });
|
||||
@@ -422,7 +429,7 @@ class Parser {
|
||||
while( true ) {
|
||||
parseFullExpr(a);
|
||||
tk = token();
|
||||
if( tk == TBrClose )
|
||||
if( tk == TBrClose || (resumeErrors && tk == TEof) )
|
||||
break;
|
||||
push(tk);
|
||||
}
|
||||
@@ -445,7 +452,7 @@ class Parser {
|
||||
case TBkOpen:
|
||||
var a = new Array();
|
||||
tk = token();
|
||||
while( tk != TBkClose ) {
|
||||
while( tk != TBkClose && (!resumeErrors || tk != TEof) ) {
|
||||
push(tk);
|
||||
a.push(parseExpr());
|
||||
tk = token();
|
||||
@@ -485,6 +492,7 @@ class Parser {
|
||||
break;
|
||||
default:
|
||||
unexpected(tk);
|
||||
break;
|
||||
}
|
||||
}
|
||||
ensureToken(TOp("->"));
|
||||
@@ -537,6 +545,8 @@ class Parser {
|
||||
}
|
||||
|
||||
function makeUnop( op, e ) {
|
||||
if( e == null && resumeErrors )
|
||||
return null;
|
||||
return switch( expr(e) ) {
|
||||
case EBinop(bop, e1, e2): mk(EBinop(bop, makeUnop(op, e1), e2), pmin(e1), pmax(e2));
|
||||
case ETernary(e1, e2, e3): mk(ETernary(makeUnop(op, e1), e2, e3), pmin(e1), pmax(e3));
|
||||
@@ -545,6 +555,8 @@ class Parser {
|
||||
}
|
||||
|
||||
function makeBinop( op, e1, e ) {
|
||||
if( e == null && resumeErrors )
|
||||
return mk(EBinop(op,e1,e),pmin(e1),pmax(e1));
|
||||
return switch( expr(e) ) {
|
||||
case EBinop(op2,e2,e3):
|
||||
if( opPriority.get(op) <= opPriority.get(op2) && !opRightAssoc.exists(op) )
|
||||
@@ -644,16 +656,16 @@ class Parser {
|
||||
case "new":
|
||||
var a = new Array();
|
||||
a.push(getIdent());
|
||||
var next = true;
|
||||
while( next ) {
|
||||
while( true ) {
|
||||
var tk = token();
|
||||
switch( tk ) {
|
||||
case TDot:
|
||||
a.push(getIdent());
|
||||
case TPOpen:
|
||||
next = false;
|
||||
break;
|
||||
default:
|
||||
unexpected(tk);
|
||||
break;
|
||||
}
|
||||
}
|
||||
var args = parseExprList(TPClose);
|
||||
@@ -739,6 +751,7 @@ class Parser {
|
||||
break;
|
||||
default:
|
||||
unexpected(tk);
|
||||
break;
|
||||
}
|
||||
}
|
||||
mk(ESwitch(e, cases, def), p1, tokenMax);
|
||||
@@ -809,7 +822,9 @@ class Parser {
|
||||
}
|
||||
switch( tk ) {
|
||||
case TId(id): name = id;
|
||||
default: unexpected(tk);
|
||||
default:
|
||||
unexpected(tk);
|
||||
break;
|
||||
}
|
||||
var arg : Argument = { name : name };
|
||||
args.push(arg);
|
||||
@@ -830,8 +845,8 @@ class Parser {
|
||||
unexpected(tk);
|
||||
}
|
||||
}
|
||||
}
|
||||
return args;
|
||||
}
|
||||
return args;
|
||||
}
|
||||
|
||||
function parseFunctionDecl() {
|
||||
@@ -891,6 +906,7 @@ class Parser {
|
||||
default:
|
||||
}
|
||||
unexpected(t);
|
||||
break;
|
||||
}
|
||||
} else
|
||||
push(t);
|
||||
@@ -901,13 +917,13 @@ class Parser {
|
||||
case TPOpen:
|
||||
var a = token(),
|
||||
b = token();
|
||||
|
||||
push(b);
|
||||
|
||||
push(b);
|
||||
push(a);
|
||||
|
||||
function withReturn(args) {
|
||||
switch token() { // I think it wouldn't hurt if ensure used enumEq
|
||||
case TOp('->'):
|
||||
case TOp('->'):
|
||||
case t: unexpected(t);
|
||||
}
|
||||
|
||||
@@ -916,7 +932,7 @@ class Parser {
|
||||
|
||||
switch [a, b] {
|
||||
case [TPClose, _] | [TId(_), TDoubleDot]:
|
||||
|
||||
|
||||
var args = [for (arg in parseFunctionArgs()) {
|
||||
switch arg.value {
|
||||
case null:
|
||||
@@ -929,12 +945,12 @@ class Parser {
|
||||
|
||||
return withReturn(args);
|
||||
default:
|
||||
|
||||
|
||||
var t = parseType();
|
||||
return switch token() {
|
||||
case TComma:
|
||||
var args = [t];
|
||||
|
||||
|
||||
while (true) {
|
||||
args.push(parseType());
|
||||
if (!maybe(TComma)) break;
|
||||
@@ -973,6 +989,7 @@ class Parser {
|
||||
meta.push({ name : name, params : parseMetaArgs() });
|
||||
default:
|
||||
unexpected(t);
|
||||
break;
|
||||
}
|
||||
}
|
||||
return parseTypeNext(CTAnon(fields));
|
||||
@@ -1017,6 +1034,7 @@ class Parser {
|
||||
default:
|
||||
if( tk == etk ) break;
|
||||
unexpected(tk);
|
||||
break;
|
||||
}
|
||||
}
|
||||
return args;
|
||||
@@ -1219,6 +1237,7 @@ class Parser {
|
||||
};
|
||||
default:
|
||||
unexpected(TId(id));
|
||||
break;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
|
||||
Reference in New Issue
Block a user