home *** CD-ROM | disk | FTP | other *** search
/ Programmer 7500 / MAX_PROGRAMMERS.iso / CLIPPER / MISC / AWK.ZIP / AWK.Y < prev    next >
Encoding:
Text File  |  1988-09-09  |  33.6 KB  |  1,113 lines

  1. /**
  2.  * $Revision:   1.1  $
  3.  * $Log:   C:/AWK/AWK.Y_V  $
  4.  * 
  5.  *    Rev 1.1   09 Sep 1988 18:34:30   vince
  6.  * MC 5.1 version
  7.  * 
  8.  *    Rev 1.0   09 Sep 1988 18:04:12   vince
  9.  * Original source
  10.  * 
  11.  *
  12.  * gawk -- GNU version of awk
  13.  * Copyright (C) 1986 Free Software Foundation
  14.  *   Written by Paul Rubin, August 1986
  15.  *
  16.  *   Modified by Andrew D. Estes, July 1988
  17.  */
  18.  
  19. /**
  20.  * GAWK is distributed in the hope that it will be useful, but WITHOUT ANY
  21.  * WARRANTY.  No author or distributor accepts responsibility to anyone
  22.  * for the consequences of using it or for whether it serves any
  23.  * particular purpose or works at all, unless he says so in writing.
  24.  * Refer to the GAWK General Public License for full details.
  25.  * 
  26.  * Everyone is granted permission to copy, modify and redistribute GAWK,
  27.  * but only under the conditions described in the GAWK General Public
  28.  * License.  A copy of this license is supposed to have been given to you
  29.  * along with GAWK so you can know your rights and responsibilities.  It
  30.  * should be in a file named COPYING.  Among other things, the copyright
  31.  * notice and this notice must be preserved on all copies.
  32.  * 
  33.  * In other words, go ahead and share GAWK, but don't try to stop
  34.  * anyone else from sharing it farther.  Help stamp out software hoarding!
  35.  */
  36.  
  37. %{
  38. #define YYDEBUG 12
  39.  
  40. #include <stdio.h>
  41. #include <string.h>
  42. #include "awk.h"
  43.  
  44. static int yylex ();
  45.   /**
  46.    * The following variable is used for a very sickening thing.
  47.    * The awk language uses white space as the string concatenation
  48.    * operator, but having a white space token that would have to appear
  49.    * everywhere in all the grammar rules would be unbearable.
  50.    * It turns out we can return CONCAT_OP exactly when there really
  51.    * is one, just from knowing what kinds of other tokens it can appear
  52.    * between (namely, constants, variables, or close parentheses).
  53.    * This is because concatenation has the lowest priority of all
  54.    * operators.  want_concat_token is used to remember that something
  55.    * that could be the left side of a concat has just been returned.
  56.    *
  57.    * If anyone knows a cleaner way to do this (don't look at the Un*x
  58.    * code to find one, though), please suggest it.
  59.    */
  60.    static int want_concat_token;
  61.  
  62.    /* Two more horrible kludges.  The same comment applies to these two too */
  63.    static int want_regexp;       /* lexical scanning kludge */
  64.    static int want_redirect; /* similarly */
  65.    int lineno = 1;       /* JF for error msgs */
  66.  
  67.   /**
  68.    * Speaking of kludges.  We don't want to treat arguments as filenames
  69.    * if there are no pattern action pairs to perform; sooo I am creating
  70.    * a counter for patterns and actions. -ADE 
  71.    */
  72.    int patterns = 0;
  73.    int actions = 0;
  74.    /* During parsing of a gawk program, the pointer to the next character is in this variable.  */
  75.    char *lexptr;         /* JF moved it up here */
  76.    char *lexptr_begin;   /* JF for error msgs */
  77. %}
  78.  
  79. %union {
  80.   long lval;
  81.   AWKNUM fval;
  82.   NODE *nodeval;
  83.   NODETYPE nodetypeval;
  84.   char *sval;
  85.   NODE *(*ptrval)();
  86. }
  87.  
  88. %type <nodeval> expr start program rule pattern regex opt_argument_expr_list
  89. %type <nodeval> action redirection argument_expr_list iteration_statement
  90. %type <nodeval> statement if_statement output_statement expression_statement
  91. %type <nodeval> opt_exp compound_statement statement_list concat_expr
  92. %type <nodeval> primary_expr postfix_expr unary_expr arith_expr mult_expr
  93. %type <nodeval> cond_expr assign_expr primary_pattern and_expr or_expr
  94. %type <nodetypeval> whitespace
  95.  
  96. %token <sval> NAME REGEXP YSTRING
  97. %token <lval> ERROR INCDEC
  98. %token <fval> NUMBER
  99. %token <nodetypeval> ASSIGNOP RELOP MATCHOP NEWLINE REDIRECT_OP CONCAT_OP
  100. %token <nodetypeval> LEX_BEGIN LEX_END LEX_IF LEX_ELSE
  101. %token <nodetypeval> LEX_WHILE LEX_FOR LEX_BREAK LEX_CONTINUE
  102. %token <nodetypeval> LEX_GETLINE LEX_PRINT LEX_PRINTF LEX_NEXT LEX_EXIT
  103. %token  LEX_IN
  104. %token <lval> LEX_AND LEX_OR INCREMENT DECREMENT
  105. %token <ptrval> LEX_BUILTIN LEX_SUB
  106.  
  107. /* these are just yylval numbers */
  108. /* %token <lval> CHAR JF this isn't used anymore */
  109.  
  110. /* Lowest to highest */
  111. %left ','
  112. %left LEX_OR
  113. %left LEX_AND
  114. %right ASSIGNOP
  115. %left CONCAT_OP
  116. %left '+' '-'
  117. %left '*' '/'
  118. %left '%'
  119. %left '^'
  120. %right UNARY
  121. %nonassoc MATCHOP RELOP INCREMENT DECREMENT
  122. %%
  123.  
  124. start
  125.         :       optional_newlines program
  126.                         { expression_value = $2; }
  127.         ;
  128.  
  129.  
  130. program
  131.         :       rule
  132.                         { $$ = node ($1, Node_rule_list,(NODE *) NULL); }
  133.         |       program rule
  134.                         /* cons the rule onto the tail of list */
  135.                         { $$ = append_right ($1, node($2, Node_rule_list,(NODE *) NULL)); }
  136.         ;
  137.  
  138. rule
  139.         :       pattern action NEWLINE optional_newlines
  140.                         {
  141.                         ++patterns;
  142.                         ++actions;
  143.                         $$ = node ($1, Node_rule_node, $2);
  144.                         }
  145.         ;
  146.  
  147.  
  148. primary_pattern
  149.         :       /* EMPTY */
  150.                         { $$ = NULL; }
  151.         |       LEX_BEGIN
  152.                         {
  153.                           --patterns;
  154.                           --actions;
  155.                           $$ = node ((NODE *)NULL, Node_K_BEGIN,(NODE *) NULL);
  156.                         }
  157.         |       LEX_END
  158.                         {  $$ = node ((NODE *)NULL, Node_K_END,(NODE *) NULL); }
  159.         |       expr
  160.                         { $$ = $1; }
  161.         ;
  162.  
  163. pattern
  164.         :       primary_pattern
  165.                         { $$ = $1; }
  166.         |       regex
  167.                         { $$ = node (node (make_number((AWKNUM)0), Node_field_spec,
  168.                                         (NODE *)NULL), Node_match, $1); }
  169.         |       '!' primary_pattern  %prec UNARY
  170.                         { $$ = node ($2, Node_not,(NODE *) NULL); }
  171.         |       primary_pattern LEX_AND pattern
  172.                         { $$ = node ($1, Node_and, $3); }
  173.         |       primary_pattern LEX_OR pattern
  174.                         { $$ = node ($1, Node_or, $3); }
  175.         |       '(' pattern ')'
  176.                         {
  177.                           $$ = $2;
  178.                           want_concat_token = 0;
  179.                         }
  180.         |       pattern MATCHOP regex
  181.                         { $$ = node($1, Node_match, $3); }
  182.         |       pattern MATCHOP primary_pattern
  183.                         { $$ = node($1, Node_match, $3); }
  184.         |       pattern ',' pattern
  185.                         { $$ = mkrangenode ( node($1, Node_cond_pair, $3) ); } /*jfw*/
  186.         ;
  187.  
  188.  
  189. action
  190.         :       /* empty */
  191.                         { --actions; $$ = NULL; }
  192.         |       compound_statement
  193.                         { $$ = $1; }
  194.         ;
  195.  
  196.  
  197. statement
  198.         :       compound_statement optional_newlines
  199.                         { $$ = $1; }
  200.         |       expression_statement
  201.                         { $$ = $1; }
  202.         |       if_statement
  203.                         { $$ = $1; }
  204.         |       iteration_statement
  205.                         { $$ = $1; }
  206.         |       output_statement
  207.                         { $$ = $1; }
  208.         ;
  209.  
  210. compound_statement
  211.         :       '{' optional_newlines statement_list '}'
  212.                         { $$ = node ($3, Node_statement_list, (NODE *)NULL); }
  213.         ;
  214.  
  215. statement_list
  216.         :       statement
  217.                         { $$ = node ($1, Node_statement_list, (NODE *)NULL); }
  218.         |       statement_list statement
  219.                         { $$ = append_right($1,
  220.                                 node($2, Node_statement_list, (NODE *)NULL)); }
  221.         ;
  222.  
  223. expression_statement
  224.         :       ';' optional_newlines
  225.                         { $$ = (NODE *)NULL; }
  226.         |       expr statement_term
  227.                         { $$ = node ($1, Node_statement_list, (NODE *)NULL); }
  228.         ;
  229.  
  230.  
  231. if_statement
  232.         :       LEX_IF '(' expr ')' optional_newlines statement
  233.                         { $$ = node ($3, Node_K_if,
  234.                                 node ($6, Node_if_branches, (NODE *)NULL)); }
  235.         |       LEX_IF '(' expr ')' optional_newlines statement
  236.                                 LEX_ELSE optional_newlines statement
  237.                    { $$ = node ($3, Node_K_if,
  238.                                 node ($6, Node_if_branches, $9)); }
  239.         ;
  240.  
  241.  
  242. iteration_statement
  243.         :       LEX_WHILE '(' expr ')'
  244.                         { want_concat_token = 0; }
  245.                         optional_newlines statement
  246.                         { $$ = node ($3, Node_K_while, $7); }
  247.         |       LEX_FOR '(' opt_exp ';' expr ';' opt_exp ')'
  248.                         { want_concat_token = 0; }
  249.                         optional_newlines statement
  250.                         { $$ = node ($11, Node_K_for, (NODE *)make_for_loop ($3, $5, $7)); }
  251.         |       LEX_FOR '(' opt_exp ';' ';' opt_exp ')'
  252.                         { want_concat_token = 0; }
  253.                         optional_newlines statement
  254.                         { $$ = node ($10, Node_K_for,
  255.                                 (NODE *)make_for_loop ($3, (NODE *)NULL, $6)); }
  256.         |       LEX_FOR '(' NAME CONCAT_OP LEX_IN NAME ')'
  257.                         { want_concat_token = 0; }
  258.                   optional_newlines statement
  259.                         { $$ = node ($10, Node_K_arrayfor,
  260.                                 (NODE *)make_for_loop(variable($3), (NODE *)NULL, variable($6))); }
  261.         |       LEX_BREAK statement_term
  262.            /* for break, maybe we'll have to remember where to break to */
  263.                         { $$ = node ((NODE *)NULL, Node_K_break, (NODE *)NULL); }
  264.         |       LEX_CONTINUE statement_term
  265.                         { $$ = node ((NODE *)NULL, Node_K_continue, (NODE *)NULL); }
  266.         |       LEX_NEXT statement_term
  267.                         { $$ = node ((NODE *)NULL, Node_K_next, (NODE *)NULL); }
  268.         |       LEX_EXIT statement_term
  269.                         { $$ = node ((NODE *)NULL, Node_K_exit, (NODE *)NULL); }
  270.         |       LEX_EXIT '(' expr ')' statement_term
  271.                         { $$ = node ($3, Node_K_exit, (NODE *)NULL); }
  272.         ;
  273.  
  274.  
  275. output_statement
  276.         :       LEX_PRINT
  277.                         { ++want_redirect; want_concat_token = 0; }
  278.                 opt_argument_expr_list redirection statement_term
  279.                         {
  280.                           want_redirect = 0;
  281.                           /* $4->lnode = NULL; */
  282.                           $$ = node ($3, Node_K_print, $4);
  283.                         }
  284.         |       LEX_PRINTF
  285.                         { ++want_redirect; want_concat_token = 0}
  286.                 opt_argument_expr_list redirection statement_term
  287.                         {
  288.                           want_redirect = 0;
  289.                           /* $4->lnode = NULL; */
  290.                           $$ = node ($3, Node_K_printf, $4);
  291.                         }
  292.         |       LEX_PRINTF '(' argument_expr_list ')'
  293.                         { ++want_redirect;
  294.                         want_concat_token = 0; }
  295.                 redirection statement_term
  296.                         {
  297.                           want_redirect = 0;
  298.                           $$ = node ($3, Node_K_printf, $6);
  299.                         }
  300.         ;
  301.  
  302.  
  303. optional_newlines
  304.         :       /* EMPTY */
  305.         |       optional_newlines NEWLINE
  306.                         { $<nodetypeval>$ = Node_illegal; }
  307.         ;
  308.  
  309. statement_term
  310.         :       NEWLINE optional_newlines
  311.                         { $<nodetypeval>$ = Node_illegal; }
  312.         |       ';' optional_newlines
  313.                         { $<nodetypeval>$ = Node_illegal; }
  314.         ;
  315.  
  316. regex
  317.         :  '/'
  318.                         { ++want_regexp; }
  319.                 REGEXP '/'
  320.                         {
  321.                                 want_regexp = 0;
  322.                                 $$ = make_regex($3);
  323.                         }
  324.         ;
  325.  
  326. redirection
  327.         :       /* EMPTY */
  328.                         { $$ = NULL; /* node (NULL, Node_redirect_nil, NULL); */ }
  329.         |       REDIRECT_OP expr
  330.                         { $$ = node ($2, $1, (NODE *)NULL); }
  331.         ;
  332.  
  333.  
  334. /* optional expression, as in for loop */
  335. opt_exp
  336.         :       /* EMPTY */
  337.                         { $$ = NULL; /* node(NULL, Node_builtin, NULL); */ }
  338.         |       expr
  339.                         { $$ = $1; }
  340.         ;
  341.  
  342. opt_argument_expr_list
  343.         :       /* EMPTY */
  344.                         { $$ = NULL; }
  345.         |       argument_expr_list
  346.                         { $$ = $1; }
  347.         ;
  348.  
  349. primary_expr
  350.         :       NAME
  351.                         { $$ = variable ($1); }
  352.         |       NUMBER
  353.                         { $$ = make_number($1); }
  354.         |       YSTRING
  355.                         { $$ = make_string ($1, -1); }
  356.         |       LEX_BUILTIN '(' opt_argument_expr_list ')'
  357.                         { ++want_concat_token; $$ = snode ($3, Node_builtin, $1); }
  358.         |       LEX_BUILTIN
  359.                         { ++want_concat_token; $$ = snode ((NODE *)NULL, Node_builtin, $1); }
  360.         |       LEX_SUB '(' regex ',' argument_expr_list ')'
  361.                         { want_concat_token;
  362.                         $$ = snode(node($3, Node_expression_list, $5), Node_builtin, $1); }
  363.         |       LEX_GETLINE
  364.                         { ++want_redirect; }
  365.                 opt_exp redirection
  366.                         {
  367.                                 want_redirect = 0;
  368.                                 $$ = node($3, Node_K_getline, $4);
  369.                         }
  370.         |       '(' expr ')'
  371.                         { $$ = $2; }
  372.         ;
  373.  
  374. postfix_expr :
  375.                 primary_expr
  376.                         { $$ = $1; }
  377.         |       NAME '[' expr ']'
  378.                         { $$ = node (variable($1), Node_subscript, $3); }
  379.         |       postfix_expr INCREMENT
  380.                         { $$ = node ($1, Node_postincrement, (NODE *)NULL); }
  381.         |       postfix_expr DECREMENT
  382.                         { $$ = node ($1, Node_postdecrement, (NODE *)NULL); }
  383.         ;
  384.  
  385. argument_expr_list
  386.         :       assign_expr
  387.                         { $$ = node ($1, Node_expression_list, (NODE *)NULL); }
  388.         |       argument_expr_list ',' optional_newlines assign_expr
  389.                         { $$ = append_right($1,
  390.                                 node ($4, Node_expression_list, (NODE *)NULL)); }
  391.         ;
  392.  
  393. unary_expr
  394.         :       postfix_expr
  395.                         { $$ = $1; }
  396.         |       INCREMENT unary_expr
  397.                         { $$ = node ($2, Node_preincrement, (NODE *)NULL); }
  398.         |       DECREMENT unary_expr
  399.                         { $$ = node ($2, Node_predecrement, (NODE *)NULL); }
  400.         |       '-' unary_expr   %prec UNARY
  401.                         { $$ = node ($2, Node_unary_minus, (NODE *)NULL); }
  402.         |       '$' unary_expr   %prec UNARY
  403.                         { $$ = node ($2, Node_field_spec, (NODE *)NULL); }
  404.         ;
  405.  
  406. mult_expr
  407.         :       unary_expr
  408.                         { $$ = $1; }
  409.         |       unary_expr '^' mult_expr
  410.                         { $$ = node ($1, Node_pow, $3); }
  411.         |       unary_expr '%' mult_expr
  412.                         { $$ = node ($1, Node_mod, $3); }
  413.         |       unary_expr '*' mult_expr
  414.                         { $$ = node ($1, Node_times, $3); }
  415.         |       unary_expr '/' mult_expr
  416.                         { $$ = node ($1, Node_quotient, $3); }
  417.         ;
  418.  
  419. arith_expr
  420.         :       mult_expr
  421.                         { $$ = $1; }
  422.         |       mult_expr '+' arith_expr
  423.                         { $$ = node ($1, Node_plus, $3); }
  424.         |       mult_expr '-' arith_expr
  425.                         { $$ = node ($1, Node_minus, $3); }
  426.         ;
  427.  
  428. concat_expr
  429.         :       arith_expr
  430.                         { $$ = $1; }
  431.         |       arith_expr CONCAT_OP concat_expr
  432.                         { $$ = node($1, Node_concat, $3); }
  433.         |       arith_expr RELOP concat_expr
  434.                         { $$ = node($1, $2, $3); }
  435.         ;
  436.  
  437. and_expr
  438.         :       concat_expr
  439.                         { $$ = $1; }
  440.         |       concat_expr LEX_AND concat_expr
  441.                         { $$ = node ($1, Node_and, $3); }
  442.         ;
  443.  
  444. or_expr
  445.         :       and_expr
  446.                         { $$ = $1; }
  447.         |       and_expr LEX_OR and_expr
  448.                         { $$ = node ($1, Node_or, $3); }
  449.         ;
  450.  
  451. cond_expr
  452.         :       or_expr
  453.                         { $$ = $1; }
  454.         |       or_expr '?' or_expr ':' or_expr
  455.                         { $$ = node ($1, Node_cond_exp, node($3, Node_illegal, $5)); }
  456.         ;
  457.  
  458. assign_expr
  459.         :       cond_expr
  460.                         { $$ = $1; }
  461.         |       concat_expr ASSIGNOP assign_expr
  462.                         { $$ = node ($1, $2, $3); }
  463.         ;
  464.  
  465. expr
  466.         :  assign_expr
  467.                         { $$ = $1; }
  468.         ;
  469.  
  470. whitespace
  471.         :       /* EMPTY */
  472.                         { $$ = Node_illegal; }
  473.         |       CONCAT_OP
  474.         |       NEWLINE
  475.         |       whitespace CONCAT_OP
  476.         |       whitespace NEWLINE
  477.         ;
  478.  
  479. %%
  480.  
  481. struct token {
  482.   char *operator;
  483.   NODETYPE value;
  484.   int class;
  485.   NODE *(*ptr)();
  486. };
  487.  
  488. #ifndef NULL
  489.  
  490. #define NULL 0
  491.  
  492. #endif
  493.  
  494. NODE *do_atan2(),*do_close(), *do_cos(), *do_exp(), *do_getline(),
  495.      *do_gsub(), *do_index(), *do_length(), *do_log(), *do_match(),
  496.      *do_rand(), *do_sin(), *do_sqrt(),
  497.      *do_srand(), *do_sprintf(), *do_sub(), *do_substr(),  *do_system(),
  498.      *do_split(), *do_int();
  499.  
  500. /* Special functions for debugging */
  501. #ifndef FAST
  502. NODE *do_prvars(), *do_bp();
  503. #endif
  504.  
  505. /* Tokentab is sorted ascii ascending order, so it can be binary searched. */
  506. /* (later.  Right now its just sort of linear search (SLOW!!) */
  507.  
  508. #define END(s) (s-1 + sizeof(s)/sizeof(s[0]))
  509.  
  510. static struct token tokentab[] = {
  511.   {"BEGIN",     Node_illegal,           LEX_BEGIN,      0},
  512.   {"END",       Node_illegal,           LEX_END,        0},
  513.   {"atan2",     Node_builtin,           LEX_BUILTIN,    do_atan2},
  514. #ifndef FAST
  515.   {"bp",        Node_builtin,           LEX_BUILTIN,    do_bp},
  516. #endif
  517.   {"break",     Node_K_break,           LEX_BREAK,      0},
  518.   {"close",     Node_builtin,          LEX_BUILTIN, do_close},
  519.   {"continue",  Node_K_continue,        LEX_CONTINUE,   0},
  520.   {"cos",       Node_builtin,           LEX_BUILTIN,    do_cos},
  521.   {"else",      Node_illegal,           LEX_ELSE,       0},
  522.   {"exit",      Node_K_exit,            LEX_EXIT,       0},
  523.   {"exp",       Node_builtin,           LEX_BUILTIN,    do_exp},
  524.   {"for",       Node_K_for,             LEX_FOR,        0},
  525.   {"getline",   Node_K_getline,         LEX_GETLINE,    do_getline},
  526.   {"gsub",      Node_builtin,           LEX_SUB,        do_gsub},
  527.   {"if",        Node_K_if,              LEX_IF,         0},
  528.   {"in",        Node_illegal,           LEX_IN,         0},
  529.   {"index",     Node_builtin,           LEX_BUILTIN,    do_index},
  530.   {"int",       Node_builtin,           LEX_BUILTIN,    do_int},
  531.   {"length",    Node_builtin,           LEX_BUILTIN,    do_length},
  532.   {"log",       Node_builtin,           LEX_BUILTIN,    do_log},
  533.   {"match",     Node_builtin,           LEX_BUILTIN,    do_match},
  534.   {"next",      Node_K_next,            LEX_NEXT,       0},
  535.   {"print",     Node_K_print,           LEX_PRINT,      0},
  536.   {"printf",    Node_K_printf,          LEX_PRINTF,     0},
  537. #ifndef FAST
  538.   {"prvars",    Node_builtin,           LEX_BUILTIN,    do_prvars},
  539. #endif
  540.   {"rand",      Node_builtin,           LEX_BUILTIN,    do_rand},
  541.   {"sin",       Node_builtin,           LEX_BUILTIN,    do_sin},
  542.   {"split",     Node_builtin,           LEX_BUILTIN,    do_split},
  543.   {"sprintf",   Node_builtin,           LEX_BUILTIN,    do_sprintf},
  544.   {"srand",     Node_builtin,           LEX_BUILTIN,    do_srand},
  545.   {"sqrt",      Node_builtin,           LEX_BUILTIN,    do_sqrt},
  546.   {"sub",       Node_builtin,           LEX_SUB,        do_sub},
  547.   {"substr",    Node_builtin,           LEX_BUILTIN,    do_substr},
  548.   {"system",    Node_builtin,           LEX_BUILTIN,    do_system},
  549.   {"while",     Node_K_while,           LEX_WHILE,      0},
  550.   {NULL,        Node_illegal,           ERROR,          0}
  551. };
  552.  
  553. /* Read one token, getting characters through lexptr.  */
  554. static int yylex()
  555. {
  556.    register int c;
  557.    register int namelen;
  558.    register char *tokstart;
  559.    register struct token *toktab, *low, *high, *mid;
  560.    int dif;
  561.    double atof();                      /* JF know what happens if you forget this? */
  562.  
  563.    static did_newline = 0;             /* JF the grammar insists that actions end 
  564.                                         * with newlines. This was easier than hacking the
  565.                                         * grammar. */
  566.    int do_concat;
  567.  
  568.    int seen_e = 0;                     /* These are for numbers */
  569.    int seen_point = 0;
  570.  
  571. retry:
  572.  
  573.    if (!lexptr)
  574.       return 0;
  575.  
  576.    if (want_regexp)
  577.    {
  578.       want_regexp = 0;
  579.       /**
  580.        * there is a potential bug if a regexp is followed by an equal sign:
  581.        *  "/foo/=bar" would result in assign_quotient being returned as the
  582.        *  next token.  Nothing is done about it since it is not valid awk,
  583.        *  but maybe something should be done anyway. 
  584.        */
  585.  
  586.       tokstart = lexptr;
  587.       while (c = *lexptr++)
  588.       {
  589.          switch (c)
  590.          {
  591.          case '\\':
  592.             if (*lexptr++ == '\0')
  593.             {
  594.                yyerror("unterminated regexp ends with \\");
  595.                return ERROR;
  596.             }
  597.             break;
  598.          case '/':                    /* end of the regexp */
  599.             lexptr--;
  600.             yylval.sval = tokstart;
  601.             return REGEXP;
  602.          case '\n':
  603.          case '\0':
  604.             yyerror("unterminated regexp");
  605.             return ERROR;
  606.          }
  607.       }
  608.    }
  609.    do_concat = want_concat_token;
  610.    want_concat_token = 0;
  611.  
  612.    if (*lexptr == '\0')
  613.    {
  614.       lexptr = 0;
  615.       return NEWLINE;
  616.    }
  617.  
  618.    /**
  619.     * if lexptr is at white space between two terminal tokens or parens,
  620.     * it is a concatenation operator.
  621.     */
  622.    if (do_concat && (*lexptr == ' ' || *lexptr == '\t'))
  623.    {
  624.       while (*lexptr == ' ' || *lexptr == '\t')
  625.          lexptr++;
  626.       if (isalnum(*lexptr) || *lexptr == '\"' || *lexptr == '('
  627.           || *lexptr == '.' || *lexptr == '$')  /* the '.' is for decimal pt */
  628.          return CONCAT_OP;
  629.    }
  630.  
  631.    while (*lexptr == ' ' || *lexptr == '\t')
  632.       lexptr++;
  633.  
  634.    tokstart = lexptr;                  /* JF */
  635.  
  636.    switch (c = *lexptr++)
  637.    {
  638.    case 0:
  639.       return 0;
  640.  
  641.    case '\n':
  642.       lineno++;
  643.       return NEWLINE;
  644.  
  645.    case '#':                          /* it's a comment */
  646.       while (*lexptr != '\n' && *lexptr != '\0')
  647.          lexptr++;
  648.       goto retry;
  649.  
  650.    case '\\':
  651.       if (*lexptr == '\n')
  652.       {
  653.          lexptr++;
  654.          goto retry;
  655.       }
  656.       else
  657.          break;
  658.    case ')':
  659.    case ']':
  660.       ++want_concat_token;
  661.       /* fall through */
  662.    case '(':                          /* JF these were above, but I don't see why they should turn on concat. . . & */
  663.    case '[':
  664.  
  665.    case '{':
  666.    case ',':                          /* JF */
  667.    case '$':
  668.    case ';':
  669.    case ':':
  670.    case '?':
  671.       /*
  672.        * set node type to ILLEGAL because the action should set it to the right thing 
  673.        */
  674.       yylval.nodetypeval = Node_illegal;
  675.       return c;
  676.  
  677.    case '^':
  678.       if (*lexptr == '=')
  679.       {
  680.          yylval.nodetypeval = Node_assign_pow;
  681.          lexptr++;
  682.          return ASSIGNOP;
  683.       }
  684.       yylval.nodetypeval = Node_illegal;
  685.       return c;
  686.  
  687.    case '*':
  688.       if (*lexptr == '=')
  689.       {
  690.          yylval.nodetypeval = Node_assign_times;
  691.          lexptr++;
  692.          return ASSIGNOP;
  693.       }
  694.       yylval.nodetypeval = Node_illegal;
  695.       return c;
  696.  
  697.    case '/':
  698.       if (*lexptr == '=')
  699.       {
  700.          yylval.nodetypeval = Node_assign_quotient;
  701.          lexptr++;
  702.          return ASSIGNOP;
  703.       }
  704.       yylval.nodetypeval = Node_illegal;
  705.       return c;
  706.  
  707.    case '%':
  708.       if (*lexptr == '=')
  709.       {
  710.          yylval.nodetypeval = Node_assign_mod;
  711.          lexptr++;
  712.          return ASSIGNOP;
  713.       }
  714.       yylval.nodetypeval = Node_illegal;
  715.       return c;
  716.  
  717.    case '+':
  718.       if (*lexptr == '=')
  719.       {
  720.          yylval.nodetypeval = Node_assign_plus;
  721.          lexptr++;
  722.          return ASSIGNOP;
  723.       }
  724.       if (*lexptr == '+')
  725.       {
  726.          yylval.nodetypeval = Node_illegal;
  727.          lexptr++;
  728.          return INCREMENT;
  729.       }
  730.       yylval.nodetypeval = Node_illegal;
  731.       return c;
  732.  
  733.    case '!':
  734.       if (*lexptr == '=')
  735.       {
  736.          yylval.nodetypeval = Node_notequal;
  737.          lexptr++;
  738.          return RELOP;
  739.       }
  740.       if (*lexptr == '~')
  741.       {
  742.          yylval.nodetypeval = Node_nomatch;
  743.          lexptr++;
  744.          return MATCHOP;
  745.       }
  746.       yylval.nodetypeval = Node_illegal;
  747.       return c;
  748.  
  749.    case '<':
  750.       if (want_redirect)
  751.       {
  752.          yylval.nodetypeval = Node_redirect_input;
  753.          return REDIRECT_OP;
  754.       }
  755.       if (*lexptr == '=')
  756.       {
  757.          yylval.nodetypeval = Node_leq;
  758.          lexptr++;
  759.          return RELOP;
  760.       }
  761.       yylval.nodetypeval = Node_less;
  762.       return RELOP;
  763.  
  764.    case '=':
  765.       if (*lexptr == '=')
  766.       {
  767.          yylval.nodetypeval = Node_equal;
  768.          lexptr++;
  769.          return RELOP;
  770.       }
  771.       yylval.nodetypeval = Node_assign;
  772.       return ASSIGNOP;
  773.  
  774.    case '>':
  775.       if (want_redirect)
  776.       {
  777.          if (*lexptr == '>')
  778.          {
  779.             yylval.nodetypeval = Node_redirect_append;
  780.             lexptr++;
  781.          }
  782.          else
  783.             yylval.nodetypeval = Node_redirect_output;
  784.          return REDIRECT_OP;
  785.       }
  786.       if (*lexptr == '=')
  787.       {
  788.          yylval.nodetypeval = Node_geq;
  789.          lexptr++;
  790.          return RELOP;
  791.       }
  792.       yylval.nodetypeval = Node_greater;
  793.       return RELOP;
  794.  
  795.    case '~':
  796.       yylval.nodetypeval = Node_match;
  797.       return MATCHOP;
  798.  
  799.    case '}':
  800.       if (did_newline)
  801.       {
  802.          did_newline = 0;
  803.          return c;
  804.       }
  805.       did_newline++;
  806.       --lexptr;
  807.       return NEWLINE;
  808.  
  809.    case '"':
  810.       while (*lexptr != '\0')
  811.       {
  812.          switch (*lexptr++)
  813.          {
  814.          case '\\':
  815.             if (*lexptr++ != '\0')
  816.                break;
  817.             /* fall through */
  818.          case '\n':
  819.             yyerror("unterminated string");
  820.             return ERROR;
  821.          case '\"':
  822.             yylval.sval = tokstart + 1;/* JF Skip the doublequote */
  823.             ++want_concat_token;
  824.             return YSTRING;
  825.          }
  826.       }
  827.       return ERROR;                    /* JF this was one level up, wrong? */
  828.  
  829.    case '-':
  830.       if (*lexptr == '=')
  831.       {
  832.          yylval.nodetypeval = Node_assign_minus;
  833.          lexptr++;
  834.          return ASSIGNOP;
  835.       }
  836.       if (*lexptr == '-')
  837.       {
  838.          yylval.nodetypeval = Node_illegal;
  839.          lexptr++;
  840.          return DECREMENT;
  841.       }
  842.       /*
  843.        * JF I think space tab comma and newline are the legal places for a UMINUS.  Have I missed any? 
  844.        */
  845.       if ((!isdigit(*lexptr) && *lexptr != '.') || (lexptr > lexptr_begin + 1 && !index(" \t,\n", lexptr[-2])))
  846.       {
  847.          /*
  848.           * set node type to ILLEGAL because the action should set it to the right thing 
  849.           */
  850.          yylval.nodetypeval = Node_illegal;
  851.          return c;
  852.       }
  853.       /* FALL through into number code */
  854.    case '0':
  855.    case '1':
  856.    case '2':
  857.    case '3':
  858.    case '4':
  859.    case '5':
  860.    case '6':
  861.    case '7':
  862.    case '8':
  863.    case '9':
  864.    case '.':
  865.       /* It's a number */
  866.       if (c == '-')
  867.          namelen = 1;
  868.       else
  869.          namelen = 0;
  870.       for (; (c = tokstart[namelen]) != '\0'; namelen++)
  871.       {
  872.          switch (c)
  873.          {
  874.          case '.':
  875.             if (seen_point)
  876.                goto got_number;
  877.             ++seen_point;
  878.             break;
  879.          case 'e':
  880.          case 'E':
  881.             if (seen_e)
  882.                goto got_number;
  883.             ++seen_e;
  884.             if (tokstart[namelen + 1] == '-' || tokstart[namelen + 1] == '+')
  885.                namelen++;
  886.             break;
  887.          case '0':
  888.          case '1':
  889.          case '2':
  890.          case '3':
  891.          case '4':
  892.          case '5':
  893.          case '6':
  894.          case '7':
  895.          case '8':
  896.          case '9':
  897.             break;
  898.          default:
  899.             goto got_number;
  900.          }
  901.       }
  902.  
  903.       /**
  904.        * There seems to be a bug (feature?) in the Microsoft Large Model
  905.        * atof function.  If the string to convert is too long, atof returns a
  906.        * zero without bothering to scan the string.  The following hack simply
  907.        * truncates tokstart for the duration of the call. -ADE-
  908.        */
  909.  
  910. got_number:
  911.       lexptr = tokstart + namelen;
  912.       *lexptr = '\0';
  913.       yylval.fval = atof(tokstart);
  914.       *lexptr = c;
  915.       ++want_concat_token;
  916.       return NUMBER;
  917.  
  918.    case '&':
  919.       if (*lexptr == '&')
  920.       {
  921.          yylval.nodetypeval = Node_and;
  922.          lexptr++;
  923.          return LEX_AND;
  924.       }
  925.       return ERROR;
  926.  
  927.    case '|':
  928.       if (want_redirect)
  929.       {
  930.          lexptr++;
  931.          yylval.nodetypeval = Node_redirect_pipe;
  932.          return REDIRECT_OP;
  933.       }
  934.       if (*lexptr == '|')
  935.       {
  936.          yylval.nodetypeval = Node_or;
  937.          lexptr++;
  938.          return LEX_OR;
  939.       }
  940.       return ERROR;
  941.    }
  942.  
  943.    if (!(is_identchar(c)))
  944.    {
  945.       yyerror("Invalid char '%c' in expression\n", c);
  946.       return ERROR;
  947.    }
  948.  
  949.    /* its some type of name-type-thing.  Find its length */
  950.    for (namelen = 0; is_identchar(tokstart[namelen]); namelen++)
  951.       ;
  952.  
  953.  
  954.    /* See if it is a special token.      */
  955.  
  956.    low = tokentab;
  957.    high = END(tokentab);
  958.    while (low <= high)
  959.    {
  960.       mid = low + (high - low) / 2;
  961.       if (!(dif = strncmp(tokstart, mid->operator, namelen)) &&
  962.           *tokstart == mid->operator[0] && mid->operator[namelen] == '\0')
  963.       {
  964.          lexptr = tokstart + namelen;
  965.          if (mid->class == LEX_BUILTIN || mid->class == LEX_SUB)
  966.             yylval.ptrval = mid->ptr;
  967.          else
  968.             yylval.nodetypeval = mid->value;
  969.          return mid->class;
  970.       }
  971.       else
  972.       if (dif > 0)
  973.          low = mid + 1;
  974.       else
  975.          high = mid - 1;
  976.    }
  977.  
  978. #ifdef N
  979.    for (toktab = tokentab; toktab->operator != NULL; toktab++)
  980.    {
  981.       if (*tokstart == toktab->operator[0] &&
  982.           !strncmp(tokstart, toktab->operator, namelen) &&
  983.           toktab->operator[namelen] == '\0')
  984.       {
  985.          lexptr = tokstart + namelen;
  986.          if (toktab->class == LEX_BUILTIN || toktab->class == LEX_SUB)
  987.             yylval.ptrval = toktab->ptr;
  988.          else
  989.             lexptr = tokstart + namelen;
  990.          if (toktab->class == LEX_BUILTIN || toktab->class == LEX_SUB)
  991.             yylval.ptrval = toktab->ptr;
  992.          else
  993.             yylval.nodetypeval = toktab->value;
  994.          return toktab->class;
  995.       }
  996.    }
  997. #endif
  998.  
  999.    /* It's a name.  See how long it is.  */
  1000.    yylval.sval = tokstart;
  1001.    lexptr = tokstart + namelen;
  1002.    ++want_concat_token;
  1003.    return NAME;
  1004. }
  1005.  
  1006. void yyerror(mesg, a1, a2, a3, a4, a5, a6, a7, a8)
  1007. char *mesg;
  1008. {
  1009.    register char *ptr, *beg;
  1010.  
  1011.    /* Find the current line in the input file */
  1012.    if (!lexptr)
  1013.    {
  1014.       beg = "(END OF FILE)";
  1015.       ptr = beg + 13;
  1016.    }
  1017.    else
  1018.    {
  1019.       if (*lexptr == '\n' && lexptr != lexptr_begin)
  1020.          --lexptr;
  1021.       for (beg = lexptr; beg != lexptr_begin && *beg != '\n'; --beg)
  1022.          ;
  1023.       for (ptr = lexptr; *ptr && *ptr != '\n'; ptr++)   /* jfw: NL isn't guaranteed */
  1024.          ;
  1025.       if (beg != lexptr_begin)
  1026.          beg++;
  1027.    }
  1028.    fprintf(stderr, "Error near line %d,  '%.*s'\n", lineno, ptr - beg, beg);
  1029.    /* figure out line number, etc. later */
  1030.    fprintf(stderr, mesg, a1, a2, a3, a4, a5, a6, a7, a8);
  1031.    fprintf(stderr, "\n");
  1032.    exit(1);
  1033. }
  1034.  
  1035. /*
  1036.  * Parse a C escape sequence.  STRING_PTR points to a variable containing a pointer to the string to parse.  That pointer is
  1037.  * updated past the characters we use.  The value of the escape sequence is returned. 
  1038.  *
  1039.  * A negative value means the sequence \ newline was seen, which is supposed to be equivalent to nothing at all. 
  1040.  *
  1041.  * If \ is followed by a null character, we return a negative value and leave the string pointer pointing at the null character. 
  1042.  *
  1043.  * If \ is followed by 000, we return 0 and leave the string pointer after the zeros.  A value of 0 does not mean end of string.  
  1044.  */
  1045.  
  1046. static int parse_escape(string_ptr)
  1047. char **string_ptr;
  1048. {
  1049.    register int c = *(*string_ptr)++;
  1050.  
  1051.    switch (c)
  1052.    {
  1053.    case 'a':
  1054.       return '\a';
  1055.    case 'b':
  1056.       return '\b';
  1057.    case 'e':
  1058.       return 033;
  1059.    case 'f':
  1060.       return '\f';
  1061.    case 'n':
  1062.       return '\n';
  1063.    case 'r':
  1064.       return '\r';
  1065.    case 't':
  1066.       return '\t';
  1067.    case 'v':
  1068.       return '\v';
  1069.    case '\n':
  1070.       return -2;
  1071.    case 0:
  1072.       (*string_ptr)--;
  1073.       return 0;
  1074.    case '^':
  1075.       c = *(*string_ptr)++;
  1076.       if (c == '\\')
  1077.          c = parse_escape(string_ptr);
  1078.       if (c == '?')
  1079.          return 0177;
  1080.       return (c & 0200) | (c & 037);
  1081.  
  1082.    case '0':
  1083.    case '1':
  1084.    case '2':
  1085.    case '3':
  1086.    case '4':
  1087.    case '5':
  1088.    case '6':
  1089.    case '7':
  1090.       {
  1091.          register int i = c - '0';
  1092.          register int count = 0;
  1093.          while (++count < 3)
  1094.          {
  1095.             if ((c = *(*string_ptr)++) >= '0' && c <= '7')
  1096.             {
  1097.                i *= 8;
  1098.                i += c - '0';
  1099.             }
  1100.             else
  1101.             {
  1102.                (*string_ptr)--;
  1103.                break;
  1104.             }
  1105.          }
  1106.          return i;
  1107.       }
  1108.    default:
  1109.       return c;
  1110.    }
  1111. }
  1112.  
  1113.