123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362 |
- # A 'syntax-directed interpreter' (all execution is a side-effect of parsing).
- # Inspired by Dennis Allison's original Tiny BASIC grammar, circa 1975.
- #
- # Copyright (c) 2007 by Ian Piumarta
- # All rights reserved.
- #
- # Permission is hereby granted, free of charge, to any person obtaining a
- # copy of this software and associated documentation files (the 'Software'),
- # to deal in the Software without restriction, including without limitation
- # the rights to use, copy, modify, merge, publish, distribute, and/or sell
- # copies of the Software, and to permit persons to whom the Software is
- # furnished to do so, provided that the above copyright notice(s) and this
- # permission notice appear in all copies of the Software. Acknowledgement
- # of the use of this Software in supporting documentation would be
- # appreciated but is not required.
- #
- # THE SOFTWARE IS PROVIDED 'AS IS'. USE ENTIRELY AT YOUR OWN RISK.
- #
- # Last edited: 2012-04-29 15:14:06 by piumarta on emilia
- %{
- # include <stdio.h>
- typedef struct line line;
- struct line
- {
- int number;
- int length;
- char *text;
- };
- line *lines= 0;
- int numLines= 0;
- int pc= -1, epc= -1;
- int batch= 0;
- int nextline(char *buf, int max);
- # define min(x, y) ((x) < (y) ? (x) : (y))
- # define YY_INPUT(buf, result, max_size) \
- { \
- if ((pc >= 0) && (pc < numLines)) \
- { \
- line *linep= lines+pc++; \
- result= min(max_size, linep->length); \
- memcpy(buf, linep->text, result); \
- } \
- else \
- result= nextline(buf, max_size); \
- }
- union value {
- int number;
- char *string;
- int (*binop)(int lhs, int rhs);
- };
- # define YYSTYPE union value
- int variables[26];
- void accept(int number, char *line);
- void save(char *name);
- void load(char *name);
- void type(char *name);
- int lessThan(int lhs, int rhs) { return lhs < rhs; }
- int lessEqual(int lhs, int rhs) { return lhs <= rhs; }
- int notEqual(int lhs, int rhs) { return lhs != rhs; }
- int equalTo(int lhs, int rhs) { return lhs == rhs; }
- int greaterEqual(int lhs, int rhs) { return lhs >= rhs; }
- int greaterThan(int lhs, int rhs) { return lhs > rhs; }
- int input(void);
- int stack[1024], sp= 0;
- char *help;
- void error(char *fmt, ...);
- int findLine(int n, int create);
- %}
- line = - s:statement CR
- | - n:number < ( !CR . )* CR > { accept(n.number, yytext); }
- | - CR
- | - < ( !CR . )* CR > { epc= pc; error("syntax error"); }
- | - !. { exit(0); }
- statement = 'print'- expr-list
- | 'if'- e1:expression r:relop e2:expression { if (!r.binop(e1.number, e2.number)) yythunkpos= 0; }
- 'then'- statement
- | 'goto'- e:expression { epc= pc; if ((pc= findLine(e.number, 0)) < 0) error("no such line"); }
- | 'input'- var-list
- | 'let'- v:var EQUAL e:expression { variables[v.number]= e.number; }
- | 'gosub'- e:expression { epc= pc; if (sp < 1024) stack[sp++]= pc, pc= findLine(e.number, 0); else error("too many gosubs");
- if (pc < 0) error("no such line"); }
- | 'return'- { epc= pc; if ((pc= sp ? stack[--sp] : -1) < 0) error("no gosub"); }
- | 'clear'- { while (numLines) accept(lines->number, "\n"); }
- | 'list'- { int i; for (i= 0; i < numLines; ++i) printf("%5d %s", lines[i].number, lines[i].text); }
- | 'run'- s:string { load(s.string); pc= 0; }
- | 'run'- { pc= 0; }
- | 'end'- { pc= -1; if (batch) exit(0); }
- | 'rem'- ( !CR . )*
- | ('bye'|'quit'|'exit')- { exit(0); }
- | 'save'- s:string { save(s.string); }
- | 'load'- s:string { load(s.string); }
- | 'type'- s:string { type(s.string); }
- | 'dir'- { system("ls *.bas"); }
- | 'help'- { fprintf(stderr, "%s", help); }
- expr-list = ( e:string { printf("%s", e.string); }
- | e:expression { printf("%d", e.number); }
- )? ( COMMA ( e:string { printf("%s", e.string); }
- | e:expression { printf("%d", e.number); }
- )
- )* ( COMMA
- | !COMMA { printf("\n"); }
- )
- var-list = v:var { variables[v.number]= input(); }
- ( COMMA v:var { variables[v.number]= input(); }
- )*
- expression = ( PLUS? l:term
- | MINUS l:term { l.number = -l.number }
- ) ( PLUS r:term { l.number += r.number }
- | MINUS r:term { l.number -= r.number }
- )* { $$.number = l.number }
- term = l:factor ( STAR r:factor { l.number *= r.number }
- | SLASH r:factor { l.number /= r.number }
- )* { $$.number = l.number }
- factor = v:var { $$.number = variables[v.number] }
- | n:number
- | OPEN expression CLOSE
- var = < [a-z] > - { $$.number = yytext[0] - 'a' }
- number = < digit+ > - { $$.number = atoi(yytext); }
- digit = [0-9]
- string = '"' < [^\"]* > '"' - { $$.string = yytext; }
- relop = '<=' - { $$.binop= lessEqual; }
- | '<>' - { $$.binop= notEqual; }
- | '<' - { $$.binop= lessThan; }
- | '>=' - { $$.binop= greaterEqual; }
- | '>' - { $$.binop= greaterThan; }
- | '=' - { $$.binop= equalTo; }
- EQUAL = '=' - CLOSE = ')' - OPEN = '(' -
- SLASH = '/' - STAR = '*' - MINUS = '-' -
- PLUS = '+' - COMMA = ',' -
- - = [ \t]*
- CR = '\n' | '\r' | '\r\n'
- %%
- #include <unistd.h>
- #include <stdarg.h>
- char *help=
- "print <num>|<string> [, <num>|<string> ...] [,]\n"
- "if <expr> <|<=|<>|=|>=|> <expr> then <stmt>\n"
- "input <var> [, <var> ...] let <var> = <expr>\n"
- "goto <expr> gosub <expr>\n"
- "end return\n"
- "list clear\n"
- "run [\"filename\"] rem <comment...>\n"
- "dir type \"filename\"\n"
- "save \"filename\" load \"filename\"\n"
- "bye|quit|exit help\n"
- ;
- void error(char *fmt, ...)
- {
- va_list ap;
- va_start(ap, fmt);
- if (epc > 0)
- fprintf(stderr, "\nline %d: %s", lines[epc-1].number, lines[epc-1].text);
- else
- fprintf(stderr, "\n");
- vfprintf(stderr, fmt, ap);
- fprintf(stderr, "\n");
- va_end(ap);
- epc= pc= -1;
- }
- #ifdef USE_READLINE
- # include <readline/readline.h>
- # include <readline/history.h>
- #endif
- int nextline(char *buf, int max)
- {
- pc= -1;
- if (batch) exit(0);
- if (isatty(fileno(stdin)))
- {
- # ifdef USE_READLINE
- char *line= readline(">");
- if (line)
- {
- int len= strlen(line);
- if (len >= max) len= max - 1;
- strncpy(buf, line, len);
- (buf)[len]= '\n';
- add_history(line);
- free(line);
- return len + 1;
- }
- else
- {
- printf("\n");
- return 0;
- }
- # endif
- putchar('>');
- fflush(stdout);
- }
- return fgets(buf, max, stdin) ? strlen(buf) : 0;
- }
- int maxLines= 0;
- int findLine(int n, int create)
- {
- int lo= 0, hi= numLines - 1;
- while (lo <= hi)
- {
- int mid= (lo + hi) / 2, lno= lines[mid].number;
- if (lno > n)
- hi= mid - 1;
- else if (lno < n)
- lo= mid + 1;
- else
- return mid;
- }
- if (create)
- {
- if (numLines == maxLines)
- {
- maxLines *= 2;
- lines= realloc(lines, sizeof(line) * maxLines);
- }
- if (lo < numLines)
- memmove(lines + lo + 1, lines + lo, sizeof(line) * (numLines - lo));
- ++numLines;
- lines[lo].number= n;
- lines[lo].text= 0;
- return lo;
- }
- return -1;
- }
- void accept(int n, char *s)
- {
- if (s[0] < 32) /* delete */
- {
- int lno= findLine(n, 0);
- if (lno >= 0)
- {
- if (lno < numLines - 1)
- memmove(lines + lno, lines + lno + 1, sizeof(line) * (numLines - lno - 1));
- --numLines;
- }
- }
- else /* insert */
- {
- int lno= findLine(n, 1);
- if (lines[lno].text) free(lines[lno].text);
- lines[lno].length= strlen(s);
- lines[lno].text= strdup(s);
- }
- }
- char *extend(char *name)
- {
- static char path[1024];
- int len= strlen(name);
- sprintf(path, "%s%s", name, (((len > 4) && !strcasecmp(".bas", name + len - 4)) ? "" : ".bas"));
- return path;
- }
- void save(char *name)
- {
- FILE *f= fopen(name= extend(name), "w");
- if (!f)
- perror(name);
- else
- {
- int i;
- for (i= 0; i < numLines; ++i)
- fprintf(f, "%d %s", lines[i].number, lines[i].text);
- fclose(f);
- }
- }
- void load(char *name)
- {
- FILE *f= fopen(name= extend(name), "r");
- if (!f)
- perror(name);
- else
- {
- int lineNumber;
- char lineText[1024];
- while ((1 == fscanf(f, " %d ", &lineNumber)) && fgets(lineText, sizeof(lineText), f))
- accept(lineNumber, lineText);
- fclose(f);
- }
- }
- void type(char *name)
- {
- FILE *f= fopen(name= extend(name), "r");
- if (!f)
- perror(name);
- else
- {
- int c, d;
- while ((c= getc(f)) >= 0)
- putchar(d= c);
- fclose(f);
- if ('\n' != d && '\r' != d) putchar('\n');
- }
- }
- int input(void)
- {
- char line[32];
- fgets(line, sizeof(line), stdin);
- return atoi(line);
- }
- int main(int argc, char **argv)
- {
- lines= malloc(sizeof(line) * (maxLines= 32));
- numLines= 0;
- if (argc > 1)
- {
- batch= 1;
- while (argc-- > 1)
- load(*++argv);
- pc= 0;
- }
- while (!feof(stdin))
- yyparse();
- return 0;
- }
|