freebsd-src/contrib/awk/awkgram.y
David E. O'Brien 622bfc32f3 I'd put down $10 says the author did a last minute tweak to awkgram.y (which
had an error in it), and applied it by hand to awkgram.c (getting it right)
rather than really generating an new awkgram.c properly using Bison/YACC...
Fix his mistake here.
2001-11-02 23:41:29 +00:00

2876 lines
65 KiB
Plaintext

/*
* awk.y --- yacc/bison parser
*/
/*
* Copyright (C) 1986, 1988, 1989, 1991-2001 the Free Software Foundation, Inc.
*
* This file is part of GAWK, the GNU implementation of the
* AWK Programming Language.
*
* GAWK is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* GAWK is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
*/
%{
#ifdef GAWKDEBUG
#define YYDEBUG 12
#endif
#include "awk.h"
#define CAN_FREE TRUE
#define DONT_FREE FALSE
#if defined(HAVE_STDARG_H) && defined(__STDC__) && __STDC__
static void yyerror(const char *m, ...) ;
#else
static void yyerror(); /* va_alist */
#endif
static char *get_src_buf P((void));
static int yylex P((void));
static NODE *node_common P((NODETYPE op));
static NODE *snode P((NODE *subn, NODETYPE op, int sindex));
static NODE *mkrangenode P((NODE *cpair));
static NODE *make_for_loop P((NODE *init, NODE *cond, NODE *incr));
static NODE *append_right P((NODE *list, NODE *new));
static void func_install P((NODE *params, NODE *def));
static void pop_var P((NODE *np, int freeit));
static void pop_params P((NODE *params));
static NODE *make_param P((char *name));
static NODE *mk_rexp P((NODE *exp));
static int dup_parms P((NODE *func));
static void param_sanity P((NODE *arglist));
static void parms_shadow P((const char *fname, NODE *func));
static int isnoeffect P((NODETYPE t));
static int isassignable P((NODE *n));
static void dumpintlstr P((char *str, size_t len));
static void count_args P((NODE *n));
enum defref { FUNC_DEFINE, FUNC_USE };
static void func_use P((char *name, enum defref how));
static void check_funcs P((void));
static int want_assign; /* lexical scanning kludge */
static int want_regexp; /* lexical scanning kludge */
static int can_return; /* lexical scanning kludge */
static int io_allowed = TRUE; /* lexical scanning kludge */
static int parsing_end_rule = FALSE; /* for warnings */
static char *lexptr; /* pointer to next char during parsing */
static char *lexend;
static char *lexptr_begin; /* keep track of where we were for error msgs */
static char *lexeme; /* beginning of lexeme for debugging */
static char *thisline = NULL;
#define YYDEBUG_LEXER_TEXT (lexeme)
static int param_counter;
static char *tokstart = NULL;
static char *tok = NULL;
static char *tokend;
static long func_count; /* total number of functions */
#define HASHSIZE 1021 /* this constant only used here */
NODE *variables[HASHSIZE];
static int var_count; /* total number of global variables */
extern char *source;
extern int sourceline;
extern struct src *srcfiles;
extern int numfiles;
extern int errcount;
extern NODE *begin_block;
extern NODE *end_block;
%}
%union {
long lval;
AWKNUM fval;
NODE *nodeval;
NODETYPE nodetypeval;
char *sval;
NODE *(*ptrval)();
}
%type <nodeval> function_prologue function_body
%type <nodeval> rexp exp start program rule simp_exp
%type <nodeval> non_post_simp_exp
%type <nodeval> pattern
%type <nodeval> action variable param_list
%type <nodeval> rexpression_list opt_rexpression_list
%type <nodeval> expression_list opt_expression_list
%type <nodeval> statements statement if_statement opt_param_list
%type <nodeval> opt_exp opt_variable regexp
%type <nodeval> input_redir output_redir
%type <nodetypeval> print
%type <sval> func_name
%type <lval> lex_builtin
%token <sval> FUNC_CALL NAME REGEXP
%token <lval> ERROR
%token <nodeval> YNUMBER YSTRING
%token <nodetypeval> RELOP APPEND_OP
%token <nodetypeval> ASSIGNOP MATCHOP NEWLINE CONCAT_OP
%token <nodetypeval> LEX_BEGIN LEX_END LEX_IF LEX_ELSE LEX_RETURN LEX_DELETE
%token <nodetypeval> LEX_WHILE LEX_DO LEX_FOR LEX_BREAK LEX_CONTINUE
%token <nodetypeval> LEX_PRINT LEX_PRINTF LEX_NEXT LEX_EXIT LEX_FUNCTION
%token <nodetypeval> LEX_GETLINE LEX_NEXTFILE
%token <nodetypeval> LEX_IN
%token <lval> LEX_AND LEX_OR INCREMENT DECREMENT
%token <lval> LEX_BUILTIN LEX_LENGTH
/* these are just yylval numbers */
/* Lowest to highest */
%right ASSIGNOP
%right '?' ':'
%left LEX_OR
%left LEX_AND
%left LEX_GETLINE
%nonassoc LEX_IN
%left FUNC_CALL LEX_BUILTIN LEX_LENGTH
%nonassoc ','
%nonassoc MATCHOP
%nonassoc RELOP '<' '>' '|' APPEND_OP TWOWAYIO
%left CONCAT_OP
%left YSTRING YNUMBER
%left '+' '-'
%left '*' '/' '%'
%right '!' UNARY
%right '^'
%left INCREMENT DECREMENT
%left '$'
%left '(' ')'
%%
start
: opt_nls program opt_nls
{
expression_value = $2;
check_funcs();
}
;
program
: rule
{
if ($1 != NULL)
$$ = $1;
else
$$ = NULL;
yyerrok;
}
| program rule
/* add the rule to the tail of list */
{
if ($2 == NULL)
$$ = $1;
else if ($1 == NULL)
$$ = $2;
else {
if ($1->type != Node_rule_list)
$1 = node($1, Node_rule_list,
(NODE*) NULL);
$$ = append_right($1,
node($2, Node_rule_list, (NODE *) NULL));
}
yyerrok;
}
| error { $$ = NULL; }
| program error { $$ = NULL; }
| /* empty */ { $$ = NULL; }
;
rule
: LEX_BEGIN { io_allowed = FALSE; }
action
{
if (begin_block != NULL) {
if (begin_block->type != Node_rule_list)
begin_block = node(begin_block, Node_rule_list,
(NODE *) NULL);
(void) append_right(begin_block, node(
node((NODE *) NULL, Node_rule_node, $3),
Node_rule_list, (NODE *) NULL) );
} else
begin_block = node((NODE *) NULL, Node_rule_node, $3);
$$ = NULL;
io_allowed = TRUE;
yyerrok;
}
| LEX_END { io_allowed = FALSE; parsing_end_rule = TRUE; }
action
{
if (end_block != NULL) {
if (end_block->type != Node_rule_list)
end_block = node(end_block, Node_rule_list,
(NODE *) NULL);
(void) append_right (end_block, node(
node((NODE *) NULL, Node_rule_node, $3),
Node_rule_list, (NODE *) NULL));
} else
end_block = node((NODE *) NULL, Node_rule_node, $3);
$$ = NULL;
io_allowed = TRUE;
parsing_end_rule = FALSE;
yyerrok;
}
| LEX_BEGIN statement_term
{
warning(_("BEGIN blocks must have an action part"));
errcount++;
yyerrok;
}
| LEX_END statement_term
{
warning(_("END blocks must have an action part"));
errcount++;
yyerrok;
}
| pattern action
{ $$ = node($1, Node_rule_node, $2); yyerrok; }
| action
{ $$ = node((NODE *) NULL, Node_rule_node, $1); yyerrok; }
| pattern statement_term
{
$$ = node($1,
Node_rule_node,
node(node(node(make_number(0.0),
Node_field_spec,
(NODE *) NULL),
Node_expression_list,
(NODE *) NULL),
Node_K_print,
(NODE *) NULL));
yyerrok;
}
| function_prologue function_body
{
func_install($1, $2);
$$ = NULL;
yyerrok;
}
;
func_name
: NAME
{ $$ = $1; }
| FUNC_CALL
{ $$ = $1; }
| lex_builtin
{
yyerror(_("`%s' is a built-in function, it cannot be redefined"),
tokstart);
errcount++;
/* yyerrok; */
}
;
lex_builtin
: LEX_BUILTIN
| LEX_LENGTH
;
function_prologue
: LEX_FUNCTION
{
param_counter = 0;
}
func_name '(' opt_param_list r_paren opt_nls
{
NODE *t;
t = make_param($3);
t->flags |= FUNC;
$$ = append_right(t, $5);
can_return = TRUE;
/* check for duplicate parameter names */
if (dup_parms($$))
errcount++;
}
;
function_body
: l_brace statements r_brace opt_semi opt_nls
{
$$ = $2;
can_return = FALSE;
}
| l_brace r_brace opt_semi opt_nls
{
$$ = node((NODE *) NULL, Node_K_return, (NODE *) NULL);
can_return = FALSE;
}
;
pattern
: exp
{ $$ = $1; }
| exp ',' exp
{ $$ = mkrangenode(node($1, Node_cond_pair, $3)); }
;
regexp
/*
* In this rule, want_regexp tells yylex that the next thing
* is a regexp so it should read up to the closing slash.
*/
: '/'
{ ++want_regexp; }
REGEXP '/'
{
NODE *n;
size_t len;
getnode(n);
n->type = Node_regex;
len = strlen($3);
n->re_exp = make_string($3, len);
n->re_reg = make_regexp($3, len, FALSE, TRUE);
n->re_text = NULL;
n->re_flags = CONST;
n->re_cnt = 1;
$$ = n;
}
;
action
: l_brace statements r_brace opt_semi opt_nls
{ $$ = $2; }
| l_brace r_brace opt_semi opt_nls
{ $$ = NULL; }
;
statements
: statement
{
$$ = $1;
if (do_lint && isnoeffect($$->type))
lintwarn(_("statement may have no effect"));
}
| statements statement
{
if ($1 == NULL || $1->type != Node_statement_list)
$1 = node($1, Node_statement_list, (NODE *) NULL);
$$ = append_right($1,
node($2, Node_statement_list, (NODE *) NULL));
yyerrok;
}
| error
{ $$ = NULL; }
| statements error
{ $$ = NULL; }
;
statement_term
: nls
| semi opt_nls
;
statement
: semi opt_nls
{ $$ = NULL; }
| l_brace r_brace
{ $$ = NULL; }
| l_brace statements r_brace
{ $$ = $2; }
| if_statement
{ $$ = $1; }
| LEX_WHILE '(' exp r_paren opt_nls statement
{ $$ = node($3, Node_K_while, $6); }
| LEX_DO opt_nls statement LEX_WHILE '(' exp r_paren opt_nls
{ $$ = node($6, Node_K_do, $3); }
| LEX_FOR '(' NAME LEX_IN NAME r_paren opt_nls statement
{
/*
* Efficiency hack. Recognize the special case of
*
* for (iggy in foo)
* delete foo[iggy]
*
* and treat it as if it were
*
* delete foo
*
* Check that the body is a `delete a[i]' statement,
* and that both the loop var and array names match.
*/
if ($8 != NULL && $8->type == Node_K_delete
&& $8->rnode != NULL
&& ($8->rnode->type == Node_var || $8->rnode->type == Node_param_list)
&& strcmp($3, $8->rnode->var_value->vname) == 0
&& strcmp($5, $8->lnode->vname) == 0) {
$8->type = Node_K_delete_loop;
$$ = $8;
} else {
$$ = node($8, Node_K_arrayfor,
make_for_loop(variable($3, CAN_FREE, Node_var),
(NODE *) NULL, variable($5, CAN_FREE, Node_var_array)));
}
}
| LEX_FOR '(' opt_exp semi opt_nls exp semi opt_nls opt_exp r_paren opt_nls statement
{
$$ = node($12, Node_K_for, (NODE *) make_for_loop($3, $6, $9));
}
| LEX_FOR '(' opt_exp semi opt_nls semi opt_nls opt_exp r_paren opt_nls statement
{
$$ = node($11, Node_K_for,
(NODE *) make_for_loop($3, (NODE *) NULL, $8));
}
| LEX_BREAK statement_term
/* for break, maybe we'll have to remember where to break to */
{ $$ = node((NODE *) NULL, Node_K_break, (NODE *) NULL); }
| LEX_CONTINUE statement_term
/* similarly */
{ $$ = node((NODE *) NULL, Node_K_continue, (NODE *) NULL); }
| print '(' expression_list r_paren output_redir statement_term
{
$$ = node($3, $1, $5);
if ($$->type == Node_K_printf)
count_args($$);
}
| print opt_rexpression_list output_redir statement_term
{
if ($1 == Node_K_print && $2 == NULL) {
static int warned = FALSE;
$2 = node(node(make_number(0.0),
Node_field_spec,
(NODE *) NULL),
Node_expression_list,
(NODE *) NULL);
if (do_lint && ! io_allowed && ! warned) {
warned = TRUE;
lintwarn(
_("plain `print' in BEGIN or END rule should probably be `print \"\"'"));
}
}
$$ = node($2, $1, $3);
if ($$->type == Node_K_printf)
count_args($$);
}
| LEX_NEXT statement_term
{ NODETYPE type;
if (! io_allowed)
yyerror(_("`next' used in BEGIN or END action"));
type = Node_K_next;
$$ = node((NODE *) NULL, type, (NODE *) NULL);
}
| LEX_NEXTFILE statement_term
{
if (do_lint)
lintwarn(_("`nextfile' is a gawk extension"));
if (do_traditional) {
/*
* can't use yyerror, since may have overshot
* the source line
*/
errcount++;
error(_("`nextfile' is a gawk extension"));
}
if (! io_allowed) {
/* same thing */
errcount++;
error(_("`nextfile' used in BEGIN or END action"));
}
$$ = node((NODE *) NULL, Node_K_nextfile, (NODE *) NULL);
}
| LEX_EXIT opt_exp statement_term
{ $$ = node($2, Node_K_exit, (NODE *) NULL); }
| LEX_RETURN
{
if (! can_return)
yyerror(_("`return' used outside function context"));
}
opt_exp statement_term
{ $$ = node($3, Node_K_return, (NODE *) NULL); }
| LEX_DELETE NAME '[' expression_list ']' statement_term
{ $$ = node(variable($2, CAN_FREE, Node_var_array), Node_K_delete, $4); }
| LEX_DELETE NAME statement_term
{
if (do_lint)
lintwarn(_("`delete array' is a gawk extension"));
if (do_traditional) {
/*
* can't use yyerror, since may have overshot
* the source line
*/
errcount++;
error(_("`delete array' is a gawk extension"));
}
$$ = node(variable($2, CAN_FREE, Node_var_array), Node_K_delete, (NODE *) NULL);
}
| exp statement_term
{ $$ = $1; }
;
print
: LEX_PRINT
{ $$ = $1; }
| LEX_PRINTF
{ $$ = $1; }
;
if_statement
: LEX_IF '(' exp r_paren opt_nls statement
{
$$ = node($3, Node_K_if,
node($6, Node_if_branches, (NODE *) NULL));
}
| LEX_IF '(' exp r_paren opt_nls statement
LEX_ELSE opt_nls statement
{ $$ = node($3, Node_K_if,
node($6, Node_if_branches, $9)); }
;
nls
: NEWLINE
{ want_assign = FALSE; }
| nls NEWLINE
;
opt_nls
: /* empty */
| nls
;
input_redir
: /* empty */
{ $$ = NULL; }
| '<' simp_exp
{ $$ = node($2, Node_redirect_input, (NODE *) NULL); }
;
output_redir
: /* empty */
{ $$ = NULL; }
| '>' exp
{ $$ = node($2, Node_redirect_output, (NODE *) NULL); }
| APPEND_OP exp
{ $$ = node($2, Node_redirect_append, (NODE *) NULL); }
| '|' exp
{ $$ = node($2, Node_redirect_pipe, (NODE *) NULL); }
| TWOWAYIO exp
{
if ($2->type == Node_K_getline
&& $2->rnode->type == Node_redirect_twoway)
yyerror(_("multistage two-way pipelines don't work"));
$$ = node($2, Node_redirect_twoway, (NODE *) NULL);
}
;
opt_param_list
: /* empty */
{ $$ = NULL; }
| param_list
{ $$ = $1; }
;
param_list
: NAME
{ $$ = make_param($1); }
| param_list comma NAME
{ $$ = append_right($1, make_param($3)); yyerrok; }
| error
{ $$ = NULL; }
| param_list error
{ $$ = NULL; }
| param_list comma error
{ $$ = NULL; }
;
/* optional expression, as in for loop */
opt_exp
: /* empty */
{ $$ = NULL; }
| exp
{ $$ = $1; }
;
opt_rexpression_list
: /* empty */
{ $$ = NULL; }
| rexpression_list
{ $$ = $1; }
;
rexpression_list
: rexp
{ $$ = node($1, Node_expression_list, (NODE *) NULL); }
| rexpression_list comma rexp
{
$$ = append_right($1,
node($3, Node_expression_list, (NODE *) NULL));
yyerrok;
}
| error
{ $$ = NULL; }
| rexpression_list error
{ $$ = NULL; }
| rexpression_list error rexp
{ $$ = NULL; }
| rexpression_list comma error
{ $$ = NULL; }
;
opt_expression_list
: /* empty */
{ $$ = NULL; }
| expression_list
{ $$ = $1; }
;
expression_list
: exp
{ $$ = node($1, Node_expression_list, (NODE *) NULL); }
| expression_list comma exp
{
$$ = append_right($1,
node($3, Node_expression_list, (NODE *) NULL));
yyerrok;
}
| error
{ $$ = NULL; }
| expression_list error
{ $$ = NULL; }
| expression_list error exp
{ $$ = NULL; }
| expression_list comma error
{ $$ = NULL; }
;
/* Expressions, not including the comma operator. */
exp : variable ASSIGNOP
{ want_assign = FALSE; }
exp
{
if (do_lint && $4->type == Node_regex)
lintwarn(_("regular expression on right of assignment"));
$$ = node($1, $2, $4);
}
| '(' expression_list r_paren LEX_IN NAME
{ $$ = node(variable($5, CAN_FREE, Node_var_array), Node_in_array, $2); }
| exp '|' LEX_GETLINE opt_variable
{
$$ = node($4, Node_K_getline,
node($1, Node_redirect_pipein, (NODE *) NULL));
}
| exp TWOWAYIO LEX_GETLINE opt_variable
{
$$ = node($4, Node_K_getline,
node($1, Node_redirect_twoway, (NODE *) NULL));
}
| LEX_GETLINE opt_variable input_redir
{
if (do_lint && ! io_allowed && parsing_end_rule && $3 == NULL)
lintwarn(_("non-redirected `getline' undefined inside END action"));
$$ = node($2, Node_K_getline, $3);
}
| exp LEX_AND exp
{ $$ = node($1, Node_and, $3); }
| exp LEX_OR exp
{ $$ = node($1, Node_or, $3); }
| exp MATCHOP exp
{
if ($1->type == Node_regex)
warning(_("regular expression on left of `~' or `!~' operator"));
$$ = node($1, $2, mk_rexp($3));
}
| regexp
{
$$ = $1;
if (do_lint && tokstart[0] == '*') {
/* possible C comment */
int n = strlen(tokstart) - 1;
if (tokstart[n] == '*')
lintwarn(_("regexp constant `/%s/' looks like a C comment, but is not"), tokstart);
}
}
| '!' regexp %prec UNARY
{
$$ = node(node(make_number(0.0),
Node_field_spec,
(NODE *) NULL),
Node_nomatch,
$2);
}
| exp LEX_IN NAME
{ $$ = node(variable($3, CAN_FREE, Node_var_array), Node_in_array, $1); }
| exp RELOP exp
{
if (do_lint && $3->type == Node_regex)
lintwarn(_("regular expression on right of comparison"));
$$ = node($1, $2, $3);
}
| exp '<' exp
{ $$ = node($1, Node_less, $3); }
| exp '>' exp
{ $$ = node($1, Node_greater, $3); }
| exp '?' exp ':' exp
{ $$ = node($1, Node_cond_exp, node($3, Node_if_branches, $5));}
| simp_exp
{ $$ = $1; }
| exp simp_exp %prec CONCAT_OP
{ $$ = node($1, Node_concat, $2); }
;
rexp
: variable ASSIGNOP
{ want_assign = FALSE; }
rexp
{ $$ = node($1, $2, $4); }
| rexp LEX_AND rexp
{ $$ = node($1, Node_and, $3); }
| rexp LEX_OR rexp
{ $$ = node($1, Node_or, $3); }
| LEX_GETLINE opt_variable input_redir
{
if (do_lint && ! io_allowed && $3 == NULL)
lintwarn(_("non-redirected `getline' undefined inside BEGIN or END action"));
$$ = node($2, Node_K_getline, $3);
}
| regexp
{ $$ = $1; }
| '!' regexp %prec UNARY
{ $$ = node((NODE *) NULL, Node_nomatch, $2); }
| rexp MATCHOP rexp
{ $$ = node($1, $2, mk_rexp($3)); }
| rexp LEX_IN NAME
{ $$ = node(variable($3, CAN_FREE, Node_var_array), Node_in_array, $1); }
| rexp RELOP rexp
{ $$ = node($1, $2, $3); }
| rexp '?' rexp ':' rexp
{ $$ = node($1, Node_cond_exp, node($3, Node_if_branches, $5));}
| simp_exp
{ $$ = $1; }
| rexp simp_exp %prec CONCAT_OP
{ $$ = node($1, Node_concat, $2); }
;
simp_exp
: non_post_simp_exp
/* Binary operators in order of decreasing precedence. */
| simp_exp '^' simp_exp
{ $$ = node($1, Node_exp, $3); }
| simp_exp '*' simp_exp
{ $$ = node($1, Node_times, $3); }
| simp_exp '/' simp_exp
{ $$ = node($1, Node_quotient, $3); }
| simp_exp '%' simp_exp
{ $$ = node($1, Node_mod, $3); }
| simp_exp '+' simp_exp
{ $$ = node($1, Node_plus, $3); }
| simp_exp '-' simp_exp
{ $$ = node($1, Node_minus, $3); }
| variable INCREMENT
{ $$ = node($1, Node_postincrement, (NODE *) NULL); }
| variable DECREMENT
{ $$ = node($1, Node_postdecrement, (NODE *) NULL); }
;
non_post_simp_exp
: '!' simp_exp %prec UNARY
{ $$ = node($2, Node_not, (NODE *) NULL); }
| '(' exp r_paren
{ $$ = $2; }
| LEX_BUILTIN
'(' opt_expression_list r_paren
{ $$ = snode($3, Node_builtin, (int) $1); }
| LEX_LENGTH '(' opt_expression_list r_paren
{ $$ = snode($3, Node_builtin, (int) $1); }
| LEX_LENGTH
{
if (do_lint)
lintwarn(_("call of `length' without parentheses is not portable"));
$$ = snode((NODE *) NULL, Node_builtin, (int) $1);
if (do_posix)
warning(_("call of `length' without parentheses is deprecated by POSIX"));
}
| FUNC_CALL '(' opt_expression_list r_paren
{
$$ = node($3, Node_func_call, make_string($1, strlen($1)));
func_use($1, FUNC_USE);
param_sanity($3);
free($1);
}
| variable
| INCREMENT variable
{ $$ = node($2, Node_preincrement, (NODE *) NULL); }
| DECREMENT variable
{ $$ = node($2, Node_predecrement, (NODE *) NULL); }
| YNUMBER
{ $$ = $1; }
| YSTRING
{ $$ = $1; }
| '-' simp_exp %prec UNARY
{
if ($2->type == Node_val) {
$2->numbr = -(force_number($2));
$$ = $2;
} else
$$ = node($2, Node_unary_minus, (NODE *) NULL);
}
| '+' simp_exp %prec UNARY
{
/*
* was: $$ = $2
* POSIX semantics: force a conversion to numeric type
*/
$$ = node (make_number(0.0), Node_plus, $2);
}
;
opt_variable
: /* empty */
{ $$ = NULL; }
| variable
{ $$ = $1; }
;
variable
: NAME
{ $$ = variable($1, CAN_FREE, Node_var); }
| NAME '[' expression_list ']'
{
if ($3 == NULL) {
fatal(_("invalid subscript expression"));
} else if ($3->rnode == NULL) {
$$ = node(variable($1, CAN_FREE, Node_var_array), Node_subscript, $3->lnode);
freenode($3);
} else
$$ = node(variable($1, CAN_FREE, Node_var_array), Node_subscript, $3);
}
| '$' non_post_simp_exp
{ $$ = node($2, Node_field_spec, (NODE *) NULL); }
;
l_brace
: '{' opt_nls
;
r_brace
: '}' opt_nls { yyerrok; }
;
r_paren
: ')' { yyerrok; }
;
opt_semi
: /* empty */
| semi
;
semi
: ';' { yyerrok; want_assign = FALSE; }
;
comma : ',' opt_nls { yyerrok; }
;
%%
struct token {
const char *operator; /* text to match */
NODETYPE value; /* node type */
int class; /* lexical class */
unsigned flags; /* # of args. allowed and compatability */
# define ARGS 0xFF /* 0, 1, 2, 3 args allowed (any combination */
# define A(n) (1<<(n))
# define VERSION_MASK 0xFF00 /* old awk is zero */
# define NOT_OLD 0x0100 /* feature not in old awk */
# define NOT_POSIX 0x0200 /* feature not in POSIX */
# define GAWKX 0x0400 /* gawk extension */
# define RESX 0x0800 /* Bell Labs Research extension */
NODE *(*ptr)(); /* function that implements this keyword */
};
/* Tokentab is sorted ascii ascending order, so it can be binary searched. */
/* Function pointers come from declarations in awk.h. */
static struct token tokentab[] = {
{"BEGIN", Node_illegal, LEX_BEGIN, 0, 0},
{"END", Node_illegal, LEX_END, 0, 0},
#ifdef ARRAYDEBUG
{"adump", Node_builtin, LEX_BUILTIN, GAWKX|A(1), do_adump},
#endif
{"and", Node_builtin, LEX_BUILTIN, GAWKX|A(2), do_and},
{"asort", Node_builtin, LEX_BUILTIN, GAWKX|A(1)|A(2), do_asort},
{"atan2", Node_builtin, LEX_BUILTIN, NOT_OLD|A(2), do_atan2},
{"bindtextdomain", Node_builtin, LEX_BUILTIN, GAWKX|A(1)|A(2), do_bindtextdomain},
{"break", Node_K_break, LEX_BREAK, 0, 0},
{"close", Node_builtin, LEX_BUILTIN, NOT_OLD|A(1)|A(2), do_close},
{"compl", Node_builtin, LEX_BUILTIN, GAWKX|A(1), do_compl},
{"continue", Node_K_continue, LEX_CONTINUE, 0, 0},
{"cos", Node_builtin, LEX_BUILTIN, NOT_OLD|A(1), do_cos},
{"dcgettext", Node_builtin, LEX_BUILTIN, GAWKX|A(1)|A(2)|A(3), do_dcgettext},
{"delete", Node_K_delete, LEX_DELETE, NOT_OLD, 0},
{"do", Node_K_do, LEX_DO, NOT_OLD, 0},
{"else", Node_illegal, LEX_ELSE, 0, 0},
{"exit", Node_K_exit, LEX_EXIT, 0, 0},
{"exp", Node_builtin, LEX_BUILTIN, A(1), do_exp},
{"extension", Node_builtin, LEX_BUILTIN, GAWKX|A(2), do_ext},
{"fflush", Node_builtin, LEX_BUILTIN, RESX|A(0)|A(1), do_fflush},
{"for", Node_K_for, LEX_FOR, 0, 0},
{"func", Node_K_function, LEX_FUNCTION, NOT_POSIX|NOT_OLD, 0},
{"function", Node_K_function, LEX_FUNCTION, NOT_OLD, 0},
{"gensub", Node_builtin, LEX_BUILTIN, GAWKX|A(3)|A(4), do_gensub},
{"getline", Node_K_getline, LEX_GETLINE, NOT_OLD, 0},
{"gsub", Node_builtin, LEX_BUILTIN, NOT_OLD|A(2)|A(3), do_gsub},
{"if", Node_K_if, LEX_IF, 0, 0},
{"in", Node_illegal, LEX_IN, 0, 0},
{"index", Node_builtin, LEX_BUILTIN, A(2), do_index},
{"int", Node_builtin, LEX_BUILTIN, A(1), do_int},
{"length", Node_builtin, LEX_LENGTH, A(0)|A(1), do_length},
{"log", Node_builtin, LEX_BUILTIN, A(1), do_log},
{"lshift", Node_builtin, LEX_BUILTIN, GAWKX|A(2), do_lshift},
{"match", Node_builtin, LEX_BUILTIN, NOT_OLD|A(2)|A(3), do_match},
{"mktime", Node_builtin, LEX_BUILTIN, GAWKX|A(1), do_mktime},
{"next", Node_K_next, LEX_NEXT, 0, 0},
{"nextfile", Node_K_nextfile, LEX_NEXTFILE, GAWKX, 0},
{"or", Node_builtin, LEX_BUILTIN, GAWKX|A(2), do_or},
{"print", Node_K_print, LEX_PRINT, 0, 0},
{"printf", Node_K_printf, LEX_PRINTF, 0, 0},
{"rand", Node_builtin, LEX_BUILTIN, NOT_OLD|A(0), do_rand},
{"return", Node_K_return, LEX_RETURN, NOT_OLD, 0},
{"rshift", Node_builtin, LEX_BUILTIN, GAWKX|A(2), do_rshift},
{"sin", Node_builtin, LEX_BUILTIN, NOT_OLD|A(1), do_sin},
{"split", Node_builtin, LEX_BUILTIN, A(2)|A(3), do_split},
{"sprintf", Node_builtin, LEX_BUILTIN, 0, do_sprintf},
{"sqrt", Node_builtin, LEX_BUILTIN, A(1), do_sqrt},
{"srand", Node_builtin, LEX_BUILTIN, NOT_OLD|A(0)|A(1), do_srand},
#if defined(GAWKDEBUG) || defined(ARRAYDEBUG) /* || ... */
{"stopme", Node_builtin, LEX_BUILTIN, GAWKX|A(0), stopme},
#endif
{"strftime", Node_builtin, LEX_BUILTIN, GAWKX|A(0)|A(1)|A(2), do_strftime},
{"strtonum", Node_builtin, LEX_BUILTIN, GAWKX|A(1), do_strtonum},
{"sub", Node_builtin, LEX_BUILTIN, NOT_OLD|A(2)|A(3), do_sub},
{"substr", Node_builtin, LEX_BUILTIN, A(2)|A(3), do_substr},
{"system", Node_builtin, LEX_BUILTIN, NOT_OLD|A(1), do_system},
{"systime", Node_builtin, LEX_BUILTIN, GAWKX|A(0), do_systime},
{"tolower", Node_builtin, LEX_BUILTIN, NOT_OLD|A(1), do_tolower},
{"toupper", Node_builtin, LEX_BUILTIN, NOT_OLD|A(1), do_toupper},
{"while", Node_K_while, LEX_WHILE, 0, 0},
{"xor", Node_builtin, LEX_BUILTIN, GAWKX|A(2), do_xor},
};
/* getfname --- return name of a builtin function (for pretty printing) */
const char *
getfname(register NODE *(*fptr)())
{
register int i, j;
j = sizeof(tokentab) / sizeof(tokentab[0]);
/* linear search, no other way to do it */
for (i = 0; i < j; i++)
if (tokentab[i].ptr == fptr)
return tokentab[i].operator;
fatal(_("fptr %x not in tokentab\n"), fptr);
return NULL; /* to stop warnings */
}
/* yyerror --- print a syntax error message, show where */
/*
* Function identifier purposely indented to avoid mangling
* by ansi2knr. Sigh.
*/
static void
#if defined(HAVE_STDARG_H) && defined(__STDC__) && __STDC__
yyerror(const char *m, ...)
#else
/* VARARGS0 */
yyerror(va_alist)
va_dcl
#endif
{
va_list args;
const char *mesg = NULL;
register char *bp, *cp;
char *scan;
char buf[120];
static char end_of_file_line[] = "(END OF FILE)";
errcount++;
/* Find the current line in the input file */
if (lexptr && lexeme) {
if (thisline == NULL) {
cp = lexeme;
if (*cp == '\n') {
cp--;
mesg = _("unexpected newline");
}
for (; cp != lexptr_begin && *cp != '\n'; --cp)
continue;
if (*cp == '\n')
cp++;
thisline = cp;
}
/* NL isn't guaranteed */
bp = lexeme;
while (bp < lexend && *bp && *bp != '\n')
bp++;
} else {
thisline = end_of_file_line;
bp = thisline + strlen(thisline);
}
msg("%.*s", (int) (bp - thisline), thisline);
bp = buf;
cp = buf + sizeof(buf) - 24; /* 24 more than longest msg. input */
if (lexptr != NULL) {
scan = thisline;
while (bp < cp && scan < lexeme)
if (*scan++ == '\t')
*bp++ = '\t';
else
*bp++ = ' ';
*bp++ = '^';
*bp++ = ' ';
}
#if defined(HAVE_STDARG_H) && defined(__STDC__) && __STDC__
va_start(args, m);
if (mesg == NULL)
mesg = m;
#else
va_start(args);
if (mesg == NULL)
mesg = va_arg(args, char *);
#endif
strcpy(bp, mesg);
err("", buf, args);
va_end(args);
}
/* get_src_buf --- read the next buffer of source program */
static char *
get_src_buf()
{
static int samefile = FALSE;
static int nextfile = 0;
static char *buf = NULL;
static int fd;
int n;
register char *scan;
static int len = 0;
static int did_newline = FALSE;
int newfile;
struct stat sbuf;
# define SLOP 128 /* enough space to hold most source lines */
again:
newfile = FALSE;
if (nextfile > numfiles)
return NULL;
if (srcfiles[nextfile].stype == CMDLINE) {
if (len == 0) {
len = strlen(srcfiles[nextfile].val);
if (len == 0) {
/*
* Yet Another Special case:
* gawk '' /path/name
* Sigh.
*/
static int warned = FALSE;
if (do_lint && ! warned) {
warned = TRUE;
lintwarn(_("empty program text on command line"));
}
++nextfile;
goto again;
}
sourceline = 1;
lexptr = lexptr_begin = srcfiles[nextfile].val;
lexend = lexptr + len;
} else if (! did_newline && *(lexptr-1) != '\n') {
/*
* The following goop is to ensure that the source
* ends with a newline and that the entire current
* line is available for error messages.
*/
int offset;
did_newline = TRUE;
offset = lexptr - lexeme;
for (scan = lexeme; scan > lexptr_begin; scan--)
if (*scan == '\n') {
scan++;
break;
}
len = lexptr - scan;
emalloc(buf, char *, len+1, "get_src_buf");
memcpy(buf, scan, len);
thisline = buf;
lexptr = buf + len;
*lexptr = '\n';
lexeme = lexptr - offset;
lexptr_begin = buf;
lexend = lexptr + 1;
} else {
len = 0;
lexeme = lexptr = lexptr_begin = NULL;
}
if (lexptr == NULL && ++nextfile <= numfiles)
goto again;
return lexptr;
}
if (! samefile) {
source = srcfiles[nextfile].val;
if (source == NULL) {
if (buf != NULL) {
free(buf);
buf = NULL;
}
len = 0;
return lexeme = lexptr = lexptr_begin = NULL;
}
fd = pathopen(source);
if (fd <= INVALID_HANDLE) {
char *in;
/* suppress file name and line no. in error mesg */
in = source;
source = NULL;
fatal(_("can't open source file `%s' for reading (%s)"),
in, strerror(errno));
}
len = optimal_bufsize(fd, & sbuf);
newfile = TRUE;
if (buf != NULL)
free(buf);
emalloc(buf, char *, len + SLOP, "get_src_buf");
lexptr_begin = buf + SLOP;
samefile = TRUE;
sourceline = 1;
} else {
/*
* Here, we retain the current source line (up to length SLOP)
* in the beginning of the buffer that was overallocated above
*/
int offset;
int linelen;
offset = lexptr - lexeme;
for (scan = lexeme; scan > lexptr_begin; scan--)
if (*scan == '\n') {
scan++;
break;
}
linelen = lexptr - scan;
if (linelen > SLOP)
linelen = SLOP;
thisline = buf + SLOP - linelen;
memcpy(thisline, scan, linelen);
lexeme = buf + SLOP - offset;
lexptr_begin = thisline;
}
n = read(fd, buf + SLOP, len);
if (n == -1)
fatal(_("can't read sourcefile `%s' (%s)"),
source, strerror(errno));
if (n == 0) {
if (newfile) {
static int warned = FALSE;
if (do_lint && ! warned) {
warned = TRUE;
lintwarn(_("source file `%s' is empty"), source);
}
}
if (fd != fileno(stdin)) /* safety */
close(fd);
samefile = FALSE;
nextfile++;
if (lexeme)
*lexeme = '\0';
len = 0;
goto again;
}
lexptr = buf + SLOP;
lexend = lexptr + n;
return buf;
}
/* tokadd --- add a character to the token buffer */
#define tokadd(x) (*tok++ = (x), tok == tokend ? tokexpand() : tok)
/* tokexpand --- grow the token buffer */
char *
tokexpand()
{
static int toksize = 60;
int tokoffset;
tokoffset = tok - tokstart;
toksize *= 2;
if (tokstart != NULL)
erealloc(tokstart, char *, toksize, "tokexpand");
else
emalloc(tokstart, char *, toksize, "tokexpand");
tokend = tokstart + toksize;
tok = tokstart + tokoffset;
return tok;
}
/* nextc --- get the next input character */
#if GAWKDEBUG
int
nextc()
{
int c;
if (lexptr && lexptr < lexend)
c = (int) (unsigned char) *lexptr++;
else if (get_src_buf())
c = (int) (unsigned char) *lexptr++;
else
c = EOF;
return c;
}
#else
#define nextc() ((lexptr && lexptr < lexend) ? \
((int) (unsigned char) *lexptr++) : \
(get_src_buf() ? ((int) (unsigned char) *lexptr++) : EOF) \
)
#endif
/* pushback --- push a character back on the input */
#define pushback() (lexptr && lexptr > lexptr_begin ? lexptr-- : lexptr)
/* allow_newline --- allow newline after &&, ||, ? and : */
static void
allow_newline()
{
int c;
for (;;) {
c = nextc();
if (c == EOF)
break;
if (c == '#') {
while ((c = nextc()) != '\n' && c != EOF)
continue;
if (c == EOF)
break;
}
if (c == '\n')
sourceline++;
if (! ISSPACE(c)) {
pushback();
break;
}
}
}
/* yylex --- Read the input and turn it into tokens. */
static int
yylex()
{
register int c;
int seen_e = FALSE; /* These are for numbers */
int seen_point = FALSE;
int esc_seen; /* for literal strings */
int low, mid, high;
static int did_newline = FALSE;
char *tokkey;
static int lasttok = 0, eof_warned = FALSE;
int inhex = FALSE;
int intlstr = FALSE;
if (nextc() == EOF) {
if (lasttok != NEWLINE) {
lasttok = NEWLINE;
if (do_lint && ! eof_warned) {
lintwarn(_("source file does not end in newline"));
eof_warned = TRUE;
}
return NEWLINE; /* fake it */
}
return 0;
}
pushback();
#ifdef OS2
/*
* added for OS/2's extproc feature of cmd.exe
* (like #! in BSD sh)
*/
if (strncasecmp(lexptr, "extproc ", 8) == 0) {
while (*lexptr && *lexptr != '\n')
lexptr++;
}
#endif
lexeme = lexptr;
thisline = NULL;
if (want_regexp) {
int in_brack = 0; /* count brackets, [[:alnum:]] allowed */
/*
* Counting brackets is non-trivial. [[] is ok,
* and so is [\]], with a point being that /[/]/ as a regexp
* constant has to work.
*
* Do not count [ or ] if either one is preceded by a \.
* A `[' should be counted if
* a) it is the first one so far (in_brack == 0)
* b) it is the `[' in `[:'
* A ']' should be counted if not preceded by a \, since
* it is either closing `:]' or just a plain list.
* According to POSIX, []] is how you put a ] into a set.
* Try to handle that too.
*
* The code for \ handles \[ and \].
*/
want_regexp = FALSE;
tok = tokstart;
for (;;) {
c = nextc();
switch (c) {
case '[':
/* one day check for `.' and `=' too */
if (nextc() == ':' || in_brack == 0)
in_brack++;
pushback();
break;
case ']':
if (tokstart[0] == '['
&& (tok == tokstart + 1
|| (tok == tokstart + 2
&& tokstart[1] == '^')))
/* do nothing */;
else
in_brack--;
break;
case '\\':
if ((c = nextc()) == EOF) {
yyerror(_("unterminated regexp ends with `\\' at end of file"));
return lasttok = REGEXP; /* kludge */
} else if (c == '\n') {
sourceline++;
continue;
} else {
tokadd('\\');
tokadd(c);
continue;
}
break;
case '/': /* end of the regexp */
if (in_brack > 0)
break;
pushback();
tokadd('\0');
yylval.sval = tokstart;
return lasttok = REGEXP;
case '\n':
pushback();
yyerror(_("unterminated regexp"));
return lasttok = REGEXP; /* kludge */
case EOF:
yyerror(_("unterminated regexp at end of file"));
return lasttok = REGEXP; /* kludge */
}
tokadd(c);
}
}
retry:
while ((c = nextc()) == ' ' || c == '\t')
continue;
lexeme = lexptr ? lexptr - 1 : lexptr;
thisline = NULL;
tok = tokstart;
yylval.nodetypeval = Node_illegal;
switch (c) {
case EOF:
if (lasttok != NEWLINE) {
lasttok = NEWLINE;
if (do_lint && ! eof_warned) {
lintwarn(_("source file does not end in newline"));
eof_warned = TRUE;
}
return NEWLINE; /* fake it */
}
return 0;
case '\n':
sourceline++;
return lasttok = NEWLINE;
case '#': /* it's a comment */
while ((c = nextc()) != '\n') {
if (c == EOF) {
if (lasttok != NEWLINE) {
lasttok = NEWLINE;
if (do_lint && ! eof_warned) {
lintwarn(
_("source file does not end in newline"));
eof_warned = TRUE;
}
return NEWLINE; /* fake it */
}
return 0;
}
}
sourceline++;
return lasttok = NEWLINE;
case '\\':
#ifdef RELAXED_CONTINUATION
/*
* This code puports to allow comments and/or whitespace
* after the `\' at the end of a line used for continuation.
* Use it at your own risk. We think it's a bad idea, which
* is why it's not on by default.
*/
if (! do_traditional) {
/* strip trailing white-space and/or comment */
while ((c = nextc()) == ' ' || c == '\t')
continue;
if (c == '#') {
if (do_lint)
lintwarn(
_("use of `\\ #...' line continuation is not portable"));
while ((c = nextc()) != '\n')
if (c == EOF)
break;
}
pushback();
}
#endif /* RELAXED_CONTINUATION */
if (nextc() == '\n') {
sourceline++;
goto retry;
} else {
yyerror(_("backslash not last character on line"));
exit(1);
}
break;
case '$':
want_assign = TRUE;
return lasttok = '$';
case ':':
case '?':
if (! do_posix)
allow_newline();
return lasttok = c;
case ')':
case '(':
case ';':
case '{':
case ',':
want_assign = FALSE;
/* fall through */
case '[':
case ']':
return lasttok = c;
case '*':
if ((c = nextc()) == '=') {
yylval.nodetypeval = Node_assign_times;
return lasttok = ASSIGNOP;
} else if (do_posix) {
pushback();
return lasttok = '*';
} else if (c == '*') {
/* make ** and **= aliases for ^ and ^= */
static int did_warn_op = FALSE, did_warn_assgn = FALSE;
if (nextc() == '=') {
if (! did_warn_assgn) {
did_warn_assgn = TRUE;
if (do_lint)
lintwarn(_("POSIX does not allow operator `**='"));
if (do_lint_old)
warning(_("old awk does not support operator `**='"));
}
yylval.nodetypeval = Node_assign_exp;
return ASSIGNOP;
} else {
pushback();
if (! did_warn_op) {
did_warn_op = TRUE;
if (do_lint)
lintwarn(_("POSIX does not allow operator `**'"));
if (do_lint_old)
warning(_("old awk does not support operator `**'"));
}
return lasttok = '^';
}
}
pushback();
return lasttok = '*';
case '/':
if (want_assign) {
if (nextc() == '=') {
yylval.nodetypeval = Node_assign_quotient;
return lasttok = ASSIGNOP;
}
pushback();
}
return lasttok = '/';
case '%':
if (nextc() == '=') {
yylval.nodetypeval = Node_assign_mod;
return lasttok = ASSIGNOP;
}
pushback();
return lasttok = '%';
case '^':
{
static int did_warn_op = FALSE, did_warn_assgn = FALSE;
if (nextc() == '=') {
if (do_lint_old && ! did_warn_assgn) {
did_warn_assgn = TRUE;
warning(_("operator `^=' is not supported in old awk"));
}
yylval.nodetypeval = Node_assign_exp;
return lasttok = ASSIGNOP;
}
pushback();
if (do_lint_old && ! did_warn_op) {
did_warn_op = TRUE;
warning(_("operator `^' is not supported in old awk"));
}
return lasttok = '^';
}
case '+':
if ((c = nextc()) == '=') {
yylval.nodetypeval = Node_assign_plus;
return lasttok = ASSIGNOP;
}
if (c == '+')
return lasttok = INCREMENT;
pushback();
return lasttok = '+';
case '!':
if ((c = nextc()) == '=') {
yylval.nodetypeval = Node_notequal;
return lasttok = RELOP;
}
if (c == '~') {
yylval.nodetypeval = Node_nomatch;
want_assign = FALSE;
return lasttok = MATCHOP;
}
pushback();
return lasttok = '!';
case '<':
if (nextc() == '=') {
yylval.nodetypeval = Node_leq;
return lasttok = RELOP;
}
yylval.nodetypeval = Node_less;
pushback();
return lasttok = '<';
case '=':
if (nextc() == '=') {
yylval.nodetypeval = Node_equal;
return lasttok = RELOP;
}
yylval.nodetypeval = Node_assign;
pushback();
return lasttok = ASSIGNOP;
case '>':
if ((c = nextc()) == '=') {
yylval.nodetypeval = Node_geq;
return lasttok = RELOP;
} else if (c == '>') {
yylval.nodetypeval = Node_redirect_append;
return lasttok = APPEND_OP;
}
yylval.nodetypeval = Node_greater;
pushback();
return lasttok = '>';
case '~':
yylval.nodetypeval = Node_match;
want_assign = FALSE;
return lasttok = MATCHOP;
case '}':
/*
* Added did newline stuff. Easier than
* hacking the grammar.
*/
if (did_newline) {
did_newline = FALSE;
return lasttok = c;
}
did_newline++;
--lexptr; /* pick up } next time */
return lasttok = NEWLINE;
case '"':
string:
esc_seen = FALSE;
while ((c = nextc()) != '"') {
if (c == '\n') {
pushback();
yyerror(_("unterminated string"));
exit(1);
}
if (c == '\\') {
c = nextc();
if (c == '\n') {
sourceline++;
continue;
}
esc_seen = TRUE;
tokadd('\\');
}
if (c == EOF) {
pushback();
yyerror(_("unterminated string"));
exit(1);
}
tokadd(c);
}
yylval.nodeval = make_str_node(tokstart,
tok - tokstart, esc_seen ? SCAN : 0);
yylval.nodeval->flags |= PERM;
if (intlstr) {
yylval.nodeval->flags |= INTLSTR;
intlstr = FALSE;
if (do_intl)
dumpintlstr(yylval.nodeval->stptr,
yylval.nodeval->stlen);
}
return lasttok = YSTRING;
case '-':
if ((c = nextc()) == '=') {
yylval.nodetypeval = Node_assign_minus;
return lasttok = ASSIGNOP;
}
if (c == '-')
return lasttok = DECREMENT;
pushback();
return lasttok = '-';
case '.':
c = nextc();
pushback();
if (! ISDIGIT(c))
return lasttok = '.';
else
c = '.';
/* FALL THROUGH */
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
/* It's a number */
for (;;) {
int gotnumber = FALSE;
tokadd(c);
switch (c) {
case 'x':
case 'X':
if (do_traditional)
goto done;
if (tok == tokstart + 2)
inhex = TRUE;
break;
case '.':
if (seen_point) {
gotnumber = TRUE;
break;
}
seen_point = TRUE;
break;
case 'e':
case 'E':
if (inhex)
break;
if (seen_e) {
gotnumber = TRUE;
break;
}
seen_e = TRUE;
if ((c = nextc()) == '-' || c == '+')
tokadd(c);
else
pushback();
break;
case 'a':
case 'A':
case 'b':
case 'B':
case 'c':
case 'C':
case 'D':
case 'd':
case 'f':
case 'F':
if (do_traditional || ! inhex)
goto done;
/* fall through */
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
break;
default:
done:
gotnumber = TRUE;
}
if (gotnumber)
break;
c = nextc();
}
if (c != EOF)
pushback();
else if (do_lint && ! eof_warned) {
lintwarn(_("source file does not end in newline"));
eof_warned = TRUE;
}
tokadd('\0');
if (! do_traditional && isnondecimal(tokstart))
yylval.nodeval = make_number(nondec2awknum(tokstart, strlen(tokstart)));
else
yylval.nodeval = make_number(atof(tokstart));
yylval.nodeval->flags |= PERM;
return lasttok = YNUMBER;
case '&':
if ((c = nextc()) == '&') {
yylval.nodetypeval = Node_and;
allow_newline();
want_assign = FALSE;
return lasttok = LEX_AND;
}
pushback();
return lasttok = '&';
case '|':
if ((c = nextc()) == '|') {
yylval.nodetypeval = Node_or;
allow_newline();
want_assign = FALSE;
return lasttok = LEX_OR;
} else if (! do_traditional && c == '&') {
yylval.nodetypeval = Node_redirect_twoway;
want_assign = FALSE;
return lasttok = TWOWAYIO;
}
pushback();
return lasttok = '|';
}
if (c != '_' && ! ISALPHA(c)) {
yyerror(_("invalid char '%c' in expression"), c);
exit(1);
}
if (! do_traditional && c == '_') {
if ((c = nextc()) == '"') {
intlstr = TRUE;
goto string;
}
pushback();
c = '_';
}
/* it's some type of name-type-thing. Find its length. */
tok = tokstart;
while (is_identchar(c)) {
tokadd(c);
c = nextc();
}
tokadd('\0');
emalloc(tokkey, char *, tok - tokstart, "yylex");
memcpy(tokkey, tokstart, tok - tokstart);
if (c != EOF)
pushback();
else if (do_lint && ! eof_warned) {
lintwarn(_("source file does not end in newline"));
eof_warned = TRUE;
}
/* See if it is a special token. */
low = 0;
high = (sizeof(tokentab) / sizeof(tokentab[0])) - 1;
while (low <= high) {
int i;
mid = (low + high) / 2;
c = *tokstart - tokentab[mid].operator[0];
i = c ? c : strcmp(tokstart, tokentab[mid].operator);
if (i < 0) /* token < mid */
high = mid - 1;
else if (i > 0) /* token > mid */
low = mid + 1;
else {
if (do_lint) {
if (tokentab[mid].flags & GAWKX)
lintwarn(_("`%s' is a gawk extension"),
tokentab[mid].operator);
if (tokentab[mid].flags & RESX)
lintwarn(_("`%s' is a Bell Labs extension"),
tokentab[mid].operator);
if (tokentab[mid].flags & NOT_POSIX)
lintwarn(_("POSIX does not allow `%s'"),
tokentab[mid].operator);
}
if (do_lint_old && (tokentab[mid].flags & NOT_OLD))
warning(_("`%s' is not supported in old awk"),
tokentab[mid].operator);
if ((do_traditional && (tokentab[mid].flags & GAWKX))
|| (do_posix && (tokentab[mid].flags & NOT_POSIX)))
break;
if (tokentab[mid].class == LEX_BUILTIN
|| tokentab[mid].class == LEX_LENGTH
)
yylval.lval = mid;
else
yylval.nodetypeval = tokentab[mid].value;
free(tokkey);
return lasttok = tokentab[mid].class;
}
}
yylval.sval = tokkey;
if (*lexptr == '(')
return lasttok = FUNC_CALL;
else {
static short goto_warned = FALSE;
want_assign = TRUE;
#define SMART_ALECK 1
if (SMART_ALECK && do_lint
&& ! goto_warned && strcasecmp(tokkey, "goto") == 0) {
goto_warned = TRUE;
lintwarn(_("`goto' considered harmful!\n"));
}
return lasttok = NAME;
}
}
/* node_common --- common code for allocating a new node */
static NODE *
node_common(NODETYPE op)
{
register NODE *r;
getnode(r);
r->type = op;
r->flags = MALLOC;
if (r->type == Node_var)
r->flags |= UNINITIALIZED;
/* if lookahead is NL, lineno is 1 too high */
if (lexeme && *lexeme == '\n')
r->source_line = sourceline - 1;
else
r->source_line = sourceline;
r->source_file = source;
return r;
}
/* node --- allocates a node with defined lnode and rnode. */
NODE *
node(NODE *left, NODETYPE op, NODE *right)
{
register NODE *r;
r = node_common(op);
r->lnode = left;
r->rnode = right;
return r;
}
/* snode --- allocate a node with defined subnode and proc for builtin
functions. Checks for arg. count and supplies defaults where
possible. */
static NODE *
snode(NODE *subn, NODETYPE op, int idx)
{
register NODE *r;
register NODE *n;
int nexp = 0;
int args_allowed;
r = node_common(op);
/* traverse expression list to see how many args. given */
for (n = subn; n != NULL; n = n->rnode) {
nexp++;
if (nexp > 3)
break;
}
/* check against how many args. are allowed for this builtin */
args_allowed = tokentab[idx].flags & ARGS;
if (args_allowed && (args_allowed & A(nexp)) == 0)
fatal(_("%d is invalid as number of arguments for %s"),
nexp, tokentab[idx].operator);
r->proc = tokentab[idx].ptr;
/* special case processing for a few builtins */
if (nexp == 0 && r->proc == do_length) {
subn = node(node(make_number(0.0), Node_field_spec, (NODE *) NULL),
Node_expression_list,
(NODE *) NULL);
} else if (r->proc == do_match) {
static short warned = FALSE;
if (subn->rnode->lnode->type != Node_regex)
subn->rnode->lnode = mk_rexp(subn->rnode->lnode);
if (subn->rnode->rnode != NULL) { /* 3rd argument there */
if (do_lint && ! warned) {
warned = TRUE;
lintwarn(_("match: third argument is a gawk extension"));
}
if (do_traditional)
fatal(_("match: third argument is a gawk extension"));
}
} else if (r->proc == do_sub || r->proc == do_gsub) {
if (subn->lnode->type != Node_regex)
subn->lnode = mk_rexp(subn->lnode);
if (nexp == 2)
append_right(subn, node(node(make_number(0.0),
Node_field_spec,
(NODE *) NULL),
Node_expression_list,
(NODE *) NULL));
else if (subn->rnode->rnode->lnode->type == Node_val) {
if (do_lint) {
char *f;
f = (r->proc == do_sub) ? "sub" : "gsub";
lintwarn(_("%s: string literal as last arg of substitute has no effect"), f);
}
} else if (! isassignable(subn->rnode->rnode->lnode)) {
if (r->proc == do_sub)
yyerror(_("sub third parameter is not a changeable object"));
else
yyerror(_("gsub third parameter is not a changeable object"));
}
} else if (r->proc == do_gensub) {
if (subn->lnode->type != Node_regex)
subn->lnode = mk_rexp(subn->lnode);
if (nexp == 3)
append_right(subn, node(node(make_number(0.0),
Node_field_spec,
(NODE *) NULL),
Node_expression_list,
(NODE *) NULL));
} else if (r->proc == do_split) {
if (nexp == 2)
append_right(subn,
node(FS_node, Node_expression_list, (NODE *) NULL));
n = subn->rnode->rnode->lnode;
if (n->type != Node_regex)
subn->rnode->rnode->lnode = mk_rexp(n);
if (nexp == 2)
subn->rnode->rnode->lnode->re_flags |= FS_DFLT;
} else if (r->proc == do_close) {
static short warned = FALSE;
if ( nexp == 2) {
if (do_lint && nexp == 2 && ! warned) {
warned = TRUE;
lintwarn(_("close: second argument is a gawk extension"));
}
if (do_traditional)
fatal(_("close: second argument is a gawk extension"));
}
} else if (do_intl /* --gen-po */
&& r->proc == do_dcgettext /* dcgettext(...) */
&& subn->lnode->type == Node_val /* 1st arg is constant */
&& (subn->lnode->flags & STR) != 0) { /* it's a string constant */
/* ala xgettext, dcgettext("some string" ...) dumps the string */
NODE *str = subn->lnode;
if ((str->flags & INTLSTR) != 0)
warning(_("use of dcgettext(_\"...\") is incorrect: remove leading underscore"));
/* don't dump it, the lexer already did */
else
dumpintlstr(str->stptr, str->stlen);
}
r->subnode = subn;
if (r->proc == do_sprintf) {
count_args(r);
r->lnode->printf_count = r->printf_count; /* hack */
}
return r;
}
/*
* mkrangenode:
* This allocates a Node_line_range node with defined condpair and
* zeroes the trigger word to avoid the temptation of assuming that calling
* 'node( foo, Node_line_range, 0)' will properly initialize 'triggered'.
* Otherwise like node().
*/
static NODE *
mkrangenode(NODE *cpair)
{
register NODE *r;
getnode(r);
r->type = Node_line_range;
r->condpair = cpair;
r->triggered = FALSE;
return r;
}
/* make_for_loop --- build a for loop */
static NODE *
make_for_loop(NODE *init, NODE *cond, NODE *incr)
{
register FOR_LOOP_HEADER *r;
NODE *n;
emalloc(r, FOR_LOOP_HEADER *, sizeof(FOR_LOOP_HEADER), "make_for_loop");
getnode(n);
n->type = Node_illegal;
r->init = init;
r->cond = cond;
r->incr = incr;
n->sub.nodep.r.hd = r;
return n;
}
/* dup_parms --- return TRUE if there are duplicate parameters */
static int
dup_parms(NODE *func)
{
register NODE *np;
char *fname, **names;
int count, i, j, dups;
NODE *params;
if (func == NULL) /* error earlier */
return TRUE;
fname = func->param;
count = func->param_cnt;
params = func->rnode;
if (count == 0) /* no args, no problem */
return FALSE;
if (params == NULL) /* error earlier */
return TRUE;
emalloc(names, char **, count * sizeof(char *), "dup_parms");
i = 0;
for (np = params; np != NULL; np = np->rnode) {
if (np->param == NULL) { /* error earlier, give up, go home */
free(names);
return TRUE;
}
names[i++] = np->param;
}
dups = 0;
for (i = 1; i < count; i++) {
for (j = 0; j < i; j++) {
if (strcmp(names[i], names[j]) == 0) {
dups++;
error(
_("function `%s': parameter #%d, `%s', duplicates parameter #%d"),
fname, i+1, names[j], j+1);
}
}
}
free(names);
return (dups > 0 ? TRUE : FALSE);
}
/* parms_shadow --- check if parameters shadow globals */
static void
parms_shadow(const char *fname, NODE *func)
{
int count, i;
if (fname == NULL || func == NULL) /* error earlier */
return;
count = func->lnode->param_cnt;
if (count == 0) /* no args, no problem */
return;
/*
* Use warning() and not lintwarn() so that can warn
* about all shadowed parameters.
*/
for (i = 0; i < count; i++) {
if (lookup(func->parmlist[i]) != NULL) {
warning(
_("function `%s': parameter `%s' shadows global variable"),
fname, func->parmlist[i]);
}
}
}
/*
* install:
* Install a name in the symbol table, even if it is already there.
* Caller must check against redefinition if that is desired.
*/
NODE *
install(char *name, NODE *value)
{
register NODE *hp;
register size_t len;
register int bucket;
var_count++;
len = strlen(name);
bucket = hash(name, len, (unsigned long) HASHSIZE);
getnode(hp);
hp->type = Node_hashnode;
hp->hnext = variables[bucket];
variables[bucket] = hp;
hp->hlength = len;
hp->hvalue = value;
hp->hname = name;
hp->hvalue->vname = name;
return hp->hvalue;
}
/* lookup --- find the most recent hash node for name installed by install */
NODE *
lookup(const char *name)
{
register NODE *bucket;
register size_t len;
len = strlen(name);
for (bucket = variables[hash(name, len, (unsigned long) HASHSIZE)];
bucket != NULL; bucket = bucket->hnext)
if (bucket->hlength == len && STREQN(bucket->hname, name, len))
return bucket->hvalue;
return NULL;
}
/* var_comp --- compare two variable names */
static int
var_comp(const void *v1, const void *v2)
{
NODE **npp1, **npp2;
NODE *n1, *n2;
int minlen;
npp1 = (NODE **) v1;
npp2 = (NODE **) v2;
n1 = *npp1;
n2 = *npp2;
if (n1->hlength > n2->hlength)
minlen = n1->hlength;
else
minlen = n2->hlength;
return strncmp(n1->hname, n2->hname, minlen);
}
/* valinfo --- dump var info */
static void
valinfo(NODE *n, FILE *fp)
{
if (n->flags & STRING) {
fprintf(fp, "string (");
pp_string_fp(fp, n->stptr, n->stlen, '"', FALSE);
fprintf(fp, ")\n");
} else if (n->flags & NUMBER)
fprintf(fp, "number (%.17g)\n", n->numbr);
else if (n->flags & STR) {
fprintf(fp, "string value (");
pp_string_fp(fp, n->stptr, n->stlen, '"', FALSE);
fprintf(fp, ")\n");
} else if (n->flags & NUM)
fprintf(fp, "number value (%.17g)\n", n->numbr);
else
fprintf(fp, "?? flags %s\n", flags2str(n->flags));
}
/* dump_vars --- dump the symbol table */
void
dump_vars(const char *fname)
{
int i, j;
NODE **table;
NODE *p;
FILE *fp;
emalloc(table, NODE **, var_count * sizeof(NODE *), "dump_vars");
if (fname == NULL)
fp = stderr;
else if ((fp = fopen(fname, "w")) == NULL) {
warning(_("could not open `%s' for writing (%s)"), fname, strerror(errno));
warning(_("sending profile to standard error"));
fp = stderr;
}
for (i = j = 0; i < HASHSIZE; i++)
for (p = variables[i]; p != NULL; p = p->hnext)
table[j++] = p;
assert(j == var_count);
/* Shazzam! */
qsort(table, j, sizeof(NODE *), var_comp);
for (i = 0; i < j; i++) {
p = table[i];
if (p->hvalue->type == Node_func)
continue;
fprintf(fp, "%.*s: ", (int) p->hlength, p->hname);
if (p->hvalue->type == Node_var_array)
fprintf(fp, "array, %ld elements\n", p->hvalue->table_size);
else if (p->hvalue->type == Node_var)
valinfo(p->hvalue->var_value, fp);
else {
NODE **lhs = get_lhs(p->hvalue, NULL, FALSE);
valinfo(*lhs, fp);
}
}
if (fp != stderr && fclose(fp) != 0)
warning(_("%s: close failed (%s)"), fname, strerror(errno));
free(table);
}
/* release_all_vars --- free all variable memory */
void
release_all_vars()
{
int i;
NODE *p, *next;
for (i = 0; i < HASHSIZE; i++)
for (p = variables[i]; p != NULL; p = next) {
next = p->hnext;
if (p->hvalue->type == Node_func)
continue;
else if (p->hvalue->type == Node_var_array)
assoc_clear(p->hvalue);
else if (p->hvalue->type == Node_var)
unref(p->hvalue->var_value);
else {
NODE **lhs = get_lhs(p->hvalue, NULL, FALSE);
unref((*lhs)->var_value);
}
unref(p);
}
}
/* finfo --- for use in comparison and sorting of function names */
struct finfo {
char *name;
size_t nlen;
NODE *func;
};
/* fcompare --- comparison function for qsort */
static int
fcompare(const void *p1, const void *p2)
{
struct finfo *f1, *f2;
int minlen;
f1 = (struct finfo *) p1;
f2 = (struct finfo *) p2;
if (f1->nlen > f2->nlen)
minlen = f2->nlen;
else
minlen = f1->nlen;
return strncmp(f1->name, f2->name, minlen);
}
/* dump_funcs --- print all functions */
void
dump_funcs()
{
int i, j;
NODE *p;
static struct finfo *tab = NULL;
if (func_count == 0)
return;
if (tab == NULL)
emalloc(tab, struct finfo *, func_count * sizeof(struct finfo), "dump_funcs");
for (i = j = 0; i < HASHSIZE; i++) {
for (p = variables[i]; p != NULL; p = p->hnext) {
if (p->hvalue->type == Node_func) {
tab[j].name = p->hname;
tab[j].nlen = p->hlength;
tab[j].func = p->hvalue;
j++;
}
}
}
assert(j == func_count);
/* Shazzam! */
qsort(tab, func_count, sizeof(struct finfo), fcompare);
for (i = 0; i < j; i++)
pp_func(tab[i].name, tab[i].nlen, tab[i].func);
free(tab);
}
/* shadow_funcs --- check all functions for parameters that shadow globals */
void
shadow_funcs()
{
int i, j;
NODE *p;
struct finfo *tab;
static int calls = 0;
if (func_count == 0)
return;
if (calls++ != 0)
fatal(_("shadow_funcs() called twice!"));
emalloc(tab, struct finfo *, func_count * sizeof(struct finfo), "shadow_funcs");
for (i = j = 0; i < HASHSIZE; i++) {
for (p = variables[i]; p != NULL; p = p->hnext) {
if (p->hvalue->type == Node_func) {
tab[j].name = p->hname;
tab[j].nlen = p->hlength;
tab[j].func = p->hvalue;
j++;
}
}
}
assert(j == func_count);
/* Shazzam! */
qsort(tab, func_count, sizeof(struct finfo), fcompare);
for (i = 0; i < j; i++)
parms_shadow(tab[i].name, tab[i].func);
free(tab);
}
/*
* append_right:
* Add new to the rightmost branch of LIST. This uses n^2 time, so we make
* a simple attempt at optimizing it.
*/
static NODE *
append_right(NODE *list, NODE *new)
{
register NODE *oldlist;
static NODE *savefront = NULL, *savetail = NULL;
if (list == NULL || new == NULL)
return list;
oldlist = list;
if (savefront == oldlist) {
savetail = savetail->rnode = new;
return oldlist;
} else
savefront = oldlist;
while (list->rnode != NULL)
list = list->rnode;
savetail = list->rnode = new;
return oldlist;
}
/*
* func_install:
* check if name is already installed; if so, it had better have Null value,
* in which case def is added as the value. Otherwise, install name with def
* as value.
*
* Extra work, build up and save a list of the parameter names in a table
* and hang it off params->parmlist. This is used to set the `vname' field
* of each function parameter during a function call. See eval.c.
*/
static void
func_install(NODE *params, NODE *def)
{
NODE *r, *n, *thisfunc;
char **pnames, *names, *sp;
size_t pcount = 0, space = 0;
int i;
/* check for function foo(foo) { ... }. bleah. */
for (n = params->rnode; n != NULL; n = n->rnode) {
if (strcmp(n->param, params->param) == 0)
fatal(_("function `%s': can't use function name as parameter name"),
params->param);
}
thisfunc = NULL; /* turn off warnings */
/* symbol table managment */
pop_var(params, FALSE);
r = lookup(params->param);
if (r != NULL) {
fatal(_("function name `%s' previously defined"), params->param);
} else {
thisfunc = node(params, Node_func, def);
(void) install(params->param, thisfunc);
}
/* figure out amount of space to allocate */
for (n = params->rnode; n != NULL; n = n->rnode) {
pcount++;
space += strlen(n->param) + 1;
}
/* allocate it and fill it in */
if (pcount != 0) {
emalloc(names, char *, space, "func_install");
emalloc(pnames, char **, pcount * sizeof(char *), "func_install");
sp = names;
for (i = 0, n = params->rnode; i < pcount; i++, n = n->rnode) {
pnames[i] = sp;
strcpy(sp, n->param);
sp += strlen(n->param) + 1;
}
thisfunc->parmlist = pnames;
} else {
thisfunc->parmlist = NULL;
}
/* remove params from symbol table */
pop_params(params->rnode);
/* update lint table info */
func_use(params->param, FUNC_DEFINE);
func_count++; /* used by profiling / pretty printer */
}
/* pop_var --- remove a variable from the symbol table */
static void
pop_var(NODE *np, int freeit)
{
register NODE *bucket, **save;
register size_t len;
char *name;
name = np->param;
len = strlen(name);
save = &(variables[hash(name, len, (unsigned long) HASHSIZE)]);
for (bucket = *save; bucket != NULL; bucket = bucket->hnext) {
if (len == bucket->hlength && STREQN(bucket->hname, name, len)) {
var_count--;
*save = bucket->hnext;
freenode(bucket);
if (freeit)
free(np->param);
return;
}
save = &(bucket->hnext);
}
}
/* pop_params --- remove list of function parameters from symbol table */
/*
* pop parameters out of the symbol table. do this in reverse order to
* avoid reading freed memory if there were duplicated parameters.
*/
static void
pop_params(NODE *params)
{
if (params == NULL)
return;
pop_params(params->rnode);
pop_var(params, TRUE);
}
/* make_param --- make NAME into a function parameter */
static NODE *
make_param(char *name)
{
NODE *r;
getnode(r);
r->type = Node_param_list;
r->rnode = NULL;
r->param = name;
r->param_cnt = param_counter++;
return (install(name, r));
}
static struct fdesc {
char *name;
short used;
short defined;
struct fdesc *next;
} *ftable[HASHSIZE];
/* func_use --- track uses and definitions of functions */
static void
func_use(char *name, enum defref how)
{
struct fdesc *fp;
int len;
int ind;
len = strlen(name);
ind = hash(name, len, HASHSIZE);
for (fp = ftable[ind]; fp != NULL; fp = fp->next) {
if (strcmp(fp->name, name) == 0) {
if (how == FUNC_DEFINE)
fp->defined++;
else
fp->used++;
return;
}
}
/* not in the table, fall through to allocate a new one */
emalloc(fp, struct fdesc *, sizeof(struct fdesc), "func_use");
memset(fp, '\0', sizeof(struct fdesc));
emalloc(fp->name, char *, len + 1, "func_use");
strcpy(fp->name, name);
if (how == FUNC_DEFINE)
fp->defined++;
else
fp->used++;
fp->next = ftable[ind];
ftable[ind] = fp;
}
/* check_funcs --- verify functions that are called but not defined */
static void
check_funcs()
{
struct fdesc *fp, *next;
int i;
for (i = 0; i < HASHSIZE; i++) {
for (fp = ftable[i]; fp != NULL; fp = fp->next) {
#ifdef REALLYMEAN
/* making this the default breaks old code. sigh. */
if (fp->defined == 0) {
error(
_("function `%s' called but never defined"), fp->name);
errcount++;
}
#else
if (do_lint && fp->defined == 0)
lintwarn(
_("function `%s' called but never defined"), fp->name);
#endif
if (do_lint && fp->used == 0) {
lintwarn(_("function `%s' defined but never called"),
fp->name);
}
}
}
/* now let's free all the memory */
for (i = 0; i < HASHSIZE; i++) {
for (fp = ftable[i]; fp != NULL; fp = next) {
next = fp->next;
free(fp->name);
free(fp);
}
}
}
/* param_sanity --- look for parameters that are regexp constants */
static void
param_sanity(NODE *arglist)
{
NODE *argp, *arg;
int i;
for (i = 1, argp = arglist; argp != NULL; argp = argp->rnode, i++) {
arg = argp->lnode;
if (arg->type == Node_regex)
warning(_("regexp constant for parameter #%d yields boolean value"), i);
}
}
/* variable --- make sure NAME is in the symbol table */
NODE *
variable(char *name, int can_free, NODETYPE type)
{
register NODE *r;
static int env_loaded = FALSE;
static int procinfo_loaded = FALSE;
if (! env_loaded && STREQ(name, "ENVIRON")) {
load_environ();
env_loaded = TRUE;
}
if (! do_traditional && ! procinfo_loaded && STREQ(name, "PROCINFO")) {
load_procinfo();
procinfo_loaded = TRUE;
}
if ((r = lookup(name)) == NULL)
r = install(name, node(Nnull_string, type, (NODE *) NULL));
else if (can_free)
free(name);
return r;
}
/* mk_rexp --- make a regular expression constant */
static NODE *
mk_rexp(NODE *exp)
{
NODE *n;
if (exp->type == Node_regex)
return exp;
getnode(n);
n->type = Node_regex;
n->re_exp = exp;
n->re_text = NULL;
n->re_reg = NULL;
n->re_flags = 0;
n->re_cnt = 1;
return n;
}
/* isnoeffect --- when used as a statement, has no side effects */
/*
* To be completely general, we should recursively walk the parse
* tree, to make sure that all the subexpressions also have no effect.
* Instead, we just weaken the actual warning that's printed, up above
* in the grammar.
*/
static int
isnoeffect(NODETYPE type)
{
switch (type) {
case Node_times:
case Node_quotient:
case Node_mod:
case Node_plus:
case Node_minus:
case Node_subscript:
case Node_concat:
case Node_exp:
case Node_unary_minus:
case Node_field_spec:
case Node_and:
case Node_or:
case Node_equal:
case Node_notequal:
case Node_less:
case Node_greater:
case Node_leq:
case Node_geq:
case Node_match:
case Node_nomatch:
case Node_not:
case Node_val:
case Node_in_array:
case Node_NF:
case Node_NR:
case Node_FNR:
case Node_FS:
case Node_RS:
case Node_FIELDWIDTHS:
case Node_IGNORECASE:
case Node_OFS:
case Node_ORS:
case Node_OFMT:
case Node_CONVFMT:
case Node_BINMODE:
case Node_LINT:
return TRUE;
default:
break; /* keeps gcc -Wall happy */
}
return FALSE;
}
/* isassignable --- can this node be assigned to? */
static int
isassignable(register NODE *n)
{
switch (n->type) {
case Node_var:
case Node_FIELDWIDTHS:
case Node_RS:
case Node_FS:
case Node_FNR:
case Node_NR:
case Node_NF:
case Node_IGNORECASE:
case Node_OFMT:
case Node_CONVFMT:
case Node_ORS:
case Node_OFS:
case Node_LINT:
case Node_BINMODE:
case Node_field_spec:
case Node_subscript:
return TRUE;
case Node_param_list:
return ((n->flags & FUNC) == 0); /* ok if not func name */
default:
break; /* keeps gcc -Wall happy */
}
return FALSE;
}
/* stopme --- for debugging */
NODE *
stopme(NODE *tree)
{
return tmp_number((AWKNUM) 0.0);
}
/* dumpintlstr --- write out an initial .po file entry for the string */
static void
dumpintlstr(char *str, size_t len)
{
char *cp;
/* See the GNU gettext distribution for details on the file format */
if (source != NULL) {
/* ala the gettext sources, remove leading `./'s */
for (cp = source; cp[0] == '.' && cp[1] == '/'; cp += 2)
continue;
printf("#: %s:%d\n", cp, sourceline);
}
printf("msgid ");
fflush(stdout);
pp_string_fp(stdout, str, len, '"', TRUE);
putchar('\n');
printf("msgstr \"\"\n\n");
}
/* count_args --- count the number of printf arguments */
static void
count_args(NODE *tree)
{
size_t count = 0;
NODE *save_tree;
assert(tree->type == Node_K_printf
|| (tree->type == Node_builtin && tree->proc == do_sprintf));
save_tree = tree;
tree = tree->lnode; /* printf format string */
for (count = 0; tree != NULL; tree = tree->rnode)
count++;
save_tree->printf_count = count;
}