The SNH (Strings Now Handled) Interpreter

Hi, here is another installment of my adventures with tiny interpreters. Moving from IEN (Is Everything Number) Interpreter, I present the SNH Strings Now Handled Interpreter.

I will first show an example of a SmallBASIC program file translated into a SNH file.

SmallBASIC .bas sample takes a string and prints each word in a column down the middle of the screen.


'The Rain in Spain test.bas for SmallBASIC 0.12.9 (B+=MGA) 2017-08-03
'This file is included to compare and contrast with the:
' test NOT INSTR1 INSTR2 2017-08-02 SNH.txt file in the
' distribution pack. This is the BASIC version of that code.

const W = txtw("W") 'for simulating CTR in SNH Interpreter in the sub used here.

test = "In vain the rain in Spain falls mainly on the plain. What's to gain ?
lastSpacePosPlusOne = 1 'the start of the string
currentSpacePos = instr(test, " ")
while 1 ' NOT was used to test it in the SNH Interpreter, really NOT needed!
if NOT(currentSpacePos) then ' last print word
word = mid(test, lastSpacePosPlusOne)
CP word
exit loop
else 'keep doing words
word = mid(test, lastSpacePosPlusOne, currentSpacePos - lastSpacePosPlusOne)
CP word
lastSpacePosPlusOne = currentSpacePos + 1
currentSpacePos = instr(lastSpacePosPlusOne, test, " ")
fi
wend
CP "Done! B+"
pause

'this is built-in routine CTR for SNH Interpreter
sub CP(text)
cellsPerLine = xmax/W
spacesNeeded = (cellsPerLine - len(text))/2
? space(spacesNeeded) + text
end



test NOT INSTR1 INSTR2 2017-08-02 SNH.txt

Oh! we might be able to place comments in the Data section after the
semi-colon, test that too!

pseudo-code

'setup for loop
27 = 1 start of line
17 = next space
Center print title
Center print Sentence 10
17 = instr1 10 16 first scan starts at 1
do
if
not 17
14 = A24 mid1 10, 27
ctr print 14
exit loop
else
'17 = new loc
18 = 17 - 27
14 = A 25 mid2 10, 27, 18
A 20 ctr p 14
27 = 17 + 1
17 = instr2 27 10 16

end
if
loop
A20 ctr p done B19 p
{9};dummy for Functions that don't return anything
{10}In vain the rain in Spain falls mainly on the plain. What's to gain ?;
{11}NOT; test function long over due!
{12}INSTR1; test this newer one
{13}INSTR2; and this newer one too
{14}; word place
{15}1; start an index with this
{16} ; this is a space, hopefully, what we will be searching for in 10
{17}; this is reserved for space locations
{18}; this is reserved for calc 17 - 27 then - 1 for mid section
{19}Done B+;
{20}CTR; the center print function
{22}Testing both MID, both INSTR, NOT and comments in data / memory section.;
{24}MID1; 2 para MID$ to end
{25}MID2; 3 para MID$ a section start and length
{27}1; last space + 1
{0}
A20 B22 F9 P
B10 F9 P
A12 B10 C16 F17
[
A11 B17 F0
I
A24 B10 C27 F14
A20 B14 F9 P
X
E
A17 B27 ~ M18
A25 B10 C27 D18 F14
A20 B14 F9 P
A17 B15 + M27
A13 B27 C10 D16 F17
N
]
A20 B19 F9 P



Some notes, 3 part to an SNH program:
1. Until the first {###} all is comment. I find if I map out the code in basic first, it is easier to convert the variables and functions and data in the memory string array.

2. In the {###} section the memory location is the number inside the {} and the string contents from } to ; are stored in an array. Comments can be fit between the ; and the next { bracket.

The second section is ended by {0} or {end} or anything that will evaluate to < 1 between the brackets.

3. Finally the 3rd section is the actual program part that processes the text into SmallBASIC commands to execute.
The 3rd section is stripped of all tabs, spaces, CRLF's so it's just letter, digits and symbols recognized by the interpreter.

The SNH (Strings Now Handled) Interpreter, SmallBASIC .bas file:

'SNH Interpreter.bas for SmallBASIC 0.12.9 (B+=MGA) 2017-07-31
' Strings Now Handled
CONST  CHARWIDTH = TXTW("W")
CONST  CELLSPERLINE = XMAX/CHARWIDTH
memsize = 20000
DIM m$(memsize)
numChars = "-.1234567890"
cmdChars = "W?ABCDFMIENP[X]%^/*~+=<>()!&|"
allChars = numChars + cmdChars

WHILE 1
  CLS
  anyfile = FILES("*SNH.txt")
  ? "SNH (Strings Now Handled) Files:":?
  IF LEN(anyfile) > 0 THEN
    FOR i = 0 TO ubound(anyfile)
      ? i, anyfile(i)
    NEXT
    ? : INPUT "number > files quits, Enter file NUMBER to run (any else quits) > ", flnm
    IF ISNUMBER(flnm) AND flnm >= 0 AND flnm <= UBOUND(anyfile)
        getfile = anyfile(flnm)
        TLOAD getfile, source, 1
        CLS : runSource
    ELSE
      STOP
    END IF
  ELSE
    ? "Sorry, no files to run, press any..." : STOP
  END IF
WEND

SUB runSource  'NOTE watch out for locals!
  ERASE m$
  DIM m$(memsize)
  'note: anything above first {}
  bs = INSTR(source, "{") : be = INSTR(bs + 1, source, "}")
  WHILE bs AND be
    ix = VAL(MID(source, bs + 1, be - bs - 1))
    IF ix < 1 THEN EXIT LOOP
    bs = INSTR(be + 1, source, "{")
    ti = MID(source, be + 1, bs - be - 1)
    tEnd = INSTR(ti, ";")
    IF tEnd = 0 THEN ? "Missing ; for {";ix;"}." : PAUSE : EXIT SUB
    ti = MID(ti, 1, tEnd - 1)
    m$(ix) = ti
    be = INSTR(bs + 1, source, "}")
    IF be = 0 THEN ? "Unmatched { } pairs." : PAUSE : EXIT SUB
  WEND
  source = MID(source, be + 1)
 
  source = UCASE(source)
  '? "Source after {}:"
 
  'let's clean the code up, check bracket balance
  bktCnt = 0 : ifCnt = 0 : code = ""
  FOR i = 1 TO LEN(source)
    char = MID(source, i, 1)
    'check to see if this is a valid instruction character
    IF INSTR(allChars, char) THEN
      code = code + char
      'count brackets
      IF char = "[" THEN bktCnt = bktCnt + 1
      IF char = "]" THEN bktCnt = bktCnt - 1
      if char = "I" Then ifCnt = ifCnt + 1
      if char = "N" then ifCnt = ifCnt - 1
    END IF
  NEXT
  IF bktCnt THEN 'mismatched brackets
    ? "Uneven brackets" : PAUSE : EXIT SUB
  ELSEIF ifCnt THEN
    ? "Uneven I N counts" : PAUSE : EXIT SUB
  ELSE
    '? code  'check
  END IF
  cmd = "" : ds = "" : err = ""
  FOR i = 1 TO LEN(code) 'loop through the code
    c = MID(code, i, 1) 'get the instruction we're on
    IF INSTR("-.1234567890", c) THEN ds = ds + c
    IF INSTR(cmdChars, c) OR i = len(code) THEN 'hit next command or end
      IF cmd <> "" THEN 'execute unfinished command
        d = VAL(ds)
        'exec last cmd
        SELECT CASE cmd
        CASE "A" : m$(1) = m$(d)
        CASE "B" : m$(2) = m$(d)
        CASE "C" : m$(3) = m$(d)
        CASE "D" : m$(4) = m$(d)      
        CASE "F"
          SELECT CASE m$(1) 'the function name m$(2) 1st para...
          CASE "NOT" : IF VAL(m$(2)) <> 0 THEN m$(d) = "0" ELSE m$(d) = "1"
          CASE "RND" : m$(d) = STR(RND)
          CASE "INT" : m$(d) = STR(INT(VAL(m$(2))))                           
          CASE "CTR": spacesNeeded = (CELLSPERLINE - LEN(m$(2)))/2
            ? SPACE(spacesNeeded) + m$(2);
          CASE "CLS": CLS
          CASE "COLOR": COLOR VAL(m$(2)), VAL(m$(3))
          CASE "LEN": m$(d) = STR(LEN(m$(2)))
          CASE "MID1": m$(d) = MID(m$(2), VAL(m$(3)))
          CASE "MID2": m$(d) = MID(m$(2), VAL(m$(3)), VAL(m$(4)))
          CASE "INSTR1": m$(d) = STR(INSTR(m$(2), m$(3)))
          CASE "INSTR2": m$(d) = STR(INSTR(VAL(m$(2)), m$(3), m$(4)))
          CASE "LOCATE": LOCATE VAL(m$(2)), VAL(m$(3))
          END SELECT
        CASE "M" : m$(d) = m$(0)
        CASE "W" : ? m$(d);
        CASE "?" : INPUT test
          IF ISNUMBER(test) THEN test = STR(test)
          m$(d) = test
        END SELECT
        cmd = "" : ds = ""
      END IF 'if cmd <> ""

      'handle current cmd
      IF INSTR("ABCDFMW?", c) THEN  'get d first
        cmd = c
      ELSEIF c = "I" : IF m$(0) = 0 then Findi
         IF err <> "" THEN ? err : PAUSE : EXIT SUB
      ELSEIF c = "E" THEN
        Findi
        IF err <> "" THEN ? err : PAUSE : EXIT SUB
      ELSEIF c = "P" THEN
        ?
      ELSEIF c = "X" THEN
        bktCnt = 1 'count the bracket we're on
        i = i + 1 'move the code pointer to the next char
        WHILE bktCnt <> 0
          'count nested loops till we find the matching one
          IF MID(code, i, 1) = "]" THEN bktCnt = bktCnt - 1
          IF MID(code, i, 1) = "[" THEN bktCnt = bktCnt + 1
          i = i + 1 'search forward
        WEND
        i = i - 1
      ELSEIF c = "]" THEN ' end a loop if loop index is 0
        bktCnt = -1 'count the bracket we're on
        i = i - 1   'move the code pointer back a char
        WHILE bktCnt <> 0
          'count nested loops till we fine the matching one
          IF MID(code, i, 1) = "]" THEN bktCnt = bktCnt - 1
          IF MID(code, i, 1) = "[" THEN bktCnt = bktCnt + 1
          i = i - 1 'search backwards
        WEND
        i = i + 1  
      ELSEIF c = "%" THEN : m$(0) = STR(VAL(m$(1)) % VAL(m$(2)))
      ELSEIF c = "^" THEN : m$(0) = STR(VAL(m$(1)) ^ Val(m$(2)))
      ELSEIF c = "/" THEN : m$(0) = STR(VAL(m$(1)) / VAL(m$(2)))
      ELSEIF c = "*" THEN : m$(0) = STR(VAL(m$(1)) * VAL(m$(2)))
      ELSEIF c = "~" THEN : m$(0) = STR(VAL(m$(1)) - VAL(m$(2)))
      ELSEIF c = "+" THEN : m$(0) = STR(VAL(m$(1)) + VAL(m$(2)))
      ELSEIF c = "=" THEN : m$(0) = STR(VAL(m$(1)) = VAL(m$(2)))
      ELSEIF c = "<" THEN : m$(0) = STR(VAL(m$(1)) < VAL(m$(2)))
      ELSEIF c = ">" THEN : m$(0) = STR(VAL(m$(1)) > VAL(m$(2)))
      ELSEIF c = "(" THEN : m$(0) = STR(VAL(m$(1)) <= VAL(m$(2)))
      ELSEIF c = ")" THEN : m$(0) = STR(VAL(m$(1)) >= VAL(m$(2)))
      ELSEIF c = "!" THEN : m$(0) = STR(VAL(m$(1)) <> VAL(m$(2)))
      ELSEIF c = "&" THEN : m$(0) = STR(VAL(m$(1)) AND VAL(m$(2)))
      ELSEIF c = "|" THEN : m$(0) = STR(VAL(m$(1)) OR VAL(m$(2)))
      END IF
    END IF ' ran into next command
    '? mid(code, i, 1); :input temp
  NEXT
  ?:? "Run is done, hit any..." : PAUSE
END SUB

SUB Findi
  'code, i, err  are global
  LOCAL cnt, c1, j
  cnt = 1
  FOR j = i + 1 TO LEN(code)
    c1 = MID(code, j, 1)
    IF c1 = "N" THEN
      cnt = cnt - 1
      IF cnt = 0 THEN i = j  : EXIT SUB
    ELSEIF c1 = "I" THEN
      cnt = cnt + 1
    ELSEIF c1 = "E" and cnt = 1 THEN
      i = j  : EXIT SUB
    END IF
  NEXT
  err = "Could not find N"
END SUB

And the Read Me SNH Interpreter.txt file:

               * * * Strings Now Handled, the SNH Interpreter (tiny) * * *

A 180 line Novelty Interpreter the source still with comments, blank lines
and debug code remaining but commented out,
written in SmallBASIC 0.12.9 bas code.

                                            * * * History * * *
This is a sort of evolving process starting from the BrainF*** Interpreter >
Be Quicker, BQ Interpreter, less tedious but still "impossible" to code
(and enjoy doing so).

With Everything Is Number, EIN version, coding became possible because
the problem of how to do IF ELSE END IF stuff was solved. Then onto
Is Everything Number?, IEN Interpreter, which adds unary functions to the mix.
(Just a couple to show how it's done, INT, RND, NOT (which was never tested).

In IEN (Is Everything Numbered) Interpreter the memory() array was numeric.
Now for SNH, we switch the memory array to string and use VAL to convert
some of the strings to numeric values.

                                          * * * Description * * *
The program written in a text Editor like Notepad, is divided  into 3 parts.
1. The comment section is first before any {###} commands for string storage.
    You can fit a book into this section, just don't use and {} symbols in your
    literary artwork.

2. String/data storage section, list numbers, strings and functions you will
    need for the program. Current functions are NOT, RND, INT for Numbers.
    CTR, CLS, COLOR, LOCATE for Screen Control and LEN, MID1, MID2, INSTR1
    and INST2.
    
    A memory data line looks like this:
    {n}  My string with leading and ending spaces   that ends here > ;

    The curly brackets signal a data item, n the place in the memory array to
    store it. Immediately following the } is the start of the string to store. If
    you want leading spaces put them in, if storing numeric data item then
    leading spaces not advised. The end of the string is signaled by a ;
    semi-colon.

    The end of this section is signalled by {0} or anything like{end} that VAL will
    return as 0, negative numbers also would work eg(-1) 

3. The actual program commands that manipulate the data from section 2.
    All commands are a single command letter/symbol, some are followed by
    numbers some are not. All spaces, CR, LF, Tabs... are stripped before the
    Interpreter goes to work executing commands, so white space the commands
    anyway you like to keep it as readable as possible for yourself (no comments
    should go in this section).
 

                            * * * Coding *SNH.txt programs (part 3) * * *
Number digits/symbols:
-.1234567890 are reserved for number values.
Note: - sign is just used for making a number value negative;
the ~ sign is actually used for the binary operation of subtraction.

Command Characters: ?ABCDFMIENP[X]%^/*~+=<>()!&|

                ?ABCDFMW are followed by a number, say n.

                IEN are for IF... THEN... ELSE... END IF blocks.

                                   [X] are for loops.

          %^/*~+=<>()!&| are the 14 Binary Operator Symbols.

These commands are followed by a number (n): ?ABCDFMW

I/O
Wn - Writes a number from memory$(n), the cursor stays where it is.
?n - Gets Input and stores it into memory(n).

Memory storing
An - stores memory(n) into memory(1)
Bn - stores memory(n) into memory(2)
Cn - stores memory(n) into memory(3)
Dn - stores memory(n) into memory(4)
Fn - stores the result of a F for Function (if any) at memory(0) to memory(n)
       more about this below
Mn - like F takes whatever is in Memory(0) and stores to memory(n)

All the Binary Operators %^/*~+=<>()!&| perform their assigned task with
operands in memory(1) and memory(2) and store the results in memory(0).
Described in more detail below.

So as you can see Memory(0-4) are specially reserved sections of the memory
array.

These commands are NOT followed by numbers: IENP[X] and all the operators.

IEN commands for IF... THEN... ELSE... END IF blocks, THEN is not used and
ELSE is optional:
I is for IF
E is for ELSE
N is for END IF

I checks memory(0) to see if it should seek E|N when memory(0) = 0 or do
the next code section when memory(0) <> 0.
When E is hit from I block we look for N on same level as it. (OK to nest.)
N is just there as an end of block marker.

P just starts next printing on next line.

[X] are the 3 command letters for loop structure:
[ is for DO
] is for LOOP
X is for eXit, your only way out of the loop, typically after an If decision
which comes after a Boolean Binary Operation.

%^/*~+=<>()!&|  Operators:
An Bn load memory(1) and memory(2) then an operator evaluates the
relation and stores the value in memory(0).
The Wn, I, Mn, Fn all read memory(0) and do their thing with it.

14 Binary Operator Symbols:

6 Numeric Return Operators:
% is MOD or Modulus, 10 % 3 = 1
^ is power,  10 ^ 3 = 1000
/ is divide 10 / 3 = 1.3333...
* is multiply 10 * 3 = 30
~ is subtraction NOT - (used to make a number negative),  10 ~ 3 = 7
+ is addition 10 + 3 = 13

          ***    Repeat: use ~ for Subtraction NOT - sign. ***

8 Boolean's return operators false = 0 ,  -1 is set for when <> 0
= is Equal test returns -1 in memory(0) if true else 0
< is Less Than test,  10 < 3 returns 0 in memory(0)
> is Greater Than test, 10 > 3 returns -1 in memory(0)
( is <=  Less Than or Equal test,  10 ( 3 returns 0 in memory(0)
) is >= Greater Than or Equal test, 10 ) 3 returns -1 in memory(0)
! is for not, here it means <> or != NOT Equal, 3 ! 10 returns -1 in memory(0)
& is for AND if memory(1) <> 0 AND memory(2) <> 0, then -1 in memory(0)
| is used for OR so if either memory(1) <>0 OR memory(2) <> 0,
    then memory(0) = (set to) -1 else memory(0) = (set to) 0

*** Notice curves of:  < (   and  > )  best one symbol to replace two? ***

More About Functions:

Function setup and execution Steps:
1. Store the function name in a memory location, {n}COLOR;
    Store any parameters it might need also in this section eg:
    {101}7;
    {102}1;
2. An puts this name into memory(1) eg:
    A100
3. Bn set the memory location for parameter value the function is to use.
    B101
4. Cn, ditto #3 if two parameters
    C103
5. Dn, ditto #4 if 3 parameters
6. Fn calls the function execution and stores the result (if any) to memory(n).
    F9
    I use 9 as a "dummy" location, COLOR 7, 1 does not return a value

Functions currently available in this SNH Interpreter version:

                                       * * * Number * * *
NOT - (never been tested) compares memory(2), setup with Bn command,
           to 0. If memory(2) = 0 then returns -1 else returns 0 in memory(0)
           which the Fn execution reads and loads memory(n) with result.

RND - does not need any parameters, just An and Fn.

INT - converts memory(2) to Integer and store at memory(n) of Fn.

                                        * * * Screen * * *
CTR - prints memory(2) on screen centered in current row.

CLS - clears screen, not parameters need be set, just An Fn

COLOR - discussed in Function setup and execution steps.

LOCATE - row, column of character cell are Bn Cn parameters.

                                        * * * String * * *
LEN - Bn's length in characters

MID1 - two parameter MID$ function, the end section of Bn starting at Cn

MID2 - 3 parameters MID$ function, the section of Bn start at Cn for length Dn.

INSTR1 - (not tested) first string location of Cn found in Bn.

INSTR2 - (not tested) string location of Dn found in Cn starting at Bn.

If you read through all this, you are probably more a reader than I.

Thanks, I hope it is at least a little fun to play with,

B+                     2017-08-02 edit

Generic Count 2017-07-30 SNH.txt:

{10}* * * Generic Count, you will be asked for Start, Stop and Step * * *;
{11}Enter Start ;
{12}Enter Stop ;
{13}Enter Step ;
{14} * * * B+ * * *;
{15}COLOR;
{16}7;
{17}1;
{18}14;
{19}CLS;
{20}CTR;
{0}
A15 B16 C17 F9
A19 F9
A20 B10 F9 P
B11 F9 ?30
B12 F9 ?31
B13 F9 ?32
A15 B18 C17 F9
A32 B100 >
I
[A30 B31 (
I
A20 B30 F9 P
A30 B32 +
M30
E
X
N]
E
[A30 B31 )
I
A 20 B30 F9 P
A30 B32 +
M30
E
X
N]
N
A15 B16 C17 F9
A20 B14 F9 P



Hi lo Game 2017-07-30 SNH.txt

{100}* * * Hi Lo Game * * *;
{101}(0 quits) Enter a guess for my number between 1 and 100 ;
{102}That was too high.;
{103}That was too low.;
{104}That was just right!;
{105}RND;
{106}INT;
{107}100;
{108}1;
{109}* * * B+ "Thanks for playing!" * * *;
{110}CTR;
{0}
A105 F10
A10 B107 * M10
A106 B10 F10
A10 B108 + M10
A110 B100 F9 P
[P A110 B101 F 9 ?11
A11 B1000 =
I
X
N
A11 B10 >
I
A110 B102 F9 P
E
A11 B10 <
I
A110 B103 F9 P
E
A110 B104 F9 P
X
N
N
]
P A110 B109 F9 P P



test Binary Operators 2017-07-26.txt (modified from last interpreter)

rewritten for SNH Interpreter 2017-07-31
{10} * * * Binary Operators Test * * *;
{11}2;
{12}2;
{21} % ;{22} ^ ;{23} / ;{24} * ;{25} ~ ;{26} + ;
{27} = ;{28} < ;{29} > ;{30} ( ;{31} ) ;{32} ! ;{33} & ;{34} | ;
{35} is ;
{36} B+;
{0}
A11B12
W1W21W2W35
%W0P
W1W22W2W35
^W0P
W1W23W2W35
/W0P
W1W24W2W35
*W0P
W1W25W2W35
~W0P
W1W26W2W35
+W0P
W1W27W2W35
=W0P
W1W28W2W35
W1W29W2W35
>W0P
W1W30W2W35
(W0P
W1W31W2W35
)W0P
W1W32W2W35
!W0P
W1W33W2W35
&W0P
W1W34W2W35
|W0P
PW36P


test LEN MID2 LOCATE 2017-07-31 SNH.txt

{10}Test LEN MID2 LOCATE;
{11}B+;
{12}1;
{13}4;
{14}LEN;
{15}MID2;
{16}LOCATE;
{19}B+;
{21}CTR;
{0}
A14 B10 F17
A18 B12 + M18
[A18 B17 (
I
A15 B10 C18 D12 F20
A18 B13 * M22
A16 B18 C22 F9 W20
A18 B12 + M18
E
X
N
]
A21 B19 F9 P