home *** CD-ROM | disk | FTP | other *** search
/ rtsi.com / 2014.01.www.rtsi.com.tar / www.rtsi.com / OS9 / TOP / USR / SRC / gawk2.0.t.Z / gawk2.0.t / awk.y < prev    next >
Text File  |  1989-01-01  |  35KB  |  1,415 lines

  1. /*
  2.  * gawk -- GNU version of awk
  3.  * Copyright (C) 1986 Free Software Foundation
  4.  *   Written by Paul Rubin, August 1986
  5.  *
  6.  * $Log:    awk.y,v $
  7.  * Revision 1.23  88/12/07  19:59:25  david
  8.  * changes for incorporating source filename in error messages
  9.  * 
  10.  * Revision 1.22  88/11/23  21:37:24  david
  11.  * Arnold: refinements of AWKPATH code
  12.  * 
  13.  * Revision 1.21  88/11/22  13:46:45  david
  14.  * Arnold: changes for case-insensitive matching
  15.  * 
  16.  * Revision 1.20  88/11/15  10:13:37  david
  17.  * Arnold: allow multiple -f options and search in directories for awk libraries,
  18.  * directories specified by AWKPATH env. variable; cleanupo of comments and
  19.  * #includes
  20.  * 
  21.  * Revision 1.19  88/11/14  21:51:30  david
  22.  * Arnold: added error message for BEGIN or END without any action at all;
  23.  * unlink temporary source file right after creation so it goes away on bomb
  24.  * 
  25.  * Revision 1.18  88/10/19  22:00:56  david
  26.  * generalize (and correct) what pattern can be in pattern {action}; this
  27.  * introduces quite a few new conflicts that should be checked thoroughly
  28.  * at some point, but they don't seem to do any harm at first glance
  29.  * replace malloc with emalloc
  30.  * 
  31.  * Revision 1.17  88/10/17  19:52:01  david
  32.  * Arnold: cleanup, purge FAST
  33.  * 
  34.  * Revision 1.16  88/10/13  22:02:16  david
  35.  * cleanup of yyerror and other error messages
  36.  * 
  37.  * Revision 1.15  88/10/06  23:24:57  david
  38.  * accept     var space ++var
  39.  * accept underscore as first character of a variable name
  40.  * 
  41.  * Revision 1.14  88/06/13  18:01:46  david
  42.  * delete \a (change from Arnold)
  43.  * 
  44.  * Revision 1.13  88/06/08  00:29:42  david
  45.  * better attempt at keeping track of line numbers
  46.  * change grammar to properly handle newlines after && or ||
  47.  * 
  48.  * Revision 1.12  88/06/07  23:39:02  david
  49.  * little delint
  50.  * 
  51.  * Revision 1.11  88/06/05  22:17:40  david
  52.  * make_name() becomes make_param() (again!)
  53.  * func_level goes away, param_counter makes entrance
  54.  * 
  55.  * Revision 1.10  88/05/30  09:49:02  david
  56.  * obstack_free was being called at end of function definition, freeing
  57.  * memory that might be part of global variables referenced only inside
  58.  * functions; commented out for now, will have to selectively free later.
  59.  * cleanup: regexp now returns a NODE *
  60.  * 
  61.  * Revision 1.9  88/05/27  11:04:53  david
  62.  * added print[f] '(' ... ')'     (optional parentheses)
  63.  * for some reason want_redirect wasn't getting set for PRINT, so I set it in 
  64.  * yylex()
  65.  * 
  66.  * Revision 1.8  88/05/26  22:52:14  david
  67.  * fixed cmd | getline
  68.  * added compound patterns (they got lost somewhere along the line)
  69.  * fixed error message in yylex()
  70.  * added null statement 
  71.  * 
  72.  * Revision 1.7  88/05/13  22:05:29  david
  73.  * moved BEGIN and END block merging here
  74.  * BEGIN, END and function defs. are no longer incorporated into main parse tree
  75.  * fixed    command | getline
  76.  * fixed function install and definition
  77.  * 
  78.  * Revision 1.6  88/05/09  17:47:50  david
  79.  * Arnold's coded binary search
  80.  * 
  81.  * Revision 1.5  88/05/04  12:31:13  david
  82.  * be a bit more careful about types
  83.  * make_for_loop() now returns a NODE *
  84.  * keyword search now uses bsearch() -- need a public domain version of this
  85.  * added back stuff in yylex() that got lost somewhere along the line
  86.  * malloc() tokens in yylex() since they were previously just pointers into
  87.  *  current line that got overwritten by the next fgets() -- these need to get
  88.  *  freed at some point
  89.  * fixed backslash line continuation interaction with CONCAT
  90.  * 
  91.  * Revision 1.4  88/04/14  17:03:51  david
  92.  * reinstalled a fix to do with line continuation
  93.  * 
  94.  * Revision 1.3  88/04/14  14:41:01  david
  95.  * Arnold's changes to yylex to read program from a file
  96.  * 
  97.  * Revision 1.5  88/03/18  21:00:07  david
  98.  * Baseline -- hoefully all the functionality of the new awk added.
  99.  * Just debugging and tuning to do.
  100.  * 
  101.  * Revision 1.4  87/11/19  14:37:20  david
  102.  * added a bunch of ew builtin functions
  103.  * added new rules for getline to provide new functionality
  104.  * minor cleanup of redirection handling
  105.  * generalized make_param into make_name
  106.  * 
  107.  * Revision 1.3  87/11/09  21:22:33  david
  108.  * added macinery for user-defined functions (including return)
  109.  * added delete, do-while and system
  110.  * reformatted and revised grammer to improve error-handling
  111.  * changes to yyerror to give improved error messages
  112.  * 
  113.  * Revision 1.2  87/10/29  21:33:28  david
  114.  * added test for membership in an array, as in:  if ("yes" in answers) ...
  115.  * 
  116.  * Revision 1.1  87/10/27  15:23:21  david
  117.  * Initial revision
  118.  * 
  119.  */
  120.  
  121. /*
  122. GAWK is distributed in the hope that it will be useful, but WITHOUT ANY
  123. WARRANTY.  No author or distributor accepts responsibility to anyone
  124. for the consequences of using it or for whether it serves any
  125. particular purpose or works at all, unless he says so in writing.
  126. Refer to the GAWK General Public License for full details.
  127.  
  128. Everyone is granted permission to copy, modify and redistribute GAWK,
  129. but only under the conditions described in the GAWK General Public
  130. License.  A copy of this license is supposed to have been given to you
  131. along with GAWK so you can know your rights and responsibilities.  It
  132. should be in a file named COPYING.  Among other things, the copyright
  133. notice and this notice must be preserved on all copies.
  134.  
  135. In other words, go ahead and share GAWK, but don't try to stop
  136. anyone else from sharing it farther.  Help stamp out software hoarding!
  137. */
  138.  
  139. %{
  140. #define YYDEBUG 12
  141. #define YYIMPROVE
  142.  
  143. #include "awk.h"
  144.  
  145. static int yylex ();
  146.  
  147. /*
  148.  * The following variable is used for a very sickening thing.
  149.  * The awk language uses white space as the string concatenation
  150.  * operator, but having a white space token that would have to appear
  151.  * everywhere in all the grammar rules would be unbearable.
  152.  * It turns out we can return CONCAT_OP exactly when there really
  153.  * is one, just from knowing what kinds of other tokens it can appear
  154.  * between (namely, constants, variables, or close parentheses).
  155.  * This is because concatenation has the lowest priority of all
  156.  * operators.  want_concat_token is used to remember that something
  157.  * that could be the left side of a concat has just been returned.
  158.  *
  159.  * If anyone knows a cleaner way to do this (don't look at the Un*x
  160.  * code to find one, though), please suggest it.
  161.  */
  162. static int want_concat_token;
  163.  
  164. /* Two more horrible kludges.  The same comment applies to these two too */
  165. static int want_regexp;        /* lexical scanning kludge */
  166. static int want_redirect;    /* similarly */
  167. int lineno = 1;            /* for error msgs */
  168.  
  169. /* During parsing of a gawk program, the pointer to the next character
  170.    is in this variable.  */
  171. char *lexptr;        /* moved it up here */
  172. char *lexptr_begin;    /* for error msgs */
  173. char *func_def;
  174. extern int errcount;
  175. extern struct obstack var_stack;
  176. extern NODE *begin_block;
  177. extern NODE *end_block;
  178. extern struct re_pattern_buffer *mk_re_parse();
  179. extern int param_counter;
  180. struct re_pattern_buffer *rp;
  181. %}
  182.  
  183. %union {
  184.     long lval;
  185.     AWKNUM fval;
  186.     NODE *nodeval;
  187.     NODETYPE nodetypeval;
  188.     char *sval;
  189.     NODE *(*ptrval)();
  190. }
  191.  
  192. %type <nodeval> function_prologue function_body
  193. %type <nodeval> exp sub_exp start program rule pattern expression_list
  194. %type <nodeval>    action variable redirection param_list opt_expression_list
  195. %type <nodeval>    statements statement if_statement opt_param_list 
  196. %type <nodeval> opt_exp opt_variable regexp
  197. %type <nodetypeval> whitespace r_paren
  198.  
  199. %token <sval> NAME REGEXP YSTRING
  200. %token <lval> ERROR INCDEC
  201. %token <fval> NUMBER
  202. %token <nodetypeval> ASSIGNOP RELOP MATCHOP NEWLINE REDIRECT_OP CONCAT_OP
  203. %token <nodetypeval> LEX_BEGIN LEX_END LEX_IF LEX_ELSE LEX_RETURN LEX_DELETE
  204. %token <nodetypeval> LEX_WHILE LEX_DO LEX_FOR LEX_BREAK LEX_CONTINUE
  205. %token <nodetypeval> LEX_PRINT LEX_PRINTF LEX_NEXT LEX_EXIT LEX_FUNCTION
  206. %token <nodetypeval> LEX_GETLINE LEX_SUB LEX_MATCH
  207. %token <nodetypeval> LEX_IN
  208. %token <lval> LEX_AND LEX_OR INCREMENT DECREMENT
  209. %token <ptrval> LEX_BUILTIN
  210.  
  211. /* these are just yylval numbers */
  212.  
  213. /* Lowest to highest */
  214. %right ASSIGNOP
  215. %right '?' ':'
  216. %left LEX_OR
  217. %left LEX_AND
  218. %left LEX_IN
  219. %nonassoc MATCHOP
  220. %nonassoc RELOP
  221. %nonassoc REDIRECT_OP
  222. %left CONCAT_OP
  223. %left '+' '-'
  224. %left '*' '/' '%'
  225. %right UNARY
  226. %right '^'
  227. %left INCREMENT DECREMENT
  228. %left '$'
  229.  
  230. %%
  231.  
  232. start
  233.     : opt_newlines program
  234.         { expression_value = $2; }
  235.     ;
  236.  
  237. program
  238.     : rule
  239.         { 
  240.             if ($1 != NULL)
  241.                 $$ = node ($1, Node_rule_list,(NODE *) NULL);
  242.             else
  243.                 $$ = NULL;
  244.             yyerrok;
  245.         }
  246.     | program rule
  247.         /* cons the rule onto the tail of list */
  248.         {
  249.             if ($2 == NULL)
  250.                 $$ = $1;
  251.             else if ($1 == NULL)
  252.                 $$ = node($2, Node_rule_list,(NODE *) NULL);
  253.             else
  254.                 $$ = append_right ($1,
  255.                    node($2, Node_rule_list,(NODE *) NULL));
  256.             yyerrok;
  257.         }
  258.     | error    { $$ = NULL; }
  259.     | program error
  260.     ;
  261.  
  262. rule
  263.     : LEX_BEGIN action
  264.       {
  265.         if (begin_block)
  266.             append_right (begin_block, node(
  267.                 node((NODE *)NULL, Node_rule_node, $2),
  268.                 Node_rule_list, (NODE *)NULL) );
  269.         else
  270.             begin_block = node(node((NODE *)NULL,Node_rule_node,$2),
  271.                 Node_rule_list, (NODE *)NULL);
  272.         $$ = NULL;
  273.         yyerrok;
  274.       }
  275.     | LEX_END action
  276.       {
  277.         if (end_block)
  278.             append_right (end_block, node(
  279.                 node((NODE *)NULL, Node_rule_node, $2),
  280.                 Node_rule_list, (NODE *)NULL));
  281.         else
  282.             end_block = node(node((NODE *)NULL, Node_rule_node, $2),
  283.                 Node_rule_list, (NODE *)NULL);
  284.         $$ = NULL;
  285.         yyerrok;
  286.       }
  287.     | LEX_BEGIN statement_term
  288.       {
  289.         msg ("error near line %d: BEGIN blocks must have an action part", lineno);
  290.         errcount++;
  291.         yyerrok;
  292.       }
  293.     | LEX_END statement_term
  294.       {
  295.         msg ("error near line %d: END blocks must have an action part", lineno);
  296.         errcount++;
  297.         yyerrok;
  298.       }
  299.     | pattern action
  300.         { $$ = node ($1, Node_rule_node, $2); yyerrok; }
  301.     | pattern statement_term
  302.         { if($1) $$ = node ($1, Node_rule_node, (NODE *)NULL); yyerrok; }
  303.     | function_prologue function_body
  304.         {
  305.             /*obstack_free(&var_stack, func_def);*/
  306.             func_install($1, $2);
  307.             $$ = NULL;
  308.             yyerrok;
  309.         }
  310.     ;
  311.         
  312. function_prologue
  313.     : LEX_FUNCTION 
  314.         {
  315.             func_def = (char *) obstack_alloc(&var_stack, 0);
  316.             param_counter = 0;
  317.         }
  318.       NAME whitespace '(' opt_param_list r_paren whitespace
  319.         {
  320.             $$ = append_right(make_param($3), $6);
  321.         }
  322.     ;
  323.  
  324. function_body
  325.     : l_brace statements r_brace statement_term
  326.         { $$ = $2; }
  327.     ;
  328.  
  329. pattern
  330.     : /* empty */
  331.         { $$ = NULL; }
  332.     | sub_exp
  333.         { $$ = $1; }
  334.     | regexp
  335.         { 
  336.           $$ = node(
  337.                node(make_number((AWKNUM)0),Node_field_spec,(NODE*)NULL),
  338.                Node_match, $1);
  339.         }
  340.     | pattern LEX_AND pattern
  341.         { $$ = node ($1, Node_and, $3); }
  342.     | pattern LEX_OR pattern
  343.         { $$ = node ($1, Node_or, $3); }
  344.     | '!' pattern %prec UNARY
  345.         { $$ = node ($2, Node_not,(NODE *) NULL); }
  346.     | '(' pattern r_paren
  347.         { $$ = $2; }
  348.     | pattern ',' pattern
  349.         { $$ = mkrangenode ( node($1, Node_cond_pair, $3) ); }
  350.     ;
  351.  
  352. regexp
  353.     /* In this rule, want_regexp tells yylex that the next thing
  354.         is a regexp so it should read up to the closing slash. */
  355.     : '/'
  356.         { ++want_regexp; }
  357.        REGEXP '/'
  358.         { want_regexp = 0;
  359.           rp = mk_re_parse($3);
  360.           $$ = node((NODE *)NULL, Node_regex, (NODE *)rp);
  361.         }
  362.     ;
  363.  
  364. action
  365.     : l_brace r_brace 
  366.         {
  367.             /* empty actions are different from missing actions */
  368.             $$ = node ((NODE *) NULL, Node_illegal, (NODE *) NULL);
  369.         }
  370.     | l_brace statements r_brace
  371.         { $$ = $2 ; }
  372.     ;
  373.  
  374. statements
  375.     : statement
  376.         { $$ = node ($1, Node_statement_list, (NODE *)NULL); }
  377.     | statements statement
  378.         {
  379.                 $$ = append_right($1,
  380.                 node( $2, Node_statement_list, (NODE *)NULL));
  381.                 yyerrok;
  382.         }
  383.     | error
  384.         { $$ = NULL; }
  385.     | statements error
  386.     ;
  387.  
  388. statement_term
  389.     : NEWLINE opt_newlines
  390.         { $<nodetypeval>$ = Node_illegal; want_redirect = 0; }
  391.     | semi_colon opt_newlines
  392.         { $<nodetypeval>$ = Node_illegal; want_redirect = 0; }
  393.     ;
  394.  
  395. whitespace
  396.     : /* blank */
  397.         { $<nodetypeval>$ = Node_illegal; }
  398.     | CONCAT_OP
  399.         { $<nodetypeval>$ = Node_illegal; }
  400.     | NEWLINE
  401.         { $<nodetypeval>$ = Node_illegal; }
  402.     | whitespace CONCAT_OP
  403.         { $<nodetypeval>$ = Node_illegal; }
  404.     | whitespace NEWLINE
  405.         { $<nodetypeval>$ = Node_illegal; }
  406.     ;
  407.     
  408. statement
  409.     : semi_colon opt_newlines
  410.         { $$ = NULL; }
  411.     | l_brace statements r_brace whitespace
  412.         { $$ = $2; }
  413.     | if_statement
  414.         { $$ = $1; }
  415.     | LEX_WHILE '(' exp r_paren whitespace statement
  416.         { $$ = node ($3, Node_K_while, $6); }
  417.     | LEX_DO whitespace statement LEX_WHILE '(' exp r_paren whitespace
  418.         { $$ = node ($6, Node_K_do, $3); }
  419.     | LEX_FOR '(' opt_exp semi_colon exp semi_colon opt_exp r_paren whitespace statement
  420.         { $$ = node ($10, Node_K_for, (NODE *)make_for_loop ($3, $5, $7)); }
  421.     | LEX_FOR '(' opt_exp semi_colon semi_colon opt_exp r_paren whitespace statement
  422.         { $$ = node ($9, Node_K_for, (NODE *)make_for_loop ($3, (NODE *)NULL, $6)); }
  423.     | LEX_FOR '(' NAME CONCAT_OP LEX_IN NAME r_paren whitespace statement
  424.         {
  425.             $$ = node ($9, Node_K_arrayfor,
  426.                 make_for_loop(variable($3),
  427.                     (NODE *)NULL, variable($6)));
  428.         }
  429.     | LEX_BREAK statement_term
  430.        /* for break, maybe we'll have to remember where to break to */
  431.         { $$ = node ((NODE *)NULL, Node_K_break, (NODE *)NULL); }
  432.     | LEX_CONTINUE statement_term
  433.        /* similarly */
  434.         { $$ = node ((NODE *)NULL, Node_K_continue, (NODE *)NULL); }
  435.     | LEX_PRINT
  436.         { ++want_redirect; }
  437.       opt_expression_list redirection statement_term
  438.         { $$ = node ($3, Node_K_print, $4); }
  439.     | LEX_PRINT '(' opt_expression_list r_paren 
  440.         { ++want_redirect; want_concat_token = 0; }
  441.       redirection statement_term
  442.         { $$ = node ($3, Node_K_print, $6); }
  443.     | LEX_PRINTF
  444.         { ++want_redirect; }
  445.       opt_expression_list redirection statement_term
  446.         { $$ = node ($3, Node_K_printf, $4); }
  447.     | LEX_PRINTF '(' opt_expression_list r_paren
  448.         { ++want_redirect; want_concat_token = 0; }
  449.       redirection statement_term
  450.         { $$ = node ($3, Node_K_printf, $6); }
  451.     | LEX_NEXT statement_term
  452.         { $$ = node ((NODE *)NULL, Node_K_next, (NODE *)NULL); }
  453.     | LEX_EXIT opt_exp statement_term
  454.         { $$ = node ($2, Node_K_exit, (NODE *)NULL); }
  455.     | LEX_RETURN opt_exp statement_term
  456.         { $$ = node ($2, Node_K_return, (NODE *)NULL); }
  457.     | LEX_DELETE NAME '[' expression_list ']' statement_term
  458.         { $$ = node (variable($2), Node_K_delete, $4); }
  459.     | exp statement_term
  460.         { $$ = $1; }
  461.     ;
  462.  
  463. if_statement
  464.     : LEX_IF '(' exp r_paren whitespace statement
  465.         { $$ = node ($3, Node_K_if,
  466.                 node ($6, Node_if_branches, (NODE *)NULL)); }
  467.     | LEX_IF '(' exp r_paren whitespace statement
  468.          LEX_ELSE whitespace statement
  469.         { $$ = node ($3, Node_K_if,
  470.                 node ($6, Node_if_branches, $9)); }
  471.     ;
  472.  
  473. opt_newlines
  474.     : /* empty */
  475.     | opt_newlines NEWLINE
  476.         { $<nodetypeval>$ = Node_illegal; }
  477.     ;
  478.  
  479. redirection
  480.     : /* empty */
  481.         { want_redirect = 0; $$ = NULL; }
  482.     | REDIRECT_OP 
  483.         { want_redirect = 0; }
  484.         exp
  485.         { $$ = node ($3, $1, (NODE *)NULL); }
  486.     ;
  487.  
  488. opt_param_list
  489.     : /* empty */
  490.         { $$ = NULL; }
  491.     | param_list
  492.         /* $$ = $1 */
  493.     ;
  494.  
  495. param_list
  496.     : NAME
  497.         {
  498.             $$ = make_param($1);
  499.         }
  500.     | param_list ',' NAME
  501.         {
  502.             $$ = append_right($1, make_param($3));
  503.             yyerrok;
  504.         }
  505.     | error
  506.         { $$ = NULL; }
  507.     | param_list error
  508.     | param_list ',' error
  509.     ;
  510.  
  511. /* optional expression, as in for loop */
  512. opt_exp
  513.     : /* empty */
  514.         { $$ = NULL; /* node(NULL, Node_builtin, NULL); */ }
  515.     | exp
  516.     ;
  517.  
  518. opt_expression_list
  519.     : /* empty */
  520.         { $$ = NULL; }
  521.     | expression_list
  522.         { $$ = $1; }
  523.     ;
  524.  
  525. expression_list
  526.     : exp
  527.         { $$ = node ($1, Node_expression_list, (NODE *)NULL); }
  528.     | expression_list ',' exp
  529.         {
  530.             $$ = append_right($1,
  531.                 node( $3, Node_expression_list, (NODE *)NULL));
  532.             yyerrok;
  533.         }
  534.     | error
  535.         { $$ = NULL; }
  536.     | expression_list error
  537.     | expression_list error exp
  538.     | expression_list ',' error
  539.     ;
  540.  
  541. /* Expressions, not including the comma operator.  */
  542. exp    : sub_exp
  543.     | exp LEX_AND whitespace exp
  544.         { $$ = node ($1, Node_and, $4); }
  545.     | exp LEX_OR whitespace exp
  546.         { $$ = node ($1, Node_or, $4); }
  547.     | '!' exp %prec UNARY
  548.         { $$ = node ($2, Node_not,(NODE *) NULL); }
  549.     | '(' exp r_paren
  550.         { $$ = $2; }
  551.     ;
  552.  
  553. sub_exp    : LEX_BUILTIN '(' opt_expression_list r_paren
  554.         { $$ = snode ($3, Node_builtin, $1); }
  555.     | LEX_BUILTIN
  556.         { $$ = snode ((NODE *)NULL, Node_builtin, $1); }
  557.     | exp MATCHOP regexp
  558.          { $$ = node ($1, $2, $3); }
  559.     | exp MATCHOP exp
  560.          { $$ = node ($1, $2, $3); }
  561.     | exp CONCAT_OP LEX_IN NAME
  562.         { $$ = node (variable($4), Node_in_array, $1); }
  563.     | '(' expression_list r_paren CONCAT_OP LEX_IN NAME
  564.         { $$ = node (variable($6), Node_in_array, $2); }
  565.     | LEX_SUB '(' regexp ',' expression_list r_paren 
  566.         { $$ = node($5, $1, $3); }
  567.     | LEX_SUB '(' exp ',' expression_list r_paren 
  568.         { $$ = node($5, $1, $3); }
  569.     | LEX_MATCH '(' exp ',' regexp r_paren
  570.         { $$ = node($3, $1, $5); }
  571.     | LEX_MATCH '(' exp ',' exp r_paren
  572.         { $$ = node($3, $1, $5); }
  573.     | LEX_GETLINE
  574.         {++want_redirect; }
  575.         opt_variable redirection
  576.         {
  577.           $$ = node ($3, Node_K_getline, $4);
  578.         }
  579.     | exp '|' LEX_GETLINE opt_variable
  580.         {
  581.           $$ = node ($4, Node_K_getline,
  582.              node ($1, Node_redirect_pipein, (NODE *)NULL));
  583.         }
  584.     | exp RELOP exp
  585.         { $$ = node ($1, $2, $3); }
  586.     | exp '?' exp ':' exp
  587.         { $$ = node($1, Node_cond_exp, node($3, Node_if_branches, $5)); }
  588.     | NAME '(' opt_expression_list r_paren
  589.         {
  590.             $$ = node ($3, Node_func_call, make_string($1, strlen($1)));
  591.         }
  592.     | '-' exp    %prec UNARY
  593.         { $$ = node ($2, Node_unary_minus, (NODE *)NULL); }
  594.     | '+' exp    %prec UNARY
  595.         { $$ = $2; }
  596.     | INCREMENT variable
  597.         { $$ = node ($2, Node_preincrement, (NODE *)NULL); }
  598.     | DECREMENT variable
  599.         { $$ = node ($2, Node_predecrement, (NODE *)NULL); }
  600.     | variable INCREMENT
  601.         { $$ = node ($1, Node_postincrement, (NODE *)NULL); }
  602.     | variable DECREMENT
  603.         { $$ = node ($1, Node_postdecrement, (NODE *)NULL); }
  604.     | variable
  605.         { $$ = $1; }
  606.     | NUMBER
  607.         { $$ = make_number ($1); }
  608.     | YSTRING
  609.         { $$ = make_string ($1, -1); }
  610.  
  611. /* Binary operators in order of decreasing precedence.  */
  612.     | exp '^' exp
  613.         { $$ = node ($1, Node_exp, $3); }
  614.     | exp '*' exp
  615.         { $$ = node ($1, Node_times, $3); }
  616.     | exp '/' exp
  617.         { $$ = node ($1, Node_quotient, $3); }
  618.     | exp '%' exp
  619.         { $$ = node ($1, Node_mod, $3); }
  620.     | exp '+' exp
  621.         { $$ = node ($1, Node_plus, $3); }
  622.     | exp '-' exp
  623.         { $$ = node ($1, Node_minus, $3); }
  624.         /* Empty operator.  See yylex for disgusting details. */
  625.     | exp CONCAT_OP exp
  626.         { $$ = node ($1, Node_concat, $3); }
  627.     | variable ASSIGNOP exp
  628.         { $$ = node ($1, $2, $3); }
  629.     ;
  630.  
  631. opt_variable
  632.     : /* empty */
  633.         { $$ = NULL; }
  634.     | variable
  635.     ;
  636.  
  637. variable
  638.     : NAME
  639.         { $$ = variable ($1); }
  640.     | NAME '[' expression_list ']'
  641.         { $$ = node (variable($1), Node_subscript, $3); }
  642.     | '$' exp
  643.         { $$ = node ($2, Node_field_spec, (NODE *)NULL); }
  644.     ;
  645.  
  646. l_brace
  647.     : '{' whitespace
  648.     ;
  649.  
  650. r_brace
  651.     : '}'    { yyerrok; }
  652.     ;
  653.  
  654. r_paren
  655.     : ')'    { $<nodetypeval>$ = Node_illegal; yyerrok; }
  656.     ;
  657.  
  658. semi_colon
  659.     : ';'    { yyerrok; }
  660.     ;
  661.  
  662. %%
  663.  
  664. struct token {
  665.     char *operator;
  666.     NODETYPE value;
  667.     int class;
  668.     NODE *(*ptr) ();
  669. };
  670.  
  671. #define NULL 0
  672.  
  673. NODE    *do_exp(),    *do_getline(),    *do_index(),    *do_length(),
  674.     *do_sqrt(),    *do_log(),    *do_sprintf(),    *do_substr(),
  675.     *do_split(),    *do_system(),    *do_int(),    *do_close(),
  676.     *do_atan2(),    *do_sin(),    *do_cos(),    *do_rand(),
  677.     *do_srand(),    *do_match();
  678.  
  679. /* Special functions for debugging */
  680. #ifdef DEBUG
  681. NODE *do_prvars(), *do_bp();
  682. #endif
  683.  
  684. /* Tokentab is sorted ascii ascending order, so it can be binary searched. */
  685.  
  686. static struct token tokentab[] = {
  687.     { "BEGIN",    Node_illegal,        LEX_BEGIN,    0 },
  688.     { "END",    Node_illegal,        LEX_END,    0 },
  689.     { "atan2",    Node_builtin,        LEX_BUILTIN,    do_atan2 },
  690. #ifdef DEBUG
  691.     { "bp",        Node_builtin,        LEX_BUILTIN,    do_bp },
  692. #endif
  693.     { "break",    Node_K_break,        LEX_BREAK,    0 },
  694.     { "close",    Node_builtin,        LEX_BUILTIN,    do_close },
  695.     { "continue",    Node_K_continue,    LEX_CONTINUE,    0 },
  696.     { "cos",    Node_builtin,        LEX_BUILTIN,    do_cos },
  697.     { "delete",    Node_K_delete,        LEX_DELETE,    0 },
  698.     { "do",        Node_K_do,        LEX_DO,        0 },
  699.     { "else",    Node_illegal,        LEX_ELSE,    0 },
  700.     { "exit",    Node_K_exit,        LEX_EXIT,    0 },
  701.     { "exp",    Node_builtin,        LEX_BUILTIN,    do_exp },
  702.     { "for",    Node_K_for,        LEX_FOR,    0 },
  703.     { "func",    Node_K_function,    LEX_FUNCTION,    0 },
  704.     { "function",    Node_K_function,    LEX_FUNCTION,    0 },
  705.     { "getline",    Node_K_getline,        LEX_GETLINE,    0 },
  706.     { "gsub",    Node_gsub,        LEX_SUB,    0 },
  707.     { "if",        Node_K_if,        LEX_IF,        0 },
  708.     { "in",        Node_illegal,        LEX_IN,        0 },
  709.     { "index",    Node_builtin,        LEX_BUILTIN,    do_index },
  710.     { "int",    Node_builtin,        LEX_BUILTIN,    do_int },
  711.     { "length",    Node_builtin,        LEX_BUILTIN,    do_length },
  712.     { "log",    Node_builtin,        LEX_BUILTIN,    do_log },
  713.     { "match",    Node_K_match,        LEX_MATCH,    0 },
  714.     { "next",    Node_K_next,        LEX_NEXT,    0 },
  715.     { "print",    Node_K_print,        LEX_PRINT,    0 },
  716.     { "printf",    Node_K_printf,        LEX_PRINTF,    0 },
  717. #ifdef DEBUG
  718.     { "prvars",    Node_builtin,        LEX_BUILTIN,    do_prvars },
  719. #endif
  720.     { "rand",    Node_builtin,        LEX_BUILTIN,    do_rand },
  721.     { "return",    Node_K_return,        LEX_RETURN,    0 },
  722.     { "sin",    Node_builtin,        LEX_BUILTIN,    do_sin },
  723.     { "split",    Node_builtin,        LEX_BUILTIN,    do_split },
  724.     { "sprintf",    Node_builtin,        LEX_BUILTIN,    do_sprintf },
  725.     { "sqrt",    Node_builtin,        LEX_BUILTIN,    do_sqrt },
  726.     { "srand",    Node_builtin,        LEX_BUILTIN,    do_srand },
  727.     { "sub",    Node_sub,        LEX_SUB,    0 },
  728.     { "substr",    Node_builtin,        LEX_BUILTIN,    do_substr },
  729.     { "system",    Node_builtin,        LEX_BUILTIN,    do_system },
  730.     { "while",    Node_K_while,        LEX_WHILE,    0 },
  731. };
  732.  
  733. /* VARARGS0 */
  734. yyerror(va_alist)
  735. va_dcl
  736. {
  737.     va_list args;
  738.     char *mesg;
  739.     char *a1;
  740.     register char *ptr, *beg;
  741.     static int list = 0;
  742.     char *scan;
  743. #ifdef OSK
  744.     char oskbuf[50];
  745. #endif
  746.  
  747.     errcount++;
  748.     va_start(args);
  749.     mesg = va_arg(args, char *);
  750.     if (mesg || !list) {
  751.         /* Find the current line in the input file */
  752.         if (!lexptr) {
  753.             beg = "(END OF FILE)";
  754.             ptr = beg + 13;
  755.         } else {
  756.             if (*lexptr == '\n' && lexptr != lexptr_begin)
  757.                 --lexptr;
  758.             for (beg = lexptr; beg != lexptr_begin && *beg != '\n'; --beg)
  759.                 ;
  760.             /* NL isn't guaranteed */
  761.             for (ptr = lexptr; *ptr && *ptr != '\n'; ptr++)
  762.                 ;
  763.             if (beg != lexptr_begin)
  764.                 beg++;
  765.         }
  766. #ifdef OSK
  767.         sprintf(oskbuf,"syntax error near line %%d:\n%%.%ds", ptr - beg);
  768.         msg(oskbuf, lineno, beg);
  769. #else
  770.         msg("syntax error near line %d:\n%.*s", lineno, ptr - beg, beg);
  771. #endif
  772.         scan = beg;
  773.         while (scan <= lexptr)
  774.             if (*scan++ == '\t')
  775.                 putc('\t', stderr);
  776.             else
  777.                 putc(' ', stderr);
  778.         putc('^', stderr);
  779.         putc(' ', stderr);
  780.         if (mesg) {
  781.             vfprintf(stderr, mesg, args);
  782.             va_end(args);
  783.                 putc('\n', stderr);
  784.             exit(1);
  785.         } else {
  786.             a1 = va_arg(args, char *);
  787.             if (a1) {
  788.                 fputs("expecting: ", stderr);
  789.                 fputs(a1, stderr);
  790.                 list = 1;
  791.                 va_end(args);
  792.                 return;
  793.             }
  794.         }
  795.         va_end(args);
  796.         return;
  797.     }
  798.     a1 = va_arg(args, char *);
  799.     if (a1) {
  800.         fputs(" or ", stderr);
  801.         fputs(a1, stderr);
  802.         va_end(args);
  803.         putc('\n', stderr);
  804.         return;
  805.     }
  806.     putc('\n', stderr);
  807.     list = 0;
  808.     va_end(args);
  809. }
  810.  
  811. /*
  812.  * Parse a C escape sequence.  STRING_PTR points to a variable containing a
  813.  * pointer to the string to parse.  That pointer is updated past the
  814.  * characters we use.  The value of the escape sequence is returned. 
  815.  *
  816.  * A negative value means the sequence \ newline was seen, which is supposed to
  817.  * be equivalent to nothing at all. 
  818.  *
  819.  * If \ is followed by a null character, we return a negative value and leave
  820.  * the string pointer pointing at the null character. 
  821.  *
  822.  * If \ is followed by 000, we return 0 and leave the string pointer after the
  823.  * zeros.  A value of 0 does not mean end of string.  
  824.  */
  825.  
  826. static int
  827. parse_escape(string_ptr)
  828. char **string_ptr;
  829. {
  830.     register int c = *(*string_ptr)++;
  831.  
  832.     switch (c) {
  833.     case 'b':
  834.         return '\b';
  835.     case 'f':
  836.         return '\f';
  837.     case 'n':
  838.         return '\n';
  839.     case 'r':
  840.         return '\r';
  841.     case 't':
  842.         return '\t';
  843.     case 'v':
  844.         return '\v';
  845.     case '\n':
  846.         return -2;
  847.     case 0:
  848.         (*string_ptr)--;
  849.         return 0;
  850.     case '0':
  851.     case '1':
  852.     case '2':
  853.     case '3':
  854.     case '4':
  855.     case '5':
  856.     case '6':
  857.     case '7':
  858.         {
  859.             register int i = c - '0';
  860.             register int count = 0;
  861.  
  862.             while (++count < 3) {
  863.                 if ((c = *(*string_ptr)++) >= '0' && c <= '7') {
  864.                     i *= 8;
  865.                     i += c - '0';
  866.                 } else {
  867.                     (*string_ptr)--;
  868.                     break;
  869.                 }
  870.             }
  871.             return i;
  872.         }
  873.     default:
  874.         return c;
  875.     }
  876. }
  877.  
  878. /*
  879.  * Read the input and turn it into tokens. Input is now read from a file
  880.  * instead of from malloc'ed memory. The main program takes a program
  881.  * passed as a command line argument and writes it to a temp file. Otherwise
  882.  * the file name is made available in an external variable.
  883.  */
  884.  
  885. int curinfile = -1;
  886.  
  887. static int
  888. yylex()
  889. {
  890.     register int c;
  891.     register int namelen;
  892.     register char *tokstart;
  893.     register struct token *tokptr;
  894.     char *tokkey;
  895.     extern double atof();    /* know what happens if you forget this? */
  896.     static did_newline = 0;    /* the grammar insists that actions end
  897.                  * with newlines.  This was easier than
  898.                  * hacking the grammar. */
  899.     int do_concat;
  900.     int seen_e = 0;        /* These are for numbers */
  901.     int seen_point = 0;
  902.     extern char **sourcefile;
  903.     extern int tempsource, numfiles;
  904.     extern FILE *pathopen();
  905.     static int file_opened = 0;
  906.     static FILE *fin;
  907.     static char cbuf[BUFSIZ];
  908.     int low, mid, high;
  909.     extern int debugging;
  910.  
  911.     if (! file_opened) {
  912.         file_opened = 1;
  913. #ifdef DEBUG
  914.         if (debugging) {
  915.             int i;
  916.  
  917.             for (i = 0; i <= numfiles; i++)
  918.                 fprintf (stderr, "sourcefile[%d] = %s\n", i,
  919.                         sourcefile[i]);
  920.         }
  921. #endif
  922.     nextfile:
  923.         if ((fin = pathopen (sourcefile[++curinfile])) == NULL)
  924.             fatal("cannot open `%s' for reading (%s)",
  925.                 sourcefile[curinfile],
  926.                 sys_errlist[errno]);
  927.         *(lexptr = cbuf) = '\0';
  928.         /*
  929.          * immediately unlink the tempfile so that it will
  930.          * go away cleanly if we bomb.
  931.          */
  932.         if (tempsource && curinfile == 0)
  933.             (void) unlink (sourcefile[curinfile]);
  934.     }
  935.  
  936. retry:
  937.     if (! *lexptr)
  938.         if (fgets (cbuf, sizeof cbuf, fin) == NULL) {
  939.             if (fin != NULL)
  940.                 fclose (fin);    /* be neat and clean */
  941.             if (curinfile < numfiles)
  942.                 goto nextfile;
  943.             return 0;
  944.         } else
  945.             lexptr = lexptr_begin = cbuf;
  946.  
  947.     if (want_regexp) {
  948.         want_regexp = 0;
  949.  
  950.         /*
  951.          * there is a potential bug if a regexp is followed by an
  952.          * equal sign: "/foo/=bar" would result in assign_quotient
  953.          * being returned as the next token.  Nothing is done about
  954.          * it since it is not valid awk, but maybe something should
  955.          * be done anyway. 
  956.          */
  957.  
  958.         tokstart = lexptr;
  959.         while (c = *lexptr++) {
  960.             switch (c) {
  961.             case '\\':
  962.                 if (*lexptr++ == '\0') {
  963.                     yyerror("unterminated regexp ends with \\");
  964.                     return ERROR;
  965.                 } else if (lexptr[-1] == '\n')
  966.                     goto retry;
  967.                 break;
  968.             case '/':    /* end of the regexp */
  969.                 lexptr--;
  970.                 yylval.sval = tokstart;
  971.                 return REGEXP;
  972.             case '\n':
  973.                 lineno++;
  974.             case '\0':
  975.                 yyerror("unterminated regexp");
  976.                 return ERROR;
  977.             }
  978.         }
  979.     }
  980.     do_concat = want_concat_token;
  981.     want_concat_token = 0;
  982.  
  983.     if (*lexptr == '\n') {
  984.         lexptr++;
  985.         lineno++;
  986.         return NEWLINE;
  987.     }
  988.  
  989.     /*
  990.      * if lexptr is at white space between two terminal tokens or parens,
  991.      * it is a concatenation operator. 
  992.      */
  993.     if (do_concat && (*lexptr == ' ' || *lexptr == '\t')) {
  994.         while (*lexptr == ' ' || *lexptr == '\t')
  995.             lexptr++;
  996.         if (isalnum(*lexptr) || *lexptr == '_' || *lexptr == '\"' ||
  997.             *lexptr == '(' || *lexptr == '.' || *lexptr == '$' ||
  998.             (*lexptr == '+' && *(lexptr+1) == '+') ||
  999.             (*lexptr == '-' && *(lexptr+1) == '-'))
  1000.                     /* the '.' is for decimal pt */
  1001.             return CONCAT_OP;
  1002.     }
  1003.     while (*lexptr == ' ' || *lexptr == '\t')
  1004.         lexptr++;
  1005.  
  1006.     tokstart = lexptr;
  1007.  
  1008.     switch (c = *lexptr++) {
  1009.     case 0:
  1010.         return 0;
  1011.  
  1012.     case '\n':
  1013.         lineno++;
  1014.         return NEWLINE;
  1015.  
  1016.     case '#':        /* it's a comment */
  1017.         while (*lexptr != '\n' && *lexptr != '\0')
  1018.             lexptr++;
  1019.         goto retry;
  1020.  
  1021.     case '\\':
  1022.         if (*lexptr == '\n') {
  1023.             lineno++;
  1024.             lexptr++;
  1025.             want_concat_token = do_concat;
  1026.             goto retry;
  1027.         } else
  1028.             break;
  1029.     case ')':
  1030.     case ']':
  1031.         ++want_concat_token;
  1032.         /* fall through */
  1033.     case '(':    
  1034.     case '[':
  1035.     case '$':
  1036.     case ';':
  1037.     case ':':
  1038.     case '?':
  1039.  
  1040.         /*
  1041.          * set node type to ILLEGAL because the action should set it
  1042.          * to the right thing 
  1043.          */
  1044.         yylval.nodetypeval = Node_illegal;
  1045.         return c;
  1046.  
  1047.     case '{':
  1048.     case ',':
  1049.         while (isspace(*lexptr)) {
  1050.             if (*lexptr == '\n')
  1051.                 lineno++;
  1052.             lexptr++;
  1053.         }
  1054.         yylval.nodetypeval = Node_illegal;
  1055.         return c;
  1056.  
  1057.     case '*':
  1058.         if (*lexptr == '=') {
  1059.             yylval.nodetypeval = Node_assign_times;
  1060.             lexptr++;
  1061.             return ASSIGNOP;
  1062.         } else if (*lexptr == '*') {    /* make ** and **= aliases
  1063.                          * for ^ and ^= */
  1064.             if (lexptr[1] == '=') {
  1065.                 yylval.nodetypeval = Node_assign_exp;
  1066.                 lexptr += 2;
  1067.                 return ASSIGNOP;
  1068.             } else {
  1069.                 yylval.nodetypeval = Node_illegal;
  1070.                 lexptr++;
  1071.                 return '^';
  1072.             }
  1073.         }
  1074.         yylval.nodetypeval = Node_illegal;
  1075.         return c;
  1076.  
  1077.     case '/':
  1078.         if (*lexptr == '=') {
  1079.             yylval.nodetypeval = Node_assign_quotient;
  1080.             lexptr++;
  1081.             return ASSIGNOP;
  1082.         }
  1083.         yylval.nodetypeval = Node_illegal;
  1084.         return c;
  1085.  
  1086.     case '%':
  1087.         if (*lexptr == '=') {
  1088.             yylval.nodetypeval = Node_assign_mod;
  1089.             lexptr++;
  1090.             return ASSIGNOP;
  1091.         }
  1092.         yylval.nodetypeval = Node_illegal;
  1093.         return c;
  1094.  
  1095.     case '^':
  1096.         if (*lexptr == '=') {
  1097.             yylval.nodetypeval = Node_assign_exp;
  1098.             lexptr++;
  1099.             return ASSIGNOP;
  1100.         }
  1101.         yylval.nodetypeval = Node_illegal;
  1102.         return c;
  1103.  
  1104.     case '+':
  1105.         if (*lexptr == '=') {
  1106.             yylval.nodetypeval = Node_assign_plus;
  1107.             lexptr++;
  1108.             return ASSIGNOP;
  1109.         }
  1110.         if (*lexptr == '+') {
  1111.             yylval.nodetypeval = Node_illegal;
  1112.             lexptr++;
  1113.             return INCREMENT;
  1114.         }
  1115.         yylval.nodetypeval = Node_illegal;
  1116.         return c;
  1117.  
  1118.     case '!':
  1119.         if (*lexptr == '=') {
  1120.             yylval.nodetypeval = Node_notequal;
  1121.             lexptr++;
  1122.             return RELOP;
  1123.         }
  1124.         if (*lexptr == '~') {
  1125.             yylval.nodetypeval = Node_nomatch;
  1126.             if (! strict && lexptr[1] == '~') {
  1127.                 yylval.nodetypeval = Node_case_nomatch;
  1128.                 lexptr++;
  1129.             }
  1130.             lexptr++;
  1131.             return MATCHOP;
  1132.         }
  1133.         yylval.nodetypeval = Node_illegal;
  1134.         return c;
  1135.  
  1136.     case '<':
  1137.         if (want_redirect) {
  1138.             yylval.nodetypeval = Node_redirect_input;
  1139.             return REDIRECT_OP;
  1140.         }
  1141.         if (*lexptr == '=') {
  1142.             yylval.nodetypeval = Node_leq;
  1143.             lexptr++;
  1144.             return RELOP;
  1145.         }
  1146.         yylval.nodetypeval = Node_less;
  1147.         return RELOP;
  1148.  
  1149.     case '=':
  1150.         if (*lexptr == '=') {
  1151.             yylval.nodetypeval = Node_equal;
  1152.             lexptr++;
  1153.             return RELOP;
  1154.         }
  1155.         yylval.nodetypeval = Node_assign;
  1156.         return ASSIGNOP;
  1157.  
  1158.     case '>':
  1159.         if (want_redirect) {
  1160.             if (*lexptr == '>') {
  1161.                 yylval.nodetypeval = Node_redirect_append;
  1162.                 lexptr++;
  1163.             } else
  1164.                 yylval.nodetypeval = Node_redirect_output;
  1165.             return REDIRECT_OP;
  1166.         }
  1167.         if (*lexptr == '=') {
  1168.             yylval.nodetypeval = Node_geq;
  1169.             lexptr++;
  1170.             return RELOP;
  1171.         }
  1172.         yylval.nodetypeval = Node_greater;
  1173.         return RELOP;
  1174.  
  1175.     case '~':
  1176.         yylval.nodetypeval = Node_match;
  1177.         if (! strict && *lexptr == '~') {
  1178.             yylval.nodetypeval = Node_case_match;
  1179.             lexptr++;
  1180.         }
  1181.         return MATCHOP;
  1182.  
  1183.     case '}':
  1184.         /*
  1185.          * Added did newline stuff.  Easier than
  1186.          * hacking the grammar
  1187.          */
  1188.         if (did_newline) {
  1189.             did_newline = 0;
  1190.             return c;
  1191.         }
  1192.         did_newline++;
  1193.         --lexptr;
  1194.         return NEWLINE;
  1195.  
  1196.     case '"':
  1197.         while (*lexptr != '\0') {
  1198.             switch (*lexptr++) {
  1199.             case '\\':
  1200.                 if (*lexptr++ != '\0')
  1201.                     break;
  1202.                 /* fall through */
  1203.             case '\n':
  1204.                 yyerror("unterminated string");
  1205.                 return ERROR;
  1206.             case '\"':
  1207.                 /* Skip the doublequote */
  1208.                 yylval.sval = tokstart + 1;
  1209.                 ++want_concat_token;
  1210.                 return YSTRING;
  1211.             }
  1212.         }
  1213.         return ERROR;
  1214.  
  1215.     case '-':
  1216.         if (*lexptr == '=') {
  1217.             yylval.nodetypeval = Node_assign_minus;
  1218.             lexptr++;
  1219.             return ASSIGNOP;
  1220.         }
  1221.         if (*lexptr == '-') {
  1222.             yylval.nodetypeval = Node_illegal;
  1223.             lexptr++;
  1224.             return DECREMENT;
  1225.         }
  1226.  
  1227.         /*
  1228.          * It looks like space tab comma and newline are the legal
  1229.          * places for a UMINUS.  Have we missed any? 
  1230.          */
  1231.         if ((! isdigit(*lexptr) && *lexptr != '.') ||
  1232.             (lexptr > lexptr_begin + 1 &&
  1233.                     ! index(" \t,\n", lexptr[-2]))) {
  1234.  
  1235.             /*
  1236.              * set node type to ILLEGAL because the action should
  1237.              * set it to the right thing 
  1238.              */
  1239.             yylval.nodetypeval = Node_illegal;
  1240.             return c;
  1241.         }
  1242.         /* FALL through into number code */
  1243.     case '0':
  1244.     case '1':
  1245.     case '2':
  1246.     case '3':
  1247.     case '4':
  1248.     case '5':
  1249.     case '6':
  1250.     case '7':
  1251.     case '8':
  1252.     case '9':
  1253.     case '.':
  1254.         /* It's a number */
  1255.         if (c == '-')
  1256.             namelen = 1;
  1257.         else
  1258.             namelen = 0;
  1259.         for (; (c = tokstart[namelen]) != '\0'; namelen++) {
  1260.             switch (c) {
  1261.             case '.':
  1262.                 if (seen_point)
  1263.                     goto got_number;
  1264.                 ++seen_point;
  1265.                 break;
  1266.             case 'e':
  1267.             case 'E':
  1268.                 if (seen_e)
  1269.                     goto got_number;
  1270.                 ++seen_e;
  1271.                 if (tokstart[namelen + 1] == '-' || tokstart[namelen + 1] == '+')
  1272.                     namelen++;
  1273.                 break;
  1274.             case '0':
  1275.             case '1':
  1276.             case '2':
  1277.             case '3':
  1278.             case '4':
  1279.             case '5':
  1280.             case '6':
  1281.             case '7':
  1282.             case '8':
  1283.             case '9':
  1284.                 break;
  1285.             default:
  1286.                 goto got_number;
  1287.             }
  1288.         }
  1289.  
  1290. got_number:
  1291.         lexptr = tokstart + namelen;
  1292.         yylval.fval = atof(tokstart);
  1293.         ++want_concat_token;
  1294.         return NUMBER;
  1295.  
  1296.     case '&':
  1297.         if (*lexptr == '&') {
  1298.             yylval.nodetypeval = Node_and;
  1299.             lexptr++;
  1300.             return LEX_AND;
  1301.         }
  1302.         return ERROR;
  1303.  
  1304.     case '|':
  1305.         if (*lexptr == '|') {
  1306.             yylval.nodetypeval = Node_or;
  1307.             lexptr++;
  1308.             return LEX_OR;
  1309.         } else if (want_redirect) {
  1310.             yylval.nodetypeval = Node_redirect_pipe;
  1311.             return REDIRECT_OP;
  1312.         } else {
  1313.             yylval.nodetypeval = Node_illegal;
  1314.             return c;
  1315.         }
  1316.         break;
  1317.     }
  1318.  
  1319.     if (c != '_' && !isalpha(c)) {
  1320.         yyerror("Invalid char '%c' in expression\n", c);
  1321.         return ERROR;
  1322.     }
  1323.  
  1324.     /* it's some type of name-type-thing.  Find its length */
  1325.     for (namelen = 0; is_identchar(tokstart[namelen]); namelen++)
  1326.         /* null */ ;
  1327.     emalloc(tokkey, char *, namelen+1, "yylex");
  1328.     strncpy (tokkey, tokstart, namelen);
  1329.     tokkey[namelen] = '\0';
  1330.  
  1331.     /* See if it is a special token.  */
  1332.     low = 0;
  1333.     high = (sizeof (tokentab) / sizeof (tokentab[0])) - 1;
  1334.     while (low <= high) {
  1335.         int i, c;
  1336.  
  1337.         mid = (low + high) / 2;
  1338.  
  1339.     compare:
  1340.         c = *tokstart - tokentab[mid].operator[0];
  1341.         i = c ? c : strcmp (tokkey, tokentab[mid].operator);
  1342.  
  1343.         if (i < 0) {        /* token < mid */
  1344.             high = mid - 1;
  1345.         } else if (i > 0) {    /* token > mid */
  1346.             low = mid + 1;
  1347.         } else {
  1348.             lexptr = tokstart + namelen;
  1349.             if (tokentab[mid].class == LEX_BUILTIN)
  1350.                 yylval.ptrval = tokentab[mid].ptr;
  1351.             else
  1352.                 yylval.nodetypeval = tokentab[mid].value;
  1353.             if (tokentab[mid].class == LEX_PRINT)
  1354.                 want_redirect++;
  1355.             return tokentab[mid].class;
  1356.         }
  1357.     }
  1358.  
  1359.     /* It's a name.  See how long it is.  */
  1360.     yylval.sval = tokkey;
  1361.     lexptr = tokstart + namelen;
  1362.     ++want_concat_token;
  1363.     return NAME;
  1364. }
  1365.  
  1366. #ifndef DEFPATH
  1367. #ifdef OSK
  1368. #define DEFPATH    ".:/h0/usr/lib/awk:/h0/usr/local/lib/awk"
  1369. #else
  1370. #define DEFPATH    ".:/usr/lib/awk:/usr/local/lib/awk"
  1371. #endif
  1372. #endif
  1373.  
  1374. FILE *
  1375. pathopen (file)
  1376. char *file;
  1377. {
  1378.     static char defpath[] = DEFPATH;
  1379.     static char *savepath;
  1380.     static int first = 1;
  1381.     extern char *getenv ();
  1382.     char *awkpath, *cp;
  1383.     char trypath[BUFSIZ];
  1384.     FILE *fp;
  1385.     extern int debugging;
  1386.  
  1387.     if (strict)
  1388.         return (fopen (file, "r"));
  1389.  
  1390.     if (first) {
  1391.         first = 0;
  1392.         if ((awkpath = getenv ("AWKPATH")) == NULL || ! *awkpath)
  1393.             awkpath = defpath;
  1394.         savepath = awkpath;    /* savepath used for restarting */
  1395.     } else
  1396.         awkpath = savepath;
  1397.  
  1398.     if (index (file, '/') != NULL)    /* some kind of path name, no search */
  1399.         return (fopen (file, "r"));
  1400.  
  1401.     do {
  1402.         for (cp = trypath; *awkpath && *awkpath != ':'; )
  1403.             *cp++ = *awkpath++;
  1404.         *cp++ = '/';
  1405.         *cp = '\0';    /* clear left over junk */
  1406.         strcat (cp, file);
  1407.         if ((fp = fopen (trypath, "r")) != NULL)
  1408.             return (fp);
  1409.  
  1410.         /* no luck, keep going */
  1411.         awkpath++;    /* skip colon */
  1412.     } while (*awkpath);
  1413.     return (NULL);
  1414. }
  1415.