basic.leg 9.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362
  1. # A 'syntax-directed interpreter' (all execution is a side-effect of parsing).
  2. # Inspired by Dennis Allison's original Tiny BASIC grammar, circa 1975.
  3. #
  4. # Copyright (c) 2007 by Ian Piumarta
  5. # All rights reserved.
  6. #
  7. # Permission is hereby granted, free of charge, to any person obtaining a
  8. # copy of this software and associated documentation files (the 'Software'),
  9. # to deal in the Software without restriction, including without limitation
  10. # the rights to use, copy, modify, merge, publish, distribute, and/or sell
  11. # copies of the Software, and to permit persons to whom the Software is
  12. # furnished to do so, provided that the above copyright notice(s) and this
  13. # permission notice appear in all copies of the Software. Acknowledgement
  14. # of the use of this Software in supporting documentation would be
  15. # appreciated but is not required.
  16. #
  17. # THE SOFTWARE IS PROVIDED 'AS IS'. USE ENTIRELY AT YOUR OWN RISK.
  18. #
  19. # Last edited: 2012-04-29 15:14:06 by piumarta on emilia
  20. %{
  21. # include <stdio.h>
  22. typedef struct line line;
  23. struct line
  24. {
  25. int number;
  26. int length;
  27. char *text;
  28. };
  29. line *lines= 0;
  30. int numLines= 0;
  31. int pc= -1, epc= -1;
  32. int batch= 0;
  33. int nextline(char *buf, int max);
  34. # define min(x, y) ((x) < (y) ? (x) : (y))
  35. # define YY_INPUT(buf, result, max_size) \
  36. { \
  37. if ((pc >= 0) && (pc < numLines)) \
  38. { \
  39. line *linep= lines+pc++; \
  40. result= min(max_size, linep->length); \
  41. memcpy(buf, linep->text, result); \
  42. } \
  43. else \
  44. result= nextline(buf, max_size); \
  45. }
  46. union value {
  47. int number;
  48. char *string;
  49. int (*binop)(int lhs, int rhs);
  50. };
  51. # define YYSTYPE union value
  52. int variables[26];
  53. void accept(int number, char *line);
  54. void save(char *name);
  55. void load(char *name);
  56. void type(char *name);
  57. int lessThan(int lhs, int rhs) { return lhs < rhs; }
  58. int lessEqual(int lhs, int rhs) { return lhs <= rhs; }
  59. int notEqual(int lhs, int rhs) { return lhs != rhs; }
  60. int equalTo(int lhs, int rhs) { return lhs == rhs; }
  61. int greaterEqual(int lhs, int rhs) { return lhs >= rhs; }
  62. int greaterThan(int lhs, int rhs) { return lhs > rhs; }
  63. int input(void);
  64. int stack[1024], sp= 0;
  65. char *help;
  66. void error(char *fmt, ...);
  67. int findLine(int n, int create);
  68. %}
  69. line = - s:statement CR
  70. | - n:number < ( !CR . )* CR > { accept(n.number, yytext); }
  71. | - CR
  72. | - < ( !CR . )* CR > { epc= pc; error("syntax error"); }
  73. | - !. { exit(0); }
  74. statement = 'print'- expr-list
  75. | 'if'- e1:expression r:relop e2:expression { if (!r.binop(e1.number, e2.number)) yythunkpos= 0; }
  76. 'then'- statement
  77. | 'goto'- e:expression { epc= pc; if ((pc= findLine(e.number, 0)) < 0) error("no such line"); }
  78. | 'input'- var-list
  79. | 'let'- v:var EQUAL e:expression { variables[v.number]= e.number; }
  80. | 'gosub'- e:expression { epc= pc; if (sp < 1024) stack[sp++]= pc, pc= findLine(e.number, 0); else error("too many gosubs");
  81. if (pc < 0) error("no such line"); }
  82. | 'return'- { epc= pc; if ((pc= sp ? stack[--sp] : -1) < 0) error("no gosub"); }
  83. | 'clear'- { while (numLines) accept(lines->number, "\n"); }
  84. | 'list'- { int i; for (i= 0; i < numLines; ++i) printf("%5d %s", lines[i].number, lines[i].text); }
  85. | 'run'- s:string { load(s.string); pc= 0; }
  86. | 'run'- { pc= 0; }
  87. | 'end'- { pc= -1; if (batch) exit(0); }
  88. | 'rem'- ( !CR . )*
  89. | ('bye'|'quit'|'exit')- { exit(0); }
  90. | 'save'- s:string { save(s.string); }
  91. | 'load'- s:string { load(s.string); }
  92. | 'type'- s:string { type(s.string); }
  93. | 'dir'- { system("ls *.bas"); }
  94. | 'help'- { fprintf(stderr, "%s", help); }
  95. expr-list = ( e:string { printf("%s", e.string); }
  96. | e:expression { printf("%d", e.number); }
  97. )? ( COMMA ( e:string { printf("%s", e.string); }
  98. | e:expression { printf("%d", e.number); }
  99. )
  100. )* ( COMMA
  101. | !COMMA { printf("\n"); }
  102. )
  103. var-list = v:var { variables[v.number]= input(); }
  104. ( COMMA v:var { variables[v.number]= input(); }
  105. )*
  106. expression = ( PLUS? l:term
  107. | MINUS l:term { l.number = -l.number }
  108. ) ( PLUS r:term { l.number += r.number }
  109. | MINUS r:term { l.number -= r.number }
  110. )* { $$.number = l.number }
  111. term = l:factor ( STAR r:factor { l.number *= r.number }
  112. | SLASH r:factor { l.number /= r.number }
  113. )* { $$.number = l.number }
  114. factor = v:var { $$.number = variables[v.number] }
  115. | n:number
  116. | OPEN expression CLOSE
  117. var = < [a-z] > - { $$.number = yytext[0] - 'a' }
  118. number = < digit+ > - { $$.number = atoi(yytext); }
  119. digit = [0-9]
  120. string = '"' < [^\"]* > '"' - { $$.string = yytext; }
  121. relop = '<=' - { $$.binop= lessEqual; }
  122. | '<>' - { $$.binop= notEqual; }
  123. | '<' - { $$.binop= lessThan; }
  124. | '>=' - { $$.binop= greaterEqual; }
  125. | '>' - { $$.binop= greaterThan; }
  126. | '=' - { $$.binop= equalTo; }
  127. EQUAL = '=' - CLOSE = ')' - OPEN = '(' -
  128. SLASH = '/' - STAR = '*' - MINUS = '-' -
  129. PLUS = '+' - COMMA = ',' -
  130. - = [ \t]*
  131. CR = '\n' | '\r' | '\r\n'
  132. %%
  133. #include <unistd.h>
  134. #include <stdarg.h>
  135. char *help=
  136. "print <num>|<string> [, <num>|<string> ...] [,]\n"
  137. "if <expr> <|<=|<>|=|>=|> <expr> then <stmt>\n"
  138. "input <var> [, <var> ...] let <var> = <expr>\n"
  139. "goto <expr> gosub <expr>\n"
  140. "end return\n"
  141. "list clear\n"
  142. "run [\"filename\"] rem <comment...>\n"
  143. "dir type \"filename\"\n"
  144. "save \"filename\" load \"filename\"\n"
  145. "bye|quit|exit help\n"
  146. ;
  147. void error(char *fmt, ...)
  148. {
  149. va_list ap;
  150. va_start(ap, fmt);
  151. if (epc > 0)
  152. fprintf(stderr, "\nline %d: %s", lines[epc-1].number, lines[epc-1].text);
  153. else
  154. fprintf(stderr, "\n");
  155. vfprintf(stderr, fmt, ap);
  156. fprintf(stderr, "\n");
  157. va_end(ap);
  158. epc= pc= -1;
  159. }
  160. #ifdef USE_READLINE
  161. # include <readline/readline.h>
  162. # include <readline/history.h>
  163. #endif
  164. int nextline(char *buf, int max)
  165. {
  166. pc= -1;
  167. if (batch) exit(0);
  168. if (isatty(fileno(stdin)))
  169. {
  170. # ifdef USE_READLINE
  171. char *line= readline(">");
  172. if (line)
  173. {
  174. int len= strlen(line);
  175. if (len >= max) len= max - 1;
  176. strncpy(buf, line, len);
  177. (buf)[len]= '\n';
  178. add_history(line);
  179. free(line);
  180. return len + 1;
  181. }
  182. else
  183. {
  184. printf("\n");
  185. return 0;
  186. }
  187. # endif
  188. putchar('>');
  189. fflush(stdout);
  190. }
  191. return fgets(buf, max, stdin) ? strlen(buf) : 0;
  192. }
  193. int maxLines= 0;
  194. int findLine(int n, int create)
  195. {
  196. int lo= 0, hi= numLines - 1;
  197. while (lo <= hi)
  198. {
  199. int mid= (lo + hi) / 2, lno= lines[mid].number;
  200. if (lno > n)
  201. hi= mid - 1;
  202. else if (lno < n)
  203. lo= mid + 1;
  204. else
  205. return mid;
  206. }
  207. if (create)
  208. {
  209. if (numLines == maxLines)
  210. {
  211. maxLines *= 2;
  212. lines= realloc(lines, sizeof(line) * maxLines);
  213. }
  214. if (lo < numLines)
  215. memmove(lines + lo + 1, lines + lo, sizeof(line) * (numLines - lo));
  216. ++numLines;
  217. lines[lo].number= n;
  218. lines[lo].text= 0;
  219. return lo;
  220. }
  221. return -1;
  222. }
  223. void accept(int n, char *s)
  224. {
  225. if (s[0] < 32) /* delete */
  226. {
  227. int lno= findLine(n, 0);
  228. if (lno >= 0)
  229. {
  230. if (lno < numLines - 1)
  231. memmove(lines + lno, lines + lno + 1, sizeof(line) * (numLines - lno - 1));
  232. --numLines;
  233. }
  234. }
  235. else /* insert */
  236. {
  237. int lno= findLine(n, 1);
  238. if (lines[lno].text) free(lines[lno].text);
  239. lines[lno].length= strlen(s);
  240. lines[lno].text= strdup(s);
  241. }
  242. }
  243. char *extend(char *name)
  244. {
  245. static char path[1024];
  246. int len= strlen(name);
  247. sprintf(path, "%s%s", name, (((len > 4) && !strcasecmp(".bas", name + len - 4)) ? "" : ".bas"));
  248. return path;
  249. }
  250. void save(char *name)
  251. {
  252. FILE *f= fopen(name= extend(name), "w");
  253. if (!f)
  254. perror(name);
  255. else
  256. {
  257. int i;
  258. for (i= 0; i < numLines; ++i)
  259. fprintf(f, "%d %s", lines[i].number, lines[i].text);
  260. fclose(f);
  261. }
  262. }
  263. void load(char *name)
  264. {
  265. FILE *f= fopen(name= extend(name), "r");
  266. if (!f)
  267. perror(name);
  268. else
  269. {
  270. int lineNumber;
  271. char lineText[1024];
  272. while ((1 == fscanf(f, " %d ", &lineNumber)) && fgets(lineText, sizeof(lineText), f))
  273. accept(lineNumber, lineText);
  274. fclose(f);
  275. }
  276. }
  277. void type(char *name)
  278. {
  279. FILE *f= fopen(name= extend(name), "r");
  280. if (!f)
  281. perror(name);
  282. else
  283. {
  284. int c, d;
  285. while ((c= getc(f)) >= 0)
  286. putchar(d= c);
  287. fclose(f);
  288. if ('\n' != d && '\r' != d) putchar('\n');
  289. }
  290. }
  291. int input(void)
  292. {
  293. char line[32];
  294. fgets(line, sizeof(line), stdin);
  295. return atoi(line);
  296. }
  297. int main(int argc, char **argv)
  298. {
  299. lines= malloc(sizeof(line) * (maxLines= 32));
  300. numLines= 0;
  301. if (argc > 1)
  302. {
  303. batch= 1;
  304. while (argc-- > 1)
  305. load(*++argv);
  306. pc= 0;
  307. }
  308. while (!feof(stdin))
  309. yyparse();
  310. return 0;
  311. }