diff --git a/src/compiler/compiler.ts b/src/compiler/compiler.ts index d52576e..3515ae2 100644 --- a/src/compiler/compiler.ts +++ b/src/compiler/compiler.ts @@ -313,15 +313,17 @@ export class Compiler { if (opValue === '??=') { instructions.push(['LOAD', identifierName]) + const skipLabel: Label = `.skip_${this.labelCount++}` const rightInstructions = this.#compileNode(right, input) instructions.push(['DUP']) instructions.push(['PUSH', null]) instructions.push(['NEQ']) - instructions.push(['JUMP_IF_TRUE', rightInstructions.length + 1]) + instructions.push(['JUMP_IF_TRUE', skipLabel]) instructions.push(['POP']) instructions.push(...rightInstructions) + instructions.push([`${skipLabel}:`]) instructions.push(['DUP']) instructions.push(['STORE', identifierName]) @@ -419,8 +421,8 @@ export class Compiler { case terms.FunctionCallOrIdentifier: { if (node.firstChild?.type.id === terms.DotGet) { const instructions: ProgramItem[] = [] - const callLabel = `.call_dotget_${++this.labelCount}` - const afterLabel = `.after_dotget_${++this.labelCount}` + const callLabel: Label = `.call_dotget_${++this.labelCount}` + const afterLabel: Label = `.after_dotget_${++this.labelCount}` instructions.push(...this.#compileNode(node.firstChild, input)) instructions.push(['DUP']) @@ -582,19 +584,24 @@ export class Compiler { instructions.push(...this.#compileNode(conditionNode, input)) this.ifLabelCount++ const endLabel: Label = `.end_${this.ifLabelCount}` + const elseLabel: Label = `.else_${this.ifLabelCount}` const thenBlockInstructions = this.#compileNode(thenBlock, input) - instructions.push(['JUMP_IF_FALSE', thenBlockInstructions.length + 1]) + instructions.push(['JUMP_IF_FALSE', elseLabel]) instructions.push(...thenBlockInstructions) instructions.push(['JUMP', endLabel]) + instructions.push([`${elseLabel}:`]) + // Else if - elseIfBlocks.forEach(({ conditional, thenBlock }) => { + elseIfBlocks.forEach(({ conditional, thenBlock }, index) => { instructions.push(...this.#compileNode(conditional, input)) + const nextLabel: Label = `.elsif_${this.ifLabelCount}_${index}` const elseIfInstructions = this.#compileNode(thenBlock, input) - instructions.push(['JUMP_IF_FALSE', elseIfInstructions.length + 1]) + instructions.push(['JUMP_IF_FALSE', nextLabel]) instructions.push(...elseIfInstructions) instructions.push(['JUMP', endLabel]) + instructions.push([`${nextLabel}:`]) }) // Else @@ -643,34 +650,41 @@ export class Compiler { instructions.push(...leftInstructions, ...rightInstructions, ['GTE']) break - case 'and': + case 'and': { + const skipLabel: Label = `.skip_${this.labelCount++}` instructions.push(...leftInstructions) instructions.push(['DUP']) - instructions.push(['JUMP_IF_FALSE', rightInstructions.length + 1]) + instructions.push(['JUMP_IF_FALSE', skipLabel]) instructions.push(['POP']) instructions.push(...rightInstructions) + instructions.push([`${skipLabel}:`]) break + } - case 'or': + case 'or': { + const skipLabel: Label = `.skip_${this.labelCount++}` instructions.push(...leftInstructions) instructions.push(['DUP']) - instructions.push(['JUMP_IF_TRUE', rightInstructions.length + 1]) + instructions.push(['JUMP_IF_TRUE', skipLabel]) instructions.push(['POP']) instructions.push(...rightInstructions) - + instructions.push([`${skipLabel}:`]) break + } - case '??': + case '??': { // Nullish coalescing: return left if not null, else right + const skipLabel: Label = `.skip_${this.labelCount++}` instructions.push(...leftInstructions) instructions.push(['DUP']) instructions.push(['PUSH', null]) instructions.push(['NEQ']) - instructions.push(['JUMP_IF_TRUE', rightInstructions.length + 1]) + instructions.push(['JUMP_IF_TRUE', skipLabel]) instructions.push(['POP']) instructions.push(...rightInstructions) - + instructions.push([`${skipLabel}:`]) break + } default: throw new CompilerError(`Unsupported conditional operator: ${opValue}`, op.from, op.to) diff --git a/src/compiler/tests/compiler.test.ts b/src/compiler/tests/compiler.test.ts index 45fdb39..daecec8 100644 --- a/src/compiler/tests/compiler.test.ts +++ b/src/compiler/tests/compiler.test.ts @@ -188,6 +188,16 @@ describe('compiler', () => { test('single line if', () => { expect(`if 3 < 9: shire end`).toEvaluateTo('shire') }) + + test('if statement with function definition (bytecode labels)', () => { + expect(` + if false: + abc = do x: x end + else: + nope + end + `).toEvaluateTo('nope') + }) }) describe('errors', () => {