From f31c0f4ef91d8e49ba561fef9b80e6220fc1a4b5 Mon Sep 17 00:00:00 2001 From: Josh Holbrook Date: Sat, 13 Dec 2025 08:48:27 -0900 Subject: [PATCH 01/11] Initial onward implementation (failing) --- README.md | 8 +++-- compiler/base.ts | 47 ++++++++++++++++++++++------- test/__snapshots__/cli.spec.ts.snap | 36 ++++++++++++++++++++++ test/cli.spec.ts | 39 +++++++++++++----------- test/compiler/looping.ts | 3 +- test/fixtures/001-for-onward.bas | 7 +++++ test/fixtures/002-while-onward.bas | 7 +++++ test/fixtures/003-repeat-onward.bas | 6 ++++ test/helpers/files.ts | 27 ++++++++++------- test/helpers/host.ts | 7 +++-- 10 files changed, 142 insertions(+), 45 deletions(-) create mode 100644 test/fixtures/001-for-onward.bas create mode 100644 test/fixtures/002-while-onward.bas create mode 100644 test/fixtures/003-repeat-onward.bas diff --git a/README.md b/README.md index 29e86f88..0e48241c 100644 --- a/README.md +++ b/README.md @@ -44,12 +44,13 @@ Matanuska can do the following: 2. Define variables 3. Check conditionals 4. Run loops -5. Edit files in a BASIC style REPL +5. Define functions +6. Edit files in a BASIC style REPL However, some important capabilities are still missing, including (but not limited to): 1. System commands and shell jobs -2. Functions +2. Define closures 3. Arrays and other higher level types 4. Compiler level type assertions 5. Multi-line instructions @@ -58,9 +59,10 @@ For more information, check out the backlog... ### Prioritized Backlog +- [x] Implement `onward` in loops +- [ ] Non-example tests - [ ] Date/Time/Duration/TZData functions - [ ] Closures -- [ ] Implement `onward` in loops - [ ] Refactor command input - `Cmd` in AST references a list of Instructions, not a "command" as in ADR 024 - Should probably just use a `Line` diff --git a/compiler/base.ts b/compiler/base.ts index 2b8ea1a9..9930a1be 100644 --- a/compiler/base.ts +++ b/compiler/base.ts @@ -219,9 +219,8 @@ class ForBlock extends Block { super(); } - // TODO - visitOnwardInstr(onward: Onward): void { - super.visitOnwardInstr(onward); + visitOnwardInstr(_onward: Onward): void { + this.compiler.continueFor(this.incrStart); } visitNextInstr(_next: Next): void { @@ -240,9 +239,8 @@ class WhileBlock extends Block { super(); } - // TODO - visitOnwardInstr(onward: Onward): void { - super.visitOnwardInstr(onward); + visitOnwardInstr(_onward: Onward): void { + this.compiler.continueWhile(this.loopStart); } visitEndWhileInstr(_endWhile: EndWhile): void { @@ -254,17 +252,19 @@ class WhileBlock extends Block { class RepeatBlock extends Block { kind = 'repeat'; + continues: Short[]; + constructor(public startJump: Short) { super(); + this.continues = []; } - // TODO - visitOnwardInstr(onward: Onward): void { - super.visitOnwardInstr(onward); + visitOnwardInstr(_onward: Onward): void { + this.compiler.continueRepeat(this.continues); } visitUntilInstr(until: Until): void { - this.compiler.endRepeat(until, this.startJump); + this.compiler.endRepeat(until, this.startJump, this.continues); this.end(); } } @@ -918,6 +918,10 @@ export class LineCompiler implements InstrVisitor, ExprVisitor { this.patchJump(exitJump); } + continueFor(incrStart: Short): void { + this.emitLoop(incrStart); + } + visitWhileInstr(while_: While): void { const [loopStart, exitJump] = this.beginWhile(while_.condition); this.block.begin(while_, new WhileBlock(loopStart, exitJump)); @@ -943,6 +947,10 @@ export class LineCompiler implements InstrVisitor, ExprVisitor { this.patchJump(exitJump); } + continueWhile(loopStart: Short): void { + this.emitLoop(loopStart); + } + visitRepeatInstr(repeat: Repeat): void { const loopStart = this.chunk.code.length; this.block.begin(repeat, new RepeatBlock(loopStart)); @@ -952,15 +960,32 @@ export class LineCompiler implements InstrVisitor, ExprVisitor { this.block.handle(until); } - endRepeat(until: Until, start: Short): void { + endRepeat(until: Until, start: Short, continues: Short[]): void { + // Continues jump to the bottom + for (let cont of continues) { + this.patchJump(cont); + } + + // Check the condition until.condition.accept(this); + // Exit if false const exitJump = this.emitJump(OpCode.JumpIfFalse); + // Finnegan begin again this.emitByte(OpCode.Pop); this.emitLoop(start); + // Exit here this.patchJump(exitJump); + // Pop the conditional check + this.emitByte(OpCode.Pop); + } + + continueRepeat(continues: Short[]): void { + // We'll jump to the conditional check later + const jump = this.emitJump(OpCode.Jump); + continues.push(jump); } visitOnwardInstr(onward: Onward): void { diff --git a/test/__snapshots__/cli.spec.ts.snap b/test/__snapshots__/cli.spec.ts.snap index 9f0407a8..5df10938 100644 --- a/test/__snapshots__/cli.spec.ts.snap +++ b/test/__snapshots__/cli.spec.ts.snap @@ -117,3 +117,39 @@ exports[`examples > 011-datetime.bas 1`] = ` ", } `; + +exports[`fixtures > 001-for-onward.bas 1`] = ` +{ + "exitCode": 0, + "stderr": "", + "stdout": "1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +", +} +`; + +exports[`fixtures > 002-while-onward.bas 1`] = ` +{ + "exitCode": 0, + "stderr": "", + "stdout": "loop +", +} +`; + +exports[`fixtures > 009-repeat-onward.bas 1`] = ` +{ + "exitCode": 0, + "stderr": "", + "stdout": "loop +", +} +`; diff --git a/test/cli.spec.ts b/test/cli.spec.ts index e7aa87c6..bf4dc416 100644 --- a/test/cli.spec.ts +++ b/test/cli.spec.ts @@ -3,22 +3,27 @@ import { basename } from 'node:path'; import { describe, expect, test } from 'vitest'; import { run } from './helpers/cli'; -import { EXAMPLES } from './helpers/files'; +import { EXAMPLES, FIXTURES } from './helpers/files'; -describe('examples', () => { - for (const p of Object.keys(EXAMPLES)) { - const name = basename(p); - switch (name) { - // TODO: Some scripts will need mocked input - default: - test(name, async () => { - const { exitCode, host } = await run([p], process.env); - expect({ - exitCode, - stdout: host.stdout.output, - stderr: host.stderr.output, - }).toMatchSnapshot(); - }); +function scriptsSuite(files: Record): () => void { + return () => { + for (const p of Object.keys(files)) { + const name = basename(p); + switch (name) { + // TODO: Some scripts will need mocked input + default: + test(name, async () => { + const { exitCode, host } = await run([p], process.env); + expect({ + exitCode, + stdout: host.stdout.output, + stderr: host.stderr.output, + }).toMatchSnapshot(); + }); + } } - } -}); + }; +} + +describe('examples', scriptsSuite(EXAMPLES)); +describe('fixtures', scriptsSuite(FIXTURES)); diff --git a/test/compiler/looping.ts b/test/compiler/looping.ts index 87eb2090..bc76ba28 100644 --- a/test/compiler/looping.ts +++ b/test/compiler/looping.ts @@ -174,10 +174,11 @@ export const REPEAT_PROGRAMS: TestCase[] = [ OpCode.Loop, ...shortToBytes(12), // Exit + OpCode.Pop, OpCode.Undef, OpCode.Return, ], - lines: [20, 20, 20, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30], + lines: [20, 20, 20, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30], }), ], ]; diff --git a/test/fixtures/001-for-onward.bas b/test/fixtures/001-for-onward.bas new file mode 100644 index 00000000..c1a2d2e8 --- /dev/null +++ b/test/fixtures/001-for-onward.bas @@ -0,0 +1,7 @@ +10 for i% = 1 to 10 +20 print i% +30 if i% > 5 then +35 print "biggers" +40 onward +50 endif +40 next diff --git a/test/fixtures/002-while-onward.bas b/test/fixtures/002-while-onward.bas new file mode 100644 index 00000000..e9e9a173 --- /dev/null +++ b/test/fixtures/002-while-onward.bas @@ -0,0 +1,7 @@ +10 let i% = 0 +20 +30 while i% < 10 +40 i% = i% + 1 +50 if i% > 5 then onward endif +60 print "loop" +70 endwhile diff --git a/test/fixtures/003-repeat-onward.bas b/test/fixtures/003-repeat-onward.bas new file mode 100644 index 00000000..8ea9ebfa --- /dev/null +++ b/test/fixtures/003-repeat-onward.bas @@ -0,0 +1,6 @@ +10 let i% = 0 +20 repeat +30 i% = i% + 1 +40 if i% > 5 then onward endif +50 print "loop" +60 until i% >= 10 diff --git a/test/helpers/files.ts b/test/helpers/files.ts index f2bfce6a..0cd88123 100644 --- a/test/helpers/files.ts +++ b/test/helpers/files.ts @@ -6,14 +6,19 @@ export const FILENAME = '/home/josh/script.bas'; type FullPath = string; type Contents = string; -export const EXAMPLES: Record = Object.fromEntries( - readdirSync(path.join(__dirname, '../../examples')) - .filter((entry) => entry.endsWith('.bas')) - .map((entry) => [ - path.relative( - path.resolve(path.join(__dirname, '../..')), - path.resolve(path.join(__dirname, '../../examples', entry)), - ), - readFileSync(path.join(__dirname, '../../examples', entry), 'utf8'), - ]), -); +function loadFiles(dir: string): Record { + return Object.fromEntries( + readdirSync(path.join(__dirname, dir)) + .filter((entry) => entry.endsWith('.bas')) + .map((entry) => [ + path.relative( + path.resolve(path.join(__dirname, path.dirname(dir))), + path.resolve(path.join(__dirname, dir, entry)), + ), + readFileSync(path.join(__dirname, dir, entry), 'utf8'), + ]), + ); +} + +export const EXAMPLES = loadFiles('../../examples'); +export const FIXTURES = loadFiles('../fixtures'); diff --git a/test/helpers/host.ts b/test/helpers/host.ts index 26df54df..dc356992 100644 --- a/test/helpers/host.ts +++ b/test/helpers/host.ts @@ -3,7 +3,7 @@ import { expect } from 'vitest'; import { host } from '../../host'; import { mockHost, MockConsoleHost } from '@matanuska/mock'; -import { EXAMPLES } from './files'; +import { EXAMPLES, FIXTURES } from './files'; export interface MockConsoleHostOptions { files?: Record; @@ -13,7 +13,10 @@ const FILES: Record = Object.assign( { '/home/josh/.matbas_history': '', }, - EXAMPLES, + { + ...EXAMPLES, + ...FIXTURES, + }, ); export function mockConsoleHost( From 2c833a73a50a038d7dbf8087e5ec43b4bc27bbd7 Mon Sep 17 00:00:00 2001 From: Josh Holbrook Date: Sat, 13 Dec 2025 08:53:17 -0900 Subject: [PATCH 02/11] Unwind refactor (still broken) --- compiler/base.ts | 6 +++++ compiler/block.ts | 12 ++++++++- test/__snapshots__/cli.spec.ts.snap | 38 +++++++++++++++++++++++++++++ 3 files changed, 55 insertions(+), 1 deletion(-) diff --git a/compiler/base.ts b/compiler/base.ts index 9930a1be..7e29f69e 100644 --- a/compiler/base.ts +++ b/compiler/base.ts @@ -167,7 +167,10 @@ class _ElseBlock extends Block { visitEndIfInstr(_endIf: EndIf): void { this.compiler.endIf(this.endJump); this.end(); + this.unwind(); + } + private unwind(): void { let block: any = this.previous; while (block instanceof ElseIfBlock) { block.compiler.endIf(block.endJump); @@ -190,7 +193,10 @@ class _ElseIfBlock extends IfBlock { visitEndIfInstr(_endIf: EndIf): void { const endJump = this.compiler.beginElse(this.elseJump); this.compiler.endIf(endJump); + this.unwind(); + } + private unwind(): void { // eslint-disable-next-line @typescript-eslint/no-this-alias let block: any = this; while (block instanceof ElseIfBlock) { diff --git a/compiler/block.ts b/compiler/block.ts index 9163d0b9..d632fa83 100644 --- a/compiler/block.ts +++ b/compiler/block.ts @@ -1,5 +1,9 @@ import { OpCode } from '../bytecode/opcodes'; -import { AssertionError, RuntimeError } from '../exceptions'; +import { + AssertionError, + NotImplementedError, + RuntimeError, +} from '../exceptions'; import { RuntimeFault } from '../faults'; import { formatter } from '../format'; import { @@ -103,6 +107,12 @@ export abstract class Block implements InstrVisitor { this.compiler.block = this.parent; } + /* + public unwind(): void { + throw new NotImplementedError(`unwind for ${this.constructor.name}`); + } + */ + public handle(instr: Instr): void { instr.accept(this); } diff --git a/test/__snapshots__/cli.spec.ts.snap b/test/__snapshots__/cli.spec.ts.snap index 5df10938..be298315 100644 --- a/test/__snapshots__/cli.spec.ts.snap +++ b/test/__snapshots__/cli.spec.ts.snap @@ -145,6 +145,44 @@ exports[`fixtures > 002-while-onward.bas 1`] = ` } `; +exports[`fixtures > 003-repeat-onward.bas 1`] = ` +{ + "exitCode": 70, + "stderr": "=== RUNTIME FAULT === + +--- Internal Stack Trace --- +RuntimeFault: Cannot cast nil to unknown + at castFault (/Users/josh/code/jfhbrook/matanuska/value/cast.ts:29:11) + at castNil (/Users/josh/code/jfhbrook/matanuska/value/cast.ts:260:10) + at cast (/Users/josh/code/jfhbrook/matanuska/value/cast.ts:368:14) + at Module.add (/Users/josh/code/jfhbrook/matanuska/operations.ts:76:24) + at Runtime. (/Users/josh/code/jfhbrook/matanuska/runtime.ts:335:34) + at Generator.next () + at asyncGeneratorStep (/Users/josh/code/jfhbrook/matanuska/runtime.ts:18:28) + at _next (/Users/josh/code/jfhbrook/matanuska/runtime.ts:36:17) + at /Users/josh/code/jfhbrook/matanuska/runtime.ts:41:13 + at new Promise () + +--- Versions --- +Matanuska BASIC: v0.0.1 +swc: v1.7.26 +vite: v1.7.26 +Node.js: v25.2.1 + +This is a bug in Matanuska BASIC. If you copy this entire message and post it +to the issues tracker: + + https://github.com/jfhbrook/matanuska/issues + +the developers will do their best to fix it - and more importantly, they will +owe you a beer, coffee or beverage of your choice. 🍻 + + +", + "stdout": "", +} +`; + exports[`fixtures > 009-repeat-onward.bas 1`] = ` { "exitCode": 0, From f6a366736746d7541ac1740a64d4628ad374703b Mon Sep 17 00:00:00 2001 From: Josh Holbrook Date: Sat, 13 Dec 2025 09:04:19 -0900 Subject: [PATCH 03/11] Stubs for onward in conditionals --- README.md | 8 +++- compiler/base.ts | 29 +++++++++++++- test/__snapshots__/cli.spec.ts.snap | 62 +++++------------------------ test/fixtures/003-repeat-onward.bas | 8 ++-- 4 files changed, 48 insertions(+), 59 deletions(-) diff --git a/README.md b/README.md index 0e48241c..e8b4a876 100644 --- a/README.md +++ b/README.md @@ -59,8 +59,12 @@ For more information, check out the backlog... ### Prioritized Backlog -- [x] Implement `onward` in loops -- [ ] Non-example tests +- [ ] Implement `onward` in loops + - [x] Initial implementation + - [x] Fixture CLI tests + - [ ] Fixture/snapshot compiler tests + - [ ] Implement visiting `onward` inside conditionals + - [ ] Get fixture tests working - [ ] Date/Time/Duration/TZData functions - [ ] Closures - [ ] Refactor command input diff --git a/compiler/base.ts b/compiler/base.ts index 7e29f69e..68e7c459 100644 --- a/compiler/base.ts +++ b/compiler/base.ts @@ -13,6 +13,7 @@ import { showChunk } from '../debug'; import { errorType } from '../errors'; import { AssertionError, + NotImplementedError, SyntaxError, ParseError, ParseWarning, @@ -149,6 +150,10 @@ class IfBlock extends Block { this.next(elseIf, new ElseIfBlock(elseJump, endJump)); } + visitOnwardInstr(_onward: Onward): void { + throw new NotImplementedError('onward'); + } + visitEndIfInstr(_endIf: EndIf): void { // TODO: Optimize for no 'else' const endJump = this.compiler.beginElse(this.elseJump); @@ -164,6 +169,10 @@ class _ElseBlock extends Block { super(); } + visitOnwardInstr(_onward: Onward): void { + throw new NotImplementedError('onward'); + } + visitEndIfInstr(_endIf: EndIf): void { this.compiler.endIf(this.endJump); this.end(); @@ -190,6 +199,10 @@ class _ElseIfBlock extends IfBlock { super(elseJump); } + visitOnwardInstr(_onward: Onward): void { + throw new NotImplementedError('onward'); + } + visitEndIfInstr(_endIf: EndIf): void { const endJump = this.compiler.beginElse(this.elseJump); this.compiler.endIf(endJump); @@ -275,6 +288,20 @@ class RepeatBlock extends Block { } } +/* +function isConditionalBlock(block: Block) { + return [IfBlock, ElseBlock, ElseIfBlock].some( + (cls) => block instanceof cls, + ); +} + +function isLoopBlock(block: Block) { + return [ForBlock, WhileBlock, RepeatBlock].some( + (cls) => block instanceof cls, + ); +} +*/ + // // Functions // @@ -968,7 +995,7 @@ export class LineCompiler implements InstrVisitor, ExprVisitor { endRepeat(until: Until, start: Short, continues: Short[]): void { // Continues jump to the bottom - for (let cont of continues) { + for (const cont of continues) { this.patchJump(cont); } diff --git a/test/__snapshots__/cli.spec.ts.snap b/test/__snapshots__/cli.spec.ts.snap index be298315..007d8ca4 100644 --- a/test/__snapshots__/cli.spec.ts.snap +++ b/test/__snapshots__/cli.spec.ts.snap @@ -121,73 +121,29 @@ exports[`examples > 011-datetime.bas 1`] = ` exports[`fixtures > 001-for-onward.bas 1`] = ` { "exitCode": 0, - "stderr": "", - "stdout": "1 -2 -3 -4 -5 -6 -7 -8 -9 -10 + "stderr": "NotImplementedError: onward ", + "stdout": "", } `; exports[`fixtures > 002-while-onward.bas 1`] = ` -{ - "exitCode": 0, - "stderr": "", - "stdout": "loop -", -} -`; - -exports[`fixtures > 003-repeat-onward.bas 1`] = ` { "exitCode": 70, - "stderr": "=== RUNTIME FAULT === - ---- Internal Stack Trace --- -RuntimeFault: Cannot cast nil to unknown - at castFault (/Users/josh/code/jfhbrook/matanuska/value/cast.ts:29:11) - at castNil (/Users/josh/code/jfhbrook/matanuska/value/cast.ts:260:10) - at cast (/Users/josh/code/jfhbrook/matanuska/value/cast.ts:368:14) - at Module.add (/Users/josh/code/jfhbrook/matanuska/operations.ts:76:24) - at Runtime. (/Users/josh/code/jfhbrook/matanuska/runtime.ts:335:34) - at Generator.next () - at asyncGeneratorStep (/Users/josh/code/jfhbrook/matanuska/runtime.ts:18:28) - at _next (/Users/josh/code/jfhbrook/matanuska/runtime.ts:36:17) - at /Users/josh/code/jfhbrook/matanuska/runtime.ts:41:13 - at new Promise () - ---- Versions --- -Matanuska BASIC: v0.0.1 -swc: v1.7.26 -vite: v1.7.26 -Node.js: v25.2.1 - -This is a bug in Matanuska BASIC. If you copy this entire message and post it -to the issues tracker: - - https://github.com/jfhbrook/matanuska/issues - -the developers will do their best to fix it - and more importantly, they will -owe you a beer, coffee or beverage of your choice. 🍻 - - + "stderr": "TypeError: Invalid operand for < on undef: integer + Value: 10 + From: integer + To: invalid ", "stdout": "", } `; -exports[`fixtures > 009-repeat-onward.bas 1`] = ` +exports[`fixtures > 003-repeat-onward.bas 1`] = ` { "exitCode": 0, - "stderr": "", - "stdout": "loop + "stderr": "NotImplementedError: onward ", + "stdout": "", } `; diff --git a/test/fixtures/003-repeat-onward.bas b/test/fixtures/003-repeat-onward.bas index 8ea9ebfa..bf6dd27c 100644 --- a/test/fixtures/003-repeat-onward.bas +++ b/test/fixtures/003-repeat-onward.bas @@ -1,6 +1,8 @@ 10 let i% = 0 20 repeat 30 i% = i% + 1 -40 if i% > 5 then onward endif -50 print "loop" -60 until i% >= 10 +40 if i% > 5 then +50 onward +60 endif +70 print "loop" +80 until i% >= 10 From c4d9d9e013dac02b3985235a1abcb0b182946508 Mon Sep 17 00:00:00 2001 From: Josh Holbrook Date: Sat, 13 Dec 2025 09:18:00 -0900 Subject: [PATCH 04/11] Failing compiler tests --- .../compiler/__snapshots__/index.spec.ts.snap | 100 ++++++++++++++++++ test/compiler/fixtures.ts | 9 ++ test/compiler/index.spec.ts | 3 + test/fixtures/001-for-onward.bas | 8 +- test/helpers/compiler.ts | 41 ++++--- 5 files changed, 142 insertions(+), 19 deletions(-) create mode 100644 test/compiler/__snapshots__/index.spec.ts.snap create mode 100644 test/compiler/fixtures.ts diff --git a/test/compiler/__snapshots__/index.spec.ts.snap b/test/compiler/__snapshots__/index.spec.ts.snap new file mode 100644 index 00000000..f58a295a --- /dev/null +++ b/test/compiler/__snapshots__/index.spec.ts.snap @@ -0,0 +1,100 @@ +// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html + +exports[`fixtures > 10 let i% = 0 +20 +30 while i% < 10 +40 i% = i% + 1 +50 if i% > 5 then onward endif +60 print "loop" +70 endwhile + 1`] = ` +{ + "chunk": { + "code": "=== Disassembly of test.bas:
: === +10 1 CONSTANT 0 +10 3 DEFINE_GLOBAL i% +30 5 GET_GLOBAL i% +30 7 CONSTANT 10 +30 9 LT +30 10 JUMP_IF_FALSE 10 -> 43 +30 13 POP +40 14 GET_GLOBAL i% +40 16 CONSTANT 1 +40 18 ADD +40 19 SET_GLOBAL i% +50 21 GET_GLOBAL i% +50 23 CONSTANT 5 +50 25 GT +50 26 JUMP_IF_FALSE 26 -> 36 +50 29 POP +50 30 LOOP 30 -> 5 +50 33 JUMP 33 -> 37 +50 36 POP +60 37 CONSTANT loop +60 39 PRINT +70 40 LOOP 40 -> 5 +70 43 UNDEF +70 44 RETURN", + "constants": [ + "i%", + 0, + "i%", + 10, + "i%", + 1, + "i%", + "i%", + 5, + "loop", + ], + "lines": [ + 10, + 10, + 10, + 10, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 40, + 40, + 40, + 40, + 40, + 40, + 40, + 50, + 50, + 50, + 50, + 50, + 50, + 50, + 50, + 50, + 50, + 50, + 50, + 50, + 50, + 50, + 50, + 60, + 60, + 60, + 70, + 70, + 70, + 70, + 70, + ], + }, + "filename": "test.bas", + "name": null, +} +`; diff --git a/test/compiler/fixtures.ts b/test/compiler/fixtures.ts new file mode 100644 index 00000000..63c6f0b2 --- /dev/null +++ b/test/compiler/fixtures.ts @@ -0,0 +1,9 @@ +import type { TestCase } from '../helpers/compiler'; + +import { FIXTURES } from '../helpers/files'; + +export const FIXTURE_PROGRAMS: TestCase[] = Object.values(FIXTURES).map( + (script) => { + return [script, null, null]; + }, +); diff --git a/test/compiler/index.spec.ts b/test/compiler/index.spec.ts index 9cf215c8..65194ab3 100644 --- a/test/compiler/index.spec.ts +++ b/test/compiler/index.spec.ts @@ -5,6 +5,7 @@ import { SIMPLE_INSTRUCTIONS, EXPRESSION_INSTRUCTIONS } from './instr'; import { SIMPLE_PROGRAMS } from './programs'; import { VARIABLE_INSTRUCTIONS } from './variables'; import { FOR_PROGRAMS, WHILE_PROGRAMS, REPEAT_PROGRAMS } from './looping'; +import { FIXTURE_PROGRAMS } from './fixtures'; runCompilerSuite('expressions', EXPRESSION_INSTRUCTIONS); @@ -17,3 +18,5 @@ runCompilerSuite('conditional programs', CONDITIONAL_PROGRAMS); runCompilerSuite('for loop programs', FOR_PROGRAMS); runCompilerSuite('while loop programs', WHILE_PROGRAMS); runCompilerSuite('repeat loop programs', REPEAT_PROGRAMS); + +runCompilerSuite('fixtures', FIXTURE_PROGRAMS); diff --git a/test/fixtures/001-for-onward.bas b/test/fixtures/001-for-onward.bas index c1a2d2e8..807fa35f 100644 --- a/test/fixtures/001-for-onward.bas +++ b/test/fixtures/001-for-onward.bas @@ -1,7 +1,7 @@ 10 for i% = 1 to 10 20 print i% 30 if i% > 5 then -35 print "biggers" -40 onward -50 endif -40 next +40 print "biggers" +50 onward +60 endif +70 next diff --git a/test/helpers/compiler.ts b/test/helpers/compiler.ts index 453b8c02..16d8eebf 100644 --- a/test/helpers/compiler.ts +++ b/test/helpers/compiler.ts @@ -11,6 +11,8 @@ import { } from '../../compiler'; import { Routine } from '../../value'; +import { parseProgram } from './parser'; + export function compile( ast: Program | Instr, options: CompilerOptions = {}, @@ -22,27 +24,36 @@ export function compile( } } -export type TestCase = [string, Instr | Program, Routine]; +export type TestCase = [string, Instr | Program | null, Routine | null]; + +function parse(source: string, filename: string): Program { + const result = parseProgram(source, filename); + expect(result[1]).toBe(null); + return result[0]; +} export function runCompilerTest([source, ast, expected]: TestCase): void { test(source, () => { - const actual = compile(ast, { filename: expected.filename })[0]; - - /* - // NOTE: Test chunks typically do not have an initialized filename or routine - expected.filename = actual.filename; - expected.routine = actual.routine; - */ + const filename = expected ? expected.filename : 'test.bas'; + const _ast = ast ? ast : parse(source, filename); + const compiled = compile(_ast, { filename })[0]; - expect({ - filename: actual.filename, - name: actual.name, + const actual = { + filename: compiled.filename, + name: compiled.name, chunk: { - constants: actual.chunk.constants, - code: disassemble(actual.chunk), - lines: actual.chunk.lines, + constants: compiled.chunk.constants, + code: disassemble(compiled.chunk), + lines: compiled.chunk.lines, }, - }).toEqual({ + }; + + if (!expected) { + expect(actual).toMatchSnapshot(); + return; + } + + expect(actual).toEqual({ filename: expected.filename, name: expected.name, chunk: { From c6ce820f136934722216939e036f7547ca663090 Mon Sep 17 00:00:00 2001 From: Josh Holbrook Date: Sun, 21 Dec 2025 12:16:00 -0900 Subject: [PATCH 05/11] Onward in conditional half works --- compiler/base.ts | 62 +++--- compiler/block.ts | 15 +- test/__snapshots__/cli.spec.ts.snap | 18 +- .../compiler/__snapshots__/index.spec.ts.snap | 210 ++++++++++++++++++ test/compiler/instr.ts | 2 +- test/compiler/programs.ts | 4 +- 6 files changed, 267 insertions(+), 44 deletions(-) diff --git a/compiler/base.ts b/compiler/base.ts index 68e7c459..a9fc6773 100644 --- a/compiler/base.ts +++ b/compiler/base.ts @@ -131,8 +131,23 @@ export function isRootBlock(block: Block): boolean { // relevant blocks are defined. let ElseBlock: any = null; let ElseIfBlock: any = null; +let ForBlock: any = null; +let WhileBlock: any = null; +let RepeatBlock: any = null; -class IfBlock extends Block { +class ConditionalBlock extends Block { + visitOnwardInstr(onward: Onward): void { + const block = this.find(ForBlock, WhileBlock, RepeatBlock); + if (!block) { + this.mismatched(onward, 'onward'); + return; + } + + block.visitOnwardInstr(onward); + } +} + +class IfBlock extends ConditionalBlock { kind = 'if'; constructor(public elseJump: Short) { @@ -150,10 +165,6 @@ class IfBlock extends Block { this.next(elseIf, new ElseIfBlock(elseJump, endJump)); } - visitOnwardInstr(_onward: Onward): void { - throw new NotImplementedError('onward'); - } - visitEndIfInstr(_endIf: EndIf): void { // TODO: Optimize for no 'else' const endJump = this.compiler.beginElse(this.elseJump); @@ -162,17 +173,13 @@ class IfBlock extends Block { } } -class _ElseBlock extends Block { +class _ElseBlock extends ConditionalBlock { kind = 'else'; constructor(public endJump: Short) { super(); } - visitOnwardInstr(_onward: Onward): void { - throw new NotImplementedError('onward'); - } - visitEndIfInstr(_endIf: EndIf): void { this.compiler.endIf(this.endJump); this.end(); @@ -199,10 +206,6 @@ class _ElseIfBlock extends IfBlock { super(elseJump); } - visitOnwardInstr(_onward: Onward): void { - throw new NotImplementedError('onward'); - } - visitEndIfInstr(_endIf: EndIf): void { const endJump = this.compiler.beginElse(this.elseJump); this.compiler.endIf(endJump); @@ -220,15 +223,11 @@ class _ElseIfBlock extends IfBlock { } } -// Update the references here -ElseBlock = _ElseBlock; -ElseIfBlock = _ElseIfBlock; - // -// For, while, and repeat/until +// Looping blocks // -class ForBlock extends Block { +class _ForBlock extends Block { kind = 'for'; constructor( @@ -248,7 +247,7 @@ class ForBlock extends Block { } } -class WhileBlock extends Block { +class _WhileBlock extends Block { kind = 'while'; constructor( @@ -268,7 +267,7 @@ class WhileBlock extends Block { } } -class RepeatBlock extends Block { +class _RepeatBlock extends Block { kind = 'repeat'; continues: Short[]; @@ -288,19 +287,12 @@ class RepeatBlock extends Block { } } -/* -function isConditionalBlock(block: Block) { - return [IfBlock, ElseBlock, ElseIfBlock].some( - (cls) => block instanceof cls, - ); -} - -function isLoopBlock(block: Block) { - return [ForBlock, WhileBlock, RepeatBlock].some( - (cls) => block instanceof cls, - ); -} -*/ +// Update the references here +ElseBlock = _ElseBlock; +ElseIfBlock = _ElseIfBlock; +ForBlock = _ForBlock; +WhileBlock = _WhileBlock; +RepeatBlock = _RepeatBlock; // // Functions diff --git a/compiler/block.ts b/compiler/block.ts index d632fa83..9b01a03f 100644 --- a/compiler/block.ts +++ b/compiler/block.ts @@ -107,11 +107,18 @@ export abstract class Block implements InstrVisitor { this.compiler.block = this.parent; } - /* - public unwind(): void { - throw new NotImplementedError(`unwind for ${this.constructor.name}`); + public find(...types: any[]): Block | null { + let block: Block | null = this; + while (true) { + if (block === null) { + return block; + } + if (types.some((t) => block instanceof t)) { + return block; + } + block = block.parent || null; + } } - */ public handle(instr: Instr): void { instr.accept(this); diff --git a/test/__snapshots__/cli.spec.ts.snap b/test/__snapshots__/cli.spec.ts.snap index 007d8ca4..66baf2e4 100644 --- a/test/__snapshots__/cli.spec.ts.snap +++ b/test/__snapshots__/cli.spec.ts.snap @@ -121,9 +121,23 @@ exports[`examples > 011-datetime.bas 1`] = ` exports[`fixtures > 001-for-onward.bas 1`] = ` { "exitCode": 0, - "stderr": "NotImplementedError: onward + "stderr": "", + "stdout": "1 +2 +3 +4 +5 +6 +biggers +7 +biggers +8 +biggers +9 +biggers +10 +biggers ", - "stdout": "", } `; diff --git a/test/compiler/__snapshots__/index.spec.ts.snap b/test/compiler/__snapshots__/index.spec.ts.snap index f58a295a..a6b811e5 100644 --- a/test/compiler/__snapshots__/index.spec.ts.snap +++ b/test/compiler/__snapshots__/index.spec.ts.snap @@ -1,5 +1,113 @@ // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html +exports[`fixtures > 10 for i% = 1 to 10 +20 print i% +30 if i% > 5 then +40 print "biggers" +50 onward +60 endif +70 next + 1`] = ` +{ + "chunk": { + "code": "=== Disassembly of test.bas:
: === +10 1 CONSTANT 1 +10 3 GET_LOCAL 1 +10 5 CONSTANT 10 +10 7 LE +10 8 JUMP_IF_FALSE 8 -> 52 +10 11 POP +10 12 JUMP 12 -> 26 +10 15 GET_LOCAL 1 +10 17 CONSTANT 1 +10 19 ADD +10 20 SET_LOCAL 1 +10 22 POP +10 23 LOOP 23 -> 3 +20 26 GET_LOCAL 1 +20 28 PRINT +30 29 GET_LOCAL 1 +30 31 CONSTANT 5 +30 33 GT +30 34 JUMP_IF_FALSE 34 -> 47 +30 37 POP +40 38 CONSTANT biggers +40 40 PRINT +50 41 LOOP 41 -> 15 +60 44 JUMP 44 -> 48 +60 47 POP +70 48 LOOP 48 -> 15 +70 51 POP +70 52 UNDEF +70 53 RETURN", + "constants": [ + 1, + 10, + 1, + 5, + "biggers", + ], + "lines": [ + 10, + 10, + 10, + 10, + 10, + 10, + 10, + 10, + 10, + 10, + 10, + 10, + 10, + 10, + 10, + 10, + 10, + 10, + 10, + 10, + 10, + 10, + 10, + 10, + 10, + 20, + 20, + 20, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 40, + 40, + 40, + 50, + 50, + 50, + 60, + 60, + 60, + 60, + 70, + 70, + 70, + 70, + 70, + 70, + ], + }, + "filename": "test.bas", + "name": null, +} +`; + exports[`fixtures > 10 let i% = 0 20 30 while i% < 10 @@ -98,3 +206,105 @@ exports[`fixtures > 10 let i% = 0 "name": null, } `; + +exports[`fixtures > 10 let i% = 0 +20 repeat +30 i% = i% + 1 +40 if i% > 5 then +50 onward +60 endif +70 print "loop" +80 until i% >= 10 + 1`] = ` +{ + "chunk": { + "code": "=== Disassembly of test.bas:
: === +10 1 CONSTANT 0 +10 3 DEFINE_GLOBAL i% +30 5 GET_GLOBAL i% +30 7 CONSTANT 1 +30 9 ADD +30 10 SET_GLOBAL i% +40 12 GET_GLOBAL i% +40 14 CONSTANT 5 +40 16 GT +40 17 JUMP_IF_FALSE 17 -> 27 +40 20 POP +50 21 JUMP 21 -> 31 +60 24 JUMP 24 -> 28 +60 27 POP +70 28 CONSTANT loop +70 30 PRINT +80 31 GET_GLOBAL i% +80 33 CONSTANT 10 +80 35 GE +80 36 JUMP_IF_FALSE 36 -> 43 +80 39 POP +80 40 LOOP 40 -> 5 +80 43 POP +80 44 UNDEF +80 45 RETURN", + "constants": [ + "i%", + 0, + "i%", + 1, + "i%", + "i%", + 5, + "loop", + "i%", + 10, + ], + "lines": [ + 10, + 10, + 10, + 10, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 40, + 40, + 40, + 40, + 40, + 40, + 40, + 40, + 40, + 50, + 50, + 50, + 60, + 60, + 60, + 60, + 70, + 70, + 70, + 80, + 80, + 80, + 80, + 80, + 80, + 80, + 80, + 80, + 80, + 80, + 80, + 80, + 80, + 80, + ], + }, + "filename": "test.bas", + "name": null, +} +`; diff --git a/test/compiler/instr.ts b/test/compiler/instr.ts index fd87aa1e..f740a754 100644 --- a/test/compiler/instr.ts +++ b/test/compiler/instr.ts @@ -114,7 +114,7 @@ function instructionExpr1Cases( // Expressions are handled differently in Programs versus other commands, // so we leave them off when building out programs from the other commands, // and append them to COMMANDS afterwards. -export const EXPRESSION_INSTRUCTIONS = EXPRESSION_STATEMENTS.map( +export const EXPRESSION_INSTRUCTIONS = (EXPRESSION_STATEMENTS as any).map( ([ source, ast, diff --git a/test/compiler/programs.ts b/test/compiler/programs.ts index ab29a762..015d4c28 100644 --- a/test/compiler/programs.ts +++ b/test/compiler/programs.ts @@ -13,7 +13,7 @@ import { EXPRESSION_STATEMENTS } from './expr'; import { SIMPLE_INSTRUCTIONS } from './instr'; export const SIMPLE_PROGRAMS: TestCase[] = [ - ...EXPRESSION_STATEMENTS.map( + ...(EXPRESSION_STATEMENTS as any).map( ([ source, ast, @@ -36,7 +36,7 @@ export const SIMPLE_PROGRAMS: TestCase[] = [ ]; }, ), - ...SIMPLE_INSTRUCTIONS.map( + ...(SIMPLE_INSTRUCTIONS as any).map( ([ source, ast, From 9efd718086c22ddf70c68aedd3c308f517d71813 Mon Sep 17 00:00:00 2001 From: Josh Holbrook Date: Sun, 21 Dec 2025 12:25:07 -0900 Subject: [PATCH 06/11] for/onward fixture has expected output --- test/__snapshots__/cli.spec.ts.snap | 38 ++++++++++++--- .../compiler/__snapshots__/index.spec.ts.snap | 46 +++++++++---------- test/fixtures/001-for-onward.bas | 10 ++-- 3 files changed, 59 insertions(+), 35 deletions(-) diff --git a/test/__snapshots__/cli.spec.ts.snap b/test/__snapshots__/cli.spec.ts.snap index 66baf2e4..3bdd5bcd 100644 --- a/test/__snapshots__/cli.spec.ts.snap +++ b/test/__snapshots__/cli.spec.ts.snap @@ -127,15 +127,10 @@ exports[`fixtures > 001-for-onward.bas 1`] = ` 3 4 5 -6 biggers -7 biggers -8 biggers -9 biggers -10 biggers ", } @@ -155,8 +150,37 @@ exports[`fixtures > 002-while-onward.bas 1`] = ` exports[`fixtures > 003-repeat-onward.bas 1`] = ` { - "exitCode": 0, - "stderr": "NotImplementedError: onward + "exitCode": 70, + "stderr": "=== RUNTIME FAULT === + +--- Internal Stack Trace --- +RuntimeFault: Cannot cast nil to unknown + at castFault (/Users/josh/code/jfhbrook/matanuska/value/cast.ts:29:11) + at castNil (/Users/josh/code/jfhbrook/matanuska/value/cast.ts:260:10) + at cast (/Users/josh/code/jfhbrook/matanuska/value/cast.ts:368:14) + at Module.add (/Users/josh/code/jfhbrook/matanuska/operations.ts:76:24) + at Runtime. (/Users/josh/code/jfhbrook/matanuska/runtime.ts:335:34) + at Generator.next () + at asyncGeneratorStep (/Users/josh/code/jfhbrook/matanuska/runtime.ts:18:28) + at _next (/Users/josh/code/jfhbrook/matanuska/runtime.ts:36:17) + at /Users/josh/code/jfhbrook/matanuska/runtime.ts:41:13 + at new Promise () + +--- Versions --- +Matanuska BASIC: v0.0.1 +swc: v1.7.26 +vite: v1.7.26 +Node.js: v25.2.1 + +This is a bug in Matanuska BASIC. If you copy this entire message and post it +to the issues tracker: + + https://github.com/jfhbrook/matanuska/issues + +the developers will do their best to fix it - and more importantly, they will +owe you a beer, coffee or beverage of your choice. 🍻 + + ", "stdout": "", } diff --git a/test/compiler/__snapshots__/index.spec.ts.snap b/test/compiler/__snapshots__/index.spec.ts.snap index a6b811e5..fcbdabde 100644 --- a/test/compiler/__snapshots__/index.spec.ts.snap +++ b/test/compiler/__snapshots__/index.spec.ts.snap @@ -1,11 +1,11 @@ // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html exports[`fixtures > 10 for i% = 1 to 10 -20 print i% -30 if i% > 5 then -40 print "biggers" -50 onward -60 endif +20 if i% > 5 then +30 print "biggers" +40 onward +50 endif +60 print i% 70 next 1`] = ` { @@ -25,17 +25,17 @@ exports[`fixtures > 10 for i% = 1 to 10 10 22 POP 10 23 LOOP 23 -> 3 20 26 GET_LOCAL 1 -20 28 PRINT -30 29 GET_LOCAL 1 -30 31 CONSTANT 5 -30 33 GT -30 34 JUMP_IF_FALSE 34 -> 47 -30 37 POP -40 38 CONSTANT biggers -40 40 PRINT -50 41 LOOP 41 -> 15 -60 44 JUMP 44 -> 48 -60 47 POP +20 28 CONSTANT 5 +20 30 GT +20 31 JUMP_IF_FALSE 31 -> 44 +20 34 POP +30 35 CONSTANT biggers +30 37 PRINT +40 38 LOOP 38 -> 15 +50 41 JUMP 41 -> 45 +50 44 POP +60 45 GET_LOCAL 1 +60 47 PRINT 70 48 LOOP 48 -> 15 70 51 POP 70 52 UNDEF @@ -76,12 +76,12 @@ exports[`fixtures > 10 for i% = 1 to 10 20, 20, 20, - 30, - 30, - 30, - 30, - 30, - 30, + 20, + 20, + 20, + 20, + 20, + 20, 30, 30, 30, @@ -91,7 +91,7 @@ exports[`fixtures > 10 for i% = 1 to 10 50, 50, 50, - 60, + 50, 60, 60, 60, diff --git a/test/fixtures/001-for-onward.bas b/test/fixtures/001-for-onward.bas index 807fa35f..f50eac6f 100644 --- a/test/fixtures/001-for-onward.bas +++ b/test/fixtures/001-for-onward.bas @@ -1,7 +1,7 @@ 10 for i% = 1 to 10 -20 print i% -30 if i% > 5 then -40 print "biggers" -50 onward -60 endif +20 if i% > 5 then +30 print "biggers" +40 onward +50 endif +60 print i% 70 next From dfbfa09ccc3050e1c00f4738ae21127123d04244 Mon Sep 17 00:00:00 2001 From: Josh Holbrook Date: Sun, 21 Dec 2025 12:30:32 -0900 Subject: [PATCH 07/11] Update TODOs --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index e8b4a876..98eb53d4 100644 --- a/README.md +++ b/README.md @@ -62,8 +62,8 @@ For more information, check out the backlog... - [ ] Implement `onward` in loops - [x] Initial implementation - [x] Fixture CLI tests - - [ ] Fixture/snapshot compiler tests - - [ ] Implement visiting `onward` inside conditionals + - [x] Fixture/snapshot compiler tests + - [x] Implement visiting `onward` inside conditionals - [ ] Get fixture tests working - [ ] Date/Time/Duration/TZData functions - [ ] Closures From 9604de2d0abffbf1eb94018722a4d58cf4d00a47 Mon Sep 17 00:00:00 2001 From: Josh Holbrook Date: Sun, 21 Dec 2025 12:36:44 -0900 Subject: [PATCH 08/11] fix error --- compiler/base.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/compiler/base.ts b/compiler/base.ts index a9fc6773..cceb16d3 100644 --- a/compiler/base.ts +++ b/compiler/base.ts @@ -13,7 +13,6 @@ import { showChunk } from '../debug'; import { errorType } from '../errors'; import { AssertionError, - NotImplementedError, SyntaxError, ParseError, ParseWarning, @@ -139,7 +138,10 @@ class ConditionalBlock extends Block { visitOnwardInstr(onward: Onward): void { const block = this.find(ForBlock, WhileBlock, RepeatBlock); if (!block) { - this.mismatched(onward, 'onward'); + this.compiler.syntaxFault( + onward, + 'onward must be used inside a for, while, or repeat block', + ); return; } From bd75ac578707cce19ec2c6a103c1cd123d1cef97 Mon Sep 17 00:00:00 2001 From: Josh Holbrook Date: Sun, 21 Dec 2025 14:48:40 -0900 Subject: [PATCH 09/11] Remove ordering of fixtures --- test/__snapshots__/cli.spec.ts.snap | 28 +++++++++---------- .../{001-for-onward.bas => for-onward.bas} | 0 ...03-repeat-onward.bas => repeat-onward.bas} | 0 ...{002-while-onward.bas => while-onward.bas} | 0 4 files changed, 14 insertions(+), 14 deletions(-) rename test/fixtures/{001-for-onward.bas => for-onward.bas} (100%) rename test/fixtures/{003-repeat-onward.bas => repeat-onward.bas} (100%) rename test/fixtures/{002-while-onward.bas => while-onward.bas} (100%) diff --git a/test/__snapshots__/cli.spec.ts.snap b/test/__snapshots__/cli.spec.ts.snap index 3bdd5bcd..2284d749 100644 --- a/test/__snapshots__/cli.spec.ts.snap +++ b/test/__snapshots__/cli.spec.ts.snap @@ -118,7 +118,7 @@ exports[`examples > 011-datetime.bas 1`] = ` } `; -exports[`fixtures > 001-for-onward.bas 1`] = ` +exports[`fixtures > for-onward.bas 1`] = ` { "exitCode": 0, "stderr": "", @@ -136,19 +136,7 @@ biggers } `; -exports[`fixtures > 002-while-onward.bas 1`] = ` -{ - "exitCode": 70, - "stderr": "TypeError: Invalid operand for < on undef: integer - Value: 10 - From: integer - To: invalid -", - "stdout": "", -} -`; - -exports[`fixtures > 003-repeat-onward.bas 1`] = ` +exports[`fixtures > repeat-onward.bas 1`] = ` { "exitCode": 70, "stderr": "=== RUNTIME FAULT === @@ -185,3 +173,15 @@ owe you a beer, coffee or beverage of your choice. 🍻 "stdout": "", } `; + +exports[`fixtures > while-onward.bas 1`] = ` +{ + "exitCode": 70, + "stderr": "TypeError: Invalid operand for < on undef: integer + Value: 10 + From: integer + To: invalid +", + "stdout": "", +} +`; diff --git a/test/fixtures/001-for-onward.bas b/test/fixtures/for-onward.bas similarity index 100% rename from test/fixtures/001-for-onward.bas rename to test/fixtures/for-onward.bas diff --git a/test/fixtures/003-repeat-onward.bas b/test/fixtures/repeat-onward.bas similarity index 100% rename from test/fixtures/003-repeat-onward.bas rename to test/fixtures/repeat-onward.bas diff --git a/test/fixtures/002-while-onward.bas b/test/fixtures/while-onward.bas similarity index 100% rename from test/fixtures/002-while-onward.bas rename to test/fixtures/while-onward.bas From 54d788d37bd9ade981c2911aba8bf2df67bbb349 Mon Sep 17 00:00:00 2001 From: Josh Holbrook Date: Sun, 21 Dec 2025 14:50:27 -0900 Subject: [PATCH 10/11] Variables are wonky --- test/__snapshots__/cli.spec.ts.snap | 38 ++++++++++++++ .../compiler/__snapshots__/index.spec.ts.snap | 50 +++++++++++++++++++ test/fixtures/variable-increment.bas | 4 ++ 3 files changed, 92 insertions(+) create mode 100644 test/fixtures/variable-increment.bas diff --git a/test/__snapshots__/cli.spec.ts.snap b/test/__snapshots__/cli.spec.ts.snap index 2284d749..7e0bafdc 100644 --- a/test/__snapshots__/cli.spec.ts.snap +++ b/test/__snapshots__/cli.spec.ts.snap @@ -169,6 +169,44 @@ the developers will do their best to fix it - and more importantly, they will owe you a beer, coffee or beverage of your choice. 🍻 +", + "stdout": "", +} +`; + +exports[`fixtures > variable-increment.bas 1`] = ` +{ + "exitCode": 70, + "stderr": "=== RUNTIME FAULT === + +--- Internal Stack Trace --- +RuntimeFault: Cannot cast nil to unknown + at castFault (/Users/josh/code/jfhbrook/matanuska/value/cast.ts:29:11) + at castNil (/Users/josh/code/jfhbrook/matanuska/value/cast.ts:260:10) + at cast (/Users/josh/code/jfhbrook/matanuska/value/cast.ts:368:14) + at Module.add (/Users/josh/code/jfhbrook/matanuska/operations.ts:76:24) + at Runtime. (/Users/josh/code/jfhbrook/matanuska/runtime.ts:335:34) + at Generator.next () + at asyncGeneratorStep (/Users/josh/code/jfhbrook/matanuska/runtime.ts:18:28) + at _next (/Users/josh/code/jfhbrook/matanuska/runtime.ts:36:17) + at /Users/josh/code/jfhbrook/matanuska/runtime.ts:41:13 + at new Promise () + +--- Versions --- +Matanuska BASIC: v0.0.1 +swc: v1.7.26 +vite: v1.7.26 +Node.js: v25.2.1 + +This is a bug in Matanuska BASIC. If you copy this entire message and post it +to the issues tracker: + + https://github.com/jfhbrook/matanuska/issues + +the developers will do their best to fix it - and more importantly, they will +owe you a beer, coffee or beverage of your choice. 🍻 + + ", "stdout": "", } diff --git a/test/compiler/__snapshots__/index.spec.ts.snap b/test/compiler/__snapshots__/index.spec.ts.snap index fcbdabde..ca341401 100644 --- a/test/compiler/__snapshots__/index.spec.ts.snap +++ b/test/compiler/__snapshots__/index.spec.ts.snap @@ -108,6 +108,56 @@ exports[`fixtures > 10 for i% = 1 to 10 } `; +exports[`fixtures > 10 let i% = 0 +20 +30 i% = i% + 1 +40 print i% + 1`] = ` +{ + "chunk": { + "code": "=== Disassembly of test.bas:
: === +10 1 CONSTANT 0 +10 3 DEFINE_GLOBAL i% +30 5 GET_GLOBAL i% +30 7 CONSTANT 1 +30 9 ADD +30 10 SET_GLOBAL i% +40 12 GET_GLOBAL i% +40 14 PRINT +40 15 UNDEF +40 16 RETURN", + "constants": [ + "i%", + 0, + "i%", + 1, + "i%", + "i%", + ], + "lines": [ + 10, + 10, + 10, + 10, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 40, + 40, + 40, + 40, + 40, + ], + }, + "filename": "test.bas", + "name": null, +} +`; + exports[`fixtures > 10 let i% = 0 20 30 while i% < 10 diff --git a/test/fixtures/variable-increment.bas b/test/fixtures/variable-increment.bas new file mode 100644 index 00000000..b067a261 --- /dev/null +++ b/test/fixtures/variable-increment.bas @@ -0,0 +1,4 @@ +10 let i% = 0 +20 +30 i% = i% + 1 +40 print i% From 2f9f37e1281f2dbd4b6b70ad3284867e2a91aee7 Mon Sep 17 00:00:00 2001 From: Josh Holbrook Date: Sun, 21 Dec 2025 14:51:06 -0900 Subject: [PATCH 11/11] Update TODOs --- README.md | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 98eb53d4..68f172f3 100644 --- a/README.md +++ b/README.md @@ -59,12 +59,8 @@ For more information, check out the backlog... ### Prioritized Backlog -- [ ] Implement `onward` in loops - - [x] Initial implementation - - [x] Fixture CLI tests - - [x] Fixture/snapshot compiler tests - - [x] Implement visiting `onward` inside conditionals - - [ ] Get fixture tests working +- [ ] Fix variable self-assignment + - [ ] Debug variable test - [ ] Date/Time/Duration/TZData functions - [ ] Closures - [ ] Refactor command input