Pristine Ack-5.5
[Ack-5.5.git] / util / grind / commands.g
1 /* $Id: commands.g,v 1.19 1995/08/15 09:10:39 ceriel Exp $ */
2
3 /* Command grammar */
4 {
5 #include        <stdio.h>
6 #include        <alloc.h>
7 #include        <signal.h>
8
9 #include        "ops.h"
10 #include        "class.h"
11 #include        "position.h"
12 #include        "file.h"
13 #include        "idf.h"
14 #include        "symbol.h"
15 #include        "tree.h"
16 #include        "langdep.h"
17 #include        "token.h"
18 #include        "expr.h"
19 #include        "misc.h"
20
21 extern char     *Salloc();
22 extern char     *strindex();
23 extern char     *strcpy();
24 extern void     signal_child();
25 extern FILE     *db_in;
26 extern int      disable_intr;
27 extern p_tree   run_command, print_command;
28
29 struct token    tok, aside;
30 int             errorgiven = 0;
31 int             child_interrupted = 0;
32 int             interrupted = 0;
33 int             eof_seen = 0;
34
35 static int      shellescape();
36
37 static int      extended_charset = 0;
38 static int      in_expression = 0;
39
40 #define binprio(op)     ((*(currlang->binop_prio))(op))
41 #define unprio(op)      ((*(currlang->unop_prio))(op))
42 }
43 %start Commands, commands;
44
45 %lexical LLlex;
46
47 commands
48   { p_tree com, lastcom = 0;
49     int give_prompt;
50   }
51 :
52                         { errorgiven = 0; }
53   [ %persistent command_line(&com)
54     [   '\n'            { give_prompt = 1; }
55     |   %default ';'    { give_prompt = 0; }
56     ]
57                         { if (com) {
58                                 if (lastcom) {
59                                         freenode(lastcom);
60                                         lastcom = 0;
61                                 }
62                                 if (errorgiven) {
63                                         if (com != run_command) freenode(com);
64                                         com = 0;
65                                 }
66                                 else {
67                                         enterlog(com);
68                                         eval(com);
69                                         if (repeatable(com)) {
70                                                 lastcom = com;
71                                         }
72                                         else if (! in_status(com) &&
73                                                 com != run_command &&
74                                                 com != print_command) {
75                                                 freenode(com);
76                                                 com = 0;
77                                         }
78                                 }
79                           } else if (lastcom && ! errorgiven) {
80                                 enterlog(lastcom);
81                                 eval(lastcom);
82                           }
83                           if (give_prompt) {
84                                 errorgiven = 0;
85                                 interrupted = 0;
86                                 prompt();
87                           }
88                         }
89   ]*
90 ;
91
92 command_line(p_tree *p;)
93 :
94                         { *p = 0; }
95 [
96   list_command(p)
97 | file_command(p)
98 | run_command(p)
99 | stop_command(p)
100 | when_command(p)
101 | continue_command(p)
102 | step_command(p)
103 | next_command(p)
104 | regs_command(p)
105 | where_command(p)
106 | STATUS                { *p = mknode(OP_STATUS); }
107 | DUMP                  { *p = mknode(OP_DUMP); }
108 | RESTORE opt_num(p)    { *p = mknode(OP_RESTORE, *p); }
109 | delete_command(p)
110 | print_command(p)
111 | display_command(p)
112 | trace_command(p)
113 | set_command(p)
114 | help_command(p)
115 | FIND qualified_name(p){ *p = mknode(OP_FIND, *p); }
116 | WHICH qualified_name(p){ *p = mknode(OP_WHICH, *p); }
117 | able_command(p)
118 | '!'                   { (void) shellescape();
119                           *p = mknode(OP_SHELL);
120                         }
121 | source_command(p)
122 | log_command(p)
123 | frame_command(p)
124 |
125 ]
126 ;
127
128 frame_command(p_tree    *p;)
129 :
130   FRAME 
131   [                     { *p = mknode(OP_FRAME, (p_tree) 0); }
132   | count(p)            { *p = mknode(OP_FRAME, *p); }
133   | '-' count(p)        { *p = mknode(OP_DOWN, *p); }
134   | '+' count(p)        { *p = mknode(OP_UP, *p); }
135   ]
136 ;
137
138 source_command(p_tree *p;)
139 :
140   SOURCE                { extended_charset = 1; }
141   name(p)               { (*p)->t_idf = str2idf((*p)->t_str, 0); }
142                         { *p = mknode(OP_SOURCE, *p);
143                           extended_charset = 0;
144                         }
145 ;
146
147 log_command(p_tree *p;)
148 :
149   LOG                   { extended_charset = 1; }
150   [ name(p)             { (*p)->t_idf = str2idf((*p)->t_str, 0); }
151   |                     { *p = 0; }
152   ]
153                         { *p = mknode(OP_LOG, *p);
154                           extended_charset = 0;
155                         }
156 ;
157
158 where_command(p_tree *p;)
159 :
160   WHERE opt_num(p)      { *p = mknode(OP_WHERE, *p); }
161 ;
162
163 list_command(p_tree *p;)
164   { p_tree t1 = 0, t2 = 0; }
165 :
166   LIST
167   [
168   | position(&t1)
169   | qualified_name(&t1)
170   ]
171   [ ',' count(&t2)
172   | '-' 
173     [   count(&t2)      { t2->t_ival = - t2->t_ival; }
174     |                   { t2 = mknode(OP_INTEGER, -100000000L); }
175     ]
176   |
177   ]
178                         { *p = mknode(OP_LIST, t1, t2); }
179 ;
180
181 file_command(p_tree *p;)
182 :
183   XFILE                 { extended_charset = 1; }
184   [                     { *p = 0; }
185   | name(p)             { (*p)->t_idf = str2idf((*p)->t_str, 0); }
186   ]                     { *p = mknode(OP_FILE, *p);
187                           extended_charset = 0;
188                         }
189 ;
190
191 help_command(p_tree *p;)
192 :
193   [ HELP | '?' ]
194                         { *p = mknode(OP_HELP, (p_tree) 0); }
195   [ name(&(*p)->t_args[0])?
196   | '?'                 { (*p)->t_args[0] = mknode(OP_NAME, str2idf("help",0), (char *) 0); }
197   | '!'                 { (*p)->t_args[0] = mknode(OP_NAME, (struct idf *) 0, "!"); }
198   ]
199 ;
200
201 run_command(p_tree *p;)
202 :
203   RUN                   { extended_charset = 1; }
204   args(p)               { *p = mknode(OP_RUN, *p);
205                           extended_charset = 0;
206                         }
207 | RERUN                 { if (! run_command) {
208                                 error("no run command given yet");
209                           }
210                           else *p = run_command;
211                         }
212   [ '?'                 { *p = mknode(OP_PRCOMM, *p); }
213   |
214   ]
215 ;
216
217 stop_command(p_tree *p;)
218   { p_tree whr = 0, cond = 0; }
219 :
220   STOP
221   where(&whr)?
222   condition(&cond)?     { if (! whr && ! cond) {
223                                 error("no position or condition");
224                                 *p = 0;
225                           }
226                           else *p = mknode(OP_STOP, whr, cond);
227                         }
228 ;
229
230 trace_command(p_tree *p;)
231   { p_tree whr = 0, cond = 0, exp = 0; }
232 :
233   TRACE
234   [ ON format_expression(&exp) ]?
235   where(&whr)?
236   condition(&cond)?     { *p = mknode(OP_TRACE, whr, cond, exp); }
237 ;
238
239 continue_command(p_tree *p;)
240   { long l; p_tree pos = 0; }
241 :
242   CONT
243   [ INTEGER             { l = tok.ival; }
244   |                     { l = 1; }
245   ]
246   [ AT position(&pos) ]?
247                         { *p = mknode(OP_CONT, mknode(OP_INTEGER, l), pos); }
248 ;
249
250 when_command(p_tree *p;)
251   { p_tree      whr = 0, cond = 0; }
252 :
253   WHEN
254   where(&whr)?
255   condition(&cond)?     { *p = mknode(OP_WHEN, whr, cond, (p_tree) 0); 
256                           p = &(*p)->t_args[2];
257                         }
258   '{' 
259   command_line(p)
260   [ ';'                 { if (*p) {
261                                 *p = mknode(OP_LINK, *p, (p_tree) 0);
262                                 p = &((*p)->t_args[1]);
263                           }
264                         }
265     command_line(p)
266   ]*
267   '}'
268                         { if (! whr && ! cond) {
269                                 error("no position or condition");
270                           }
271                           else if (! *p) {
272                                 error("no commands given");
273                           }
274                         }
275 ;
276
277 step_command(p_tree *p;)
278 :
279   STEP                  { *p = mknode(OP_STEP, (p_tree) 0); }
280   count(&(*p)->t_args[0])?
281 ;
282
283 next_command(p_tree *p;)
284 :
285   NEXT                  { *p = mknode(OP_NEXT, (p_tree) 0); }
286   count(&(*p)->t_args[0])?
287 ;
288
289 regs_command(p_tree *p;)
290 :
291   REGS                  { *p = mknode(OP_REGS, (p_tree) 0); }
292   count(&(*p)->t_args[0])?
293 ;
294
295 delete_command(p_tree *p;)
296 :
297   DELETE num_list(p)?   { *p = mknode(OP_DELETE, *p); }
298 ;
299
300 print_command(p_tree *p;)
301 :
302   PRINT 
303   [ format_expression_list(p)
304                         { *p = mknode(OP_PRINT, *p); }
305   |
306                         { *p = mknode(OP_PRINT, (p_tree) 0); }
307   ]
308 ;
309
310 display_command(p_tree *p;)
311 :
312   DISPLAY format_expression_list(p)
313                         { *p = mknode(OP_DISPLAY, *p); }
314 ;
315
316 format_expression_list(p_tree *p;)
317 :
318   format_expression(p)
319   [ ','                 { *p = mknode(OP_LINK, *p, (p_tree) 0);
320                           p = &((*p)->t_args[1]);
321                         }
322     format_expression(p)
323   ]*
324 ;
325
326 format_expression(p_tree *p;)
327   { p_tree      p1; }
328 :
329   expression(p, 0)
330   [ '\\' name(&p1)      { register char *c = p1->t_str;
331                           while (*c) {
332                                 if (! strindex("doshcax", *c)) {
333                                         error("illegal format: %c", *c);
334                                         break;
335                                 }
336                                 c++;
337                           }
338                           *p = mknode(OP_FORMAT, *p, p1);
339                         }
340   |
341   ]
342 ;
343
344 set_command(p_tree *p;)
345 :
346   SET expression(p, 0)  { *p = mknode(OP_SET, *p, (p_tree) 0); }
347   TO expression(&((*p)->t_args[1]), 0)
348 ;
349
350 able_command(p_tree *p;)
351 :
352   [ ENABLE              { *p = mknode(OP_ENABLE, (p_tree) 0); }
353   | DISABLE             { *p = mknode(OP_DISABLE, (p_tree) 0); }
354   ]
355   num_list(&(*p)->t_args[0])?
356 ;
357
358 num_list(p_tree *p;)
359 :
360   num(p)
361   [ ','                 { *p = mknode(OP_LINK, *p, (p_tree) 0); }
362     num(&(*p)->t_args[1])
363   ]*
364 ;
365
366 condition(p_tree *p;)
367 :
368   IF expression(p, 0)
369 ;
370
371 where(p_tree *p;)
372 :
373   IN qualified_name(p)  { *p = mknode(OP_IN, *p, (p_tree) 0); }
374   [ AT position(&((*p)->t_args[1])) ]?
375 |
376   AT position(p)
377 ;
378
379 expression(p_tree *p; int level;)
380   { int currprio, currop; }
381 :                       { in_expression++; }
382   factor(p)
383   [ %while ((currprio = binprio(currop = (int) tok.ival)) > level)
384         [ BIN_OP | PREF_OR_BIN_OP ] 
385                         { *p = mknode(OP_BINOP, *p, (p_tree) 0);
386                           (*p)->t_whichoper = currop;
387                         }
388         expression(&((*p)->t_args[1]), currprio)
389   |
390         SEL_OP          { *p = mknode(OP_BINOP, *p, (p_tree) 0);
391                           (*p)->t_whichoper = (int) tok.ival;
392                         }
393         name(&(*p)->t_args[1])
394   |
395         '['             { *p = mknode(OP_BINOP, *p, (p_tree) 0);
396                           (*p)->t_whichoper = E_ARRAY;
397                         }
398         expression(&(*p)->t_args[1], 0)
399         [       ','     { *p = mknode(OP_BINOP, *p, (p_tree) 0);
400                           (*p)->t_whichoper = E_ARRAY;
401                         }
402                 expression(&(*p)->t_args[1], 0)
403         ]*
404         ']'
405   ]*
406                         { in_expression--; }
407 ;
408
409 factor(p_tree *p;)
410 :
411   [
412         %default EXPRESSION     /* lexical analyzer will never return this token */
413                         { *p = mknode(OP_INTEGER, 0L); }
414   |
415         '(' expression(p, 0) ')'
416   |
417         INTEGER         { *p = mknode(OP_INTEGER, tok.ival); }
418   |
419         REAL            { *p = mknode(OP_REAL, tok.fval); }
420   |
421         STRING          { *p = mknode(OP_STRING, tok.str); }
422   |
423         qualified_name(p)
424   |
425                         { *p = mknode(OP_UNOP, (p_tree) 0);
426                           (*p)->t_whichoper = (int) tok.ival;
427                         }
428         [ PREF_OP 
429         | PREF_OR_BIN_OP
430                         { (*currlang->fix_bin_to_pref)(*p); }
431         ]
432         expression(&(*p)->t_args[0], unprio((*p)->t_whichoper))
433   ]
434   [ %while(1)
435         POST_OP         { *p = mknode(OP_UNOP, *p);
436                           (*p)->t_whichoper = (int) tok.ival;
437                         }
438   ]*
439 ;
440
441 position(p_tree *p;)
442   { p_tree lin;
443     char *str;
444   }
445 :
446   [ STRING              { str = tok.str; }
447     ':'
448   |                     { if (! listfile) str = 0;
449                           else str = listfile->sy_idf->id_text;
450                         }
451   ]
452   count(&lin)           { *p = mknode(OP_AT, lin->t_ival, str);
453                           freenode(lin);
454                         }
455 ;
456
457 args(p_tree *p;)
458   { int first_time = 1; }
459 :
460   [                     { if (! first_time) {
461                                 *p = mknode(OP_LINK, *p, (p_tree) 0);
462                                 p = &((*p)->t_args[1]);
463                           }
464                           first_time = 0;
465                         }
466         arg(p)
467   ]*
468 ;
469
470 arg(p_tree *p;)
471 :
472   name(p)
473 |
474   '>' name(p)           { (*p)->t_oper = OP_OUTPUT; }
475 |
476   '<' name(p)           { (*p)->t_oper = OP_INPUT; }
477 ;
478
479 count(p_tree *p;)
480 :
481   INTEGER               { *p = mknode(OP_INTEGER, tok.ival); }
482 ;
483
484 opt_num(p_tree *p;)
485 :
486   num(p)
487 |
488                         { *p = 0; }
489 ;
490
491 num(p_tree *p;)
492 :
493   count(p)
494 |
495   '-' count(p)          { (*p)->t_ival = - (*p)->t_ival; }
496 ;
497
498 qualified_name(p_tree *p;)
499 :
500   name(p)
501   [     '`'             { *p = mknode(OP_SELECT, *p, (p_tree) 0); }
502         name(&((*p)->t_args[1]))
503   ]*
504 ;
505
506 name(p_tree *p;)
507 :
508   [ XFILE
509   | LIST
510   | RUN
511   | RERUN
512   | STOP
513   | WHEN
514   | AT
515   | IN
516   | IF
517   | %default NAME
518   | CONT
519   | STEP
520   | NEXT
521   | REGS
522   | WHERE
523   | STATUS
524   | PRINT
525   | DELETE
526   | DUMP
527   | RESTORE
528   | TRACE
529   | ON
530   | SET
531   | TO
532   | FIND
533   | DISPLAY
534   | WHICH
535   | HELP
536   | DISABLE
537   | ENABLE
538   | SOURCE
539   | FRAME
540   | LOG
541   ]                     { *p = mknode(OP_NAME, tok.idf, tok.str); }
542 ;
543
544 {
545 int
546 LLlex()
547 {
548   register int c;
549
550   if (ASIDE) {
551         tok = aside;
552         ASIDE = 0;
553         return TOK;
554   }
555   do {
556         c = getc(db_in);
557         if (interrupted && c == EOF) {
558                 c = ' ';
559                 interrupted = 0;
560                 continue;
561         }
562   } while (c != EOF && class(c) == STSKIP);
563   if (c == EOF) {
564         eof_seen = 1;
565         return c;
566   }
567   if (extended_charset && in_ext(c)) {
568         TOK = get_name(c);
569         return TOK;
570   }
571   switch(class(c)) {
572   case STSTR:
573         TOK = (*currlang->get_string)(c);
574         break;
575   case STIDF:
576         if (in_expression) TOK = (*currlang->get_name)(c);
577         else TOK = get_name(c);
578         break;
579   case STNUM:
580         TOK = (*currlang->get_number)(c);
581         break;
582   case STNL:
583         TOK = c;
584         break;
585   case STSIMP:
586         if (! in_expression) {
587                 TOK = c;
588                 break;
589         }
590         /* Fall through */
591   default:
592         TOK = (*currlang->get_token)(c);
593         break;
594   }
595   return TOK;
596 }
597
598 int
599 get_name(c)
600   register int  c;
601 {
602   char  buf[512+1];
603   register char *p = &buf[0];
604   register struct idf *id;
605
606   do {
607         if (p - buf < 512) *p++ = c;
608         c = getc(db_in);
609   } while ((extended_charset && in_ext(c)) || in_idf(c));
610   ungetc(c, db_in);
611   *p++ = 0;
612   if (extended_charset) {
613         tok.idf = 0;
614         tok.str = Salloc(buf, (unsigned) (p - buf));
615         return NAME;
616   }
617   id = str2idf(buf, 1);
618   tok.idf = id;
619   tok.str = id->id_text;
620   return id->id_reserved ? id->id_reserved : NAME;
621 }
622
623 extern char *symbol2str();
624
625 LLmessage(t)
626 {
627   if (t > 0) {
628         if (! errorgiven) {
629                 error("%s missing before %s", symbol2str(t), symbol2str(TOK));
630         }
631         aside = tok;
632   }
633   else if (t == 0) {
634         if (! errorgiven) {
635                 error("%s unexpected", symbol2str(TOK));
636         }
637   }
638   else if (! errorgiven) {
639         error("EOF expected");
640   }
641   errorgiven = 1;
642 }
643
644 static void
645 catch_del()
646 {
647   signal(SIGINT, catch_del);
648   if (! disable_intr) {
649         signal_child(7);
650         child_interrupted = 1;
651   }
652   interrupted = 1;
653 }
654
655 void
656 init_del()
657 {
658   signal(SIGINT, catch_del);
659 }
660
661 static void
662 ctch()
663 {
664   /* Only for shell escapes ... */
665   signal(SIGINT, ctch);
666 }
667
668 #define SHBUFSIZ        512
669
670 static int
671 shellescape()
672 {
673   register char *p;                     /* walks through command */
674   static char previous[SHBUFSIZ];       /* previous command */
675   char comm[SHBUFSIZ];                  /* space for command */
676   register int cnt;                     /* prevent array bound errors */
677   register int c;                       /* current char */
678   register int lastc = 0;               /* will contain the previous char */
679
680   p = comm;
681   cnt = SHBUFSIZ-2;
682   while (c = getc(db_in), c != '\n') {
683         switch(c) {
684           case '!':
685                 /*
686                  * An unescaped ! expands to the previous
687                  * command, but disappears if there is none
688                  */
689                 if (lastc != '\\') {
690                         if (*previous) {
691                                 int len = strlen(previous);
692                                 if ((cnt -= len) <= 0) break;
693                                 strcpy(p,previous);
694                                 p += len;
695                         }
696                 }
697                 else {
698                         *p++ = c;
699                 }
700                 continue;
701           case '%':
702                 /*
703                  * An unescaped % will expand to the current
704                  * filename, but disappears is there is none
705                  */
706                 if (lastc != '\\') {
707                         if (listfile) {
708                                 int len = strlen(listfile->sy_idf->id_text);
709                                 if ((cnt -= len) <= 0) break;
710                                 strcpy(p,listfile->sy_idf->id_text);
711                                 p += len;
712                         }
713                 }
714                 else {
715                         *p++ = c;
716                 }
717                 continue;
718           default:
719                 lastc = c;
720                 if (cnt-- <= 0) break;
721                 *p++ = c;
722                 continue;
723         }
724         break;
725   }
726   *p = '\0';
727   if (c != '\n') {
728         warning("shell command too long");
729         while (c != '\n') c = getc(db_in);
730   }
731   ungetc(c, db_in);
732   strcpy(previous, comm);
733   signal(SIGINT, ctch);
734   cnt = system(comm);
735   signal(SIGINT, catch_del);
736   return cnt;
737 }
738 }