mirror of
https://github.com/RetroShare/RetroShare.git
synced 2024-12-29 09:26:18 -05:00
362 lines
9.1 KiB
Plaintext
362 lines
9.1 KiB
Plaintext
|
# 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;
|
||
|
}
|