RetroShare/supportlibs/pegmarkdown/peg-0.1.9/examples/basic.leg
2012-12-09 12:12:57 +00:00

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