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

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