Compare commits

..

4 Commits

2 changed files with 38 additions and 14 deletions

View File

@ -334,15 +334,17 @@ export class Compiler {
if (opValue === '??=') { if (opValue === '??=') {
instructions.push(['LOAD', identifierName]) instructions.push(['LOAD', identifierName])
const skipLabel: Label = `.skip_${this.labelCount++}`
const rightInstructions = this.#compileNode(right, input) const rightInstructions = this.#compileNode(right, input)
instructions.push(['DUP']) instructions.push(['DUP'])
instructions.push(['PUSH', null]) instructions.push(['PUSH', null])
instructions.push(['NEQ']) instructions.push(['NEQ'])
instructions.push(['JUMP_IF_TRUE', rightInstructions.length + 1]) instructions.push(['JUMP_IF_TRUE', skipLabel])
instructions.push(['POP']) instructions.push(['POP'])
instructions.push(...rightInstructions) instructions.push(...rightInstructions)
instructions.push([`${skipLabel}:`])
instructions.push(['DUP']) instructions.push(['DUP'])
instructions.push(['STORE', identifierName]) instructions.push(['STORE', identifierName])
@ -440,8 +442,8 @@ export class Compiler {
case terms.FunctionCallOrIdentifier: { case terms.FunctionCallOrIdentifier: {
if (node.firstChild?.type.id === terms.DotGet) { if (node.firstChild?.type.id === terms.DotGet) {
const instructions: ProgramItem[] = [] const instructions: ProgramItem[] = []
const callLabel = `.call_dotget_${++this.labelCount}` const callLabel: Label = `.call_dotget_${++this.labelCount}`
const afterLabel = `.after_dotget_${++this.labelCount}` const afterLabel: Label = `.after_dotget_${++this.labelCount}`
instructions.push(...this.#compileNode(node.firstChild, input)) instructions.push(...this.#compileNode(node.firstChild, input))
instructions.push(['DUP']) instructions.push(['DUP'])
@ -603,19 +605,24 @@ export class Compiler {
instructions.push(...this.#compileNode(conditionNode, input)) instructions.push(...this.#compileNode(conditionNode, input))
this.ifLabelCount++ this.ifLabelCount++
const endLabel: Label = `.end_${this.ifLabelCount}` const endLabel: Label = `.end_${this.ifLabelCount}`
const elseLabel: Label = `.else_${this.ifLabelCount}`
const thenBlockInstructions = this.#compileNode(thenBlock, input) 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(...thenBlockInstructions)
instructions.push(['JUMP', endLabel]) instructions.push(['JUMP', endLabel])
instructions.push([`${elseLabel}:`])
// Else if // Else if
elseIfBlocks.forEach(({ conditional, thenBlock }) => { elseIfBlocks.forEach(({ conditional, thenBlock }, index) => {
instructions.push(...this.#compileNode(conditional, input)) instructions.push(...this.#compileNode(conditional, input))
const nextLabel: Label = `.elsif_${this.ifLabelCount}_${index}`
const elseIfInstructions = this.#compileNode(thenBlock, input) 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(...elseIfInstructions)
instructions.push(['JUMP', endLabel]) instructions.push(['JUMP', endLabel])
instructions.push([`${nextLabel}:`])
}) })
// Else // Else
@ -664,34 +671,41 @@ export class Compiler {
instructions.push(...leftInstructions, ...rightInstructions, ['GTE']) instructions.push(...leftInstructions, ...rightInstructions, ['GTE'])
break break
case 'and': case 'and': {
const skipLabel: Label = `.skip_${this.labelCount++}`
instructions.push(...leftInstructions) instructions.push(...leftInstructions)
instructions.push(['DUP']) instructions.push(['DUP'])
instructions.push(['JUMP_IF_FALSE', rightInstructions.length + 1]) instructions.push(['JUMP_IF_FALSE', skipLabel])
instructions.push(['POP']) instructions.push(['POP'])
instructions.push(...rightInstructions) instructions.push(...rightInstructions)
instructions.push([`${skipLabel}:`])
break break
}
case 'or': case 'or': {
const skipLabel: Label = `.skip_${this.labelCount++}`
instructions.push(...leftInstructions) instructions.push(...leftInstructions)
instructions.push(['DUP']) instructions.push(['DUP'])
instructions.push(['JUMP_IF_TRUE', rightInstructions.length + 1]) instructions.push(['JUMP_IF_TRUE', skipLabel])
instructions.push(['POP']) instructions.push(['POP'])
instructions.push(...rightInstructions) instructions.push(...rightInstructions)
instructions.push([`${skipLabel}:`])
break break
}
case '??': case '??': {
// Nullish coalescing: return left if not null, else right // Nullish coalescing: return left if not null, else right
const skipLabel: Label = `.skip_${this.labelCount++}`
instructions.push(...leftInstructions) instructions.push(...leftInstructions)
instructions.push(['DUP']) instructions.push(['DUP'])
instructions.push(['PUSH', null]) instructions.push(['PUSH', null])
instructions.push(['NEQ']) instructions.push(['NEQ'])
instructions.push(['JUMP_IF_TRUE', rightInstructions.length + 1]) instructions.push(['JUMP_IF_TRUE', skipLabel])
instructions.push(['POP']) instructions.push(['POP'])
instructions.push(...rightInstructions) instructions.push(...rightInstructions)
instructions.push([`${skipLabel}:`])
break break
}
default: default:
throw new CompilerError(`Unsupported conditional operator: ${opValue}`, op.from, op.to) throw new CompilerError(`Unsupported conditional operator: ${opValue}`, op.from, op.to)

View File

@ -188,6 +188,16 @@ describe('compiler', () => {
test('single line if', () => { test('single line if', () => {
expect(`if 3 < 9: shire end`).toEvaluateTo('shire') 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', () => { describe('errors', () => {