Digital Research
This commit is contained in:
2020-11-06 18:50:37 +01:00
parent 621ed8ccaf
commit 31738079c4
8481 changed files with 1888323 additions and 0 deletions

View File

@@ -0,0 +1,44 @@
# $Id: Makefile,v 1.3 1994/02/28 22:28:36 hays Exp $
LEX=flex
LEXFLAGS= -v
YACC=bison
YACCFLAGS= -y -v -d
CFLAGS= -g
DIST=README Makefile plm-lex.l plm-manifest.h plm-parse.y scope.c scope.h
OBJS= plm-lex.o plm-parse.o scope.o tree.o
#NOTE: tree.o and tree.h come from Paul Vixie's PD AVL Tree package,
# comp.sources.unix Volume 27, Issue 34.
.y.c:
plm: $(OBJS)
$(CC) $(CFLAGS) -o $@ $(OBJS)
plm-parse.h: plm-parse.c
plm-lex.c: plm-lex.l
$(LEX) $(LEXFLAGS) -t $< > $@
plm-parse.c: plm-parse.y
$(YACC) $(YACCFLAGS) $< -o $@
plm-lex.o: plm-lex.c plm-manifest.h plm-parse.h scope.h
plm-parse.o: plm-parse.c plm-manifest.h scope.h
scope.o: scope.c tree.h scope.h
clean:
rm -f plm-parse.c plm-parse.h plm-parse.output \
plm-*.o scope.o lex.backtrack tmp *~ plm-lex.c core plm \
plm.shar
source:
$(CO) $(DIST)
dist: source
shar $(DIST) > plm.shar

View File

@@ -0,0 +1,189 @@
$Id: README,v 1.4 1994/04/05 20:33:58 hays Exp $
Ah, the wisdom of the ages...
Introduction
------------
What you are looking at is a basic (very basic) parser for a PL/M
language.
The parser does nothing useful, and it isn't even a terribly wonderful
example. On the other hand, it appears that no one else has bothered
to publish even this much, before.
However, the parser does recognize a language very like PL/M-86,
PL/M-286, or PL/M-386, as best we can determine.
All the information used to derive this parser comes from published
manuals, sold to the public. No proprietary information, trade
secrets, patented information, corporate assets, or skulduggery was
used to develop this parser. Neither of the authors has ever seen the
source to a working PL/M compiler (or, for that matter, to a
non-working PL/M compiler).
Implementation Limits
---------------------
This PL/M parser was developed and tested on a 486DX2/66 clone PC
running Linux. The C code is written for an ANSI-compliant C
compiler; GCC was used in our testing. Also, flex and bison were
used, not lex and yacc. Paul Vixie's comp.sources.unix implementation
of AVL trees was used to implement symbol table lookups.
You should expect some problems if you plan on building this parser
with a K&R style C compiler. Using yacc and/or lex may be
problematic, as well.
This parser does not support any of the "dollar" directives of a
proper PL/M compiler. In fact, it will croak with the helpful message
"parse error". Thus, implementing include files and compiler
directives is left as an exercise for the reader.
The macro facility (aka "literally" declarations) depends on the
lexical analysis skeleton allowing multiple characters of push-back on
the input stream. This is a very, very poor assumption, but, with
flex, at least, workable for this example. A real PL/M compiler would
allow literals of unlimited length. To find the offending code, grep
for the string "very weak" in the file "plm-lex.l".
No error recovery is implemented in the parser, at all.
There are no shift-reduce conflicts, nor reduce-reduce conflicts.
There are a couple of places in the parser where similar constructs
cannot be distinguished, except by semantic analysis. These are
marked by appropriate comments in the parser source file.
The "scoped literal table" implementation depends on Paul Vixie's
(paul@vix.com) public domain AVL tree code, available as
comp.sources.unix Volume 27, Issue 34 (`avl-subs'), at a friendly ftp
site near you. We use "gatekeeper.dec.com". The benefits of using
AVL trees for a symbol table (versus, say, hashing) are not subject to
discussion. We used the avl-subs source code because it is reliable
and easy to use.
This grammar has been validated against about 10,000 lines of real and
artificial PL/M code.
PL/M Quirks
-----------
PL/M has some very interesting quirks. For example, a value is
considered to be "true", for the purposes of an `if' test, if it is
odd (low bit set). Thus, the value 0x3 is true, whereas 0x4 is not.
The language itself, given a boolean expression, generates the value
0xff for true. [This factoid doesn't affect the parser per se, but
does appear to be the main pitfall for those whose hubris leads them
to translate PL/M to C.]
String constants can contain any ASCII value, excepting a single
apostrophe, a newline, or 0x81. The latter, presumably, has something
to do with Kanji support.
To embed a single apostrophe in a string constant, two apostrophes may
be used. Thus, 'k''s' is a string consisting of a letter k, a single
apostrophe, and a letter s. Strings are not null terminated, so our
example string, 'k''s', requires just three bytes of storage.
PL/M supports a macro language, of sorts, that is integrated into the
language's declaration syntax:
declare Ford literally 'Edsel';
declare Mercury literally 'Ford';
After the above declarations, any instance of the identifier "Ford"
will be replaced with the string "Edsel", and any occurrence of the
identifier "Mercury" will be replaced by the string "Ford", which will
then be replaced by the string "Edsel". The literal string can be
more complicated, of course. Only identifiers are subject to
substitution - substitution does not occur inside string constants.
Literal macros are parameterless, and obey the scoping rules of the
language. Thus, it is possible to have different values for the same
macro in different, non-nested scopes. [Exercise: Why can't you have
different values for literals in nested scopes?]
Keywords, of course, cannot be macro names, because they are not
allowed as variable names.
PL/M allows dollar signs ("$") to be used inside keywords,
identifiers, and numerical constants. PL/M is also case insensitive.
Thus, the following two identifiers are the "same":
my_very_own_variable_02346
m$Y_$$$VeRy_$$O$$$$$W$$$$$$N_varIABLE$$$$$$$$$$_$02$346
Loverly, eh? Obfuscated C, stand to the side.
Casting in PL/M (a relatively late addition to the language) is
provided by a motley assortment of functions with the same names as
the basic types to which they are casting, accepting a single argument
of some other (or even the same) type.
Note that the EBNF grammar published in what must be considered the
definitive work, _PL/M Programmer's Guide_, Intel order number
452161-003, Appendix C, is incorrect in several respects. If you're
interested in the differences, we've preserved, as much as is
possible, the production names of that EBNF in the YACCable grammar.
Some known problems with the published, Appendix C, EBNF grammar:
- One of the productions is an orphan, ("scoping_statements").
- unary minus is shown as a prefix operator, and unary plus as a
postfix operator ("secondary").
- Casting does not appear in the published grammar.
- Nested structures do not appear in the published grammar, and
the reference syntax for selecting a nested structure member
is also missing.
- The WORD type is missing from the "basic_type" production.
- The "initialization" production allows the initial value list
only after the INITIAL keyword, when, in fact, the initial value
list may follow the DATA keyword, as well.
On the other hand, the precedence of the expression operators is
correct as written in the EBNF grammar, the dangling else problem is
non-existent, and there are no associativity problems, as all
operators associate left-to-right.
To complicate matters, the above referenced manual may be out of
print. A more recent version, which covers the PL/M-386 dialect only,
is _PL/M-386 Programmer's Guide_, Intel order number 611052-001.
The latter manual has some corrections, but has some introduced errors
in the EBNF, as well. The problems with the unary minus and the
"initialization" production are repaired, but the definition for a
"binary_number" is malformed, as are the definitions for the
"fractional_part", "string_body_element", "variable_element", and
"if_condition" productions.
We're right, they're wrong.
The Authors
-----------
Gary Funck (gary@intrepid.com) was responsible for starting this
effort. He authored the original grammar.
Kirk Hays (hays@ichips.intel.com) wrote the lexical analyzer and the
scoped literal table implementation. He also validated and corrected
the grammar, and extended it to cover documented features not
appearing in the published EBNF.
Future Plans
------------
If there is enough interest (or, even if there isn't), Kirk is
planning on producing a PL/M front end for the GNU compiler. Contact
him at the above Email address for further information. Donations of
PL/M source code of any dialect (including PL/M-80, PL/M-51, and
PL/M-96)(yes, we already have the Kermit implementations), or a
willingness to be a pre-alpha tester with code you cannot donate, are
sufficient grounds to contact Kirk.

View File

@@ -0,0 +1,301 @@
%{
/* lexer for PL/M syntax.
$Id: plm-lex.l,v 1.2 1994/02/28 22:24:34 hays Exp $
Copyright 1994 by Kirk Hays (hays@ichips.intel.com) and Gary Funck
(gary@intrepid.com)
USE AT YOUR OWN RISK. NO WARRANTY EXPRESSED OR IMPLIED.
This code is distributed in the hope that it will be useful,
but without any warranty. Further, there is no implied warranty of
merchantability or fitness for a particular purpose.
*/
/* This file defines the syntax of PL/M. */
#include <ctype.h>
#include <string.h>
#include "plm-manifest.h"
#include "plm-parse.h"
#include "scope.h"
int lineno = 1;
/* forward references */
char * canonical_string (char *);
char * canonical_identifier (char *);
static void error (char *);
%}
DIGIT_STRING [0-9][0-9$]*
A [Aa]($)*
B [Bb]($)*
C [Cc]($)*
D [Dd]($)*
E [Ee]($)*
F [Ff]($)*
G [Gg]($)*
H [Hh]($)*
I [Ii]($)*
L [Ll]($)*
M [Mm]($)*
N [Nn]($)*
O [Oo]($)*
P [Pp]($)*
Q [Qq]($)*
R [Rr]($)*
S [Ss]($)*
T [Tt]($)*
U [Uu]($)*
W [Ww]($)*
X [Xx]($)*
Y [Yy]($)*
%%
{A}{D}{D}{R}{E}{S}{S} return ADDRESS;
{A}{N}{D} return AND;
{A}{T} return AT;
{B}{A}{S}{E}{D} return BASED;
{B}{Y} return BY;
{B}{Y}{T}{E} return BYTE;
{C}{A}{L}{L} return CALL;
{C}{A}{S}{E} return CASE;
{C}{A}{U}{S}{E}{I}{N}{T}{E}{R}{R}{U}{P}{T} return CAUSE_INTERRUPT;
{C}{H}{A}{R}{I}{N}{T} return CHARINT;
{D}{A}{T}{A} return DATA;
{D}{E}{C}{L}{A}{R}{E} return DECLARE;
{D}{I}{S}{A}{B}{L}{E} return DISABLE;
{D}{O} return DO;
{D}{W}{O}{R}{D} return DWORD;
{E}{L}{S}{E} return ELSE;
{E}{N}{A}{B}{L}{E} return ENABLE;
{E}{N}{D} return END;
{E}{O}{F} return EOF_KW;
{E}{X}{T}{E}{R}{N}{A}{L} return EXTERNAL;
{G}{O} return GO;
{G}{O}{T}{O} return GOTO;
{H}{A}{L}{T} return HALT;
{H}{W}{O}{R}{D} return HWORD;
{I}{F} return IF;
{I}{N}{I}{T}{I}{A}{L} return INITIAL_KW;
{I}{N}{T}{E}{G}{E}{R} return INTEGER;
{I}{N}{T}{E}{R}{R}{U}{P}{T} return INTERRUPT;
{L}{A}{B}{E}{L} return LABEL;
{L}{I}{T}{E}{R}{A}{L}{L}{Y} return LITERALLY;
{L}{O}{N}{G}{I}{N}{T} return LONGINT;
{M}{I}{N}{U}{S} return MINUS;
{M}{O}{D} return MOD;
{N}{O}{T} return NOT;
{O}{F}{F}{S}{E}{T} return OFFSET;
{O}{R} return OR;
{P}{L}{U}{S} return PLUS;
{P}{O}{I}{N}{T}{E}{R} return POINTER;
{P}{R}{O}{C}{E}{D}{U}{R}{E} return PROCEDURE;
{P}{U}{B}{L}{I}{C} return PUBLIC;
{Q}{W}{O}{R}{D} return QWORD;
{R}{E}{A}{L} return REAL;
{R}{E}{E}{N}{T}{R}{A}{N}{T} return REENTRANT;
{R}{E}{T}{U}{R}{N} return RETURN;
{S}{E}{L}{E}{C}{T}{O}{R} return SELECTOR;
{S}{H}{O}{R}{T}{I}{N}{T} return SHORTINT;
{S}{T}{R}{U}{C}{T}{U}{R}{E} return STRUCTURE;
{T}{H}{E}{N} return THEN;
{T}{O} return TO;
{W}{H}{I}{L}{E} return WHILE;
{W}{O}{R}{D} return WORD;
{X}{O}{R} return XOR;
[_A-Za-z][_$0-9A-Za-z]* {
char * string;
int i;
yylval = canonical_identifier(yytext);
string = lookup_literal(yylval);
if (!string)
{
return IDENTIFIER;
}
free(yylval);
yylval=0; /*excessive neatness*/
/* push the string back onto the input
stream - it is necessary to push
from right to left */
for (i = strlen (string);
i >= 0;
i--)
{
/* very weak - depends on lexical
generator allowing sufficient
push-back */
unput (string[i]);
}
}
[0-9][0-9$]*(d)? return DECIMAL_NUMBER;
{DIGIT_STRING}\.({DIGIT_STRING})?([E|e][+|-]?{DIGIT_STRING})? return FLOATING_POINT_NUMBER;
[01][01$]*[bB] return BINARY_NUMBER;
[0-9][0-9a-fA-F$]*[hH] return HEX_NUMBER;
[0-7][0-7$]*[OoQq] return OCTAL_NUMBER;
'([^'\n\201]|(''))+' {
yylval = canonical_string(yytext);
return STRING;
}
:= return ASSIGN_OP;
[\@] return AT_SIGN;
: return COLON;
, return COMMA;
[\.] return DOT;
= return EQ;
>= return GE;
> return GT;
"<=" return LE;
\( return LPAREN;
"<" return LT;
- return MINUS_SIGN;
"<>" return NE;
"+" return PLUS_SIGN;
\) return RPAREN;
; return SEMI;
"/" return SLASH;
\* return STAR;
\f
\n lineno++;
[\t ]*
"/*" {
register int c;
for ( ; ; )
{
while ( (c = input()) != '*' &&
c != EOF )
if (c == '\n') lineno++;
/* eat up text of comment */
if ( c == '*' )
{
while ( (c = input()) == '*' )
;
if ( c == '/' )
break; /* found the end */
}
if (c == '\n') lineno++;
if ( c == EOF )
{
error( "EOF in comment" );
break;
}
}
}
.
%%
static void error (char * x) {printf (x);}
/* Strip the single quotes from a pl/m string, and
compress any single quote pairs to one quote.
Allocates new storage for the string.
*/
char *
canonical_string (char * s)
{
int i, i_ret;
char * ret;
int len = strlen (s);
ret = malloc (len+1);
if (!ret)
return 0;
for (i = 1, i_ret = 0; i < (len-1); i++)
{
ret[i_ret++] = s[i];
if (s[i] == '\'')
{
i++;
}
}
ret[i_ret] = 0;
return ret;
}
/* Strip the dollar signs from a pl/m identifier or
numeric constant, and force all alphabetic characters
to lower case.
Allocates new storage for the identifier string.
*/
char *
canonical_identifier (char * s)
{
int i, i_ret;
char * ret;
int len = strlen (s);
ret = malloc (len+1);
if (!ret)
return 0;
for (i = 0, i_ret = 0; i < len; i++)
{
if (s[i] == '$')
{
continue;
}
ret[i_ret++] = tolower (s[i]);
}
ret[i_ret] = 0;
return ret;
}
#ifdef LEX_ONLY
main() {
for (;;)
yylex();
}
#endif

View File

@@ -0,0 +1,21 @@
/* manifest constants/types for PL/M lexer/parser.
$Id: plm-manifest.h,v 1.1 1994/02/28 20:54:26 hays Exp $
Copyright 1994 by Kirk Hays (hays@ichips.intel.com)
USE AT YOUR OWN RISK. NO WARRANTY EXPRESSED OR IMPLIED.
This code is distributed in the hope that it will be useful,
but without any warranty. Further, there is no implied warranty of
merchantability or fitness for a particular purpose.
*/
extern int lineno;
/* brute force type the value stack - this would not be appropriate in
a real compiler, but suits our purposes just fine */
typedef char * CHAR_PTR;
#define YYSTYPE CHAR_PTR

View File

@@ -0,0 +1,634 @@
/* YACC parser for PL/M syntax.
$Id: plm-parse.y,v 1.1 1994/02/28 20:54:56 hays Exp $
Copyright 1994 by Kirk Hays (hays@ichips.intel.com) and Gary Funck
(gary@intrepid.com)
USE AT YOUR OWN RISK. NO WARRANTY EXPRESSED OR IMPLIED.
This code is distributed in the hope that it will be useful,
but without any warranty. Further, there is no implied warranty of
merchantability or fitness for a particular purpose.
*/
/* This file defines the grammar of PL/M. */
%{
/*
* YACC grammar of PL/M
*/
#include <stdio.h>
#include <stdlib.h>
#include "plm-manifest.h"
#include "scope.h"
void yyerror ();
/* Cause the `yydebug' variable to be defined. */
#define YYDEBUG (1)
%}
%token ADDRESS AND ASSIGN_OP AT AT_SIGN BASED
%token BINARY_NUMBER BY BYTE CALL CASE
%token CAUSE_INTERRUPT CHARINT COLON COMMA DATA
%token DECIMAL_NUMBER DECLARE DISABLE DO DOT
%token DWORD ELSE ENABLE END EOF_KW EQ
%token EXTERNAL FLOATING_POINT_NUMBER GE GO
%token GOTO GT HALT HEX_NUMBER HWORD
%token IDENTIFIER
%token IF INITIAL_KW INTEGER INTERRUPT
%token LABEL LE LITERALLY LONGINT LPAREN LT
%token MINUS MINUS_SIGN MOD NE NOT
%token OCTAL_NUMBER OFFSET OR PLUS PLUS_SIGN
%token POINTER PROCEDURE PUBLIC QWORD
%token REAL REENTRANT RETURN RPAREN SELECTOR
%token SEMI SHORTINT SLASH STAR STRING
%token STRUCTURE THEN TO WHILE WORD XOR
%start compilation
%%
actual_parameters:
LPAREN expression_list RPAREN
;
adding_operator:
MINUS
| MINUS_SIGN
| PLUS
| PLUS_SIGN
;
and_operator:
AND
;
arithmetic_expression:
term
| arithmetic_expression adding_operator term
;
array_specifier:
explicit_dimension
| implicit_dimension
;
assignment_statement:
left_part EQ expression SEMI
;
base_specifier:
IDENTIFIER
| IDENTIFIER DOT IDENTIFIER
;
basic_statement:
assignment_statement
| call_statement
| goto_statement
| microprocessor_dependent_statement
| null_statement
| return_statement
;
basic_type:
ADDRESS
| BYTE
| CHARINT
| DWORD
| HWORD
| INTEGER
| LONGINT
| OFFSET
| POINTER
| QWORD
| REAL
| SELECTOR
| SHORTINT
| WORD
;
bound_expression:
expression
;
by_part:
BY step_expression
;
call_statement:
CALL simple_variable SEMI
| CALL simple_variable actual_parameters SEMI
;
cast_type:
basic_type
;
cause_interrupt_statement:
CAUSE_INTERRUPT LPAREN integer_constant RPAREN SEMI
;
compilation:
module
| module EOF_KW
;
conditional_clause:
if_condition true_unit
| if_condition true_element ELSE false_element
;
constant:
STRING
| numeric_constant
;
constant_attribute:
DATA
;
constant_list:
constant
| constant_list COMMA constant
;
declaration:
declare_statement
| procedure_definition
;
declaration_sequence:
declaration
| declaration_sequence declaration
;
declare_element:
factored_element
| unfactored_element
;
declare_element_list:
declare_element
| declare_element_list COMMA declare_element
;
declare_statement:
DECLARE declare_element_list SEMI
;
disable_statement:
DISABLE SEMI
;
do_block:
do_case_block
| do_while_block
| iterative_do_block
| simple_do_block
;
do_case_block:
do_case_statement ending
| do_case_statement unit_sequence ending
;
do_case_statement:
DO CASE expression SEMI {
push_scope();
}
;
do_while_block:
do_while_statement ending
| do_while_statement unit_sequence ending
;
do_while_statement:
DO WHILE expression SEMI {
push_scope();
}
;
embedded_assignment:
variable_reference ASSIGN_OP logical_expression
;
enable_statement:
ENABLE SEMI
;
end_statement:
END opt_identifier SEMI {
pop_scope();
}
;
ending:
end_statement
| label_definition_sequence end_statement
;
explicit_dimension:
LPAREN numeric_constant RPAREN
;
expression:
embedded_assignment
| logical_expression
;
expression_list:
expression
| expression_list COMMA expression
;
factored_element:
factored_label_element
| factored_variable_element
;
/*
* factored_label_element doesn't permit based variables,
* yet factored_variable_element does. This can't be disambiguated
* syntactically. Thus, the factored_label element will have to
* make the proper semantic checks to make the sure that the
* variable_name_specifier_list is in fact an identifier_list.
*/
factored_label_element:
LPAREN variable_name_specifier_list RPAREN LABEL opt_public_or_external
;
factored_member:
LPAREN member_name_list RPAREN opt_explicit_dimension variable_type
;
factored_variable_element:
LPAREN variable_name_specifier_list RPAREN opt_explicit_dimension variable_type opt_variable_attributes
;
false_element:
unit
;
formal_parameter:
IDENTIFIER
;
formal_parameter_list:
formal_parameter
| formal_parameter_list COMMA formal_parameter
;
formal_parameter_specifier:
LPAREN formal_parameter_list RPAREN
;
go_to:
GOTO
| GO TO
;
goto_statement:
go_to IDENTIFIER SEMI
;
halt_statement:
HALT SEMI
;
id_colon:
IDENTIFIER COLON
;
if_condition:
IF expression THEN
;
implicit_dimension:
LPAREN STAR RPAREN
;
index_part:
index_variable EQ start_expression
;
index_variable:
simple_variable
;
initial_value:
expression
;
initial_value_list:
initial_value
| initial_value_list COMMA initial_value
;
initialization:
DATA LPAREN initial_value_list RPAREN
| INITIAL_KW LPAREN initial_value_list RPAREN
;
integer_constant:
BINARY_NUMBER
| DECIMAL_NUMBER
| HEX_NUMBER
| OCTAL_NUMBER
;
interrupt:
INTERRUPT opt_interrupt_number
;
interrupt_number:
integer_constant
;
iterative_do_block:
iterative_do_statement ending
| iterative_do_statement unit_sequence ending
;
iterative_do_statement:
DO index_part to_part opt_by_part SEMI {
push_scope();
}
;
label_definition:
id_colon
;
label_definition_sequence:
label_definition
| label_definition_sequence label_definition
;
label_element:
IDENTIFIER LABEL opt_public_or_external
;
left_part:
variable_reference_list
;
literal_element:
IDENTIFIER LITERALLY STRING {
enter_literal ($1, $3);
}
;
location_reference:
AT_SIGN variable_reference
| AT_SIGN LPAREN constant_list RPAREN
| DOT variable_reference
| DOT LPAREN constant_list RPAREN
;
locator:
AT LPAREN expression RPAREN
;
locator_initialization:
locator
| initialization
| locator initialization
;
logical_expression:
logical_factor
| logical_expression or_operator logical_factor
;
logical_factor:
logical_secondary
| logical_factor and_operator logical_secondary
;
logical_primary:
arithmetic_expression
| arithmetic_expression relation_operator arithmetic_expression
;
logical_secondary:
logical_primary
| NOT logical_primary
;
member_element:
structure_type
| factored_member
| unfactored_member
;
member_element_list:
member_element
| member_element_list COMMA member_element
;
member_name:
IDENTIFIER
;
member_name_list:
member_name
| member_name_list COMMA member_name
;
member_specifier:
DOT member_name
| DOT member_name subscript
;
member_specifier_sequence:
member_specifier_sequence member_specifier
| member_specifier
;
microprocessor_dependent_statement:
cause_interrupt_statement
| disable_statement
| enable_statement
| halt_statement
;
module:
module_name COLON simple_do_block
;
module_name:
IDENTIFIER
;
multiplying_operator:
MOD
| SLASH
| STAR
;
null_statement:
SEMI
;
numeric_constant:
FLOATING_POINT_NUMBER
| integer_constant
;
opt_array_specifier:
/* empty */
| array_specifier
;
opt_by_part:
/* empty */
| by_part
;
opt_explicit_dimension:
/* empty */
| explicit_dimension
;
opt_formal_parameter_specifier:
/* empty */
| formal_parameter_specifier
;
opt_identifier:
/* empty */
| IDENTIFIER
;
opt_interrupt_number:
/* empty */
| interrupt_number
;
opt_procedure_attribute_sequence:
/* empty */
| procedure_attribute_sequence
;
opt_procedure_type:
/* empty */
| procedure_type
;
opt_public_or_external:
/* empty */
| EXTERNAL
| PUBLIC
;
opt_variable_attributes:
/* empty */
| variable_attributes
;
or_operator:
OR
| XOR
;
primary:
constant
| location_reference
| subexpression
| variable_reference
;
procedure_attribute:
EXTERNAL
| PUBLIC
| REENTRANT
| interrupt PUBLIC
| interrupt EXTERNAL
;
procedure_attribute_sequence:
procedure_attribute
| procedure_attribute_sequence procedure_attribute
;
procedure_definition:
procedure_statement ending
| procedure_statement declaration_sequence ending
| procedure_statement unit_sequence ending
| procedure_statement declaration_sequence unit_sequence ending
;
procedure_statement:
id_colon PROCEDURE opt_formal_parameter_specifier opt_procedure_type opt_procedure_attribute_sequence SEMI
{
push_scope();
}
;
procedure_type:
basic_type
;
relation_operator:
EQ
| GE
| GT
| LE
| LT
| NE
;
return_statement:
typed_return
| untyped_return
;
secondary:
primary
| unary_sign primary
;
simple_do_block:
simple_do_statement ending
| simple_do_statement unit_sequence ending
| simple_do_statement declaration_sequence ending
| simple_do_statement declaration_sequence unit_sequence ending
;
simple_do_statement:
DO SEMI {
push_scope();
}
;
simple_variable:
IDENTIFIER
| IDENTIFIER DOT IDENTIFIER
;
start_expression:
expression
;
step_expression:
expression
;
structure_type:
STRUCTURE LPAREN member_element_list RPAREN
;
subexpression:
LPAREN expression RPAREN
;
subscript:
LPAREN expression RPAREN
;
subscript_or_actual_parameters:
LPAREN expression_list RPAREN
;
term:
secondary
| term multiplying_operator secondary
;
to_part:
TO bound_expression
;
true_element:
true_statement
| label_definition_sequence true_statement
;
true_statement:
do_block
| basic_statement
;
true_unit:
unit
;
typed_return:
RETURN expression SEMI
;
unary_minus:
MINUS_SIGN
;
unary_plus:
PLUS_SIGN
;
unary_sign:
unary_minus
| unary_plus
;
unfactored_element:
label_element
| literal_element
| variable_element
;
unfactored_member:
member_name opt_explicit_dimension variable_type
;
unit:
unit_element
| label_definition_sequence unit_element
;
unit_element:
basic_statement
| conditional_clause
| do_block
;
unit_sequence:
unit
| unit_sequence unit
;
untyped_return:
RETURN SEMI
;
variable_attributes:
EXTERNAL constant_attribute
| EXTERNAL
| PUBLIC locator_initialization
| PUBLIC
| locator_initialization
;
variable_element:
variable_name_specifier opt_array_specifier variable_type opt_variable_attributes
;
variable_name_specifier:
IDENTIFIER
| IDENTIFIER BASED base_specifier
;
variable_name_specifier_list:
variable_name_specifier
| variable_name_specifier_list COMMA variable_name_specifier
;
/*
* Variable references may be either data references or function
* references. Syntactically, they appear to be the same, each
* is followed by a parenthesized comma separated list of expressions.
*
* A function reference, of course, cannot have the trailing list of
* member specifiers - semantic checking will catch this.
*/
variable_reference:
IDENTIFIER
| IDENTIFIER member_specifier_sequence
| cast_type subscript
| IDENTIFIER subscript_or_actual_parameters
| IDENTIFIER subscript_or_actual_parameters member_specifier_sequence
;
variable_reference_list:
variable_reference
| variable_reference_list COMMA variable_reference
;
variable_type:
basic_type
| structure_type
;
%%
void
yyerror(char * s)
{
fprintf (stderr, "error at line %d: %s\n", lineno, s);
}
main()
{
init_scope();
return yyparse();
}

View File

@@ -0,0 +1,151 @@
/* literal symbol table for PL/M.
$Id: scope.c,v 1.1 1994/02/28 20:55:56 hays Exp $
Copyright 1994 by Kirk Hays (hays@ichips.intel.com)
USE AT YOUR OWN RISK. NO WARRANTY EXPRESSED OR IMPLIED.
This code is distributed in the hope that it will be useful,
but without any warranty. Further, there is no implied warranty of
merchantability or fitness for a particular purpose.
This code implements a scoped symbol table. It depends on Paul
Vixie's PD AVL Tree code, distributed in comp.sources.unix, Volume 27,
Issue 34.
*/
#include <stdlib.h>
#include <stdio.h>
#include "tree.h"
#include "scope.h"
typedef struct lit_table_stack_t_elem {
struct lit_table_stack_t_elem *next;
tree * literal_table;
} lit_table_stack_t;
/* innermost scope for scope list */
static lit_table_stack_t *lit_table_stack;
typedef struct {
char * identifier;
char * string;
} literal_t;
/* static forward references */
static void new_scope (lit_table_stack_t *);
static int literal_compar (literal_t *, literal_t *);
static void literal_error (literal_t *);
static void literal_t_delete (literal_t *);
/* lookup an identifier in the scoped symbol table */
char *
lookup_literal (char * identifier)
{
literal_t * datum;
literal_t * node;
lit_table_stack_t * scope;
node = alloca (sizeof (literal_t));
node->identifier = identifier;
for (scope = lit_table_stack; scope; scope=scope->next)
{
datum = tree_srch(&(scope->literal_table), literal_compar, node);
if (datum)
{
return datum->string;
}
}
return 0;
}
/* enter an identifier in the current scoping level */
void
enter_literal (char * identifier, char * string)
{
literal_t * datum;
datum = malloc (sizeof (literal_t));
datum->identifier = identifier;
datum->string = string;
tree_add (&(lit_table_stack->literal_table),
literal_compar,
datum,
literal_error);
}
/* increase scope depth by one level - creates a new symbol table
for the current scope */
void
push_scope (void)
{
new_scope (lit_table_stack);
}
/* remove the innermost scope of the symbol table - releases all
allocated storage for that scope */
void
pop_scope (void)
{
lit_table_stack_t * p;
p = lit_table_stack;
lit_table_stack = lit_table_stack->next;
tree_mung (&(p->literal_table), literal_t_delete);
if (p->literal_table)
{
free(p->literal_table);
}
free(p);
}
/* initialize this module, creating the outermost scope */
void
init_scope (void)
{
new_scope (0);
}
/* work procedure to create a new scope */
static void
new_scope (lit_table_stack_t * next)
{
lit_table_stack = malloc (sizeof (lit_table_stack_t));
lit_table_stack->next = next;
tree_init (&(lit_table_stack->literal_table));
}
/* internal procedure to free storage when a symbol table entry is
deleted */
static
void literal_t_delete (literal_t * datum)
{
free (datum->string);
free (datum->identifier);
}
/* internal procedure to determine the match order for two symbol
table entries */
static int
literal_compar (literal_t * left, literal_t * right)
{
return strcmp (left->identifier, right->identifier);
}
/* assertion procedure to assure that duplicate literals are never
added at a single scoping level */
static void
literal_error (literal_t * in_error)
{
fprintf (stderr,
"literal table error - attempt to enter same identifier twice\n");
exit (255);
}

View File

@@ -0,0 +1,22 @@
/* public interfaces for literal symbol table for PL/M.
$Id: scope.h,v 1.1 1994/02/28 20:56:13 hays Exp $
Copyright 1994 by Kirk Hays (hays@ichips.intel.com)
USE AT YOUR OWN RISK. NO WARRANTY EXPRESSED OR IMPLIED.
This code is distributed in the hope that it will be useful,
but without any warranty. Further, there is no implied warranty of
merchantability or fitness for a particular purpose.
This code implements a scoped symbol table. It depends on Paul
Vixie's PD AVL Tree code, distributed in comp.sources.unix, Volume 27,
Issue 34.
*/
char * lookup_literal (char *);
void enter_literal (char *, char *);
void push_scope (void);
void pop_scope (void);
void init_scope (void);