388 lines
12 KiB
Plaintext
388 lines
12 KiB
Plaintext
%{
|
|
/* This file is part of the Project Athena Zephyr Notification System.
|
|
* It is one of the source files comprising zwgc, the Zephyr WindowGram
|
|
* client.
|
|
*
|
|
* Created by: Marc Horowitz <marc@athena.mit.edu>
|
|
*
|
|
* $Id$
|
|
*
|
|
* Copyright (c) 1989 by the Massachusetts Institute of Technology.
|
|
* For copying and distribution information, see the file
|
|
* "mit-copyright.h".
|
|
*/
|
|
|
|
#include <sysdep.h>
|
|
|
|
#if (!defined(lint) && !defined(SABER))
|
|
static const char rcsid_parser_y[] = "$Id$";
|
|
#endif
|
|
|
|
#include <zephyr/mit-copyright.h>
|
|
|
|
/* Saber-C suppressions because yacc loses */
|
|
|
|
/*SUPPRESS 288*/
|
|
/*SUPPRESS 287*/
|
|
|
|
#include <stdio.h>
|
|
#include "lexer.h"
|
|
#include "parser.h"
|
|
#include "node.h"
|
|
#include "zwgc.h"
|
|
|
|
static void yyerror(char *);
|
|
|
|
/*
|
|
* the_program - local variable used to communicate the program's node
|
|
* representation from the program action to the parse_file
|
|
* function.
|
|
*/
|
|
|
|
static Node *the_program;
|
|
%}
|
|
|
|
%union{
|
|
char *text;
|
|
struct _Node *node;
|
|
}
|
|
|
|
%start program
|
|
|
|
%token ERROR
|
|
%token <text> VARNAME VARREF STRING SHOW
|
|
|
|
%token APPENDPORT BUFFER BREAK CLOSEINPUT CLOSEOUTPUT
|
|
%token CLOSEPORT CASE CLEARBUF DEFAULT DISPLAY DO DOWNCASE
|
|
%token ELSE ELSEIF ENDCASE ENDIF ENDWHILE EXEC EXECPORT EXIT
|
|
%token FIELDS GET GETENV IF INPUTPORT LANY LBREAK LSPAN
|
|
%token MATCH NOOP NOT OUTPUTPORT PRINT PROTECT VERBATIM PUT RANY RBREAK
|
|
%token RSPAN SET SUBSTITUTE THEN UPCASE WHILE ZVAR STYLESTRIP
|
|
|
|
%type <node> expr varname string
|
|
%type <node> exprlist comma_exprlist varnamelist
|
|
%type <node> statement statements program elseparts elseifparts
|
|
%type <node> match matchlist
|
|
|
|
%left '|'
|
|
%left '&'
|
|
%left EQ NEQ REGEQ REGNEQ
|
|
%left '+'
|
|
%left '!'
|
|
|
|
%%
|
|
|
|
/*
|
|
* A program is simply a list of statements: (may be NULL if no statements...)
|
|
*/
|
|
program : statements
|
|
{ the_program = reverse_list_of_nodes($1);
|
|
$$ = the_program; }
|
|
;
|
|
|
|
varname : VARNAME
|
|
{ $$ = node_create_string_constant(VARNAME_OPCODE, $1); }
|
|
;
|
|
|
|
string : STRING
|
|
{ $$ = node_create_string_constant(STRING_CONSTANT_OPCODE, $1); }
|
|
;
|
|
|
|
expr : '(' expr ')'
|
|
{ $$ = $2; }
|
|
|
|
| string
|
|
{ $$ = $1; }
|
|
| VARREF
|
|
{ $$ = node_create_string_constant(VARREF_OPCODE, $1); }
|
|
|
|
| '!' expr
|
|
{ $$ = node_create_unary(NOT_OPCODE, $2); }
|
|
|
|
| expr '+' expr
|
|
{ $$ = node_create_binary(PLUS_OPCODE, $1, $3); }
|
|
| expr '|' expr /* note "or" == '|' */
|
|
{ $$ = node_create_binary(OR_OPCODE, $1, $3); }
|
|
| expr '&' expr /* note "and" == '&' */
|
|
{ $$ = node_create_binary(AND_OPCODE, $1, $3); }
|
|
| expr EQ expr
|
|
{ $$ = node_create_binary(EQ_OPCODE, $1, $3); }
|
|
| expr NEQ expr
|
|
{ $$ = node_create_binary(NEQ_OPCODE, $1, $3); }
|
|
| expr REGEQ expr
|
|
{ $$ = node_create_binary(REGEQ_OPCODE, $1, $3); }
|
|
| expr REGNEQ expr
|
|
{ $$ = node_create_binary(REGNEQ_OPCODE, $1, $3); }
|
|
|
|
| BUFFER '(' ')'
|
|
{ $$ = node_create_noary(BUFFER_OPCODE); }
|
|
|
|
| SUBSTITUTE '(' expr ')'
|
|
{ $$ = node_create_unary(SUBSTITUTE_OPCODE, $3); }
|
|
| PROTECT '(' expr ')'
|
|
{ $$ = node_create_unary(PROTECT_OPCODE, $3); }
|
|
| VERBATIM '(' expr ')'
|
|
{ $$ = node_create_unary(VERBATIM_OPCODE, $3); }
|
|
| GETENV '(' expr ')'
|
|
{ $$ = node_create_unary(GETENV_OPCODE, $3); }
|
|
| UPCASE '(' expr ')'
|
|
{ $$ = node_create_unary(UPCASE_OPCODE, $3); }
|
|
| DOWNCASE '(' expr ')'
|
|
{ $$ = node_create_unary(DOWNCASE_OPCODE, $3); }
|
|
| ZVAR '(' expr ')'
|
|
{ $$ = node_create_unary(ZVAR_OPCODE, $3); }
|
|
| GET '(' expr ')'
|
|
{ $$ = node_create_unary(GET_OPCODE, $3); }
|
|
| STYLESTRIP '(' expr ')'
|
|
{ $$ = node_create_unary(STYLESTRIP_OPCODE, $3); }
|
|
|
|
| LANY '(' expr ',' expr ')'
|
|
{ $$ = node_create_binary(LANY_OPCODE, $3, $5 ); }
|
|
| RANY '(' expr ',' expr ')'
|
|
{ $$ = node_create_binary(RANY_OPCODE, $3, $5 ); }
|
|
| LBREAK '(' expr ',' expr ')'
|
|
{ $$ = node_create_binary(LBREAK_OPCODE, $3, $5 ); }
|
|
| RBREAK '(' expr ',' expr ')'
|
|
{ $$ = node_create_binary(RBREAK_OPCODE, $3, $5 ); }
|
|
| LSPAN '(' expr ',' expr ')'
|
|
{ $$ = node_create_binary(LSPAN_OPCODE, $3, $5 ); }
|
|
| RSPAN '(' expr ',' expr ')'
|
|
{ $$ = node_create_binary(RSPAN_OPCODE, $3, $5 ); }
|
|
;
|
|
|
|
statement : NOOP
|
|
{ $$ = node_create_noary(NOOP_OPCODE); }
|
|
| SET varname '=' expr
|
|
{ $$ = node_create_binary(SET_OPCODE, $2, $4); }
|
|
| FIELDS varnamelist
|
|
{ $$ = node_create_unary(FIELDS_OPCODE,
|
|
reverse_list_of_nodes($2)); }
|
|
|
|
/*
|
|
* Output to & control of output buffer statements:
|
|
*/
|
|
| PRINT exprlist
|
|
{ $$ = node_create_unary(PRINT_OPCODE,
|
|
reverse_list_of_nodes($2)); }
|
|
| SHOW
|
|
{ $$ = node_create_unary(PRINT_OPCODE,
|
|
node_create_unary(SUBSTITUTE_OPCODE,
|
|
node_create_string_constant(STRING_CONSTANT_OPCODE,
|
|
$1))); }
|
|
| CLEARBUF
|
|
{ $$ = node_create_noary(CLEARBUF_OPCODE); }
|
|
|
|
/*
|
|
* Statements to manage ports:
|
|
*/
|
|
| APPENDPORT expr expr
|
|
{ $$ = node_create_binary(APPENDPORT_OPCODE, $2, $3); }
|
|
| EXECPORT expr expr exprlist
|
|
{ $3->next = reverse_list_of_nodes($4);
|
|
$$ = node_create_binary(EXECPORT_OPCODE, $2, $3); }
|
|
| INPUTPORT expr expr
|
|
{ $$ = node_create_binary(INPUTPORT_OPCODE, $2, $3); }
|
|
| OUTPUTPORT expr expr
|
|
{ $$ = node_create_binary(OUTPUTPORT_OPCODE, $2, $3); }
|
|
| PUT expr exprlist
|
|
{ $$ = node_create_binary(PUT_OPCODE, $2,
|
|
reverse_list_of_nodes($3)); }
|
|
| PUT
|
|
{ $$ = node_create_binary(PUT_OPCODE, 0, 0); }
|
|
| CLOSEINPUT expr
|
|
{ $$ = node_create_unary(CLOSEINPUT_OPCODE, $2); }
|
|
| CLOSEOUTPUT expr
|
|
{ $$ = node_create_unary(CLOSEOUTPUT_OPCODE, $2); }
|
|
| CLOSEPORT expr
|
|
{ $$ = node_create_unary(CLOSEPORT_OPCODE, $2); }
|
|
|
|
/*
|
|
* Statements to run subprocesses without I/O to them:
|
|
*/
|
|
| EXEC expr exprlist
|
|
{ $2->next = reverse_list_of_nodes($3);
|
|
$$ = node_create_unary(EXEC_OPCODE, $2); }
|
|
|
|
/*
|
|
* Control statements:
|
|
*/
|
|
| IF expr THEN statements elseparts ENDIF
|
|
{ Node *n = node_create_binary(IF_OPCODE, $2,
|
|
reverse_list_of_nodes($4));
|
|
n->next = $5;
|
|
$$ = node_create_unary(IF_STMT_OPCODE, n); }
|
|
| CASE expr matchlist ENDCASE
|
|
{ $$ = node_create_binary(CASE_OPCODE, $2,
|
|
reverse_list_of_nodes($3)); }
|
|
| WHILE expr DO statements ENDWHILE
|
|
{ $$ = node_create_binary(WHILE_OPCODE, $2,
|
|
reverse_list_of_nodes($4)); }
|
|
| BREAK
|
|
{ $$ = node_create_noary(BREAK_OPCODE); }
|
|
| EXIT
|
|
{ $$ = node_create_noary(EXIT_OPCODE); }
|
|
;
|
|
|
|
elseparts : elseifparts
|
|
{ $$ = reverse_list_of_nodes($1); }
|
|
| elseifparts ELSE statements
|
|
{ $$ = node_create_binary(ELSE_OPCODE, 0,
|
|
reverse_list_of_nodes($3));
|
|
$$->next = $1;
|
|
$$ = reverse_list_of_nodes($$); }
|
|
;
|
|
|
|
/* elseifparts needs to be reversed before using... */
|
|
elseifparts : /* empty */
|
|
{ $$ = 0; }
|
|
| elseifparts ELSEIF expr THEN statements
|
|
{ $$ = node_create_binary(ELSEIF_OPCODE, $3,
|
|
reverse_list_of_nodes($5));
|
|
$$->next = $1; }
|
|
;
|
|
|
|
match : MATCH comma_exprlist statements
|
|
{ $$ = node_create_binary(MATCHLIST_OPCODE,
|
|
reverse_list_of_nodes($2),
|
|
reverse_list_of_nodes($3)); }
|
|
| DEFAULT statements
|
|
{ $$ = node_create_binary(DEFAULT_OPCODE, 0,
|
|
reverse_list_of_nodes($2)); }
|
|
;
|
|
|
|
/*
|
|
* Various lists of non-terminals like expr's and varname's. Each is
|
|
* built up as a linked list using the nodes' next fields. To prevent
|
|
* Yacc stack overflow on long lists, these are put on the linked list
|
|
* BACKWARDS. The user of these must first call reverse_list_of_nodes
|
|
* on one of these before using it. All except comma_exprlist
|
|
* allow 0 elements on the list in which case their value is NULL.
|
|
* (comma_exprlist requires at least one element)
|
|
*/
|
|
|
|
exprlist : /* empty */
|
|
{ $$ = 0; }
|
|
| exprlist expr
|
|
{ $$ = $2;
|
|
$$->next = $1; }
|
|
;
|
|
|
|
comma_exprlist : expr
|
|
{ $$ = $1; }
|
|
| comma_exprlist ',' expr
|
|
{ $$ = $3;
|
|
$$->next = $1; }
|
|
;
|
|
|
|
varnamelist : /* empty */
|
|
{ $$ = 0; }
|
|
| varnamelist varname
|
|
{ $$ = $2;
|
|
$$->next = $1; }
|
|
;
|
|
|
|
matchlist : /* empty */
|
|
{ $$ = 0; }
|
|
| matchlist match
|
|
{ $$ = $2;
|
|
$$->next = $1; }
|
|
;
|
|
|
|
statements : /* empty */
|
|
{ $$ = 0; }
|
|
| statements statement
|
|
{ $$ = $2;
|
|
$$->next = $1; }
|
|
;
|
|
|
|
%%
|
|
|
|
/*
|
|
* error_occured - Set to true when a parse error is reported. If it is false
|
|
* at the time a parse error is reported, a message is
|
|
* printed on stderr. See report_parse_error for more
|
|
* details.
|
|
*/
|
|
|
|
static int error_occured = 0;
|
|
|
|
/*
|
|
* Parser-Lexer Internal Routine:
|
|
*
|
|
* void report_parse_error(char *error_message, int line_number)
|
|
* Modifies: error_occured, stderr
|
|
* Effects: This routine is called to report a parser or lexer
|
|
* error. Error_message is the error message and line_number
|
|
* the line number it occured on. The reported error message
|
|
* is of the form "....<error_message> on line <line #>.\n".
|
|
* This routine sets error_occured (local to parser.y) to
|
|
* true. If it was previously false, the error message
|
|
* is reported to the user via stderr.
|
|
*/
|
|
|
|
void
|
|
report_parse_error(char *error_message,
|
|
int line_number)
|
|
{
|
|
if (error_occured)
|
|
return;
|
|
error_occured = 1;
|
|
|
|
fprintf(stderr, "zwgc: error in description file: %s on line %d.\n",
|
|
error_message, line_number);
|
|
fflush(stderr);
|
|
}
|
|
|
|
/*
|
|
* yyerror - internal routine - used by yacc to report syntax errors and
|
|
* stack overflow errors.
|
|
*/
|
|
|
|
static void yyerror(char *message)
|
|
{
|
|
report_parse_error(message, yylineno);
|
|
}
|
|
|
|
/*
|
|
* struct _Node *parse_file(FILE *input_file)
|
|
* Requires: input_file is opened for reading, no pointers to
|
|
* existing nodes will ever be dereferened.
|
|
* Modifies: *input_file, stderr, all existing nodes
|
|
* Effects: First this routine destroys all nodes. Then it parses
|
|
* input_file as a zwgc description langauge file. If
|
|
* an error is encountered, an error message is printed
|
|
* on stderr and NULL is returned. If no error is
|
|
* encountered, a pointer to the node representation of
|
|
* the parsed program is returned, suitable for passing to
|
|
* exec.c. Note that NULL will also be returned for a
|
|
* empty file & is a valid program. Either way, input_file
|
|
* is closed before this routine returns.
|
|
*/
|
|
|
|
struct _Node *
|
|
parse_file(FILE *input_file)
|
|
{
|
|
the_program = NULL;
|
|
error_occured = 0;
|
|
node_DestroyAllNodes();
|
|
|
|
lex_open(input_file);
|
|
yyparse();
|
|
fclose(input_file);
|
|
|
|
if (error_occured) {
|
|
node_DestroyAllNodes();
|
|
the_program = NULL;
|
|
}
|
|
|
|
#ifdef DEBUG
|
|
if (zwgc_debug) {
|
|
printf("****************************************************************************\n");
|
|
node_display(the_program);
|
|
printf("****************************************************************************\n");
|
|
}
|
|
#endif
|
|
|
|
return(the_program);
|
|
}
|