%{ /* sbc.y: A POSIX bc processor written for minix with no extensions. */ /* This file is part of GNU bc. Copyright (C) 1991, 1992, 1993, 1994, 1997 Free Software Foundation, Inc. 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; see the file COPYING. If not, write to The Free Software Foundation, Inc. 51 Franklin Street, Fifth Floor Boston, MA 02110-1301 USA You may contact the author by: e-mail: philnelson@acm.org us-mail: Philip A. Nelson Computer Science Department, 9062 Western Washington University Bellingham, WA 98226-9062 *************************************************************************/ #include "bcdefs.h" #include "global.h" /* To get the global variables. */ #include "proto.h" %} %start program %union { char *s_value; char c_value; int i_value; arg_list *a_value; } %token ENDOFLINE AND OR NOT %token STRING NAME NUMBER /* '-', '+' are tokens themselves */ %token ASSIGN_OP /* '=', '+=', '-=', '*=', '/=', '%=', '^=' */ %token REL_OP /* '==', '<=', '>=', '!=', '<', '>' */ %token INCR_DECR /* '++', '--' */ %token Define Break Quit Length /* 'define', 'break', 'quit', 'length' */ %token Return For If While Sqrt Else /* 'return', 'for', 'if', 'while', 'sqrt', 'else' */ %token Scale Ibase Obase Auto Read /* 'scale', 'ibase', 'obase', 'auto', 'read' */ %token Warranty, Halt, Last, Continue, Print, Limits /* 'warranty', 'halt', 'last', 'continue', 'print', 'limits' */ /* The types of all other non-terminals. */ %type expression named_expression return_expression %type opt_parameter_list parameter_list opt_auto_define_list %type define_list opt_argument_list argument_list %type program input_item semicolon_list statement_list %type statement_or_error statement function relational_expression /* precedence */ %nonassoc REL_OP %right ASSIGN_OP %left '+' '-' %left '*' '/' '%' %right '^' %nonassoc UNARY_MINUS %nonassoc INCR_DECR %% program : /* empty */ { $$ = 0; std_only = TRUE; if (interactive && !quiet) { show_bc_version (); welcome (); } } | program input_item ; input_item : semicolon_list ENDOFLINE { run_code (); } | function { run_code (); } | error ENDOFLINE { yyerrok; init_gen () ; } ; semicolon_list : /* empty */ { $$ = 0; } | statement_or_error | semicolon_list ';' statement_or_error | semicolon_list ';' ; statement_list : /* empty */ { $$ = 0; } | statement | statement_list ENDOFLINE | statement_list ENDOFLINE statement | statement_list ';' | statement_list ';' statement ; statement_or_error : statement | error statement { $$ = $2; } ; statement : Warranty { warranty ("s"); } | expression { if ($1 & 1) generate ("W"); else generate ("p"); } | STRING { $$ = 0; generate ("w"); generate ($1); free ($1); } | Break { if (break_label == 0) yyerror ("Break outside a for/while"); else { sprintf (genstr, "J%1d:", break_label); generate (genstr); } } | Quit { bc_exit (0); } | Return { generate ("0R"); } | Return '(' return_expression ')' { generate ("R"); } | For { $1 = break_label; break_label = next_label++; } '(' expression ';' { $4 = next_label++; sprintf (genstr, "pN%1d:", $4); generate (genstr); } relational_expression ';' { $7 = next_label++; sprintf (genstr, "B%1d:J%1d:", $7, break_label); generate (genstr); $$ = next_label++; sprintf (genstr, "N%1d:", $$); generate (genstr); } expression ')' { sprintf (genstr, "pJ%1d:N%1d:", $4, $7); generate (genstr); } statement { sprintf (genstr, "J%1d:N%1d:", $9, break_label); generate (genstr); break_label = $1; } | If '(' relational_expression ')' { $3 = next_label++; sprintf (genstr, "Z%1d:", $3); generate (genstr); } statement { sprintf (genstr, "N%1d:", $3); generate (genstr); } | While { $1 = next_label++; sprintf (genstr, "N%1d:", $1); generate (genstr); } '(' relational_expression { $4 = break_label; break_label = next_label++; sprintf (genstr, "Z%1d:", break_label); generate (genstr); } ')' statement { sprintf (genstr, "J%1d:N%1d:", $1, break_label); generate (genstr); break_label = $4; } | '{' statement_list '}' { $$ = 0; } ; function : Define NAME '(' opt_parameter_list ')' '{' ENDOFLINE opt_auto_define_list { char *params, *autos; check_params ($4,$8); params = arg_str ($4); autos = arg_str ($8); set_genstr_size (30 + strlen (params) + strlen (autos)); sprintf (genstr, "F%d,%s.%s[", lookup ($2,FUNCT), params, autos); generate (genstr); free_args ($4); free_args ($8); $1 = next_label; next_label = 0; } statement_list ENDOFLINE '}' { generate ("0R]"); next_label = $1; } ; opt_parameter_list : /* empty */ { $$ = NULL; } | parameter_list ; parameter_list : NAME { $$ = nextarg (NULL, lookup ($1,SIMPLE), FALSE); } | define_list ',' NAME { $$ = nextarg ($1, lookup ($3,SIMPLE), FALSE); } ; opt_auto_define_list : /* empty */ { $$ = NULL; } | Auto define_list ENDOFLINE { $$ = $2; } | Auto define_list ';' { $$ = $2; } ; define_list : NAME { $$ = nextarg (NULL, lookup ($1,SIMPLE), FALSE); } | NAME '[' ']' { $$ = nextarg (NULL, lookup ($1,ARRAY), FALSE); } | define_list ',' NAME { $$ = nextarg ($1, lookup ($3,SIMPLE), FALSE); } | define_list ',' NAME '[' ']' { $$ = nextarg ($1, lookup ($3,ARRAY), FALSE); } ; opt_argument_list : /* empty */ { $$ = NULL; } | argument_list ; argument_list : expression { $$ = nextarg (NULL,0, FALSE); } | argument_list ',' expression { $$ = nextarg ($1,0, FALSE); } ; relational_expression : expression { $$ = 0; } | expression REL_OP expression { $$ = 0; switch (*($2)) { case '=': generate ("="); break; case '!': generate ("#"); break; case '<': if ($2[1] == '=') generate ("{"); else generate ("<"); break; case '>': if ($2[1] == '=') generate ("}"); else generate (">"); break; } } ; return_expression : /* empty */ { $$ = 0; generate ("0"); } | expression ; expression : named_expression ASSIGN_OP { if ($2 != '=') { if ($1 < 0) sprintf (genstr, "DL%d:", -$1); else sprintf (genstr, "l%d:", $1); generate (genstr); } } expression { $$ = 0; if ($2 != '=') { sprintf (genstr, "%c", $2); generate (genstr); } if ($1 < 0) sprintf (genstr, "S%d:", -$1); else sprintf (genstr, "s%d:", $1); generate (genstr); } | expression '+' expression { generate ("+"); } | expression '-' expression { generate ("-"); } | expression '*' expression { generate ("*"); } | expression '/' expression { generate ("/"); } | expression '%' expression { generate ("%"); } | expression '^' expression { generate ("^"); } | '-' expression %prec UNARY_MINUS { generate ("n"); $$ = 1;} | named_expression { $$ = 1; if ($1 < 0) sprintf (genstr, "L%d:", -$1); else sprintf (genstr, "l%d:", $1); generate (genstr); } | NUMBER { int len = strlen ($1); $$ = 1; if (len == 1 && *$1 == '0') generate ("0"); else { if (len == 1 && *$1 == '1') generate ("1"); else { generate ("K"); generate ($1); generate (":"); } free ($1); } } | '(' expression ')' { $$ = 1; } | NAME '(' opt_argument_list ')' { $$ = 1; if ($3 != NULL) { char *params = call_str ($3); set_genstr_size (20 + strlen (params)); sprintf (genstr, "C%d,%s:", lookup ($1,FUNCT), params); free_args ($3); } else sprintf (genstr, "C%d:", lookup ($1,FUNCT)); generate (genstr); } | INCR_DECR named_expression { $$ = 1; if ($2 < 0) { if ($1 == '+') sprintf (genstr, "DA%d:L%d:", -$2, -$2); else sprintf (genstr, "DM%d:L%d:", -$2, -$2); } else { if ($1 == '+') sprintf (genstr, "i%d:l%d:", $2, $2); else sprintf (genstr, "d%d:l%d:", $2, $2); } generate (genstr); } | named_expression INCR_DECR { $$ = 1; if ($1 < 0) { sprintf (genstr, "DL%d:x", -$1); generate (genstr); if ($2 == '+') sprintf (genstr, "A%d:", -$1); else sprintf (genstr, "M%d:", -$1); } else { sprintf (genstr, "l%d:", $1); generate (genstr); if ($2 == '+') sprintf (genstr, "i%d:", $1); else sprintf (genstr, "d%d:", $1); } generate (genstr); } | Length '(' expression ')' { generate ("cL"); $$ = 1;} | Sqrt '(' expression ')' { generate ("cR"); $$ = 1;} | Scale '(' expression ')' { generate ("cS"); $$ = 1;} ; named_expression : NAME { $$ = lookup ($1,SIMPLE); } | NAME '[' expression ']' { $$ = lookup ($1,ARRAY); } | Ibase { $$ = 0; } | Obase { $$ = 1; } | Scale { $$ = 2; } ; %%