]> asedeno.scripts.mit.edu Git - 1ts-debian.git/blob - zephyr/zwgc/parser.y
r4264@bucket (orig r254): kcr | 2008-01-20 22:11:44 -0500
[1ts-debian.git] / zephyr / zwgc / parser.y
1 %{
2 /* This file is part of the Project Athena Zephyr Notification System.
3  * It is one of the source files comprising zwgc, the Zephyr WindowGram
4  * client.
5  *
6  *      Created by:     Marc Horowitz <marc@athena.mit.edu>
7  *
8  *      $Id$
9  *
10  *      Copyright (c) 1989 by the Massachusetts Institute of Technology.
11  *      For copying and distribution information, see the file
12  *      "mit-copyright.h".
13  */
14
15 #include <sysdep.h>
16
17 #if (!defined(lint) && !defined(SABER))
18 static const char rcsid_parser_y[] = "$Id$";
19 #endif
20
21 #include <zephyr/mit-copyright.h>
22
23 /* Saber-C suppressions because yacc loses */
24
25 /*SUPPRESS 288*/
26 /*SUPPRESS 287*/
27
28 #include <stdio.h>
29 #include "lexer.h"
30 #include "parser.h"
31 #include "node.h"
32 #include "zwgc.h"
33
34 static void yyerror(char *);
35
36 /*
37  * the_program - local variable used to communicate the program's node
38  *               representation from the program action to the parse_file
39  *               function.
40  */
41
42 static Node *the_program;
43 %}
44
45 %union{
46     char *text;
47     struct _Node *node;
48 }
49
50 %start program
51
52 %token  ERROR
53 %token  <text>    VARNAME VARREF STRING SHOW
54
55 %token  APPENDPORT BUFFER BREAK CLOSEINPUT CLOSEOUTPUT
56 %token  CLOSEPORT CASE CLEARBUF DEFAULT DISPLAY DO DOWNCASE
57 %token  ELSE ELSEIF ENDCASE ENDIF ENDWHILE EXEC EXECPORT EXIT
58 %token  FIELDS GET GETENV IF INPUTPORT LANY LBREAK LSPAN
59 %token  MATCH NOOP NOT OUTPUTPORT PRINT PROTECT VERBATIM PUT RANY RBREAK
60 %token  RSPAN SET SUBSTITUTE THEN UPCASE WHILE ZVAR STYLESTRIP
61
62 %type <node> expr varname string
63 %type <node> exprlist comma_exprlist varnamelist
64 %type <node> statement statements program elseparts elseifparts
65 %type <node> match matchlist
66
67 %left '|'
68 %left '&'
69 %left EQ NEQ REGEQ REGNEQ
70 %left '+'
71 %left '!'
72
73 %%
74
75 /*
76  * A program is simply a list of statements: (may be NULL if no statements...)
77  */
78 program : statements
79         { the_program = reverse_list_of_nodes($1);
80           $$ = the_program; }
81         ;
82
83 varname : VARNAME
84        { $$ = node_create_string_constant(VARNAME_OPCODE, $1); }
85         ;
86
87 string : STRING
88        { $$ = node_create_string_constant(STRING_CONSTANT_OPCODE, $1); }
89        ;
90
91 expr : '(' expr ')'
92         { $$ = $2; }
93
94      | string
95         { $$ = $1; }
96      | VARREF
97        { $$ = node_create_string_constant(VARREF_OPCODE, $1); }
98
99      | '!' expr
100         { $$ = node_create_unary(NOT_OPCODE, $2); }
101
102      | expr '+' expr
103         { $$ = node_create_binary(PLUS_OPCODE, $1, $3); }
104      | expr '|' expr                             /* note "or" == '|' */
105         { $$ = node_create_binary(OR_OPCODE, $1, $3); }
106      | expr '&' expr                             /* note "and" == '&' */
107         { $$ = node_create_binary(AND_OPCODE, $1, $3); }
108      | expr EQ expr
109         { $$ = node_create_binary(EQ_OPCODE, $1, $3); }
110      | expr NEQ expr
111         { $$ = node_create_binary(NEQ_OPCODE, $1, $3); }
112      | expr REGEQ expr
113         { $$ = node_create_binary(REGEQ_OPCODE, $1, $3); }
114      | expr REGNEQ expr
115         { $$ = node_create_binary(REGNEQ_OPCODE, $1, $3); }
116
117      | BUFFER '(' ')'
118         { $$ = node_create_noary(BUFFER_OPCODE); }
119
120      | SUBSTITUTE '(' expr ')'
121         { $$ = node_create_unary(SUBSTITUTE_OPCODE, $3); }
122      | PROTECT '(' expr ')'
123         { $$ = node_create_unary(PROTECT_OPCODE, $3); }
124      | VERBATIM '(' expr ')'
125         { $$ = node_create_unary(VERBATIM_OPCODE, $3); }
126      | GETENV '(' expr ')'
127         { $$ = node_create_unary(GETENV_OPCODE, $3); }
128      | UPCASE '(' expr ')'
129         { $$ = node_create_unary(UPCASE_OPCODE, $3); }
130      | DOWNCASE '(' expr ')'
131         { $$ = node_create_unary(DOWNCASE_OPCODE, $3); }
132      | ZVAR '(' expr ')'
133         { $$ = node_create_unary(ZVAR_OPCODE, $3); }
134      | GET '(' expr ')'
135         { $$ = node_create_unary(GET_OPCODE, $3); }
136      | STYLESTRIP '(' expr ')'
137         { $$ = node_create_unary(STYLESTRIP_OPCODE, $3); }
138
139      | LANY '(' expr ',' expr ')'
140         { $$ = node_create_binary(LANY_OPCODE, $3, $5 ); }
141      | RANY '(' expr ',' expr ')'
142         { $$ = node_create_binary(RANY_OPCODE, $3, $5 ); }
143      | LBREAK '(' expr ',' expr ')'
144         { $$ = node_create_binary(LBREAK_OPCODE, $3, $5 ); }
145      | RBREAK '(' expr ',' expr ')'
146         { $$ = node_create_binary(RBREAK_OPCODE, $3, $5 ); }
147      | LSPAN '(' expr ',' expr ')'
148         { $$ = node_create_binary(LSPAN_OPCODE, $3, $5 ); }
149      | RSPAN '(' expr ',' expr ')'
150         { $$ = node_create_binary(RSPAN_OPCODE, $3, $5 ); }
151      ;
152
153 statement : NOOP
154               { $$ = node_create_noary(NOOP_OPCODE); }
155           | SET varname '=' expr
156               { $$ = node_create_binary(SET_OPCODE, $2, $4); }
157           | FIELDS varnamelist
158               { $$ = node_create_unary(FIELDS_OPCODE,
159                                        reverse_list_of_nodes($2)); }
160
161          /*
162           * Output to & control of output buffer statements:
163           */
164           | PRINT exprlist
165               { $$ = node_create_unary(PRINT_OPCODE,
166                                        reverse_list_of_nodes($2)); }
167           | SHOW
168               { $$ = node_create_unary(PRINT_OPCODE,
169                        node_create_unary(SUBSTITUTE_OPCODE,
170                          node_create_string_constant(STRING_CONSTANT_OPCODE,
171                                                      $1))); }
172           | CLEARBUF
173               { $$ = node_create_noary(CLEARBUF_OPCODE); }
174
175           /*
176            * Statements to manage ports:
177            */
178           | APPENDPORT expr expr
179               { $$ = node_create_binary(APPENDPORT_OPCODE, $2, $3); }
180           | EXECPORT expr expr exprlist
181               { $3->next = reverse_list_of_nodes($4);
182                 $$ = node_create_binary(EXECPORT_OPCODE, $2, $3); }
183           | INPUTPORT expr expr
184               { $$ = node_create_binary(INPUTPORT_OPCODE, $2, $3); }
185           | OUTPUTPORT expr expr
186               { $$ = node_create_binary(OUTPUTPORT_OPCODE, $2, $3); }
187           | PUT expr exprlist
188               { $$ = node_create_binary(PUT_OPCODE, $2,
189                                         reverse_list_of_nodes($3)); }
190           | PUT
191               { $$ = node_create_binary(PUT_OPCODE, 0, 0); }
192           | CLOSEINPUT expr
193               { $$ = node_create_unary(CLOSEINPUT_OPCODE, $2); }
194           | CLOSEOUTPUT expr
195               { $$ = node_create_unary(CLOSEOUTPUT_OPCODE, $2); }
196           | CLOSEPORT expr
197               { $$ = node_create_unary(CLOSEPORT_OPCODE, $2); }
198
199           /*
200            * Statements to run subprocesses without I/O to them:
201            */
202           | EXEC expr exprlist
203               { $2->next = reverse_list_of_nodes($3);
204                 $$ = node_create_unary(EXEC_OPCODE, $2); }
205
206           /*
207            * Control statements:
208            */
209           | IF expr THEN statements elseparts ENDIF
210               { Node *n = node_create_binary(IF_OPCODE, $2,
211                                              reverse_list_of_nodes($4));
212                 n->next = $5;
213                 $$ = node_create_unary(IF_STMT_OPCODE, n); }
214           | CASE expr matchlist ENDCASE
215               { $$ = node_create_binary(CASE_OPCODE, $2,
216                                         reverse_list_of_nodes($3)); }
217           | WHILE expr DO statements ENDWHILE
218               { $$ = node_create_binary(WHILE_OPCODE, $2,
219                                         reverse_list_of_nodes($4)); }
220           | BREAK
221               { $$ = node_create_noary(BREAK_OPCODE); }
222           | EXIT
223               { $$ = node_create_noary(EXIT_OPCODE); }
224           ;
225
226 elseparts : elseifparts
227                 { $$ = reverse_list_of_nodes($1); }
228           | elseifparts ELSE statements
229                 { $$ = node_create_binary(ELSE_OPCODE, 0,
230                                           reverse_list_of_nodes($3));
231                   $$->next = $1;
232                   $$ = reverse_list_of_nodes($$); }
233           ;
234
235 /* elseifparts needs to be reversed before using... */
236 elseifparts : /* empty */
237                 { $$ = 0; }
238             | elseifparts ELSEIF expr THEN statements
239                 { $$ = node_create_binary(ELSEIF_OPCODE, $3,
240                                           reverse_list_of_nodes($5));
241                   $$->next = $1; }
242             ;
243
244 match : MATCH comma_exprlist statements
245                 { $$ = node_create_binary(MATCHLIST_OPCODE,
246                                           reverse_list_of_nodes($2),
247                                           reverse_list_of_nodes($3)); }
248       | DEFAULT statements
249                 { $$ = node_create_binary(DEFAULT_OPCODE, 0,
250                                           reverse_list_of_nodes($2)); }
251       ;
252
253 /*
254  * Various lists of non-terminals like expr's and varname's.  Each is
255  * built up as a linked list using the nodes' next fields.  To prevent
256  * Yacc stack overflow on long lists, these are put on the linked list
257  * BACKWARDS.  The user of these must first call reverse_list_of_nodes
258  * on one of these before using it.  All except comma_exprlist
259  * allow 0 elements on the list in which case their value is NULL.
260  * (comma_exprlist requires at least one element)
261  */
262
263 exprlist : /* empty */
264              { $$ = 0; }
265          | exprlist expr
266              { $$ = $2;
267                $$->next = $1; }
268          ;
269
270 comma_exprlist : expr
271                  { $$ = $1; }
272                | comma_exprlist ',' expr
273                  { $$ = $3;
274                    $$->next = $1; }
275                ;
276
277 varnamelist : /* empty */
278              { $$ = 0; }
279             | varnamelist varname
280              { $$ = $2;
281                $$->next = $1; }
282             ;
283
284 matchlist : /* empty */
285                 { $$ = 0; }
286           | matchlist match
287                 { $$ = $2;
288                   $$->next = $1; }
289           ;
290
291 statements : /* empty */
292         { $$ = 0; }
293            | statements statement
294         { $$ = $2;
295           $$->next = $1; }
296            ;
297
298 %%
299
300 /*
301  * error_occured - Set to true when a parse error is reported.  If it is false
302  *                 at the time a parse error is reported, a message is
303  *                 printed on stderr.  See report_parse_error for more
304  *                 details.
305  */
306
307 static int error_occured = 0;
308
309 /*
310  *  Parser-Lexer Internal Routine:
311  *
312  *    void report_parse_error(char *error_message, int line_number)
313  *        Modifies: error_occured, stderr
314  *        Effects: This routine is called to report a parser or lexer
315  *                 error.  Error_message is the error message and line_number
316  *                 the line number it occured on.  The reported error message
317  *                 is of the form "....<error_message> on line <line #>.\n".
318  *                 This routine sets error_occured (local to parser.y) to
319  *                 true.  If it was previously false, the error message
320  *                 is reported to the user via stderr. 
321  */
322
323 void
324 report_parse_error(char *error_message,
325                    int line_number)
326 {
327     if (error_occured)
328       return;
329     error_occured = 1;
330
331     fprintf(stderr, "zwgc: error in description file: %s on line %d.\n",
332             error_message, line_number);
333     fflush(stderr);
334 }
335
336 /*
337  *  yyerror - internal routine - used by yacc to report syntax errors and
338  *            stack overflow errors.
339  */
340  
341 static void yyerror(char *message)
342 {
343     report_parse_error(message, yylineno);
344 }
345
346 /*
347  *    struct _Node *parse_file(FILE *input_file)
348  *        Requires: input_file is opened for reading, no pointers to
349  *                  existing nodes will ever be dereferened.
350  *        Modifies: *input_file, stderr, all existing nodes
351  *        Effects: First this routine destroys all nodes.  Then it parses
352  *                 input_file as a zwgc description langauge file.  If
353  *                 an error is encountered, an error message is printed
354  *                 on stderr and NULL is returned.  If no error is
355  *                 encountered, a pointer to the node representation of
356  *                 the parsed program is returned, suitable for passing to
357  *                 exec.c.  Note that NULL will also be returned for a
358  *                 empty file & is a valid program.  Either way, input_file
359  *                 is closed before this routine returns.
360  */
361
362 struct _Node *
363 parse_file(FILE *input_file)
364 {
365     the_program = NULL;
366     error_occured = 0;
367     node_DestroyAllNodes();
368
369     lex_open(input_file);
370     yyparse();
371     fclose(input_file);
372
373     if (error_occured) {
374         node_DestroyAllNodes();
375         the_program = NULL;
376     }
377
378 #ifdef DEBUG
379     if (zwgc_debug) {
380         printf("****************************************************************************\n");
381         node_display(the_program);
382         printf("****************************************************************************\n");
383     }
384 #endif
385     
386     return(the_program);
387 }