/*
* Copyright (C) 1991-1994, 1997, 2006, 2008, 2012-2017 Free Software Foundation, Inc.
* Copyright (C) 2016-2017 Philip A. Nelson.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. The names Philip A. Nelson and Free Software Foundation may not be
* used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY PHILIP A. NELSON ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL PHILIP A. NELSON OR THE FREE SOFTWARE FOUNDATION BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
* THE POSSIBILITY OF SUCH DAMAGE.
*/
/* bc.y: The grammar for a POSIX compatable bc processor with some
extensions to the language. */
/* Extensions over POSIX bc.
a) NAME was LETTER. This grammar allows longer names.
Single letter names will still work.
b) Relational_expression allowed only one comparison.
This grammar has added boolean expressions with
&& (and) || (or) and ! (not) and allowed all of them in
full expressions.
c) Added an else to the if.
d) Call by variable array parameters
e) read() procedure that reads a number under program control from stdin.
f) halt statement that halts the the program under program control. It
is an executed statement.
g) continue statement for for loops.
h) optional expressions in the for loop.
i) print statement to print multiple numbers per line.
j) warranty statement to print an extended warranty notice.
k) limits statement to print the processor's limits.
l) void functions.
*/
%token <i_value> ENDOFLINE AND OR NOT
%token <s_value> STRING NAME NUMBER
/* '-', '+' are tokens themselves */
/* '=', '+=', '-=', '*=', '/=', '%=', '^=' */
%token <c_value> ASSIGN_OP
/* '==', '<=', '>=', '!=', '<', '>' */
%token <s_value> REL_OP
/* '++', '--' */
%token <c_value> INCR_DECR
/* 'define', 'break', 'quit', 'length' */
%token <i_value> Define Break Quit Length
/* 'return', 'for', 'if', 'while', 'sqrt', 'else' */
%token <i_value> Return For If While Sqrt Else
/* 'scale', 'ibase', 'obase', 'auto', 'read', 'random' */
%token <i_value> Scale Ibase Obase Auto Read Random
/* 'warranty', 'halt', 'last', 'continue', 'print', 'limits' */
%token <i_value> Warranty Halt Last Continue Print Limits
/* 'history', 'void' */
%token <i_value> UNARY_MINUS HistoryVar Void
/* Types of all other things. */
%type <i_value> expression return_expression named_expression opt_expression
%type <c_value> '+' '-' '*' '/' '%'
%type <a_value> opt_parameter_list opt_auto_define_list define_list
%type <a_value> opt_argument_list argument_list
%type <i_value> program input_item semicolon_list statement_list
%type <i_value> statement function statement_or_error required_eol
%type <i_value> opt_void
/* precedence */
%left OR
%left AND
%nonassoc NOT
%left REL_OP
%right ASSIGN_OP
%left '+' '-'
%left '*' '/' '%'
%right '^'
%nonassoc UNARY_MINUS
%nonassoc INCR_DECR
/* Expression lval meanings! (Bits mean something!) (See defines above)
* 0 => Top op is assignment.
* 1 => Top op is not assignment.
* 2 => Comparison is somewhere in expression.
* 4 => Expression is in parenthesis.
* 8 => Expression is void!
* 16 => Empty optional expression.
*/
opt_expression : /* empty */
{
$$ = EX_EMPTY;
ct_warn ("Missing expression in for statement");
}
| expression
;
return_expression : /* empty */
{
$$ = 0;
generate ("0");
if (cur_func == -1)
yyerror("Return outside of a function.");
}
| expression
{
if ($1 & EX_COMP)
ct_warn ("comparison in return expresion");
if (!($1 & EX_PAREN))
ct_warn ("return expression requires parenthesis");
if ($1 & EX_VOID)
yyerror("return requires non-void expression");
if (cur_func == -1)
yyerror("Return outside of a function.");
else if (functions[cur_func].f_void)
yyerror("Return expression in a void function.");
}
;
expression : named_expression ASSIGN_OP
{
if ($2 != '=')
{
if ($1 < 0)
snprintf (genstr, genlen, "DL%d:", -$1);
else
snprintf (genstr, genlen, "l%d:", $1);
generate (genstr);
}
}
expression
{
if ($4 & EX_ASSGN)
ct_warn("comparison in assignment");
if ($4 & EX_VOID)
yyerror("Assignment of a void expression");
if ($2 != '=')
{
snprintf (genstr, genlen, "%c", $2);
generate (genstr);
}
if ($1 < 0)
snprintf (genstr, genlen, "S%d:", -$1);
else
snprintf (genstr, genlen, "s%d:", $1);
generate (genstr);
$$ = EX_ASSGN;
}
| expression AND
{
ct_warn("&& operator");
$2 = next_label++;
snprintf (genstr, genlen, "DZ%d:p", $2);
generate (genstr);
}
expression
{
if (($1 & EX_VOID) || ($4 & EX_VOID))
yyerror ("void expression with &&");
snprintf (genstr, genlen, "DZ%d:p1N%d:", $2, $2);
generate (genstr);
$$ = ($1 | $4) & ~EX_PAREN;
}
| expression OR
{
ct_warn("|| operator");
$2 = next_label++;
snprintf (genstr, genlen, "B%d:", $2);
generate (genstr);
}
expression
{
int tmplab;
if (($1 & EX_VOID) || ($4 & EX_VOID))
yyerror ("void expression with ||");
tmplab = next_label++;
snprintf (genstr, genlen, "B%d:0J%d:N%d:1N%d:",
$2, tmplab, $2, tmplab);
generate (genstr);
$$ = ($1 | $4) & ~EX_PAREN;
}
| NOT expression
{
if ($2 & EX_VOID)
yyerror ("void expression with !");
$$ = $2 & ~EX_PAREN;
ct_warn("! operator");
generate ("!");
}
| expression REL_OP expression
{
if (($1 & EX_VOID) || ($3 & EX_VOID))
yyerror ("void expression with comparison");
$$ = EX_REG | EX_COMP;
switch (*($2))
{
case '=':
generate ("=");
break;
case '!':
generate ("#");
break;
case '<':
if ($2[1] == '=')
generate ("{");
else
generate ("<");
break;