baisc: nothing to see here - putting the framework together for it
authorAlan Cox <alan@linux.intel.com>
Sat, 4 Nov 2017 21:01:16 +0000 (21:01 +0000)
committerAlan Cox <alan@linux.intel.com>
Sat, 4 Nov 2017 21:01:16 +0000 (21:01 +0000)
Applications/basic/README [new file with mode: 0644]
Applications/basic/basic.h [new file with mode: 0644]
Applications/basic/calc.c [new file with mode: 0644]
Applications/basic/maketokens.c [new file with mode: 0644]
Applications/basic/tokenizer.c [new file with mode: 0644]
Applications/basic/tokens.h [new file with mode: 0644]

diff --git a/Applications/basic/README b/Applications/basic/README
new file mode 100644 (file)
index 0000000..cf5429d
--- /dev/null
@@ -0,0 +1 @@
+Internal working tree for the replacmeent of ubasic
diff --git a/Applications/basic/basic.h b/Applications/basic/basic.h
new file mode 100644 (file)
index 0000000..ba9ec68
--- /dev/null
@@ -0,0 +1,9 @@
+extern void error(int n);
+void execute_line_content(uint8_t *l);
+
+
+#define MISSING_QUOTE  1
+#define OUT_OF_MEMORY  2
+#define BAD_LINE_NUMBER        3
+#define SYNTAX_ERROR   4
+#define RETURN_UDNERFLOW 5
\ No newline at end of file
diff --git a/Applications/basic/calc.c b/Applications/basic/calc.c
new file mode 100644 (file)
index 0000000..f0ccafa
--- /dev/null
@@ -0,0 +1,365 @@
+#include <stdio.h>
+#include <stdint.h>
+#include <string.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <errno.h>
+
+
+static const char *ctypes[9] = {
+    "eof",
+    "constant",
+    "leftassoc",
+    "leftparen",
+    "rightparen",
+    "function",
+    "unary",
+    "symbol",
+    "postfix",
+};
+
+struct token {
+    uint8_t tok;
+#define EOF_TOKEN              128
+#define INTEGER                        129
+#define SIZEOF                 130
+    uint8_t class;
+#define CONSTANT               1
+#define        LEFT_ASSOC              2
+#define LEFTPAREN              3
+#define RIGHTPAREN             4
+#define FUNCTION               5
+#define UNARY                  6
+#define SYMBOL                 7
+#define POSTFIX                        8
+    uint16_t data;
+};
+
+/* For now before we pack it all as an integer value */
+#define CLASS(x)       ((x)->class)
+#define PRECEDENCE(x)  ((x)->data)
+
+#define OPMAX 32
+
+char *input;
+
+static const char ops[] = {"+-*/%"};
+
+static struct token eoftok = {
+    0,
+    EOF_TOKEN,
+    0
+};
+
+static struct token eoftok2 = {
+    ';',
+    EOF_TOKEN,
+    0
+};
+
+static struct token peek;
+static int peeked;
+
+struct token *token(void)
+{
+    static struct token n;
+    char *x;
+
+    if (peeked) {
+        peeked = 0;
+        return &peek;
+    }
+
+    while(isspace(*input))
+        input++;
+    if (*input == 0)
+        return &eoftok;
+
+    if (*input == ';')
+        return &eoftok2;
+
+    if ((x = strchr(ops, *input)) != NULL) {
+        n.tok = *input;                /* Op code */
+        n.class = LEFT_ASSOC;
+        n.data = 1 + x - ops;  /* Priority */
+        input++;
+        return &n;
+    }
+    if (*input == '(') {
+        n.tok = *input;
+        n.class = LEFTPAREN;
+        input++;
+        return &n;
+    }
+    if (*input == ')') {
+        n.tok = *input;
+        n.class = RIGHTPAREN;
+        input++;
+        return &n;
+    }
+    if (*input == '!') {
+        n.tok = '!';
+        n.class = UNARY;
+        n.data = 0;
+        input++;
+        return &n;
+    }
+    /* symbol or numeric value */
+    if (isdigit(*input)) {
+        errno = 0;
+        n.data = strtol(input, &input, 0);
+        if (errno) {
+            fprintf(stderr, "invalid integer.\n");
+            exit(1);
+        }
+        n.class = CONSTANT;
+        return &n;
+    }
+    if (isalpha(*input) || *input == '_') {
+        char *p = input++;
+        while(*input && (isalnum(*input) || *input == '_'))
+            input++;
+        printf("Will look for symbol\n");
+        n.class = SYMBOL;
+        n.data = 1;    /* Will be symbol index */
+        return &n;
+    }
+    /* To add for testing function and comma */
+    fprintf(stderr, "?? %s\n", input);
+    exit(1);
+}
+
+const struct token *token_peek(void)
+{
+    if (peeked)
+        return &peek;
+    memcpy(&peek, token(), sizeof(peek));
+    peeked = 1;
+    return &peek;
+}
+
+struct token opstack[OPMAX];
+struct token *optop = opstack;
+uint16_t datastack[OPMAX];
+uint16_t *datatop = datastack;
+
+void push(uint16_t data)
+{
+    if (datatop == &datastack[OPMAX]) {
+        fprintf(stderr, "push: too complex\n");
+        exit(1);
+    }
+    *datatop++ = data;
+}
+
+uint16_t pop(void)
+{
+    if (datatop == datastack) {
+        fprintf(stderr, "pop: underflow\n");
+        exit(1);
+    }
+    return *--datatop;
+}
+
+void popop(void)
+{
+    struct token t;
+    uint16_t tmp;
+    if (optop == opstack) {
+        fprintf(stderr, "popop: underflow\n");
+        exit(1);
+    }
+    t = *--optop;
+    switch(t.tok) {
+        case '(':
+            break;
+        case '+':
+            push(pop() + pop());
+            break;
+        case '*':
+            push(pop() * pop());
+            break;
+        case '/':
+            tmp = pop();
+            push(pop() / tmp);
+            break;
+        case '%':
+            tmp = pop();
+            push(pop() % tmp);
+            break;
+        case '-':
+            tmp = pop();
+            if(t.class == LEFT_ASSOC)
+                push(pop() - tmp);
+            else
+                push(-tmp);
+            break;
+        case '!':
+            push(!pop());
+            break;
+        default:
+            if (t.class == FUNCTION) {
+                /* ??? */
+            }
+            fprintf(stderr,"popop: bad op '%c'\n", t.tok);
+            exit(1);
+    }
+}
+
+void pushop(const struct token *t)
+{
+    if (optop == &opstack[OPMAX]) {
+        fprintf(stderr, "pushop: too complex\n");
+        exit(1);
+    }
+#ifdef DEBUG
+    printf("pushop %d %c\n",t->class,t->tok);
+#endif    
+    *optop++ = *t;
+}
+      
+void do_popout(uint8_t stopclass)
+{
+    while(optop != opstack) {
+        if (optop[-1].class == stopclass)
+            return;
+        popop();
+    }
+}
+
+void popout(void) {
+    do_popout(LEFTPAREN);
+    if (optop == opstack)
+        fprintf(stderr, "Unbalanced brackets in expression.\n");
+}
+
+/* Do we need to be smarter here and spot ?'s left onthe opstack too ? */
+void popout_final(void)
+{
+    do_popout(LEFTPAREN);
+    if (optop != opstack)
+        fprintf(stderr, "Unbalanced brackets expression end.\n");
+    printf("Answer = %u\n", pop());
+    if (datatop != datastack)
+        fprintf(stderr, "Unbalanced data end.\n");
+}
+
+
+static uint8_t next;
+#define LPAREN         1               /* A left bracket - eg func( */
+#define OPERAND                2               /* Any operand - symbol/constnat */
+#define OPERATOR       4               /* An operator */
+#define RPAREN         8               /* A right bracket - closing typedef */
+
+
+/* We use bitmasks. The need mask must overlap the types passed. Usually
+   it's a single bit but functions require a left-paren only, while a
+   a left-paren is also valid for an operand */
+
+void neednext(uint8_t h, uint8_t n)
+{
+    if (next & h) {
+        next = n;
+        return;
+    }
+    fprintf(stderr, "Syntax error: Want %x got %x.\n",
+        (int)h, (int)next);
+}
+
+static const struct token fncall = {
+    FUNCTION,
+    FUNCTION,
+    0
+};
+
+/* Write out an expression tree as we linearly parse the code. We arrange
+   things so we can write out all constants and in particular strings as
+   we go so we don't buffer anything we don't need to in this pass
+   
+   TODO: ?: finish postfix operators (x++, x--, []) and typecasts (type)x
+   Postfix is generalising the function call stuff. We may well want
+   to defer knowing about function calls to tree parsing and just do
+   generic markers and logic, however we do need to handle [expr] at
+   this layer. sizeof may force our hands a bit
+   
+   Note that at this level we don't care about types. We will do type
+   checking and type conversion insertions when we parse the resulting
+   trees
+   
+   Would it be better to recurse , and do ?: at a higher level ?
+   
+   */
+
+const struct token *eval(int in_decl)
+{
+    struct token *t;
+    next = OPERAND;
+
+    while((t = token())->class != EOF_TOKEN) {
+#if DEBUG
+        printf("|Token %d Class %s Data %d\n",
+            t->tok, ctypes[t->class], t->data);
+#endif            
+        switch(CLASS(t)) {
+            case CONSTANT:
+                neednext(OPERAND, OPERATOR);
+                push(t->data);
+                break;
+            case SYMBOL:
+                /* symbols might be functions - tidy this up */
+                neednext(OPERAND, OPERATOR|LPAREN);
+                push(t->data);
+                break;
+            case UNARY:
+                neednext(OPERAND, OPERAND);
+                pushop(t);
+                break;
+            case LEFT_ASSOC:
+                /* Umary minus is special */
+                if (t->tok == '-' && next == OPERAND) {
+                    neednext(OPERAND, OPERAND);
+                    t->class = UNARY;
+                    pushop(t);
+                    break;
+                }
+            case FUNCTION:
+                neednext(OPERATOR, OPERAND);
+                while (optop > opstack && optop->class == LEFT_ASSOC &&
+                       PRECEDENCE(t) <= PRECEDENCE(optop))
+                    popop();
+                pushop(t);
+                if (optop > opstack && optop->class == FUNCTION)
+                    popop();
+                break;
+            case LEFTPAREN:
+                if (next & OPERATOR) {
+                    /* Function call */
+                    neednext(LPAREN,OPERAND);
+                    pushop(&fncall);
+                } else
+                    /* Else precedence bracketing */
+                    neednext(OPERAND|LPAREN,OPERAND);
+                pushop(t);
+                break;
+            case RIGHTPAREN:
+                neednext(OPERATOR|RPAREN, OPERATOR);
+                popout();
+                popop();       /* drop the left paren */
+                break;
+            default:           /* Assume we've hit the expression end */
+                goto done;
+        }
+    }
+done:
+    neednext(OPERATOR,0);
+    popout_final();
+    return t;
+}
+
+int main(int argc, char *argv[])
+{
+    char buf[512];
+    fgets(buf, 512, stdin);
+    input = buf;
+    eval(0);
+}
diff --git a/Applications/basic/maketokens.c b/Applications/basic/maketokens.c
new file mode 100644 (file)
index 0000000..bdd985f
--- /dev/null
@@ -0,0 +1,42 @@
+#include <stdio.h>
+#include <ctype.h>
+
+char *tokens[] = {
+    "PRINT",
+    "IF",
+    "GO",
+    "TO",
+    "SUB",
+    "LET",
+    "INPUT",
+    "RETURN",
+    "CLEAR",
+    "LIST",
+    "RUN",
+    "END",
+    NULL
+};
+
+/* Print the token table. All tokens must be at least two bytes long */
+int main(int argc, char *argv[])
+{
+    int tokbase = 192;
+    char **p = tokens;
+    char *x;
+    printf("#define TOKEN_BASE %d\n", tokbase);
+    while(x = *p) {
+        putchar('\t');
+        while(x[1])
+            printf("'%c',", *x++);
+        printf("0x%02X,\n", *x|0x80);
+        printf("#define TOK_");
+        x = *p;
+        while(*x) {
+            if (isalnum(*x))
+                putchar(*x);
+            x++;
+        }
+        printf(" %d\n", tokbase++);
+        p++;
+    }
+}
diff --git a/Applications/basic/tokenizer.c b/Applications/basic/tokenizer.c
new file mode 100644 (file)
index 0000000..400683b
--- /dev/null
@@ -0,0 +1,622 @@
+/*
+ *     This module deals with keeping the BASIC program present and tokenized
+ *
+ *     The code ends with a fake like 65535 which we must ensure the user
+ *     never gets to replace as it avoids us ever having to special case
+ *     last line.
+ *
+ *     The interpreter core uses memory above lines_end for data. We don't
+ *     do anything to make life easy for it if lines are added.
+ */
+
+#include <stdio.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+
+#include "basic.h"
+
+uint8_t blob[2048];
+uint8_t *lines = blob;
+uint8_t *lines_end;
+uint8_t *lines_limit = &blob[2047];
+uint8_t *execp;
+uint16_t run_line;
+uint8_t *next_line;
+uint8_t running;
+uint8_t pending_go;
+uint16_t go_target;
+uint8_t no_colon;
+uint16_t editbase;
+uint16_t newest;
+
+
+uint8_t cursor_x;
+uint8_t cursor_y;
+uint8_t width = 80;
+uint8_t height = 24;
+
+static uint8_t last_ch;
+
+static void print_ch(uint8_t c)
+{
+       if (c == 13)
+               cursor_x = 0;
+       else if (c == 10) {
+               cursor_x = 0;
+               if (cursor_y < height - 1)
+                       cursor_y++;
+       }
+       else if (c == 8) {
+               if (cursor_x)
+                       cursor_x--;
+               else
+                       return;
+       }
+       else if (c == '\t') {
+               do {
+                       print_ch(' ');
+               } while(cursor_x % 8);
+               return;
+       } else {
+               cursor_x++;
+               if (cursor_x == width - 1) {
+                       if (cursor_y < height - 1)
+                               cursor_y++;
+                       cursor_x = 0;
+               }
+       }
+       putchar(c);
+       last_ch = c;
+}
+
+static void print_str(char *c)
+{
+       while(*c)
+               print_ch((uint8_t)*c++);
+}
+       
+
+static void printat(uint8_t y, uint8_t x)
+{
+       printf("\033[%d;%dH", y, x);
+       cursor_x = x;
+       cursor_y = y;
+}
+
+static void wipe(void)
+{
+       printat(0,0);
+       printf("\033[J");
+}
+
+static char toktab[] = {
+#include "tokens.h"
+       0xFF
+};
+
+static uint8_t *outptr;
+
+/* Input is a 7bit basic token with 0x80 on the last byte */
+/* Could binary search this as we can always find a marker with 0x80 to
+   split the search in halfish */
+
+uint8_t tokget(uint8_t * c)
+{
+       uint8_t *ptr = toktab;
+       uint8_t *cp = c;
+       uint8_t v;
+       uint8_t n = TOKEN_BASE;
+
+       while (*ptr != 0xFF) {
+               /* keep matching bytes */
+               while (*ptr == *cp) {
+                       /* If we match and 0x80 is set then we matched end bytes
+                          and we got a hit */
+                       if (*ptr++ & 0x80)
+                               return n;
+                       /* If we matched top bit clear then keep matching */
+                       cp++;
+               }
+               /* We matched until a '.' in the input that indicates a short form
+                  except for the case of '.' */
+               if (*cp == ('.' | 0x80) && cp != c)
+                       return n;
+               /* When the match fails walk on until the start of the next match
+                  in the table */
+               while (!(*ptr++ & 0x80));
+               cp = c;
+               n++;
+       }
+       return 0;
+}
+
+void tokenize_line(uint8_t * input)
+{
+       uint8_t *p = input;
+       uint8_t t;
+
+       while (*p) {
+               /* Strip spaces */
+               while (*p && isspace(*p))
+                       p++;
+               if (*p == 0)
+                       break;
+               /* Strings require special handling so we don't tokenize them */
+               if (*p == '"') {
+                       *outptr++ = *p++;
+                       while (*p != '"') {
+                               if (*p == 0)
+                                       error(MISSING_QUOTE);
+                               *outptr++ = *p++;
+                       }
+                       *outptr++ = *p++;
+               } else {
+                       uint8_t *f = p++;
+                       /* Otherwise we keep adding letters and trying to tokenize. This
+                          is inefficient but we don't do it often */
+                       while (isalnum(*p) || *p == '.') {
+                               *p |= 0x80;
+                               t = tokget(f);
+                               if (t == 0)
+                                       *p++ &= 0x7f;
+                               else {
+                                       *outptr++ = t;
+                                       p++;
+                                       break;
+                               }
+                       }
+                       if (t)
+                               continue;
+                       *outptr++ = *f;
+                       p = f + 1;
+               }
+       }
+       *outptr++ = 0;
+}
+
+/* Find the line (or next one). As we have a dummy line 65535 this never
+   fails to find something */
+uint8_t *find_line(uint16_t line)
+{
+       uint8_t *ptr = lines;
+       /* We pad lines on alignment icky processors */
+       while (*(uint16_t *) ptr < line)
+               ptr += *(uint16_t *) (ptr + 2); /* Length in bytes */
+       return ptr;
+}
+
+/* Currently we linear search. It's possible to do clever things but we don't
+   as it's not a performance critical spot. Pass 0 size for a delete */
+void insdel_line(uint16_t num, uint8_t * toks, uint16_t size)
+{
+       uint8_t *ptr = lines;
+       int16_t shift = size;
+       uint8_t *cptr;
+
+       if (size == 1) {
+               size = 0;       /* End mark only - delete */
+               shift = 0;
+       }
+       if (size) {
+               size += 4;      /* Length, number */
+               shift += 4;
+       }
+
+       /* This is conservative - we should check for deletion/insertion to see
+          if a replaced line will fit.. but it's close to the line anyway so
+          already deep in trouble */
+
+       if (size + lines_end > lines_limit)
+               error(OUT_OF_MEMORY);
+
+       /* We pad lines on alignment icky processors */
+       while (*(uint16_t *) ptr < num)
+               ptr += *(uint16_t *) (ptr + 2); /* Length in bytes */
+
+       cptr = ptr;
+
+       /* Our line exists, we will need to shift by the difference in line
+          size, and also we need to move relative to the next line */
+       if (*(uint16_t *) ptr == num) {
+               /* Find the next line */
+               shift -= *(uint16_t *) (ptr + 2);
+               cptr = ptr + *(uint16_t *) (ptr + 2);   /* Length in bytes */
+       }
+       if (shift) {
+               memmove(cptr + shift, cptr, lines_end - cptr);
+               lines_end += shift;
+       }
+       if (size) {
+               *(uint16_t *)ptr = num;
+               *(uint16_t *)(ptr + 2) = size;
+               memcpy(ptr + 4, toks, size - 4);
+               lines_end += size;
+       }
+}
+
+uint8_t *detok(uint8_t c)
+{
+       static uint8_t rv;
+       uint8_t *p;
+
+       rv = c | 0x80;
+       if (c < TOKEN_BASE)
+               return &rv;
+       p = toktab;
+
+       c -= TOKEN_BASE;
+       while (c--) {
+               while (!(*p++ & 0x80));
+               if (*p == 0xFF) {
+                       fprintf(stderr, "badtok\n");
+                       exit(1);
+               }
+       }
+       return p;
+}
+
+void print_line(uint8_t * p)
+{
+       uint8_t *o;
+       while (*p) {
+               /* Quoted text */
+               if (*p == '"') {
+                       do {
+                               print_ch(*p++);
+                       } while (*p != '"');
+                       print_ch(*p++);
+                       /* Symbols */
+               } else if (*p < TOKEN_BASE) {
+                       print_ch(*p);
+                       p++;
+               }
+               /* Tokens */
+               else {
+                       if (last_ch != ' ')
+                               print_ch(' ');
+                       o = detok(*p++);
+                       do {
+                               print_ch(*o & 0x7F);
+                       } while (!(*o++ & 0x80));
+                       if (o[-1] != '(')
+                               print_ch(' ');
+               }
+       }
+}
+
+void find_exec_line(uint16_t l)
+{
+       uint8_t *ptr = find_line(l);
+       run_line = *(uint16_t *)ptr;
+       next_line = ptr + *(uint16_t *)(ptr + 2);
+}
+
+
+/* Might also be worth having renumber but that involves parsing each line
+   and fixing up anything that's a constant for goto/gosub etc - messy */
+
+void error(int n)
+{
+       char tmpbuf[64];
+       sprintf(tmpbuf, "\nError %d/%d\n", n, run_line);
+       print_str(tmpbuf);
+       running = 0;
+       longjmp(aborted, 1);
+}
+
+/* Will use the more general parser once present */
+uint8_t *linenumber(uint8_t * n, uint16_t * v)
+{
+       uint16_t vp = 0, vn;
+       while (isdigit(*n)) {
+               vn = vp * 10 + *n - '0';
+               if (vn < vp)
+                       goto bad;
+               vp = vn;
+               n++;
+       }
+       *v = vp;
+       return n;
+
+      bad:
+       error(BAD_LINE_NUMBER);
+       return NULL;
+}
+
+/*
+ *     Wipe and initialize the program area with the single dummy line 0xFFFF
+ */
+
+void new_command(void)
+{
+//    statememt_end();
+       lines[0] = 0xFF;
+       lines[1] = 0xFF;
+       /* FIXME: endianness */
+       lines[2] = 0x01;
+       lines[3] = 0x00;
+       lines[4] = TOK_END;     /* An END marker so we always end */
+       lines[5] = 0x00;
+       lines_end = lines + 6;
+//    variables = lines_end;
+//    *variables++ = 0;
+}
+
+void cls_command(void)
+{
+       wipe();
+}
+
+void clear_command(void)
+{
+}
+
+
+/*
+ *     Wipe and initialize the program area with the single dummy line 0xFFFF
+ */
+
+void new_command(void)
+{
+//    statememt_end();
+       lines[0] = 0xFF;
+       lines[1] = 0xFF;
+       /* FIXME: endianness */
+       lines[2] = 0x01;
+       lines[3] = 0x00;
+       lines[4] = TOK_END;     /* An END marker so we always end */
+       lines[5] = 0x00;
+       lines_end = lines + 6;
+       clear_command();
+}
+
+void do_list_command(uint16_t low, uint16_t high, int must)
+{
+       char tmp[6];
+       uint8_t *p;
+       uint16_t n;
+       int c = 0;
+       uint8_t seen = 0;
+
+retry:
+       wipe();
+
+       p = find_line(low);
+
+       for (n = *(uint16_t *) p; n <= high; p += *(uint16_t *) (p + 2)) {
+               n = *(uint16_t *)p;
+               if (n == must)
+                       seen = 1;
+               /* End marker - never print */
+               if (n == 0xFFFF)
+                       break;
+               sprintf(tmp, "%5d ", n);
+               print_str(tmp);
+               print_line(p + 4);
+               print_ch('\n');
+               if (must && cursor_y == height - 5) {
+                       if (seen)
+                               return;
+                       editbase += (must - editbase) / 2;
+                       low = editbase;
+                       goto retry;
+               }
+       }
+}
+
+void editor_update(void)
+{
+       wipe();
+       do_list_command(editbase, 65534, newest);
+}
+
+void list_command(void)
+{
+       do_list_command(0,65534,0);
+}
+
+void run_command(void)
+{
+       find_exec_line(0);
+       execute_line_content(execp);
+}
+
+void go_statement(void) {
+       uint8_t *c = *execp++;
+       if (c == TOK_SUB)
+               stack_frame();
+       else if (c != TOK_TO) {
+               error(SYNTAX_ERROR);
+               return;
+       }
+       if (int_expr(&l) == 0)
+               return;
+       find_exec_line(l);
+}
+
+void let_statement(void) {
+       struct variable v;
+       variable_name(&v);
+       if (*c != '=')
+               error(SYNTAX_ERROR);
+       else
+               variable_assign(v, expression());
+}
+
+void if_statement(void) {
+       uint8_t b = boolean_expression();
+       if (*execp++ != TOK_THEN)
+               error(SYNTAX_ERROR);
+       if (b == 0 && run_line)
+               find_exec_line(run_line + 1);
+       else
+               no_colon = 1;
+}
+       
+void return_command(void)
+{
+       if (unstack_frame() == 0)
+               error(RETURN_UNDERFLOW);
+}
+
+void clear_command(void)
+{
+       /* TODO */
+}
+
+void run_command(void)
+{
+       clear_command();
+       find_exec_line(0);
+       execute_line_content(execp);
+}
+
+void do_print_input(uint8_t in)
+{
+       uint8_t need_punc = 0;
+       while(ch = *execp++) {
+               if (c == '"' && !need_punc) {
+                       while((c = *execp++) != '"') {
+                               if (c == 0) {
+                                       error(MISSING_QUOTE);
+                                       return;
+                               }
+                               print_ch(c);
+                       }
+                       need_punc = 1;
+               }
+               /* TODO: AT TAB and SPC(x) */
+               if (c == ',') {
+                       print_ch('\t');
+                       need_punc = 0;
+                       continue;
+               }
+               if (c == ';') {
+                       need_punc  0;
+                       continue;
+               }
+               if (need_punc)
+                       error(SYNTAX_ERROR);
+               if (in) {
+                       struct variable v;
+                       variable_name(&v);
+                       do_input(&v);
+               } else
+                       print_expression();
+       }
+}
+       
+void execute_statement(void)
+{
+       switch(*execp++) {
+       case '?':
+       case TOK_PRINT:
+               print_command();
+               break;
+       case TOK_IF:
+               if_command();
+               break;
+       case TOK_GO:
+               go_command();
+               break;
+       case TOK_LET:
+               let_command();
+               break;
+       case TOK_INPUT:
+               input_command();
+               break;
+       case TOK_RETURN:
+               return_command();
+               break;
+       case TOK_CLEAR:
+               clear_command();
+               break;
+       case TOK_LIST:
+               list_command();
+               break;
+       case TOK_RUN:
+               run_command();
+               break;
+       case TOK_END:
+               running = 0;
+               return;
+       default:
+               execp--;
+               let_command();
+               break;
+       }
+}
+
+void execute_line_content(uint8_t *l)
+{
+       uint8_t c;
+       execp = l;
+       running = 1;
+       do {
+               execute_statement();
+               if (!running)
+                       return;
+               if (no_colon)
+                       continue;
+               if(pending_go) {
+                       find_exec_line(go_target);
+                       continue;
+               }
+               c = *execp++;
+               if (c == 0)
+                       return;
+       } while(c == ':');
+       error(SYNTAX_ERROR);
+}
+
+void parse_line(uint8_t * l, int s)
+{
+       uint16_t n;
+       if (isdigit(*l)) {
+               uint8_t *dp = linenumber(l, &n);
+               if (l == NULL)
+                       return;
+               newest = n;
+               insdel_line(n, dp, s - (dp - l));
+               editor_update();
+               printat(height-4, 0);
+               return;
+       }
+       run_line = 0;
+       execute_line_content(l);
+}
+
+int main(int argc, char *argv[])
+{
+       char buf[256];
+       uint8_t o[256];
+       uint8_t *p;
+
+       cls_command();
+       printf("Fuzix Basic 0.1\n");
+       printf("%d bytes free.\n", lines_limit - lines);
+
+       new_command();
+
+       while (1) {
+               fgets(buf, 256, stdin);
+               outptr = o;
+               tokenize_line(buf);
+#if 0          
+               p = o;
+               while (p < outptr) {
+                       if (*p > 31 && *p < 127)
+                               putchar(*p);
+                       else
+                               printf("[%02X]", *p);
+                       p++;
+               }
+               printf("\n");
+               print_line(o);
+               printf("\n");
+#endif         
+               parse_line(o, outptr - o);
+       }
+}
diff --git a/Applications/basic/tokens.h b/Applications/basic/tokens.h
new file mode 100644 (file)
index 0000000..0c10ee7
--- /dev/null
@@ -0,0 +1,25 @@
+#define TOKEN_BASE 192
+       'P','R','I','N',0xD4,
+#define TOK_PRINT 192
+       'I',0xC6,
+#define TOK_IF 193
+       'G',0xCF,
+#define TOK_GO 194
+       'T',0xCF,
+#define TOK_TO 195
+       'S','U',0xC2,
+#define TOK_SUB 196
+       'L','E',0xD4,
+#define TOK_LET 197
+       'I','N','P','U',0xD4,
+#define TOK_INPUT 198
+       'R','E','T','U','R',0xCE,
+#define TOK_RETURN 199
+       'C','L','E','A',0xD2,
+#define TOK_CLEAR 200
+       'L','I','S',0xD4,
+#define TOK_LIST 201
+       'R','U',0xCE,
+#define TOK_RUN 202
+       'E','N',0xC4,
+#define TOK_END 203