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= emptyVARS["A"]..VARS["Z"] = 0CALLSTACK= emptyIP= 0
Overall interpreter session flow
- Print the welcome banner.
- If the implementation is CLI-based and there is a file name passed into the command-line argument, run the
LOADroutine on this file. - Output the prompt
>and expect user input on the same line. - Read the user input, trim leading/trailing whitespace and uppercase it. Save it as
CMD. - If
CMDequals exactly toBYE, print the "Bye!" message and exit the interpreter. - If
CMDis exactly one of theseRUN LIST NEW CLEAR LOAD SAVE, execute the corresponding interactive routine and go to step 3. ForNEWandCLEAR, the interactive routine must be the same. - Execute program line input routine
PROGINPUTwithCMDas a parameter and then go to step 3.
Program line input routine (PROGINPUT)
Accepts the statement string STMT as the input line argument.
- Trim leading/trailing whitespace from
STMT. - Return from the routine if the
STMTis an empty string. - Initialize line number variable
LNOas 0. - Iterate through characters of
STMT. If the current characterCis a digit, setLNO = LNO * 10 + C. Repeat this step as long asCis a digit or a whitespace character. - Starting from the first non-digit and non-whitespace character, save the rest of the
STMTstring as the statement bodyBODY. - If
LNO > 0, setPROGMEM[LNO] = BODY. Otherwise, run statement execution routinePROGEXECwithBODYas a parameter.
Program statement execution routine (PROGEXEC)
Accepts the statement string STMT as the input line argument.
- Read the keyword identifier
KWfrom the first two non-whitespace characters inSTMT. - If the second character in
KWis equal to=, go to step 3, otherwise go to step 5. - Set
KWto the valueLE. - Prepend the value
LE(two charactersLEand then a whitespace) to the beginning ofSTMT. - If
KWis exactly one of theseEN GO IF IN LE PR RE, go to the next step, otherwise return. - Find the position of the first whitespace in
STMT, then copy everything from this position to the end ofSTMTintoBODY. - Trim leading/trailing whitespace from
BODY. - If
KWis equal toGOand the third non-whitespace character ofSTMTis equal toS, execute core language routine namedGOSwithBODYas a parameter and return. - Execute core language routine with the name equal to the value of
KWwithBODYas a parameter and return.
Number normalization function (NORM)
Accepts any number-like value V as the argument.
- If
Vis an empty string, return 0 immediately. - Convert
Vinto an integer (by truncating the decimal part if necessary). - If
Vis inside the allowed range (-32768 to 32767), returnV. - Set
V = V % 65536. - If
Vexceeds 32767, setV = V - 65536. - Return
V.
Evaluation helper function (EVALHELPER)
Simple two-value evaluator. Accepts two stacks, value stack VALSTACK and operator stack OPSTACK.
- Pop operation
OPfromOPSTACK. - Pop operand
V2fromVALSTACK. - Pop operand
V1fromVALSTACK. - If
OPis equal to$, setRESto a random value between 0 and(V2 + 32768) % 32768. - If
OPis equal to+, setRES = V1 + V2. - If
OPis equal to-, setRES = V1 - V2. - If
OPis equal to*, setRES = V1 * V2. - If
OPis equal to/andV2is not 0, setRES = V1 / V2. - Push the value of
NORM(RES)toVALSTACK. - Return
VALSTACKandOPSTACK.
Expression evaluation function (EXPREVAL)
Mathematical expression and intrinsic function application engine. Accepts a single EXPR string as a parameter.
- Trim leading/trailing whitespace from
EXPR. - If
EXPRis empty, return 0. - Initialize
VALSTACKandOPSTACKas empty stacks. - Initialize
VALBUFandPREVCas empty strings. - Set index
I = 0. - [Main Loop] Fetch character
C = EXPR[I]. If out of bounds, go to step 16. - If
Cis whitespace, incrementIand go to step 6. - If
Cis a digit:- Append
CtoVALBUF. - Set
PREVC = C, incrementI, go to step 6.
- Append
- If
VALBUF is not empty, push NORM(VALBUF) to VALSTACK and clearVALBUF`. - If EXPR[I onwards] starts with
RND:- Push 0 to
VALSTACK. - Set
Cto$. - Advance
Iby 3, go to step 14 (process$as operator).
- Push 0 to
- If
Cis a variableA-Z:- Push
NORM(VARS[C])toVALSTACK. - Set
PREVC = C, incrementI, go to step 6.
- Push
- If
Cis(:- Push
(to OPSTACK. - Set
PREVC = C, incrementI, go to step 6.
- Push
- If
Cis):- While
OPSTACKtop is not(: ExecuteEVALHELPER(VALSTACK, OPSTACK). - Pop
(fromOPSTACK. - Set
PREVC = C, incrementI, go to step 6.
- While
- If
Cis an operator (+ - * / $):- If C is
+or-AND (PREVCis empty OR belongs to( + - * / $):- Push 0 to
VALSTACK.
- Push 0 to
- While
OPSTACKis not empty ANDPRECMAP[OPSTACK top] >= PRECMAP[C]:- Execute
EVALHELPER(VALSTACK, OPSTACK).
- Execute
- Push
CtoOPSTACK. - Set
PREVC = C, incrementI, go to step 6.
- If C is
- [Finalization] If
VALBUFis not empty, pushNORM(VALBUF)toVALSTACK. - While
OPSTACKis not empty: ExecuteEVALHELPER(VALSTACK, OPSTACK). - Return pop from
VALSTACK.
Core language routines
EN
Program end routine.
- Set
IP = -1. - 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.
- Push
IPtoCALLSTACK. - 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.
- Treat the first Latin letter in
STMTas the variable nameVARNAME. - Treat everything after the first
=character inSTMTas the expressionEXPR. - Set
VARS[VARNAME] = EXPREVAL(EXPR).
IF
Conditionally execute the statement that follows the expressions with the relative operator. Accepts STMT string as a parameter.
- Find the first position of either of these characters
> = <insideSTMT. Record the position into two integer variables,ROPOSandROEND, and the first found character itself into the string variableRELOP. - If the character at
ROPOS + 1position insideSTMTalso is either of> = <, then append this character toRELOPand incrementROENDby 1. - Set the string
EXPR1to everything from the start ofSTMTto the character at positionROPOS - 1inclusively. - Set the string
RESTto everything from the positionROEND + 1to the end ofSTMTinclusively. - Initialize the integer position variable
THENPOSto -1. - Pick the next keyword
KWfrom this list in this particular order:THEN LET = RETURN GOSUB INPUT PRINT GOTO END IF. Go to step 11 when the list is exhausted. - Set
THENPOSto the first position of the currently iterated keywordKWinsideREST. If not found, go to step 6. - If
KWis equal exactly to=, decrementTHENPOSby 1. - Copy the value of
THENPOSto another integer variable,RESTPOS. - If
KWis equal exactly toTHEN, incrementRESTPOSby 4. - Set the string
EXPR2to everything from the start ofRESTtoTHENPOS - 1inclusively. - Set
RESTto everything from the positionRESTPOSto the end of theRESTstring inclusively. - Perform comparison operation denoted by
RELOPonEXPREVAL(EXPR1)andEXPREVAL(EXPR2). If the result of comparison is true, executePROGINPUT(REST).
IN
Value input routine. Accepts STMT string as a parameter.
- Remove all whitespace inside
STMT. - Split the value of
STMTby a comma,into a listVARLIST. - Output the prompt
?and expect user input on the same line. - Read the user input into a buffer string, remove all whitespace inside it and uppercase it. Save it as
INPUTBUF. - Split the value of
INPUTBUFby a comma,into a listINPUTLIST. - Iterate over
VARLIST. For each 1-character variableVARinsideVARLIST, take the corresponding expressionVAREXPRfromINPUTLIST(under the same index) and setVARS[VAR] = EXPREVAL(VAREXPR).
PR
Value and string printing routine. Accepts STMT string as a parameter.
- Initialize an empty list
PRINTLISTand an empty string bufferITEM. - Set the
QUOTINGflag to 0. - Fetch the next character
CfromSTMT. Go to step 10 if there are no more characters. - If
Cis a double quote", then flip theQUOTINGflag (setQUOTING = 1 - QUOTING), append the value ofCtoITEMand go to step 3. Otherwise, go to step 6. - If the
QUOTINGflag is set, append the value ofCtoITEMand go to step 3. - If
Cis either a comma (,) or a semicolon (;), go to step 7, otherwise go to step 9. - If the current
ITEMvalue is not empty, append it to thePRINTLISTand clear theITEMvariable. - Append the value of
CtoPRINTLISTand go to step 3. - If
Cis not a whitespace, append its value toITEMand go to step 3. - If the current
ITEMvalue is not empty, append it to thePRINTLISTand clear theITEMvariable. - Fetch the next item string
ITEMfromPRINTLIST. Go to step 15 if there are no more items. - If
ITEMbegins 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. - If
ITEMis equal exactly to a comma,, then output a TAB character (\t, ASCII 9) and go to step 11. - If
ITEMis not equal to a semicolon;, then output the value ofEXPREVAL(ITEM)verbatim, without a newline. Go to step 11. - If the last
ITEMvalue is not a comma,or a semicolon;, output a newline.
Interactive routines
RUN
Run the program currently loaded into the working memory.
- Set
IP = 1. - If
PROGMEM[IP]exists and is not empty, executePROGEXEC(PROGMEM[IP]). - Increment
IPby 1. - If
IP > 0andIP < 32768, go to step 2.
LIST
Output program listing.
- Set
I = 1. - If
PROGMEM[I]exists and is not empty, output the value ofI, TAB character (ASCII 9) andPROGMEM[I]with a newline at the end. - Increment
Iby 1. - If
I > 0andI < 32768, go to step 2.
NEW / CLEAR
Memory clear routine.
Restore all globals to their initial values:
PROGMEM= emptyVARS["A"]..VARS["Z"] = 0CALLSTACK= emptyIP= 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.
- Prompt the user for a file name/path
FNAME. - If the file does not exist, notify the user about this error and return.
- Execute the
CLEARinteractive routine. - Open a file handle
FHfor reading from the file under the pathFNAME. - Read the next program line
PROGLINEfrom the file handleFH. Go to step 9 upon end-of-file condition. - Trim the leading and trailing whitespace from
PROGLINE. - If
PROGLINEis not empty, executePROGINPUT(PROGLINE). - Go to step 5.
- Close the handle
FHand 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.
- Prompt the user for a file name/path
FNAME. - If the file name/path isn't writable, notify the user about this error and return.
- Open a file handle
FHfor writing into the file under the pathFNAME. - Set
I = 1. - If
PROGMEM[I]exists and is not empty, write the value ofI, space character (ASCII 32) andPROGMEM[I]with a newline at the end to the file handleFH. - Increment
Iby 1. - If
I > 0andI < 32768, go to step 5. - Close the handle
FHand 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.