home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 11 Util / 11-Util.zip / NGAWK1.ZIP / OGAWK.Y < prev    next >
Text File  |  1988-04-06  |  24KB  |  900 lines

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