diff options
author | Joe Hunkeler <jhunkeler@gmail.com> | 2015-08-11 16:51:37 -0400 |
---|---|---|
committer | Joe Hunkeler <jhunkeler@gmail.com> | 2015-08-11 16:51:37 -0400 |
commit | 40e5a5811c6ffce9b0974e93cdd927cbcf60c157 (patch) | |
tree | 4464880c571602d54f6ae114729bf62a89518057 /pkg/vocl/grammar.y | |
download | iraf-osx-40e5a5811c6ffce9b0974e93cdd927cbcf60c157.tar.gz |
Repatch (from linux) of OSX IRAF
Diffstat (limited to 'pkg/vocl/grammar.y')
-rw-r--r-- | pkg/vocl/grammar.y | 2108 |
1 files changed, 2108 insertions, 0 deletions
diff --git a/pkg/vocl/grammar.y b/pkg/vocl/grammar.y new file mode 100644 index 00000000..b6c56656 --- /dev/null +++ b/pkg/vocl/grammar.y @@ -0,0 +1,2108 @@ +%{ + +#define import_spp +#define import_libc +#define import_stdio +#define import_ctype +#include <iraf.h> + +#include "config.h" +#include "mem.h" +#include "operand.h" +#include "param.h" +#include "grammar.h" +#include "opcodes.h" +#include "clmodes.h" +#include "task.h" +#include "construct.h" +#include "errs.h" + + +/* CL parser, written as a yacc grammar: + * build up an (rpn) instruction sequence begining at the base of the + * operand stack as the grammar is recognized. + * + * The parser may be called during parameter initialization (initiated by + * the CALL meta-code instruction), and to parse the executable portion + * (from the EXEC instruction). + * + * CONSTANT's are put on the dictionary by addconst() rather than the operand + * stack to avoid conflict with the code being created. They are accessed + * by using the yylval of IDENT and CONSTANT as dictionary indices that + * point to struct operands. This is facilitated with the stkop() macro. + * Make sure that topd and topcs are restored on return to discard these + * temporaries. + * When building offsets for branches, such as BIFF and GOTO, allow + * for the advancement of the pc by the size of the instruction (in ints). + * See opcodes.c for the code executed by the branch instructions. + */ + +extern int cldebug; +#define lint /* turns off sccsid in Yacc parser */ + +/* shorthand way to get at operands in dictionary. x will be values returned + * from addconst() by way of $n's from CONSTANT and IDENT tokens; see gram.c + * and its uses in grammar.l. also see pushop() for a description of the stack. + */ +#define stkop(x) (reference (operand, (x))) + +int dobkg = 0; /* set when want to do code in bkground */ +int npipes = 0; /* number of pipes in a command */ +XINT pipe_pc = 0; /* pc of last ADDPIPE instruction */ +int posit = 0; /* positional argument count */ +int inarglist = 0; /* set when in argument list */ +int parenlevel = 0; /* level of paren nesting in command */ +int in_iferr = 0; /* in an iferr block */ +int cl_level = 0; /* CL calling level */ + +int index_cnt; /* Index counter in array ref's */ +char curr_param[SZ_FNAME]; /* Parameter name of ref's */ +char curr_task[SZ_FNAME]; /* ltaskname of command */ +XINT stmt_pc; /* PC at beginning of current statement */ +int varlist; /* Declaration is list directed. */ +int vartype; /* Type of declaration. */ +int do_params; /* Are param definitions legal here? */ +int errcnt; /* Syntax error count. */ +int inited; /* Was variable already initialized. */ +struct param *pp; /* Pointer to param being compiled. */ +int n_aval; /* Number of array init values. */ +int lastref; /* Was last ref an array? */ +int for_expr; /* Was there an expression in FOR? */ +char *ifseen; /* Have we just processed an IF? */ +char *errmsg; /* Syntax error message. */ + +/* context-sensitive switches. technique is ok, but beware of nesting! + */ +static int absmode = 0; /* set by first absolute mode arg in cmd*/ +static int newstdout = 0; /* set if stdout redirected in arg */ +static int bracelevel = 0; /* set while in s_list to inhibit & */ +static int tbrace = 0; /* fake braces for declarations */ +static int dobrace = 0; /* handling braces. */ +static int sawnl = 0; /* set when EOST was \n, else 0 */ +static int printstmt = 0; /* set when parsing FPRINT statement */ +static int scanstmt = 0; /* set when parsing SCAN statement */ +static int iferr_tok = 0; /* iferr/ifnoerr token type seen */ + +/* printf-format error messages. + */ +char *arrdeferr = "Error in array initialization for `%s'."; +char *badparm = "Parameter definition of `%s' is illegal here."; +char *inval_arr = "Invalid array type for `%s'."; +char *inv_index = "Invalid index definition for `%s'."; +char *twoinits = "Two initializations for parameter `%s'."; + +char *exlimits = "Explicit range required for loop in external param."; +char *illegalvar = "Illegal variable declarations."; +char *locallist = "Local list variables are not permitted."; +char *nestediferr = "Nested iferr not allowed in test or handler block."; +char *posfirst = "All positional arguments must be first"; + + +extern char cmdblk[SZ_CMDBLK+1]; /* Command buffer in history.c */ +extern char *ip_cmdblk; /* Pointer to current char in command.*/ +extern char *err_cmdblk; /* ip_cmdblk when error detected. */ + +char *index(); +struct param *initparam(); +struct label *getlabel(), *setlabel(); + +/* arbitrary large number for bracelevel in a procedure script + */ +#define MAX_ERR 10 +#define EYYERROR { err_cmdblk = ip_cmdblk; YYERROR; } + +%} + +%token Y_SCAN Y_SCANF Y_FSCAN Y_FSCANF Y_OSESC +%token Y_APPEND Y_ALLAPPEND Y_ALLREDIR Y_GSREDIR Y_ALLPIPE +%token D_D D_PEEK +%token Y_NEWLINE Y_CONSTANT Y_IDENT +%token Y_WHILE Y_IF Y_ELSE +%token Y_FOR Y_BREAK Y_NEXT +%token Y_SWITCH Y_CASE Y_DEFAULT +%token Y_RETURN Y_GOTO +%token Y_PROCEDURE Y_BEGIN Y_END +%token Y_BOOL Y_INT Y_REAL Y_STRING Y_FILE Y_STRUCT +%token Y_GCUR Y_IMCUR Y_UKEY Y_PSET +%token Y_IFERR Y_IFNOERR Y_THEN + +%right '=' YOP_AOADD YOP_AOSUB YOP_AOMUL YOP_AODIV YOP_AOCAT +%left YOP_OR +%left YOP_AND +%left YOP_EQ YOP_NE +%left '<' '>' YOP_LE YOP_GE +%left YOP_CONCAT +%left '+' '-' +%left '*' '/' '%' +%left YOP_NOT UMINUS /* supplies precedence for unary minus */ +%left YOP_POW + +%start block + +%% + +block : /* empty */ { + /* Done once on entry but after at least one call to + * yylex(). Good for initing parser flags. + * Note: this does not get called in procedure scripts. + */ + if (cldebug) + eprintf ("parse init (block)...\n"); + + errcnt = 0; + err_cmdblk = 0; + dobkg = 0; + inarglist = 0; + parenlevel = 0; + bracelevel = 0; + tbrace = 0; + dobrace = 0; + in_iferr = 0; + do_params = YES; + last_parm = NULL; + ifseen = NULL; + label1 = NULL; + errmsg = NULL; + parse_pfile= currentask->t_pfp; + } + + | '.' NL { + /* Prepare to rerun whatever was compiled last. + * Does not work for the debug commands builtin here. + */ + if (parse_state != PARSE_FREE) { + errmsg = "Illegal parser state."; + EYYERROR; + } + rerun(); + YYACCEPT; + } + + | block { + if (parse_state == PARSE_PARAMS) { + errmsg = "Illegal parser state."; + EYYERROR; + } + } + debug xstmt { + if (sawnl && bracelevel == 0) { + if (!errcnt) + compile (END); + if (ifseen) { + /* Simulate an unput of what has been read + * from the current line. + */ + ip_cmdblk = ifseen; + } + YYACCEPT; + } + } + + | script_params { + /* Parse the parameters in a script file. This will + * normally be done on a call by pfileread(). + */ + if (parse_state != PARSE_PARAMS) { + eprintf ("Illegal parser state.\n"); + errcnt++; + } + YYACCEPT; + } + + | script_body { + /* Parse the executable statements in a script. + */ + if (parse_state != PARSE_BODY) { + eprintf ("Illegal parser state.\n"); + errcnt++; + } + if (!errcnt) + compile (END); + YYACCEPT; + } + + | error NL { + /* This catches errors that the two other error lines + * can't get, e.g. a missing `}' at the end of a script, + * or errors occuring in interactive input. + */ + yyerrok; + + /* Discard everything and compile a null statement. + */ + if (!errcnt) { + do_params = YES; + pc = currentask->t_bascode; + if (parse_state != PARSE_PARAMS) + compile (END); + + topd = currentask->t_topd; + topcs = currentask->t_topcs; + + /* Unlink any added parms. Resetting of topd will + * already have reclaimed space. + */ + if (last_parm) { + last_parm->p_np = NULL; + currentask->t_pfp->pf_lastpp = last_parm; + last_parm = NULL; + } + } + + /* Print cmdblk and show position of error. + */ + p_position(); + if (currentask->t_flags & T_SCRIPT) + cl_error (E_UERR, "syntax error, line %d", + currentask->t_scriptln); + else + cl_error (E_UERR, "syntax error"); + + YYACCEPT; + } + ; + +debug : /* empty */ + | D_XXX EOST { + /* debug are those debugging functions that + * should be run directly and not through a + * builtin task due to stack or other changes, + * ie, don't change what we are trying to show. + */ + printf ("\n"); + } debug + ; + +D_XXX : D_D { + d_d(); /* show dictionary/stack pointers */ + } + | D_PEEK Y_CONSTANT { /* show a dictionary location */ + if (stkop($2)->o_type & OT_INT) { + int idx; + idx = stkop($2)->o_val.v_i; + eprintf ("%d:\t%d (0%o)\n", idx, stack[idx], + stack[idx]); + } else + eprintf ("usage: D_PEEK <d. index>\n"); + } + | '~' { + d_stack (pc, 0, 0); /* show compiled code */ + } + ; + +script_params : proc_stmt + var_decls + begin_stmt { + /* Check for required params. + */ + if (!errcnt) + proc_params(n_procpar); + } + ; + +script_body: begin_stmt { + /* Initialize parser for procedure body. + */ + if (cldebug) + eprintf ("parse init (script_body)...\n"); + + errcnt = 0; + err_cmdblk = 0; + dobkg = 0; + inarglist = 0; + parenlevel = 0; + in_iferr = 0; + dobrace = 0; + bracelevel = PBRACE; /* disable lexmodes; force "end" */ + tbrace = 0; + do_params = NO; + last_parm = NULL; + ifseen = NULL; + label1 = NULL; + parse_pfile= currentask->t_pfp; + } + s_list + opnl + end_stmt + ; + +proc_stmt: Y_PROCEDURE { + /* Initialize parser for procedure parameters. + */ + if (cldebug) + eprintf ("parse init (proc_stmt)...\n"); + + errcnt = 0; + err_cmdblk = 0; + dobkg = 0; + inarglist = 0; + parenlevel = 0; + bracelevel = PBRACE; + tbrace = 0; + dobrace = 0; + in_iferr = 0; + do_params = YES; + last_parm = NULL; + label1 = NULL; + } + param bparam_list EOST + ; + +bparam_list: /* Nothing at all, not even parens. */ + { + n_procpar = 0; + } + | LP param_list RP + ; + +/* The definition of the parameter list excludes lists of the + * form a,,b + */ +param_list: /* empty */ { + n_procpar = 0; + } + | xparam_list + ; + +xparam_list: param { + n_procpar = 1; + if (!errcnt) + push (stkop($1)); + } + | xparam_list DELIM param { + n_procpar++; + if (!errcnt) + push (stkop($3)); + } + ; + +var_decls: /* No params. */ + | var_decl_block + ; + +var_decl_block: var_decl_line + | var_decl_block var_decl_line + ; + +var_decl_line: EOST /* Blank. */ + | var_decl_stmt + | error NL { + /* This catches errors in the parameter declarations + * of a procedure script. + */ + yyerrok; + + /* Discard everything and compile a null statement. + */ + if (!errcnt) { + do_params = YES; + pc = currentask->t_bascode; + if (parse_state != PARSE_PARAMS) + compile (END); + + topd = currentask->t_topd; + topcs = currentask->t_topcs; + + /* Unlink any added parms. Resetting of topd will + * already have reclaimed space. + */ + if (last_parm) { + last_parm->p_np = NULL; + currentask->t_pfp->pf_lastpp = last_parm; + last_parm = NULL; + } + } + + /* Print cmdblk and show position of error. We know + * we're parsing a procedure script, so print the line + * number too. + */ + p_position(); + cl_error (E_UERR, "syntax error, line %d", + currentask->t_scriptln); + } + ; + +var_decl_stmt: typedefs { + /* For in-line definitions we don't want + * to freeze stuff on the dictionary, so + * only allow additions if the dictionary + * is the same as at the beginning of the task. + */ + if (!errcnt) { + if (parse_state != PARSE_PARAMS) { + if (currentask->t_topd != topd) + cl_error (E_UERR, illegalvar); + last_parm = currentask->t_pfp->pf_lastpp; + } + } + + /* Increment bracelevel temporarily to defeat command + * mode, in case this is an in-line declaration and + * lexmodes=yes. + */ + bracelevel += PBRACE; + tbrace++; + + } var_decl_list EOST { + /* Update dictionary to include these definitions. + */ + if (!errcnt) { + if (parse_state != PARSE_PARAMS) { + currentask->t_topd = topd; + last_parm = 0; + } + } + + /* Restore command mode */ + bracelevel -= PBRACE; + tbrace--; + } + ; + +typedefs: Y_BOOL { vartype = V_BOOL; } + | Y_STRING { vartype = V_STRING; } + | Y_REAL { vartype = V_REAL; } + | Y_FILE { vartype = V_FILE; } + | Y_GCUR { vartype = V_GCUR; } + | Y_IMCUR { vartype = V_IMCUR; } + | Y_UKEY { vartype = V_UKEY; } + | Y_PSET { vartype = V_PSET; } + | Y_INT { vartype = V_INT; } + | Y_STRUCT { vartype = V_STRUCT; } + ; + +var_decl_list: var_decl_plus + | var_decl_plus DELIM var_decl_list + ; + +var_decl_plus: var_decl { + if (!errcnt) { + if (pp != NULL) { + if (n_aval > 1) + pp->p_type |= PT_ARRAY; + + if (pp->p_type & PT_ARRAY) + do_arrayinit (pp, n_aval, index_cnt); + else + do_scalarinit (pp, inited); + } + } + } + + /* Semi-colon in following rule is not input by user, but + * rather by lexical analyzer to help close compound + * statements. + */ + | var_decl '{' options_list ';' '}' { + if (!errcnt) { + if (pp != NULL) { + if (!do_params) + cl_error (E_UERR, badparm, pp->p_name); + + if (n_aval > 1) + pp->p_type |= PT_ARRAY; + + if (pp->p_type & PT_ARRAY) + do_arrayinit (pp, n_aval, index_cnt); + else + do_scalarinit (pp, n_aval); + } + } + } + ; + +var_decl: var_def { + inited = NO; + n_aval = 0; + } + | var_def '=' { + n_aval = 0; + } + init_list { + inited = YES; + } + ; + +var_def : var_name { + index_cnt = 0; + if (!errcnt) + pp = initparam (stkop($1), do_params, vartype, varlist); + } + | var_name { + int itemp; + + if (!errcnt) { + pp = initparam (stkop($1), do_params, vartype, varlist); + + if (pp != NULL) { + itemp = (pp->p_type & OT_BASIC) == pp->p_type; + itemp = itemp && !varlist; + if (itemp) + pp->p_type |= PT_ARRAY; + else + cl_error (E_UERR, inval_arr, pp->p_name); + } + } + } + '[' init_index_list ']' + ; + +var_name: param { + varlist = NO; + index_cnt = 0; + } + | '*' param { + if (!do_params) { + errmsg = locallist; + EYYERROR; + } + varlist = YES; + index_cnt = 0; + $$ = $2; + } + ; + +init_index_list: + /* A null index list means get the length of the array + * from the initializer. + */ + | init_index_range + | init_index_list DELIM init_index_range + ; + +init_index_range: + const { + if (!errcnt) { + if (pp != NULL) { + if (stkop($1)->o_type == OT_INT) { + push (stkop($1)->o_val.v_i); + push (1); + } else if (maybeindex) { + /* Confusion between sexagesimal and index + * range. Maybeindex is set only when operand + * is real. + */ + int i1,i2; + sexa_to_index (stkop($1)->o_val.v_r, &i1, &i2); + push (i2-i1+1); + push (i1); + } else { + eprintf (inv_index, pp->p_name); + EYYERROR; + } + index_cnt++; + } + } + } + | const ':' const { + if (!errcnt) { + if (pp != NULL) { + if (stkop($1)->o_type != OT_INT || + stkop($3)->o_type != OT_INT) + cl_error (E_UERR, inv_index, pp->p_name); + else { + push (stkop($3)->o_val.v_i - + stkop($1)->o_val.v_i + 1); + push (stkop($1)->o_val.v_i); + } + index_cnt++; + } + } + } + ; + +init_list: init_elem + | init_list DELIM init_elem + ; + +init_elem: const { + if (!errcnt) { + if (pp != NULL) { + push (stkop($1) ); + n_aval++; + } + } + } + | Y_CONSTANT LP const RP /* PL/I notation. */ + { + int cnt; + + if (!errcnt) + if (pp != NULL) { + if (stkop($1)->o_type != OT_INT) + cl_error (E_UERR, arrdeferr, pp->p_name); + + cnt = stkop($1)->o_val.v_i; + if (cnt <= 0) + cl_error (E_UERR, arrdeferr, pp->p_name); + + while (cnt-- > 0) { + push (stkop($3)); + n_aval++; + } + } + } + ; + +const : Y_CONSTANT + | number + ; + +/* The parser and lexical analyzer don't see negative numbers as an + * entity. So we must join signs to their constants. + */ +number : sign Y_CONSTANT { + if (stkop($2)->o_type == OT_INT) { + stkop($2)->o_val.v_i *= $1; + $$ = $2; + } else if (stkop($2)->o_type == OT_REAL) { + stkop($2)->o_val.v_r *= $1; + $$ = $2; + } else { + errmsg = "Invalid constant in declaration."; + EYYERROR; + } + } + ; + +sign : '+' { $$ = 1; } + | '-' { $$ = -1; } + +options_list: init_list DELIM options { + /* Check if we already had an initialization. + */ + if (!errcnt) { + if (inited && pp != NULL) { + eprintf (twoinits, pp->p_name); + EYYERROR; + } + } + } + | init_list { + if (!errcnt) { + if (inited && pp != NULL) { + eprintf (twoinits, pp->p_name); + EYYERROR; + } + } + } + | options + ; + +options : option + | options DELIM option + ; + +option : Y_IDENT '=' const { + if (!errcnt) + if (pp != NULL) + do_option (pp, stkop($1), stkop($3)); + } + ; + +begin_stmt: Y_BEGIN NL + ; + +/* In normal expressions, a param means the name of a parameter, but in + * command line arguments, it may be a string constant. Pull out param + * from expr to let the arg rule deal with it specially. + */ + +expr : expr0 + | ref { + if (!errcnt) + compile (PUSHPARAM, stkop($1)->o_val.v_s); + } + ; + +/* EXPR0 is everything but a simple parameter. This is needed for argument + * lists so that a simple parameter may be treated as a special case of a + * string constant. EXPR1 also excludes constants. This is needed + * to eliminate ambiguities in the grammar which would arise from + * the handling of the lexical ambiguity of sexagesimal constants + * and array index ranges. + */ +expr0 : expr1 + | Y_CONSTANT { + if (!errcnt) + compile (PUSHCONST, stkop($1)); + } + | Y_GCUR { + /* "gcur" is both a keyword and a CL global parameter, + * and must be built into the grammar here to permit + * reference of the parameter in expressions. + */ + if (!errcnt) + compile (PUSHPARAM, "gcur"); + } + | Y_IMCUR { + if (!errcnt) + compile (PUSHPARAM, "imcur"); + } + | Y_UKEY { + if (!errcnt) + compile (PUSHPARAM, "ukey"); + } + | Y_PSET { + if (!errcnt) + compile (PUSHPARAM, "pset"); + } + ; + +expr1 : LP expr RP + + | expr '+' opnl expr { + if (!errcnt) + compile (ADD); + } + | expr '-' opnl expr { + if (!errcnt) + compile (SUB); + } + | expr '*' opnl expr { + if (!errcnt) + compile (MUL); + } + | expr '/' opnl expr { + if (!errcnt) + compile (DIV); + } + | expr YOP_POW opnl expr { + if (!errcnt) + compile (POW); + } + | expr '%' opnl expr { + struct operand o; + if (!errcnt) { + o.o_type = OT_INT; + o.o_val.v_i = 2; + compile (PUSHCONST, &o); + compile (INTRINSIC, "mod"); + } + } + | expr YOP_CONCAT opnl expr { + if (!errcnt) + compile (CONCAT); + } + | expr '<' opnl expr { + if (!errcnt) + compile (LT); + } + | expr '>' opnl expr { + if (!errcnt) + compile (GT); + } + | expr YOP_LE opnl expr { + if (!errcnt) + compile (LE); + } + | expr YOP_GE opnl expr { + if (!errcnt) + compile (GE); + } + | expr YOP_EQ opnl expr { + if (!errcnt) + compile (EQ); + } + | expr YOP_NE opnl expr { + if (!errcnt) + compile (NE); + } + | expr YOP_OR opnl expr { + if (!errcnt) + compile (OR); + } + | expr YOP_AND opnl expr { + if (!errcnt) + compile (AND); + } + | YOP_NOT expr { + if (!errcnt) + compile (NOT); + } + | '-' expr %prec UMINUS { + if (!errcnt) + compile (CHSIGN); + } + + | Y_SCAN LP { + /* Free format scan. */ + if (!errcnt) + push (0); /* use control stack to count args */ + } scanarg RP { + if (!errcnt) { + struct operand o; + o.o_type = OT_INT; + o.o_val.v_i = pop(); /* get total number of args*/ + compile (PUSHCONST, &o); + compile (SCAN); + } + } + | Y_SCANF LP { + /* Formatted scan. */ + if (!errcnt) + push (0); /* use control stack to count args */ + } scanfmt DELIM scanarg RP { + if (!errcnt) { + struct operand o; + + /* Compile number of arguments. */ + o.o_type = OT_INT; + o.o_val.v_i = pop(); + compile (PUSHCONST, &o); + + compile (SCANF); + } + } + + | Y_FSCAN LP { + /* Free format scan from a parameter. */ + if (!errcnt) + push (0); /* use control stack to count args */ + } scanarg RP { + if (!errcnt) { + struct operand o; + o.o_type = OT_INT; + o.o_val.v_i = pop(); /* get total number of args*/ + compile (PUSHCONST, &o); + compile (FSCAN); + } + } + + | Y_FSCANF LP Y_IDENT DELIM { + /* Formatted scan from a parameter. + * fscanf (param, format, arg1, ...) + */ + if (!errcnt) { + compile (PUSHCONST, stkop ($3)); + push (1); /* use control stack to count args */ + } + } scanfmt DELIM scanarg RP { + if (!errcnt) { + struct operand o; + + /* Compile number of arguments. */ + o.o_type = OT_INT; + o.o_val.v_i = pop(); + compile (PUSHCONST, &o); + + compile (FSCANF); + } + } + + | intrinsx LP { + if (!errcnt) + push (0); /* use control stack to count args */ + } intrarg RP { + if (!errcnt) { + struct operand o; + o.o_type = OT_INT; + o.o_val.v_i = pop(); + compile (PUSHCONST, &o); + compile (INTRINSIC, stkop($1)->o_val.v_s); + } + } + ; + +/* Variable types are keywords, so any types which are also intrinsic + * functions are added here. + */ +intrinsx: intrins + | Y_INT { + /* The YACC value of this must match normal intrinsics + * so we must generate an operand with the proper + * string. + */ + if (!errcnt) + $$ = addconst ("int", OT_STRING); + } + | Y_REAL { + if (!errcnt) + $$ = addconst ("real", OT_STRING); + } + ; + +scanfmt : expr { + if (!errcnt) { + push (pop() + 1); /* inc num args */ + } + } + ; + +scanarg : /* empty. This is bad for scan but we don't want to + * generate a cryptic syntax error. See also intrarg. + * This rule reduces the strings from right to left. + * Note the lexical analyzer strips optional newlines + * after comma delimiters, so we don't need an opnl here. + */ + | Y_IDENT { + if (!errcnt) { + compile (PUSHCONST, stkop ($1)); + push (pop() + 1); /* inc num args */ + } + } + | Y_IDENT DELIM scanarg { + if (!errcnt) { + compile (PUSHCONST, stkop ($1)); + push (pop() + 1); /* inc num args */ + } + } + ; + +intrarg : /* empty. this is to allow () but it also allows + * (x,,x). may want to prune this out. + */ + | expr { + if (!errcnt) + push (pop() + 1); /* inc num args */ + } + | intrarg DELIM expr { + if (!errcnt) + push (pop() + 1); /* inc num args */ + } + ; + + +/* Statements. */ + +stmt : c_stmt + | assign EOST + | cmdlist EOST + | immed EOST + | inspect EOST + | osesc EOST + | popstk EOST + | if + | ifelse + | iferr + | iferr_else + | while + | for + | switch + | case + | default + | next EOST + | break EOST + | goto EOST + | return EOST + | label_stmt + | nullstmt + ; + + /* A compound statement may be followed by zero or one + * newlines. + */ +c_stmt : c_blk + | c_blk NL + ; + +c_blk : '{' { + bracelevel++; + } s_list opnl { + --bracelevel; + } '}' + ; + +s_list : /* empty */ + | s_list opnl xstmt + ; + +/* Put "implicit" parentheses around right hand side of assignments to + * permit easy arithmetic even with lexmodes=yes. + */ +assign : ref equals expr0 { + --parenlevel; + if (!errcnt) + compile (ASSIGN, stkop($1)->o_val.v_s); + } + | ref equals ref { + /* Old code pushed a constant rather than a param + * when not within braces. This doesn't seem + * to be what most people want. + */ + --parenlevel; + if (!errcnt) { + compile (PUSHPARAM, stkop($3)->o_val.v_s); + compile (ASSIGN, stkop($1)->o_val.v_s); + } + } + | ref { + parenlevel++; + } + assign_oper expr { + --parenlevel; + if (!errcnt) + compile ($3, stkop($1)->o_val.v_s); + } + ; + + /* Breaking out the '=' avoids grammar ambiguities. + */ +equals : '=' { + parenlevel++; + } + ; + +assign_oper: YOP_AOADD { $$ = ADDASSIGN; } + | YOP_AOSUB { $$ = SUBASSIGN; } + | YOP_AOMUL { $$ = MULASSIGN; } + | YOP_AODIV { $$ = DIVASSIGN; } + | YOP_AOCAT { $$ = CATASSIGN; } + ; + +cmdlist : command { + npipes = 0; + } cmdpipe { + if (!errcnt) { + compile (EXEC); + if (npipes > 0) + compile (RMPIPES, npipes); + } + } + ; + +cmdpipe : /* empty */ + | cmdpipe pipe { + /* Pipefiles must be allocated at run time using a stack + * to permit pipe commands within loops, and to permit + * scripts called in a pipe to themselves contain pipe + * commands. ADDPIPE allocates a new pipefile on the + * pipe stack and pushes its name on the operand stack. + * GETPIPE pushes the pipefile at the top of the pipe + * stack onto the operand stack. RMPIPES removes N pipes + * from the pipe stack, and deletes the physical pipefiles. + */ + + if (!newstdout) { + /* When the runtime code creates the pipe it needs to + * know the identity of the two tasks sharing the pipe + * to determine what type of pipe to create (text or + * binary). Save the pc of the ADDPIPE instruction + * so that we can backpatch it below with a pointer to + * the name of the second task in the pipe (ADDPIPE + * will be called during startup of the first task + * hence will know its name). + */ + pipe_pc = compile (ADDPIPE, NULL); + + if ($2 == 1) + compile (REDIR); + else + compile (ALLREDIR); + compile (EXEC); + + } else { + eprintf ("multiple redirection\n"); + YYERROR; + } + + } command { + /* Compile the GETPIPE instruction with the name of the + * second task in the current pipe, and backpatch the + * matching ADDPIPE instruction with the PC of the GETPIPE. + */ + (coderef(pipe_pc))->c_args = compile (GETPIPE, curr_task); + compile (REDIRIN); + npipes++; /* Overflow checking is in ADDPIPE */ + } + ; + +pipe : '|' opnl { + $$ = 1; + } + | Y_ALLPIPE opnl { + $$ = 2; + } + ; + +command : tasknam { + char *ltname; + + ltname = stkop($1)->o_val.v_s; + compile (CALL, ltname); + strcpy (curr_task, ltname); + + /* The FPRINT task is special; the first arg + * is the destination and must be compiled as + * a string constant no matter what. Set flag + * so that 'arg' compiles PUSHCONST. + */ + printstmt = (strcmp (ltname, "fprint") == 0); + + /* Ditto with SCAN; all the arguments are call by + * reference and must be compiled as string constants. + */ + scanstmt = (strcmp (ltname, "scan") == 0 || + strcmp (ltname, "scanf") == 0); + + absmode = 0; + posit = 0; + newstdout = 0; + parenlevel = 0; + } BARG { + inarglist = 1; + } args EARG { + extern char *onerr_handler; + + inarglist = 0; + parenlevel = 0; + scanstmt = 0; + } + ; + +args : DELIM { + /* (,x) equates to nargs == 2. Call posargset with + * negative dummy argument to bump nargs. + */ + if (!errcnt) { + compile (POSARGSET, -1); + posit++; + printstmt = 0; + scanstmt = 0; + } + } arglist + | arglist + ; + +arglist : arg + | arglist DELIM arg + ; + +arg : /* nothing - compile a null posargset to bump nargs */ + { + if (!errcnt) { + if (posit > 0) { /* not first time */ + compile (POSARGSET, -posit); + printstmt = 0; + scanstmt = 0; + } + posit++; + } + } + | expr0 { + if (absmode) { + errmsg = posfirst; + EYYERROR; + } else + if (!errcnt) + compile (POSARGSET, posit++); + } + | ref { + if (absmode) { + errmsg = posfirst; + EYYERROR; + } else if (!errcnt) { + if (scanstmt) { + char pname[SZ_FNAME]; + char *pk, *t, *p, *f; + struct pfile *pfp; + struct operand o; + + /* If no task name specified check the pfile for + * the task containing the scan statement for the + * named parameter. + */ + breakout (stkop($1)->o_val.v_s, &pk, &t, &p, &f); + pfp = currentask->t_pfp; + if (*pk == NULL && *t == NULL && + pfp && paramfind(pfp,p,0,1)) { + + sprintf (pname, "%s.%s", + currentask->t_ltp->lt_lname, p); + if (*f) { + strcat (pname, "."); + strcat (pname, f); + } + } else + strcpy (pname, stkop($1)->o_val.v_s); + + o = *(stkop($1)); + o.o_val.v_s = pname; + compile (PUSHCONST, &o); + compile (INDIRPOSSET, posit++); + + } else if (parenlevel == 0 || printstmt) { + compile (PUSHCONST, stkop($1)); + compile (INDIRPOSSET, posit++); + /* only first arg of fprint stmt is special. */ + printstmt = 0; + + } else { + compile (PUSHPARAM, stkop($1)->o_val.v_s); + compile (POSARGSET, posit++); + } + } + } + | ref '=' expr0 { + absmode++; + if (!errcnt) + compile (ABSARGSET, stkop($1)->o_val.v_s); + } + | ref '=' ref { + absmode++; + if (!errcnt) { + if (parenlevel == 0) { + compile (PUSHCONST, stkop($3)); + compile (INDIRABSSET, stkop($1)->o_val.v_s); + } else { + compile (PUSHPARAM, stkop($3)->o_val.v_s); + compile (ABSARGSET, stkop($1)->o_val.v_s); + } + } + } + | param '+' { + absmode++; + if (!errcnt) + compile (SWON, stkop($1)->o_val.v_s); + } + | param '-' { + absmode++; + if (!errcnt) + compile (SWOFF, stkop($1)->o_val.v_s); + } + | '<' file { + if (!errcnt) + compile (REDIRIN); + } + | '>' file { + newstdout++; + if (!errcnt) + compile (REDIR); + } + | Y_ALLREDIR file { + newstdout++; + if (!errcnt) + compile (ALLREDIR); + } + | Y_APPEND file { + newstdout++; + if (!errcnt) + compile (APPENDOUT); + } + | Y_ALLAPPEND file { + newstdout++; + if (!errcnt) + compile (ALLAPPEND); + } + | Y_GSREDIR file { + if (!errcnt) + compile (GSREDIR, stkop($1)->o_val.v_s); + } + ; + +file : expr0 { + absmode++; + /* constant already pushed by expr0. + */ + } + | param { + absmode++; + if (!errcnt) { + if (parenlevel == 0) + compile (PUSHCONST, stkop($1)); + else + compile (PUSHPARAM, stkop($1)->o_val.v_s); + } + } + ; + +immed : equals expr0 { + --parenlevel; + if (!errcnt) + compile (IMMED); + } + | equals ref { + --parenlevel; + if (!errcnt) + compile (INSPECT, stkop($2)->o_val.v_s); + } + ; + +inspect : ref equals { + --parenlevel; + if (!errcnt) + compile (INSPECT, stkop($1)->o_val.v_s); + } + ; + +osesc : Y_OSESC { + if (!errcnt) + compile (OSESC, stkop($1)->o_val.v_s); + } + ; + +popstk : equals { + --parenlevel; + if (!errcnt) + compile (IMMED); + } + ; + +/* IFERR checking code. + */ + +iferr: iferr_stat { + /* pop BIFF addr and set branch to just after statement */ + if (!errcnt) { + XINT biffaddr = pop(); + coderef (biffaddr)->c_args = pc - biffaddr - SZ_CE; + } + in_iferr = 0; + } + ; + +iferr_stat: iferr_tok { + if (++in_iferr > 1) { + errmsg = nestediferr; + EYYERROR; + } + compile (CALL, "_errpsh"); + compile (EXEC); + + } c_blk { + if (!errcnt) { + struct operand o; + + o.o_type = OT_INT; + o.o_val.v_i = 0; + compile (PUSHCONST, &o); /* if (_errpop() != 0) */ + compile (INTRINSIC, "_errpop"); + compile (PUSHCONST, &o); + compile (((iferr_tok == 0) ? NE : EQ)); + push (compile (BIFF, 0)); + } + } op_then opnl xstmt { + in_iferr--; + } + ; + +iferr_else : iferr_stat Y_ELSE { + if (!errcnt) { + /* Pop and save BIFF address, compile and push addr + * of GOTO, and set BIFF branch to just after GOTO. + */ + XINT biffaddr = pop(); + push (compile (GOTO, 0)); + coderef (biffaddr)->c_args = pc - biffaddr - SZ_CE; + } + + } opnl xstmt { + if (!errcnt) { + /* Pop GOTO addr and set branch to just after statement + */ + XINT gotoaddr = pop(); + coderef (gotoaddr)->c_args = pc - gotoaddr - SZ_CE; + } + } + ; + +iferr_tok: Y_IFERR { iferr_tok = 0; } + | Y_IFNOERR { iferr_tok = 1; } + ; + +op_then: /* empty */ + | Y_THEN + ; + +/* END IFERR checking rules. + */ + + +if : if_stat { + /* pop BIFF addr and set branch to just after statement + */ + XINT biffaddr; + if (!errcnt) { + biffaddr = pop(); + coderef (biffaddr)->c_args = pc - biffaddr - SZ_CE; + } + } + ; + +if_stat : Y_IF LP expr RP { + /* save BIFF addr so branch can be filled in + */ + if (!errcnt) + push (compile (BIFF, 0)); + } opnl xstmt { + /* The shift/reduce conflict in the IF-IF/ELSE + * construct can cause errors in compilation + * because the IF statement can also be a + * terminal symbol, i.e. it may be all that + * is parsed in one call to the parser. + * The parser must look ahead one token + * to find if there is an else statement + * following. If there is no following + * token an EOF may be detected prematurely. + * When the IF statement is being parsed not + * inside any braces, then when the next token + * is not an ELSE care must be taken that this + * token is seen on a subsequent invocation + * of the parser. The `ifseen' flag is + * used within the support for the lexical + * analyzer located in `history.c'. + */ + if (cldebug) + eprintf ("ytab: setting ifseen=yes\n"); + + if (currentask->t_flags & T_INTERACTIVE) + ifseen = ip_cmdblk; + else + ifseen = cmdblk; + } + ; + +ifelse : if_stat Y_ELSE { + XINT biffaddr; + + ifseen = NULL; + if (!errcnt) { + /* Pop and save BIFF address, compile and push addr + * of GOTO, and set BIFF branch to just after GOTO. + */ + biffaddr = pop(); + push (compile (GOTO, 0)); + coderef (biffaddr)->c_args = pc - biffaddr - SZ_CE; + } + } opnl xstmt { + XINT gotoaddr; + if (!errcnt) { + /* Pop GOTO addr and set branch to just after statement + */ + gotoaddr = pop(); + coderef (gotoaddr)->c_args = pc - gotoaddr - SZ_CE; + } + } + ; + +while : Y_WHILE LP { + /* Save starting addr of while expression. + */ + if (!errcnt) { + push (pc); + loopincr(); + } + } expr RP { + /* Save BIFF addr so branch can be filled in. + */ + if (!errcnt) + push (compile (BIFF, 0)); + } opnl xstmt { + XINT biffaddr; + + if (!errcnt) { + /* Pop and save addr of BIFF instruction. */ + biffaddr = pop(); + /* Pop addr of expression and build a goto there. */ + compile (GOTO, pop() - pc - SZ_CE); + /* Now can set BIFF branch to just after statement.*/ + coderef (biffaddr)->c_args = pc - biffaddr - SZ_CE; + loopdecr(); + } + } + ; + + /* The line of code: + * + * for (e1, e2, e3) stmt + * + * is compiled into: + * + * e1 + * loop1: if (!e2) goto end + * goto loop3 + * loop2: e3 + * goto loop1 + * loop3: stmt + * goto loop2 + * end: + * + * Note that e1 and e3 are assignments while e2 is an expression. + */ + +for : Y_FOR LP opnl xassign ';' opnl { + if (!errcnt) + push(pc); /* Loop1: */ + } + xexpr ';' opnl { + if (!errcnt) { + if (for_expr) + ppush (compile(BIFF, 0)); /* if (!e2) */ + + /* Add SZ_CE to skip following GOTO. + */ + ppush (pc+SZ_CE); /* Loop2: */ + ppush (compile(GOTO,0)); /* goto Loop3 */ + + /* Save current location as the destination + * for NEXT statements. + */ + loopincr(); + } + } + xassign RP opnl { + XINT stmtaddr; + + if (!errcnt) { + stmtaddr = pop(); + compile (GOTO, stmtaddr-pc-SZ_CE); /* Goto loop1 */ + stmtaddr = pop(); + coderef(stmtaddr)->c_args = pc - stmtaddr - SZ_CE; + } + } + stmt { + XINT stmtaddr; + + if (!errcnt) { + stmtaddr = pop(); + compile (GOTO, stmtaddr-pc-SZ_CE); /* goto loop2 */ + + if (for_expr) { + stmtaddr = pop(); + coderef(stmtaddr)->c_args = pc-stmtaddr-SZ_CE; + } + loopdecr(); + } + } + ; + +/* The following allow skipping of fields in the FOR statement. + */ + +xassign : assign + | /* empty */ + ; + +xexpr : expr { + for_expr = YES; + } + | /* empty */ { + for_expr = NO; + } + ; + + /* The compiled code for the switch statement + * consists of a SWITCH, followed by a series of + * CASE and DEFAULT blocks, followed by a jump table. + * The first operand in each CASE and DEFAULT block + * is a CASE or DEFAULT operand which is never + * executed, but is used to store the values which + * will enter this block. Executable statements + * follow. + * + * The jump table consists of the addresses of the + * CASE and DEFAULT blocks. The DEFAULT block + * comes first, and is 0 if no default has + * been given. The list of addresses is terminated + * by a 0 address. + * + * The last statement of each CASE and DEFAULT + * statement is a branch back to a GOTO following + * the SWITCH. This GOTO points to after the jumptable. + */ + +switch : Y_SWITCH opnl LP opnl expr opnl RP opnl + { + if (!errcnt) { + push (compile(SWITCH)); + + /* Compile GOTO which will branch past end of + * switch. This is needed if there is no DEFAULT. + */ + compile (GOTO, 0); + } + } xstmt { + /* Set up jumptable and pop space on stack. + */ + if (!errcnt) + setswitch(); + } + ; + +case : Y_CASE { + if (!errcnt) { + ncaseval = 0; + if (!in_switch()) { + errmsg = "Improper CASE statement."; + EYYERROR; + } + } + } const_expr_list ':' opnl { + XINT pcase; + + if (!errcnt) { + pcase = compile (CASE, ncaseval); + + /* Fill in argument list. + */ + caseset (&(coderef(pcase)->c_args), ncaseval); + push (pcase); + } + } xstmt { + /* Branch to end of switch block + */ + if (!errcnt) + push (compile(GOTO, 0)); + } + ; + +default : Y_DEFAULT ':' opnl { + /* Compile an operand to store the current PC. + */ + if (!errcnt) { + if (!in_switch()) { + errmsg = "Improper DEFAULT statement."; + EYYERROR; + } + push (compile(DEFAULT)); + } + } xstmt { + /* Branch past jump table. + */ + if (!errcnt) + push (compile(GOTO, 0)); + } + ; + +next : Y_NEXT { + /* All NEXT statements are backward references, + * so we simply store the addresses in an array. + */ + if (!errcnt) { + if (nestlevel) + compile (GOTO, nextdest[nestlevel-1]-pc-SZ_CE); + else { + errmsg = "NEXT outside of loop."; + EYYERROR; + } + } + } + ; + +break : Y_BREAK { + /* Each BREAK is a forward reference. For the + * first BREAK in each loop we compile a + * GOTO statement which will be the object of + * all BREAK statements within the loop. When + * the loop is terminated the target of this + * GOTO will be set. + */ + int dest; + + if (!errcnt) { + if (!nestlevel) { + errmsg = "Break outside of loop."; + EYYERROR; + } else if ((dest = brkdest[nestlevel-1]) != 0) + compile (GOTO, dest-pc-SZ_CE); + else { + brkdest[nestlevel-1] = pc; + compile (GOTO, 0); + } + } + } + ; + +return : Y_RETURN { + if (!errcnt) + compile (END); + } + | Y_RETURN expr { + /* Return values currently not implemented. + */ + eprintf ("Warning: return value ignored.\n"); + if (!errcnt) + compile (END); + } + ; + + /* Require end to terminate with a new-line, because + * it should be at the end of the file. + */ +end_stmt: Y_END NL { + bracelevel -= PBRACE; + if (bracelevel < 0) { + errmsg = "Too few left braces."; + EYYERROR; + } else if (bracelevel > 0) { + errmsg = "Too few right braces."; + EYYERROR; + } + } + ; + +label_stmt: Y_IDENT ':' opnl { + /* Put symbol in table in dictionary and + * process indirect references if present. + */ + struct label *l; + + if (!errcnt) { + l = getlabel (stkop($1)); + + if (l == NULL) { + l = setlabel (stkop($1)); + l->l_loc = pc; + } else if (l->l_defined) { + errmsg = "Identical labels."; + EYYERROR; + } else { + /* Get this GOTO out of the + * indirect list so we can use + * the argument as the destination + */ + XINT gotopc; + gotopc = l->l_loc; + unsetigoto (gotopc); + + /* Fix the indirect reference. + */ + coderef(gotopc)->c_args = pc - gotopc - SZ_CE; + } + (l->l_defined)++; + } + } + xstmt + ; + +goto : Y_GOTO Y_IDENT { + /* Get the address corresponding to the label. + */ + struct label *l; + + if (!errcnt) { + l = getlabel (stkop($2)); + + if (l != NULL) + compile (GOTO, l->l_loc - pc - SZ_CE); + else { + /* Ready for indirect GOTO + */ + l = setlabel (stkop($2)); + l->l_loc = pc; + setigoto (compile(GOTO, 0)); + l->l_defined = 0; + } + } + } + ; + +nullstmt: ';' + | ';' NL + ; + +/* xstmt is defined so that to handle implicit do loops created by + * open array references e.g. a[*,3]=a[3,*]. + */ + +xstmt : /* empty */ { + /* Save pc before compiling statement for loop back + */ + stmt_pc = pc; + n_oarr = 0; + i_oarr = 0; + ifseen = NULL; + } + stmt { + /* If there was an open reference compile the + * loop increment and goback. + */ + XINT push_pc; + + if (!errcnt) { + if (n_oarr) { + compile (INDXINCR, stmt_pc-pc-4, 2*n_oarr+1); + + /* We are going to store initialization + * info for the implicit loop here. + * It is loopincr's responsibility to + * branch around it. This data is what + * should be pointed to by the special + * PUSHINDEX compiled at the first open + * array reference. + */ + push_pc = pop(); /* Location of PUSHINDEX */ + coderef(push_pc)->c_args = pc - push_pc - SZ_CE; + + stack[pc++] = n_oarr; + for (i_oarr=0; i_oarr<n_oarr; i_oarr++) { + stack[pc++] = oarr_beg[i_oarr]; + stack[pc++] = oarr_end[i_oarr]; + } + + /* Clear n_oarr. This must be done here + * because we may have the end of a compound + * statement following on the heels of the + * end of the simple statement with the + * implicit loop. + */ + n_oarr = 0; + i_oarr = 0; + } + } + } + | var_decl_stmt + | error NL { + /* This should get most errors in executable statements + * or in the local variable declarations in a script. + */ + yyerrok; + + /* Get rid of any fake braces. + */ + bracelevel -= tbrace; + + /* Discard everything and compile a null statement. + */ + if (!errcnt) { + do_params = YES; + pc = currentask->t_bascode; + if (parse_state != PARSE_PARAMS) + compile (END); + + topd = currentask->t_topd; + topcs = currentask->t_topcs; + + /* Unlink any added parms. Resetting of topd will + * already have reclaimed space. + */ + if (last_parm) { + last_parm->p_np = NULL; + currentask->t_pfp->pf_lastpp = last_parm; + last_parm = NULL; + } + } + + /* Tell user about the syntax error, printing the + * offending line and position if possible. + */ + if (currentask->t_flags & T_SCRIPT) { + if (errmsg != NULL) { + eprintf ("** Syntax error, line %d: %s\n", + currentask->t_scriptln, errmsg); + } else { + eprintf ("** Syntax error, line %d\n", + currentask->t_scriptln); + } + } else + eprintf ("** Syntax error\n"); + p_position(); + + if (!(currentask->t_flags & T_SCRIPT)) { + /* If interactive, we're finished if not within braces. + */ + if (!bracelevel) + YYACCEPT; + } + + /* Note that we do not call cl_error() here to abort, but + * continue on parsing the script for more syntax errors. + */ + if (++errcnt > MAX_ERR) + cl_error (E_UERR, "Too many syntax errors."); + } + ; + +const_expr_list : const_expr + | const_expr DELIM const_expr_list + ; + +const_expr : const { + if (!errcnt) { + push(stkop($1)) ; + ncaseval++; + } + } + ; + + /* Use opnl when blank lines are permitted, + * or where a statement may be broken into more + * than one line. The lexical analyzer (actually + * get_command in history.c) ensures that all blank + * lines are deleted. So we don't have to use + * a recursive definition here. + */ + +opnl : /* empty */ + | NL + ; + +ref : param { + int dim, d, i1, i2, mode; + + /* In command arguments, when not in parentheses + * we just pass the param as a string constant. + */ + if (!errcnt) { + lastref = NO; + if (!inarglist || parenlevel) { + i_oarr = 0; + index_cnt = 0; + + strncpy (curr_param, stkop($1)->o_val.v_s, + SZ_FNAME); + + /* If a '.' is found in the name we have a + * reference to an external task, or to a + * specific field. In these cases we don't + * want implicit looping. + */ + if (index (curr_param, '.') == NULL) { + if ((dim = get_dim (curr_param)) > 0) { + lastref = YES; + for (d = 0; d < dim; d++) { + getlimits (curr_param, d, &i1, &i2); + mode = make_imloop (i1, i2); + if (mode) + compile (PUSHINDEX, -1); + else + push (compile(PUSHINDEX, 0)); + } + n_oarr = dim; + } + } + } + } + } + | param { + if (!errcnt) { + strncpy (curr_param, stkop($1)->o_val.v_s, SZ_FNAME); + index_cnt = 0; + } + } + '[' index_list ']' + { + if (i_oarr > 0 && n_oarr == 0) + n_oarr = i_oarr; + i_oarr = 0; + lastref = YES; + } + ; + +index_list: index { + index_cnt = 1; + } + | index { + index_cnt++; + } + DELIM index_list + ; + +index : expr1 { + if (!errcnt) + compile (PUSHINDEX, 0); + } + | ref /* This isn't included in expr1 */ + { + if (!errcnt) { + compile (PUSHPARAM, stkop($1)->o_val.v_s); + compile (PUSHINDEX, 0); + } + } + | '*' { + int i1, i2, mode; + + if (!errcnt) { + if (index(curr_param, '.') != NULL) { + errmsg = exlimits; + EYYERROR; + } + if (getlimits (curr_param, index_cnt, &i1, &i2) + == ERR) { + eprintf ("Implicit index error for %s.\n", + curr_param); + EYYERROR; + } + mode = make_imloop (i1, i2); + if (mode) + compile (PUSHINDEX, mode); + else + push (compile (PUSHINDEX, mode)); + } + } + | Y_CONSTANT { + /* There is an ambiguity in the grammar between + * sexagesimal constants, and array range references. + * Since the sexagesimal constants are recognized + * in the lexical analyzer we can't just change the + * grammar. The kludge around this is to have + * makeop set a flag telling us that the last + * constant it compiled COULD have been an index + * range. We check the flag here and if it is + * set we convert back and compile an implicit loop + * otherwise we just push the constant. + */ + int i1, i2, mode; + + if (!errcnt) { + if (maybeindex) { + sexa_to_index (stkop($1)->o_val.v_r, &i1, &i2); + mode = make_imloop (i1, i2); + if (mode) + compile (PUSHINDEX, mode); + else + push (compile (PUSHINDEX, mode)); + } else { + compile (PUSHCONST, stkop($1)); + compile (PUSHINDEX, 0); + } + } + } + ; + +/* these are just to make the grammar a bit easier to read. + * can yank them out to shrink parser a bit... + */ + +intrins : Y_IDENT { + $$ = $1; + } + ; + +param : Y_IDENT { + $$ = $1; + } + ; + +tasknam : Y_IDENT { + $$ = $1; + } + ; + +EOST : NL + | ';' { + /* If statements are delimited by ';'s, do not execute + * until next newline EOST is received. + */ + sawnl = 0; + } + ; + +DELIM : ',' + ; + +BARG : /* empty */ + | LP + ; + +EARG : /* empty */ + | RP + ; + +/* These eliminate several interior actions. + */ + +LP : '(' { parenlevel++; } + ; + +RP : ')' { --parenlevel; } + ; + +NL : Y_NEWLINE { sawnl = 1; } + ; + +%% + +#include "lexyy.c" +#include "lexicon.c" |