diff --git a/src/common/ecs/compiler.ts b/src/common/ecs/compiler.ts index 2f109608..13de3ed8 100644 --- a/src/common/ecs/compiler.ts +++ b/src/common/ecs/compiler.ts @@ -92,9 +92,11 @@ export class ECSCompiler extends Tokenizer { } annotate(fn: () => T) { - let tok = this.peekToken(); + let start = this.peekToken(); let obj = fn(); - if (obj) (obj as SourceLocated).$loc = tok.$loc; + let end = this.lasttoken; + let $loc = end ? mergeLocs(start.$loc, end.$loc) : start.$loc; + if (obj) (obj as SourceLocated).$loc = $loc; return obj; } @@ -349,7 +351,7 @@ export class ECSCompiler extends Tokenizer { tempbytes = this.parseIntegerConstant(); } let system: System = { name, tempbytes, actions: [] }; - let expr = this.parseBlockStatement(); + let expr = this.annotate(() => this.parseBlockStatement()); let action: Action = { expr, event: name }; system.actions.push(action); return system; @@ -363,7 +365,7 @@ export class ECSCompiler extends Tokenizer { let critical = undefined; if (this.ifToken('critical')) critical = true; if (this.ifToken('fit')) fitbytes = this.parseIntegerConstant(); - let expr = this.parseBlockStatement(); + let expr = this.annotate(() => this.parseBlockStatement()); //query, join, select, direction, let action : Action = { expr, event, fitbytes, critical }; return action as Action; @@ -770,6 +772,7 @@ export class ECSCompiler extends Tokenizer { parseExprList(): Expr[] { return this.parseList(this.parseExpr, ','); } + // TODO: annotate with location parseBlockStatement(): Statement { let valtype : IntType = { dtype:'int', lo:0, hi: 0 } // TODO? if (this.peekToken().type == ECSTokenType.CodeFragment) { @@ -778,7 +781,7 @@ export class ECSCompiler extends Tokenizer { if (this.ifToken('begin')) { let stmts = []; while (this.peekToken().str != 'end') { - stmts.push(this.parseBlockStatement()); + stmts.push(this.annotate(() => this.parseBlockStatement())); } this.expectToken('end'); return { valtype, stmts }; @@ -812,7 +815,7 @@ export class ECSCompiler extends Tokenizer { let direction = undefined; if (modifiers['asc']) direction = 'asc'; else if (modifiers['desc']) direction = 'desc'; - let body = this.parseBlockStatement(); + let body = this.annotate(() => this.parseBlockStatement()); return { select, query, join, direction, stmts: [body], loop: select == 'foreach' } as QueryExpr; } } diff --git a/src/common/ecs/ecs.ts b/src/common/ecs/ecs.ts index e60e91bf..c38b46e8 100644 --- a/src/common/ecs/ecs.ts +++ b/src/common/ecs/ecs.ts @@ -3,8 +3,10 @@ import { Token } from "../tokenizer"; import { SourceLocated, SourceLocation } from "../workertypes"; import { Bin, Packer } from "./binpack"; -export class ECSError extends Error { +export class ECSError extends Error implements SourceLocated { $loc: SourceLocation; + $sources: SourceLocated[] = []; + constructor(msg: string, obj?: SourceLocation | SourceLocated) { super(msg); Object.setPrototypeOf(this, ECSError.prototype); @@ -834,7 +836,12 @@ class ActionEval { __emit(args: string[]) { let event = args[0]; let eventargs = args.slice(1); - return this.scope.generateCodeForEvent(event, eventargs); + try { + return this.scope.generateCodeForEvent(event, eventargs); + } catch (e) { + if (e.$sources) e.$sources.push(this.action); + throw e; + } } __local(args: string[]) { let tempinc = parseInt(args[0]); @@ -1053,6 +1060,7 @@ class ActionEval { case 'once': // TODO: how is this different from begin/end? //state.xreg = state.yreg = null; + //state.working = new EntitySet(scope, undefined, [], []); break; case 'foreach': case 'unroll': @@ -1077,7 +1085,7 @@ class ActionEval { if (select == 'if') { qr.entities = []; // "if" failed } else { - throw new ECSError(`no entities in statement`, action); + throw new ECSError(`no entities match query`, qexpr); } } else { // TODO: must be a better way... diff --git a/src/worker/tools/ecs.ts b/src/worker/tools/ecs.ts index 040d1b94..6798ca5a 100644 --- a/src/worker/tools/ecs.ts +++ b/src/worker/tools/ecs.ts @@ -27,6 +27,11 @@ export function assembleECS(step: BuildStep): BuildStepResult { } catch (e) { if (e instanceof ECSError) { compiler.addError(e.message, e.$loc); + for (let obj of e.$sources) { + let name = (obj as any).event; + if (name == 'start') break; + compiler.addError(`... ${name}`, obj.$loc); // TODO? + } return { errors: compiler.errors }; } else if (e instanceof CompileError) { return { errors: compiler.errors };