diff options
Diffstat (limited to 'js/baba-yaga/runner.js')
-rw-r--r-- | js/baba-yaga/runner.js | 59 |
1 files changed, 30 insertions, 29 deletions
diff --git a/js/baba-yaga/runner.js b/js/baba-yaga/runner.js index 4d2e9df..da9830a 100644 --- a/js/baba-yaga/runner.js +++ b/js/baba-yaga/runner.js @@ -1,51 +1,52 @@ // runner.js // Provides a host-agnostic evaluate(source, host) entrypoint that lexes, parses, and interprets. -import { createLexer } from './lexer.js'; -import { createParser } from './parser.js'; -import { createInterpreter } from './interpreter.js'; +import { createLexer } from './src/core/lexer.js'; +import { createParser } from './src/core/parser.js'; +import { createInterpreter } from './src/core/interpreter.js'; /** * Evaluate source code in the toy language and return a result object. * @param {string} source - The program source code. * @param {object} host - Optional host bindings, e.g. { io: { out, in } }. - * @returns {{ ok: true, value: any } | { ok: false, error: { message: string, line?: number, column?: number, codeFrame?: string } }} + * @returns {object} Result object with { ok: boolean, value?: any, error?: string } */ export function evaluate(source, host = {}) { try { const lexer = createLexer(source); const tokens = lexer.allTokens(); - const parser = createParser(tokens); + const parser = createParser(tokens, false, source); const ast = parser.parse(); const interpreter = createInterpreter(ast, host); - const value = interpreter.interpret(); - return { ok: true, value }; + const result = interpreter.interpret(); + return { ok: true, value: result }; } catch (error) { - const message = error && error.message ? error.message : String(error); - const match = / at (\d+):(\d+)/.exec(message); - const line = match ? Number(match[1]) : undefined; - const column = match ? Number(match[2]) : undefined; - const codeFrame = makeCodeFrame(source, line, column); - return { ok: false, error: { message, line, column, codeFrame } }; + return { ok: false, error: error.message }; } } /** - * Create a simple code frame for the given position. - * @param {string} source - * @param {number|undefined} line - * @param {number|undefined} column - * @returns {string} + * Create a code frame showing the error location in source code + * @param {string} source - The source code + * @param {object} location - Location object with line/column + * @param {string} message - Error message + * @returns {string} Formatted code frame */ -export function makeCodeFrame(source, line, column) { - if (!line || !column) return ''; - const lines = source.split(/\r?\n/); - const idx = line - 1; - const context = [idx - 1, idx, idx + 1].filter(i => i >= 0 && i < lines.length); - const pad = n => String(n + 1).padStart(4, ' '); - const caret = ' '.repeat(column - 1) + '^'; - const rows = context.map(i => `${pad(i)} | ${lines[i]}`); - rows.splice(context.indexOf(idx) + 1, 0, ` | ${caret}`); - return rows.join('\n'); -} +export function makeCodeFrame(source, location, message) { + if (!location || !location.line) { + return message; + } + const lines = source.split('\n'); + const lineIndex = location.line - 1; + const line = lines[lineIndex]; + + if (!line) { + return message; + } + + const column = location.column || 1; + const pointer = ' '.repeat(Math.max(0, column - 1)) + '^'; + + return `${message}\n ${location.line} | ${line}\n | ${pointer}`; +} |