# 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;}
# 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;
}