added Parser.resumeError, added Checker.getCompletion

This commit is contained in:
Nicolas Cannasse
2019-10-20 11:16:23 +02:00
parent 22eb58b3de
commit f464767ee7
2 changed files with 83 additions and 26 deletions
+50 -12
View File
@@ -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
View File
@@ -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;