361 lines · 9.1 KB · Text
RawDownload
# 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;}