view tests/agcl/contrib/yabasic.syn @ 21:1c9dac05d040

Add lint-style FALLTHROUGH annotations to fallthrough cases. (in the parse engine and thus the output code) Document this, because the old output causes warnings with gcc10.
author David A. Holland
date Mon, 13 Jun 2022 00:04:38 -0400
parents 13d2b8934445
children
line wrap: on
line source

{

/*
     YABASIC --- a tiny integrated Basic Compiler/Interpreter

     BISON - part

     this Program is subject to the GNU General Public License;
     see the file yabasic.c for details.
*/


//#undef WINDOWS
#include "yabasic.h"     /* definitions of yabasic */
#include <malloc.h>

#if HAVE_ALLOCA_H
#include <alloca.h>
#endif

void __yy_bcopy(char *,char *,int); /* prototype missing */

int yylineno=1;
int yylex(void);

char *popString(int);
void pushChar(int);

}

[
  ~case sensitive
  disregard white space
  lexeme {digit string, name, STRING, STRSYM, number}
  distinguish lexemes
  parser name = parseBasic
  line numbers
  escape backslashes
]

white space = ' ' + '\t'

//(double *) step_part


SEP
 -> ["REM", ~(eof + white space + '\n')?...], '\n'      ={
     yylineno++;
     if (interactive) PCB.exit_flag = AG_SUCCESS_CODE;
     return;
 }

(double) number
 -> simple real
 -> simple real:x, 'e'+'E', '+'?,exponent:e            =x*pow(10,e);
 -> simple real:x, 'e'+'E', '-',exponent:e            =x*pow(10,-e);

(double) simple real
 -> integer part:i, '.', fraction part:f                      = i+f;
 -> integer part, '.'?
 -> '.', fraction part:f                                        = f;

(double) integer part
 -> digit:d                                                 = d-'0';
 -> integer part:x, digit:d                          = 10*x + d-'0';

(double) fraction part
 -> digit:d                                            =(d-'0')/10.;
 -> digit:d, fraction part:f                       =(d-'0' + f)/10.;

(int) exponent
 -> digit:d                                                 = d-'0';
 -> exponent:x, digit:d                              = 10*x + d-'0';

(int) name           //value of name token is length of name string
 -> letter: c                                       =pushChar(c), 1;
 -> name:k, letter+digit: c                       =pushChar(c), k+1;


(char *) SYMBOL
  -> name:k                                   =strdup(popString(k));

(char *) STRSYM
  -> name:k, '$'                              =pushChar('$'), strdup(popString(k+1));

(char *) DIGITS
  -> digit string:k                                  =strdup(popString(k));

(int) digit string
 -> digit:d                                         =pushChar(d), 1;
 -> digit string:k, digit:d                         =pushChar(d), k+1;

(char *) STRING
 -> '"', string text:k, '"'                         =strdup(popString(k));
 -> '"', string text:k, '\n'                        =strdup(popString(k));

(int) string text
 ->                                                 =0;
 -> string text:k, ~(eof + '"' + '\n'):c            =pushChar(c), k+1;
 -> string text:k, "\\\""                           =pushChar('"'), k+1;

/*
[
  left {"OR"}
  left {"AND"}
  left {"NOT"}
  left {'-', '+'}
  left {'*', '/'}
  left {'^'}
  nonassoc {UMINUS}
]
*/

letter = 'a-z' + 'A-Z'
digit  = '0-9'
eof = 0 + -1


program $
 -> statement list, eof             =end_of_file = TRUE;

statement list
 ->
/*
 -> statement_list, !{
if (errorlevel<=ERROR) {YYABORT;}},
   SEP, !{
yylineno+=$3;},
   statement
*/
 -> statement list, SEP, statement
 -> statement list, SEP, label, statement

label
 -> "LABEL", SYMBOL:s                =create_label(s);
 -> DIGITS:s                         =create_label(s);


statement  /* empty */
 ->
 -> string_assignment
 -> assignment
 -> for_loop
 -> if_clause
 -> "GOTO", symbol_or_lineno:s ={create_goto(s);}
 -> "GOSUB", symbol_or_lineno:s ={create_gosub(s);}
 -> "ON", "INTERRUPT", "BREAK" ={create_exception(TRUE);}
 -> "ON", "INTERRUPT", "CONTINUE" ={create_exception(FALSE);}
 -> "ON", expression, "GOTO", !{create_skipper();}, goto_list ={create_nop();}
 -> "ON", expression, "GOSUB", !{create_skipper();}, gosub_list ={create_nop();}
// -> LABEL, symbol_or_lineno ={create_label($2);}
 -> "OPEN", hashed_number:n, ',', string expression, ',', string expression ={create_myopen(n,'+');}
 -> "OPEN", hashed_number:n, ',', string expression ={create_myopen(n,'-');}
 -> "CLOSE", hashed_number:n ={create_myclose(n);}
 -> "PRINT", printintro, printlist, !{
create_revert(FALSE);},
   semicolon
 -> "INPUT", inputintro, inputlist =lastinput->args=0;
 -> "READ", readlist
 -> "DATA", datalist
 -> "RESTORE" =create_restore("");
 -> "RESTORE", symbol_or_lineno:s =create_restore(s);
 -> "RETURN" ={create_return();}
 -> "DIM", dimlist
 -> "OPEN", "WINDOW", expression, ',', expression =create_openwin(FALSE);
 -> "OPEN", "WINDOW", expression, ',', expression, ',', string expression ={create_openwin(TRUE);}
 -> "DOT", mapping                    =create_dot();
 -> "LINE", mapping, "TO", mapping =create_line('l');
 -> "CIRCLE", mapping, ',', expression =create_circle();
 -> "TEXT", string expression, ',', mapping =create_text(TRUE);
 -> "TEXT", mapping, ',', string expression =create_text(FALSE);
 -> "MAP", expression, ',', expression, ',', expression, ',', expression, "TO", expression, ',', expression, ',', expression, ',', expression =create_makemap();
 -> "ARROW", mapping, "TO", mapping =create_line('a');
 -> "XTICK", mapping, ',', string expression =create_tick(1);
 -> "YTICK", mapping, ',', string expression =create_tick(3);
 -> "XTICK", mapping =create_tick(0);
 -> "YTICK", mapping =create_tick(2);
 -> "CLOSE", "WINDOW" =create_closewin();
 -> "CLEAR", "WINDOW" =create_clearwin();
 -> "CLEAR", "SCREEN" =create_clearscreen();
 -> "OPEN", "PRINTER" =create_openprinter(0);
 -> "OPEN", "PRINTER", string expression =create_openprinter(1);
 -> "CLOSE", "PRINTER" =create_closeprinter();
 -> "WAIT", expression =create_mywait();
 -> "BELL" =create_bell();
 -> "INKEY" ={create_function(MYINKEY); create_popstrsym(NULL);}
 -> "SYSTEM2", '(', string expression, ')' ={create_function(MYSYSTEM2);
	create_popdblsym(NULL);}
 -> "POKE", string expression, ',', string expression ={create_poke('s');}
 -> "POKE", string expression, ',', expression ={create_poke('d');}
 -> "END" ={create_myend();}

string_assignment
 -> STRSYM:s, '=', string expression ={create_popstrsym(s);}
 -> "MID", '(', STRSYM:s, !{
create_pushstrptr(s);},
   ',', expression, ',', expression, ')', '=', string expression ={create_changestring(MYMID);}
 -> "LEFT", '(', STRSYM:s, !{
create_pushstrptr(s);},
   ',', expression, ')', '=', string expression ={create_changestring(MYLEFT);}
 -> "RIGHT", '(', STRSYM:s, !{
create_pushstrptr(s);},
   ',', expression, ')', '=', string expression ={create_changestring(MYRIGHT);}
 -> STRSYM:s, '(', !{
pushcounter();},
   indexlist, ')', '=', string expression ={create_doarray(s,ASSIGNSTRINGARRAY);}
 -> "MID", '(', STRSYM:s, '(', !{
pushcounter();},
   indexlist, ')', !{
create_doarray(s,GETSTRINGPOINTER);},
   ',', expression, ',', expression, ')', '=', string expression ={create_changestring(MYMID);}
 -> "LEFT", '(', STRSYM:s, '(', !{
pushcounter();},
   indexlist, ')', !{
create_doarray(s,GETSTRINGPOINTER);},
   ',', expression, ')', '=', string expression ={create_changestring(MYLEFT);}
 -> "RIGHT", '(', STRSYM:s, '(', !{
pushcounter();},
   indexlist, ')', !{
create_doarray(s,GETSTRINGPOINTER);},
   ',', expression, ')', '=', string expression ={create_changestring(MYRIGHT);}

string expression
 -> primary string expression
 -> string expression, '+', primary string expression ={create_concat();}

primary string expression
 -> STRSYM:s ={create_pushstrsym(s);}
 -> string_function
 -> STRING:s ={if (s==NULL) {error(ERROR,"String not terminated");create_pushstr("");} else {create_pushstr(s);}}
 -> STRSYM:s, '(', !{
pushcounter();},
   indexlist, ')' ={create_doarray(s,CALLSTRINGARRAY);}
 -> '(', string expression, ')'

string_function
 -> "LEFT", '(', string expression, ',', expression, ')' ={create_function(MYLEFT);}
 -> "RIGHT", '(', string expression, ',', expression, ')' ={create_function(MYRIGHT);}
 -> "MID", '(', string expression, ',', expression, ',', expression, ')' ={create_function(MYMID);}
 -> "STR", '(', expression, ')' ={create_function(MYSTR);}
 -> "STR", '(', expression, ',', string expression, ')' ={create_function(MYSTR2);}
 -> "INKEY" ={create_function(MYINKEY);}
 -> "CHR", '(', expression, ')' ={create_function(MYCHR);}
 -> "UPPER", '(', string expression, ')' ={create_function(MYUPPER);}
 -> "LOWER", '(', string expression, ')' ={create_function(MYLOWER);}
 -> "LTRIM", '(', string expression, ')' ={create_function(MYLTRIM);}
 -> "RTRIM", '(', string expression, ')' ={create_function(MYRTRIM);}
 -> "TRIM", '(', string expression, ')' ={create_function(MYTRIM);}
 -> "SYSTEM", '(', string expression, ')' ={create_function(MYSYSTEM);}
 -> "DATE" ={create_function(MYDATE);}
 -> "TIME" ={create_function(MYTIME);}
 -> "PEEK2", '(', string expression, ')' ={create_function(MYPEEK2);}

assignment
 -> SYMBOL:s, '=', expression ={create_popdblsym(s);}
 -> SYMBOL:s, '(', !{
pushcounter();},
   indexlist, ')', '=', expression ={create_doarray(s,ASSIGNARRAY);}

primary expression
 -> number:n                           =create_pushdbl(n);
 -> function
 -> SYMBOL:s                           =create_pushdblsym(s);
 -> SYMBOL:s, '(', !pushcounter();,
      indexlist, ')'                   ={create_doarray(s,CALLARRAY);}
 -> '(', expression, ')'
 -> '-', primary expression /* 1ef4:fa2arec <֠ */ ={create_negate();}

exponential expression
 -> primary expression
 -> exponential expression, '^', primary expression ={create_dblbin('^');}

multiplicative expression
 -> exponential expression
 -> multiplicative expression, '*', exponential expression ={create_dblbin('*');}
 -> multiplicative expression, '/', exponential expression ={create_dblbin('/');}

expression
 -> multiplicative expression
 -> expression, '+', multiplicative expression ={create_dblbin('+');}
 -> expression, '-', multiplicative expression ={create_dblbin('-');}

/*
 -> expression, '+', expression ={create_dblbin('+');}
 -> expression, '-', expression ={create_dblbin('-');}
 -> expression, '*', expression ={create_dblbin('*');}
 -> expression, '/', expression ={create_dblbin('/');}
 -> expression, '^', expression ={create_dblbin('^');}
 -> '-', expression /* 1ef4:fa2arec <֠ * / ={create_negate();}
*/

mapping
 -> expression, ',', expression
 -> "MAP", '(', expression, ',', expression, ')' ={create_map();}

function
 -> "SIN", '(', expression, ')' ={create_function(MYSIN);}
 -> "ASIN", '(', expression, ')' ={create_function(MYASIN);}
 -> "COS", '(', expression, ')' ={create_function(MYCOS);}
 -> "ACOS", '(', expression, ')' ={create_function(MYACOS);}
 -> "TAN", '(', expression, ')' ={create_function(MYTAN);}
 -> "ATAN", '(', expression, ')' ={create_function(MYATAN);}
 -> "ATAN", '(', expression, ',', expression, ')' ={create_function(MYATAN2);}
 -> "EXP", '(', expression, ')' ={create_function(MYEXP);}
 -> "LOG", '(', expression, ')' ={create_function(MYLOG);}
 -> "SQRT", '(', expression, ')' ={create_function(MYSQRT);}
 -> "INT", '(', expression, ')' ={create_function(MYINT);}
 -> "FRAC", '(', expression, ')' ={create_function(MYFRAC);}
 -> "MOD", '(', expression, ',', expression, ')' ={create_function(MYMOD);}
 -> "RAN", '(', expression, ')' ={create_function(MYRAN);}
 -> "RAN", '(', ')' ={create_function(MYRAN2);}
 -> "MIN", '(', expression, ',', expression, ')' ={create_function(MYMIN);}
 -> "MAX", '(', expression, ',', expression, ')' ={create_function(MYMAX);}
 -> "XMAP", '(', expression, ')' ={create_function(MYXMAP);}
 -> "YMAP", '(', expression, ')' ={create_function(MYYMAP);}
 -> "LEN", '(', string expression, ')' ={create_function(MYLEN);}
 -> "VAL", '(', string expression, ')' ={create_function(MYVAL);}
 -> "ASC", '(', string expression, ')' ={create_function(MYASC);}
 -> "INSTR", '(', string expression, ',', string expression, ')' ={create_function(MYINSTR);}
 -> "SYSTEM2", '(', string expression, ')' ={create_function(MYSYSTEM2);}
 -> "PEEK", '(', string expression, ')' ={create_function(MYPEEK);}

(double) const
 -> number:n =n;
 -> '+', number:n =n;
 -> '-', number:n =-n;

/*
number
 -> FNUM ={$$=$1;}
 -> DIGITS ={$$=atoi($1);}
*/

(int) intnum
 -> DIGITS:d                            =atoi(d);

(char *) symbol_or_lineno
 -> DIGITS:s =s;
 -> SYMBOL:s =s;

dimlist
 -> SYMBOL:s, '(', !{
pushcounter();},
   indexlist, ')' ={create_dim(s,'d');}
 -> dimlist, ',', SYMBOL:s, '(', !{
pushcounter();},
   indexlist, ')' ={create_dim(s,'d');}
 -> STRSYM:s, '(', !{
pushcounter();},
   indexlist, ')' ={create_dim(s,'s');}
 -> dimlist, ',', STRSYM:s, '(', !{
pushcounter();},
   indexlist, ')' ={create_dim(s,'s');}

indexlist
 -> expression ={inccounter();}
 -> indexlist, ',', expression ={inccounter();}

for_loop
 -> "FOR", SYMBOL:s, '=', expression, !{
pushname(s);create_popdblsym(s);pushgoto();
             create_pushdblsym(s);},
   "TO", expression, step_part:p, !{

       create_dblrelop((p>0)?'{':'}');
             create_decide();
             pushlabel();},
/*
   SEP, !{
yylineno+=$10;},
*/ SEP,
   statement list, !{
             create_pushdbl(p);
       create_pushdblsym(s);
             create_dblbin('+');
       create_popdblsym(s);
             swap();popgoto();poplabel();},
   next_or_eofile, next_symbol                    ={/* cookie*/}

next_or_eofile
 -> "NEXT"
 -> eof ={end_of_file=TRUE;
      error(ERROR,"'next'-statement is missing"); PCB.exit_flag = AG_SYNTAX_ERROR_CODE;}

(double) step_part
 ->  /* can be omitted */ =1.0;
 -> "STEP", const:v =v;

next_symbol
 -> /* can be omitted */ ={pop();}
 -> SYMBOL:s ={if (strcmp(pop()->pointer,s))
             {error(ERROR,"'for' and 'next' do not match"); PCB.exit_flag = AG_SYNTAX_ERROR_CODE;}
           }

if_clause
 -> "IF", condition, !{
create_decide();pushlabel();},
   "THEN", statement list, !{
pushlabel();swap();poplabel();},
   else_part, !{
poplabel();},
   endif_or_eof

endif_or_eof
 -> "ENDIF"
 -> eof ={end_of_file=TRUE;
            error(ERROR,"'endif'-statement is missing"); PCB.exit_flag = AG_SYNTAX_ERROR_CODE;}
/*
condition
 -> '(', condition, ')'
 -> condition, "OR", condition ={create_boole('|');}
 -> "NOT", condition ={create_boole('!');}
*/

condition
 -> and condition
 -> condition, "OR", and condition ={create_boole('|');}


and condition
 -> primary condition
 -> and condition, "AND", primary condition ={create_boole('&');}

primary condition
 -> comparison
 -> '(', condition, ')'
 -> "NOT", primary condition

comparison
 -> string expression, '=', string expression ={create_strrelop('=');}
 -> string expression, "<>", string expression ={create_strrelop('!');}
 -> string expression, '<', string expression ={create_strrelop('<');}
 -> string expression, "<=", string expression ={create_strrelop('{');}
 -> string expression, '>', string expression ={create_strrelop('>');}
 -> string expression, ">=", string expression ={create_strrelop('}');}
 -> expression, '=', expression ={create_dblrelop('=');}
 -> expression, "<>", expression ={create_dblrelop('!');}
 -> expression, '<', expression ={create_dblrelop('<');}
 -> expression, "<=", expression ={create_dblrelop('{');}
 -> expression, '>', expression ={create_dblrelop('>');}
 -> expression, ">=", expression ={create_dblrelop('}');}
 //-> MYEOF, '(', hashed_number, ')' ={create_testeof($3);}

else_part /* can be omitted */
 ->
 -> "ELSE", statement list

inputlist
 -> input
 -> input, ',', inputlist

input
 -> SYMBOL:s ={create_myread('d');create_popdblsym(s);}
 -> SYMBOL:s, '(', !{
pushcounter();},
   indexlist, ')' ={create_myread('d');create_doarray(s,ASSIGNARRAY);}
 -> STRSYM:s ={create_myread('s');create_popstrsym(s);}
 -> STRSYM:s, '(', !{
pushcounter();},
   indexlist, ')' ={create_myread('s');create_doarray(s,ASSIGNSTRINGARRAY);}

readlist
 -> readitem
 -> readlist, ',', readitem

readitem
 -> SYMBOL:s ={create_readdata('d');create_popdblsym(s);}
 -> SYMBOL:s, '(', !{
pushcounter();},
   indexlist, ')' ={create_readdata('d');create_doarray(s,ASSIGNARRAY);}
 -> STRSYM:s ={create_readdata('s');create_popstrsym(s);}
 -> STRSYM:s, '(', !{
pushcounter();},
   indexlist, ')' ={create_readdata('s');create_doarray(s,ASSIGNSTRINGARRAY);}

datalist
 -> STRING:s ={create_strdata(s);}
 -> const:v ={create_dbldata(v);}
 -> datalist, ',', STRING:s ={create_strdata(s);}
 -> datalist, ',', const:v ={create_dbldata(v);}

printlist  /* possible empty */
 ->
 -> expression ={create_print('d');}
 -> printlist, ',', expression ={create_print('d');}
 -> string expression ={create_print('s');}
 -> printlist, ',', string expression ={create_print('s');}

inputintro
 ->  ={create_myswitch(0);create_readline(NULL);}
 -> stream
 -> position ={create_myswitch(0);create_readline(NULL);}
 -> !{
create_myswitch(0);},
   prompt
 -> position, !{
create_myswitch(0);},
   prompt

printintro
 ->  /* can be empty */ ={create_myswitch(0);}
 -> stream
 -> "REVERSE" ={create_revert(TRUE);create_myswitch(0);}
 -> position ={create_myswitch(0);}
 -> "REVERSE", position ={create_revert(TRUE);create_myswitch(0);}

prompt
 -> STRING:s ={create_readline(s);}

position
 -> "AT", '(', expression, ',', expression, ')' ={create_mymove();}

stream
 -> '#', intnum:n ={create_myswitch(n);}

(int) hashed_number
 -> '#', intnum:n =n;
 -> intnum:n /* need not contain hash */ =n;

semicolon /* can be left out */
 ->  ={create_print('n');}
 -> ';'

goto_list
 -> symbol_or_lineno:s ={create_goto(s);create_findnop();}
 -> goto_list, ',', symbol_or_lineno:s ={create_goto(s);create_findnop();}

gosub_list
 -> symbol_or_lineno:s ={create_gosub(s);create_findnop();}
 -> gosub_list, ',', symbol_or_lineno:s ={create_gosub(s);create_findnop();}

{
#define CHAR_STACK_LENGTH 500

static char  charStack[CHAR_STACK_LENGTH+1];
static char *charStackTop = charStack;

/* Define an error record */
typedef struct {
  char *message;                 /* identifies error */
  int line;                      /* location of error */
  int column;
} ErrorRecord;

ErrorRecord errorRecord;           /* define an error record */

void diagnoseError(char *msg) {
  if (parseBasic_pcb.exit_flag == AG_RUNNING_CODE)
    parseBasic_pcb.exit_flag = AG_SEMANTIC_ERROR_CODE;      /* stop parse */
  errorRecord.message = msg;
  errorRecord.line    = parseBasic_pcb.line;
  errorRecord.column  = parseBasic_pcb.column;
}

void pushChar(int c) {              /* append char to name string */
  if (charStackTop < charStack+CHAR_STACK_LENGTH) {
    *charStackTop++ = (char) c;
    return;
  }
  /* buffer overflow, kill parse and issue diagnostic */
  diagnoseError("Character Stack Overflow");
}

static char *popString(int nChars) {                /* get string */
  *charStackTop = 0;
  return charStackTop -= nChars;
}

FILE *parserInputFile;
void switch_to_my_file(FILE *inputfile) /* switches lex input to given file */
{
  parserInputFile = inputfile;
  return;
}

#define GET_INPUT ((PCB).input_code = fgetc(parserInputFile))

int yyparse() {
  parseBasic();
  return !(PCB.exit_flag == AG_SUCCESS_CODE);
}


}