aboutsummaryrefslogtreecommitdiffstats
path: root/awkgram.y
diff options
context:
space:
mode:
Diffstat (limited to 'awkgram.y')
-rw-r--r--awkgram.y327
1 files changed, 202 insertions, 125 deletions
diff --git a/awkgram.y b/awkgram.y
index 279b502b..c024afd5 100644
--- a/awkgram.y
+++ b/awkgram.y
@@ -35,6 +35,9 @@
#endif
static void yyerror(const char *m, ...) ATTRIBUTE_PRINTF_1;
+static void error_ln(int line, const char *m, ...) ATTRIBUTE_PRINTF_2;
+static void lintwarn_ln(int line, const char *m, ...) ATTRIBUTE_PRINTF_2;
+static void warning_ln(int line, const char *m, ...) ATTRIBUTE_PRINTF_2;
static char *get_src_buf(void);
static int yylex(void);
int yyparse(void);
@@ -44,7 +47,7 @@ static void pop_params(NODE *params);
static NODE *make_param(char *pname);
static NODE *mk_rexp(INSTRUCTION *exp);
static void append_param(char *pname);
-static int dup_parms(NODE *func);
+static int dup_parms(INSTRUCTION *fp, NODE *func);
static void param_sanity(INSTRUCTION *arglist);
static int parms_shadow(INSTRUCTION *pc, int *shadow);
static int isnoeffect(OPCODE type);
@@ -52,7 +55,7 @@ static INSTRUCTION *make_assignable(INSTRUCTION *ip);
static void dumpintlstr(const char *str, size_t len);
static void dumpintlstr2(const char *str1, size_t len1, const char *str2, size_t len2);
static int isarray(NODE *n);
-static int include_source(char *src);
+static int include_source(INSTRUCTION *file);
static void next_sourcefile(void);
static char *tokexpand(void);
@@ -269,10 +272,9 @@ rule
source
: FILENAME
{
- char *src = $1->lextok;
- if (include_source(src) < 0)
+ if (include_source($1) < 0)
YYABORT;
- efree(src);
+ efree($1->lextok);
bcfree($1);
$$ = NULL;
}
@@ -317,7 +319,8 @@ pattern
{
static int begin_seen = 0;
if (do_lint_old && ++begin_seen == 2)
- warning(_("old awk does not support multiple `BEGIN' or `END' rules"));
+ warning_ln($1->source_line,
+ _("old awk does not support multiple `BEGIN' or `END' rules"));
$1->in_rule = rule = BEGIN;
$1->source_file = source;
@@ -327,7 +330,8 @@ pattern
{
static int end_seen = 0;
if (do_lint_old && ++end_seen == 2)
- warning(_("old awk does not support multiple `BEGIN' or `END' rules"));
+ warning_ln($1->source_line,
+ _("old awk does not support multiple `BEGIN' or `END' rules"));
$1->in_rule = rule = END;
$1->source_file = source;
@@ -402,7 +406,7 @@ function_prologue
$$ = $1;
can_return = TRUE;
/* check for duplicate parameter names */
- if (dup_parms(t))
+ if (dup_parms($1, t))
errcount++;
}
;
@@ -424,10 +428,12 @@ regexp
len = strlen(re);
if (do_lint) {
if (len == 0)
- lintwarn(_("regexp constant `//' looks like a C++ comment, but is not"));
+ lintwarn_ln($3->source_line,
+ _("regexp constant `//' looks like a C++ comment, but is not"));
else if ((re)[0] == '*' && (re)[len-1] == '*')
/* possible C comment */
- lintwarn(_("regexp constant `/%s/' looks like a C comment, but is not"), re);
+ lintwarn_ln($3->source_line,
+ _("regexp constant `/%s/' looks like a C comment, but is not"), re);
}
exp = make_str_node(re, len, ALREADY_MALLOCED);
@@ -517,11 +523,9 @@ statement
char *caseval;
caseval = force_string(caseexp->memory)->stptr;
for (i = 0; i < case_count; i++) {
- if (strcmp(caseval, case_values[i]) == 0) {
- /* can't use yyerror, since may have overshot the source line */
- errcount++;
- error(_("duplicate case values in switch body: %s"), caseval);
- }
+ if (strcmp(caseval, case_values[i]) == 0)
+ error_ln(curr->source_line,
+ _("duplicate case values in switch body: %s"), caseval);
}
if (case_values == NULL)
@@ -540,11 +544,10 @@ statement
(void) list_prepend(cexp, curr);
(void) list_prepend(cexp, caseexp);
} else {
- if (dflt->target_jmp != tbreak) {
- /* can't use yyerror, since may have overshot the source line */
- errcount++;
- error(_("duplicate `default' detected in switch body"));
- } else
+ if (dflt->target_jmp != tbreak)
+ error_ln(curr->source_line,
+ _("duplicate `default' detected in switch body"));
+ else
dflt->target_jmp = casestmt->nexti;
if (do_profiling) {
@@ -804,8 +807,8 @@ non_compound_stmt
: LEX_BREAK statement_term
{
if (! break_allowed)
- yyerror(_("`break' is not allowed outside a loop or switch"));
-
+ error_ln($1->source_line,
+ _("`break' is not allowed outside a loop or switch"));
$1->target_jmp = NULL;
$$ = list_create($1);
@@ -813,8 +816,8 @@ non_compound_stmt
| LEX_CONTINUE statement_term
{
if (! continue_allowed)
- yyerror(_("`continue' is not allowed outside a loop"));
-
+ error_ln($1->source_line,
+ _("`continue' is not allowed outside a loop"));
$1->target_jmp = NULL;
$$ = list_create($1);
@@ -822,30 +825,19 @@ non_compound_stmt
| LEX_NEXT statement_term
{
if (rule != Rule)
- yyerror(_("`next' used in %s action"), ruletab[rule]);
+ error_ln($1->source_line,
+ _("`next' used in %s action"), ruletab[rule]);
$1->target_jmp = ip_rec;
$$ = list_create($1);
}
| LEX_NEXTFILE statement_term
{
- static short warned = FALSE;
-
- if (do_traditional) {
- /*
- * can't use yyerror, since may have overshot
- * the source line
- */
- errcount++;
- error(_("`nextfile' is a gawk extension"));
- }
- if (do_lint && ! warned) {
- warned = TRUE;
- lintwarn(_("`nextfile' is a gawk extension"));
- }
- if (rule == BEGIN || rule == END || rule == ENDFILE) {
- errcount++;
- error(_("`nextfile' used in %s action"), ruletab[rule]);
- }
+ if (do_traditional)
+ error_ln($1->source_line,
+ _("`nextfile' is a gawk extension"));
+ if (rule == BEGIN || rule == END || rule == ENDFILE)
+ error_ln($1->source_line,
+ _("`nextfile' used in %s action"), ruletab[rule]);
$1->target_jmp = ip_newfile;
$1->target_endfile = ip_endfile;
@@ -927,7 +919,7 @@ simple_stmt
} else {
if (do_lint && (rule == BEGIN || rule == END) && ! warned) {
warned = TRUE;
- lintwarn(
+ lintwarn_ln($1->source_line,
_("plain `print' in BEGIN or END rule should probably be `print \"\"'"));
}
}
@@ -994,18 +986,15 @@ simple_stmt
if ($4 == NULL) {
static short warned = FALSE;
+
if (do_lint && ! warned) {
warned = TRUE;
- 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"));
+ lintwarn_ln($1->source_line,
+ _("`delete array' is a gawk extension"));
}
+ if (do_traditional)
+ error_ln($1->source_line,
+ _("`delete array' is a gawk extension"));
$1->expr_count = 0;
$$ = list_append(list_create($2), $1);
} else {
@@ -1024,15 +1013,12 @@ simple_stmt
if (do_lint && ! warned) {
warned = TRUE;
- lintwarn(_("`delete(array)' is a non-portable tawk extension"));
+ lintwarn_ln($1->source_line,
+ _("`delete(array)' is a non-portable tawk extension"));
}
if (do_traditional) {
- /*
- * can't use yyerror, since may have overshot
- * the source line.
- */
- errcount++;
- error(_("`delete(array)' is a non-portable tawk extension"));
+ error_ln($1->source_line,
+ _("`delete array' is a gawk extension"));
}
$3->memory = variable(arr, Node_var_array);
$3->opcode = Op_push_array;
@@ -1246,7 +1232,8 @@ exp
: variable assign_operator exp %prec ASSIGNOP
{
if (do_lint && $3->lasti->opcode == Op_match_rec)
- lintwarn(_("regular expression on right of assignment"));
+ lintwarn_ln($2->source_line,
+ _("regular expression on right of assignment"));
$$ = mk_assignment($1, $3, $2);
}
| exp LEX_AND exp
@@ -1256,7 +1243,8 @@ exp
| exp MATCHOP exp
{
if ($1->lasti->opcode == Op_match_rec)
- warning(_("regular expression on left of `~' or `!~' operator"));
+ warning_ln($2->source_line,
+ _("regular expression on left of `~' or `!~' operator"));
if ($3->lasti == $3->nexti && $3->nexti->opcode == Op_match_rec) {
$2->memory = $3->nexti->memory;
@@ -1271,7 +1259,8 @@ exp
| exp LEX_IN simple_variable
{
if (do_lint_old)
- warning(_("old awk does not support the keyword `in' except after `for'"));
+ warning_ln($2->source_line,
+ _("old awk does not support the keyword `in' except after `for'"));
$3->nexti->opcode = Op_push_array;
$2->opcode = Op_in_array;
$2->expr_count = 1;
@@ -1280,7 +1269,8 @@ exp
| exp a_relop exp %prec RELOP
{
if (do_lint && $3->lasti->opcode == Op_match_rec)
- lintwarn(_("regular expression on right of comparison"));
+ lintwarn_ln($2->source_line,
+ _("regular expression on right of comparison"));
$$ = list_append(list_merge($1, $3), $2);
}
| exp '?' exp ':' exp
@@ -1392,20 +1382,22 @@ simp_exp
/*
* In BEGINFILE/ENDFILE, allow `getline var < file'
*/
+
if (rule == BEGINFILE || rule == ENDFILE) {
if ($2 != NULL && $3 != NULL)
- ; /* all ok */
+ ; /* all ok */
else {
if ($2 != NULL)
- yyerror(_("`getline var' invalid inside `%s' rule"), ruletab[rule]);
+ error_ln($1->source_line,
+ _("`getline var' invalid inside `%s' rule"), ruletab[rule]);
else
- yyerror(_("`getline' invalid inside `%s' rule"), ruletab[rule]);
- YYABORT;
+ error_ln($1->source_line,
+ _("`getline' invalid inside `%s' rule"), ruletab[rule]);
}
}
-
if (do_lint && rule == END && $3 == NULL)
- lintwarn(_("non-redirected `getline' undefined inside END action"));
+ lintwarn_ln($1->source_line,
+ _("non-redirected `getline' undefined inside END action"));
$$ = mk_getline($1, $2, $3, redirect_input);
}
| variable INCREMENT
@@ -1421,8 +1413,10 @@ simp_exp
| '(' expression_list r_paren LEX_IN simple_variable
{
if (do_lint_old) {
- warning(_("old awk does not support the keyword `in' except after `for'"));
- warning(_("old awk does not support multidimensional arrays"));
+ warning_ln($4->source_line,
+ _("old awk does not support the keyword `in' except after `for'"));
+ warning_ln($4->source_line,
+ _("old awk does not support multidimensional arrays"));
}
$5->nexti->opcode = Op_push_array;
$4->opcode = Op_in_array;
@@ -1517,7 +1511,8 @@ non_post_simp_exp
if (do_lint && ! warned1) {
warned1 = TRUE;
- lintwarn(_("call of `length' without parentheses is not portable"));
+ lintwarn_ln($1->source_line,
+ _("call of `length' without parentheses is not portable"));
}
$$ = snode(NULL, $1);
if ($$ == NULL)
@@ -1670,8 +1665,8 @@ bracketed_exp_list
{
INSTRUCTION *t = $2;
if ($2 == NULL) {
- errcount++;
- error(_("invalid subscript expression"));
+ error_ln($3->source_line,
+ _("invalid subscript expression"));
/* install Null string as subscript. */
t = list_create(instruction(Op_push_i));
t->nexti->memory = Nnull_string;
@@ -1930,42 +1925,27 @@ getfname(NODE *(*fptr)(int))
return NULL;
}
-/* yyerror --- print a syntax error message, show where */
-
-/*
- * Function identifier purposely indented to avoid mangling
- * by ansi2knr. Sigh.
- */
+/* print_included_from --- print `Included from ..' file names and locations */
static void
-yyerror(const char *m, ...)
+print_included_from()
{
- va_list args;
- const char *mesg = NULL;
- char *bp, *cp;
- char *scan;
- char *buf;
- int count;
- static char end_of_file_line[] = "(END OF FILE)";
- char save;
- int saveline;
+ int saveline, line;
SRCFILE *s;
/* suppress current file name, line # from `.. included from ..' msgs */
saveline = sourceline;
sourceline = 0;
- for (s = sourcefile; s->stype == SRC_INC; ) {
- int line;
+ for (s = sourcefile; s != NULL && s->stype == SRC_INC; ) {
s = s->next;
- if (s->fd <= INVALID_HANDLE)
+ if (s == NULL || s->fd <= INVALID_HANDLE)
continue;
-
line = s->srclines;
+
/* if last token is NEWLINE, line number is off by 1. */
if (s->lasttok == NEWLINE)
line--;
-
msg("%s %s:%d%c",
s->prev == sourcefile ? "In file included from"
: " from",
@@ -1976,6 +1956,80 @@ yyerror(const char *m, ...)
);
}
sourceline = saveline;
+}
+
+/* warning_ln --- print a warning message with location */
+
+static void
+warning_ln(int line, const char *mesg, ...)
+{
+ va_list args;
+ int saveline;
+
+ saveline = sourceline;
+ sourceline = line;
+ print_included_from();
+ va_start(args, mesg);
+ err(_("warning: "), mesg, args);
+ va_end(args);
+ sourceline = saveline;
+}
+
+/* lintwarn_ln --- print a lint warning and location */
+
+static void
+lintwarn_ln(int line, const char *mesg, ...)
+{
+ va_list args;
+ int saveline;
+
+ saveline = sourceline;
+ sourceline = line;
+ print_included_from();
+ va_start(args, mesg);
+ if (lintfunc == r_fatal)
+ err(_("fatal: "), mesg, args);
+ else
+ err(_("warning: "), mesg, args);
+ va_end(args);
+ sourceline = saveline;
+ if (lintfunc == r_fatal)
+ gawk_exit(EXIT_FATAL);
+}
+
+/* error_ln --- print an error message and location */
+
+static void
+error_ln(int line, const char *m, ...)
+{
+ va_list args;
+ int saveline;
+
+ saveline = sourceline;
+ sourceline = line;
+ print_included_from();
+ errcount++;
+ va_start(args, m);
+ err("error: ", m, args);
+ va_end(args);
+ sourceline = saveline;
+}
+
+/* yyerror --- print a syntax error message, show where */
+
+static void
+yyerror(const char *m, ...)
+{
+ va_list args;
+ const char *mesg = NULL;
+ char *bp, *cp;
+ char *scan;
+ char *buf;
+ int count;
+ static char end_of_file_line[] = "(END OF FILE)";
+ char save;
+
+ print_included_from();
errcount++;
/* Find the current line in the input file */
@@ -2014,10 +2068,10 @@ yyerror(const char *m, ...)
msg("%.*s", (int) (bp - thisline), thisline);
*bp = save;
-
va_start(args, m);
if (mesg == NULL)
mesg = m;
+
count = (bp - thisline) + strlen(mesg) + 2 + 1;
emalloc(buf, char *, count, "yyerror");
@@ -2189,8 +2243,9 @@ parse_program(INSTRUCTION **pcode)
/* avoid false source indications */
source = NULL;
sourceline = 0;
+ if (ret == 0) /* avoid spurious warning if parser aborted with YYABORT */
+ check_funcs();
- check_funcs();
return (ret || errcount);
}
@@ -2247,8 +2302,17 @@ add_srcfile(int stype, char *src, SRCFILE *thisfile, int *already_included, int
if ((s->stype == SRC_FILE || s->stype == SRC_INC)
&& files_are_same(path, s)
) {
- if (do_lint)
- lintwarn(_("already included source file `%s'"), src);
+ if (do_lint) {
+ int line = sourceline;
+ /* Kludge: the line number may be off for `@include file'.
+ * Since, this function is also used for '-f file' in main.c,
+ * sourceline > 1 check ensures that the call is at
+ * parse time.
+ */
+ if (sourceline > 1 && lasttok == NEWLINE)
+ line--;
+ lintwarn_ln(line, _("already included source file `%s'"), src);
+ }
efree(path);
if (already_included)
*already_included = TRUE;
@@ -2265,21 +2329,21 @@ add_srcfile(int stype, char *src, SRCFILE *thisfile, int *already_included, int
/* include_source --- read program from source included using `@include' */
static int
-include_source(char *src)
+include_source(INSTRUCTION *file)
{
SRCFILE *s;
+ char *src = file->lextok;
int errcode;
int already_included;
if (do_traditional || do_posix) {
- error(_("@include is a gawk extension"));
- errcount++;
+ error_ln(file->source_line, _("@include is a gawk extension"));
return -1;
}
if (strlen(src) == 0) {
if (do_lint)
- lintwarn(_("empty filename after @include"));
+ lintwarn_ln(file->source_line, _("empty filename after @include"));
return 0;
}
@@ -2287,9 +2351,9 @@ include_source(char *src)
if (s == NULL) {
if (already_included)
return 0;
- error(_("can't open source file `%s' for reading (%s)"),
- src, errcode ? strerror(errcode) : _("reason unknown"));
- errcount++;
+ error_ln(file->source_line,
+ _("can't open source file `%s' for reading (%s)"),
+ src, errcode ? strerror(errcode) : _("reason unknown"));
return -1;
}
@@ -2549,6 +2613,7 @@ get_src_buf()
static short warned = FALSE;
if (do_lint && newfile && ! warned){
warned = TRUE;
+ sourceline = 0;
lintwarn(_("source file `%s' is empty"), source);
}
lexeof = TRUE;
@@ -3385,6 +3450,7 @@ retry:
/* See if it is a special token. */
if ((mid = check_special(tokstart)) >= 0) {
+ static int warntab[sizeof(tokentab) / sizeof(tokentab[0])];
int class = tokentab[mid].class;
if ((class == LEX_INCLUDE || class == LEX_EVAL)
@@ -3392,19 +3458,30 @@ retry:
goto out;
if (do_lint) {
- if (tokentab[mid].flags & GAWKX)
+ if ((tokentab[mid].flags & GAWKX) && ! (warntab[mid] & GAWKX)) {
lintwarn(_("`%s' is a gawk extension"),
tokentab[mid].operator);
- if (tokentab[mid].flags & RESX)
+ warntab[mid] |= GAWKX;
+ }
+ if ((tokentab[mid].flags & RESX) && ! (warntab[mid] & RESX)) {
lintwarn(_("`%s' is a Bell Labs extension"),
tokentab[mid].operator);
- if (tokentab[mid].flags & NOT_POSIX)
+ warntab[mid] |= RESX;
+ }
+ if ((tokentab[mid].flags & NOT_POSIX) && ! (warntab[mid] & NOT_POSIX)) {
lintwarn(_("POSIX does not allow `%s'"),
tokentab[mid].operator);
+ warntab[mid] |= NOT_POSIX;
+ }
}
- if (do_lint_old && (tokentab[mid].flags & NOT_OLD))
+ if (do_lint_old && (tokentab[mid].flags & NOT_OLD)
+ && ! (warntab[mid] & NOT_OLD)
+ ) {
warning(_("`%s' is not supported in old awk"),
tokentab[mid].operator);
+ warntab[mid] |= NOT_OLD;
+ }
+
if (tokentab[mid].flags & BREAK)
break_allowed++;
if (tokentab[mid].flags & CONTINUE)
@@ -3764,7 +3841,7 @@ append_param(char *pname)
/* dup_parms --- return TRUE if there are duplicate parameters */
static int
-dup_parms(NODE *func)
+dup_parms(INSTRUCTION *fp, NODE *func)
{
NODE *np;
const char *fname, **names;
@@ -3800,7 +3877,7 @@ dup_parms(NODE *func)
for (j = 0; j < i; j++) {
if (strcmp(names[i], names[j]) == 0) {
dups++;
- error(
+ error_ln(fp->source_line,
_("function `%s': parameter #%d, `%s', duplicates parameter #%d"),
fname, i + 1, names[j], j+1);
}
@@ -4095,14 +4172,13 @@ func_install(INSTRUCTION *func, INSTRUCTION *def)
/* check for function foo(foo) { ... }. bleah. */
for (n = params->rnode; n != NULL; n = n->rnode) {
if (strcmp(n->param, params->param) == 0) {
- error(_("function `%s': can't use function name as parameter name"),
- params->param);
- errcount++;
+ error_ln(func->source_line,
+ _("function `%s': can't use function name as parameter name"), params->param);
return -1;
} else if (is_std_var(n->param)) {
- error(_("function `%s': can't use special variable `%s' as a function parameter"),
- params->param, n->param);
- errcount++;
+ error_ln(func->source_line,
+ _("function `%s': can't use special variable `%s' as a function parameter"),
+ params->param, n->param);
return -1;
}
}
@@ -4116,8 +4192,8 @@ func_install(INSTRUCTION *func, INSTRUCTION *def)
freenode(hp);
r = lookup(fname);
if (r != NULL) {
- error(_("function name `%s' previously defined"), fname);
- errcount++;
+ error_ln(func->source_line,
+ _("function name `%s' previously defined"), fname);
return -1;
} else if (fname == builtin_func) /* not a valid function name */
goto remove_params;
@@ -4325,7 +4401,8 @@ param_sanity(INSTRUCTION *arglist)
for (argl = arglist->nexti; argl; ) {
arg = argl->lasti;
if (arg->opcode == Op_match_rec)
- warning(_("regexp constant for parameter #%d yields boolean value"), i);
+ warning_ln(arg->source_line,
+ _("regexp constant for parameter #%d yields boolean value"), i);
argl = arg->nexti;
i++;
}
@@ -4681,7 +4758,7 @@ mk_binary(INSTRUCTION *s1, INSTRUCTION *s2, INSTRUCTION *op)
case Op_quotient:
if (n2->numbr == 0.0) {
/* don't fatalize, allow parsing rest of the input */
- yyerror(_("division by zero attempted"));
+ error_ln(op->source_line, _("division by zero attempted"));
goto regular;
}
@@ -4690,7 +4767,7 @@ mk_binary(INSTRUCTION *s1, INSTRUCTION *s2, INSTRUCTION *op)
case Op_mod:
if (n2->numbr == 0.0) {
/* don't fatalize, allow parsing rest of the input */
- yyerror(_("division by zero attempted in `%%'"));
+ error_ln(op->source_line, _("division by zero attempted in `%%'"));
goto regular;
}
#ifdef HAVE_FMOD
@@ -5361,7 +5438,7 @@ add_lint(INSTRUCTION *list, LINTTYPE linttype)
if (do_lint) { /* compile-time warning */
if (isnoeffect(ip->opcode))
- lintwarn(_("statement may have no effect"));
+ lintwarn_ln(ip->source_line, ("statement may have no effect"));
}
if (ip->opcode == Op_push) { /* run-time warning */