quakeforge/tools/qfcc/source/qc-parse.y
Bill Currie 3da7ca4421 [qfcc] Support qc function local variables
This had gotten completely lost in the rework.
2023-02-14 12:45:04 +09:00

2274 lines
49 KiB
Text
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

%{
/*
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;
}
// 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
%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
%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 <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 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 || new.storage) {
if ((spec.is_typedef || spec.storage) && !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 int
is_anonymous_struct (specifier_t spec)
{
if (spec.sym) {
return 0;
}
if (!is_struct (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)) {
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
{
specifier_t spec = default_type ($1, 0);
$<spec>$ = parse_qc_params (spec, $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
{
$$ = new_param (0, $1.type, $2->name);
}
| typespec_reserved qc_func_params identifier
{
specifier_t spec = default_type ($1, 0);
spec = parse_qc_params (spec, $2);
$$ = new_param (0, spec.type, $3->name);
}
| ELLIPSIS { $$ = new_param (0, 0, 0); }
;
qc_param
: typespec identifier
{
$$ = new_param (0, $1.type, $2->name);
}
| typespec qc_func_params identifier
{
specifier_t spec = default_type ($1, 0);
spec = parse_qc_params (spec, $2);
$$ = new_param (0, spec.type, $3->name);
}
| ELLIPSIS { $$ = new_param (0, 0, 0); }
;
/* 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->params = spec.params;
sym->type = find_type (spec.type);
sym = function_symbol (sym, spec.is_overload, 1);
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;
sym->type = find_type (spec.type);
if (!local_expr && sym->type->type != ev_field) {
sym->params = spec.params;
sym = function_symbol (sym, spec.is_overload, 1);
}
if (!local_expr && sym->type->type != ev_field) {
// 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
{
specifier_t spec = $<spec>0;
symbol_t *sym = $1;
$<symtab>$ = current_symtab;
sym->params = spec.params;
sym->type = find_type (spec.type);
sym = function_symbol (sym, spec.is_overload, 1);
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
{
specifier_t spec = default_type ($1, $1.sym);
spec.sym->params = $2;
spec.type = parse_params (spec.type, $2);
spec.is_function = 1; //FIXME do proper void(*)() -> ev_func
$$ = spec;
}
| after_type_declarator array_decl
{
specifier_t spec = default_type ($1, $1.sym);
spec.type = array_type (spec.type, $2);
$$ = spec;
}
| '*' ptr_spec after_type_declarator
{
$$ = $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
: /* empty */
{
$$ = default_type ($<spec>-1, 0);
$$.type = pointer_type ($$.type);
}
;
notype_declarator
: '(' copy_spec notype_declarator ')' { $$ = $3; }
| notype_declarator function_params
{
//printf ("notype_declarator p %d\n", pr.source_line);
specifier_t spec = default_type ($1, $1.sym);
spec.sym->params = $2;
spec.type = parse_params (spec.type, $2);
spec.is_function = 1; //FIXME do proper void(*)() -> ev_func
$$ = spec;
}
| notype_declarator array_decl
{
specifier_t spec = default_type ($1, $1.sym);
spec.type = array_type (spec.type, $2);
$$ = spec;
}
| '*' ptr_spec notype_declarator
{
$$ = $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 = spec.sym;
set_func_type_attrs (spec.type, spec);
sym->type = find_type (spec.type);
$<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 = spec.sym;
set_func_type_attrs (spec.type, spec);
sym->type = find_type (spec.type);
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);
}
}
;
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
;
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
{
symbol_t *s = $1.sym;
specifier_t spec = default_type ($1, s);
s->type = find_type (spec.type);
s->sy_type = sy_var;
s->visibility = current_visibility;
symtab_addsymbol (current_symtab, s);
if (!s->table) {
error (0, "duplicate field `%s'", s->name);
}
}
| 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
{
$$ = new_param (0, 0, 0);
}
;
parameter_list
: parameter
| parameter_list ',' parameter
{
$$ = append_params ($1, $3);
}
;
parameter
: declspecs_ts param_declarator
{
$2 = default_type ($2, $2.sym);
$$ = new_param (0, $2.type, $2.sym->name);
}
| declspecs_ts notype_declarator
{
$2 = default_type ($2, $2.sym);
$$ = new_param (0, $2.type, $2.sym->name);
}
| declspecs_ts absdecl
{
$2 = default_type ($2, $2.sym);
$$ = new_param (0, $2.type, 0);
}
| declspecs_nosc_nots notype_declarator
{
$2 = default_type ($2, $2.sym);
$$ = new_param (0, $2.type, $2.sym->name);
}
| declspecs_nosc_nots absdecl
{
$2 = default_type ($2, 0);
$$ = new_param (0, $2.type, 0);
}
;
absdecl
: /* empty */ { $$ = $<spec>0; }
| absdecl1
;
absdecl1
: direct_absdecl
| '*' ptr_spec absdecl
{
$$ = $3;
}
;
direct_absdecl
: '(' copy_spec absdecl1 ')' { $$ = $3; }
| direct_absdecl function_params
{
specifier_t spec = $1;
spec.type = parse_params (spec.type, $2);
$$ = spec;
}
| direct_absdecl array_decl
{
specifier_t spec = default_type ($1, 0);
spec.type = array_type (spec.type, $2);
$$ = spec;
}
| function_params
{
specifier_t spec = default_type ($<spec>0, 0);
spec.type = parse_params (spec.type, $1);
$$ = spec;
}
| array_decl
{
specifier_t spec = default_type ($<spec>0, 0);
spec.type = array_type (spec.type, $1);
$$ = spec;
}
;
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
{
//printf ("%s:%d:typename %p %p\n",
// pr.strings->strings + pr.source_file, pr.source_line,
// $1.type, $2.type);
//if ($1.type) print_type ($1.type);
//if ($2.type) print_type ($2.type);
$$ = $2.type ? $2 : default_type ($1, 0);
}
;
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
{
specifier_t spec = default_type ($1, 0);
$<spec>$ = parse_qc_params (spec, $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
: compound_init { $$ = new_element ($1, 0); }
| expr { $$ = new_element ($1, 0); }
;
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 ($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
{
symbol_t *s = $1.sym;
specifier_t spec = default_type ($1, s);
s->type = find_type (spec.type);
s->sy_type = sy_var;
s->visibility = current_visibility;
symtab_addsymbol (current_symtab, s);
if (!s->table) {
error (0, "duplicate field `%s'", s->name);
}
}
| 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, new_param ("", 0, 0), 0); }
| '+' error ';'
{ $$ = new_method (&type_id, new_param ("", 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 { $$ = new_param ($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
{ $$ = new_param ($1->name, $4.type, $6->name); }
| selector ':' identifier
{ $$ = new_param ($1->name, &type_id, $3->name); }
| ':' '(' typename ')' identifier
{ $$ = new_param ("", $3.type, $5->name); }
| ':' identifier
{ $$ = new_param ("", &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;
}
;
%%