diff --git a/src/yarn-spinner/parse/parser.ts b/src/yarn-spinner/parse/parser.ts index afa3a43..4faa91f 100644 --- a/src/yarn-spinner/parse/parser.ts +++ b/src/yarn-spinner/parse/parser.ts @@ -21,7 +21,12 @@ export class ParseError extends Error {} export function parseYarn(text: string): YarnDocument { const tokens = lex(text); const p = new Parser(tokens); - return p.parseDocument(); + try{ + return p.parseDocument(); + }catch(e){ + console.log(`parser status: `, p.status()); + throw e; + } } class Parser { @@ -493,5 +498,16 @@ class Parser { } return body; } + + public status(){ + // find the first title before the current token + let closestNode = this.tokens.slice(0, this.i).reverse().findIndex(t => t.type === "HEADER_KEY" && t.text === "title"); + return { + i: this.i, + tokens: this.tokens, + token: this.peek(), + closestNode: this.tokens[this.i - closestNode] + } + } } diff --git a/src/yarn-spinner/runtime/runner.ts b/src/yarn-spinner/runtime/runner.ts index 2a41249..d20f835 100644 --- a/src/yarn-spinner/runtime/runner.ts +++ b/src/yarn-spinner/runtime/runner.ts @@ -536,100 +536,6 @@ export class YarnRunner { } } - private executeBlock(block: { title: string; instructions: IRInstruction[] }) { - // Execute instructions of block, then resume - const saved = { title: this.nodeTitle, ip: this.ip } as const; - this.nodeTitle = block.title; - const tempIpStart = 0; - const tempNode = { title: block.title, instructions: block.instructions } as const; - // Use a temporary node context - const restore = () => { - this.nodeTitle = saved.title; - this.ip = saved.ip; - }; - - // Step through block, emitting first result - let idx = tempIpStart; - while (true) { - const ins = tempNode.instructions[idx++]; - if (!ins) break; - switch (ins.op) { - case "line": { - const { text: interpolatedText, markup: interpolatedMarkup } = this.interpolate(ins.text, ins.markup); - this.emit({ type: "text", text: interpolatedText, speaker: ins.speaker, markup: interpolatedMarkup, isDialogueEnd: false }); - restore(); - return; - } - case "command": - try { - const parsed = parseCommand(ins.content); - this.commandHandler.execute(parsed, this.evaluator).catch(() => {}); - if (this.handleCommand) this.handleCommand(ins.content, parsed); - } catch { - if (this.handleCommand) this.handleCommand(ins.content); - } - this.emit({ type: "command", command: ins.content, isDialogueEnd: false }); - restore(); - return; - case "options": { - const available = this.filterOptions(ins.options); - if (available.length === 0) { - continue; - } - this.pendingOptions = available; - this.emit({ - type: "options", - options: available.map((o) => { - const { text: interpolatedText, markup: interpolatedMarkup } = this.interpolate(o.text, o.markup); - return { text: interpolatedText, markup: interpolatedMarkup }; - }), - isDialogueEnd: false, - }); - // Maintain context that options belong to main node at ip-1 - restore(); - return; - } - case "if": { - const branch = ins.branches.find((b) => (b.condition ? this.evaluator.evaluate(b.condition) : true)); - if (branch) { - // enqueue nested block and resume from main context - this.callStack.push({ kind: "block", title: this.nodeTitle, ip: this.ip, block: branch.block, idx: 0 }); - restore(); - if (this.resumeBlock()) return; - return; - } - break; - } - case "once": { - if (!this.onceSeen.has(ins.id)) { - this.onceSeen.add(ins.id); - this.callStack.push({ kind: "block", title: this.nodeTitle, ip: this.ip, block: ins.block, idx: 0 }); - restore(); - if (this.resumeBlock()) return; - return; - } - break; - } - case "jump": { - this.nodeTitle = ins.target; - this.ip = 0; - this.step(); - return; - } - case "detour": { - this.callStack.push({ kind: "detour", title: saved.title, ip: saved.ip }); - this.nodeTitle = ins.target; - this.ip = 0; - this.step(); - return; - } - } - } - // Block produced no output; resume - restore(); - this.step(); - } - private filterOptions(options: CompiledOption[]): CompiledOption[] { const available: CompiledOption[] = []; for (const option of options) {