Menu

[r5829]: / trunk / fuse-basic / basicy.y  Maximize  Restore  History

Download this file

548 lines (471 with data), 14.1 kB

/* basicy.y: ZX Spectrum BASIC grammar
   Copyright (c) 1998,2002-2004 Philip Kendall

   $Id$

   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation; either version 2 of the License, or
   (at your option) any later version.

   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.

   You should have received a copy of the GNU General Public License along
   with this program; if not, write to the Free Software Foundation, Inc.,
   51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.

   Author contact information:

   E-mail: philip-fuse@shadowmagic.org.uk

*/

%{

#include "config.h"

#define YYDEBUG 1
#define YYERROR_VERBOSE

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "basic.h"
#include "explist.h"
#include "line.h"
#include "numexp.h"
#include "parse.h"
#include "printlist.h"
#include "program.h"
#include "statement.h"
#include "strexp.h"
#include "token.h"

%}

%union {

  int integer;
  float real;
  struct string *string;
  char *varname;

  struct slicer *slicer;

  enum basic_token token;

  struct numexp *numexp;
  struct strexp *strexp;

  struct expression *expression;
  struct explist *explist;

  struct printitem *printitem;
  struct printlist *printlist;

  struct statement *statement;

  struct line *line;
  struct program *program;

}

/* Simple tokens */

%token <integer>   LINENUM	/* An integer in the range 0-9999 */
%token <real>      NUM		/* Any other number */

%token <string>	   STRING	/* A simple string */

%token <varname>   FORVAR	/* A one letter variable name */
%token <varname>   NUMVAR	/* A longer variable name */
%token <varname>   STRVAR	/* A string variable name */

%token <token>	   TIMES_DIVIDE	/* '*' or '/' */
%token <token>	   NOARG_COMMAND /* A command which takes no arguments */
%token <token>	   NUMEXP_COMMAND /* One which takes one numeric arg */
%token <token>	   OPTNUMEXP_COMMAND /* Optional numeric arg */
%token <token>	   STREXP_COMMAND /* One string arg */
%token <token>     TWONUMEXP_COMMAND /* Two numeric args */

/* Some unique commands */
%token		   DEFFN_TOKEN
%token		   DIM_TOKEN
%token		   FOR_TOKEN
%token		   INPUT_TOKEN
%token		   LET_TOKEN
%token		   NEXT_TOKEN
%token		   PRINT_TOKEN
%token		   IF_TOKEN

%token		   THEN_TOKEN
%token		   STEP_TOKEN
%token		   FN_TOKEN
%token		   TO_TOKEN

/* Operators */

%token <token>	   NUMEXP_NO_ARG /* A numerical operator which takes no args */
%token <token>     NUMEXP_ONE_ARG /* One which takes one numerical argument */
%token <token>     NUMEXP_TWO_ARG /* Two numerical arguments */
%token <token>	   NUMEXP_STREXP /* One string argument */
%token <token>	   COMPARISON    /* Comparison operator */

%token <token>	   STREXP_NUMEXP /* A strexp which takes one numexp arg */
%token <token>	   STREXP_STREXP /* A strexp which takes one strexp arg */

/* Derived types */

%type <real>       number

%type <varname>	   numericvar
%type <numexp>     numexp
%type <numexp>     optnumexp

%type <slicer>	   slicer
%type <slicer>     optslicer

%type <strexp>	   strexp

%type <explist>	   explist
%type <explist>	   optexplist

%type <numexp>     optstepexp
%type <explist>    numexplist
%type <explist>	   optsvarlist

%type <token>	   printsep

%type <printitem>  printitem

%type <printlist>  printlist
%type <printlist>  printlist1
%type <printlist>  printlist2

%type <statement>  command

%type <line>	   commandlist
%type <line>	   line

%type <program>	   program
%type <program>	   input

/* Operator precedences */

/* Low precedence */

%left OR_TOKEN
%left AND_TOKEN
%left NOT_TOKEN
%left COMPARISON '='
%left '+' '-'
%left TIMES_DIVIDE
%left NEG		/* Unary minus; also used for unary plus */
%right '^'
%left NUMEXP_NO_ARG NUMEXP_ONE_ARG NUMEXP_TWO_ARG NUMEXP_STREXP STREXP_NUMEXP

/* High precedence */

%%

input:   /* empty */ { ; }
       | program     { if( parse_target != PARSE_PROGRAM ) YYERROR;
		       parse_program_target = $1; }
       | numexp { if( parse_target != PARSE_NUMEXP ) YYERROR;
                  parse_numexp_target = $1; }
       | strexp { if( parse_target != PARSE_STREXP ) YYERROR;
                  parse_strexp_target = $1; }
;

program:    line { $$ = malloc( sizeof( *$$ ) );
		   program_initialise( $$ ); 
		   program_add_line( $$, $1 ); }
          | program line { program_add_line( $1, $2 ); }
;

line:   '\n' { $$ = NULL; }
      | LINENUM commandlist '\n' { $$ = $2; $$->line_number = $1; }
;

commandlist:   command           { $$ = malloc( sizeof *$$ );
                                   $$->statements = NULL;
                                   line_append_statement( $$, $1 ); }
             | commandlist ':' command
                                 { $$ = $1;
				   line_append_statement( $$, $3 ); }
;

command:   NOARG_COMMAND { if( statement_new_noarg( &($$), $1 ) !=
			       BASIC_ERROR_NONE )
			     YYERROR;
	                 }
	 | NUMEXP_COMMAND numexp
           { if( statement_new_numexp( &($$), $1, $2 ) != BASIC_ERROR_NONE )
	       YYERROR;
	   }
         | OPTNUMEXP_COMMAND optnumexp
           { if( statement_new_optnumexp( &($$), $1, $2 ) != BASIC_ERROR_NONE )
	       YYERROR;
	   }
         | TWONUMEXP_COMMAND numexp ',' numexp
           { if( statement_new_twonumexp( &($$), $1, $2, $4 ) !=
		 BASIC_ERROR_NONE )
	       YYERROR;
	   }
         | LET_TOKEN numericvar '=' numexp
	   { if( statement_new_let_numeric( &($$), $2, $4 ) !=
		 BASIC_ERROR_NONE ) {
	       YYERROR;
	       free( $2 );
	     }
	     free( $2 );
	   }
	 | LET_TOKEN FORVAR '(' explist ')' '=' numexp
	   { if( statement_new_let_numeric_array( &($$), $2, $4, $7 ) !=
		 BASIC_ERROR_NONE ) {
	       free( $2 );
	       YYERROR;
	     }
	     free( $2 );
	   }
         | LET_TOKEN STRVAR optslicer '=' strexp
	   { if( statement_new_let_string( &($$), $2, $5, $3 ) !=
		 BASIC_ERROR_NONE ) {
	       free( $2 );
	       YYERROR;
	     }
	     free( $2 );
	   }
	 | STREXP_COMMAND strexp
	   { if( statement_new_strexp( &($$), $1, $2 ) != BASIC_ERROR_NONE )
	       YYERROR;
	   }
	 | PRINT_TOKEN printlist
	   { if( statement_new_print( &($$), $2 ) != BASIC_ERROR_NONE )
	       YYERROR;
	   }
	 | IF_TOKEN numexp THEN_TOKEN command
	   { if( statement_new_if( &($$), $2, $4 ) != BASIC_ERROR_NONE )
	       YYERROR;
	   }
	 | FOR_TOKEN FORVAR '=' numexp TO_TOKEN numexp optstepexp
	   { if( statement_new_for( &($$), $2, $4, $6, $7 ) !=
		 BASIC_ERROR_NONE ) {
	       free( $2 );
	       YYERROR;
	     }
	     free( $2 );
	   }
	 | NEXT_TOKEN FORVAR
	   { if( statement_new_next( &($$), $2 ) != BASIC_ERROR_NONE ) {
	       free( $2 );
	       YYERROR;
	     }
	     free( $2 );
	   }
	 | DEFFN_TOKEN FORVAR '(' optsvarlist ')' '=' numexp
	   { if( statement_new_deffn( &($$), $2, $4, $7 ) !=
		 BASIC_ERROR_NONE ) {
	       free( $2 );
	       YYERROR;
	     }
	     free( $2 );
	   }
         | DIM_TOKEN FORVAR '(' numexplist ')'
           { if( statement_new_dim_numeric( &($$), $2, $4 ) !=
		 BASIC_ERROR_NONE ) {
	       free( $2 );
	       YYERROR;
	     }
	     free( $2 );
	   }
	 | INPUT_TOKEN printlist
           {
	     if( statement_new_input( &($$), $2 ) ) YYERROR;
	   }
;

optnumexp:   /* empty */ { $$ = NULL; }
	   | numexp      { $$ = $1;   }
;

numexp:   number { $$ = malloc( sizeof( *$$ ) );
                   $$->type = NUMEXP_NUMBER_ID; 
		   $$->types.number = $1;
                 }
	| numericvar
	  { if( numexp_new_variable( &($$), $1 ) != BASIC_ERROR_NONE ) {
	      YYERROR;
	      free( $1 );
	    }
	    free( $1 );
	  }
	| FORVAR '(' numexplist ')'
          { if( numexp_new_array( &($$), $1, $3 ) != BASIC_ERROR_NONE )
	      YYERROR;
	  }
	| NUMEXP_NO_ARG { if( numexp_new_noarg( &($$), $1 ) !=
			      BASIC_ERROR_NONE )
	                  YYERROR;
	                }
	| NUMEXP_ONE_ARG numexp
          { if( numexp_new_onearg( &($$), $1, $2 ) != BASIC_ERROR_NONE )
	      YYERROR;
	  }
        | NOT_TOKEN numexp		/* Different precedence */
          { if( numexp_new_onearg( &($$), NOT, $2 ) != BASIC_ERROR_NONE )
	      YYERROR;
	  }
        | '(' numexp ')' { $$ = $2; }
        | '+' numexp %prec NEG { $$ = $2; }
        | '-' numexp %prec NEG
          { if( numexp_new_onearg( &($$), '-', $2 ) != BASIC_ERROR_NONE )
	      YYERROR;
	  }
        | numexp '+' numexp
          { if( numexp_new_twoarg( &($$), '+', $1, $3 ) != BASIC_ERROR_NONE )
	      YYERROR;
	  }
        | numexp '-' numexp
          { if( numexp_new_twoarg( &($$), '-', $1, $3 ) != BASIC_ERROR_NONE )
	      YYERROR;
	  }
        | numexp TIMES_DIVIDE numexp
          { if( numexp_new_twoarg( &($$), $2, $1, $3 ) != BASIC_ERROR_NONE )
	      YYERROR;
	  }
        | numexp '^' numexp
          { if( numexp_new_twoarg( &($$), '^', $1, $3 ) != BASIC_ERROR_NONE )
	      YYERROR;
	  }
        | numexp COMPARISON numexp
          { if( numexp_new_twoarg( &($$), $2, $1, $3 ) != BASIC_ERROR_NONE )
	      YYERROR;
	  }
	| numexp '=' numexp
          { if( numexp_new_twoarg( &($$), '=', $1, $3 ) != BASIC_ERROR_NONE )
	      YYERROR;
	  }
        | numexp OR_TOKEN numexp
          { if( numexp_new_twoarg( &($$), OR, $1, $3 ) != BASIC_ERROR_NONE )
	      YYERROR;
	  }
        | numexp AND_TOKEN numexp
          { if( numexp_new_twoarg( &($$), AND, $1, $3 ) != BASIC_ERROR_NONE )
	      YYERROR;
	  }
        | NUMEXP_TWO_ARG '(' numexp ',' numexp ')'
          { if( numexp_new_twoarg( &($$), $1, $3, $5 ) != BASIC_ERROR_NONE )
	      YYERROR;
	  }
        | NUMEXP_STREXP strexp
	  { if( numexp_new_strexp( &($$), $1, $2 ) != BASIC_ERROR_NONE )
	      YYERROR;
	  }
	| FN_TOKEN FORVAR '(' optexplist ')'
	  { if( numexp_new_fn( &($$), $2, $4 ) != BASIC_ERROR_NONE ) {
	      free( $2 );
	      YYERROR;
	    }
	    free( $2 );
	  }
;

number:   NUM      { $$ = $1; }
        | LINENUM  { $$ = $1; }
;

numericvar:   FORVAR { $$ = $1; }
	    | NUMVAR { $$ = $1; }
;

strexp:   STRING
          { if( strexp_new_string( &($$), $1 ) != BASIC_ERROR_NONE )
	      YYERROR;
	  }
        | STRVAR
	  { if( strexp_new_variable( &($$), $1 ) != BASIC_ERROR_NONE ) {
	      free( $1 );
	      YYERROR;
	    }
	    free( $1 );
	  }
	| STREXP_NUMEXP numexp
	  { if( strexp_new_numexp( &($$), $1, $2 ) != BASIC_ERROR_NONE )
	      YYERROR;
	  }
        | STREXP_STREXP strexp
	  { if( strexp_new_strexp( &($$), $1, $2 ) != BASIC_ERROR_NONE )
	      YYERROR;
	  }
	| strexp '+' strexp
	  { if( strexp_new_twostrexp( &($$), '+', $1, $3 ) !=
		BASIC_ERROR_NONE )
	      YYERROR;
	  }
        | '(' strexp ')' { $$ = $2; }
	| strexp slicer
	  { if( strexp_new_slicer( &($$), $1, $2 ) != BASIC_ERROR_NONE )
	      YYERROR;
	  }
        | strexp AND_TOKEN numexp
	  { if( strexp_new_and( &($$), $1, $3 ) != BASIC_ERROR_NONE )
	      YYERROR;
	  }
;

optslicer:   /* empty */ { $$ = NULL; }
	   | slicer	 { $$ = $1;   }
;

slicer:   '(' optnumexp TO_TOKEN optnumexp ')'
	  { if( slicer_new( &($$), $2, $4 ) != BASIC_ERROR_NONE ) YYERROR; }
;

optexplist:   /* empty */
	      { if( explist_new( &($$) ) != BASIC_ERROR_NONE ) YYERROR; }
	    | explist     { $$ = $1;   }
;

explist:   numexp
	   { if( explist_new( &($$) ) != BASIC_ERROR_NONE ) YYERROR;
	     if( explist_append_numexp( $$, $1 ) != BASIC_ERROR_NONE )
	       YYERROR;
	   }
	 | strexp
	   { if( explist_new( &($$) ) != BASIC_ERROR_NONE ) YYERROR;
	     if( explist_append_strexp( $$, $1 ) != BASIC_ERROR_NONE )
	       YYERROR;
	   }
	 | explist ',' numexp
           { $$ = $1; 
	     if( explist_append_numexp( $$, $3 ) != BASIC_ERROR_NONE )
	       YYERROR;
	   }
	 | explist ',' strexp
	   { $$ = $1;
	     if( explist_append_strexp( $$, $3 ) != BASIC_ERROR_NONE )
	       YYERROR;
	   }
;

numexplist:   numexp
	      { if( explist_new( &($$) ) != BASIC_ERROR_NONE ) YYERROR;
	        if( explist_append_numexp( $$, $1 ) != BASIC_ERROR_NONE )
	          YYERROR;
	      }
	    | numexplist ',' numexp
              { $$ = $1; 
	        if( explist_append_numexp( $$, $3 ) != BASIC_ERROR_NONE )
	          YYERROR;
	      }
;

printlist:   printlist1 { $$ = $1; }
           | printlist2 { $$ = $1; }
;

printlist1:   /* empty */
	      { if( printlist_new( &($$) ) != BASIC_ERROR_NONE ) YYERROR; }
	    | printlist printsep
	      {
		struct printitem *separator;

	        $$ = $1;

		if( printitem_new_separator( &separator, $2 ) !=
		    BASIC_ERROR_NONE )
		  YYERROR;
		if( printlist_append( $$, separator ) != BASIC_ERROR_NONE )
		  YYERROR;
	      }
;

printlist2:   printlist1 printitem
	      { if( printlist_append( $$, $2 ) != BASIC_ERROR_NONE ) YYERROR; }
;

printitem:   numexp
	     { if( printitem_new_numexp( &($$), $1 ) != BASIC_ERROR_NONE )
	         YYERROR;
	     }
	   | strexp
	     { if( printitem_new_strexp( &($$), $1 ) != BASIC_ERROR_NONE )
	         YYERROR;
	     }
;

printsep:   ';'  { $$ = ';';  }
	  | ','  { $$ = ',';  }
	  | '\'' { $$ = '\''; }
;

optstepexp:   /* empty */       { $$ = NULL; }
	    | STEP_TOKEN numexp { $$ = $2; }
;

optsvarlist:   /* empty */
	       { if( explist_new( &($$) ) != BASIC_ERROR_NONE ) YYERROR; }
	     | FORVAR
	       { if( explist_new( &($$) ) != BASIC_ERROR_NONE ) YYERROR;
	         if( explist_append_forvar( $$, $1 ) != BASIC_ERROR_NONE ) {
		   free( $1 );
		   YYERROR;
		 }
		 free( $1 );
	       }
	     | STRVAR
	       { if( explist_new( &($$) ) != BASIC_ERROR_NONE ) YYERROR;
	         if( explist_append_strvar( $$, $1 ) != BASIC_ERROR_NONE ) {
		   free( $1 );
		   YYERROR;
		 }
		 free( $1 );
	       }
	     | optsvarlist ',' FORVAR
	       { $$ = $1;
	         if( explist_append_forvar( $$, $3 ) != BASIC_ERROR_NONE ) {
		   free( $3 );
		   YYERROR;
		 }
		 free( $3 );
	       }
	     | optsvarlist ',' STRVAR
	       { $$ = $1;
	         if( explist_append_strvar( $$, $3 ) != BASIC_ERROR_NONE ) {
		   free( $3 );
		   YYERROR;
		 }
		 free( $3 );
	       }
;

%%
Want the latest updates on software, tech news, and AI?
Get latest updates about software, tech news, and AI from SourceForge directly in your inbox once a month.