Import awk-20110810.
[dragonfly.git] / contrib / awk / awkgram.y
1 /****************************************************************
2 Copyright (C) Lucent Technologies 1997
3 All Rights Reserved
4
5 Permission to use, copy, modify, and distribute this software and
6 its documentation for any purpose and without fee is hereby
7 granted, provided that the above copyright notice appear in all
8 copies and that both that the copyright notice and this
9 permission notice and warranty disclaimer appear in supporting
10 documentation, and that the name Lucent Technologies or any of
11 its entities not be used in advertising or publicity pertaining
12 to distribution of the software without specific, written prior
13 permission.
14
15 LUCENT DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
16 INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS.
17 IN NO EVENT SHALL LUCENT OR ANY OF ITS ENTITIES BE LIABLE FOR ANY
18 SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
19 WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER
20 IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
21 ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
22 THIS SOFTWARE.
23 ****************************************************************/
24
25 %{
26 #include <stdio.h>
27 #include <string.h>
28 #include "awk.h"
29
30 void checkdup(Node *list, Cell *item);
31 int yywrap(void) { return(1); }
32
33 Node    *beginloc = 0;
34 Node    *endloc = 0;
35 int     infunc  = 0;    /* = 1 if in arglist or body of func */
36 int     inloop  = 0;    /* = 1 if in while, for, do */
37 char    *curfname = 0;  /* current function name */
38 Node    *arglist = 0;   /* list of args for current function */
39 %}
40
41 %union {
42         Node    *p;
43         Cell    *cp;
44         int     i;
45         char    *s;
46 }
47
48 %token  <i>     FIRSTTOKEN      /* must be first */
49 %token  <p>     PROGRAM PASTAT PASTAT2 XBEGIN XEND
50 %token  <i>     NL ',' '{' '(' '|' ';' '/' ')' '}' '[' ']'
51 %token  <i>     ARRAY
52 %token  <i>     MATCH NOTMATCH MATCHOP
53 %token  <i>     FINAL DOT ALL CCL NCCL CHAR OR STAR QUEST PLUS EMPTYRE
54 %token  <i>     AND BOR APPEND EQ GE GT LE LT NE IN
55 %token  <i>     ARG BLTIN BREAK CLOSE CONTINUE DELETE DO EXIT FOR FUNC 
56 %token  <i>     SUB GSUB IF INDEX LSUBSTR MATCHFCN NEXT NEXTFILE
57 %token  <i>     ADD MINUS MULT DIVIDE MOD
58 %token  <i>     ASSIGN ASGNOP ADDEQ SUBEQ MULTEQ DIVEQ MODEQ POWEQ
59 %token  <i>     PRINT PRINTF SPRINTF
60 %token  <p>     ELSE INTEST CONDEXPR
61 %token  <i>     POSTINCR PREINCR POSTDECR PREDECR
62 %token  <cp>    VAR IVAR VARNF CALL NUMBER STRING
63 %token  <s>     REGEXPR
64
65 %type   <p>     pas pattern ppattern plist pplist patlist prarg term re
66 %type   <p>     pa_pat pa_stat pa_stats
67 %type   <s>     reg_expr
68 %type   <p>     simple_stmt opt_simple_stmt stmt stmtlist
69 %type   <p>     var varname funcname varlist
70 %type   <p>     for if else while
71 %type   <i>     do st
72 %type   <i>     pst opt_pst lbrace rbrace rparen comma nl opt_nl and bor
73 %type   <i>     subop print
74
75 %right  ASGNOP
76 %right  '?'
77 %right  ':'
78 %left   BOR
79 %left   AND
80 %left   GETLINE
81 %nonassoc APPEND EQ GE GT LE LT NE MATCHOP IN '|'
82 %left   ARG BLTIN BREAK CALL CLOSE CONTINUE DELETE DO EXIT FOR FUNC 
83 %left   GSUB IF INDEX LSUBSTR MATCHFCN NEXT NUMBER
84 %left   PRINT PRINTF RETURN SPLIT SPRINTF STRING SUB SUBSTR
85 %left   REGEXPR VAR VARNF IVAR WHILE '('
86 %left   CAT
87 %left   '+' '-'
88 %left   '*' '/' '%'
89 %left   NOT UMINUS
90 %right  POWER
91 %right  DECR INCR
92 %left   INDIRECT
93 %token  LASTTOKEN       /* must be last */
94
95 %%
96
97 program:
98           pas   { if (errorflag==0)
99                         winner = (Node *)stat3(PROGRAM, beginloc, $1, endloc); }
100         | error { yyclearin; bracecheck(); SYNTAX("bailing out"); }
101         ;
102
103 and:
104           AND | and NL
105         ;
106
107 bor:
108           BOR | bor NL
109         ;
110
111 comma:
112           ',' | comma NL
113         ;
114
115 do:
116           DO | do NL
117         ;
118
119 else:
120           ELSE | else NL
121         ;
122
123 for:
124           FOR '(' opt_simple_stmt ';' opt_nl pattern ';' opt_nl opt_simple_stmt rparen {inloop++;} stmt
125                 { --inloop; $$ = stat4(FOR, $3, notnull($6), $9, $12); }
126         | FOR '(' opt_simple_stmt ';'  ';' opt_nl opt_simple_stmt rparen {inloop++;} stmt
127                 { --inloop; $$ = stat4(FOR, $3, NIL, $7, $10); }
128         | FOR '(' varname IN varname rparen {inloop++;} stmt
129                 { --inloop; $$ = stat3(IN, $3, makearr($5), $8); }
130         ;
131
132 funcname:
133           VAR   { setfname($1); }
134         | CALL  { setfname($1); }
135         ;
136
137 if:
138           IF '(' pattern rparen         { $$ = notnull($3); }
139         ;
140
141 lbrace:
142           '{' | lbrace NL
143         ;
144
145 nl:
146           NL | nl NL
147         ;
148
149 opt_nl:
150           /* empty */   { $$ = 0; }
151         | nl
152         ;
153
154 opt_pst:
155           /* empty */   { $$ = 0; }
156         | pst
157         ;
158
159
160 opt_simple_stmt:
161           /* empty */                   { $$ = 0; }
162         | simple_stmt
163         ;
164
165 pas:
166           opt_pst                       { $$ = 0; }
167         | opt_pst pa_stats opt_pst      { $$ = $2; }
168         ;
169
170 pa_pat:
171           pattern       { $$ = notnull($1); }
172         ;
173
174 pa_stat:
175           pa_pat                        { $$ = stat2(PASTAT, $1, stat2(PRINT, rectonode(), NIL)); }
176         | pa_pat lbrace stmtlist '}'    { $$ = stat2(PASTAT, $1, $3); }
177         | pa_pat ',' opt_nl pa_pat              { $$ = pa2stat($1, $4, stat2(PRINT, rectonode(), NIL)); }
178         | pa_pat ',' opt_nl pa_pat lbrace stmtlist '}'  { $$ = pa2stat($1, $4, $6); }
179         | lbrace stmtlist '}'           { $$ = stat2(PASTAT, NIL, $2); }
180         | XBEGIN lbrace stmtlist '}'
181                 { beginloc = linkum(beginloc, $3); $$ = 0; }
182         | XEND lbrace stmtlist '}'
183                 { endloc = linkum(endloc, $3); $$ = 0; }
184         | FUNC funcname '(' varlist rparen {infunc++;} lbrace stmtlist '}'
185                 { infunc--; curfname=0; defn((Cell *)$2, $4, $8); $$ = 0; }
186         ;
187
188 pa_stats:
189           pa_stat
190         | pa_stats opt_pst pa_stat      { $$ = linkum($1, $3); }
191         ;
192
193 patlist:
194           pattern
195         | patlist comma pattern         { $$ = linkum($1, $3); }
196         ;
197
198 ppattern:
199           var ASGNOP ppattern           { $$ = op2($2, $1, $3); }
200         | ppattern '?' ppattern ':' ppattern %prec '?'
201                 { $$ = op3(CONDEXPR, notnull($1), $3, $5); }
202         | ppattern bor ppattern %prec BOR
203                 { $$ = op2(BOR, notnull($1), notnull($3)); }
204         | ppattern and ppattern %prec AND
205                 { $$ = op2(AND, notnull($1), notnull($3)); }
206         | ppattern MATCHOP reg_expr     { $$ = op3($2, NIL, $1, (Node*)makedfa($3, 0)); }
207         | ppattern MATCHOP ppattern
208                 { if (constnode($3))
209                         $$ = op3($2, NIL, $1, (Node*)makedfa(strnode($3), 0));
210                   else
211                         $$ = op3($2, (Node *)1, $1, $3); }
212         | ppattern IN varname           { $$ = op2(INTEST, $1, makearr($3)); }
213         | '(' plist ')' IN varname      { $$ = op2(INTEST, $2, makearr($5)); }
214         | ppattern term %prec CAT       { $$ = op2(CAT, $1, $2); }
215         | re
216         | term
217         ;
218
219 pattern:
220           var ASGNOP pattern            { $$ = op2($2, $1, $3); }
221         | pattern '?' pattern ':' pattern %prec '?'
222                 { $$ = op3(CONDEXPR, notnull($1), $3, $5); }
223         | pattern bor pattern %prec BOR
224                 { $$ = op2(BOR, notnull($1), notnull($3)); }
225         | pattern and pattern %prec AND
226                 { $$ = op2(AND, notnull($1), notnull($3)); }
227         | pattern EQ pattern            { $$ = op2($2, $1, $3); }
228         | pattern GE pattern            { $$ = op2($2, $1, $3); }
229         | pattern GT pattern            { $$ = op2($2, $1, $3); }
230         | pattern LE pattern            { $$ = op2($2, $1, $3); }
231         | pattern LT pattern            { $$ = op2($2, $1, $3); }
232         | pattern NE pattern            { $$ = op2($2, $1, $3); }
233         | pattern MATCHOP reg_expr      { $$ = op3($2, NIL, $1, (Node*)makedfa($3, 0)); }
234         | pattern MATCHOP pattern
235                 { if (constnode($3))
236                         $$ = op3($2, NIL, $1, (Node*)makedfa(strnode($3), 0));
237                   else
238                         $$ = op3($2, (Node *)1, $1, $3); }
239         | pattern IN varname            { $$ = op2(INTEST, $1, makearr($3)); }
240         | '(' plist ')' IN varname      { $$ = op2(INTEST, $2, makearr($5)); }
241         | pattern '|' GETLINE var       { 
242                         if (safe) SYNTAX("cmd | getline is unsafe");
243                         else $$ = op3(GETLINE, $4, itonp($2), $1); }
244         | pattern '|' GETLINE           { 
245                         if (safe) SYNTAX("cmd | getline is unsafe");
246                         else $$ = op3(GETLINE, (Node*)0, itonp($2), $1); }
247         | pattern term %prec CAT        { $$ = op2(CAT, $1, $2); }
248         | re
249         | term
250         ;
251
252 plist:
253           pattern comma pattern         { $$ = linkum($1, $3); }
254         | plist comma pattern           { $$ = linkum($1, $3); }
255         ;
256
257 pplist:
258           ppattern
259         | pplist comma ppattern         { $$ = linkum($1, $3); }
260         ;
261
262 prarg:
263           /* empty */                   { $$ = rectonode(); }
264         | pplist
265         | '(' plist ')'                 { $$ = $2; }
266         ;
267
268 print:
269           PRINT | PRINTF
270         ;
271
272 pst:
273           NL | ';' | pst NL | pst ';'
274         ;
275
276 rbrace:
277           '}' | rbrace NL
278         ;
279
280 re:
281            reg_expr
282                 { $$ = op3(MATCH, NIL, rectonode(), (Node*)makedfa($1, 0)); }
283         | NOT re        { $$ = op1(NOT, notnull($2)); }
284         ;
285
286 reg_expr:
287           '/' {startreg();} REGEXPR '/'         { $$ = $3; }
288         ;
289
290 rparen:
291           ')' | rparen NL
292         ;
293
294 simple_stmt:
295           print prarg '|' term          { 
296                         if (safe) SYNTAX("print | is unsafe");
297                         else $$ = stat3($1, $2, itonp($3), $4); }
298         | print prarg APPEND term       {
299                         if (safe) SYNTAX("print >> is unsafe");
300                         else $$ = stat3($1, $2, itonp($3), $4); }
301         | print prarg GT term           {
302                         if (safe) SYNTAX("print > is unsafe");
303                         else $$ = stat3($1, $2, itonp($3), $4); }
304         | print prarg                   { $$ = stat3($1, $2, NIL, NIL); }
305         | DELETE varname '[' patlist ']' { $$ = stat2(DELETE, makearr($2), $4); }
306         | DELETE varname                 { $$ = stat2(DELETE, makearr($2), 0); }
307         | pattern                       { $$ = exptostat($1); }
308         | error                         { yyclearin; SYNTAX("illegal statement"); }
309         ;
310
311 st:
312           nl
313         | ';' opt_nl
314         ;
315
316 stmt:
317           BREAK st              { if (!inloop) SYNTAX("break illegal outside of loops");
318                                   $$ = stat1(BREAK, NIL); }
319         | CONTINUE st           {  if (!inloop) SYNTAX("continue illegal outside of loops");
320                                   $$ = stat1(CONTINUE, NIL); }
321         | do {inloop++;} stmt {--inloop;} WHILE '(' pattern ')' st
322                 { $$ = stat2(DO, $3, notnull($7)); }
323         | EXIT pattern st       { $$ = stat1(EXIT, $2); }
324         | EXIT st               { $$ = stat1(EXIT, NIL); }
325         | for
326         | if stmt else stmt     { $$ = stat3(IF, $1, $2, $4); }
327         | if stmt               { $$ = stat3(IF, $1, $2, NIL); }
328         | lbrace stmtlist rbrace { $$ = $2; }
329         | NEXT st       { if (infunc)
330                                 SYNTAX("next is illegal inside a function");
331                           $$ = stat1(NEXT, NIL); }
332         | NEXTFILE st   { if (infunc)
333                                 SYNTAX("nextfile is illegal inside a function");
334                           $$ = stat1(NEXTFILE, NIL); }
335         | RETURN pattern st     { $$ = stat1(RETURN, $2); }
336         | RETURN st             { $$ = stat1(RETURN, NIL); }
337         | simple_stmt st
338         | while {inloop++;} stmt        { --inloop; $$ = stat2(WHILE, $1, $3); }
339         | ';' opt_nl            { $$ = 0; }
340         ;
341
342 stmtlist:
343           stmt
344         | stmtlist stmt         { $$ = linkum($1, $2); }
345         ;
346
347 subop:
348           SUB | GSUB
349         ;
350
351 term:
352           term '/' ASGNOP term          { $$ = op2(DIVEQ, $1, $4); }
353         | term '+' term                 { $$ = op2(ADD, $1, $3); }
354         | term '-' term                 { $$ = op2(MINUS, $1, $3); }
355         | term '*' term                 { $$ = op2(MULT, $1, $3); }
356         | term '/' term                 { $$ = op2(DIVIDE, $1, $3); }
357         | term '%' term                 { $$ = op2(MOD, $1, $3); }
358         | term POWER term               { $$ = op2(POWER, $1, $3); }
359         | '-' term %prec UMINUS         { $$ = op1(UMINUS, $2); }
360         | '+' term %prec UMINUS         { $$ = $2; }
361         | NOT term %prec UMINUS         { $$ = op1(NOT, notnull($2)); }
362         | BLTIN '(' ')'                 { $$ = op2(BLTIN, itonp($1), rectonode()); }
363         | BLTIN '(' patlist ')'         { $$ = op2(BLTIN, itonp($1), $3); }
364         | BLTIN                         { $$ = op2(BLTIN, itonp($1), rectonode()); }
365         | CALL '(' ')'                  { $$ = op2(CALL, celltonode($1,CVAR), NIL); }
366         | CALL '(' patlist ')'          { $$ = op2(CALL, celltonode($1,CVAR), $3); }
367         | CLOSE term                    { $$ = op1(CLOSE, $2); }
368         | DECR var                      { $$ = op1(PREDECR, $2); }
369         | INCR var                      { $$ = op1(PREINCR, $2); }
370         | var DECR                      { $$ = op1(POSTDECR, $1); }
371         | var INCR                      { $$ = op1(POSTINCR, $1); }
372         | GETLINE var LT term           { $$ = op3(GETLINE, $2, itonp($3), $4); }
373         | GETLINE LT term               { $$ = op3(GETLINE, NIL, itonp($2), $3); }
374         | GETLINE var                   { $$ = op3(GETLINE, $2, NIL, NIL); }
375         | GETLINE                       { $$ = op3(GETLINE, NIL, NIL, NIL); }
376         | INDEX '(' pattern comma pattern ')'
377                 { $$ = op2(INDEX, $3, $5); }
378         | INDEX '(' pattern comma reg_expr ')'
379                 { SYNTAX("index() doesn't permit regular expressions");
380                   $$ = op2(INDEX, $3, (Node*)$5); }
381         | '(' pattern ')'               { $$ = $2; }
382         | MATCHFCN '(' pattern comma reg_expr ')'
383                 { $$ = op3(MATCHFCN, NIL, $3, (Node*)makedfa($5, 1)); }
384         | MATCHFCN '(' pattern comma pattern ')'
385                 { if (constnode($5))
386                         $$ = op3(MATCHFCN, NIL, $3, (Node*)makedfa(strnode($5), 1));
387                   else
388                         $$ = op3(MATCHFCN, (Node *)1, $3, $5); }
389         | NUMBER                        { $$ = celltonode($1, CCON); }
390         | SPLIT '(' pattern comma varname comma pattern ')'     /* string */
391                 { $$ = op4(SPLIT, $3, makearr($5), $7, (Node*)STRING); }
392         | SPLIT '(' pattern comma varname comma reg_expr ')'    /* const /regexp/ */
393                 { $$ = op4(SPLIT, $3, makearr($5), (Node*)makedfa($7, 1), (Node *)REGEXPR); }
394         | SPLIT '(' pattern comma varname ')'
395                 { $$ = op4(SPLIT, $3, makearr($5), NIL, (Node*)STRING); }  /* default */
396         | SPRINTF '(' patlist ')'       { $$ = op1($1, $3); }
397         | STRING                        { $$ = celltonode($1, CCON); }
398         | subop '(' reg_expr comma pattern ')'
399                 { $$ = op4($1, NIL, (Node*)makedfa($3, 1), $5, rectonode()); }
400         | subop '(' pattern comma pattern ')'
401                 { if (constnode($3))
402                         $$ = op4($1, NIL, (Node*)makedfa(strnode($3), 1), $5, rectonode());
403                   else
404                         $$ = op4($1, (Node *)1, $3, $5, rectonode()); }
405         | subop '(' reg_expr comma pattern comma var ')'
406                 { $$ = op4($1, NIL, (Node*)makedfa($3, 1), $5, $7); }
407         | subop '(' pattern comma pattern comma var ')'
408                 { if (constnode($3))
409                         $$ = op4($1, NIL, (Node*)makedfa(strnode($3), 1), $5, $7);
410                   else
411                         $$ = op4($1, (Node *)1, $3, $5, $7); }
412         | SUBSTR '(' pattern comma pattern comma pattern ')'
413                 { $$ = op3(SUBSTR, $3, $5, $7); }
414         | SUBSTR '(' pattern comma pattern ')'
415                 { $$ = op3(SUBSTR, $3, $5, NIL); }
416         | var
417         ;
418
419 var:
420           varname
421         | varname '[' patlist ']'       { $$ = op2(ARRAY, makearr($1), $3); }
422         | IVAR                          { $$ = op1(INDIRECT, celltonode($1, CVAR)); }
423         | INDIRECT term                 { $$ = op1(INDIRECT, $2); }
424         ;       
425
426 varlist:
427           /* nothing */         { arglist = $$ = 0; }
428         | VAR                   { arglist = $$ = celltonode($1,CVAR); }
429         | varlist comma VAR     {
430                         checkdup($1, $3);
431                         arglist = $$ = linkum($1,celltonode($3,CVAR)); }
432         ;
433
434 varname:
435           VAR                   { $$ = celltonode($1, CVAR); }
436         | ARG                   { $$ = op1(ARG, itonp($1)); }
437         | VARNF                 { $$ = op1(VARNF, (Node *) $1); }
438         ;
439
440
441 while:
442           WHILE '(' pattern rparen      { $$ = notnull($3); }
443         ;
444
445 %%
446
447 void setfname(Cell *p)
448 {
449         if (isarr(p))
450                 SYNTAX("%s is an array, not a function", p->nval);
451         else if (isfcn(p))
452                 SYNTAX("you can't define function %s more than once", p->nval);
453         curfname = p->nval;
454 }
455
456 int constnode(Node *p)
457 {
458         return isvalue(p) && ((Cell *) (p->narg[0]))->csub == CCON;
459 }
460
461 char *strnode(Node *p)
462 {
463         return ((Cell *)(p->narg[0]))->sval;
464 }
465
466 Node *notnull(Node *n)
467 {
468         switch (n->nobj) {
469         case LE: case LT: case EQ: case NE: case GT: case GE:
470         case BOR: case AND: case NOT:
471                 return n;
472         default:
473                 return op2(NE, n, nullnode);
474         }
475 }
476
477 void checkdup(Node *vl, Cell *cp)       /* check if name already in list */
478 {
479         char *s = cp->nval;
480         for ( ; vl; vl = vl->nnext) {
481                 if (strcmp(s, ((Cell *)(vl->narg[0]))->nval) == 0) {
482                         SYNTAX("duplicate argument %s", s);
483                         break;
484                 }
485         }
486 }