// Copyright(C) 1999-2023 National Technology & Engineering Solutions // of Sandia, LLC (NTESS). Under the terms of Contract DE-NA0003525 with // NTESS, the U.S. Government retains certain rights in this software. // // See packages/seacas/LICENSE for details %{ #include "aprepro.h" #include "apr_util.h" #include "apr_array.h" #include #include #include #include #include #include #include namespace { void reset_error() { #if !defined(WIN32) && !defined(__WIN32__) && !defined(_WIN32) && !defined(_MSC_VER) && \ !defined(__MINGW32__) && !defined(_WIN64) && !defined(__MINGW64__) #ifndef math_errhandling #define math_errhandling MATH_ERRNO #endif if (math_errhandling & MATH_ERREXCEPT) { std::feclearexcept(FE_ALL_EXCEPT); } if (math_errhandling & MATH_ERRNO) { errno = 0; } #endif } } namespace SEAMS { extern bool echo; } %} %require "3.2" /* add debug output code to generated parser. disable this for release * versions. */ %debug /* write out a header file containing the token defines */ %defines /* use newer C++ skeleton file */ %skeleton "lalr1.cc" /* namespace to enclose parser in */ %define api.prefix {SEAMS} /* set the parser's class identifier */ %define api.parser.class {Parser} %define parse.error verbose /* aprepro is passed by reference to the parser and to the scanner. This * provides a simple but effective pure interface, not relying on global * variables. */ %parse-param { class Aprepro& aprepro } %union { double val; /* For returning numbers. */ struct symrec *tptr; /* For returning symbol-table pointers */ char *string; /* For returning quoted strings */ struct array *arrval; /* For returning arrays */ } %token NUM /* Simple double precision number */ %token QSTRING /* Quoted string */ %token UNDVAR /* Variable and function */ %token VAR %token SVAR /* String Variable */ %token IMMVAR /* Immutable Variable */ %token IMMSVAR /* Immutable String Variable */ %token AVAR /* array data [i,j] */ %token FNCT %token SFNCT %token AFNCT %type exp %type aexp %type bool %type sexp %token END 0 "end of file" %token COMMA LPAR RPAR LBRACK RBRACK LBRACE RBRACE SEMI /* Precedence (Lowest to Highest) and associativity */ %right EQUAL %right EQ_PLUS EQ_MINUS %right EQ_TIME EQ_DIV %right EQ_POW %right QUEST COLON %left LOR /* Logical OR */ %left LAND /* Logical AND */ %left LT GT LE GE EQ NE /* <=, >=, ==, != */ %left PLU SUB %left DIV TIM MOD %left UNARY NOT /* Negation--unary minus/plus */ %right POW /* Exponentiation */ %left INC DEC /* increment (++), decrement (--) */ %left CONCAT /* Concatenate Strings */ %{ #include "aprepro.h" #include "apr_scanner.h" /* this "connects" the bison parser in aprepro to the flex scanner class * object. it defines the yylex() function call to pull the next token from the * current lexer object of the aprepro context. */ #undef yylex #define yylex aprepro.lexer->lex %} /* Grammar Rules: */ %% input: /* empty rule */ | input line ; line: '\n' { if (echo) aprepro.lexer->LexerOutput("\n", 1); } | LBRACE exp RBRACE { if (echo) { static char tmpstr[512]; SEAMS::symrec *format = aprepro.getsym("_FORMAT"); int len = snprintf(tmpstr, 512, format->value.svar.c_str(), $2); aprepro.lexer->LexerOutput(tmpstr, len); } } | LBRACE sexp RBRACE { if (echo && $2 != NULL) { aprepro.lexer->LexerOutput($2, strlen($2)); } } | LBRACE aexp RBRACE { } | LBRACE RBRACE { } | error RBRACE { yyerrok; } ; bool: exp LT exp { $$ = $1 < $3; } | exp GT exp { $$ = $1 > $3; } | NOT exp { $$ = !($2); } | exp LE exp { $$ = $1 <= $3; } | exp GE exp { $$ = $1 >= $3; } | exp EQ exp { $$ = $1 == $3; } | exp NE exp { $$ = $1 != $3; } | exp LOR exp { $$ = $1 || $3; } | exp LAND exp { $$ = $1 && $3; } | bool LOR bool { $$ = $1 || $3; } | bool LAND bool { $$ = $1 && $3; } | bool LOR exp { $$ = $1 || $3; } | bool LAND exp { $$ = $1 && $3; } | exp LOR bool { $$ = $1 || $3; } | exp LAND bool { $$ = $1 && $3; } | LPAR bool RPAR { $$ = $2; } ; bool: sexp LT sexp { $$ = (strcmp($1,$3) < 0 ? 1 : 0); } | sexp GT sexp { $$ = (strcmp($1,$3) > 0 ? 1 : 0); } | sexp LE sexp { $$ = (strcmp($1,$3) <= 0 ? 1 : 0); } | sexp GE sexp { $$ = (strcmp($1,$3) >= 0 ? 1 : 0); } | sexp EQ sexp { $$ = (strcmp($1,$3) == 0 ? 1 : 0); } | sexp NE sexp { $$ = (strcmp($1,$3) != 0 ? 1 : 0); } aexp: AVAR { $$ = aprepro.make_array(*($1->value.avar)); } | AFNCT LPAR sexp RPAR { if (arg_check($1, $1->value.arrfnct_c == NULL)) $$ = (*($1->value.arrfnct_c))($3); else yyerrok; } | AFNCT LPAR sexp COMMA exp RPAR { if (arg_check($1, $1->value.arrfnct_cd == NULL)) $$ = (*($1->value.arrfnct_cd))($3,$5); else yyerrok; } | AFNCT LPAR sexp COMMA sexp RPAR { if (arg_check($1, $1->value.arrfnct_cc == NULL)) $$ = (*($1->value.arrfnct_cc))($3,$5); else yyerrok; } | AFNCT LPAR exp COMMA exp COMMA exp RPAR { if (arg_check($1, $1->value.arrfnct_ddd == NULL)) $$ = (*($1->value.arrfnct_ddd))($3,$5,$7); else yyerrok; } | AFNCT LPAR exp COMMA exp RPAR { if (arg_check($1, $1->value.arrfnct_dd == NULL)) $$ = (*($1->value.arrfnct_dd))($3,$5); else yyerrok; } | AFNCT LPAR exp RPAR { if (arg_check($1, $1->value.arrfnct_d == NULL)) $$ = (*($1->value.arrfnct_d))($3); else yyerrok; } | AFNCT LPAR aexp RPAR { if (arg_check($1, $1->value.arrfnct_a == NULL)) $$ = (*($1->value.arrfnct_a))($3); else yyerrok; } | SVAR EQUAL aexp { $$ = $3; $1->value.avar = $3; redefined_warning(aprepro, $1); set_type(aprepro, $1, token::AVAR); } | VAR EQUAL aexp { $$ = $3; $1->value.avar= $3; redefined_warning(aprepro, $1); set_type(aprepro, $1, token::AVAR); } | AVAR EQUAL aexp { $$ = $3; aprepro.redefine_array($1->value.avar); $1->value.avar = $3; redefined_warning(aprepro, $1); set_type(aprepro, $1, token::AVAR); } | UNDVAR EQUAL aexp { $$ = $3; $1->value.avar = $3; set_type(aprepro, $1, token::AVAR); } | aexp PLU aexp { if ($1->cols == $3->cols && $1->rows == $3->rows ) { $$ = array_add($1, $3); } else { yyerror(aprepro, "Arrays do not have same row and column count"); yyerrok; } } | SUB aexp %prec UNARY { $$ = array_scale($2, -1.0); } | aexp SUB aexp { if ($1->cols == $3->cols && $1->rows == $3->rows ) { $$ = array_sub($1, $3); } else { yyerror(aprepro, "Arrays do not have same row and column count"); yyerrok; } } | aexp TIM exp { $$ = array_scale($1, $3); } | aexp DIV exp { $$ = array_scale($1, 1.0/$3); } | exp TIM aexp { $$ = array_scale($3, $1); } | aexp TIM aexp { if ($1->cols == $3->rows) { $$ = array_mult($1, $3); } else { yyerror(aprepro, "Column count of first array does not match row count of second array"); yyerrok; } } sexp: QSTRING { $$ = $1; } | SVAR { $$ = (char*)$1->value.svar.c_str(); } | IMMSVAR { $$ = (char*)$1->value.svar.c_str(); } | UNDVAR EQUAL sexp { $$ = $3; $1->value.svar = $3; set_type(aprepro, $1, Parser::token::SVAR); } | SVAR EQUAL sexp { $$ = $3; $1->value.svar = $3; redefined_warning(aprepro, $1); } | VAR EQUAL sexp { $$ = $3; $1->value.svar= $3; redefined_warning(aprepro, $1); set_type(aprepro, $1, token::SVAR); } | AVAR EQUAL sexp { $$ = $3; aprepro.redefine_array($1->value.avar); $1->value.svar= $3; redefined_warning(aprepro, $1); set_type(aprepro, $1, token::SVAR); } | IMMSVAR EQUAL sexp { $$ = (char*)$1->value.svar.c_str(); immutable_modify(aprepro, $1); } | IMMVAR EQUAL sexp { immutable_modify(aprepro, $1); YYERROR; } | SFNCT LPAR sexp RPAR { if (arg_check($1, $1->value.strfnct_c == NULL)) $$ = (char*)(*($1->value.strfnct_c))($3); else $$ = (char*)""; } | SFNCT LPAR RPAR { if (arg_check($1, $1->value.strfnct == NULL)) $$ = (char*)(*($1->value.strfnct))(); else $$ = (char*)""; } | SFNCT LPAR exp RPAR { if (arg_check($1, $1->value.strfnct_d == NULL)) $$ = (char*)(*($1->value.strfnct_d))($3); else $$ = (char*)""; } | SFNCT LPAR aexp RPAR { if (arg_check($1, $1->value.strfnct_a == NULL)) $$ = (char*)(*($1->value.strfnct_a))($3); else $$ = (char*)""; } | sexp CONCAT sexp { concat_string($1, $3, &$$); } | SFNCT LPAR exp COMMA exp RPAR { if (arg_check($1, $1->value.strfnct_dd == NULL)) $$ = (char*)(*($1->value.strfnct_dd))($3, $5); else $$ = (char*)""; } | SFNCT LPAR exp COMMA sexp COMMA sexp COMMA sexp COMMA sexp RPAR { if (arg_check($1, $1->value.strfnct_dcccc == NULL)) $$ = (char*)(*($1->value.strfnct_dcccc))($3, $5, $7, $9, $11); else $$ = (char*)""; } | SFNCT LPAR exp COMMA sexp COMMA sexp RPAR { if (arg_check($1, $1->value.strfnct_dcc == NULL)) $$ = (char*)(*($1->value.strfnct_dcc))($3, $5, $7); else $$ = (char*)""; } | SFNCT LPAR sexp COMMA sexp COMMA sexp RPAR { if (arg_check($1, $1->value.strfnct_ccc == NULL)) $$ = (char*)(*($1->value.strfnct_ccc))($3, $5, $7); else $$ = (char*)""; } | SFNCT LPAR sexp COMMA sexp RPAR { if (arg_check($1, $1->value.strfnct_cc == NULL)) $$ = (char*)(*($1->value.strfnct_cc))($3, $5); else $$ = (char*)""; } | bool QUEST sexp COLON sexp { $$ = ($1) ? ($3) : ($5); } exp: NUM { $$ = $1; } | INC NUM { $$ = $2 + 1; } | DEC NUM { $$ = $2 - 1; } | VAR { $$ = $1->value.var; } | IMMVAR { $$ = $1->value.var; } | INC VAR { $$ = ++($2->value.var); } | DEC VAR { $$ = --($2->value.var); } | VAR INC { $$ = ($1->value.var)++; } | VAR DEC { $$ = ($1->value.var)--; } | VAR EQUAL exp { $$ = $3; $1->value.var = $3; redefined_warning(aprepro, $1); } | SVAR EQUAL exp { $$ = $3; $1->value.var = $3; redefined_warning(aprepro, $1); set_type(aprepro, $1, token::VAR); } | AVAR EQUAL exp { $$ = $3; aprepro.redefine_array($1->value.avar); $1->value.var= $3; redefined_warning(aprepro, $1); set_type(aprepro, $1, token::VAR); } | VAR EQ_PLUS exp { $1->value.var += $3; $$ = $1->value.var; } | VAR EQ_MINUS exp { $1->value.var -= $3; $$ = $1->value.var; } | VAR EQ_TIME exp { $1->value.var *= $3; $$ = $1->value.var; } | VAR EQ_DIV exp { $1->value.var /= $3; $$ = $1->value.var; } | VAR EQ_POW exp { reset_error(); $1->value.var = std::pow($1->value.var,$3); $$ = $1->value.var; SEAMS::math_error(aprepro, "Power"); } | INC IMMVAR { $$ = $2->value.var; immutable_modify(aprepro, $2); } | DEC IMMVAR { $$ = $2->value.var; immutable_modify(aprepro, $2); } | IMMVAR INC { $$ = $1->value.var; immutable_modify(aprepro, $1); } | IMMVAR DEC { $$ = $1->value.var; immutable_modify(aprepro, $1); } | IMMVAR EQUAL exp { $$ = $1->value.var; immutable_modify(aprepro, $1); } | IMMSVAR EQUAL exp { immutable_modify(aprepro, $1); YYERROR; } | IMMVAR EQ_PLUS exp { $$ = $1->value.var; immutable_modify(aprepro, $1); } | IMMVAR EQ_MINUS exp { $$ = $1->value.var; immutable_modify(aprepro, $1); } | IMMVAR EQ_TIME exp { $$ = $1->value.var; immutable_modify(aprepro, $1); } | IMMVAR EQ_DIV exp { $$ = $1->value.var; immutable_modify(aprepro, $1); } | IMMVAR EQ_POW exp { $$ = $1->value.var; immutable_modify(aprepro, $1); } | UNDVAR { $$ = $1->value.var; undefined_error(aprepro, $1->name); } | INC UNDVAR { $$ = ++($2->value.var); set_type(aprepro, $2, token::VAR); undefined_error(aprepro, $2->name); } | DEC UNDVAR { $$ = --($2->value.var); set_type(aprepro, $2, token::VAR); undefined_error(aprepro, $2->name); } | UNDVAR INC { $$ = ($1->value.var)++; set_type(aprepro, $1, token::VAR); undefined_error(aprepro, $1->name); } | UNDVAR DEC { $$ = ($1->value.var)--; set_type(aprepro, $1, token::VAR); undefined_error(aprepro, $1->name); } | UNDVAR EQUAL exp { $$ = $3; $1->value.var = $3; set_type(aprepro, $1, token::VAR); } | UNDVAR EQ_PLUS exp { $1->value.var += $3; $$ = $1->value.var; set_type(aprepro, $1, token::VAR); undefined_error(aprepro, $1->name); } | UNDVAR EQ_MINUS exp { $1->value.var -= $3; $$ = $1->value.var; set_type(aprepro, $1, token::VAR); undefined_error(aprepro, $1->name); } | UNDVAR EQ_TIME exp { $1->value.var *= $3; $$ = $1->value.var; set_type(aprepro, $1, token::VAR); undefined_error(aprepro, $1->name); } | UNDVAR EQ_DIV exp { $1->value.var /= $3; $$ = $1->value.var; set_type(aprepro, $1, token::VAR); undefined_error(aprepro, $1->name); } | UNDVAR EQ_POW exp { reset_error(); $1->value.var = std::pow($1->value.var,$3); $$ = $1->value.var; set_type(aprepro, $1, token::VAR); SEAMS::math_error(aprepro, "Power"); undefined_error(aprepro, $1->name); } | FNCT LPAR RPAR { if (arg_check($1, $1->value.fnctptr == NULL)) $$ = (*($1->value.fnctptr))(); else $$ = 0.0; } | FNCT LPAR exp RPAR { if (arg_check($1, $1->value.fnctptr_d == NULL)) $$ = (*($1->value.fnctptr_d))($3); else $$ = 0.0; } | FNCT LPAR sexp RPAR { if (arg_check($1, $1->value.fnctptr_c == NULL)) $$ = (*($1->value.fnctptr_c))($3); else $$ = 0.0; } | FNCT LPAR aexp RPAR { if (arg_check($1, $1->value.fnctptr_a == NULL)) $$ = (*($1->value.fnctptr_a))($3); else $$ = 0.0; } | FNCT LPAR sexp COMMA exp RPAR { if (arg_check($1, $1->value.fnctptr_cd == NULL)) $$ = (*($1->value.fnctptr_cd))($3, $5); else $$ = 0.0; } | FNCT LPAR exp COMMA sexp RPAR { if (arg_check($1, $1->value.fnctptr_dc == NULL)) $$ = (*($1->value.fnctptr_dc))($3, $5); else $$ = 0.0; } | FNCT LPAR sexp COMMA sexp RPAR { if (arg_check($1, $1->value.fnctptr_cc == NULL)) $$ = (*($1->value.fnctptr_cc))($3, $5); else $$ = 0.0; } | FNCT LPAR sexp COMMA sexp COMMA sexp RPAR { if (arg_check($1, $1->value.fnctptr_ccc == NULL)) $$ = (*($1->value.fnctptr_ccc))($3,$5,$7); else yyerrok; } | FNCT LPAR exp COMMA exp RPAR { if (arg_check($1, $1->value.fnctptr_dd == NULL)) $$ = (*($1->value.fnctptr_dd))($3, $5); else $$ = 0.0; } | FNCT LPAR exp COMMA exp COMMA exp RPAR { if (arg_check($1, $1->value.fnctptr_ddd == NULL)) $$ = (*($1->value.fnctptr_ddd))($3, $5, $7); else $$ = 0.0; } | FNCT LPAR sexp COMMA sexp COMMA exp RPAR { if (arg_check($1, $1->value.fnctptr_ccd == NULL)) $$ = (*($1->value.fnctptr_ccd))($3, $5, $7); else $$ = 0.0; } | FNCT LPAR exp COMMA exp SEMI exp COMMA exp RPAR { if (arg_check($1, $1->value.fnctptr_dddd == NULL)) $$ = (*($1->value.fnctptr_dddd))($3, $5, $7, $9); else $$ = 0.0; } | FNCT LPAR exp COMMA exp COMMA exp COMMA exp RPAR { if (arg_check($1, $1->value.fnctptr_dddd == NULL)) $$ = (*($1->value.fnctptr_dddd))($3, $5, $7, $9); else $$ = 0.0; } | FNCT LPAR exp COMMA exp COMMA exp COMMA exp COMMA sexp RPAR { if (arg_check($1, $1->value.fnctptr_ddddc == NULL)) $$ = (*($1->value.fnctptr_ddddc))($3, $5, $7, $9, $11); else $$ = 0.0; } | FNCT LPAR exp COMMA exp COMMA exp COMMA exp COMMA exp COMMA exp RPAR { if (arg_check($1, $1->value.fnctptr_dddddd == NULL)) $$ = (*($1->value.fnctptr_dddddd))($3, $5, $7, $9, $11, $13); else $$ = 0.0; } | exp PLU exp { $$ = $1 + $3; } | exp SUB exp { $$ = $1 - $3; } | exp TIM exp { $$ = $1 * $3; } | exp DIV exp { if ($3 == 0.) { yyerror(aprepro, "Zero divisor"); yyerrok; } else $$ = $1 / $3; } | exp MOD exp { if ($3 == 0.) { yyerror(aprepro, "Zero divisor"); yyerrok; } else $$ = (int)$1 % (int)$3; } | SUB exp %prec UNARY { $$ = -$2; } | PLU exp %prec UNARY { $$ = $2; } | exp POW exp { reset_error(); $$ = std::pow($1, $3); SEAMS::math_error(aprepro, "Power"); } | LPAR exp RPAR { $$ = $2; } | LBRACK exp RBRACK { reset_error(); $$ = (double)($2 < 0 ? -floor(-($2)): floor($2) ); SEAMS::math_error(aprepro, "floor (int)"); } | bool { $$ = ($1) ? 1 : 0; } | bool QUEST exp COLON exp { $$ = ($1) ? ($3) : ($5); } | AVAR LBRACK exp RBRACK { $$ = array_value($1->value.avar, $3, 0); } | AVAR LBRACK exp COMMA exp RBRACK { $$ = array_value($1->value.avar, $3, $5); } | AVAR LBRACK exp RBRACK EQUAL exp { $$ = $6; array *arr = $1->value.avar; int cols = arr->cols; if (cols > 1) { yyerror(aprepro, "Cannot use [index] array access with multi-column array"); yyerrok; } int rows = arr->rows; int row = $3; if (aprepro.ap_options.one_based_index) { row--; } if (row < rows) { int offset = row*cols; $1->value.avar->data[offset] = $6; } else { yyerror(aprepro, "Row or Column index out of range"); yyerrok; } } | AVAR LBRACK exp COMMA exp RBRACK EQUAL exp { $$ = $8; array *arr = $1->value.avar; int cols = arr->cols; int rows = arr->rows; int row = $3; int col = $5; if (aprepro.ap_options.one_based_index) { row--; col--; } if (row < rows && col < cols) { int offset = row*cols+col; $1->value.avar->data[offset] = $8; } else { yyerror(aprepro, "Row or Column index out of range"); yyerrok; } } /* End of grammar */ %% void SEAMS::Parser::error(const std::string& m) { aprepro.error(m); }