📄 c.y
字号:
/*@A (C) 1992 Allen I. Holub */
/* ANSI C, more or less.
*
* Note that I've restricted myself to yacc-compatible constructions here.
* Negative attributes would be very handy in places and it's difficult
* not to use them.
*/
%{
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <string.h>
#include <tools/debug.h> /* Misc. macros. (see Appendix A) */
#include <tools/hash.h> /* Hash-table support. (see Appendix A) */
#include <tools/compiler.h> /* Prototypes for comp.lib functions. */
#include <tools/l.h> /* Prototypes for l.lib functions. */
#include <tools/occs.h> /* Prototypes for LeX/occs-generated stuff */
#include <tools/c-code.h> /* Virtual-machine definitions. */
#ifdef __TURBOC__ /* Borland */
#include <dir.h> /* for mktemp() prototype */
#endif
#ifdef MSDOS /* Microsoft. */
#include <io.h> /* for mktemp() prototype */
#endif
#ifdef YYACTION
#define ALLOC /* Define ALLOC to create symbol table in symtab.h. */
#endif
#include "symtab.h" /* Definitions for the symbol-table. */
#include "value.h" /* Definitions used for expression processing. */
#include "label.h" /* Prefixes used for compiler-generated labels. */
#include "switch.h" /* Definitions used for switch processing. */
#include "proto.h" /* Function prototypes for all .c files used by the */
/* parser. It is not printed anywhere in the book, */
/* but is included on the distribution disk. */
PRIVATE void clean_up P((void));
%}
/*----------------------------------------------------------------------*/
%union {
char *p_char;
symbol *p_sym;
link *p_link;
structdef *p_sdef;
specifier *p_spec;
value *p_val;
int num; /* Make short if sizeof(int) > sizeof(int*) */
int ascii;
}
/*----------------------------------------------------------------------*/
%term STRING /* String constant. */
%term ICON /* Integer or long constant including '\t', etc. */
%term FCON /* Floating-point constant. */
%term TYPE /* int char long float double signed unsigned short */
/* const volatile void */
%term <ascii> STRUCT /* struct union */
%term ENUM /* enum */
%term RETURN GOTO
%term IF ELSE
%term SWITCH CASE DEFAULT
%term BREAK CONTINUE
%term WHILE DO FOR
%term LC RC /* { } */
%term SEMI /* ; */
%term ELLIPSIS /* ... */
/* The attributes used below tend to be the sensible thing. For example, the
* ASSIGNOP attribute is the operator component of the lexeme; most other
* attributes are the first character of the lexeme. Exceptions are as follows:
* token attribute
* RELOP > '>'
* RELOP < '<'
* RELOP >= 'G'
* RELOP <= 'L'
*/
%left COMMA /* , */
%right EQUAL <ascii> ASSIGNOP /* = *= /= %= += -= <<= >>= &= |= ^= */
%right QUEST COLON /* ? : */
%left OROR /* || */
%left ANDAND /* && */
%left OR /* | */
%left XOR /* ^ */
%left AND /* & */
%left <ascii> EQUOP /* == != */
%left <ascii> RELOP /* <= >= < > */
%left <ascii> SHIFTOP /* >> << */
%left PLUS MINUS /* + - */
%left STAR <ascii> DIVOP /* * / % */
%right SIZEOF <ascii> UNOP INCOP /* sizeof ! ~ ++ -- */
%left LB RB LP RP <ascii> STRUCTOP /* [ ] ( ) . -> */
/* These attributes are shifted by the scanner. */
%term <p_sym> TTYPE /* Name of a type created with a previous typedef.*/
/* Attribute is a pointer to the symbol table */
/* entry for that typedef. */
%nonassoc <ascii> CLASS /* extern register auto static typedef. Attribute */
/* is the first character of the lexeme. */
%nonassoc <p_sym> NAME /* Identifier or typedef name. Attribute is NULL */
/* if the symbol doesn't exist, a pointer to the */
/* associated "symbol" structure, otherwise. */
%nonassoc ELSE /* This gives a high precedence to ELSE to suppress
* the shift/reduce conflict error message in:
* s -> IF LP expr RP expr | IF LP expr RP s ELSE s
* The precedence of the first production is the same
* as RP. Making ELSE higher precedence forces
* resolution in favor of the shift.
*/
/* Abbreviations used in nonterminal names:
*
* abs == abstract
* arg(s) == argument(s)
* const == constant
* decl == declarator
* def == definition
* expr == expression
* ext == external
* opt == optional
* param == parameter
* struct == structure
*/
%type <num> args const_expr test
%type <p_sym> ext_decl_list ext_decl def_list def decl_list decl
%type <p_sym> var_decl funct_decl local_defs new_name name enumerator
%type <p_sym> name_list var_list param_declaration abs_decl abstract_decl
%type <p_val> expr binary non_comma_expr unary initializer initializer_list
%type <p_val> or_expr and_expr or_list and_list
%type <p_link> type specifiers opt_specifiers type_or_class type_specifier
%type <p_sdef> opt_tag tag struct_specifier
%type <p_char> string_const target
/*----------------------------------------------------------------------
* Global and external variables. Initialization to zero for all globals is
* assumed. Since occs -a and -p is used, these variables may not be private.
* The ifdef assures that space is allocated only once (this, header, section
* will be placed in both yyact.c and yyout.c, YYACTION is defined only in
* yyact.c, however.
*/
%{
#ifdef YYACTION
%}
/*@A ------------------------------------------------------------- */
/*@A ------------------------------------------------------------- */
%{
int Nest_lev; /* Current block-nesting level. */
%}
%{
int Enum_val; /* Current enumeration constant value */
%}
/*@A ------------------------------------------------------------- */
%{
char Vspace[16];
char Tspace[16]; /* The compiler doesn't know the stack-frame size
* when it creates a link() directive, so it outputs
* a link(VSPACE+TSPACE). Later on, it #defines VSPACE
* to the size of the local-variable space and TSPACE
* to the size of the temporary-variable space. Vspace
* holds the actual name of the VSPACE macro, and
* Tspace the TSPACE macro. (There's a different name
* for each subroutine.)
*/
char Funct_name[ NAME_MAX+1 ]; /* Name of the current function */
%}
%{
#define STR_MAX 512 /* Maximum size of a string constant. */
char Str_buf[STR_MAX]; /* Place to assemble string constants. */
%}
/*@A ------------------------------------------------------------- */
/* Stacks. The stack macros are all in */
/* <tools/stack.h>, included earlier */
%{
#include <tools/stack.h> /* Stack macros. (see Appendix A) */
int stk_err( o ) /* declared as int to keep the compiler happy */
int o;
{
yyerror( o ? "Loop/switch nesting too deep or logical expr. too complex.\n"
: "INTERNAL, label stack underflow.\n" );
exit( 1 );
BCC( return 0 ); /* keep the compiler happy */
}
#undef stack_err
#define stack_err(o) stk_err(o)
stack_dcl (S_andor, int, 32); /* This stack wouldn't be necessary if I were */
/* willing to put a structure onto the value */
/* stack--or_list and and_list must both */
/* return 2 attributes; this stack will hold */
/* one of them. */
%}
/*@A ------------------------------------------------------------- */
%{
/* These stacks are necessary because there's no syntactic connection break,
* continue, case, default and the affected loop-control statement.
*/
stack_dcl (S_brk, int, 32); /* number part of current break target */
stack_dcl (S_brk_label, char *, 32); /* string part of current break target */
stack_dcl (S_con, int, 32); /* number part of current continue targ. */
stack_dcl (S_con_label, char *, 32); /* string part of current continue targ. */
%}
/*@A ------------------------------------------------------------- */
%{
int Case_label = 0; /* Label used to process case statements. */
stack_dcl (S_switch, stab *, 32); /* Switch table for current switch. */
%}
/*@A ------------------------------------------------------------- */
%{
#endif /* ifdef YYACTION */
%}
%%
program : ext_def_list { clean_up(); }
;
ext_def_list
: ext_def_list ext_def
| /* epsilon */
{
yydata( "#include <tools/virtual.h>\n" );
yydata( "#define T(x)\n" );
yydata( "SEG(data)\n" );
yycode( "\nSEG(code)\n" );
yybss ( "\nSEG(bss)\n" );
}
;
opt_specifiers
: CLASS TTYPE { set_class_bit( 0, $2->etype ); /* Reset class. */
set_class_bit( $1, $2->etype ); /* Add new class. */
$$ = $2->type ;
}
| TTYPE { set_class_bit(0, $1->etype); /* Reset class bits.*/
$$ = $1->type ;
}
| specifiers
| /* empty */ %prec COMMA
{
$$ = new_link();
$$->class = SPECIFIER;
$$->NOUN = INT;
}
;
specifiers
: type_or_class
| specifiers type_or_class { spec_cpy( $$, $2 );
discard_link_chain( $2 ); }
;
type
: type_specifier
| type type_specifier { spec_cpy( $$, $2 );
discard_link_chain( $2 ); }
;
type_or_class
: type_specifier
| CLASS { $$ = new_class_spec( $1 ); }
;
type_specifier
: TYPE { $$ = new_type_spec( yytext ); }
| enum_specifier { $$ = new_type_spec( "int" ); }
| struct_specifier { $$ = new_link();
$$->class = SPECIFIER;
$$->NOUN = STRUCTURE;
$$->V_STRUCT = $1;
}
;
var_decl
: new_name %prec COMMA /* This production is done first. */
| var_decl LP RP { add_declarator( $$, FUNCTION ); }
| var_decl LP var_list RP { add_declarator( $$, FUNCTION );
discard_symbol_chain( $3 );
}
| var_decl LB RB
{
/* At the global level, this must be treated as an array of
* indeterminate size; at the local level this is equivalent to
* a pointer. The latter case is patched after the declaration
* is assembled.
*/
add_declarator( $$, ARRAY );
$$->etype->NUM_ELE = 0;
YYD( yycomment("Add POINTER specifier\n"); )
}
| var_decl LB const_expr RB
{
add_declarator( $$, ARRAY );
$$->etype->NUM_ELE = $3;
YYD(yycomment("Add array[%d] spec.\n", $$->etype->NUM_ELE);)
}
| STAR var_decl %prec UNOP
{
add_declarator( $$ = $2, POINTER );
YYD( yycomment("Add POINTER specifier\n"); )
}
| LP var_decl RP { $$ = $2; }
;
/*----------------------------------------------------------------------
* Name productions. new_name always creates a new symbol, initialized with the
* current lexeme. Name returns a preexisting symbol with the associated name
* (if there is one); otherwise, the symbol is allocated. The NAME token itself
* has a NULL attribute if the symbol doesn't exist, otherwise it returns a
* pointer to a "symbol" for the name.
*/
new_name: NAME { $$ = new_symbol( yytext, Nest_lev ); }
;
name : NAME { if( !$1 || $1->level != Nest_lev )
$$ = new_symbol( yytext, Nest_lev );
}
;
/*----------------------------------------------------------------------
* Global declarations: take care of the declarator part of the declaration.
* (The specifiers are handled by specifiers).
* Assemble the declarators into a chain, using the cross links.
*/
ext_decl_list
: ext_decl
{
$$->next = NULL; /* First link in chain. */
}
| ext_decl_list COMMA ext_decl
{
/* Initially, $1 and $$ point at the head of the chain.
* $3 is a pointer to the new declarator.
*/
$3->next = $1;
$$ = $3;
}
;
ext_decl
: var_decl
| var_decl EQUAL initializer { $$->args = (symbol *)$3; }
| funct_decl
;
funct_decl
: STAR funct_decl { add_declarator( $$ = $2 , POINTER ); }
| funct_decl LB RB { add_declarator( $$, ARRAY );
$$->etype->NUM_ELE = 0;
}
| funct_decl LB const_expr RB { add_declarator( $$, ARRAY );
$$->etype->NUM_ELE = $3;
}
| LP funct_decl RP { $$ = $2; }
| funct_decl LP RP { add_declarator( $$, FUNCTION ); }
| new_name LP RP { add_declarator( $$, FUNCTION ); }
| new_name LP { ++Nest_lev; } name_list { --Nest_lev; } RP
{
add_declarator( $$, FUNCTION );
$4 = reverse_links( $4 );
$$->args = $4;
}
| new_name LP { ++Nest_lev; } var_list { --Nest_lev; } RP
{
add_declarator( $$, FUNCTION );
$$->args = $4;
}
;
name_list
: new_name {
$$->next = NULL;
$$->type = new_link();
$$->type->class = SPECIFIER;
$$->type->SCLASS = AUTO;
}
| name_list COMMA new_name {
$$ = $3;
$$->next = $1;
$$->type = new_link();
$$->type->class = SPECIFIER;
$$->type->SCLASS = AUTO;
}
;
var_list
: param_declaration { if($1) $$->next = NULL; }
| var_list COMMA param_declaration { if($3)
{
$$ = $3;
$3->next = $1;
}
}
;
param_declaration
: type var_decl { add_spec_to_decl($1, $$ = $2 ); }
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -