Su and C are out of town on a road trip with Grandpa, and I'm taking it easy. Slept in 'til 10:30 today. Yay! The weather was crappy so I stayed around the house, and got sucked into a project to the exclusion of all else.
The more I wrote, the more I realized the resulting implementation would look nothing like the original, which had to contend with the joys of BASIC's limited flow control, data structures and variable naming. Which really defeated the whole purpose of the project, since just reimplementing the Merchant Prince rules from scratch would probably take a quarter of the time. The only advantage of doing a translation was to get it to be an exact simulation... which would invariably fail since the code was radically restructured and differences inadvertantly introduced.
And that's what became of Saturday, April 21st, 2007 in Josh's life - sitting at the computer, dog at my side, music on.
At this point, it's running inside the Windows Scripting Host (command line JScript interpreter for Windows) and I've got the following fully functional:
- CLEAR // Clear all variables
- END // End program
- FRE // Garbage collect
- GOTO // GOTO linenum
- GOSUB // GOSUB linenum
- RETURN // Return from a GOSUB
- POP // Change last GOSUB into a GOTO
- FOR // FOR i = m TO m STEP s
- NEXT // NEXT [i]
- LET // Assign a variable, LET x = expr
- PRINT // Output to the screen
- INPUT // Read input from keyboard
- IF // IF expr (THEN|GOTO) linenum|statement [statement ... ]
- REM // Comment
- NOTRACE // Turn off line tracing
- TRACE // Turn on line tracing
- Binary boolean operators: OR, AND
- Relational operators: '<', '>', '=', '<=' /'=<', '>='/'=>', '<>'/'><'
- Additive operators: '+', '-'
- Multiplicative operators: '*', '/'
- Power operators: '^'
- Unary operators: '+', '-', NOT
- ABS // Absolute value
- ASC // ASCII code of a character
- ATN // Arctangent
- CHR$ // Character, given an ASCII code
- COS // Cosine
- EXP // Raise e to the specified power
- INT // Integer portion of the value
- LEFT$ // Left end of a string
- LEN // Length substring
- LOG // Natural log
- MID$ // Arbitrary substring
- RIGHT$ // Right substring
- RND // Pseudorandom number
- SGN // Sign (-1, 0, 1)
- SIN // Sine
- SQR // Square root
- STR$ // Convert number to string
- TAN // Tangent
- VAL // Convert string to number
- I haven't thoroughly exercised string variables and expressions
- ON expr GOTO ... is not yet implemented
- Arrays (and DIM) are not yet implemented
- DATA/READ/RESTORE are not yet implemented
- User defined functions (DEF FN) are not yet implemented)
- ONERR/RESUME are not yet implemented... and probably won't be, since this was buggy on the Apple II
- HOME, HTAB, VTAB, GET, TAB, POS() are tricky as console APIs are not supported (easily?) by WSH
- PRINT CHR$(4) hooks and DOS emulation for loading files.
- TEXT, COLOR=, DRAW, FLASH, GR, HLIN, HPLOT, HCOLOR=, HGR, HGR2, INVERSE, NORMAL, PLOT, ROT=, SCALE=, SCRN(), VLIN, XDRAW - I'm not touching different display modes!
- CONT, DEL, LIST, NEW, RUN, SPEED= - primarily immediate mode, not interesting for 99.99% of programs
- CALL, HIMEM:, IN#, LOMEM:, PDL(), PEEK(), POKE, PR#, USR(), WAIT - interact with the native memory space of the Apple II/6502. Not happening.
- LOAD, RECALL, SAVE, SHLOAD, STORE - cassette tape I/O. Yeah, I'm all over that.
The next big step, really, is to get it working in a web page. This is fairly straightforward:
- I already have a basic TTY emulator. Not glass, paper. Write-only display. Line-oriented programs. Not sexy but it works.
- The program is an array of statements, with the execution context an index into that. There is an explicit stack for statements (but not for functions). Each statement, in turn, is a pointer into that. Each statement, in turn, is an array of tokens. To handle making INPUT blocking, I can push a stack object (like a GOSUB) that points to the middle of the INPUT statement, and then basically "yield" out of the program. Restoring should be as simple a RETURNing from a gosub.
So how did I scare myself?
Well, as I've been coding up the interpreter I've also been writing a test program in Applesoft that I run to test features as they come online or as I refactor and clean up the code - I have code which spits out the Fibbonacci sequence, a "guess my number" game and a "guess your number" game, various output demos, and so on. Well, I decided to see if the code-to-character function (CHR$) was working. So I plunked in this code:
10 PRINT CHR$(7) : ENDAnd ran it. It gave a syntax error - it didn't think CHR$ was a valid variable name, let alone a special function. Oops. After a few minutes of puttering around in the code, I realized that the regular expression matching reserved words didn't have $ escaped. But I was curious as to why CHR$ wasn't a valid variable, and it took several more minutes to figure that out (another regular expression glitch). So I escaped it, and ran it again.
To understand what happened next, there are some key points above:
- I've been home alone for a week
- I've been staring at the computer all day
- I have the music cranked on my computer
- ASCII code point 7 is the "BELL" character
- Between the time I wrote the test code and the time it ran successfully, almost 10 minutes had passed.
But it worked!