home *** CD-ROM | disk | FTP | other *** search
/ The C Users' Group Library 1994 August / wc-cdrom-cusersgrouplibrary-1994-08.iso / vol_300 / 333_02 / awktab.y < prev    next >
Text File  |  1989-02-20  |  30KB  |  1,293 lines

  1.  
  2. /***************************************************************************/
  3. /*            gawk -- GNU version of awk               */
  4. /*         YACC input file to create the gAWK semantic parser        */
  5. /*                                       */
  6. /*         Copyright (C) 1986 Free Software Foundation           */
  7. /*             Written by Paul Rubin, August 1986            */
  8. /*                                       */
  9. /***************************************************************************/
  10. /*                                       */
  11. /* GAWK is distributed in the hope that it will be useful, but WITHOUT ANY */
  12. /* WARRANTY.  No author or distributor accepts responsibility to anyone    */
  13. /* for the consequences of using it or for whether it serves any       */
  14. /* particular purpose or works at all, unless he says so in writing.       */
  15. /* Refer to the GAWK General Public License for full details.           */
  16. /*                                       */
  17. /* Everyone is granted permission to copy, modify and redistribute GAWK,   */
  18. /* but only under the conditions described in the GAWK General Public       */
  19. /* License.  A copy of this license is supposed to have been given to you  */
  20. /* along with GAWK so you can know your rights and responsibilities.  It   */
  21. /* should be in a file named COPYING.  Among other things, the copyright   */
  22. /* notice and this notice must be preserved on all copies.           */
  23. /*                                       */
  24. /* In other words, go ahead and share GAWK, but don't try to stop          */
  25. /* anyone else from sharing it farther.  Help stamp out software hoarding! */
  26. /*                                       */
  27. /***************************************************************************/
  28.  
  29.  
  30. %{
  31. #define YYDEBUG 12
  32.  
  33. #include <stdio.h>
  34. #include <stdlib.h>
  35. #include <stdarg.h>
  36. #include <string.h>
  37. #include "awk.h"
  38.  
  39. STATIC int      NEAR PASCAL     yylex(void);
  40. STATIC int      NEAR PASCAL     parse_escape(char **string_ptr);
  41.  
  42.  
  43. /* The following variable is used for a very sickening thing.  The awk      */
  44. /* language uses white space as the string concatenation operator, but      */
  45. /* having a white space token that would have to appear everywhere in all */
  46. /* the grammar rules would be unbearable.  It turns out we can return      */
  47. /* CONCAT_OP exactly when there really is one, just from knowing what      */
  48. /* kinds of other tokens it can appear between (namely, constants,      */
  49. /* variables, or close parentheses).  This is because concatenation has   */
  50. /* the lowest priority of all operators.  want_concat_token is used to      */
  51. /* remember that something that could be the left side of a concat has      */
  52. /* just been returned.    If anyone knows a cleaner way to do this (don't   */
  53. /* look at the Un*x code to find one, though), please suggest it.      */
  54.  
  55. static int          want_concat_token;
  56.  
  57. /* Two more horrible kludges.  The same comment applies to these two too  */
  58.  
  59. static int          want_regexp     = 0; /* lexical scanning kludge   */
  60.  
  61. int              lineno = 1;         /* JF for error msgs      */
  62.  
  63. /* During parsing of a gAWK program, the pointer to the next character      */
  64. /* is in this variable.                           */
  65.  
  66. char             *lexptr;
  67. char             *lexptr_begin;
  68.  
  69. %}
  70.  
  71. %union
  72. {
  73.     long      lval;
  74.     AWKNUM      fval;
  75.     NODE     *nodeval;
  76.     int       nodetypeval;
  77.     char     *sval;
  78.     NODE    *(PASCAL *ptrval)(NODE *);
  79. }
  80.  
  81. %type <nodeval> exp start program rule pattern conditional regexp
  82. %type <nodeval> action variable redirect_in redirect_out exp_list builtin
  83. %type <nodeval> statements statement if_statement opt_exp
  84. %type <nodetypeval> whitespace relop
  85.  
  86. %token <sval> NAME REGEXP YSTRING
  87. %token <lval> ERROR INCDEC
  88. %token <fval> NUMBER
  89. %token <nodetypeval> ASSIGNOP MATCHOP NEWLINE CONCAT_OP
  90. %token <nodetypeval> LEX_BEGIN LEX_END LEX_IF LEX_ELSE
  91. %token <nodetypeval> LEX_WHILE LEX_FOR LEX_BREAK LEX_CONTINUE LEX_DELETE
  92. %token <nodetypeval> LEX_PRINT LEX_PRINTF LEX_NEXT LEX_EXIT
  93. %token <nodetypeval> RELOP_EQ RELOP_GEQ RELOP_LEQ RELOP_NEQ REDIR_APPEND
  94. %token  LEX_IN
  95. %token <lval> LEX_AND LEX_OR INCREMENT DECREMENT
  96. %token <ptrval> LEX_BUILTIN LEX_MATCH_FUNC LEX_SUB_FUNC LEX_SPLIT_FUNC
  97. %token <ptrval> LEX_GETLINE
  98.  
  99.  
  100. /* Lowest to highest */
  101. %right ASSIGNOP
  102. %left ','
  103. %right '?' ':'
  104. %left LEX_OR
  105. %left LEX_AND
  106. %left CONCAT_OP
  107. %nonassoc MATCHOP '>' '<' RELOP_EQ RELOP_GEQ RELOP_LEQ RELOP_NEQ
  108. %left '+' '-'
  109. %left '*' '/' '%'
  110. %right UNARY
  111. %right '^'
  112.  
  113. %%
  114.  
  115. start    : optional_newlines program
  116.       {
  117.           expression_value = $2;
  118.       }
  119.     ;
  120.  
  121.  
  122. program    : rule
  123.       {
  124.           $$ = node($1, NODE_RULE_LIST, NULL);
  125.       }
  126.  
  127.     | program rule
  128.       {           /* cons the rule onto the tail of list */
  129.           $$ = append_right($1, node($2, NODE_RULE_LIST, NULL));
  130.       }
  131.     ;
  132.  
  133. rule    : pattern action NEWLINE optional_newlines
  134.       {
  135.           $$ = node($1, NODE_RULE_NODE, $2);
  136.       }
  137.     ;
  138.  
  139.  
  140. pattern    : /* empty */
  141.       {
  142.           $$ = NULL;
  143.       }
  144.  
  145.     | LEX_BEGIN
  146.       {
  147.           $$ = node(NULL, NODE_K_BEGIN, NULL);
  148.       }
  149.  
  150.     | LEX_END
  151.       {
  152.           $$ = node(NULL, NODE_K_END, NULL);
  153.       }
  154.     | conditional
  155.       {
  156.           $$ = $1;
  157.       }
  158.  
  159.     | conditional ',' conditional
  160.       {
  161.           $$ = mkrangenode(node($1, NODE_COND_PAIR, $3));
  162.       }
  163.     ;
  164.  
  165.  
  166. conditional :
  167.       '!' conditional    %prec UNARY
  168.       {
  169.           $$ = node($2, NODE_NOT, NULL);
  170.       }
  171.  
  172.     | '(' exp_list ')' CONCAT_OP LEX_IN NAME
  173.       {
  174.           $$ = node(variable($6), NODE_MEMBER_COND, $2);
  175.       }
  176.  
  177.     | exp CONCAT_OP LEX_IN NAME
  178.       {
  179.           $$ = node(variable($4), NODE_MEMBER_COND, $1);
  180.       }
  181.  
  182.     | conditional LEX_AND conditional
  183.       {
  184.           $$ = node($1, NODE_AND, $3);
  185.       }
  186.  
  187.     | conditional LEX_OR conditional
  188.       {
  189.           $$ = node ($1, NODE_OR, $3);
  190.       }
  191.  
  192.     | '(' conditional ')'
  193.       {
  194.           $$ = $2;
  195.           want_concat_token = 0;
  196.       }
  197.  
  198.     | regexp
  199.       {
  200.           $$ = $1;
  201.       }
  202.  
  203.     | exp MATCHOP regexp
  204.       {
  205.           $$ = node($1, $2, $3);
  206.       }
  207.  
  208.     | exp MATCHOP variable
  209.       {
  210.           $$ = node($1, $2, $3);
  211.       }
  212.  
  213.     | exp relop exp
  214.       {
  215.           $$ = node($1, $2, $3);
  216.       }
  217.     ;
  218.  
  219. action    : /* empty */
  220.       {
  221.           $$ = NULL;
  222.       }
  223.  
  224.     | '{' whitespace statements '}'
  225.       {
  226.           $$ = $3;
  227.       }
  228.     ;
  229.  
  230.  
  231. statements :
  232.       statement
  233.       {
  234.           $$ = node($1, NODE_STATEMENT_LIST, NULL);
  235.       }
  236.  
  237.     | statements statement
  238.       {
  239.           $$ = append_right($1, node($2, NODE_STATEMENT_LIST, NULL));
  240.       }
  241.     ;
  242.  
  243. statement_term :
  244.       NEWLINE optional_newlines
  245.       {
  246.           $<nodetypeval>$ = NODE_ILLEGAL;
  247.       }
  248.  
  249.     | ';' optional_newlines
  250.       {
  251.           $<nodetypeval>$ = NODE_ILLEGAL;
  252.       }
  253.     ;
  254.  
  255. regexp    : '/'
  256.       {
  257.           ++want_regexp;
  258.       }
  259.       REGEXP '/'
  260.       {
  261.           want_regexp = 0;
  262.           $$ = node(NULL, NODE_REGEXP, (NODE *) make_regexp($3));
  263.       }
  264.  
  265. relop    : '>'
  266.       {
  267.           $$ = NODE_GREATER;
  268.       }
  269.     | '<'
  270.       {
  271.           $$ = NODE_LESS;
  272.       }
  273.     | RELOP_EQ
  274.       {
  275.           $$ = NODE_EQUAL;
  276.       }
  277.     | RELOP_GEQ
  278.       {
  279.           $$ = NODE_GEQ;
  280.       }
  281.     | RELOP_LEQ
  282.       {
  283.           $$ = NODE_LEQ;
  284.       }
  285.     | RELOP_NEQ
  286.       {
  287.           $$ = NODE_NOTEQUAL;
  288.       }
  289.     ;
  290.  
  291. whitespace :
  292.       /* blank */
  293.       {
  294.           $$ = NODE_ILLEGAL;
  295.       }
  296.  
  297.     | CONCAT_OP
  298.     | NEWLINE
  299.     | whitespace CONCAT_OP
  300.     | whitespace NEWLINE
  301.     ;
  302.  
  303. statement :
  304.       '{' whitespace statements '}' whitespace
  305.       {
  306.           $$ = $3;
  307.       }
  308.  
  309.     | if_statement
  310.       {
  311.           $$ = $1;
  312.       }
  313.  
  314.     | LEX_WHILE '(' conditional ')' whitespace statement
  315.       {
  316.           $$ = node($3, NODE_K_WHILE, $6);
  317.       }
  318.  
  319.     | LEX_FOR '(' opt_exp ';' conditional ';' opt_exp ')' whitespace statement
  320.       {
  321.           $$ = node($10, NODE_K_FOR, (NODE *) make_for_loop($3, $5, $7));
  322.       }
  323.  
  324.     | LEX_FOR '(' opt_exp ';' ';' opt_exp ')' whitespace statement
  325.       {
  326.           $$ = node($9, NODE_K_FOR,
  327.             (NODE *) make_for_loop($3, NULL, $6));
  328.       }
  329.  
  330.     | LEX_FOR '(' NAME CONCAT_OP LEX_IN NAME ')' whitespace statement
  331.       {
  332.           $$ = node($9, NODE_K_ARRAYFOR,
  333.             (NODE *) make_for_loop(variable($3),
  334.                            NULL, variable($6)));
  335.       }
  336.  
  337.     | LEX_BREAK statement_term
  338.       /* for break, maybe we'll have to remember where to break to */
  339.       {
  340.           $$ = node(NULL, NODE_K_BREAK, NULL);
  341.       }
  342.  
  343.     | LEX_CONTINUE statement_term
  344.       /* similarly */
  345.       {
  346.           $$ = node(NULL, NODE_K_CONTINUE, NULL);
  347.       }
  348.  
  349.     | LEX_PRINT exp_list redirect_out statement_term
  350.       {
  351.           $$ = node($2, NODE_K_PRINT, $3);
  352.       }
  353.  
  354.     | LEX_PRINT '(' exp_list ')'          /* BW: print(...) */
  355.       {
  356.           want_concat_token = FALSE;
  357.       }
  358.       redirect_out statement_term
  359.       {
  360.           $$ = node($3, NODE_K_PRINT, $6);
  361.       }
  362.  
  363.     | LEX_PRINTF exp_list redirect_out statement_term
  364.       {
  365.           $$ = node($2, NODE_K_PRINTF, $3);
  366.       }
  367.  
  368.     | LEX_PRINTF '(' exp_list ')'
  369.       {
  370.           want_concat_token = FALSE;
  371.       }
  372.       redirect_out statement_term
  373.       {
  374.           $$ = node($3, NODE_K_PRINTF, $6);
  375.       }
  376.  
  377.     | LEX_NEXT statement_term
  378.       {
  379.           $$ = node(NULL, NODE_K_NEXT, NULL);
  380.       }
  381.  
  382.     | LEX_EXIT statement_term
  383.       {
  384.           $$ = node(NULL, NODE_K_EXIT, NULL);
  385.       }
  386.  
  387.     | LEX_EXIT '(' exp ')' statement_term
  388.       {
  389.           $$ = node($3, NODE_K_EXIT, NULL);
  390.       }
  391.  
  392.     | LEX_DELETE NAME '[' exp_list ']' statement_term
  393.       {
  394.           $$ = node(variable($2), NODE_K_DELETE, $4);
  395.       }
  396.  
  397.     | exp statement_term
  398.       {
  399.           $$ = $1;
  400.       }
  401.     ;
  402.  
  403.  
  404. if_statement:
  405.       LEX_IF '(' conditional ')' whitespace statement
  406.       {
  407.           $$ = node($3, NODE_K_IF,
  408.             node($6, NODE_IF_BRANCHES, NULL));
  409.       }
  410.  
  411.     | LEX_IF '(' conditional ')' whitespace statement
  412.           LEX_ELSE whitespace statement
  413.       {
  414.           $$ = node($3, NODE_K_IF,
  415.             node($6, NODE_IF_BRANCHES, $9));
  416.       }
  417.     ;
  418.  
  419. optional_newlines :
  420.       /* empty */
  421.     | optional_newlines NEWLINE
  422.       {
  423.           $<nodetypeval>$ = NODE_ILLEGAL;
  424.       }
  425.     ;
  426.  
  427. redirect_out :
  428.       /* empty */
  429.       {
  430.           $$ = NULL;
  431.       }
  432.  
  433.     | '>' YSTRING           %prec UNARY
  434.       {
  435.           $$ = node(make_string($2, -1), NODE_REDIRECT_OUTPUT, NULL);
  436.       }
  437.     | '>' variable           %prec UNARY
  438.       {
  439.           $$ = node($2, NODE_REDIRECT_OUTPUT, NULL);
  440.       }
  441.     | REDIR_APPEND YSTRING    %prec UNARY
  442.       {
  443.           $$ = node(make_string($2, -1), NODE_REDIRECT_APPEND, NULL);
  444.       }
  445.     | REDIR_APPEND variable %prec UNARY
  446.       {
  447.           $$ = node($2, NODE_REDIRECT_APPEND, NULL);
  448.       }
  449.     ;
  450.  
  451. redirect_in :
  452.       /* empty */
  453.       {
  454.           $$ = NULL;
  455.       }
  456.  
  457.     | '<' YSTRING           %prec UNARY
  458.       {
  459.           $$ = node(make_string($2, -1), NODE_REDIRECT_INPUT, NULL);
  460.       }
  461.     | '<' variable           %prec UNARY
  462.       {
  463.           $$ = node($2, NODE_REDIRECT_INPUT, NULL);
  464.       }
  465.     ;
  466.  
  467. variable :
  468.       NAME
  469.       {
  470.           $$ = variable($1);
  471.       }
  472.  
  473.     | NAME '[' exp_list ']'
  474.       {
  475.           $$ = node(variable($1), NODE_SUBSCRIPT, $3);
  476.       }
  477.  
  478.     | '$' exp      %prec UNARY
  479.       {
  480.           $$ = node($2, NODE_FIELD_SPEC, NULL);
  481.       }
  482.     ;
  483.  
  484. /* optional expression, as in for loop */
  485.  
  486. opt_exp : /* empty */
  487.       {
  488.           $$ = NULL;
  489.       }
  490.  
  491.     | exp
  492.       {
  493.           $$ = $1;
  494.       }
  495.     ;
  496.  
  497. exp_list :
  498.       /* empty */
  499.       {
  500.           $$ = NULL;
  501.       }
  502.  
  503.     | exp
  504.       {
  505.           $$ = node($1, NODE_EXPRESSION_LIST, NULL);
  506.       }
  507.  
  508.     | exp_list ',' exp
  509.       {
  510.           $$ = append_right($1, node($3, NODE_EXPRESSION_LIST, NULL));
  511.       }
  512.     ;
  513.  
  514. exp    : '-' exp      %prec UNARY
  515.       {
  516.           $$ = node($2, NODE_UNARY_MINUS, NULL);
  517.       }
  518.  
  519.     | variable ASSIGNOP exp
  520.       {
  521.           $$ = node($1, $2, $3);
  522.       }
  523.  
  524.     | builtin
  525.  
  526.     | '(' exp ')'
  527.       {
  528.           $$ = $2;
  529.       }
  530.  
  531.     | INCREMENT variable %prec UNARY
  532.       {
  533.           $$ = node($2, NODE_PREINCREMENT, NULL);
  534.       }
  535.  
  536.     | DECREMENT variable %prec UNARY
  537.       {
  538.           $$ = node($2, NODE_PREDECREMENT, NULL);
  539.       }
  540.  
  541.     | variable INCREMENT  %prec UNARY
  542.       {
  543.           $$ = node($1, NODE_POSTINCREMENT, NULL);
  544.       }
  545.  
  546.     | variable DECREMENT  %prec UNARY
  547.       {
  548.           $$ = node($1, NODE_POSTDECREMENT, NULL);
  549.       }
  550.  
  551.     | variable
  552.       {
  553.           $$ = $1;
  554.       }
  555.  
  556.     | NUMBER
  557.       {
  558.           $$ = make_number($1);
  559.       }
  560.  
  561.     | YSTRING
  562.       {
  563.           $$ = make_string($1, -1);
  564.       }
  565.  
  566. /* Binary operators in order of decreasing precedence.  */
  567.     | exp '^' exp
  568.       {
  569.           $$ = node($1, NODE_EXPONENTIAL, $3);
  570.       }
  571.  
  572.     | exp '*' exp
  573.       {
  574.           $$ = node($1, NODE_TIMES, $3);
  575.       }
  576.  
  577.     | exp '/' exp
  578.       {
  579.           $$ = node($1, NODE_QUOTIENT, $3);
  580.       }
  581.  
  582.     | exp '%' exp
  583.       {
  584.           $$ = node($1, NODE_MOD, $3);
  585.       }
  586.  
  587.     | exp '+' exp
  588.       {
  589.           $$ = node($1, NODE_PLUS, $3);
  590.       }
  591.  
  592.     | exp '-' exp
  593.       {
  594.           $$ = node($1, NODE_MINUS, $3);
  595.       }
  596.  
  597.       /* Empty operator.  See yylex for disgusting details. */
  598.     | exp CONCAT_OP exp
  599.       {
  600.           $$ = node($1, NODE_CONCAT, $3);
  601.       }
  602.  
  603.     | conditional '?' exp ':' exp
  604.       {
  605.           $$ = node($1, NODE_CONDEXP,
  606.             node($3, NODE_CONDEXP_BRANCHES, $5));
  607.       }
  608.     ;
  609.  
  610. builtin : LEX_BUILTIN '(' exp_list ')'
  611.       {
  612.           $$ = snode($3, NODE_BUILTIN, $1);
  613.       }
  614.  
  615.     | LEX_BUILTIN
  616.       {
  617.           $$ = snode(NULL, NODE_BUILTIN, $1);
  618.       }
  619.  
  620.     | LEX_GETLINE redirect_in
  621.       {
  622.           auto     NODE    *tmp;
  623.  
  624.           tmp = node(NULL, NODE_GETLINE, $2);
  625.           $$  = snode(tmp, NODE_BUILTIN, $1);
  626.       }
  627.  
  628.     | LEX_GETLINE variable redirect_in
  629.       {
  630.           auto     NODE    *tmp;
  631.  
  632.           tmp = node($2, NODE_GETLINE, $3);
  633.           $$  = snode(tmp, NODE_BUILTIN, $1);
  634.       }
  635.  
  636.     | LEX_SPLIT_FUNC '(' exp_list ')'
  637.       {
  638.           auto     int    num;
  639.  
  640.           num = count_arguments($3);
  641.           if (num < 2 || num > 3)
  642.           yyerror("Invalid number of arguments for function");
  643.           $$ = snode($3, NODE_BUILTIN, $1);
  644.       }
  645.  
  646.     | LEX_SPLIT_FUNC '(' exp_list ',' regexp ')'
  647.       {
  648.           auto     NODE    *tmp;
  649.  
  650.           tmp = node($5, NODE_EXPRESSION_LIST, NULL);
  651.           tmp = append_right($3, tmp);
  652.           if (count_arguments($3) != 3)
  653.           yyerror("Invalid number of arguments for function");
  654.           $$  = snode($3, NODE_BUILTIN, $1);
  655.       }
  656.  
  657.     | LEX_MATCH_FUNC '(' exp ',' regexp ')'
  658.       {
  659.           auto     NODE    *tmp;
  660.  
  661.           tmp = node($5, NODE_EXPRESSION_LIST, NULL);
  662.           tmp = node($3, NODE_EXPRESSION_LIST, tmp);
  663.           $$  = snode(tmp, NODE_BUILTIN, $1);
  664.       }
  665.  
  666.     | LEX_MATCH_FUNC '(' exp_list ')'
  667.       {
  668.           if (count_arguments($3) != 2)
  669.           yyerror("Invalid number of arguments for function");
  670.           $$ = snode($3, NODE_BUILTIN, $1);
  671.       }
  672.  
  673.     | LEX_SUB_FUNC '(' regexp ',' exp ',' exp ')'
  674.       {
  675.           auto     NODE     *tmp;
  676.  
  677.           tmp = node($7, NODE_EXPRESSION_LIST, NULL);
  678.           tmp = node($5, NODE_EXPRESSION_LIST, tmp);
  679.           tmp = node($3, NODE_EXPRESSION_LIST, tmp);
  680.           $$  = snode(tmp, NODE_BUILTIN, $1);
  681.       }
  682.  
  683.     | LEX_SUB_FUNC '(' regexp ',' exp ')'
  684.       {
  685.           auto     NODE     *tmp;
  686.  
  687.           tmp = make_number((AWKNUM) 0.0);
  688.           tmp = node(tmp, NODE_FIELD_SPEC, NULL);
  689.           tmp = node(tmp, NODE_EXPRESSION_LIST, NULL);
  690.           tmp = node($5,  NODE_EXPRESSION_LIST, tmp);
  691.           tmp = node($3,  NODE_EXPRESSION_LIST, tmp);
  692.           $$  = snode(tmp, NODE_BUILTIN, $1);
  693.       }
  694.  
  695.     | LEX_SUB_FUNC '(' exp_list ')'
  696.       {
  697.           auto     int    num;
  698.           auto     NODE    *tmp;
  699.  
  700.           num = count_arguments($3);
  701.           if (num < 2 || num > 3)
  702.           yyerror("Invalid number of arguments for function");
  703.           if (num == 2)
  704.           {
  705.           tmp = make_number((AWKNUM) 0.0);
  706.           tmp = node(tmp, NODE_FIELD_SPEC, NULL);
  707.           append_right($3, node(tmp, NODE_EXPRESSION_LIST, NULL));
  708.           }
  709.           $$  = snode($3, NODE_BUILTIN, $1);
  710.       }
  711.     ;
  712.  
  713. %%
  714.  
  715.  
  716. struct token
  717. {
  718.     char      *operator;
  719.     int        value;
  720.     int        class;
  721.     NODE     *(PASCAL *ptr)(NODE *);
  722. };
  723.  
  724.  
  725.  
  726. /* Tokentab is sorted ascii ascending order, so it can be binary searched. */
  727. /* DO NOT enter table entries out of order lest search can't find them.    */
  728.  
  729. static struct token tokentab[] =
  730. {
  731.     { "BEGIN",       NODE_ILLEGAL,       LEX_BEGIN,       NULL        },
  732.     { "END",       NODE_ILLEGAL,       LEX_END,       NULL        },
  733.     { "atan2",       NODE_BUILTIN,       LEX_BUILTIN,    do_atan2    },
  734. #ifndef FAST
  735.     { "bp",       NODE_BUILTIN,       LEX_BUILTIN,    do_bp       },
  736. #endif
  737.     { "break",       NODE_K_BREAK,       LEX_BREAK,       NULL        },
  738.     { "close",       NODE_BUILTIN,       LEX_BUILTIN,    do_close    },
  739.     { "continue",  NODE_K_CONTINUE,       LEX_CONTINUE,   NULL        },
  740.     { "cos",       NODE_BUILTIN,       LEX_BUILTIN,    do_cos      },
  741.     { "delete",    NODE_K_DELETE,       LEX_DELETE,       NULL        },
  742.     { "else",       NODE_ILLEGAL,       LEX_ELSE,       NULL        },
  743.     { "exit",       NODE_K_EXIT,        LEX_EXIT,       NULL        },
  744.     { "exp",       NODE_BUILTIN,       LEX_BUILTIN,    do_exp      },
  745.     { "for",       NODE_K_FOR,           LEX_FOR,       NULL        },
  746.     { "getline",   NODE_BUILTIN,       LEX_GETLINE,    do_getline  },
  747.     { "gsub",       NODE_BUILTIN,       LEX_SUB_FUNC,   do_gsub     },
  748.     { "if",       NODE_K_IF,           LEX_IF,       NULL        },
  749.     { "in",       NODE_ILLEGAL,       LEX_IN,       NULL        },
  750.     { "index",       NODE_BUILTIN,       LEX_BUILTIN,    do_index    },
  751.     { "int",       NODE_BUILTIN,       LEX_BUILTIN,    do_int      },
  752.     { "length",    NODE_BUILTIN,       LEX_BUILTIN,    do_length   },
  753.     { "log",       NODE_BUILTIN,       LEX_BUILTIN,    do_log      },
  754.     { "lower",       NODE_BUILTIN,       LEX_BUILTIN,    do_lower    },
  755.     { "match",       NODE_BUILTIN,       LEX_MATCH_FUNC, do_match    },
  756.     { "next",       NODE_K_NEXT,        LEX_NEXT,       NULL        },
  757.     { "print",       NODE_K_PRINT,       LEX_PRINT,       NULL        },
  758.     { "printf",    NODE_K_PRINTF,       LEX_PRINTF,       NULL        },
  759. #ifndef FAST
  760.     { "prvars",    NODE_BUILTIN,       LEX_BUILTIN,    do_prvars   },
  761. #endif
  762.     { "rand",       NODE_BUILTIN,       LEX_BUILTIN,    do_rand     },
  763.     { "reverse",   NODE_BUILTIN,       LEX_BUILTIN,    do_reverse  },
  764.     { "sin",       NODE_BUILTIN,       LEX_BUILTIN,    do_sin      },
  765.     { "split",       NODE_BUILTIN,       LEX_SPLIT_FUNC, do_split    },
  766.     { "sprintf",   NODE_BUILTIN,       LEX_BUILTIN,    do_sprintf  },
  767.     { "sqrt",       NODE_BUILTIN,       LEX_BUILTIN,    do_sqrt     },
  768.     { "srand",       NODE_BUILTIN,       LEX_BUILTIN,    do_srand    },
  769.     { "sub",       NODE_BUILTIN,       LEX_SUB_FUNC,   do_sub      },
  770.     { "substr",    NODE_BUILTIN,       LEX_BUILTIN,    do_substr   },
  771.     { "system",    NODE_BUILTIN,       LEX_BUILTIN,    do_system   },
  772.     { "upper",       NODE_BUILTIN,       LEX_BUILTIN,    do_upper    },
  773.     { "while",       NODE_K_WHILE,       LEX_WHILE,       NULL        }
  774. };
  775.  
  776. /* Read one token, getting characters through lexptr.  */
  777.  
  778. STATIC int NEAR PASCAL yylex(void)
  779. {
  780.     register int         c;
  781.     register int         namelen;
  782.     register char        *tokstart;
  783.     static   int         last_tok_1  = 0;
  784.     static   int         last_tok_2  = 0;
  785.     static   int         did_newline = 0; /* JF the grammar insists that */
  786.                           /* actions end with newlines.  */
  787.     auto     int         do_concat;
  788.     auto     int         seen_e = 0;      /* These are for numbers         */
  789.     auto     int         seen_point = 0;
  790.     auto     int         next_tab;
  791.  
  792. retry:
  793.     if(!lexptr)
  794.     return(0);
  795.  
  796.     if (want_regexp)
  797.     {
  798.     /* there is a potential bug if a regexp is followed by an equal     */
  799.     /* sign: "/foo/=bar" would result in assign_quotient being returned */
  800.     /* as the next token.  Nothing is done about it since it is not     */
  801.     /* valid awk, but maybe something should be done anyway.        */
  802.     want_regexp = 0;
  803.  
  804.     tokstart = lexptr;
  805.     while (c = *lexptr++)
  806.     {
  807.         switch (c)
  808.         {
  809.         case '\\':
  810.             if (*lexptr++ == EOS)
  811.             {
  812.             yyerror ("unterminated regexp ends with \\");
  813.             return(ERROR);
  814.             }
  815.             break;
  816.         case '/':          /* end of the regexp */
  817.             lexptr--;
  818.             yylval.sval = tokstart;
  819.             return(REGEXP);
  820.         case '\n':
  821.         case EOS:
  822.             yyerror("unterminated regexp");
  823.             return(ERROR);
  824.         }
  825.     }
  826.     }
  827.     do_concat          = want_concat_token;
  828.     want_concat_token = 0;
  829.  
  830.     if (*lexptr == EOS)
  831.     {
  832.     lexptr = NULL;
  833.     return(NEWLINE);
  834.     }
  835.  
  836.     /* if lexptr is at white space between two terminal tokens or parens,  */
  837.     /* it is a concatenation operator.                       */
  838.     if (do_concat && (*lexptr == ' ' || *lexptr == '\t'))
  839.     {
  840.     while (*lexptr == ' ' || *lexptr == '\t')
  841.         lexptr++;
  842.     if (isalnum(*lexptr) || *lexptr == '\"' || *lexptr == '('
  843.                  || *lexptr == '.'    || *lexptr == '$')
  844.         return(CONCAT_OP);
  845.     }
  846.  
  847.     while (*lexptr == ' ' || *lexptr == '\t')
  848.     lexptr++;
  849.  
  850.     tokstart   = lexptr;          /* JF */
  851.     last_tok_1 = last_tok_2;
  852.     last_tok_2 = *lexptr;
  853.  
  854.     switch (c = *lexptr++)
  855.     {
  856.     case 0:
  857.         return(0);
  858.     case '\n':
  859.         ++lineno;
  860.         if (',' == last_tok_1)     /* BW: allow lines to be continued */
  861.         goto retry;         /*    at a comma            */
  862.         return(NEWLINE);
  863.     case '#':              /* it's a comment */
  864.         while (*lexptr != '\n' && *lexptr != EOS)
  865.         lexptr++;
  866.         goto retry;
  867.     case '\\':
  868.         if (*lexptr == '\n')
  869.         {
  870.         ++lexptr;
  871.         ++lineno;
  872.         goto retry;
  873.         }
  874.         break;
  875.     case ')':
  876.     case ']':
  877.         ++want_concat_token;         /* intentional fall thru */
  878.     case '(':         /* JF these were above, but I don't see why */
  879.     case '[':         /* they should turn on concat. . . &         */
  880.     case '{':
  881.     case ',':         /* JF */
  882.     case '$':
  883.     case ';':
  884.     case '?':
  885.     case ':':
  886.         /* set node type to ILLEGAL because the action should set it to */
  887.         /* the right thing                            */
  888.         yylval.nodetypeval = NODE_ILLEGAL;
  889.         return(c);
  890.     case '^':                   /* added by BW 12-12-88 */
  891.         if (*lexptr == '=')
  892.         {
  893.         yylval.nodetypeval = NODE_ASSIGN_EXPONENTIAL;
  894.         ++lexptr;
  895.         return(ASSIGNOP);
  896.         }
  897.         yylval.nodetypeval = NODE_ILLEGAL;
  898.         return(c);
  899.     case '*':
  900.         if (*lexptr == '=')
  901.         {
  902.         yylval.nodetypeval = NODE_ASSIGN_TIMES;
  903.         ++lexptr;
  904.         return(ASSIGNOP);
  905.         }
  906.         yylval.nodetypeval = NODE_ILLEGAL;
  907.         return(c);
  908.     case '/':
  909.         if (*lexptr == '=')
  910.         {
  911.         yylval.nodetypeval = NODE_ASSIGN_QUOTIENT;
  912.         ++lexptr;
  913.         return(ASSIGNOP);
  914.         }
  915.         yylval.nodetypeval = NODE_ILLEGAL;
  916.         return(c);
  917.     case '%':
  918.         if (*lexptr == '=')
  919.         {
  920.         yylval.nodetypeval = NODE_ASSIGN_MOD;
  921.         ++lexptr;
  922.         return(ASSIGNOP);
  923.         }
  924.         yylval.nodetypeval = NODE_ILLEGAL;
  925.         return(c);
  926.     case '+':
  927.         if (*lexptr == '=')
  928.         {
  929.         yylval.nodetypeval = NODE_ASSIGN_PLUS;
  930.         ++lexptr;
  931.         return(ASSIGNOP);
  932.         }
  933.         if (*lexptr == '+')
  934.         {
  935.         yylval.nodetypeval = NODE_ILLEGAL;
  936.         ++lexptr;
  937.         return(INCREMENT);
  938.         }
  939.         yylval.nodetypeval = NODE_ILLEGAL;
  940.         return(c);
  941.     case '!':
  942.         if (*lexptr == '=')
  943.         {
  944.         yylval.nodetypeval = NODE_ILLEGAL;
  945.         ++lexptr;
  946.         return(RELOP_NEQ);
  947.         }
  948.         if (*lexptr == '~')
  949.         {
  950.         yylval.nodetypeval = NODE_NOMATCH;
  951.         ++lexptr;
  952.         return(MATCHOP);
  953.         }
  954.         yylval.nodetypeval = NODE_ILLEGAL;
  955.         return(c);
  956.     case '<':
  957.         yylval.nodetypeval = NODE_ILLEGAL;
  958.         if (*lexptr == '=')
  959.         {
  960.         ++lexptr;
  961.         return(RELOP_LEQ);
  962.         }
  963.         return(c);
  964.     case '=':
  965.         if (*lexptr == '=')
  966.         {
  967.         yylval.nodetypeval = NODE_ILLEGAL;
  968.         ++lexptr;
  969.         return(RELOP_EQ);
  970.         }
  971.         yylval.nodetypeval = NODE_ASSIGN;
  972.         return(ASSIGNOP);
  973.     case '>':
  974.         yylval.nodetypeval = NODE_ILLEGAL;
  975.         if ('>' == *lexptr)
  976.         {
  977.         ++lexptr;
  978.         return(REDIR_APPEND);
  979.         }
  980.         if (*lexptr == '=')
  981.         {
  982.         ++lexptr;
  983.         return(RELOP_GEQ);
  984.         }
  985.         return(c);
  986.     case '~':
  987.         yylval.nodetypeval = NODE_MATCH;
  988.         return(MATCHOP);
  989.     case '}':
  990.         if (did_newline)
  991.         {
  992.         did_newline = 0;
  993.         return(c);
  994.         }
  995.         ++did_newline;
  996.         --lexptr;
  997.         return(NEWLINE);
  998.     case '"':
  999.         while (*lexptr != EOS)
  1000.         {
  1001.         switch (*lexptr++)
  1002.         {
  1003.             case '\\':
  1004.             if (*lexptr++ != EOS)
  1005.                 break;
  1006.                             /* fall through */
  1007.             case '\n':
  1008.             yyerror("unterminated string");
  1009.             return(ERROR);
  1010.             case '\"':
  1011.             yylval.sval = tokstart + 1;
  1012.             ++want_concat_token;
  1013.             return(YSTRING);
  1014.         }
  1015.         }
  1016.         return(ERROR);        /* JF this was one level up, wrong? */
  1017.     case '-':
  1018.         if (*lexptr == '=')
  1019.         {
  1020.         yylval.nodetypeval = NODE_ASSIGN_MINUS;
  1021.         ++lexptr;
  1022.         return(ASSIGNOP);
  1023.         }
  1024.         if (*lexptr == '-')
  1025.         {
  1026.         yylval.nodetypeval = NODE_ILLEGAL;
  1027.         ++lexptr;
  1028.         return(DECREMENT);
  1029.         }
  1030.  
  1031.         /* JF I think space tab comma and newline are the legal places  */
  1032.         /* for a UMINUS.  Have I missed any?                */
  1033.         if ((!isdigit(*lexptr) && *lexptr!='.')
  1034.                    ||
  1035.         (lexptr > lexptr_begin + 1 &&  !index(" \t,\n",lexptr[-2])))
  1036.         {
  1037.         /* set node type to ILLEGAL because the action should set   */
  1038.         /* it to the right thing                    */
  1039.         yylval.nodetypeval = NODE_ILLEGAL;
  1040.         return(c);
  1041.         }
  1042.                        /* FALL through into number code */
  1043.     case '0':
  1044.     case '1':
  1045.     case '2':
  1046.     case '3':
  1047.     case '4':
  1048.     case '5':
  1049.     case '6':
  1050.     case '7':
  1051.     case '8':
  1052.     case '9':
  1053.     case '.':
  1054.         namelen = ('-' == c) ? 1 : 0;
  1055.         for (; (c = tokstart[namelen]) != EOS; namelen++)
  1056.         {
  1057.         switch (c)
  1058.         {
  1059.             case '.':
  1060.             if (seen_point)
  1061.                 goto got_number;
  1062.             ++seen_point;
  1063.             break;
  1064.             case 'e':
  1065.             case 'E':
  1066.             if (seen_e)
  1067.                 goto got_number;
  1068.             ++seen_e;
  1069.             if (tokstart[namelen+1] == '-'
  1070.                       ||
  1071.                 tokstart[namelen+1] == '+')
  1072.                 namelen++;
  1073.             break;
  1074.             default:
  1075.             if (c < '0' || c > '9')
  1076.                 goto got_number;
  1077.             break;
  1078.         }
  1079.         }
  1080.  
  1081. got_number:
  1082.         lexptr = tokstart + namelen;
  1083.         c        = *lexptr;            /* BW: some versions of  */
  1084.         *lexptr    = EOS;               /*      atof() are very   */
  1085.         yylval.fval = (AWKNUM) atof(tokstart);  /*       picky about what  */
  1086.         *lexptr    = c;                /*       follows a number. */
  1087.         ++want_concat_token;
  1088.         return(NUMBER);
  1089.  
  1090.     case '&':
  1091.         if (*lexptr == '&')
  1092.         {
  1093.         yylval.nodetypeval = NODE_AND;
  1094.         ++lexptr;
  1095.         return(LEX_AND);
  1096.         }
  1097.         return(ERROR);
  1098.     case '|':
  1099.         if (*lexptr == '|')
  1100.         {
  1101.         yylval.nodetypeval = NODE_OR;
  1102.         ++lexptr;
  1103.         return(LEX_OR);
  1104.         }
  1105.         return(ERROR);
  1106.     }
  1107.  
  1108.     if (!isalpha(c))
  1109.     {
  1110.     yyerror("Invalid char '%c' in expression\n", c);
  1111.     return(ERROR);
  1112.     }
  1113.  
  1114.     /* its some type of name-type-thing.  Find its length */
  1115.     for (namelen = 0; is_identchar(tokstart[namelen]); namelen++)
  1116.     ;
  1117.  
  1118.     /* See if it is a special token.  binary search added by BW */
  1119.     {
  1120.     register int        x;
  1121.     auto     char        tok1;
  1122.     auto     int        l = 0, r = MAXDIM(tokentab) - 1;
  1123.  
  1124.     do
  1125.     {
  1126.         x = (l + r) / 2;
  1127.         tok1 = tokentab[x].operator[0];
  1128.         if (tok1 == *tokstart)
  1129.         {
  1130.         tok1 = memcmp(tokstart, tokentab[x].operator, namelen);
  1131.         if (0 == tok1 && EOS == tokentab[x].operator[namelen])
  1132.             break;
  1133.         if (tok1 > 0)
  1134.             l = x + 1;
  1135.         else
  1136.             r = x - 1;
  1137.         }
  1138.         else
  1139.         {
  1140.         if (*tokstart < tok1)
  1141.             r = x - 1;
  1142.         else
  1143.             l = x + 1;
  1144.         }
  1145.     } while (l <= r);
  1146.     if (l <= r)
  1147.     {
  1148.         lexptr = tokstart + namelen;
  1149.         if (NODE_BUILTIN == tokentab[x].value)
  1150.         yylval.ptrval = tokentab[x].ptr;
  1151.         else
  1152.         yylval.nodetypeval = tokentab[x].value;
  1153.         return(tokentab[x].class);
  1154.     }
  1155.     }
  1156.  
  1157.     /* It's a name.  See how long it is.  */
  1158.     yylval.sval = tokstart;
  1159.     lexptr = tokstart+namelen;
  1160.     ++want_concat_token;
  1161.     return(NAME);
  1162. }
  1163.  
  1164.  
  1165. void yyerror(char *mesg, ...)
  1166. {
  1167.     register char     *ptr, *beg;
  1168.     auto     va_list      ap;
  1169.     auto     int      colno = 0;
  1170.     auto     int      i, next_tab;
  1171.  
  1172.     /* Find the current line in the input file */
  1173.     if (!lexptr)
  1174.     {
  1175.     beg   = "(END OF FILE)";
  1176.     ptr   = beg + strlen(beg);
  1177.     }
  1178.     else
  1179.     {
  1180.     if (*lexptr == '\n' && lexptr != lexptr_begin)
  1181.         --lexptr;
  1182.     for (beg = lexptr; beg != lexptr_begin && *beg != '\n'; --beg)
  1183.         ;
  1184.     for (ptr = lexptr; *ptr && *ptr != '\n'; ptr++)
  1185.         ;
  1186.     if (beg != lexptr_begin)
  1187.         ++beg;
  1188.     i = 0;
  1189.     while (beg + i <= lexptr)
  1190.     {
  1191.         if ('\t' == beg[i])
  1192.         {
  1193.         next_tab = colno % TABSTOPS;
  1194.         colno    += (next_tab ? next_tab : TABSTOPS);
  1195.         }
  1196.         else
  1197.         ++colno;
  1198.         ++i;
  1199.     }
  1200.     }
  1201.  
  1202.     fprintf(stderr, "\n'%.*s'\n", ptr - beg, beg);
  1203.     fprintf(stderr, "%*s\n", colno + 1, "^");
  1204.  
  1205.     /* figure out line number, etc. later */
  1206.     va_start(ap, mesg);
  1207.     vfprintf(stderr, mesg, ap);
  1208.     va_end(ap);
  1209.     fprintf(stderr, " near line %d column %d\n", lineno, colno);
  1210.     exit(1);
  1211. }
  1212.  
  1213.  
  1214. /* Parse a C escape sequence.  STRING_PTR points to a variable
  1215.    containing a pointer to the string to parse.  That pointer
  1216.    is updated past the characters we use.  The value of the
  1217.    escape sequence is returned.
  1218.  
  1219.    A negative value means the sequence \ newline was seen,
  1220.    which is supposed to be equivalent to nothing at all.
  1221.  
  1222.    If \ is followed by a null character, we return a negative
  1223.    value and leave the string pointer pointing at the null character.
  1224.  
  1225.    If \ is followed by 000, we return 0 and leave the string pointer
  1226.    after the zeros.  A value of 0 does not mean end of string.  */
  1227.  
  1228. STATIC int NEAR PASCAL parse_escape(char **string_ptr)
  1229. {
  1230.     register int      c = *(*string_ptr)++;
  1231.  
  1232.     switch (c)
  1233.     {
  1234.     case 'a':
  1235.         return('\a');
  1236.     case 'b':
  1237.         return('\b');
  1238.     case 'e':
  1239.         return(033);
  1240.     case 'f':
  1241.         return('\f');
  1242.     case 'n':
  1243.         return('\n');
  1244.     case 'r':
  1245.         return('\r');
  1246.     case 't':
  1247.         return('\t');
  1248.     case 'v':
  1249.         return('\v');
  1250.     case '\n':
  1251.         return(-2);
  1252.     case 0:
  1253.         (*string_ptr)--;
  1254.         return(0);
  1255.     case '^':
  1256.         c = *(*string_ptr)++;
  1257.         if (c == '\\')
  1258.         c = parse_escape(string_ptr);
  1259.         if (c == '?')
  1260.         return(0177);
  1261.         return((c & 0200) | (c & 037));
  1262.     case '0':
  1263.     case '1':
  1264.     case '2':
  1265.     case '3':
  1266.     case '4':
  1267.     case '5':
  1268.     case '6':
  1269.     case '7':
  1270.         {
  1271.         register int      i = c - '0';
  1272.         register int      count = 0;
  1273.  
  1274.         while (++count < 3)
  1275.         {
  1276.             if ((c = *(*string_ptr)++) >= '0' && c <= '7')
  1277.             {
  1278.             i *= 8;
  1279.             i += c - '0';
  1280.             }
  1281.             else
  1282.             {
  1283.             (*string_ptr)--;
  1284.             break;
  1285.             }
  1286.         }
  1287.         return(i);
  1288.         }
  1289.     default:
  1290.         return(c);
  1291.     }
  1292. }
  1293.