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