Files
lttb/lttb-impl-notes.md
T
2026-06-13 20:11:12 +03:00

13 KiB

Luxferre's Truly Tiny BASIC (LTTB) implementation notes

This document serves as the definite reference for everyone intending to create a new and fully compatible LTTB language interpreter implementation. It describes all the algorithms and data structures necessary to recreate a fully functional LTTB interpreter from scratch.

Data structures required for LTTB implementations

  • Integer numbers (signed)
  • Strings
  • Lists (arrays that can store numbers and strings)
  • Associative arrays (key-value objects)
  • Stacks (or any other linear structure that can emulate them, including lists)

Globals

An LTTB interpreter requires the following globals:

  • Program working memory PROGMEM (associative array)
  • Variable storage area VARS (associative array)
  • Subroutine call stack CALLSTACK
  • Instruction pointer IP (integer)

Initial values:

  • PROGMEM = empty
  • VARS["A"]..VARS["Z"] = 0
  • CALLSTACK = empty
  • IP = 0

Overall interpreter session flow

  1. Print the welcome banner.
  2. If the implementation is CLI-based and there is a file name passed into the command-line argument, run the LOAD routine on this file.
  3. Output the prompt > and expect user input on the same line.
  4. Read the user input, trim leading/trailing whitespace and uppercase it. Save it as CMD.
  5. If CMD equals exactly to BYE, print the "Bye!" message and exit the interpreter.
  6. If CMD is exactly one of these RUN LIST NEW CLEAR LOAD SAVE, execute the corresponding interactive routine and go to step 3. For NEW and CLEAR, the interactive routine must be the same.
  7. Execute program line input routine PROGINPUT with CMD as a parameter and then go to step 3.

Program line input routine (PROGINPUT)

Accepts the statement string STMT as the input line argument.

  1. Trim leading/trailing whitespace from STMT.
  2. Return from the routine if the STMT is an empty string.
  3. Initialize line number variable LNO as 0.
  4. Iterate through characters of STMT. If the current character C is a digit, set LNO = LNO * 10 + C. Repeat this step as long as C is a digit or a whitespace character.
  5. Starting from the first non-digit and non-whitespace character, save the rest of the STMT string as the statement body BODY.
  6. If LNO > 0, set PROGMEM[LNO] = BODY. Otherwise, run statement execution routine PROGEXEC with BODY as a parameter.

Program statement execution routine (PROGEXEC)

Accepts the statement string STMT as the input line argument.

  1. Read the keyword identifier KW from the first two non-whitespace characters in STMT.
  2. If the second character in KW is equal to =, go to step 3, otherwise go to step 5.
  3. Set KW to the value LE.
  4. Prepend the value LE (two characters LE and then a whitespace) to the beginning of STMT.
  5. If KW is exactly one of these EN GO IF IN LE PR RE, go to the next step, otherwise return.
  6. Find the position of the first whitespace in STMT, then copy everything from this position to the end of STMT into BODY.
  7. Trim leading/trailing whitespace from BODY.
  8. If KW is equal to GO and the third non-whitespace character of STMT is equal to S, execute core language routine named GOS with BODY as a parameter and return.
  9. Execute core language routine with the name equal to the value of KW with BODY as a parameter and return.

Number normalization function (NORM)

Accepts any number-like value V as the argument.

  1. If V is an empty string, return 0 immediately.
  2. Convert V into an integer (by truncating the decimal part if necessary).
  3. If V is inside the allowed range (-32768 to 32767), return V.
  4. Set V = V % 65536.
  5. If V exceeds 32767, set V = V - 65536.
  6. Return V.

Evaluation helper function (EVALHELPER)

Simple two-value evaluator. Accepts two stacks, value stack VALSTACK and operator stack OPSTACK.

  1. Pop operation OP from OPSTACK.
  2. Pop operand V2 from VALSTACK.
  3. Pop operand V1 from VALSTACK.
  4. If OP is equal to $, set RES to a random value between 0 and (V2 + 32768) % 32768.
  5. If OP is equal to +, set RES = V1 + V2.
  6. If OP is equal to -, set RES = V1 - V2.
  7. If OP is equal to *, set RES = V1 * V2.
  8. If OP is equal to / and V2 is not 0, set RES = V1 / V2.
  9. Push the value of NORM(RES) to VALSTACK.
  10. Return VALSTACK and OPSTACK.

Expression evaluation function (EXPREVAL)

Mathematical expression and intrinsic function application engine. Accepts a single EXPR string as a parameter.

  1. Trim leading/trailing whitespace from EXPR.
  2. If EXPR is empty, return 0.
  3. Initialize VALSTACK and OPSTACK as empty stacks.
  4. Initialize VALBUF and PREVC as empty strings.
  5. Set index I = 0.
  6. [Main Loop] Fetch character C = EXPR[I]. If out of bounds, go to step 16.
  7. If C is whitespace, increment I and go to step 6.
  8. If C is a digit:
    • Append C to VALBUF.
    • Set PREVC = C, increment I, go to step 6.
  9. If VALBUF is not empty, push NORM(VALBUF) to VALSTACK and clear VALBUF`.
  10. If EXPR[I onwards] starts with RND:
    • Push 0 to VALSTACK.
    • Set C to $.
    • Advance I by 3, go to step 14 (process $ as operator).
  11. If C is a variable A-Z:
    • Push NORM(VARS[C]) to VALSTACK.
    • Set PREVC = C, increment I, go to step 6.
  12. If C is (:
    • Push ( to OPSTACK.
    • Set PREVC = C, increment I, go to step 6.
  13. If C is ):
    • While OPSTACK top is not (: Execute EVALHELPER(VALSTACK, OPSTACK).
    • Pop ( from OPSTACK.
    • Set PREVC = C, increment I, go to step 6.
  14. If C is an operator (+ - * / $):
    • If C is + or - AND (PREVC is empty OR belongs to ( + - * / $):
      • Push 0 to VALSTACK.
    • While OPSTACK is not empty AND PRECMAP[OPSTACK top] >= PRECMAP[C]:
      • Execute EVALHELPER(VALSTACK, OPSTACK).
    • Push C to OPSTACK.
    • Set PREVC = C, increment I, go to step 6.
  15. [Finalization] If VALBUF is not empty, push NORM(VALBUF) to VALSTACK.
  16. While OPSTACK is not empty: Execute EVALHELPER(VALSTACK, OPSTACK).
  17. Return pop from VALSTACK.

Core language routines

EN

Program end routine.

  1. Set IP = -1.
  2. Clear CALLSTACK.

GO

Direct jump routine. Accepts EXPR string as a parameter.

Set IP = EXPREVAL(EXPR) - 1.

GOS

Subroutine caller. Accepts EXPR string as a parameter.

  1. Push IP to CALLSTACK.
  2. Set IP = EXPREVAL(EXPR) - 1.

RE

Return from a subroutine.

Pop the value from CALLSTACK and set IP to it.

LE

Assign a value to the variable. Accepts STMT string as a parameter.

  1. Treat the first Latin letter in STMT as the variable name VARNAME.
  2. Treat everything after the first = character in STMT as the expression EXPR.
  3. Set VARS[VARNAME] = EXPREVAL(EXPR).

IF

Conditionally execute the statement that follows the expressions with the relative operator. Accepts STMT string as a parameter.

  1. Find the first position of either of these characters > = < inside STMT. Record the position into two integer variables, ROPOS and ROEND, and the first found character itself into the string variable RELOP.
  2. If the character at ROPOS + 1 position inside STMT also is either of > = <, then append this character to RELOP and increment ROEND by 1.
  3. Set the string EXPR1 to everything from the start of STMT to the character at position ROPOS - 1 inclusively.
  4. Set the string REST to everything from the position ROEND + 1 to the end of STMT inclusively.
  5. Initialize the integer position variable THENPOS to -1.
  6. Pick the next keyword KW from this list in this particular order: THEN LET = RETURN GOSUB INPUT PRINT GOTO END IF. Go to step 11 when the list is exhausted.
  7. Set THENPOS to the first position of the currently iterated keyword KW inside REST. If not found, go to step 6.
  8. If KW is equal exactly to =, decrement THENPOS by 1.
  9. Copy the value of THENPOS to another integer variable, RESTPOS.
  10. If KW is equal exactly to THEN, increment RESTPOS by 4.
  11. Set the string EXPR2 to everything from the start of REST to THENPOS - 1 inclusively.
  12. Set REST to everything from the position RESTPOS to the end of the REST string inclusively.
  13. Perform comparison operation denoted by RELOP on EXPREVAL(EXPR1) and EXPREVAL(EXPR2). If the result of comparison is true, execute PROGINPUT(REST).

IN

Value input routine. Accepts STMT string as a parameter.

  1. Remove all whitespace inside STMT.
  2. Split the value of STMT by a comma , into a list VARLIST.
  3. Output the prompt ? and expect user input on the same line.
  4. Read the user input into a buffer string, remove all whitespace inside it and uppercase it. Save it as INPUTBUF.
  5. Split the value of INPUTBUF by a comma , into a list INPUTLIST.
  6. Iterate over VARLIST. For each 1-character variable VAR inside VARLIST, take the corresponding expression VAREXPR from INPUTLIST (under the same index) and set VARS[VAR] = EXPREVAL(VAREXPR).

PR

Value and string printing routine. Accepts STMT string as a parameter.

  1. Initialize an empty list PRINTLIST and an empty string buffer ITEM.
  2. Set the QUOTING flag to 0.
  3. Fetch the next character C from STMT. Go to step 10 if there are no more characters.
  4. If C is a double quote ", then flip the QUOTING flag (set QUOTING = 1 - QUOTING), append the value of C to ITEM and go to step 3. Otherwise, go to step 6.
  5. If the QUOTING flag is set, append the value of C to ITEM and go to step 3.
  6. If C is either a comma (,) or a semicolon (;), go to step 7, otherwise go to step 9.
  7. If the current ITEM value is not empty, append it to the PRINTLIST and clear the ITEM variable.
  8. Append the value of C to PRINTLIST and go to step 3.
  9. If C is not a whitespace, append its value to ITEM and go to step 3.
  10. If the current ITEM value is not empty, append it to the PRINTLIST and clear the ITEM variable.
  11. Fetch the next item string ITEM from PRINTLIST. Go to step 15 if there are no more items.
  12. If ITEM begins with a double quote ", trim all double quotes from the beginning and end and output its value verbatim (without a newline). Go to step 11.
  13. If ITEM is equal exactly to a comma ,, then output a TAB character (\t, ASCII 9) and go to step 11.
  14. If ITEM is not equal to a semicolon ;, then output the value of EXPREVAL(ITEM) verbatim, without a newline. Go to step 11.
  15. If the last ITEM value is not a comma , or a semicolon ;, output a newline.

Interactive routines

RUN

Run the program currently loaded into the working memory.

  1. Set IP = 1.
  2. If PROGMEM[IP] exists and is not empty, execute PROGEXEC(PROGMEM[IP]).
  3. Increment IP by 1.
  4. If IP > 0 and IP < 32768, go to step 2.

LIST

Output program listing.

  1. Set I = 1.
  2. If PROGMEM[I] exists and is not empty, output the value of I, TAB character (ASCII 9) and PROGMEM[I] with a newline at the end.
  3. Increment I by 1.
  4. If I > 0 and I < 32768, go to step 2.

NEW / CLEAR

Memory clear routine.

Restore all globals to their initial values:

  • PROGMEM = empty
  • VARS["A"]..VARS["Z"] = 0
  • CALLSTACK = empty
  • IP = 0

LOAD

Program loading routine from external storage media. The provided algorithm works with disk file I/O as the most popular form of interaction.

  1. Prompt the user for a file name/path FNAME.
  2. If the file does not exist, notify the user about this error and return.
  3. Execute the CLEAR interactive routine.
  4. Open a file handle FH for reading from the file under the path FNAME.
  5. Read the next program line PROGLINE from the file handle FH. Go to step 9 upon end-of-file condition.
  6. Trim the leading and trailing whitespace from PROGLINE.
  7. If PROGLINE is not empty, execute PROGINPUT(PROGLINE).
  8. Go to step 5.
  9. Close the handle FH and notify the user about successful program loading.

SAVE

Program saving routine into external storage media. The provided algorithm works with disk file I/O as the most popular form of interaction.

  1. Prompt the user for a file name/path FNAME.
  2. If the file name/path isn't writable, notify the user about this error and return.
  3. Open a file handle FH for writing into the file under the path FNAME.
  4. Set I = 1.
  5. If PROGMEM[I] exists and is not empty, write the value of I, space character (ASCII 32) and PROGMEM[I] with a newline at the end to the file handle FH.
  6. Increment I by 1.
  7. If I > 0 and I < 32768, go to step 5.
  8. Close the handle FH and notify the user about successful program saving.

Closing notes

The set of algorithms in this document should be enough to create a complete and functional LTTB interpreter in any modern programming language. If you have any corrections or optimizations regarding the algorithms, feel free to create an issue in this repository.