Skip to content

Commit

Permalink
added the very basic stack tracking in SchemioScript to improve the e…
Browse files Browse the repository at this point in the history
…rror message
  • Loading branch information
ishubin committed Jan 28, 2025
1 parent ea1b120 commit 20bf06d
Show file tree
Hide file tree
Showing 6 changed files with 53 additions and 18 deletions.
6 changes: 3 additions & 3 deletions src/ui/templater/ast.js
Original file line number Diff line number Diff line change
Expand Up @@ -442,7 +442,7 @@ class ASTParser extends TokenScanner {
throw new Error(`Expected function body declaration for "${funcNameToken.v}" function, got: "${funcBodyToken.text}"`);
}

const functionAST = parseFunctionDeclarationUsing(funcArgsToken, funcBodyToken);
const functionAST = parseFunctionDeclarationUsing(funcArgsToken, funcBodyToken, funcNameToken.v);
return new ASTAssign(new ASTVarRef(funcNameToken.v), functionAST);
}

Expand Down Expand Up @@ -545,7 +545,7 @@ class ASTParser extends TokenScanner {
* @param {ScriptToken} argsToken
* @param {ScriptToken} bodyToken
*/
function parseFunctionDeclarationUsing(argsToken, bodyToken) {
function parseFunctionDeclarationUsing(argsToken, bodyToken, funcName = '< anonymous >') {
if (!argsToken) {
throw new Error('Cannot parse function declaration. Missing arguments definition');
}
Expand Down Expand Up @@ -579,7 +579,7 @@ function parseFunctionDeclarationUsing(argsToken, bodyToken) {

const funcBody = parseAST(bodyToken.groupTokens);

return new ASTFunctionDeclaration(argNames, funcBody);
return new ASTFunctionDeclaration(argNames, funcBody, funcName);
}

/**
Expand Down
19 changes: 19 additions & 0 deletions src/ui/templater/error.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
export class SchemioScriptError extends Error{
constructor(message, scope, error) {
super(message);
this.message = message;
this.scope = scope;
this.error = error;
}

print() {
let fullMessage = this.message;

let scope = this.scope;
while(scope) {
fullMessage += '\n\tat ' + scope.stackName;
scope = scope.parent;
}
console.error(fullMessage);
}
}
28 changes: 19 additions & 9 deletions src/ui/templater/nodes.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import { parseColor } from "../colors";
import { Color } from "./color";
import { Area } from "./area";
import { Fill } from "./fill";
import { SchemioScriptError } from "./error";

const FUNC_INVOKE = 'funcInvoke';
const VAR_REF = 'var-ref';
Expand Down Expand Up @@ -205,8 +206,8 @@ export class ASTWhileStatement extends ASTNode {
*/
evalNode(scope) {
let lastResult = null;
while (this.whileExpression.evalNode(scope.newScope())) {
lastResult = this.whileBlock.evalNode(scope.newScope());
while (this.whileExpression.evalNode(scope.newScope('while expression'))) {
lastResult = this.whileBlock.evalNode(scope.newScope('while block'));
}
return lastResult;
}
Expand Down Expand Up @@ -235,7 +236,7 @@ export class ASTForLoop extends ASTNode {
* @param {Scope} scope
*/
evalNode(scope) {
scope = scope.newScope();
scope = scope.newScope('for loop');
this.init.evalNode(scope);

while(this.condition.evalNode(scope)) {
Expand All @@ -258,14 +259,14 @@ export class ASTIFStatement extends ASTNode {
}

evalNode(scope) {
const result = this.conditionExpression.evalNode(scope.newScope());
const result = this.conditionExpression.evalNode(scope.newScope('if statement'));
if (result) {
if (!this.trueBlock) {
return null;
}
return this.trueBlock.evalNode(scope.newScope());
return this.trueBlock.evalNode(scope.newScope('if block'));
} else if (this.falseBlock) {
return this.falseBlock.evalNode(scope.newScope());
return this.falseBlock.evalNode(scope.newScope('else block'));
}
return null;
}
Expand Down Expand Up @@ -680,10 +681,11 @@ export class ASTFunctionDeclaration extends ASTNode {
* @param {Array<String>} argNames
* @param {ASTNode} body
*/
constructor(argNames, body) {
constructor(argNames, body, funcName) {
super('function');
this.argNames = argNames;
this.body = body;
this.funcName = funcName;
}
print() {
return `(${this.argNames.join(',')}) => {(${this.body.print()})}`;
Expand All @@ -694,11 +696,19 @@ export class ASTFunctionDeclaration extends ASTNode {
*/
evalNode(scope) {
return (...args) => {
const funcScope = scope.newScope();
const funcScope = scope.newScope('func ' + this.funcName);
for (let i = 0; i < args.length && i < this.argNames.length; i++) {
funcScope.setLocal(this.argNames[i], args[i]);
}
return this.body.evalNode(funcScope);
try {
return this.body.evalNode(funcScope);
} catch (err) {
if (err instanceof SchemioScriptError) {
throw err;
} else {
throw new SchemioScriptError(err.message, funcScope, err);
}
}
};
}
}
Expand Down
7 changes: 4 additions & 3 deletions src/ui/templater/scope.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,9 @@ export class Scope {
* @param {Scope|null} parent
* @param {function(string): any} externalObjectProvider
*/
constructor(data, parent, externalObjectProvider) {
constructor(data, parent, externalObjectProvider, stackName = '< unknown >') {
this.data = data || {};
this.stackName = stackName;
this.parent = parent;
this.externalObjectProvider = externalObjectProvider;
}
Expand Down Expand Up @@ -54,8 +55,8 @@ export class Scope {
this.data[varName] = value;
}

newScope(data = {}) {
return new Scope(data, this, this.externalObjectProvider);
newScope(stackName = '< unknown >', data = {}) {
return new Scope(data, this, this.externalObjectProvider, stackName);
}

getData() {
Expand Down
2 changes: 1 addition & 1 deletion src/ui/templater/struct.js
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ export class ASTStructNode extends ASTNode {
evalNode(scope) {
const initFunc = (...args) => {
const structObj = {};
const structScope = scope.newScope({'this': structObj});
const structScope = scope.newScope('struct ' + this.name, {'this': structObj});

this.fieldDefinitions.forEach((fieldDef, idx) => {
if (idx < args.length) {
Expand Down
9 changes: 7 additions & 2 deletions src/ui/templater/templater.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { ASTNode, parseExpression } from "./ast";
import { SchemioScriptError } from "./error";
import { List } from "./list";
import { Scope } from './scope';
import { parseStringExpression } from "./strings";
Expand Down Expand Up @@ -192,7 +193,11 @@ function compileExpression(expr) {
return ast.evalNode(scope);
} catch(ex) {
console.error('Failed to evaluate expression:\n' + expr);
console.error(ex);
if (ex instanceof SchemioScriptError) {
ex.print();
} else {
console.error(ex);
}
}
};
}
Expand Down Expand Up @@ -356,7 +361,7 @@ function compileRecursiveBuilder(item, $recurse, customDefinitions) {
}
const data = {};
data[iteratorName] = sourceObject;
const iteratorScope = scope.newScope(data);
const iteratorScope = scope.newScope('buildObject', data);

const processedObj = itemProcessor(iteratorScope);
const children = childrenFetcher.evalNode(iteratorScope);
Expand Down

0 comments on commit 20bf06d

Please sign in to comment.