home *** CD-ROM | disk | FTP | other *** search
/ Club Amiga de Montreal - CAM / CAM_CD_1.iso / files / 419b.lha / GNU_Awk_v2.10_Beta / awk.y < prev    next >
Text File  |  1990-10-01  |  44KB  |  1,878 lines

  1. /*
  2.  * gawk -- GNU version of awk
  3.  * awk.y --- yacc/bison parser for awk
  4.  *
  5.  * $Log:    awk.y,v $
  6.  * Revision 1.35  89/03/31  13:24:41  david
  7.  * GNU license; MSDOS support; YYDEBUG inside #ifdef DEBUG
  8.  * 
  9.  * Revision 1.34  89/03/30  20:55:55  david
  10.  * avoid constructing lists in the case of one instance of a rule, statement
  11.  * or BEGIN or END clause
  12.  * 
  13.  * Revision 1.33  89/03/29  21:53:26  david
  14.  * wierd: this stuff worked just fine with cc, but I had to add a lot
  15.  * of $$ = $1 lines for it to work with gcc -- I thought that $$ = $1
  16.  * was the default action
  17.  * 
  18.  * Revision 1.32  89/03/29  14:16:08  david
  19.  * grammar fix
  20.  * delinting
  21.  * some code movement -- devopen to awk7.c, variable() to here
  22.  * change interface to devopen()
  23.  * 
  24.  * Revision 1.31  89/03/24  21:08:13  david
  25.  * STREQN takes care of extra test
  26.  * 
  27.  * Revision 1.30  89/03/24  15:52:15  david
  28.  * add getline production to rexp
  29.  * merge HASHNODE with NODE
  30.  * 
  31.  * Revision 1.29  89/03/21  11:57:49  david
  32.  * substantial cleanup and code movement from awk1.c
  33.  * this and previous two changes represent a major reworking of the grammar
  34.  * to fix a number of bugs;  two general problems were in I/O redirection
  35.  * specifications and in the handling of whitespace -- the general strategies
  36.  * in fixing these problems were to define some more specific grammatical 
  37.  * elements (e.g. simp_exp and rexp) and use these in particular places; 
  38.  * also got rid of want_concat and want_redirect kludges
  39.  * 
  40.  * Revision 1.28  89/03/15  21:58:01  david
  41.  * more grammar changes (explanation to come) plus changes from Arnold:
  42.  * new case stuff added and old removed
  43.  * tolower and toupper added
  44.  * fix vararg stuff
  45.  * add new escape sequences
  46.  * fix bug in reporting unterminated regexps
  47.  * fix to allow -f -
  48.  * /dev/fd/N etc special files added
  49.  * 
  50.  * Revision 1.27  89/03/02  21:10:09  david
  51.  * intermediate step in major revision -- description later
  52.  * 
  53.  * Revision 1.26  89/01/18  20:39:58  david
  54.  * allow regexp && regexp as pattern and get rid of remaining reduce/reduce conflicts
  55.  * 
  56.  * Revision 1.25  89/01/04  21:53:21  david
  57.  * purge obstack remnants
  58.  * 
  59.  * Revision 1.24  88/12/15  12:52:58  david
  60.  * changes from Jay to get rid of some reduce/reduce conflicts - some remain
  61.  * 
  62.  * Revision 1.23  88/12/07  19:59:25  david
  63.  * changes for incorporating source filename in error messages
  64.  * 
  65.  * Revision 1.22  88/11/23  21:37:24  david
  66.  * Arnold: refinements of AWKPATH code
  67.  * 
  68.  * Revision 1.21  88/11/22  13:46:45  david
  69.  * Arnold: changes for case-insensitive matching
  70.  * 
  71.  * Revision 1.20  88/11/15  10:13:37  david
  72.  * Arnold: allow multiple -f options and search in directories for awk libraries,
  73.  * directories specified by AWKPATH env. variable; cleanupo of comments and
  74.  * #includes
  75.  * 
  76.  * Revision 1.19  88/11/14  21:51:30  david
  77.  * Arnold: added error message for BEGIN or END without any action at all;
  78.  * unlink temporary source file right after creation so it goes away on bomb
  79.  * 
  80.  * Revision 1.18  88/10/19  22:00:56  david
  81.  * generalize (and correct) what pattern can be in pattern {action}; this
  82.  * introduces quite a few new conflicts that should be checked thoroughly
  83.  * at some point, but they don't seem to do any harm at first glance
  84.  * replace malloc with emalloc
  85.  * 
  86.  * Revision 1.17  88/10/17  19:52:01  david
  87.  * Arnold: cleanup, purge FAST
  88.  * 
  89.  * Revision 1.16  88/10/13  22:02:16  david
  90.  * cleanup of yyerror and other error messages
  91.  * 
  92.  * Revision 1.15  88/10/06  23:24:57  david
  93.  * accept     var space ++var
  94.  * accept underscore as first character of a variable name
  95.  * 
  96.  * Revision 1.14  88/06/13  18:01:46  david
  97.  * delete \a (change from Arnold)
  98.  * 
  99.  * Revision 1.13  88/06/08  00:29:42  david
  100.  * better attempt at keeping track of line numbers
  101.  * change grammar to properly handle newlines after && or ||
  102.  * 
  103.  * Revision 1.12  88/06/07  23:39:02  david
  104.  * little delint
  105.  * 
  106.  * Revision 1.11  88/06/05  22:17:40  david
  107.  * make_name() becomes make_param() (again!)
  108.  * func_level goes away, param_counter makes entrance
  109.  * 
  110.  * Revision 1.10  88/05/30  09:49:02  david
  111.  * obstack_free was being called at end of function definition, freeing
  112.  * memory that might be part of global variables referenced only inside
  113.  * functions; commented out for now, will have to selectively free later.
  114.  * cleanup: regexp now returns a NODE *
  115.  * 
  116.  * Revision 1.9  88/05/27  11:04:53  david
  117.  * added print[f] '(' ... ')'     (optional parentheses)
  118.  * for some reason want_redirect wasn't getting set for PRINT, so I set it in 
  119.  * yylex()
  120.  * 
  121.  * Revision 1.8  88/05/26  22:52:14  david
  122.  * fixed cmd | getline
  123.  * added compound patterns (they got lost somewhere along the line)
  124.  * fixed error message in yylex()
  125.  * added null statement 
  126.  * 
  127.  * Revision 1.7  88/05/13  22:05:29  david
  128.  * moved BEGIN and END block merging here
  129.  * BEGIN, END and function defs. are no longer incorporated into main parse tree
  130.  * fixed    command | getline
  131.  * fixed function install and definition
  132.  * 
  133.  * Revision 1.6  88/05/09  17:47:50  david
  134.  * Arnold's coded binary search
  135.  * 
  136.  * Revision 1.5  88/05/04  12:31:13  david
  137.  * be a bit more careful about types
  138.  * make_for_loop() now returns a NODE *
  139.  * keyword search now uses bsearch() -- need a public domain version of this
  140.  * added back stuff in yylex() that got lost somewhere along the line
  141.  * malloc() tokens in yylex() since they were previously just pointers into
  142.  *  current line that got overwritten by the next fgets() -- these need to get
  143.  *  freed at some point
  144.  * fixed backslash line continuation interaction with CONCAT
  145.  * 
  146.  * Revision 1.4  88/04/14  17:03:51  david
  147.  * reinstalled a fix to do with line continuation
  148.  * 
  149.  * Revision 1.3  88/04/14  14:41:01  david
  150.  * Arnold's changes to yylex to read program from a file
  151.  * 
  152.  * Revision 1.5  88/03/18  21:00:07  david
  153.  * Baseline -- hoefully all the functionality of the new awk added.
  154.  * Just debugging and tuning to do.
  155.  * 
  156.  * Revision 1.4  87/11/19  14:37:20  david
  157.  * added a bunch of ew builtin functions
  158.  * added new rules for getline to provide new functionality
  159.  * minor cleanup of redirection handling
  160.  * generalized make_param into make_name
  161.  * 
  162.  * Revision 1.3  87/11/09  21:22:33  david
  163.  * added macinery for user-defined functions (including return)
  164.  * added delete, do-while and system
  165.  * reformatted and revised grammer to improve error-handling
  166.  * changes to yyerror to give improved error messages
  167.  * 
  168.  * Revision 1.2  87/10/29  21:33:28  david
  169.  * added test for membership in an array, as in:  if ("yes" in answers) ...
  170.  * 
  171.  * Revision 1.1  87/10/27  15:23:21  david
  172.  * Initial revision
  173.  * 
  174.  */
  175.  
  176. /* 
  177.  * Copyright (C) 1986, 1988, 1989 the Free Software Foundation, Inc.
  178.  * 
  179.  * This file is part of GAWK, the GNU implementation of the
  180.  * AWK Progamming Language.
  181.  * 
  182.  * GAWK is free software; you can redistribute it and/or modify
  183.  * it under the terms of the GNU General Public License as published by
  184.  * the Free Software Foundation; either version 1, or (at your option)
  185.  * any later version.
  186.  * 
  187.  * GAWK is distributed in the hope that it will be useful,
  188.  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  189.  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  190.  * GNU General Public License for more details.
  191.  * 
  192.  * You should have received a copy of the GNU General Public License
  193.  * along with GAWK; see the file COPYING.  If not, write to
  194.  * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
  195.  */
  196.  
  197. %{
  198. #ifdef DEBUG
  199. #define YYDEBUG 12
  200. #endif
  201. #define YYIMPROVE
  202.  
  203. #include "awk.h"
  204.  
  205. extern void msg();
  206. extern struct re_pattern_buffer *mk_re_parse();
  207.  
  208. NODE *node();
  209. NODE *lookup();
  210. NODE *install();
  211.  
  212. static NODE *snode();
  213. static NODE *mkrangenode();
  214. static FILE *pathopen();
  215. static NODE *make_for_loop();
  216. static NODE *append_right();
  217. static void func_install();
  218. static NODE *make_param();
  219. static int hashf();
  220. static void pop_params();
  221. static void pop_var();
  222. static int yylex ();
  223. static void yyerror();
  224.  
  225. static int want_regexp;        /* lexical scanning kludge */
  226. static int lineno = 1;        /* for error msgs */
  227. static char *lexptr;        /* pointer to next char during parsing */
  228. static char *lexptr_begin;    /* keep track of where we were for error msgs */
  229. static int curinfile = -1;    /* index into sourcefiles[] */
  230.  
  231. NODE *variables[HASHSIZE];
  232.  
  233. extern int errcount;
  234. extern NODE *begin_block;
  235. extern NODE *end_block;
  236. extern int param_counter;
  237. %}
  238.  
  239. %union {
  240.     long lval;
  241.     AWKNUM fval;
  242.     NODE *nodeval;
  243.     NODETYPE nodetypeval;
  244.     char *sval;
  245.     NODE *(*ptrval)();
  246. }
  247.  
  248. %type <nodeval> function_prologue function_body
  249. %type <nodeval> rexp exp start program rule simp_exp
  250. %type <nodeval> simp_pattern pattern 
  251. %type <nodeval>    action variable param_list
  252. %type <nodeval>    rexpression_list opt_rexpression_list
  253. %type <nodeval>    expression_list opt_expression_list
  254. %type <nodeval>    statements statement if_statement opt_param_list 
  255. %type <nodeval> opt_exp opt_variable regexp p_regexp
  256. %type <nodeval> input_redir output_redir
  257. %type <nodetypeval> r_paren comma nls opt_nls print
  258.  
  259. %type <sval> func_name
  260. %token <sval> FUNC_CALL NAME REGEXP YSTRING
  261. %token <lval> ERROR INCDEC
  262. %token <fval> NUMBER
  263. %token <nodetypeval> RELOP APPEND_OP
  264. %token <nodetypeval> ASSIGNOP MATCHOP NEWLINE CONCAT_OP
  265. %token <nodetypeval> LEX_BEGIN LEX_END LEX_IF LEX_ELSE LEX_RETURN LEX_DELETE
  266. %token <nodetypeval> LEX_WHILE LEX_DO LEX_FOR LEX_BREAK LEX_CONTINUE
  267. %token <nodetypeval> LEX_PRINT LEX_PRINTF LEX_NEXT LEX_EXIT LEX_FUNCTION
  268. %token <nodetypeval> LEX_GETLINE LEX_SUB LEX_MATCH
  269. %token <nodetypeval> LEX_IN
  270. %token <lval> LEX_AND LEX_OR INCREMENT DECREMENT
  271. %token <ptrval> LEX_BUILTIN
  272.  
  273. /* these are just yylval numbers */
  274.  
  275. /* Lowest to highest */
  276. %right ASSIGNOP
  277. %right '?' ':'
  278. %left LEX_OR
  279. %left LEX_AND
  280. %left LEX_GETLINE
  281. %left NUMBER
  282. %left FUNC_CALL LEX_SUB LEX_BUILTIN LEX_MATCH
  283. %nonassoc MATCHOP
  284. %nonassoc RELOP '<' '>' '|' APPEND_OP
  285. %left NAME
  286. %nonassoc LEX_IN
  287. %left YSTRING
  288. %left '(' ')'
  289. %left CONCAT_OP
  290. %left '+' '-'
  291. %left '*' '/' '%'
  292. %right '!' UNARY
  293. %right '^'
  294. %left INCREMENT DECREMENT
  295. %left '$'
  296.  
  297. %%
  298.  
  299. start
  300.     : opt_nls program
  301.         { expression_value = $2; }
  302.     ;
  303.  
  304. program
  305.     : rule
  306.         { 
  307.             if ($1 != NULL)
  308.                 $$ = $1;
  309.             else
  310.                 $$ = NULL;
  311.             yyerrok;
  312.         }
  313.     | program rule
  314.         /* add the rule to the tail of list */
  315.         {
  316.             if ($2 == NULL)
  317.                 $$ = $1;
  318.             else if ($1 == NULL)
  319.                 $$ = $2;
  320.             else {
  321.                 if ($1->type != Node_rule_list)
  322.                     $1 = node($1, Node_rule_list,
  323.                         (NODE*)NULL);
  324.                 $$ = append_right ($1,
  325.                    node($2, Node_rule_list,(NODE *) NULL));
  326.             }
  327.             yyerrok;
  328.         }
  329.     | error    { $$ = NULL; }
  330.     | program error { $$ = NULL; }
  331.     ;
  332.  
  333. rule
  334.     : LEX_BEGIN action
  335.       {
  336.         if (begin_block) {
  337.             if (begin_block->type != Node_rule_list)
  338.                 begin_block = node(begin_block, Node_rule_list,
  339.                     (NODE *)NULL);
  340.             append_right (begin_block, node(
  341.                 node((NODE *)NULL, Node_rule_node, $2),
  342.                 Node_rule_list, (NODE *)NULL) );
  343.         } else
  344.             begin_block = node((NODE *)NULL, Node_rule_node, $2);
  345.         $$ = NULL;
  346.         yyerrok;
  347.       }
  348.     | LEX_END action
  349.       {
  350.         if (end_block) {
  351.             if (end_block->type != Node_rule_list)
  352.                 end_block = node(end_block, Node_rule_list,
  353.                     (NODE *)NULL);
  354.             append_right (end_block, node(
  355.                 node((NODE *)NULL, Node_rule_node, $2),
  356.                 Node_rule_list, (NODE *)NULL));
  357.         } else
  358.             end_block = node((NODE *)NULL, Node_rule_node, $2);
  359.         $$ = NULL;
  360.         yyerrok;
  361.       }
  362.     | LEX_BEGIN statement_term
  363.       {
  364.         msg ("error near line %d: BEGIN blocks must have an action part", lineno);
  365.         errcount++;
  366.         yyerrok;
  367.       }
  368.     | LEX_END statement_term
  369.       {
  370.         msg ("error near line %d: END blocks must have an action part", lineno);
  371.         errcount++;
  372.         yyerrok;
  373.       }
  374.     | pattern action
  375.         { $$ = node ($1, Node_rule_node, $2); yyerrok; }
  376.     | action
  377.         { $$ = node ((NODE *)NULL, Node_rule_node, $1); yyerrok; }
  378.     | pattern statement_term
  379.         { if($1) $$ = node ($1, Node_rule_node, (NODE *)NULL); yyerrok; }
  380.     | function_prologue function_body
  381.         {
  382.             func_install($1, $2);
  383.             $$ = NULL;
  384.             yyerrok;
  385.         }
  386.     ;
  387.  
  388. func_name
  389.     : NAME
  390.         { $$ = $1; }
  391.     | FUNC_CALL
  392.         { $$ = $1; }
  393.     ;
  394.         
  395. function_prologue
  396.     : LEX_FUNCTION 
  397.         {
  398.             param_counter = 0;
  399.         }
  400.       func_name '(' opt_param_list r_paren opt_nls
  401.         {
  402.             $$ = append_right(make_param($3), $5);
  403.         }
  404.     ;
  405.  
  406. function_body
  407.     : l_brace statements r_brace
  408.         { $$ = $2; }
  409.     ;
  410.  
  411.  
  412. simp_pattern
  413.     : exp
  414.         { $$ = $1; }
  415.     | p_regexp
  416.         { $$ = $1; }
  417.     | p_regexp LEX_AND simp_pattern
  418.         { $$ = node ($1, Node_and, $3); }
  419.     | p_regexp LEX_OR simp_pattern
  420.         { $$ = node ($1, Node_or, $3); }
  421.     | '!' p_regexp %prec UNARY
  422.         { $$ = node ($2, Node_not,(NODE *) NULL); }
  423.     | '(' p_regexp r_paren
  424.         { $$ = $2; }
  425.     ;
  426.  
  427. pattern
  428.     : simp_pattern
  429.         { $$ = $1; }
  430.     | simp_pattern comma simp_pattern
  431.         { $$ = mkrangenode ( node($1, Node_cond_pair, $3) ); }
  432.     ;
  433.  
  434. p_regexp
  435.     : regexp
  436.         { 
  437.           $$ = node(
  438.                node(make_number((AWKNUM)0),Node_field_spec,(NODE*)NULL),
  439.                Node_match, $1);
  440.         }
  441.     ;
  442.  
  443. regexp
  444.     /*
  445.      * In this rule, want_regexp tells yylex that the next thing
  446.      * is a regexp so it should read up to the closing slash.
  447.      */
  448.     : '/'
  449.         { ++want_regexp; }
  450.        REGEXP '/'
  451.         {
  452.           want_regexp = 0;
  453.           $$ = node((NODE *)NULL,Node_regex,(NODE *)mk_re_parse($3, 0));
  454.           $$ -> re_case = 0;
  455.           emalloc ($$ -> re_text, char *, strlen($3)+1, "regexp");
  456.           strcpy ($$ -> re_text, $3);
  457.         }
  458.     ;
  459.  
  460. action
  461.     : l_brace r_brace opt_semi
  462.         {
  463.             /* empty actions are different from missing actions */
  464.             $$ = node ((NODE *) NULL, Node_illegal, (NODE *) NULL);
  465.         }
  466.     | l_brace statements r_brace opt_semi
  467.         { $$ = $2 ; }
  468.     ;
  469.  
  470. statements
  471.     : statement
  472.         { $$ = $1; }
  473.     | statements statement
  474.         {
  475.             if ($1->type != Node_statement_list)
  476.                 $1 = node($1, Node_statement_list,(NODE *)NULL);
  477.                 $$ = append_right($1,
  478.                 node( $2, Node_statement_list, (NODE *)NULL));
  479.                 yyerrok;
  480.         }
  481.     | error
  482.         { $$ = NULL; }
  483.     | statements error
  484.         { $$ = NULL; }
  485.     ;
  486.  
  487. statement_term
  488.     : nls
  489.         { $<nodetypeval>$ = Node_illegal; }
  490.     | semi opt_nls
  491.         { $<nodetypeval>$ = Node_illegal; }
  492.     ;
  493.  
  494.     
  495. statement
  496.     : semi opt_nls
  497.         { $$ = NULL; }
  498.     | l_brace statements r_brace
  499.         { $$ = $2; }
  500.     | if_statement
  501.         { $$ = $1; }
  502.     | LEX_WHILE '(' exp r_paren opt_nls statement
  503.         { $$ = node ($3, Node_K_while, $6); }
  504.     | LEX_DO opt_nls statement LEX_WHILE '(' exp r_paren opt_nls
  505.         { $$ = node ($6, Node_K_do, $3); }
  506.     | LEX_FOR '(' NAME LEX_IN NAME r_paren opt_nls statement
  507.       {
  508.         $$ = node ($8, Node_K_arrayfor, make_for_loop(variable($3),
  509.             (NODE *)NULL, variable($5)));
  510.       }
  511.     | LEX_FOR '(' opt_exp semi exp semi opt_exp r_paren opt_nls statement
  512.       {
  513.         $$ = node($10, Node_K_for, (NODE *)make_for_loop($3, $5, $7));
  514.       }
  515.     | LEX_FOR '(' opt_exp semi semi opt_exp r_paren opt_nls statement
  516.       {
  517.         $$ = node ($9, Node_K_for,
  518.             (NODE *)make_for_loop($3, (NODE *)NULL, $6));
  519.       }
  520.     | LEX_BREAK statement_term
  521.        /* for break, maybe we'll have to remember where to break to */
  522.         { $$ = node ((NODE *)NULL, Node_K_break, (NODE *)NULL); }
  523.     | LEX_CONTINUE statement_term
  524.        /* similarly */
  525.         { $$ = node ((NODE *)NULL, Node_K_continue, (NODE *)NULL); }
  526.     | print '(' expression_list r_paren output_redir statement_term
  527.         { $$ = node ($3, $1, $5); }
  528.     | print opt_rexpression_list output_redir statement_term
  529.         { $$ = node ($2, $1, $3); }
  530.     | LEX_NEXT statement_term
  531.         { $$ = node ((NODE *)NULL, Node_K_next, (NODE *)NULL); }
  532.     | LEX_EXIT opt_exp statement_term
  533.         { $$ = node ($2, Node_K_exit, (NODE *)NULL); }
  534.     | LEX_RETURN opt_exp statement_term
  535.         { $$ = node ($2, Node_K_return, (NODE *)NULL); }
  536.     | LEX_DELETE NAME '[' expression_list ']' statement_term
  537.         { $$ = node (variable($2), Node_K_delete, $4); }
  538.     | exp statement_term
  539.         { $$ = $1; }
  540.     ;
  541.  
  542. print
  543.     : LEX_PRINT
  544.         { $$ = $1; }
  545.     | LEX_PRINTF
  546.         { $$ = $1; }
  547.     ;
  548.  
  549. if_statement
  550.     : LEX_IF '(' exp r_paren opt_nls statement
  551.       {
  552.         $$ = node($3, Node_K_if, 
  553.             node($6, Node_if_branches, (NODE *)NULL));
  554.       }
  555.     | LEX_IF '(' exp r_paren opt_nls statement
  556.          LEX_ELSE opt_nls statement
  557.         { $$ = node ($3, Node_K_if,
  558.                 node ($6, Node_if_branches, $9)); }
  559.     ;
  560.  
  561. nls
  562.     : NEWLINE
  563.         { $<nodetypeval>$ = NULL; }
  564.     | nls NEWLINE
  565.         { $<nodetypeval>$ = NULL; }
  566.     ;
  567.  
  568. opt_nls
  569.     : /* empty */
  570.         { $<nodetypeval>$ = NULL; }
  571.     | nls
  572.         { $<nodetypeval>$ = NULL; }
  573.     ;
  574.  
  575. input_redir
  576.     : /* empty */
  577.         { $$ = NULL; }
  578.     | '<' simp_exp
  579.         { $$ = node ($2, Node_redirect_input, (NODE *)NULL); }
  580.     ;
  581.  
  582. output_redir
  583.     : /* empty */
  584.         { $$ = NULL; }
  585.     | '>' simp_exp
  586.         { $$ = node ($2, Node_redirect_output, (NODE *)NULL); }
  587.     | APPEND_OP simp_exp
  588.         { $$ = node ($2, Node_redirect_append, (NODE *)NULL); }
  589.     | '|' simp_exp
  590.         { $$ = node ($2, Node_redirect_pipe, (NODE *)NULL); }
  591.     ;
  592.  
  593. opt_param_list
  594.     : /* empty */
  595.         { $$ = NULL; }
  596.     | param_list
  597.         { $$ = $1; }
  598.     ;
  599.  
  600. param_list
  601.     : NAME
  602.         { $$ = make_param($1); }
  603.     | param_list comma NAME
  604.         { $$ = append_right($1, make_param($3)); yyerrok; }
  605.     | error
  606.         { $$ = NULL; }
  607.     | param_list error
  608.         { $$ = NULL; }
  609.     | param_list comma error
  610.         { $$ = NULL; }
  611.     ;
  612.  
  613. /* optional expression, as in for loop */
  614. opt_exp
  615.     : /* empty */
  616.         { $$ = NULL; }
  617.     | exp
  618.         { $$ = $1; }
  619.     ;
  620.  
  621. opt_rexpression_list
  622.     : /* empty */
  623.         { $$ = NULL; }
  624.     | rexpression_list
  625.         { $$ = $1; }
  626.     ;
  627.  
  628. rexpression_list
  629.     : rexp
  630.         { $$ = node ($1, Node_expression_list, (NODE *)NULL); }
  631.     | rexpression_list comma rexp
  632.       {
  633.         $$ = append_right($1,
  634.             node( $3, Node_expression_list, (NODE *)NULL));
  635.         yyerrok;
  636.       }
  637.     | error
  638.         { $$ = NULL; }
  639.     | rexpression_list error
  640.         { $$ = NULL; }
  641.     | rexpression_list error rexp
  642.         { $$ = NULL; }
  643.     | rexpression_list comma error
  644.         { $$ = NULL; }
  645.     ;
  646.  
  647. opt_expression_list
  648.     : /* empty */
  649.         { $$ = NULL; }
  650.     | expression_list
  651.         { $$ = $1; }
  652.     ;
  653.  
  654. expression_list
  655.     : exp
  656.         { $$ = node ($1, Node_expression_list, (NODE *)NULL); }
  657.     | expression_list comma exp
  658.         {
  659.             $$ = append_right($1,
  660.                 node( $3, Node_expression_list, (NODE *)NULL));
  661.             yyerrok;
  662.         }
  663.     | error
  664.         { $$ = NULL; }
  665.     | expression_list error
  666.         { $$ = NULL; }
  667.     | expression_list error exp
  668.         { $$ = NULL; }
  669.     | expression_list comma error
  670.         { $$ = NULL; }
  671.     ;
  672.  
  673. /* Expressions, not including the comma operator.  */
  674. exp    : variable ASSIGNOP exp
  675.         { $$ = node ($1, $2, $3); }
  676.     | '(' expression_list r_paren LEX_IN NAME
  677.         { $$ = node (variable($5), Node_in_array, $2); }
  678.     | exp '|' LEX_GETLINE opt_variable
  679.         {
  680.           $$ = node ($4, Node_K_getline,
  681.              node ($1, Node_redirect_pipein, (NODE *)NULL));
  682.         }
  683.     | LEX_GETLINE opt_variable input_redir
  684.         {
  685.           $$ = node ($2, Node_K_getline, $3);
  686.         }
  687.     | exp LEX_AND exp
  688.         { $$ = node ($1, Node_and, $3); }
  689.     | exp LEX_OR exp
  690.         { $$ = node ($1, Node_or, $3); }
  691.     | exp MATCHOP regexp
  692.          { $$ = node ($1, $2, $3); }
  693.     | exp MATCHOP exp
  694.          { $$ = node ($1, $2, $3); }
  695.     | exp LEX_IN NAME
  696.         { $$ = node (variable($3), Node_in_array, $1); }
  697.     | exp RELOP exp
  698.         { $$ = node ($1, $2, $3); }
  699.     | exp '<' exp
  700.         { $$ = node ($1, Node_less, $3); }
  701.     | exp '>' exp
  702.         { $$ = node ($1, Node_greater, $3); }
  703.     | exp '?' exp ':' exp
  704.         { $$ = node($1, Node_cond_exp, node($3, Node_if_branches, $5));}
  705.     | exp exp %prec CONCAT_OP
  706.         { $$ = node ($1, Node_concat, $2); }
  707.     | simp_exp
  708.         { $$ = $1; }
  709.     ;
  710.  
  711. rexp    
  712.     : variable ASSIGNOP rexp
  713.         { $$ = node ($1, $2, $3); }
  714.     | rexp LEX_AND rexp
  715.         { $$ = node ($1, Node_and, $3); }
  716.     | rexp LEX_OR rexp
  717.         { $$ = node ($1, Node_or, $3); }
  718.     | LEX_GETLINE opt_variable input_redir
  719.         {
  720.           $$ = node ($2, Node_K_getline, $3);
  721.         }
  722.     | rexp MATCHOP regexp
  723.          { $$ = node ($1, $2, $3); }
  724.     | rexp MATCHOP rexp
  725.          { $$ = node ($1, $2, $3); }
  726.     | rexp LEX_IN NAME
  727.         { $$ = node (variable($3), Node_in_array, $1); }
  728.     | rexp RELOP rexp
  729.         { $$ = node ($1, $2, $3); }
  730.     | rexp '?' rexp ':' rexp
  731.         { $$ = node($1, Node_cond_exp, node($3, Node_if_branches, $5));}
  732.     | rexp rexp %prec CONCAT_OP
  733.         { $$ = node ($1, Node_concat, $2); }
  734.     | simp_exp
  735.         { $$ = $1; }
  736.     ;
  737.  
  738. simp_exp
  739.     : '!' simp_exp %prec UNARY
  740.         { $$ = node ($2, Node_not,(NODE *) NULL); }
  741.     | '(' exp r_paren
  742.         { $$ = $2; }
  743.     | LEX_BUILTIN '(' opt_expression_list r_paren
  744.         { $$ = snode ($3, Node_builtin, $1); }
  745.     | LEX_BUILTIN
  746.         { $$ = snode ((NODE *)NULL, Node_builtin, $1); }
  747.     | LEX_SUB '(' regexp comma expression_list r_paren 
  748.         { $$ = node($5, $1, $3); }
  749.     | LEX_SUB '(' exp comma expression_list r_paren 
  750.         { $$ = node($5, $1, $3); }
  751.     | LEX_MATCH '(' exp comma regexp r_paren
  752.         { $$ = node($3, $1, $5); }
  753.     | LEX_MATCH '(' exp comma exp r_paren
  754.         { $$ = node($3, $1, $5); }
  755.     | FUNC_CALL '(' opt_expression_list r_paren
  756.       {
  757.         $$ = node ($3, Node_func_call, make_string($1, strlen($1)));
  758.       }
  759.     | INCREMENT variable
  760.         { $$ = node ($2, Node_preincrement, (NODE *)NULL); }
  761.     | DECREMENT variable
  762.         { $$ = node ($2, Node_predecrement, (NODE *)NULL); }
  763.     | variable INCREMENT
  764.         { $$ = node ($1, Node_postincrement, (NODE *)NULL); }
  765.     | variable DECREMENT
  766.         { $$ = node ($1, Node_postdecrement, (NODE *)NULL); }
  767.     | variable
  768.         { $$ = $1; }
  769.     | NUMBER
  770.         { $$ = make_number ($1); }
  771.     | YSTRING
  772.         { $$ = make_string ($1, -1); }
  773.  
  774.     /* Binary operators in order of decreasing precedence.  */
  775.     | simp_exp '^' simp_exp
  776.         { $$ = node ($1, Node_exp, $3); }
  777.     | simp_exp '*' simp_exp
  778.         { $$ = node ($1, Node_times, $3); }
  779.     | simp_exp '/' simp_exp
  780.         { $$ = node ($1, Node_quotient, $3); }
  781.     | simp_exp '%' simp_exp
  782.         { $$ = node ($1, Node_mod, $3); }
  783.     | simp_exp '+' simp_exp
  784.         { $$ = node ($1, Node_plus, $3); }
  785.     | simp_exp '-' simp_exp
  786.         { $$ = node ($1, Node_minus, $3); }
  787.     | '-' simp_exp    %prec UNARY
  788.         { $$ = node ($2, Node_unary_minus, (NODE *)NULL); }
  789.     | '+' simp_exp    %prec UNARY
  790.         { $$ = $2; }
  791.     ;
  792.  
  793. opt_variable
  794.     : /* empty */
  795.         { $$ = NULL; }
  796.     | variable
  797.         { $$ = $1; }
  798.     ;
  799.  
  800. variable
  801.     : NAME
  802.         { $$ = variable ($1); }
  803.     | NAME '[' expression_list ']'
  804.         { $$ = node (variable($1), Node_subscript, $3); }
  805.     | '$' simp_exp
  806.         { $$ = node ($2, Node_field_spec, (NODE *)NULL); }
  807.     ;
  808.  
  809. l_brace
  810.     : '{' opt_nls
  811.     ;
  812.  
  813. r_brace
  814.     : '}' opt_nls    { yyerrok; }
  815.     ;
  816.  
  817. r_paren
  818.     : ')' { $<nodetypeval>$ = Node_illegal; yyerrok; }
  819.     ;
  820.  
  821. opt_semi
  822.     : /* empty */
  823.     | semi
  824.     ;
  825.  
  826. semi
  827.     : ';'    { yyerrok; }
  828.     ;
  829.  
  830. comma    : ',' opt_nls    { $<nodetypeval>$ = Node_illegal; yyerrok; }
  831.     ;
  832.  
  833. %%
  834.  
  835. struct token {
  836.     char *operator;        /* text to match */
  837.     NODETYPE value;        /* node type */
  838.     int class;        /* lexical class */
  839.     short nostrict;        /* ignore if in strict compatibility mode */
  840.     NODE *(*ptr) ();    /* function that implements this keyword */
  841. };
  842.  
  843. #ifndef NULL
  844. #define NULL 0
  845. #endif
  846.  
  847. extern NODE
  848.     *do_exp(),    *do_getline(),    *do_index(),    *do_length(),
  849.     *do_sqrt(),    *do_log(),    *do_sprintf(),    *do_substr(),
  850.     *do_split(),    *do_system(),    *do_int(),    *do_close(),
  851.     *do_atan2(),    *do_sin(),    *do_cos(),    *do_rand(),
  852.     *do_srand(),    *do_match(),    *do_tolower(),    *do_toupper();
  853.  
  854. /* Special functions for debugging */
  855. #ifdef DEBUG
  856. NODE *do_prvars(), *do_bp();
  857. #endif
  858.  
  859. /* Tokentab is sorted ascii ascending order, so it can be binary searched. */
  860.  
  861. static struct token tokentab[] = {
  862.     { "BEGIN",    Node_illegal,        LEX_BEGIN,    0,    0 },
  863.     { "END",    Node_illegal,        LEX_END,    0,    0 },
  864.     { "atan2",    Node_builtin,        LEX_BUILTIN,    0,    do_atan2 },
  865. #ifdef DEBUG
  866.     { "bp",        Node_builtin,        LEX_BUILTIN,    0,    do_bp },
  867. #endif
  868.     { "break",    Node_K_break,        LEX_BREAK,    0,    0 },
  869.     { "close",    Node_builtin,        LEX_BUILTIN,    0,    do_close },
  870.     { "continue",    Node_K_continue,    LEX_CONTINUE,    0,    0 },
  871.     { "cos",    Node_builtin,        LEX_BUILTIN,    0,    do_cos },
  872.     { "delete",    Node_K_delete,        LEX_DELETE,    0,    0 },
  873.     { "do",        Node_K_do,        LEX_DO,        0,    0 },
  874.     { "else",    Node_illegal,        LEX_ELSE,    0,    0 },
  875.     { "exit",    Node_K_exit,        LEX_EXIT,    0,    0 },
  876.     { "exp",    Node_builtin,        LEX_BUILTIN,    0,    do_exp },
  877.     { "for",    Node_K_for,        LEX_FOR,    0,    0 },
  878.     { "func",    Node_K_function,    LEX_FUNCTION,    0,    0 },
  879.     { "function",    Node_K_function,    LEX_FUNCTION,    0,    0 },
  880.     { "getline",    Node_K_getline,        LEX_GETLINE,    0,    0 },
  881.     { "gsub",    Node_gsub,        LEX_SUB,    0,    0 },
  882.     { "if",        Node_K_if,        LEX_IF,        0,    0 },
  883.     { "in",        Node_illegal,        LEX_IN,        0,    0 },
  884.     { "index",    Node_builtin,        LEX_BUILTIN,    0,    do_index },
  885.     { "int",    Node_builtin,        LEX_BUILTIN,    0,    do_int },
  886.     { "length",    Node_builtin,        LEX_BUILTIN,    0,    do_length },
  887.     { "log",    Node_builtin,        LEX_BUILTIN,    0,    do_log },
  888.     { "match",    Node_K_match,        LEX_MATCH,    0,    0 },
  889.     { "next",    Node_K_next,        LEX_NEXT,    0,    0 },
  890.     { "print",    Node_K_print,        LEX_PRINT,    0,    0 },
  891.     { "printf",    Node_K_printf,        LEX_PRINTF,    0,    0 },
  892. #ifdef DEBUG
  893.     { "prvars",    Node_builtin,        LEX_BUILTIN,    0,    do_prvars },
  894. #endif
  895.     { "rand",    Node_builtin,        LEX_BUILTIN,    0,    do_rand },
  896.     { "return",    Node_K_return,        LEX_RETURN,    0,    0 },
  897.     { "sin",    Node_builtin,        LEX_BUILTIN,    0,    do_sin },
  898.     { "split",    Node_builtin,        LEX_BUILTIN,    0,    do_split },
  899.     { "sprintf",    Node_builtin,        LEX_BUILTIN,    0,    do_sprintf },
  900.     { "sqrt",    Node_builtin,        LEX_BUILTIN,    0,    do_sqrt },
  901.     { "srand",    Node_builtin,        LEX_BUILTIN,    0,    do_srand },
  902.     { "sub",    Node_sub,        LEX_SUB,    0,    0 },
  903.     { "substr",    Node_builtin,        LEX_BUILTIN,    0,    do_substr },
  904.     { "system",    Node_builtin,        LEX_BUILTIN,    0,    do_system },
  905.     { "tolower",    Node_builtin,        LEX_BUILTIN,    1,    do_tolower },
  906.     { "toupper",    Node_builtin,        LEX_BUILTIN,    1,    do_toupper },
  907.     { "while",    Node_K_while,        LEX_WHILE,    0,    0 },
  908. };
  909.  
  910. /* VARARGS0 */
  911. static void
  912. yyerror(va_alist)
  913. va_dcl
  914. {
  915.     va_list args;
  916.     char *mesg;
  917.     char *a1;
  918.     register char *ptr, *beg;
  919.     static int list = 0;
  920.     char *scan;
  921.  
  922.     errcount++;
  923.     va_start(args);
  924.     mesg = va_arg(args, char *);
  925.     if (! list)
  926.         a1 = va_arg(args, char *);
  927.     va_end(args);
  928.     if (mesg || !list) {
  929.         /* Find the current line in the input file */
  930.         if (!lexptr) {
  931.             beg = "(END OF FILE)";
  932.             ptr = beg + 13;
  933.         } else {
  934.             if (*lexptr == '\n' && lexptr != lexptr_begin)
  935.                 --lexptr;
  936.             for (beg = lexptr; beg != lexptr_begin && *beg != '\n'; --beg)
  937.                 ;
  938.             /* NL isn't guaranteed */
  939.             for (ptr = lexptr; *ptr && *ptr != '\n'; ptr++)
  940.                 ;
  941.             if (beg != lexptr_begin)
  942.                 beg++;
  943.         }
  944.         msg("syntax error near line %d:\n%.*s", lineno, ptr - beg, beg);
  945.         scan = beg;
  946.         while (scan <= lexptr)
  947.             if (*scan++ == '\t')
  948.                 putc('\t', stderr);
  949.             else
  950.                 putc(' ', stderr);
  951.         putc('^', stderr);
  952.         putc(' ', stderr);
  953.         if (mesg) {
  954.             vfprintf(stderr, mesg, args);
  955.                 putc('\n', stderr);
  956.             exit(1);
  957.         } else {
  958.             if (a1) {
  959.                 fputs("expecting: ", stderr);
  960.                 fputs(a1, stderr);
  961.                 list = 1;
  962.                 return;
  963.             }
  964.         }
  965.         return;
  966.     }
  967.     if (a1) {
  968.         fputs(" or ", stderr);
  969.         fputs(a1, stderr);
  970.         putc('\n', stderr);
  971.         return;
  972.     }
  973.     putc('\n', stderr);
  974.     list = 0;
  975. }
  976.  
  977. /*
  978.  * Parse a C escape sequence.  STRING_PTR points to a variable containing a
  979.  * pointer to the string to parse.  That pointer is updated past the
  980.  * characters we use.  The value of the escape sequence is returned. 
  981.  *
  982.  * A negative value means the sequence \ newline was seen, which is supposed to
  983.  * be equivalent to nothing at all. 
  984.  *
  985.  * If \ is followed by a null character, we return a negative value and leave
  986.  * the string pointer pointing at the null character. 
  987.  *
  988.  * If \ is followed by 000, we return 0 and leave the string pointer after the
  989.  * zeros.  A value of 0 does not mean end of string.  
  990.  */
  991.  
  992. static int
  993. parse_escape(string_ptr)
  994. char **string_ptr;
  995. {
  996.     register int c = *(*string_ptr)++;
  997.     register int i;
  998.  
  999.     switch (c) {
  1000.     case 'a':
  1001.         if (strict)
  1002.             goto def;
  1003.         else
  1004.             return BELL;
  1005.     case 'b':
  1006.         return '\b';
  1007.     case 'f':
  1008.         return '\f';
  1009.     case 'n':
  1010.         return '\n';
  1011.     case 'r':
  1012.         return '\r';
  1013.     case 't':
  1014.         return '\t';
  1015.     case 'v':
  1016.         if (strict)
  1017.             goto def;
  1018.         else
  1019.             return '\v';
  1020.     case '\n':
  1021.         return -2;
  1022.     case 0:
  1023.         (*string_ptr)--;
  1024.         return 0;
  1025.     case '0':
  1026.     case '1':
  1027.     case '2':
  1028.     case '3':
  1029.     case '4':
  1030.     case '5':
  1031.     case '6':
  1032.     case '7':
  1033.         {
  1034.             register int i = c - '0';
  1035.             register int count = 0;
  1036.  
  1037.             while (++count < 3) {
  1038.                 if ((c = *(*string_ptr)++) >= '0' && c <= '7') {
  1039.                     i *= 8;
  1040.                     i += c - '0';
  1041.                 } else {
  1042.                     (*string_ptr)--;
  1043.                     break;
  1044.                 }
  1045.             }
  1046.             return i;
  1047.         }
  1048.     case 'x':
  1049.         if (strict)
  1050.             goto def;
  1051.  
  1052.         i = 0;
  1053.         while (1) {
  1054.             if (isxdigit((c = *(*string_ptr)++))) {
  1055.                 if (isdigit(c))
  1056.                     i += c - '0';
  1057.                 else if (isupper(c))
  1058.                     i += c - 'A' + 10;
  1059.                 else
  1060.                     i += c - 'a' + 10;
  1061.             } else {
  1062.                 (*string_ptr)--;
  1063.                 break;
  1064.             }
  1065.         }
  1066.         return i;
  1067.     default:
  1068.     def:
  1069.         return c;
  1070.     }
  1071. }
  1072.  
  1073. /*
  1074.  * Read the input and turn it into tokens. Input is now read from a file
  1075.  * instead of from malloc'ed memory. The main program takes a program
  1076.  * passed as a command line argument and writes it to a temp file. Otherwise
  1077.  * the file name is made available in an external variable.
  1078.  */
  1079. #line 1080 "awk.y"
  1080. static int
  1081. yylex()
  1082. {
  1083.     register int c;
  1084.     register int namelen;
  1085.     register char *tokstart;
  1086.     char *tokkey;
  1087.     static did_newline = 0;    /* the grammar insists that actions end
  1088.                  * with newlines.  This was easier than
  1089.                  * hacking the grammar. */
  1090.     int seen_e = 0;        /* These are for numbers */
  1091.     int seen_point = 0;
  1092.     extern char **sourcefile;
  1093.     extern int tempsource, numfiles;
  1094.     static int file_opened = 0;
  1095.     static FILE *fin;
  1096.     static char cbuf[BUFSIZ];
  1097.     int low, mid, high;
  1098. #ifdef DEBUG
  1099.     extern int debugging;
  1100. #endif
  1101.  
  1102.     if (! file_opened) {
  1103.         file_opened = 1;
  1104. #ifdef DEBUG
  1105.         if (debugging) {
  1106.             int i;
  1107.  
  1108.             for (i = 0; i <= numfiles; i++)
  1109.                 fprintf (stderr, "sourcefile[%d] = %s\n", i,
  1110.                         sourcefile[i]);
  1111.         }
  1112. #endif
  1113.     nextfile:
  1114.         if ((fin = pathopen (sourcefile[++curinfile])) == NULL)
  1115.             fatal("cannot open `%s' for reading (%s)",
  1116.                 sourcefile[curinfile],
  1117.                 sys_errlist[errno]);
  1118.         *(lexptr = cbuf) = '\0';
  1119.         /*
  1120.          * immediately unlink the tempfile so that it will
  1121.          * go away cleanly if we bomb.
  1122.          */
  1123.         if (tempsource && curinfile == 0)
  1124.             (void) unlink (sourcefile[curinfile]);
  1125.     }
  1126.  
  1127. retry:
  1128.     if (! *lexptr)
  1129.         if (fgets (cbuf, sizeof cbuf, fin) == NULL) {
  1130.             if (fin != NULL)
  1131.                 fclose (fin);    /* be neat and clean */
  1132.             if (curinfile < numfiles)
  1133.                 goto nextfile;
  1134.             return 0;
  1135.         } else
  1136.             lexptr = lexptr_begin = cbuf;
  1137.  
  1138.     if (want_regexp) {
  1139.         want_regexp = 0;
  1140.  
  1141.         /*
  1142.          * there is a potential bug if a regexp is followed by an
  1143.          * equal sign: "/foo/=bar" would result in assign_quotient
  1144.          * being returned as the next token.  Nothing is done about
  1145.          * it since it is not valid awk, but maybe something should
  1146.          * be done anyway. 
  1147.          */
  1148.  
  1149.         tokstart = lexptr;
  1150.         while (c = *lexptr++) {
  1151.             switch (c) {
  1152.             case '\\':
  1153.                 if (*lexptr++ == '\0') {
  1154.                     yyerror("unterminated regexp ends with \\");
  1155.                     return ERROR;
  1156.                 } else if (lexptr[-1] == '\n')
  1157.                     goto retry;
  1158.                 break;
  1159.             case '/':    /* end of the regexp */
  1160.                 lexptr--;
  1161.                 yylval.sval = tokstart;
  1162.                 return REGEXP;
  1163.             case '\n':
  1164.                 lineno++;
  1165.             case '\0':
  1166.                 lexptr--;    /* so error messages work */
  1167.                 yyerror("unterminated regexp");
  1168.                 return ERROR;
  1169.             }
  1170.         }
  1171.     }
  1172.  
  1173.     if (*lexptr == '\n') {
  1174.         lexptr++;
  1175.         lineno++;
  1176.         return NEWLINE;
  1177.     }
  1178.  
  1179.     while (*lexptr == ' ' || *lexptr == '\t')
  1180.         lexptr++;
  1181.  
  1182.     tokstart = lexptr;
  1183.  
  1184.     switch (c = *lexptr++) {
  1185.     case 0:
  1186.         return 0;
  1187.  
  1188.     case '\n':
  1189.         lineno++;
  1190.         return NEWLINE;
  1191.  
  1192.     case '#':        /* it's a comment */
  1193.         while (*lexptr != '\n' && *lexptr != '\0')
  1194.             lexptr++;
  1195.         goto retry;
  1196.  
  1197.     case '\\':
  1198.         if (*lexptr == '\n') {
  1199.             lineno++;
  1200.             lexptr++;
  1201.             goto retry;
  1202.         } else
  1203.             break;
  1204.     case ')':
  1205.     case ']':
  1206.     case '(':    
  1207.     case '[':
  1208.     case '$':
  1209.     case ';':
  1210.     case ':':
  1211.     case '?':
  1212.  
  1213.         /*
  1214.          * set node type to ILLEGAL because the action should set it
  1215.          * to the right thing 
  1216.          */
  1217.         yylval.nodetypeval = Node_illegal;
  1218.         return c;
  1219.  
  1220.     case '{':
  1221.     case ',':
  1222.         yylval.nodetypeval = Node_illegal;
  1223.         return c;
  1224.  
  1225.     case '*':
  1226.         if (*lexptr == '=') {
  1227.             yylval.nodetypeval = Node_assign_times;
  1228.             lexptr++;
  1229.             return ASSIGNOP;
  1230.         } else if (*lexptr == '*') {    /* make ** and **= aliases
  1231.                          * for ^ and ^= */
  1232.             if (lexptr[1] == '=') {
  1233.                 yylval.nodetypeval = Node_assign_exp;
  1234.                 lexptr += 2;
  1235.                 return ASSIGNOP;
  1236.             } else {
  1237.                 yylval.nodetypeval = Node_illegal;
  1238.                 lexptr++;
  1239.                 return '^';
  1240.             }
  1241.         }
  1242.         yylval.nodetypeval = Node_illegal;
  1243.         return c;
  1244.  
  1245.     case '/':
  1246.         if (*lexptr == '=') {
  1247.             yylval.nodetypeval = Node_assign_quotient;
  1248.             lexptr++;
  1249.             return ASSIGNOP;
  1250.         }
  1251.         yylval.nodetypeval = Node_illegal;
  1252.         return c;
  1253.  
  1254.     case '%':
  1255.         if (*lexptr == '=') {
  1256.             yylval.nodetypeval = Node_assign_mod;
  1257.             lexptr++;
  1258.             return ASSIGNOP;
  1259.         }
  1260.         yylval.nodetypeval = Node_illegal;
  1261.         return c;
  1262.  
  1263.     case '^':
  1264.         if (*lexptr == '=') {
  1265.             yylval.nodetypeval = Node_assign_exp;
  1266.             lexptr++;
  1267.             return ASSIGNOP;
  1268.         }
  1269.         yylval.nodetypeval = Node_illegal;
  1270.         return c;
  1271.  
  1272.     case '+':
  1273.         if (*lexptr == '=') {
  1274.             yylval.nodetypeval = Node_assign_plus;
  1275.             lexptr++;
  1276.             return ASSIGNOP;
  1277.         }
  1278.         if (*lexptr == '+') {
  1279.             yylval.nodetypeval = Node_illegal;
  1280.             lexptr++;
  1281.             return INCREMENT;
  1282.         }
  1283.         yylval.nodetypeval = Node_illegal;
  1284.         return c;
  1285.  
  1286.     case '!':
  1287.         if (*lexptr == '=') {
  1288.             yylval.nodetypeval = Node_notequal;
  1289.             lexptr++;
  1290.             return RELOP;
  1291.         }
  1292.         if (*lexptr == '~') {
  1293.             yylval.nodetypeval = Node_nomatch;
  1294.             lexptr++;
  1295.             return MATCHOP;
  1296.         }
  1297.         yylval.nodetypeval = Node_illegal;
  1298.         return c;
  1299.  
  1300.     case '<':
  1301.         if (*lexptr == '=') {
  1302.             yylval.nodetypeval = Node_leq;
  1303.             lexptr++;
  1304.             return RELOP;
  1305.         }
  1306.         yylval.nodetypeval = Node_less;
  1307.         return c;
  1308.  
  1309.     case '=':
  1310.         if (*lexptr == '=') {
  1311.             yylval.nodetypeval = Node_equal;
  1312.             lexptr++;
  1313.             return RELOP;
  1314.         }
  1315.         yylval.nodetypeval = Node_assign;
  1316.         return ASSIGNOP;
  1317.  
  1318.     case '>':
  1319.         if (*lexptr == '=') {
  1320.             yylval.nodetypeval = Node_geq;
  1321.             lexptr++;
  1322.             return RELOP;
  1323.         } else if (*lexptr == '>') {
  1324.             yylval.nodetypeval = Node_redirect_append;
  1325.             lexptr++;
  1326.             return APPEND_OP;
  1327.         }
  1328.         yylval.nodetypeval = Node_greater;
  1329.         return c;
  1330.  
  1331.     case '~':
  1332.         yylval.nodetypeval = Node_match;
  1333.         return MATCHOP;
  1334.  
  1335.     case '}':
  1336.         /*
  1337.          * Added did newline stuff.  Easier than
  1338.          * hacking the grammar
  1339.          */
  1340.         if (did_newline) {
  1341.             did_newline = 0;
  1342.             return c;
  1343.         }
  1344.         did_newline++;
  1345.         --lexptr;
  1346.         return NEWLINE;
  1347.  
  1348.     case '"':
  1349.         while (*lexptr != '\0') {
  1350.             switch (*lexptr++) {
  1351.             case '\\':
  1352.                 if (*lexptr++ != '\0')
  1353.                     break;
  1354.                 /* fall through */
  1355.             case '\n':
  1356.                 yyerror("unterminated string");
  1357.                 return ERROR;
  1358.             case '\"':
  1359.                 /* Skip the doublequote */
  1360.                 yylval.sval = tokstart + 1;
  1361.                 return YSTRING;
  1362.             }
  1363.         }
  1364.         return ERROR;
  1365.  
  1366.     case '-':
  1367.         if (*lexptr == '=') {
  1368.             yylval.nodetypeval = Node_assign_minus;
  1369.             lexptr++;
  1370.             return ASSIGNOP;
  1371.         }
  1372.         if (*lexptr == '-') {
  1373.             yylval.nodetypeval = Node_illegal;
  1374.             lexptr++;
  1375.             return DECREMENT;
  1376.         }
  1377.  
  1378.         /*
  1379.          * It looks like space tab comma and newline are the legal
  1380.          * places for a UMINUS.  Have we missed any? 
  1381.          */
  1382.         if ((! isdigit(*lexptr) && *lexptr != '.') ||
  1383.             (lexptr > lexptr_begin + 1 &&
  1384.                     ! index(" \t,\n", lexptr[-2]))) {
  1385.  
  1386.             /*
  1387.              * set node type to ILLEGAL because the action should
  1388.              * set it to the right thing 
  1389.              */
  1390.             yylval.nodetypeval = Node_illegal;
  1391.             return c;
  1392.         }
  1393.         /* FALL through into number code */
  1394.     case '0':
  1395.     case '1':
  1396.     case '2':
  1397.     case '3':
  1398.     case '4':
  1399.     case '5':
  1400.     case '6':
  1401.     case '7':
  1402.     case '8':
  1403.     case '9':
  1404.     case '.':
  1405.         /* It's a number */
  1406.         if (c == '-')
  1407.             namelen = 1;
  1408.         else
  1409.             namelen = 0;
  1410.         for (; (c = tokstart[namelen]) != '\0'; namelen++) {
  1411.             switch (c) {
  1412.             case '.':
  1413.                 if (seen_point)
  1414.                     goto got_number;
  1415.                 ++seen_point;
  1416.                 break;
  1417.             case 'e':
  1418.             case 'E':
  1419.                 if (seen_e)
  1420.                     goto got_number;
  1421.                 ++seen_e;
  1422.                 if (tokstart[namelen + 1] == '-' ||
  1423.                     tokstart[namelen + 1] == '+')
  1424.                     namelen++;
  1425.                 break;
  1426.             case '0':
  1427.             case '1':
  1428.             case '2':
  1429.             case '3':
  1430.             case '4':
  1431.             case '5':
  1432.             case '6':
  1433.             case '7':
  1434.             case '8':
  1435.             case '9':
  1436.                 break;
  1437.             default:
  1438.                 goto got_number;
  1439.             }
  1440.         }
  1441.  
  1442. got_number:
  1443.         lexptr = tokstart + namelen;
  1444.         yylval.fval = atof(tokstart);
  1445.         return NUMBER;
  1446.  
  1447.     case '&':
  1448.         if (*lexptr == '&') {
  1449.             yylval.nodetypeval = Node_and;
  1450.             while (c = *++lexptr) {
  1451.                 if (c == '#')
  1452.                     while ((c = *++lexptr) != '\n'
  1453.                            && c != '\0')
  1454.                         ;
  1455.                 if (c == '\n')
  1456.                     lineno++;
  1457.                 else if (!isspace(c))
  1458.                     break;
  1459.             }
  1460.             return LEX_AND;
  1461.         }
  1462.         return ERROR;
  1463.  
  1464.     case '|':
  1465.         if (*lexptr == '|') {
  1466.             yylval.nodetypeval = Node_or;
  1467.             while (c = *++lexptr) {
  1468.                 if (c == '#')
  1469.                     while ((c = *++lexptr) != '\n'
  1470.                            && c != '\0')
  1471.                         ;
  1472.                 if (c == '\n')
  1473.                     lineno++;
  1474.                 else if (!isspace(c))
  1475.                     break;
  1476.             }
  1477.             return LEX_OR;
  1478.         }
  1479.             yylval.nodetypeval = Node_illegal;
  1480.             return c;
  1481.         }
  1482.  
  1483.     if (c != '_' && !isalpha(c)) {
  1484.         yyerror("Invalid char '%c' in expression\n", c);
  1485.         return ERROR;
  1486.     }
  1487.  
  1488.     /* it's some type of name-type-thing.  Find its length */
  1489.     for (namelen = 0; is_identchar(tokstart[namelen]); namelen++)
  1490.         /* null */ ;
  1491.     emalloc(tokkey, char *, namelen+1, "yylex");
  1492.     (void) strncpy (tokkey, tokstart, namelen);
  1493.     tokkey[namelen] = '\0';
  1494.  
  1495.     /* See if it is a special token.  */
  1496.     low = 0;
  1497.     high = (sizeof (tokentab) / sizeof (tokentab[0])) - 1;
  1498.     while (low <= high) {
  1499.         int i, c;
  1500.  
  1501.         mid = (low + high) / 2;
  1502.  
  1503.     compare:
  1504.         c = *tokstart - tokentab[mid].operator[0];
  1505.         i = c ? c : strcmp (tokkey, tokentab[mid].operator);
  1506.  
  1507.         if (i < 0) {        /* token < mid */
  1508.             high = mid - 1;
  1509.         } else if (i > 0) {    /* token > mid */
  1510.             low = mid + 1;
  1511.         } else {
  1512.             lexptr = tokstart + namelen;
  1513.             if (strict && tokentab[mid].nostrict)
  1514.                 break;
  1515.             if (tokentab[mid].class == LEX_BUILTIN)
  1516.                 yylval.ptrval = tokentab[mid].ptr;
  1517.             else
  1518.                 yylval.nodetypeval = tokentab[mid].value;
  1519.             return tokentab[mid].class;
  1520.         }
  1521.     }
  1522.  
  1523.     /* It's a name.  See how long it is.  */
  1524.     yylval.sval = tokkey;
  1525.     lexptr = tokstart + namelen;
  1526.     if (*lexptr == '(')
  1527.         return FUNC_CALL;
  1528.     else
  1529.         return NAME;
  1530. }
  1531.  
  1532. #ifndef DEFPATH
  1533. #ifdef MSDOS
  1534. #define DEFPATH    "."
  1535. #define ENVSEP    ';'
  1536. #else
  1537. #define DEFPATH    ".:/usr/lib/awk:/usr/local/lib/awk"
  1538. #define ENVSEP    ':'
  1539. #endif
  1540. #endif
  1541.  
  1542. static FILE *
  1543. pathopen (file)
  1544. char *file;
  1545. {
  1546.     static char *savepath = DEFPATH;
  1547.     static int first = 1;
  1548.     char *awkpath, *cp;
  1549.     char trypath[BUFSIZ];
  1550.     FILE *fp;
  1551.     extern int debugging;
  1552.  
  1553.     if (strcmp (file, "-") == 0)
  1554.         return (stdin);
  1555.  
  1556.     if (strict)
  1557.         return (fopen (file, "r"));
  1558.  
  1559.     if (first) {
  1560.         first = 0;
  1561.         if ((awkpath = getenv ("AWKPATH")) != NULL && *awkpath)
  1562.             savepath = awkpath;    /* used for restarting */
  1563. #ifdef MSDOS
  1564.         else if ((awkpath = getenv ("INIT")) != NULL && *awkpath)
  1565.             savepath = awkpath;    /* MSC 5.1 users may prefer */
  1566.                         /* to use INIT            */
  1567. #endif
  1568.     }
  1569.     awkpath = savepath;
  1570.  
  1571.     /* some kind of path name, no search */
  1572. #ifndef MSDOS
  1573.     if (index (file, '/') != NULL)
  1574. #else
  1575.     if (index (file, '/') != NULL || index (file, '\\') != NULL
  1576.             || index (file, ':') != NULL)
  1577. #endif
  1578.         return (fdopen(devopen (file, "r"), "r"));
  1579.  
  1580.     do {
  1581.         /* this should take into account limits on size of trypath */
  1582.         for (cp = trypath; *awkpath && *awkpath != ENVSEP; )
  1583.             *cp++ = *awkpath++;
  1584.         *cp++ = '/';
  1585.         *cp = '\0';    /* clear left over junk */
  1586.         strcat (cp, file);
  1587.         if ((fp = fdopen(devopen (trypath, "r"), "r")) != NULL)
  1588.             return (fp);
  1589.  
  1590.         /* no luck, keep going */
  1591.         if(*awkpath)
  1592.             awkpath++;    /* skip colon */
  1593.     } while (*awkpath);
  1594. #ifdef MSDOS
  1595. /*
  1596. ** under DOS (and probably elsewhere) you might have one of the awk paths
  1597. ** defined, WITHOUT the current working directory in it.  Therefore you
  1598. ** should try to open the file in the current directory
  1599. */
  1600.     return fdopen(devopen(file,"r"),"r");
  1601. #else
  1602.     return (NULL);
  1603. #endif
  1604. }
  1605.  
  1606. static NODE *
  1607. node_common(op)
  1608. NODETYPE op;
  1609. {
  1610.     register NODE *r;
  1611.     extern int numfiles;
  1612.     extern int tempsource;
  1613.     extern char **sourcefile;
  1614.  
  1615.     r = newnode(op);
  1616.     r->source_line = lineno;
  1617.     if (numfiles > 1 && !tempsource)
  1618.         r->source_file = sourcefile[curinfile];
  1619.     else
  1620.         r->source_file = NULL;
  1621.     return r;
  1622. }
  1623.  
  1624. /*
  1625.  * This allocates a node with defined lnode and rnode. 
  1626.  * This should only be used by yyparse+co while reading in the program 
  1627.  */
  1628. NODE *
  1629. node(left, op, right)
  1630. NODE *left, *right;
  1631. NODETYPE op;
  1632. {
  1633.     register NODE *r;
  1634.  
  1635.     r = node_common(op);
  1636.     r->lnode = left;
  1637.     r->rnode = right;
  1638.     return r;
  1639. }
  1640.  
  1641. /*
  1642.  * This allocates a node with defined subnode and proc
  1643.  * Otherwise like node()
  1644.  */
  1645. static NODE *
  1646. snode(subn, op, procp)
  1647. NODETYPE op;
  1648. NODE *(*procp) ();
  1649. NODE *subn;
  1650. {
  1651.     register NODE *r;
  1652.  
  1653.     r = node_common(op);
  1654.     r->subnode = subn;
  1655.     r->proc = procp;
  1656.     return r;
  1657. }
  1658.  
  1659. /*
  1660.  * This allocates a Node_line_range node with defined condpair and
  1661.  * zeroes the trigger word to avoid the temptation of assuming that calling
  1662.  * 'node( foo, Node_line_range, 0)' will properly initialize 'triggered'. 
  1663.  */
  1664. /* Otherwise like node() */
  1665. static NODE *
  1666. mkrangenode(cpair)
  1667. NODE *cpair;
  1668. {
  1669.     register NODE *r;
  1670.  
  1671.     r = newnode(Node_line_range);
  1672.     r->condpair = cpair;
  1673.     r->triggered = 0;
  1674.     return r;
  1675. }
  1676.  
  1677. /* Build a for loop */
  1678. static NODE *
  1679. make_for_loop(init, cond, incr)
  1680. NODE *init, *cond, *incr;
  1681. {
  1682.     register FOR_LOOP_HEADER *r;
  1683.     NODE *n;
  1684.  
  1685.     emalloc(r, FOR_LOOP_HEADER *, sizeof(FOR_LOOP_HEADER), "make_for_loop");
  1686.     n = newnode(Node_illegal);
  1687.     r->init = init;
  1688.     r->cond = cond;
  1689.     r->incr = incr;
  1690.     n->sub.nodep.r.hd = r;
  1691.     return n;
  1692. }
  1693.  
  1694. /*
  1695.  * Install a name in the hash table specified, even if it is already there.
  1696.  * Name stops with first non alphanumeric. Caller must check against
  1697.  * redefinition if that is desired. 
  1698.  */
  1699. NODE *
  1700. install(table, name, value)
  1701. NODE **table;
  1702. char *name;
  1703. NODE *value;
  1704. {
  1705.     register NODE *hp;
  1706.     register int len, bucket;
  1707.     register char *p;
  1708.  
  1709.     len = 0;
  1710.     p = name;
  1711.     while (is_identchar(*p))
  1712.         p++;
  1713.     len = p - name;
  1714.  
  1715.     hp = newnode(Node_hashnode);
  1716.     bucket = hashf(name, len, HASHSIZE);
  1717.     hp->hnext = table[bucket];
  1718.     table[bucket] = hp;
  1719.     hp->hlength = len;
  1720.     hp->hvalue = value;
  1721.     emalloc(hp->hname, char *, len + 1, "install");
  1722.     bcopy(name, hp->hname, len);
  1723.     hp->hname[len] = '\0';
  1724.     hp->hvalue->varname = hp->hname;
  1725.     return hp->hvalue;
  1726. }
  1727.  
  1728. /*
  1729.  * find the most recent hash node for name name (ending with first
  1730.  * non-identifier char) installed by install 
  1731.  */
  1732. NODE *
  1733. lookup(table, name)
  1734. NODE **table;
  1735. char *name;
  1736. {
  1737.     register char *bp;
  1738.     register NODE *bucket;
  1739.     register int len;
  1740.  
  1741.     for (bp = name; is_identchar(*bp); bp++)
  1742.         ;
  1743.     len = bp - name;
  1744.     bucket = table[hashf(name, len, HASHSIZE)];
  1745.     while (bucket) {
  1746.         if (bucket->hlength == len && STREQN(bucket->hname, name, len))
  1747.             return bucket->hvalue;
  1748.         bucket = bucket->hnext;
  1749.     }
  1750.     return NULL;
  1751. }
  1752.  
  1753. #define HASHSTEP(old, c) ((old << 1) + c)
  1754. #define MAKE_POS(v) (v & ~0x80000000)    /* make number positive */
  1755.  
  1756. /*
  1757.  * return hash function on name.
  1758.  */
  1759. static int
  1760. hashf(name, len, hashsize)
  1761. register char *name;
  1762. register int len;
  1763. int hashsize;
  1764. {
  1765.     register int r = 0;
  1766.  
  1767.     while (len--)
  1768.         r = HASHSTEP(r, *name++);
  1769.  
  1770.     r = MAKE_POS(r) % hashsize;
  1771.     return r;
  1772. }
  1773.  
  1774. /*
  1775.  * Add new to the rightmost branch of LIST.  This uses n^2 time, but doesn't
  1776.  * get used enough to make optimizing worth it. . . 
  1777.  */
  1778. /* You don't believe me?  Profile it yourself! */
  1779. static NODE *
  1780. append_right(list, new)
  1781. NODE *list, *new;
  1782.  
  1783. {
  1784.     register NODE *oldlist;
  1785.  
  1786.     oldlist = list;
  1787.     while (list->rnode != NULL)
  1788.         list = list->rnode;
  1789.     list->rnode = new;
  1790.     return oldlist;
  1791. }
  1792.  
  1793. /*
  1794.  * check if name is already installed;  if so, it had better have Null value,
  1795.  * in which case def is added as the value. Otherwise, install name with def
  1796.  * as value. 
  1797.  */
  1798. static void
  1799. func_install(params, def)
  1800. NODE *params;
  1801. NODE *def;
  1802. {
  1803.     NODE *r;
  1804.  
  1805.     pop_params(params->rnode);
  1806.     pop_var(params, 0);
  1807.     r = lookup(variables, params->param);
  1808.     if (r != NULL) {
  1809.         fatal("function name `%s' previously defined", params->param);
  1810.     } else
  1811.         (void) install(variables, params->param,
  1812.             node(params, Node_func, def));
  1813. }
  1814.  
  1815. static void
  1816. pop_var(np, freeit)
  1817. NODE *np;
  1818. int freeit;
  1819. {
  1820.     register char *bp;
  1821.     register NODE *bucket, **save;
  1822.     register int len;
  1823.     char *name;
  1824.  
  1825.     name = np->param;
  1826.     for (bp = name; is_identchar(*bp); bp++)
  1827.         ;
  1828.     len = bp - name;
  1829.     save = &(variables[hashf(name, len, HASHSIZE)]);
  1830.     for (bucket = *save; bucket; bucket = bucket->hnext) {
  1831.         if (len == bucket->hlength && STREQN(bucket->hname, name, len)) {
  1832.             *save = bucket->hnext;
  1833.             freenode(bucket);
  1834.             free(bucket->hname);
  1835.             if (freeit)
  1836.                 free(np->param);
  1837.             return;
  1838.         }
  1839.         save = &(bucket->hnext);
  1840.     }
  1841. }
  1842.  
  1843. static void
  1844. pop_params(params)
  1845. NODE *params;
  1846. {
  1847.     register NODE *np;
  1848.  
  1849.     for (np = params; np != NULL; np = np->rnode)
  1850.         pop_var(np, 1);
  1851. }
  1852.  
  1853. static NODE *
  1854. make_param(name)
  1855. char *name;
  1856. {
  1857.     NODE *r;
  1858.  
  1859.     r = newnode(Node_param_list);
  1860.     r->param = name;
  1861.     r->rnode = NULL;
  1862.     r->param_cnt = param_counter++;
  1863.     return (install(variables, name, r));
  1864. }
  1865.  
  1866. /* Name points to a variable name.  Make sure its in the symbol table */
  1867. NODE *
  1868. variable(name)
  1869. char *name;
  1870. {
  1871.     register NODE *r;
  1872.  
  1873.     if ((r = lookup(variables, name)) == NULL)
  1874.         r = install(variables, name,
  1875.             node(Nnull_string, Node_var, (NODE *) NULL));
  1876.     return r;
  1877. }
  1878.