mirror of
https://git.code.sf.net/p/quake/quakeforge
synced 2024-11-29 15:41:59 +00:00
5d9823af30
Conforms fairly closely to GCC's C implementation.
2378 lines
50 KiB
Text
2378 lines
50 KiB
Text
%{
|
||
/*
|
||
qc-parse.y
|
||
|
||
parser for quakec
|
||
|
||
Copyright (C) 2001 Bill Currie <bill@taniwha.org>
|
||
|
||
Author: Bill Currie <bill@taniwha.org>
|
||
Date: 2001/06/12
|
||
|
||
This program is free software; you can redistribute it and/or
|
||
modify it under the terms of the GNU General Public License
|
||
as published by the Free Software Foundation; either version 2
|
||
of the License, or (at your option) any later version.
|
||
|
||
This program is distributed in the hope that it will be useful,
|
||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||
|
||
See the GNU General Public License for more details.
|
||
|
||
You should have received a copy of the GNU General Public License
|
||
along with this program; if not, write to:
|
||
|
||
Free Software Foundation, Inc.
|
||
59 Temple Place - Suite 330
|
||
Boston, MA 02111-1307, USA
|
||
|
||
*/
|
||
#ifdef HAVE_CONFIG_H
|
||
# include "config.h"
|
||
#endif
|
||
|
||
#ifdef HAVE_STRING_H
|
||
# include <string.h>
|
||
#endif
|
||
#ifdef HAVE_STRINGS_H
|
||
# include <strings.h>
|
||
#endif
|
||
#include <stdlib.h>
|
||
|
||
#include <QF/hash.h>
|
||
#include <QF/sys.h>
|
||
#include <QF/va.h>
|
||
|
||
#include "tools/qfcc/include/attribute.h"
|
||
#include "tools/qfcc/include/class.h"
|
||
#include "tools/qfcc/include/debug.h"
|
||
#include "tools/qfcc/include/def.h"
|
||
#include "tools/qfcc/include/diagnostic.h"
|
||
#include "tools/qfcc/include/emit.h"
|
||
#include "tools/qfcc/include/expr.h"
|
||
#include "tools/qfcc/include/function.h"
|
||
#include "tools/qfcc/include/method.h"
|
||
#include "tools/qfcc/include/options.h"
|
||
#include "tools/qfcc/include/qfcc.h"
|
||
#include "tools/qfcc/include/reloc.h"
|
||
#include "tools/qfcc/include/shared.h"
|
||
#include "tools/qfcc/include/strpool.h"
|
||
#include "tools/qfcc/include/struct.h"
|
||
#include "tools/qfcc/include/switch.h"
|
||
#include "tools/qfcc/include/symtab.h"
|
||
#include "tools/qfcc/include/type.h"
|
||
#include "tools/qfcc/include/value.h"
|
||
|
||
#define YYDEBUG 1
|
||
#define YYERROR_VERBOSE 1
|
||
#undef YYERROR_VERBOSE
|
||
|
||
extern char *qc_yytext;
|
||
|
||
static void
|
||
yyerror (const char *s)
|
||
{
|
||
#ifdef YYERROR_VERBOSE
|
||
error (0, "%s %s\n", qc_yytext, s);
|
||
#else
|
||
error (0, "%s before %s", s, qc_yytext);
|
||
#endif
|
||
}
|
||
|
||
static void
|
||
parse_error (void)
|
||
{
|
||
error (0, "parse error before %s", qc_yytext);
|
||
}
|
||
|
||
#define PARSE_ERROR do { parse_error (); YYERROR; } while (0)
|
||
|
||
int yylex (void);
|
||
|
||
%}
|
||
|
||
%union {
|
||
int op;
|
||
unsigned size;
|
||
specifier_t spec;
|
||
void *pointer; // for ensuring pointer values are null
|
||
struct type_s *type;
|
||
struct expr_s *expr;
|
||
struct element_s *element;
|
||
struct function_s *function;
|
||
struct switch_block_s *switch_block;
|
||
struct param_s *param;
|
||
struct method_s *method;
|
||
struct class_s *class;
|
||
struct category_s *category;
|
||
struct class_type_s *class_type;
|
||
struct protocol_s *protocol;
|
||
struct protocollist_s *protocol_list;
|
||
struct keywordarg_s *keywordarg;
|
||
struct methodlist_s *methodlist;
|
||
struct symbol_s *symbol;
|
||
struct symtab_s *symtab;
|
||
struct attribute_s *attribute;
|
||
struct designator_s *designator;
|
||
}
|
||
|
||
// these tokens are common between qc and qp
|
||
%left LOW
|
||
%nonassoc IFX
|
||
%nonassoc ELSE
|
||
%nonassoc BREAK_PRIMARY
|
||
%nonassoc ';'
|
||
%nonassoc CLASS_NOT_CATEGORY
|
||
%nonassoc STORAGEX
|
||
|
||
%left COMMA
|
||
%right <op> '=' ASX
|
||
%right '?' ':'
|
||
%left OR
|
||
%left AND
|
||
%left '|'
|
||
%left '^'
|
||
%left '&'
|
||
%left EQ NE
|
||
%left LT GT GE LE
|
||
%token NAND NOR XNOR
|
||
// end of tokens common between qc and qp
|
||
|
||
%left SHL SHR
|
||
%left '+' '-'
|
||
%left '*' '/' '%' MOD SCALE
|
||
%left CROSS DOT HADAMARD
|
||
%right <op> SIZEOF UNARY INCOP
|
||
%left HYPERUNARY
|
||
%left '.' '(' '['
|
||
|
||
%token <symbol> CLASS_NAME NAME
|
||
%token <expr> VALUE STRING
|
||
|
||
%token LOCAL WHILE DO IF ELSE FOR BREAK CONTINUE
|
||
%token RETURN AT_RETURN ELLIPSIS
|
||
%token NIL GOTO SWITCH CASE DEFAULT ENUM
|
||
%token ARGS TYPEDEF EXTERN STATIC SYSTEM OVERLOAD NOT ATTRIBUTE
|
||
%token <op> STRUCT HANDLE
|
||
%token <spec> TYPE_SPEC TYPE_NAME TYPE_QUAL
|
||
%token <spec> OBJECT_NAME
|
||
%token CLASS DEFS ENCODE END IMPLEMENTATION INTERFACE PRIVATE
|
||
%token PROTECTED PROTOCOL PUBLIC SELECTOR REFERENCE SELF THIS
|
||
|
||
%type <spec> storage_class save_storage
|
||
%type <spec> typespec typespec_reserved typespec_nonreserved
|
||
%type <spec> declspecs declspecs_nosc declspecs_nots
|
||
%type <spec> declspecs_ts
|
||
%type <spec> declspecs_nosc_ts declspecs_nosc_nots
|
||
%type <spec> declspecs_sc_ts declspecs_sc_nots defspecs
|
||
%type <spec> declarator notype_declarator after_type_declarator
|
||
%type <spec> param_declarator param_declarator_starttypename
|
||
%type <spec> param_declarator_nostarttypename
|
||
%type <spec> absdecl absdecl1 direct_absdecl typename ptr_spec copy_spec
|
||
%type <spec> qc_comma
|
||
|
||
%type <attribute> attribute_list attribute
|
||
|
||
%type <param> function_params
|
||
%type <param> qc_func_params qc_param_list qc_first_param qc_param
|
||
|
||
%type <symbol> tag
|
||
%type <spec> struct_specifier struct_list
|
||
%type <spec> enum_specifier
|
||
%type <symbol> optional_enum_list enum_list enumerator_list enumerator
|
||
%type <symbol> enum_init
|
||
%type <size> array_decl
|
||
|
||
%type <expr> const string
|
||
|
||
%type <spec> ivar_decl
|
||
%type <expr> decl
|
||
%type <spec> ivars
|
||
%type <param> param_list parameter_list parameter
|
||
%type <symbol> methoddef
|
||
%type <expr> var_initializer local_def
|
||
|
||
%type <expr> opt_init_semi opt_expr comma_expr expr
|
||
%type <expr> compound_init element_list
|
||
%type <designator> designator designator_spec
|
||
%type <element> element
|
||
%type <expr> ose optional_state_expr texpr vector_expr
|
||
%type <expr> statement statements compound_statement
|
||
%type <expr> else bool_label break_label continue_label
|
||
%type <expr> unary_expr ident_expr cast_expr expr_list
|
||
%type <expr> opt_arg_list arg_list arg_expr
|
||
%type <switch_block> switch_block
|
||
%type <symbol> identifier label
|
||
|
||
%type <expr> identifier_list
|
||
%type <symbol> protocol_name_list selector reserved_word
|
||
%type <param> optional_param_list unaryselector keyworddecl
|
||
%type <param> keywordselector
|
||
%type <method> methodproto methoddecl
|
||
%type <expr> obj_expr obj_messageexpr obj_string receiver
|
||
%type <protocol_list> protocolrefs protocol_list
|
||
%type <keywordarg> messageargs keywordarg keywordarglist selectorarg
|
||
%type <keywordarg> keywordnamelist keywordname
|
||
%type <class> class_name new_class_name class_with_super
|
||
%type <class> classdef new_class_with_super
|
||
%type <category> category_name new_category_name
|
||
%type <protocol> protocol_name
|
||
%type <methodlist> methodprotolist methodprotolist2
|
||
%type <symtab> ivar_decl_list
|
||
%type <op> ci not
|
||
|
||
%{
|
||
|
||
static switch_block_t *switch_block;
|
||
static expr_t *break_label;
|
||
static expr_t *continue_label;
|
||
|
||
static specifier_t
|
||
make_spec (type_t *type, storage_class_t storage, int is_typedef,
|
||
int is_overload)
|
||
{
|
||
specifier_t spec;
|
||
|
||
memset (&spec, 0, sizeof (spec));
|
||
spec.type = type;
|
||
spec.storage = storage;
|
||
spec.is_typedef = is_typedef;
|
||
spec.is_overload = is_overload;
|
||
if (spec.storage && spec.is_typedef)
|
||
internal_error (0, "setting both storage and is_typedef");
|
||
return spec;
|
||
}
|
||
|
||
static specifier_t
|
||
parse_attributes (attribute_t *attr_list)
|
||
{
|
||
specifier_t spec = {};
|
||
for (attribute_t *attr = attr_list; attr; attr = attr->next) {
|
||
if (!strcmp (attr->name, "no_va_list")) {
|
||
spec.no_va_list = 1;
|
||
} else if (!strcmp (attr->name, "nosave")) {
|
||
spec.nosave = 1;
|
||
} else if (!strcmp (attr->name, "void_return")) {
|
||
spec.void_return = 1;
|
||
} else {
|
||
warning (0, "skipping unknown attribute '%s'", attr->name);
|
||
}
|
||
}
|
||
return spec;
|
||
}
|
||
|
||
static int
|
||
storage_auto (specifier_t spec)
|
||
{
|
||
return spec.storage == sc_global || spec.storage == sc_local;
|
||
}
|
||
|
||
static specifier_t
|
||
spec_merge (specifier_t spec, specifier_t new)
|
||
{
|
||
if (new.type) {
|
||
// deal with "type <type_name>"
|
||
if (!spec.type || new.sym) {
|
||
spec.sym = new.sym;
|
||
if (!spec.type) {
|
||
spec.type = new.type;
|
||
}
|
||
} else if (!spec.multi_type) {
|
||
error (0, "two or more data types in declaration specifiers");
|
||
spec.multi_type = 1;
|
||
}
|
||
}
|
||
if (new.is_typedef || !storage_auto (new)) {
|
||
if ((spec.is_typedef || !storage_auto (spec)) && !spec.multi_store) {
|
||
error (0, "multiple storage classes in declaration specifiers");
|
||
spec.multi_store = 1;
|
||
}
|
||
spec.storage = new.storage;
|
||
spec.is_typedef = new.is_typedef;
|
||
}
|
||
if ((new.is_unsigned && spec.is_signed)
|
||
|| (new.is_signed && spec.is_unsigned)) {
|
||
if (!spec.multi_type) {
|
||
error (0, "both signed and unsigned in declaration specifiers");
|
||
spec.multi_type = 1;
|
||
}
|
||
}
|
||
if ((new.is_long && spec.is_short) || (new.is_short && spec.is_long)) {
|
||
if (!spec.multi_store) {
|
||
error (0, "both long and short in declaration specifiers");
|
||
spec.multi_store = 1;
|
||
}
|
||
}
|
||
spec.sym = new.sym;
|
||
spec.spec_bits |= new.spec_bits;
|
||
return spec;
|
||
}
|
||
|
||
static specifier_t
|
||
typename_spec (specifier_t spec)
|
||
{
|
||
spec = default_type (spec, 0);
|
||
spec.sym->type = find_type (append_type (spec.sym->type, spec.type));
|
||
spec.type = spec.sym->type;
|
||
return spec;
|
||
}
|
||
|
||
static specifier_t
|
||
function_spec (specifier_t spec, param_t *params)
|
||
{
|
||
// empty param list in an abstract decle does not create a symbol
|
||
if (!spec.sym) {
|
||
spec.sym = new_symbol (0);
|
||
}
|
||
spec = default_type (spec, spec.sym);
|
||
spec.sym->params = params;
|
||
spec.sym->type = append_type (spec.sym->type, parse_params (0, params));
|
||
spec.is_function = 1; //FIXME do proper void(*)() -> ev_func
|
||
return spec;
|
||
}
|
||
|
||
static specifier_t
|
||
array_spec (specifier_t spec, unsigned size)
|
||
{
|
||
spec = default_type (spec, spec.sym);
|
||
spec.sym->type = append_type (spec.sym->type, array_type (0, size));
|
||
return spec;
|
||
}
|
||
|
||
static specifier_t
|
||
pointer_spec (specifier_t quals, specifier_t spec)
|
||
{
|
||
spec.sym->type = append_type (spec.sym->type, pointer_type (0));
|
||
return spec;
|
||
}
|
||
|
||
static specifier_t
|
||
parse_qc_params (specifier_t spec, param_t *params)
|
||
{
|
||
type_t **type;
|
||
// .float () foo; is a field holding a function variable rather
|
||
// than a function that returns a float field.
|
||
for (type = &spec.type; *type && is_field (*type);
|
||
type = &(*type)->t.fldptr.type) {
|
||
}
|
||
type_t *ret_type = *type;
|
||
*type = 0;
|
||
|
||
spec.sym = new_symbol (0);
|
||
spec.sym->type = spec.type;
|
||
spec.type = ret_type;
|
||
|
||
spec = function_spec (spec, params);
|
||
return spec;
|
||
}
|
||
|
||
static symbol_t *
|
||
funtion_sym_type (specifier_t spec, symbol_t *sym)
|
||
{
|
||
sym->type = append_type (spec.sym->type, spec.type);
|
||
set_func_type_attrs (sym->type, spec);
|
||
sym->type = find_type (sym->type);
|
||
return sym;
|
||
}
|
||
|
||
static symbol_t *
|
||
qc_nocode_symbol (specifier_t spec, symbol_t *sym)
|
||
{
|
||
sym->params = spec.sym->params;
|
||
sym = funtion_sym_type (spec, sym);
|
||
return sym;
|
||
}
|
||
|
||
static symbol_t *
|
||
qc_function_symbol (specifier_t spec, symbol_t *sym)
|
||
{
|
||
sym = qc_nocode_symbol (spec, sym);
|
||
sym = function_symbol (sym, spec.is_overload, 1);
|
||
return sym;
|
||
}
|
||
|
||
static param_t *
|
||
make_ellipsis (void)
|
||
{
|
||
return new_param (0, 0, 0);
|
||
}
|
||
|
||
static param_t *
|
||
make_param (specifier_t spec)
|
||
{
|
||
spec = default_type (spec, spec.sym);
|
||
spec.type = find_type (append_type (spec.sym->type, spec.type));
|
||
param_t *param = new_param (0, spec.type, spec.sym->name);
|
||
return param;
|
||
}
|
||
|
||
static param_t *
|
||
make_selector (const char *selector, struct type_s *type, const char *name)
|
||
{
|
||
param_t *param = new_param (selector, type, name);
|
||
return param;
|
||
}
|
||
|
||
static param_t *
|
||
make_qc_param (specifier_t spec, symbol_t *sym)
|
||
{
|
||
spec = default_type (spec, sym);
|
||
sym->type = spec.type;
|
||
param_t *param = new_param (0, sym->type, sym->name);
|
||
return param;
|
||
}
|
||
|
||
static param_t *
|
||
make_qc_func_param (specifier_t spec, param_t *params, symbol_t *sym)
|
||
{
|
||
spec = parse_qc_params (spec, params);
|
||
sym->type = append_type (spec.sym->type, spec.type);
|
||
param_t *param = new_param (0, sym->type, sym->name);
|
||
return param;
|
||
}
|
||
|
||
static int
|
||
is_anonymous_struct (specifier_t spec)
|
||
{
|
||
if (spec.sym) {
|
||
return 0;
|
||
}
|
||
if (!is_struct (spec.type) && !is_union (spec.type)) {
|
||
return 0;
|
||
}
|
||
if (!spec.type->t.symtab || spec.type->t.symtab->parent) {
|
||
return 0;
|
||
}
|
||
// struct and union type names always begin with "tag ". Untagged s/u
|
||
// are "tag .<filename>.<id>".
|
||
if (spec.type->name[4] != '.') {
|
||
return 0;
|
||
}
|
||
return 1;
|
||
}
|
||
|
||
static int
|
||
is_null_spec (specifier_t spec)
|
||
{
|
||
static specifier_t null_spec;
|
||
return memcmp (&spec, &null_spec, sizeof (spec)) == 0;
|
||
}
|
||
|
||
static int
|
||
use_type_name (specifier_t spec)
|
||
{
|
||
spec.sym = new_symbol (spec.sym->name);
|
||
spec.sym->type = spec.type;
|
||
spec.sym->sy_type = sy_var;
|
||
symbol_t *s = symtab_addsymbol (current_symtab, spec.sym);
|
||
// a different symbol being returned means that this is a redefinition
|
||
// of that symbol in the same scope. However, typedefs to the same type
|
||
// are allowed.
|
||
if (s != spec.sym && spec.is_typedef && s->sy_type == sy_type
|
||
&& type_same (s->type, spec.type)) {
|
||
spec.sym = s;
|
||
}
|
||
return !!spec.sym->table;
|
||
}
|
||
|
||
static void __attribute__((used))
|
||
check_specifiers (specifier_t spec)
|
||
{
|
||
if (!is_null_spec (spec)) {
|
||
if (!spec.type && !spec.sym) {
|
||
warning (0, "useless specifiers");
|
||
} else if (spec.type && !spec.sym) {
|
||
if (is_anonymous_struct (spec)){
|
||
warning (0, "unnamed struct/union that defines "
|
||
"no instances");
|
||
} else if (!is_enum (spec.type)
|
||
&& !is_struct (spec.type) && !is_union (spec.type)) {
|
||
warning (0, "useless type name in empty declaration");
|
||
}
|
||
} else if (!spec.type && spec.sym) {
|
||
bug (0, "wha? %p %p", spec.type, spec.sym);
|
||
} else {
|
||
// a type name (id, typedef, etc) was used as a variable name.
|
||
// this is allowed in C, so long as it's in a different scope,
|
||
// or the types are the same
|
||
if (!use_type_name (spec)) {
|
||
error (0, "%s redeclared as different kind of symbol",
|
||
spec.sym->name);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
%}
|
||
|
||
%expect 2
|
||
|
||
%%
|
||
|
||
program
|
||
: external_def_list
|
||
{
|
||
if (current_class) {
|
||
warning (0, "‘@end’ missing in implementation context");
|
||
class_finish (current_class);
|
||
current_class = 0;
|
||
}
|
||
}
|
||
|
||
external_def_list
|
||
: /* empty */
|
||
{
|
||
current_symtab = pr.symtab;
|
||
}
|
||
| external_def_list external_def
|
||
| external_def_list obj_def
|
||
;
|
||
|
||
external_def
|
||
: fndef
|
||
| datadef
|
||
| storage_class '{' save_storage
|
||
{
|
||
current_storage = $1.storage;
|
||
}
|
||
external_def_list '}' ';'
|
||
{
|
||
current_storage = $3.storage;
|
||
}
|
||
;
|
||
|
||
fndef
|
||
: declspecs_ts declarator function_body
|
||
| declspecs_nots notype_declarator function_body
|
||
| defspecs notype_declarator function_body
|
||
;
|
||
|
||
datadef
|
||
: defspecs notype_initdecls ';'
|
||
| declspecs_nots notype_initdecls ';'
|
||
| declspecs_ts initdecls ';'
|
||
| declspecs_ts qc_func_params
|
||
{
|
||
$<spec>$ = parse_qc_params ($1, $2);
|
||
}
|
||
qc_func_decls
|
||
| declspecs ';'
|
||
| error ';'
|
||
| error '}'
|
||
| ';'
|
||
;
|
||
|
||
qc_func_params
|
||
: '(' copy_spec qc_param_list ')' { $$ = check_params ($3); }
|
||
| '(' copy_spec typespec_reserved ')'
|
||
{
|
||
if (!is_void ($3.type)) {
|
||
PARSE_ERROR;
|
||
}
|
||
$$ = 0;
|
||
}
|
||
| '(' copy_spec ')' { $$ = 0; }
|
||
;
|
||
|
||
qc_param_list
|
||
: qc_first_param
|
||
| qc_param_list ',' qc_param
|
||
{
|
||
$$ = append_params ($1, $3);
|
||
}
|
||
;
|
||
// quakec function parameters cannot use a typedef as the return type
|
||
// in the first parameter (really, they should't use any as standard quakec
|
||
// doesn't support typedef, but that seems overly restrictive), howevery,
|
||
// they can stil use a typedef first parameter. This is due to grammar issues
|
||
qc_first_param
|
||
: typespec identifier
|
||
{
|
||
$$ = make_qc_param ($1, $2);
|
||
}
|
||
| typespec_reserved qc_func_params identifier
|
||
{
|
||
$$ = make_qc_func_param ($1, $2, $3);
|
||
}
|
||
| ELLIPSIS { $$ = make_ellipsis (); }
|
||
;
|
||
|
||
qc_param
|
||
: typespec identifier
|
||
{
|
||
$$ = make_qc_param ($1, $2);
|
||
}
|
||
| typespec qc_func_params identifier
|
||
{
|
||
$$ = make_qc_func_param ($1, $2, $3);
|
||
}
|
||
| ELLIPSIS { $$ = make_ellipsis (); }
|
||
;
|
||
|
||
/* This rule is used only to get an action before both qc_func_decl and
|
||
qc_func_decl_term so that the function spec can be inherited.
|
||
*/
|
||
qc_comma
|
||
: ',' { $$ = $<spec>0; }
|
||
;
|
||
|
||
qc_func_decls
|
||
: qc_func_decl_list qc_comma qc_func_decl_term
|
||
| qc_func_decl_term
|
||
;
|
||
|
||
qc_func_decl_term
|
||
: qc_nocode_func ';'
|
||
| qc_code_func ';'
|
||
| qc_code_func %prec IFX
|
||
;
|
||
|
||
qc_func_decl_list
|
||
: qc_func_decl_list qc_comma qc_func_decl
|
||
| qc_func_decl
|
||
;
|
||
|
||
qc_func_decl
|
||
: qc_nocode_func
|
||
| qc_code_func
|
||
;
|
||
|
||
qc_nocode_func
|
||
: identifier '=' '#' expr
|
||
{
|
||
specifier_t spec = $<spec>0;
|
||
symbol_t *sym = $1;
|
||
expr_t *expr = $4;
|
||
sym = qc_function_symbol (spec, sym);
|
||
build_builtin_function (sym, expr, 0, spec.storage);
|
||
}
|
||
| identifier '=' expr
|
||
{
|
||
specifier_t spec = $<spec>0;
|
||
spec.sym = $1;
|
||
spec.sym->params = spec.params;
|
||
spec.sym->type = find_type (spec.type);
|
||
spec.is_function = 0;
|
||
declare_symbol (spec, $3, current_symtab);
|
||
}
|
||
| identifier
|
||
{
|
||
specifier_t spec = $<spec>0;
|
||
symbol_t *sym = $1;
|
||
if (!local_expr && !is_field (spec.sym->type)) {
|
||
sym = qc_function_symbol (spec, sym);
|
||
} else {
|
||
sym = qc_nocode_symbol (spec, sym);
|
||
}
|
||
if (!local_expr && !is_field (sym->type)) {
|
||
// things might be a confused mess from earlier errors
|
||
if (sym->sy_type == sy_func)
|
||
make_function (sym, 0, sym->table->space, spec.storage);
|
||
} else {
|
||
initialize_def (sym, 0, current_symtab->space, spec.storage,
|
||
current_symtab);
|
||
if (sym->s.def)
|
||
sym->s.def->nosave |= spec.nosave;
|
||
}
|
||
}
|
||
;
|
||
|
||
qc_code_func
|
||
: identifier '=' optional_state_expr
|
||
save_storage
|
||
{
|
||
$<symtab>$ = current_symtab;
|
||
specifier_t spec = $<spec>0;
|
||
symbol_t *sym = $1;
|
||
sym = qc_function_symbol (spec, sym);
|
||
current_func = begin_function (sym, 0, current_symtab, 0,
|
||
spec.storage);
|
||
current_symtab = current_func->locals;
|
||
current_storage = sc_local;
|
||
$1 = sym;
|
||
}
|
||
compound_statement
|
||
{
|
||
build_code_function ($1, $3, $6);
|
||
current_symtab = $<symtab>5;
|
||
current_storage = $4.storage;
|
||
current_func = 0;
|
||
}
|
||
;
|
||
|
||
declarator
|
||
: after_type_declarator
|
||
| notype_declarator
|
||
;
|
||
|
||
after_type_declarator
|
||
: '(' copy_spec after_type_declarator ')' { $$ = $3; }
|
||
| after_type_declarator function_params
|
||
{
|
||
$$ = function_spec ($1, $2);
|
||
}
|
||
| after_type_declarator array_decl
|
||
{
|
||
$$ = array_spec ($1, $2);
|
||
}
|
||
| '*' ptr_spec after_type_declarator
|
||
{
|
||
$$ = pointer_spec ($2, $3);
|
||
}
|
||
| TYPE_NAME
|
||
{
|
||
$$ = $<spec>0;
|
||
$$.sym = new_symbol ($1.sym->name);
|
||
}
|
||
| OBJECT_NAME
|
||
{
|
||
$$ = $<spec>0;
|
||
$$.sym = new_symbol ($1.sym->name);
|
||
}
|
||
;
|
||
|
||
copy_spec
|
||
: /* empty */
|
||
{
|
||
$<spec>$ = $<spec>-1;
|
||
}
|
||
;
|
||
|
||
ptr_spec
|
||
: copy_spec // for when no qualifiers are present
|
||
;
|
||
|
||
notype_declarator
|
||
: '(' copy_spec notype_declarator ')' { $$ = $3; }
|
||
| notype_declarator function_params
|
||
{
|
||
$$ = function_spec ($1, $2);
|
||
}
|
||
| notype_declarator array_decl
|
||
{
|
||
$$ = array_spec ($1, $2);
|
||
}
|
||
| '*' ptr_spec notype_declarator
|
||
{
|
||
$$ = pointer_spec ($2, $3);
|
||
}
|
||
| NAME
|
||
{
|
||
$$ = $<spec>0;
|
||
$$.sym = new_symbol ($1->name);
|
||
}
|
||
;
|
||
|
||
initdecls
|
||
: initdecl
|
||
| initdecls ',' { $<spec>$ = $<spec>0; } initdecl
|
||
;
|
||
|
||
initdecl
|
||
: declarator '=' var_initializer
|
||
{ declare_symbol ($1, $3, current_symtab); }
|
||
| declarator
|
||
{ declare_symbol ($1, 0, current_symtab); }
|
||
;
|
||
|
||
notype_initdecls
|
||
: notype_initdecl
|
||
| notype_initdecls ',' { $<spec>$ = $<spec>0; } notype_initdecl
|
||
;
|
||
|
||
notype_initdecl
|
||
: notype_declarator '=' var_initializer
|
||
{ declare_symbol ($1, $3, current_symtab); }
|
||
| notype_declarator
|
||
{ declare_symbol ($1, 0, current_symtab); }
|
||
;
|
||
|
||
/* various lists of type specifiers, storage class etc */
|
||
declspecs_ts
|
||
: declspecs_nosc_ts
|
||
| declspecs_sc_ts
|
||
;
|
||
|
||
declspecs_nots
|
||
: declspecs_nosc_nots
|
||
| declspecs_sc_nots
|
||
;
|
||
|
||
declspecs_nosc
|
||
: declspecs_nosc_nots
|
||
| declspecs_nosc_ts
|
||
;
|
||
|
||
declspecs
|
||
: declspecs_nosc_nots
|
||
| declspecs_nosc_ts
|
||
| declspecs_sc_nots
|
||
| declspecs_sc_ts
|
||
;
|
||
|
||
declspecs_nosc_nots
|
||
: TYPE_QUAL
|
||
| declspecs_nosc_nots TYPE_QUAL
|
||
{
|
||
$$ = spec_merge ($1, $2);
|
||
}
|
||
;
|
||
|
||
declspecs_nosc_ts
|
||
: typespec
|
||
| declspecs_nosc_ts TYPE_QUAL
|
||
{
|
||
$$ = spec_merge ($1, $2);
|
||
}
|
||
| declspecs_nosc_ts typespec_reserved
|
||
{
|
||
$$ = spec_merge ($1, $2);
|
||
}
|
||
| declspecs_nosc_nots typespec
|
||
{
|
||
$$ = spec_merge ($1, $2);
|
||
}
|
||
;
|
||
|
||
declspecs_sc_nots
|
||
: storage_class
|
||
| declspecs_sc_nots TYPE_QUAL
|
||
{
|
||
$$ = spec_merge ($1, $2);
|
||
}
|
||
| declspecs_nosc_nots storage_class
|
||
{
|
||
$$ = spec_merge ($1, $2);
|
||
}
|
||
| declspecs_sc_nots storage_class
|
||
{
|
||
$$ = spec_merge ($1, $2);
|
||
}
|
||
;
|
||
|
||
declspecs_sc_ts
|
||
: declspecs_sc_ts TYPE_QUAL
|
||
{
|
||
$$ = spec_merge ($1, $2);
|
||
}
|
||
| declspecs_sc_ts typespec_reserved
|
||
{
|
||
$$ = spec_merge ($1, $2);
|
||
}
|
||
| declspecs_sc_nots typespec
|
||
{
|
||
$$ = spec_merge ($1, $2);
|
||
}
|
||
| declspecs_nosc_ts storage_class
|
||
{
|
||
$$ = spec_merge ($1, $2);
|
||
}
|
||
| declspecs_sc_ts storage_class
|
||
{
|
||
$$ = spec_merge ($1, $2);
|
||
}
|
||
;
|
||
|
||
typespec
|
||
: typespec_reserved
|
||
{
|
||
$$ = $1;
|
||
if (!$$.storage) {
|
||
$$.storage = current_storage;
|
||
}
|
||
}
|
||
| typespec_nonreserved
|
||
{
|
||
$$ = $1;
|
||
if (!$$.storage) {
|
||
$$.storage = current_storage;
|
||
}
|
||
}
|
||
;
|
||
|
||
typespec_reserved
|
||
: TYPE_SPEC
|
||
| enum_specifier
|
||
| struct_specifier
|
||
// NOTE: fields don't parse the way they should. This is not a problem
|
||
// for basic types, but functions need special treatment
|
||
| '.' typespec_reserved
|
||
{
|
||
// avoid find_type()
|
||
$$ = make_spec (field_type (0), 0, 0, 0);
|
||
$$.type = append_type ($$.type, $2.type);
|
||
}
|
||
;
|
||
|
||
|
||
typespec_nonreserved
|
||
: TYPE_NAME
|
||
| OBJECT_NAME protocolrefs
|
||
{
|
||
if ($2) {
|
||
type_t type = *type_id.t.fldptr.type;
|
||
type.next = 0;
|
||
type.protos = $2;
|
||
$$ = make_spec (pointer_type (find_type (&type)), 0, 0, 0);
|
||
} else {
|
||
$$ = make_spec (&type_id, 0, 0, 0);
|
||
}
|
||
$$.sym = $1.sym;
|
||
}
|
||
| CLASS_NAME protocolrefs
|
||
{
|
||
if ($2) {
|
||
type_t type = *$1->type;
|
||
type.next = 0;
|
||
type.protos = $2;
|
||
$$ = make_spec (find_type (&type), 0, 0, 0);
|
||
} else {
|
||
$$ = make_spec ($1->type, 0, 0, 0);
|
||
}
|
||
$$.sym = $1;
|
||
}
|
||
// NOTE: fields don't parse the way they should. This is not a problem
|
||
// for basic types, but functions need special treatment
|
||
| '.' typespec_nonreserved
|
||
{
|
||
// avoid find_type()
|
||
$$ = make_spec (field_type (0), 0, 0, 0);
|
||
$$.type = append_type ($$.type, $2.type);
|
||
}
|
||
;
|
||
|
||
defspecs
|
||
: /* empty */
|
||
{
|
||
$$ = (specifier_t) {};
|
||
}
|
||
;
|
||
|
||
save_storage
|
||
: /* emtpy */
|
||
{
|
||
$$.storage = current_storage;
|
||
}
|
||
;
|
||
|
||
function_body
|
||
: ose
|
||
{
|
||
specifier_t spec = default_type ($<spec>0, $<spec>0.sym);
|
||
symbol_t *sym = funtion_sym_type (spec, spec.sym);
|
||
$<symbol>$ = function_symbol (sym, spec.is_overload, 1);
|
||
}
|
||
save_storage
|
||
{
|
||
$<symtab>$ = current_symtab;
|
||
current_func = begin_function ($<symbol>2, 0, current_symtab, 0,
|
||
$<spec>-1.storage);
|
||
current_symtab = current_func->locals;
|
||
current_storage = sc_local;
|
||
}
|
||
compound_statement
|
||
{
|
||
build_code_function ($<symbol>2, $1, $5);
|
||
current_symtab = $<symtab>4;
|
||
current_storage = $3.storage;
|
||
current_func = 0;
|
||
}
|
||
| '=' '#' expr ';'
|
||
{
|
||
specifier_t spec = default_type ($<spec>0, $<spec>0.sym);
|
||
symbol_t *sym = funtion_sym_type (spec, spec.sym);
|
||
sym = function_symbol (sym, spec.is_overload, 1);
|
||
build_builtin_function (sym, $3, 0, spec.storage);
|
||
}
|
||
;
|
||
|
||
storage_class
|
||
: EXTERN { $$ = make_spec (0, sc_extern, 0, 0); }
|
||
| STATIC { $$ = make_spec (0, sc_static, 0, 0); }
|
||
| SYSTEM { $$ = make_spec (0, sc_system, 0, 0); }
|
||
| TYPEDEF { $$ = make_spec (0, sc_global, 1, 0); }
|
||
| OVERLOAD { $$ = make_spec (0, current_storage, 0, 1); }
|
||
| ATTRIBUTE '(' attribute_list ')'
|
||
{
|
||
$$ = parse_attributes ($3);
|
||
}
|
||
;
|
||
|
||
attribute_list
|
||
: attribute
|
||
| attribute_list ',' attribute
|
||
{
|
||
if ($3) {
|
||
$3->next = $1;
|
||
$$ = $3;
|
||
} else {
|
||
$$ = $1;
|
||
}
|
||
}
|
||
;
|
||
|
||
attribute
|
||
: NAME { $$ = new_attribute ($1->name, 0); }
|
||
| NAME '(' expr_list ')' { $$ = new_attribute ($1->name, $3); }
|
||
;
|
||
|
||
tag : NAME ;
|
||
|
||
enum_specifier
|
||
: ENUM tag optional_enum_list
|
||
{
|
||
$$ = make_spec ($3->type, 0, 0, 0);
|
||
if (!$3->table)
|
||
symtab_addsymbol (current_symtab, $3);
|
||
}
|
||
| ENUM enum_list
|
||
{
|
||
$$ = make_spec ($2->type, 0, 0, 0);
|
||
if (!$2->table)
|
||
symtab_addsymbol (current_symtab, $2);
|
||
}
|
||
;
|
||
|
||
optional_enum_list
|
||
: enum_list
|
||
| /* empty */ { $$ = find_enum ($<symbol>0); }
|
||
;
|
||
|
||
enum_list
|
||
: '{' enum_init enumerator_list optional_comma '}'
|
||
{
|
||
current_symtab = current_symtab->parent;
|
||
$$ = finish_enum ($3);
|
||
}
|
||
;
|
||
|
||
enum_init
|
||
: /* empty */
|
||
{
|
||
$$ = find_enum ($<symbol>-1);
|
||
start_enum ($$);
|
||
current_symtab = $$->type->t.symtab;
|
||
}
|
||
;
|
||
|
||
enumerator_list
|
||
: enumerator { $$ = $<symbol>0; }
|
||
| enumerator_list ',' { $<symbol>$ = $<symbol>0; }
|
||
enumerator
|
||
{
|
||
$$ = $<symbol>0;
|
||
}
|
||
;
|
||
|
||
enumerator
|
||
: identifier { add_enum ($<symbol>0, $1, 0); }
|
||
| identifier '=' expr { add_enum ($<symbol>0, $1, $3); }
|
||
;
|
||
|
||
struct_specifier
|
||
: STRUCT tag struct_list { $$ = $3; }
|
||
| STRUCT {$<symbol>$ = 0;} struct_list { $$ = $3; }
|
||
| STRUCT tag
|
||
{
|
||
symbol_t *sym;
|
||
|
||
sym = find_struct ($1, $2, 0);
|
||
sym->type = find_type (sym->type);
|
||
$$ = make_spec (sym->type, 0, 0, 0);
|
||
if (!sym->table) {
|
||
symtab_t *tab = current_symtab;
|
||
while (tab->parent && tab->type == stab_struct) {
|
||
tab = tab->parent;
|
||
}
|
||
symtab_addsymbol (tab, sym);
|
||
}
|
||
}
|
||
| HANDLE tag
|
||
{
|
||
symbol_t *sym = find_handle ($2, 0);
|
||
sym->type = find_type (sym->type);
|
||
$$ = make_spec (sym->type, 0, 0, 0);
|
||
if (!sym->table) {
|
||
symtab_t *tab = current_symtab;
|
||
while (tab->parent && tab->type == stab_struct) {
|
||
tab = tab->parent;
|
||
}
|
||
symtab_addsymbol (tab, sym);
|
||
}
|
||
}
|
||
;
|
||
|
||
struct_list
|
||
: '{'
|
||
{
|
||
int op = $<op>-1;
|
||
symbol_t *sym = $<symbol>0;
|
||
current_symtab = start_struct (&op, sym, current_symtab);
|
||
$<op>1 = op;
|
||
$<symbol>$ = sym;
|
||
}
|
||
struct_defs '}'
|
||
{
|
||
symbol_t *sym;
|
||
symtab_t *symtab = current_symtab;
|
||
current_symtab = symtab->parent;
|
||
|
||
if ($<op>1) {
|
||
sym = $<symbol>2;
|
||
sym = build_struct ($<op>1, sym, symtab, 0, 0);
|
||
$$ = make_spec (sym->type, 0, 0, 0);
|
||
if (!sym->table)
|
||
symtab_addsymbol (current_symtab, sym);
|
||
}
|
||
}
|
||
;
|
||
|
||
struct_defs
|
||
: component_decl_list
|
||
| DEFS '(' identifier ')'
|
||
{
|
||
$3 = check_undefined ($3);
|
||
if (!$3->type || !is_class ($3->type)) {
|
||
error (0, "`%s' is not a class", $3->name);
|
||
} else {
|
||
// replace the struct symbol table with one built from
|
||
// the class ivars and the current struct fields. ivars
|
||
// will replace any fields of the same name.
|
||
current_symtab = class_to_struct ($3->type->t.class,
|
||
current_symtab);
|
||
}
|
||
}
|
||
;
|
||
|
||
component_decl_list
|
||
: component_decl_list2
|
||
| component_decl_list2 component_decl
|
||
{
|
||
warning (0, "no semicolon at end of struct or union");
|
||
}
|
||
;
|
||
|
||
component_decl_list2
|
||
: /* empty */
|
||
| component_decl_list2 component_decl ';'
|
||
| component_decl_list2 ';'
|
||
;
|
||
|
||
component_decl
|
||
: declspecs_nosc_ts components
|
||
| declspecs_nosc_ts
|
||
{
|
||
if (is_anonymous_struct ($1)) {
|
||
// type->name always begins with "tag "
|
||
$1.sym = new_symbol (va (0, ".anonymous.%s",
|
||
$1.type->name + 4));
|
||
$1.sym->type = $1.type;
|
||
$1.sym->sy_type = sy_var;
|
||
$1.sym->visibility = vis_anonymous;
|
||
symtab_addsymbol (current_symtab, $1.sym);
|
||
if (!$1.sym->table) {
|
||
error (0, "duplicate field `%s'", $1.sym->name);
|
||
}
|
||
}
|
||
}
|
||
| declspecs_nosc_nots components_notype
|
||
| declspecs_nosc_nots { internal_error (0, "not implemented"); }
|
||
;
|
||
|
||
components
|
||
: component_declarator
|
||
| components ',' { $<spec>$ = $<spec>0; } component_declarator
|
||
;
|
||
|
||
component_declarator
|
||
: declarator
|
||
{
|
||
declare_field ($1, current_symtab);
|
||
}
|
||
| declarator ':' expr
|
||
| ':' expr
|
||
;
|
||
|
||
components_notype
|
||
: component_notype_declarator
|
||
| components_notype ',' component_notype_declarator
|
||
;
|
||
|
||
component_notype_declarator
|
||
: notype_declarator { internal_error (0, "not implemented"); }
|
||
| notype_declarator ':' expr
|
||
| ':' expr
|
||
;
|
||
|
||
function_params
|
||
: '(' copy_spec param_list ')' { $$ = check_params ($3); }
|
||
;
|
||
|
||
param_list
|
||
: /* empty */ { $$ = 0; }
|
||
| parameter_list
|
||
| parameter_list ',' ELLIPSIS
|
||
{
|
||
$$ = param_append_identifiers ($1, 0, 0);
|
||
}
|
||
| ELLIPSIS
|
||
{
|
||
$$ = make_ellipsis ();
|
||
}
|
||
;
|
||
|
||
parameter_list
|
||
: parameter
|
||
| parameter_list ',' parameter
|
||
{
|
||
$$ = append_params ($1, $3);
|
||
}
|
||
;
|
||
|
||
parameter
|
||
: declspecs_ts param_declarator
|
||
{
|
||
$$ = make_param ($2);
|
||
}
|
||
| declspecs_ts notype_declarator
|
||
{
|
||
$$ = make_param ($2);
|
||
}
|
||
| declspecs_ts absdecl
|
||
{
|
||
$$ = make_param ($2);
|
||
}
|
||
| declspecs_nosc_nots notype_declarator
|
||
{
|
||
$$ = make_param ($2);
|
||
}
|
||
| declspecs_nosc_nots absdecl
|
||
{
|
||
$$ = make_param ($2);
|
||
}
|
||
;
|
||
|
||
absdecl
|
||
: /* empty */
|
||
{
|
||
$$ = $<spec>0;
|
||
$$.sym = new_symbol (0);
|
||
}
|
||
| absdecl1
|
||
;
|
||
|
||
absdecl1
|
||
: direct_absdecl
|
||
| '*' ptr_spec absdecl
|
||
{
|
||
$$ = pointer_spec ($2, $3);
|
||
}
|
||
;
|
||
|
||
direct_absdecl
|
||
: '(' copy_spec absdecl1 ')' { $$ = $3; }
|
||
| direct_absdecl function_params
|
||
{
|
||
$$ = function_spec ($1, $2);
|
||
}
|
||
| direct_absdecl array_decl
|
||
{
|
||
$$ = array_spec ($1, $2);
|
||
}
|
||
| function_params
|
||
{
|
||
$$ = function_spec ($<spec>0, $1);
|
||
}
|
||
| array_decl
|
||
{
|
||
$$ = array_spec ($<spec>0, $1);
|
||
}
|
||
;
|
||
|
||
param_declarator
|
||
: param_declarator_starttypename
|
||
| param_declarator_nostarttypename
|
||
;
|
||
|
||
param_declarator_starttypename
|
||
: param_declarator_starttypename function_params
|
||
{ $$ = $1; internal_error (0, "not implemented"); }
|
||
| param_declarator_starttypename array_decl
|
||
{ $$ = $1; internal_error (0, "not implemented"); }
|
||
| TYPE_NAME
|
||
{ $$ = $1; internal_error (0, "not implemented"); }
|
||
| OBJECT_NAME
|
||
{ $$ = $1; internal_error (0, "not implemented"); }
|
||
;
|
||
|
||
param_declarator_nostarttypename
|
||
: param_declarator_nostarttypename function_params
|
||
{ $$ = $1; internal_error (0, "not implemented"); }
|
||
| param_declarator_nostarttypename array_decl
|
||
{ $$ = $1; internal_error (0, "not implemented"); }
|
||
| '*' ptr_spec param_declarator_starttypename
|
||
{ $$ = $3; internal_error (0, "not implemented"); }
|
||
| '*' ptr_spec param_declarator_nostarttypename
|
||
{ $$ = $3; internal_error (0, "not implemented"); }
|
||
| '(' copy_spec param_declarator_nostarttypename ')' { $$ = $3; }
|
||
;
|
||
|
||
typename
|
||
: declspecs_nosc absdecl
|
||
{
|
||
$$ = typename_spec ($2);
|
||
}
|
||
;
|
||
|
||
array_decl
|
||
: '[' expr ']'
|
||
{
|
||
if (is_int_val ($2) && expr_int ($2) > 0) {
|
||
$$ = expr_int ($2);
|
||
} else if (is_uint_val ($2) && expr_uint ($2) > 0) {
|
||
$$ = expr_uint ($2);
|
||
} else {
|
||
error (0, "invalid array size");
|
||
$$ = 0;
|
||
}
|
||
}
|
||
| '[' ']' { $$ = 0; }
|
||
;
|
||
|
||
decl
|
||
: declspecs_ts local_expr initdecls ';'
|
||
{
|
||
$$ = local_expr;
|
||
local_expr = 0;
|
||
}
|
||
| declspecs_nots local_expr notype_initdecls ';'
|
||
{
|
||
$$ = local_expr;
|
||
local_expr = 0;
|
||
}
|
||
| declspecs_ts local_expr qc_func_params
|
||
{
|
||
$<spec>$ = parse_qc_params ($1, $3);
|
||
}
|
||
qc_func_decls
|
||
{
|
||
$$ = local_expr;
|
||
local_expr = 0;
|
||
}
|
||
| declspecs ';' { $$ = 0; }
|
||
;
|
||
|
||
local_expr
|
||
: /* emtpy */
|
||
{
|
||
$<spec>$ = $<spec>0;
|
||
local_expr = new_block_expr ();
|
||
}
|
||
;
|
||
|
||
var_initializer
|
||
: expr { $$ = $1; }
|
||
| compound_init
|
||
{
|
||
if (!$1 && is_scalar ($<spec>-1.type)) {
|
||
error (0, "empty scalar initializer");
|
||
}
|
||
$$ = $1 ? $1 : new_nil_expr ();
|
||
}
|
||
;
|
||
|
||
compound_init
|
||
: '{' element_list optional_comma '}' { $$ = $2; }
|
||
| '{' '}' { $$ = 0; }
|
||
;
|
||
|
||
ose
|
||
: /* emtpy */ { $$ = 0; }
|
||
| SHR vector_expr { $$ = build_state_expr ($2); }
|
||
;
|
||
|
||
optional_state_expr
|
||
: /* emtpy */ { $$ = 0; }
|
||
| vector_expr { $$ = build_state_expr ($1); }
|
||
;
|
||
|
||
element_list
|
||
: element
|
||
{
|
||
$$ = new_compound_init ();
|
||
append_element ($$, $1);
|
||
}
|
||
| element_list ',' element
|
||
{
|
||
append_element ($$, $3);
|
||
}
|
||
;
|
||
|
||
element
|
||
: designator '=' compound_init { $$ = new_element ($3, $1); }
|
||
| designator '=' expr { $$ = new_element ($3, $1); }
|
||
| compound_init { $$ = new_element ($1, 0); }
|
||
| expr { $$ = new_element ($1, 0); }
|
||
;
|
||
|
||
designator
|
||
: designator_spec
|
||
| designator designator_spec
|
||
{
|
||
designator_t *des = $1;
|
||
while (des->next) {
|
||
des = des->next;
|
||
}
|
||
des->next = $2;
|
||
$$ = $1;
|
||
}
|
||
;
|
||
|
||
designator_spec
|
||
: '.' ident_expr { $$ = new_designator ($2, 0); }
|
||
| '.' NAME { $$ = new_designator (new_symbol_expr ($2), 0); }
|
||
| '[' expr ']' { $$ = new_designator (0, $2); }
|
||
;
|
||
|
||
optional_comma
|
||
: /* empty */
|
||
| ','
|
||
;
|
||
|
||
push_scope
|
||
: /* empty */
|
||
{
|
||
if (!options.traditional) {
|
||
current_symtab = new_symtab (current_symtab, stab_local);
|
||
current_symtab->space = current_symtab->parent->space;
|
||
}
|
||
}
|
||
;
|
||
|
||
pop_scope
|
||
: /* empty */
|
||
{
|
||
if (!options.traditional)
|
||
current_symtab = current_symtab->parent;
|
||
}
|
||
;
|
||
|
||
compound_statement
|
||
: '{' push_scope statements '}' pop_scope { $$ = $3; }
|
||
;
|
||
|
||
statements
|
||
: /*empty*/
|
||
{
|
||
$$ = new_block_expr ();
|
||
}
|
||
| statements statement
|
||
{
|
||
$$ = append_expr ($1, $2);
|
||
}
|
||
;
|
||
|
||
local_def
|
||
: LOCAL decl { $$ = $2; }
|
||
| decl
|
||
;
|
||
|
||
statement
|
||
: ';' { $$ = 0; }
|
||
| error ';' { $$ = 0; yyerrok; }
|
||
| compound_statement { $$ = $1; }
|
||
| local_def { $$ = $1; }
|
||
| RETURN opt_expr ';' { $$ = return_expr (current_func, $2); }
|
||
| RETURN compound_init ';' { $$ = return_expr (current_func, $2); }
|
||
| AT_RETURN expr ';' { $$ = at_return_expr (current_func, $2); }
|
||
| BREAK ';'
|
||
{
|
||
$$ = 0;
|
||
if (break_label)
|
||
$$ = goto_expr (break_label);
|
||
else
|
||
error (0, "break outside of loop or switch");
|
||
}
|
||
| CONTINUE ';'
|
||
{
|
||
$$ = 0;
|
||
if (continue_label)
|
||
$$ = goto_expr (continue_label);
|
||
else
|
||
error (0, "continue outside of loop");
|
||
}
|
||
| label
|
||
{
|
||
$$ = named_label_expr ($1);
|
||
}
|
||
| CASE expr ':'
|
||
{
|
||
$$ = case_label_expr (switch_block, $2);
|
||
}
|
||
| DEFAULT ':'
|
||
{
|
||
$$ = case_label_expr (switch_block, 0);
|
||
}
|
||
| SWITCH break_label '(' expr switch_block ')' compound_statement
|
||
{
|
||
$$ = switch_expr (switch_block, break_label, $7);
|
||
switch_block = $5;
|
||
break_label = $2;
|
||
}
|
||
| GOTO NAME
|
||
{
|
||
expr_t *label = named_label_expr ($2);
|
||
$$ = goto_expr (label);
|
||
}
|
||
| IF not '(' texpr ')' statement %prec IFX
|
||
{
|
||
$$ = build_if_statement ($2, $4, $6, 0, 0);
|
||
}
|
||
| IF not '(' texpr ')' statement else statement
|
||
{
|
||
$$ = build_if_statement ($2, $4, $6, $7, $8);
|
||
}
|
||
| FOR push_scope break_label continue_label
|
||
'(' opt_init_semi opt_expr ';' opt_expr ')' statement pop_scope
|
||
{
|
||
if ($6) {
|
||
$6 = build_block_expr ($6);
|
||
}
|
||
$$ = build_for_statement ($6, $7, $9, $11,
|
||
break_label, continue_label);
|
||
break_label = $3;
|
||
continue_label = $4;
|
||
}
|
||
| WHILE break_label continue_label not '(' texpr ')' statement
|
||
{
|
||
$$ = build_while_statement ($4, $6, $8, break_label,
|
||
continue_label);
|
||
break_label = $2;
|
||
continue_label = $3;
|
||
}
|
||
| DO break_label continue_label statement WHILE not '(' texpr ')' ';'
|
||
{
|
||
$$ = build_do_while_statement ($4, $6, $8,
|
||
break_label, continue_label);
|
||
break_label = $2;
|
||
continue_label = $3;
|
||
}
|
||
| comma_expr ';'
|
||
{
|
||
$$ = $1;
|
||
}
|
||
;
|
||
|
||
not
|
||
: NOT { $$ = 1; }
|
||
| /* empty */ { $$ = 0; }
|
||
;
|
||
|
||
else
|
||
: ELSE
|
||
{
|
||
// this is only to get the the file and line number info
|
||
$$ = new_nil_expr ();
|
||
}
|
||
;
|
||
|
||
label
|
||
: NAME ':'
|
||
;
|
||
|
||
bool_label
|
||
: /* empty */
|
||
{
|
||
$$ = new_label_expr ();
|
||
}
|
||
;
|
||
|
||
break_label
|
||
: /* empty */
|
||
{
|
||
$$ = break_label;
|
||
break_label = new_label_expr ();
|
||
}
|
||
;
|
||
|
||
continue_label
|
||
: /* empty */
|
||
{
|
||
$$ = continue_label;
|
||
continue_label = new_label_expr ();
|
||
}
|
||
;
|
||
|
||
switch_block
|
||
: /* empty */
|
||
{
|
||
$$ = switch_block;
|
||
switch_block = new_switch_block ();
|
||
switch_block->test = $<expr>0;
|
||
}
|
||
;
|
||
|
||
opt_init_semi
|
||
: comma_expr ';'
|
||
| decl /* contains ; */
|
||
| ';'
|
||
{
|
||
$$ = 0;
|
||
}
|
||
;
|
||
|
||
opt_expr
|
||
: comma_expr
|
||
| /* empty */
|
||
{
|
||
$$ = 0;
|
||
}
|
||
;
|
||
|
||
unary_expr
|
||
: NAME { $$ = new_symbol_expr ($1); }
|
||
| ARGS { $$ = new_name_expr (".args"); }
|
||
| SELF { $$ = new_self_expr (); }
|
||
| THIS { $$ = new_this_expr (); }
|
||
| const { $$ = $1; }
|
||
| '(' expr ')' { $$ = $2; $$->paren = 1; }
|
||
| unary_expr '(' opt_arg_list ')' { $$ = function_expr ($1, $3); }
|
||
| unary_expr '[' expr ']' { $$ = array_expr ($1, $3); }
|
||
| unary_expr '.' ident_expr { $$ = field_expr ($1, $3); }
|
||
| unary_expr '.' unary_expr { $$ = field_expr ($1, $3); }
|
||
| INCOP unary_expr { $$ = incop_expr ($1, $2, 0); }
|
||
| unary_expr INCOP { $$ = incop_expr ($2, $1, 1); }
|
||
| '+' cast_expr %prec UNARY { $$ = $2; }
|
||
| '-' cast_expr %prec UNARY { $$ = unary_expr ('-', $2); }
|
||
| '!' cast_expr %prec UNARY { $$ = unary_expr ('!', $2); }
|
||
| '~' cast_expr %prec UNARY { $$ = unary_expr ('~', $2); }
|
||
| '&' cast_expr %prec UNARY { $$ = address_expr ($2, 0); }
|
||
| '*' cast_expr %prec UNARY { $$ = deref_pointer_expr ($2); }
|
||
| SIZEOF unary_expr %prec UNARY { $$ = sizeof_expr ($2, 0); }
|
||
| SIZEOF '(' typename ')' %prec HYPERUNARY
|
||
{
|
||
$$ = sizeof_expr (0, $3.type);
|
||
}
|
||
| vector_expr { $$ = new_vector_list ($1); }
|
||
| obj_expr { $$ = $1; }
|
||
;
|
||
|
||
ident_expr
|
||
: OBJECT_NAME { $$ = new_symbol_expr ($1.sym); }
|
||
| CLASS_NAME { $$ = new_symbol_expr ($1); }
|
||
| TYPE_NAME { $$ = new_symbol_expr ($1.sym); }
|
||
;
|
||
|
||
vector_expr
|
||
: '[' expr ',' expr_list ']'
|
||
{
|
||
expr_t *t = $4;
|
||
while (t->next)
|
||
t = t->next;
|
||
t->next = $2;
|
||
$$ = $4;
|
||
}
|
||
;
|
||
|
||
cast_expr
|
||
: '(' typename ')' cast_expr
|
||
{
|
||
$$ = cast_expr (find_type ($2.type), $4);
|
||
}
|
||
| unary_expr %prec LOW
|
||
;
|
||
|
||
expr
|
||
: cast_expr
|
||
| expr '=' expr { $$ = assign_expr ($1, $3); }
|
||
| expr '=' compound_init { $$ = assign_expr ($1, $3); }
|
||
| expr ASX expr { $$ = asx_expr ($2, $1, $3); }
|
||
| expr '?' expr ':' expr { $$ = conditional_expr ($1, $3, $5); }
|
||
| expr AND bool_label expr { $$ = bool_expr (AND, $3, $1, $4); }
|
||
| expr OR bool_label expr { $$ = bool_expr (OR, $3, $1, $4); }
|
||
| expr EQ expr { $$ = binary_expr (EQ, $1, $3); }
|
||
| expr NE expr { $$ = binary_expr (NE, $1, $3); }
|
||
| expr LE expr { $$ = binary_expr (LE, $1, $3); }
|
||
| expr GE expr { $$ = binary_expr (GE, $1, $3); }
|
||
| expr LT expr { $$ = binary_expr (LT, $1, $3); }
|
||
| expr GT expr { $$ = binary_expr (GT, $1, $3); }
|
||
| expr SHL expr { $$ = binary_expr (SHL, $1, $3); }
|
||
| expr SHR expr { $$ = binary_expr (SHR, $1, $3); }
|
||
| expr '+' expr { $$ = binary_expr ('+', $1, $3); }
|
||
| expr '-' expr { $$ = binary_expr ('-', $1, $3); }
|
||
| expr '*' expr { $$ = binary_expr ('*', $1, $3); }
|
||
| expr '/' expr { $$ = binary_expr ('/', $1, $3); }
|
||
| expr '&' expr { $$ = binary_expr ('&', $1, $3); }
|
||
| expr '|' expr { $$ = binary_expr ('|', $1, $3); }
|
||
| expr '^' expr { $$ = binary_expr ('^', $1, $3); }
|
||
| expr '%' expr { $$ = binary_expr ('%', $1, $3); }
|
||
| expr MOD expr { $$ = binary_expr (MOD, $1, $3); }
|
||
| expr CROSS expr { $$ = binary_expr (CROSS, $1, $3); }
|
||
| expr DOT expr { $$ = binary_expr (DOT, $1, $3); }
|
||
| expr HADAMARD expr { $$ = binary_expr (HADAMARD, $1, $3); }
|
||
;
|
||
|
||
texpr
|
||
: expr { $$ = convert_bool ($1, 1); }
|
||
;
|
||
|
||
comma_expr
|
||
: expr_list
|
||
{
|
||
if ($1->next) {
|
||
expr_t *res = $1;
|
||
$1 = build_block_expr ($1);
|
||
$1->e.block.result = res;
|
||
}
|
||
$$ = $1;
|
||
}
|
||
;
|
||
|
||
expr_list
|
||
: expr
|
||
| expr_list ',' expr
|
||
{
|
||
$3->next = $1;
|
||
$$ = $3;
|
||
}
|
||
;
|
||
|
||
opt_arg_list
|
||
: /* emtpy */ { $$ = 0; }
|
||
| arg_list { $$ = $1; }
|
||
;
|
||
|
||
arg_list
|
||
: arg_expr
|
||
| arg_list ',' arg_expr
|
||
{
|
||
$3->next = $1;
|
||
$$ = $3;
|
||
}
|
||
;
|
||
|
||
arg_expr
|
||
: expr
|
||
| compound_init
|
||
;
|
||
|
||
const
|
||
: VALUE
|
||
| NIL { $$ = new_nil_expr (); }
|
||
| string
|
||
;
|
||
|
||
string
|
||
: STRING
|
||
| string STRING { $$ = binary_expr ('+', $1, $2); }
|
||
;
|
||
|
||
identifier
|
||
: NAME
|
||
| OBJECT_NAME { $$ = $1.sym; }
|
||
| CLASS_NAME
|
||
| TYPE_NAME { $$ = $1.sym; }
|
||
;
|
||
|
||
// Objective-QC stuff
|
||
|
||
obj_def
|
||
: classdef { }
|
||
| classdecl
|
||
| protocoldecl
|
||
| protocoldef
|
||
| { if (!current_class) PARSE_ERROR; } methoddef
|
||
| END
|
||
{
|
||
if (!current_class)
|
||
PARSE_ERROR;
|
||
else
|
||
class_finish (current_class);
|
||
current_class = 0;
|
||
}
|
||
;
|
||
|
||
identifier_list
|
||
: identifier
|
||
{
|
||
$$ = append_expr (new_block_expr (), new_symbol_expr ($1));
|
||
}
|
||
| identifier_list ',' identifier
|
||
{
|
||
$$ = append_expr ($1, new_symbol_expr ($3));
|
||
}
|
||
;
|
||
|
||
classdecl
|
||
: CLASS identifier_list ';'
|
||
{
|
||
expr_t *e;
|
||
for (e = $2->e.block.head; e; e = e->next) {
|
||
get_class (e->e.symbol, 1);
|
||
if (!e->e.symbol->table)
|
||
symtab_addsymbol (current_symtab, e->e.symbol);
|
||
}
|
||
}
|
||
;
|
||
|
||
class_name
|
||
: identifier %prec CLASS_NOT_CATEGORY
|
||
{
|
||
if (!$1->type) {
|
||
$$ = get_class ($1, 1);
|
||
if (!$1->table) {
|
||
symtab_addsymbol (current_symtab, $1);
|
||
}
|
||
} else if (!is_class ($1->type)) {
|
||
error (0, "`%s' is not a class", $1->name);
|
||
$$ = get_class (0, 1);
|
||
} else {
|
||
$$ = $1->type->t.class;
|
||
}
|
||
}
|
||
;
|
||
|
||
new_class_name
|
||
: identifier
|
||
{
|
||
if (current_class) {
|
||
warning (0, "‘@end’ missing in implementation context");
|
||
class_finish (current_class);
|
||
current_class = 0;
|
||
}
|
||
$$ = get_class ($1, 0);
|
||
if (!$$) {
|
||
$1 = check_redefined ($1);
|
||
$$ = get_class ($1, 1);
|
||
}
|
||
$$->interface_declared = 1;
|
||
current_class = &$$->class_type;
|
||
if (!$1->table)
|
||
symtab_addsymbol (current_symtab, $1);
|
||
}
|
||
;
|
||
|
||
class_with_super
|
||
: class_name ':' class_name
|
||
{
|
||
if ($1->super_class != $3)
|
||
error (0, "%s is not a super class of %s", $3->name, $1->name);
|
||
$$ = $1;
|
||
}
|
||
;
|
||
|
||
new_class_with_super
|
||
: new_class_name ':' class_name
|
||
{
|
||
if (!$3->interface_declared) {
|
||
$3->interface_declared = 1;
|
||
error (0, "cannot find interface declaration for `%s', "
|
||
"superclass of `%s'", $3->name, $1->name);
|
||
}
|
||
$1->interface_declared = 1;
|
||
$1->super_class = $3;
|
||
$$ = $1;
|
||
}
|
||
;
|
||
|
||
category_name
|
||
: identifier '(' identifier ')'
|
||
{
|
||
$$ = get_category ($1, $3->name, 0);
|
||
if (!$$) {
|
||
error (0, "undefined category `%s (%s)'", $1->name, $3->name);
|
||
$$ = get_category (0, 0, 1);
|
||
}
|
||
}
|
||
;
|
||
|
||
new_category_name
|
||
: identifier '(' identifier ')'
|
||
{
|
||
if (current_class) {
|
||
warning (0, "‘@end’ missing in implementation context");
|
||
class_finish (current_class);
|
||
current_class = 0;
|
||
}
|
||
$$ = get_category ($1, $3->name, 1);
|
||
if ($$->defined) {
|
||
error (0, "redefinition of category `%s (%s)'",
|
||
$1->name, $3->name);
|
||
$$ = get_category (0, 0, 1);
|
||
}
|
||
current_class = &$$->class_type;
|
||
}
|
||
;
|
||
|
||
class_reference
|
||
: identifier
|
||
{
|
||
emit_class_ref ($1->name);
|
||
}
|
||
;
|
||
|
||
category_reference
|
||
: identifier '(' identifier ')'
|
||
{
|
||
emit_category_ref ($1->name, $3->name);
|
||
}
|
||
;
|
||
|
||
protocol_name
|
||
: identifier
|
||
{
|
||
$$ = get_protocol ($1->name, 0);
|
||
if ($$ && $$->methods) {
|
||
error (0, "redefinition of protocol %s", $1->name);
|
||
$$ = get_protocol (0, 1);
|
||
}
|
||
if (!$$) {
|
||
$$ = get_protocol ($1->name, 1);
|
||
}
|
||
$$->methods = new_methodlist ();
|
||
current_class = &$$->class_type;
|
||
}
|
||
;
|
||
|
||
classdef
|
||
: INTERFACE new_class_name
|
||
protocolrefs { class_add_protocols ($2, $3); }
|
||
'{' { $<class>$ = $2; }
|
||
ivar_decl_list '}'
|
||
{
|
||
class_add_ivars ($2, $7);
|
||
$<class>$ = $2;
|
||
}
|
||
methodprotolist { class_add_methods ($2, $10); }
|
||
END
|
||
{
|
||
current_class = 0;
|
||
}
|
||
| INTERFACE new_class_name
|
||
protocolrefs { class_add_protocols ($2, $3); }
|
||
{
|
||
class_add_ivars ($2, class_new_ivars ($2));
|
||
$<class>$ = $2;
|
||
}
|
||
methodprotolist { class_add_methods ($2, $6); }
|
||
END
|
||
{
|
||
current_class = 0;
|
||
}
|
||
| INTERFACE new_class_with_super
|
||
protocolrefs { class_add_protocols ($2, $3);}
|
||
'{' { $<class>$ = $2; }
|
||
ivar_decl_list '}'
|
||
{
|
||
class_add_ivars ($2, $7);
|
||
$<class>$ = $2;
|
||
}
|
||
methodprotolist { class_add_methods ($2, $10); }
|
||
END
|
||
{
|
||
current_class = 0;
|
||
}
|
||
| INTERFACE new_class_with_super
|
||
protocolrefs { class_add_protocols ($2, $3); }
|
||
{
|
||
class_add_ivars ($2, class_new_ivars ($2));
|
||
$<class>$ = $2;
|
||
}
|
||
methodprotolist { class_add_methods ($2, $6); }
|
||
END
|
||
{
|
||
current_class = 0;
|
||
}
|
||
| INTERFACE new_category_name
|
||
protocolrefs
|
||
{
|
||
category_add_protocols ($2, $3);
|
||
$<class>$ = $2->class;
|
||
}
|
||
methodprotolist { category_add_methods ($2, $5); }
|
||
END
|
||
{
|
||
current_class = 0;
|
||
}
|
||
| IMPLEMENTATION class_name { class_begin (&$2->class_type); }
|
||
'{' { $<class>$ = $2; }
|
||
ivar_decl_list '}'
|
||
{
|
||
class_check_ivars ($2, $6);
|
||
}
|
||
| IMPLEMENTATION class_name { class_begin (&$2->class_type); }
|
||
| IMPLEMENTATION class_with_super { class_begin (&$2->class_type); }
|
||
'{' { $<class>$ = $2; }
|
||
ivar_decl_list '}'
|
||
{
|
||
class_check_ivars ($2, $6);
|
||
}
|
||
| IMPLEMENTATION class_with_super { class_begin (&$2->class_type); }
|
||
| IMPLEMENTATION category_name { class_begin (&$2->class_type); }
|
||
| REFERENCE class_reference ';' { }
|
||
| REFERENCE category_reference ';' { }
|
||
;
|
||
|
||
protocoldecl
|
||
: protocol
|
||
protocol_name_list ';'
|
||
{
|
||
while ($2) {
|
||
get_protocol ($2->name, 1);
|
||
$2 = $2->next;
|
||
}
|
||
}
|
||
;
|
||
|
||
protocoldef
|
||
: protocol
|
||
protocol_name
|
||
protocolrefs { protocol_add_protocols ($2, $3); $<class>$ = 0; }
|
||
methodprotolist { protocol_add_methods ($2, $5); }
|
||
END
|
||
{
|
||
current_class = $<class_type>1;
|
||
}
|
||
;
|
||
|
||
protocol
|
||
: PROTOCOL { $<class_type>$ = current_class; }
|
||
;
|
||
|
||
protocolrefs
|
||
: /* emtpy */ { $$ = 0; }
|
||
| LT { $<protocol_list>$ = new_protocol_list (); }
|
||
protocol_list GT { $$ = $3; }
|
||
;
|
||
|
||
protocol_list
|
||
: identifier
|
||
{
|
||
$$ = add_protocol ($<protocol_list>0, $1->name);
|
||
}
|
||
| protocol_list ',' identifier
|
||
{
|
||
$$ = add_protocol ($1, $3->name);
|
||
}
|
||
;
|
||
|
||
ivar_decl_list
|
||
: /* empty */
|
||
{
|
||
symtab_t *tab, *ivars;
|
||
ivars = class_new_ivars ($<class>0);
|
||
for (tab = ivars; tab->parent; tab = tab->parent)
|
||
;
|
||
if (tab == current_symtab)
|
||
internal_error (0, "ivars already linked to parent scope");
|
||
$<symtab>$ = tab;
|
||
tab->parent = current_symtab;
|
||
current_symtab = ivars;
|
||
|
||
current_visibility = vis_protected;
|
||
}
|
||
ivar_decl_list_2
|
||
{
|
||
symtab_t *tab = $<symtab>1;
|
||
$$ = current_symtab;
|
||
current_symtab = tab->parent;
|
||
tab->parent = 0;
|
||
|
||
tab = $$->parent; // preserve the ivars inheritance chain
|
||
int base = 0;
|
||
if ($<class>0->super_class) {
|
||
base = type_size ($<class>0->super_class->type);
|
||
}
|
||
build_struct ('s', 0, $$, 0, base);
|
||
$$->parent = tab;
|
||
current_visibility = vis_public;
|
||
}
|
||
;
|
||
|
||
ivar_decl_list_2
|
||
: ivar_decl_list_2 visibility_spec ivar_decls
|
||
| ivar_decls
|
||
;
|
||
|
||
visibility_spec
|
||
: PRIVATE { current_visibility = vis_private; }
|
||
| PROTECTED { current_visibility = vis_protected; }
|
||
| PUBLIC { current_visibility = vis_public; }
|
||
;
|
||
|
||
ivar_decls
|
||
: /* empty */
|
||
| ivar_decls ivar_decl ';'
|
||
;
|
||
|
||
ivar_decl
|
||
: declspecs_nosc_ts ivars
|
||
| declspecs_nosc_ts
|
||
{
|
||
if (is_anonymous_struct ($1)) {
|
||
// type->name always begins with "tag "
|
||
$1.sym = new_symbol (va (0, ".anonymous.%s",
|
||
$1.type->name + 4));
|
||
$1.sym->type = $1.type;
|
||
$1.sym->sy_type = sy_var;
|
||
$1.sym->visibility = vis_anonymous;
|
||
symtab_addsymbol (current_symtab, $1.sym);
|
||
if (!$1.sym->table) {
|
||
error (0, "duplicate field `%s'", $1.sym->name);
|
||
}
|
||
}
|
||
}
|
||
| declspecs_nosc_nots notype_ivars
|
||
;
|
||
|
||
ivars
|
||
: ivar_declarator { }
|
||
| ivars ',' { $<spec>$ = $<spec>0; } ivar_declarator
|
||
;
|
||
|
||
notype_ivars
|
||
: notype_ivar_declarator { }
|
||
| notype_ivars ',' { $<spec>$ = $<spec>0; }
|
||
notype_ivar_declarator
|
||
;
|
||
|
||
ivar_declarator
|
||
: declarator
|
||
{
|
||
declare_field ($1, current_symtab);
|
||
}
|
||
| declarator ':' expr
|
||
| ':' expr
|
||
;
|
||
|
||
notype_ivar_declarator
|
||
: notype_declarator { internal_error (0, "not implemented"); }
|
||
| notype_declarator ':' expr
|
||
| ':' expr
|
||
;
|
||
|
||
methoddef
|
||
: ci methoddecl ose
|
||
{
|
||
method_t *method = $2;
|
||
|
||
method->instance = $1;
|
||
$2 = method = class_find_method (current_class, method);
|
||
$<symbol>$ = method_symbol (current_class, method);
|
||
}
|
||
save_storage
|
||
{
|
||
method_t *method = $2;
|
||
const char *nicename = method_name (method);
|
||
symbol_t *sym = $<symbol>4;
|
||
symtab_t *ivar_scope;
|
||
|
||
$<symtab>$ = current_symtab;
|
||
|
||
ivar_scope = class_ivar_scope (current_class, current_symtab);
|
||
current_func = begin_function (sym, nicename, ivar_scope, 1,
|
||
sc_static);
|
||
class_finish_ivar_scope (current_class, ivar_scope,
|
||
current_func->locals);
|
||
method->func = sym->s.func;
|
||
method->def = sym->s.func->def;
|
||
current_symtab = current_func->locals;
|
||
current_storage = sc_local;
|
||
}
|
||
compound_statement
|
||
{
|
||
build_code_function ($<symbol>4, $3, $7);
|
||
current_symtab = $<symtab>6;
|
||
current_storage = $5.storage;
|
||
current_func = 0;
|
||
}
|
||
| ci methoddecl '=' '#' const ';'
|
||
{
|
||
symbol_t *sym;
|
||
method_t *method = $2;
|
||
|
||
method->instance = $1;
|
||
method = class_find_method (current_class, method);
|
||
sym = method_symbol (current_class, method);
|
||
build_builtin_function (sym, $5, 1, sc_static);
|
||
method->func = sym->s.func;
|
||
method->def = sym->s.func->def;
|
||
}
|
||
;
|
||
|
||
ci
|
||
: '+' { $$ = 0; }
|
||
| '-' { $$ = 1; }
|
||
;
|
||
|
||
methodprotolist
|
||
: /* emtpy */ { $$ = 0; }
|
||
| methodprotolist2
|
||
;
|
||
|
||
methodprotolist2
|
||
: { } methodproto
|
||
{
|
||
$$ = new_methodlist ();
|
||
add_method ($$, $2);
|
||
}
|
||
| methodprotolist2 methodproto
|
||
{
|
||
add_method ($1, $2);
|
||
$$ = $1;
|
||
}
|
||
;
|
||
|
||
methodproto
|
||
: '+' methoddecl ';'
|
||
{
|
||
$2->instance = 0;
|
||
$$ = $2;
|
||
}
|
||
| '-' error ';'
|
||
{ $$ = new_method (&type_id, make_selector ("", 0, 0), 0); }
|
||
| '+' error ';'
|
||
{ $$ = new_method (&type_id, make_selector ("", 0, 0), 0); }
|
||
| '-' methoddecl ';'
|
||
{
|
||
$2->instance = 1;
|
||
$$ = $2;
|
||
}
|
||
;
|
||
|
||
methoddecl
|
||
: '(' typename ')' unaryselector
|
||
{ $$ = new_method ($2.type, $4, 0); }
|
||
| unaryselector
|
||
{ $$ = new_method (&type_id, $1, 0); }
|
||
| '(' typename ')' keywordselector optional_param_list
|
||
{ $$ = new_method ($2.type, $4, $5); }
|
||
| keywordselector optional_param_list
|
||
{ $$ = new_method (&type_id, $1, $2); }
|
||
;
|
||
|
||
optional_param_list
|
||
: /* empty */ { $$ = 0; }
|
||
| ',' param_list { $$ = $2; }
|
||
;
|
||
|
||
unaryselector
|
||
: selector { $$ = make_selector ($1->name, 0, 0); }
|
||
;
|
||
|
||
keywordselector
|
||
: keyworddecl
|
||
| keywordselector keyworddecl { $2->next = $1; $$ = $2; }
|
||
;
|
||
|
||
protocol_name_list
|
||
: identifier
|
||
| protocol_name_list ',' identifier { $3->next = $1; $$ = $3; }
|
||
|
||
selector
|
||
: NAME { $$ = $1; }
|
||
| CLASS_NAME { $$ = $1; }
|
||
| OBJECT_NAME { $$ = new_symbol (qc_yytext); }
|
||
| TYPE_SPEC { $$ = new_symbol (qc_yytext); }
|
||
| TYPE_NAME { $$ = $1.sym; }
|
||
| reserved_word
|
||
;
|
||
|
||
reserved_word
|
||
: LOCAL { $$ = new_symbol (qc_yytext); }
|
||
| RETURN { $$ = new_symbol (qc_yytext); }
|
||
| WHILE { $$ = new_symbol (qc_yytext); }
|
||
| DO { $$ = new_symbol (qc_yytext); }
|
||
| IF { $$ = new_symbol (qc_yytext); }
|
||
| ELSE { $$ = new_symbol (qc_yytext); }
|
||
| FOR { $$ = new_symbol (qc_yytext); }
|
||
| BREAK { $$ = new_symbol (qc_yytext); }
|
||
| CONTINUE { $$ = new_symbol (qc_yytext); }
|
||
| SWITCH { $$ = new_symbol (qc_yytext); }
|
||
| CASE { $$ = new_symbol (qc_yytext); }
|
||
| DEFAULT { $$ = new_symbol (qc_yytext); }
|
||
| NIL { $$ = new_symbol (qc_yytext); }
|
||
| STRUCT { $$ = new_symbol (qc_yytext); }
|
||
| ENUM { $$ = new_symbol (qc_yytext); }
|
||
| TYPEDEF { $$ = new_symbol (qc_yytext); }
|
||
;
|
||
|
||
keyworddecl
|
||
: selector ':' '(' typename ')' identifier
|
||
{ $$ = make_selector ($1->name, $4.type, $6->name); }
|
||
| selector ':' identifier
|
||
{ $$ = make_selector ($1->name, &type_id, $3->name); }
|
||
| ':' '(' typename ')' identifier
|
||
{ $$ = make_selector ("", $3.type, $5->name); }
|
||
| ':' identifier
|
||
{ $$ = make_selector ("", &type_id, $2->name); }
|
||
;
|
||
|
||
obj_expr
|
||
: obj_messageexpr
|
||
| SELECTOR '(' selectorarg ')' { $$ = selector_expr ($3); }
|
||
| PROTOCOL '(' identifier ')' { $$ = protocol_expr ($3->name); }
|
||
| ENCODE '(' typename ')' { $$ = encode_expr ($3.type); }
|
||
| obj_string /* FIXME string object? */
|
||
;
|
||
|
||
obj_messageexpr
|
||
: '[' receiver messageargs ']' { $$ = message_expr ($2, $3); }
|
||
;
|
||
|
||
receiver
|
||
: expr
|
||
| CLASS_NAME
|
||
{
|
||
$$ = new_symbol_expr ($1);
|
||
}
|
||
;
|
||
|
||
messageargs
|
||
: selector { $$ = new_keywordarg ($1->name, 0); }
|
||
| keywordarglist
|
||
;
|
||
|
||
keywordarglist
|
||
: keywordarg
|
||
| keywordarglist keywordarg
|
||
{
|
||
$2->next = $1;
|
||
$$ = $2;
|
||
}
|
||
;
|
||
|
||
keywordarg
|
||
: selector ':' arg_list { $$ = new_keywordarg ($1->name, $3); }
|
||
| ':' arg_list { $$ = new_keywordarg ("", $2); }
|
||
;
|
||
|
||
selectorarg
|
||
: selector { $$ = new_keywordarg ($1->name, 0); }
|
||
| keywordnamelist
|
||
;
|
||
|
||
keywordnamelist
|
||
: keywordname
|
||
| keywordnamelist keywordname
|
||
{
|
||
$2->next = $1;
|
||
$$ = $2;
|
||
}
|
||
;
|
||
|
||
keywordname
|
||
: selector ':' { $$ = new_keywordarg ($1->name, new_nil_expr ()); }
|
||
| ':' { $$ = new_keywordarg ("", new_nil_expr ()); }
|
||
;
|
||
|
||
obj_string
|
||
: '@' STRING
|
||
{
|
||
//FIXME string object
|
||
$$ = $2;
|
||
}
|
||
;
|
||
|
||
%%
|