quakeforge/tools/qfcc/source/qc-parse.y

2281 lines
49 KiB
Text
Raw Normal View History

2001-06-12 19:44:26 +00:00
%{
2001-09-28 07:09:38 +00:00
/*
qc-parse.y
2001-09-28 07:09:38 +00:00
parser for quakec
2001-09-28 07:09:38 +00:00
Copyright (C) 2001 Bill Currie <bill@taniwha.org>
2001-09-28 07:09:38 +00:00
Author: Bill Currie <bill@taniwha.org>
Date: 2001/06/12
2001-09-28 07:09:38 +00:00
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
*/
2002-06-01 04:41:25 +00:00
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
2002-06-01 04:41:25 +00:00
#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/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)
2001-06-12 20:24:02 +00:00
{
#ifdef YYERROR_VERBOSE
error (0, "%s %s\n", qc_yytext, s);
#else
error (0, "%s before %s", s, qc_yytext);
#endif
2001-06-12 20:24:02 +00:00
}
static void
2002-05-15 19:10:23 +00:00
parse_error (void)
{
error (0, "parse error before %s", qc_yytext);
2002-05-15 19:10:23 +00:00
}
#define PARSE_ERROR do { parse_error (); YYERROR; } while (0)
2001-06-12 20:24:02 +00:00
int yylex (void);
2001-06-21 07:08:34 +00:00
2001-06-12 19:44:26 +00:00
%}
2001-06-12 20:24:02 +00:00
%union {
int op;
int size;
specifier_t spec;
void *pointer; // for ensuring pointer values are null
struct type_s *type;
struct expr_s *expr;
struct element_s *element;
2002-06-04 18:44:03 +00:00
struct function_s *function;
struct switch_block_s *switch_block;
struct param_s *param;
2002-05-08 21:24:24 +00:00
struct method_s *method;
2002-05-10 00:00:23 +00:00
struct class_s *class;
2002-11-14 18:17:43 +00:00
struct category_s *category;
struct class_type_s *class_type;
2002-05-08 21:24:24 +00:00
struct protocol_s *protocol;
struct protocollist_s *protocol_list;
2002-05-08 23:12:49 +00:00
struct keywordarg_s *keywordarg;
2002-05-10 00:00:23 +00:00
struct methodlist_s *methodlist;
struct symbol_s *symbol;
struct symtab_s *symtab;
2001-06-12 20:24:02 +00:00
}
// 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
// end of tokens common between qc and qp
%left SHL SHR
%left '+' '-'
%left '*' '/' '%' MOD
%right <op> SIZEOF UNARY INCOP
%left HYPERUNARY
%left '.' '(' '['
%token <symbol> CLASS_NAME NAME
%token <expr> VALUE STRING
%token LOCAL RETURN WHILE DO IF ELSE FOR BREAK CONTINUE ELLIPSIS
%token NIL GOTO SWITCH CASE DEFAULT ENUM
%token ARGS TYPEDEF EXTERN STATIC SYSTEM NOSAVE OVERLOAD NOT
%token UNSIGNED SIGNED LONG SHORT
%token <op> STRUCT
%token <type> TYPE
%token <symbol> OBJECT TYPE_NAME
%token CLASS DEFS ENCODE END IMPLEMENTATION INTERFACE PRIVATE
%token PROTECTED PROTOCOL PUBLIC SELECTOR REFERENCE SELF THIS
%type <spec> optional_specifiers specifiers local_specifiers
%type <spec> storage_class save_storage set_spec_storage
%type <spec> type_specifier type_specifier_or_storage_class
%type <spec> type
%type <param> function_params var_list param_declaration
%type <param> qc_func_params qc_var_list qc_param_decl
%type <symbol> new_name
%type <symbol> var_decl function_decl
%type <symbol> abstract_decl abs_decl
%type <symbol> tag optional_tag
%type <spec> struct_specifier struct_def struct_decl
%type <spec> enum_specifier
%type <symbol> optional_enum_list enum_list enumerator_list enumerator
%type <symbol> enum_init
%type <size> array_decl
%type <spec> func_def_list comma
%type <expr> const string
%type <spec> ivar_decl decl decl_list
%type <spec> ivars
%type <param> param param_list
%type <symbol> methoddef
%type <expr> opt_initializer var_initializer local_def
%type <expr> opt_init opt_expr comma_expr expr
%type <expr> compound_init element_list
%type <element> element
%type <expr> 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 <expr> init_var_decl_list init_var_decl
%type <switch_block> switch_block
%type <symbol> identifier label
%type <symbol> overloaded_identifier
2001-06-12 19:44:26 +00:00
%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
2002-05-08 23:12:49 +00:00
%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
2002-05-10 00:00:23 +00:00
%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
spec_merge (specifier_t spec, specifier_t new)
{
if (new.type) {
if (spec.type && !spec.multi_type) {
error (0, "two or more data types in declaration specifiers");
spec.multi_type = 1;
}
spec.type = new.type;
}
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.is_signed |= new.is_signed;
spec.is_unsigned |= new.is_unsigned;
spec.is_short |= new.is_short;
spec.is_long |= new.is_long;
spec.is_overload |= new.is_overload;
spec.nosave |= new.nosave;
return spec;
}
static specifier_t
default_type (specifier_t spec, symbol_t *sym)
{
if (spec.type) {
if (is_float (spec.type) && !spec.multi_type) {
// no modifieres allowed
if (spec.is_unsigned) {
spec.multi_type = 1;
error (0, "both unsigned and float in declaration specifiers");
} else if (spec.is_signed) {
spec.multi_type = 1;
error (0, "both signed and float in declaration specifiers");
} else if (spec.is_short) {
spec.multi_type = 1;
error (0, "both short and float in declaration specifiers");
} else if (spec.is_long) {
spec.multi_type = 1;
error (0, "both long and float in declaration specifiers");
}
}
if (is_double (spec.type)) {
// long is allowed but ignored
if (spec.is_unsigned) {
spec.multi_type = 1;
error (0, "both unsigned and double in declaration specifiers");
} else if (spec.is_signed) {
spec.multi_type = 1;
error (0, "both signed and double in declaration specifiers");
} else if (spec.is_short) {
spec.multi_type = 1;
error (0, "both short and double in declaration specifiers");
}
}
if (is_int (spec.type)) {
// signed and short are ignored
if (spec.is_unsigned && spec.is_long) {
spec.type = &type_ulong;
} else if (spec.is_long) {
spec.type = &type_long;
}
}
} else {
if (spec.is_long) {
if (spec.is_unsigned) {
spec.type = type_ulong_uint;
} else {
spec.type = type_long_int;
}
} else {
if (spec.is_unsigned) {
spec.type = &type_uint;
} else if (spec.is_signed || spec.is_short) {
spec.type = &type_int;
}
}
}
if (!spec.type) {
spec.type = type_default;
warning (0, "type defaults to '%s' in declaration of '%s'",
type_default->name, sym->name);
}
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;
symtab_addsymbol (current_symtab, spec.sym);
return !!spec.sym->table;
}
static void
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
if (!use_type_name (spec)) {
error (0, "%s redeclared as different kind of symbol",
spec.sym->name);
}
}
}
}
%}
%expect 0
2001-06-12 19:44:26 +00:00
%%
program
: external_def_list
{
if (current_class) {
warning (0, "‘@end’ missing in implementation context");
class_finish (current_class);
current_class = 0;
}
}
external_def_list
2001-06-12 19:44:26 +00:00
: /* empty */
{
current_symtab = pr.symtab;
}
| external_def_list external_def
| external_def_list obj_def
| error END
{
yyerrok;
current_class = 0;
current_symtab = pr.symtab;
current_storage = sc_global;
}
| error ';'
{
yyerrok;
current_class = 0;
current_symtab = pr.symtab;
current_storage = sc_global;
}
| error '}'
{
yyerrok;
current_class = 0;
current_symtab = pr.symtab;
current_storage = sc_global;
}
2001-06-12 19:44:26 +00:00
;
external_def
: optional_specifiers external_decl_list ';' { }
| optional_specifiers ';'
{
check_specifiers ($1);
}
| optional_specifiers qc_func_params
2002-01-23 20:50:25 +00:00
{
type_t **type;
type_t *ret_type;
$<spec>$ = $1; // copy spec bits and storage
// .float () foo; is a field holding a function variable rather
// than a function that returns a float field.
for (type = &$1.type; *type && (*type)->type == ev_field;
type = &(*type)->t.fldptr.type) {
}
ret_type = *type;
*type = 0;
*type = parse_params (0, $2);
$<spec>$.type = find_type (append_type ($1.type, ret_type));
if ($<spec>$.type->type != ev_field)
$<spec>$.params = $2;
2002-01-23 20:50:25 +00:00
}
function_def_list
{
(void) ($<spec>3);
}
| optional_specifiers function_decl function_body
| storage_class '{' save_storage
{
current_storage = $1.storage;
}
external_def_list '}' ';'
{
current_storage = $3.storage;
}
;
save_storage
: /* emtpy */
{
$$.storage = current_storage;
}
;
set_spec_storage
: /* emtpy */
{
$$ = $<spec>0;
$$.storage = current_storage;
}
;
function_body
: optional_state_expr
{
symbol_t *sym = $<symbol>0;
specifier_t spec = default_type ($<spec>-1, sym);
sym->type = find_type (append_type (sym->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 ';'
{
symbol_t *sym = $<symbol>0;
specifier_t spec = default_type ($<spec>-1, sym);
sym->type = find_type (append_type (sym->type, spec.type));
sym = function_symbol (sym, spec.is_overload, 1);
build_builtin_function (sym, $3, 0, spec.storage);
}
;
external_decl_list
: external_decl
| external_decl_list ',' { $<spec>$ = $<spec>0; }
external_decl { (void) ($<spec>3); }
;
external_decl
: var_decl
{
specifier_t spec = default_type ($<spec>0, $1);
$1->type = find_type (append_type ($1->type, spec.type));
2011-02-02 12:58:11 +00:00
if (spec.is_typedef) {
$1->sy_type = sy_type;
$1->type=find_type (alias_type ($1->type, $1->type, $1->name));
symtab_addsymbol (current_symtab, $1);
2011-02-02 12:58:11 +00:00
} else {
initialize_def ($1, 0, current_symtab->space, spec.storage,
current_symtab);
if ($1->s.def)
$1->s.def->nosave |= spec.nosave;
2011-02-02 12:58:11 +00:00
}
}
| var_decl var_initializer
{
specifier_t spec = default_type ($<spec>0, $1);
$1->type = find_type (append_type ($1->type, spec.type));
if (spec.is_typedef) {
error (0, "typedef %s is initialized", $1->name);
$1->sy_type = sy_type;
$1->type=find_type (alias_type ($1->type, $1->type, $1->name));
symtab_addsymbol (current_symtab, $1);
} else {
initialize_def ($1, $2, current_symtab->space, spec.storage,
current_symtab);
if ($1->s.def)
$1->s.def->nosave |= spec.nosave;
}
}
| function_decl
{
specifier_t spec = default_type ($<spec>0, $1);
$1->type = find_type (append_type ($1->type, spec.type));
2011-03-02 13:23:24 +00:00
if (spec.is_typedef) {
$1->sy_type = sy_type;
$1->type=find_type (alias_type ($1->type, $1->type, $1->name));
2011-03-02 13:23:24 +00:00
symtab_addsymbol (current_symtab, $1);
} else {
$1 = function_symbol ($1, spec.is_overload, 1);
}
}
;
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); }
| NOSAVE
{
$$ = make_spec (0, current_storage, 0, 0);
$$.nosave = 1;
}
;
optional_specifiers
: specifiers
{
$$ = $1;
if (!$$.storage)
$$.storage = current_storage;
}
| /* empty */
2011-01-25 00:36:00 +00:00
{
$$ = make_spec (0, current_storage, 0, 0);
2011-01-25 00:36:00 +00:00
}
;
specifiers
: type_specifier_or_storage_class
| specifiers type_specifier_or_storage_class
2011-02-02 13:59:08 +00:00
{
$$ = spec_merge ($1, $2);
}
;
type
: type_specifier
| type type_specifier
{
// deal with eg "int id"
$1.sym = $2.sym;
if (!$1.sym && !$1.type) {
$1 = spec_merge ($1, $2);
} else if (!$1.sym) {
error (0, "two or more data types in declaration specifiers");
}
$$ = $1;
}
;
type_specifier_or_storage_class
: type_specifier
| storage_class
;
type_specifier
: TYPE
{
2011-03-07 08:49:01 +00:00
$$ = make_spec ($1, 0, 0, 0);
}
| UNSIGNED
{
$$ = make_spec (0, current_storage, 0, 0);
$$.is_unsigned = 1;
}
| SIGNED
{
$$ = make_spec (0, current_storage, 0, 0);
$$.is_signed = 1;
}
| LONG
{
$$ = make_spec (0, current_storage, 0, 0);
$$.is_long = 1;
}
| SHORT
{
$$ = make_spec (0, current_storage, 0, 0);
$$.is_short = 1;
}
| enum_specifier
| struct_specifier
| TYPE_NAME
{
2011-03-07 08:49:01 +00:00
$$ = make_spec ($1->type, 0, 0, 0);
$$.sym = $1;
}
| OBJECT 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;
}
| 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
| '.' type_specifier
{
// avoid find_type()
2011-03-07 08:49:01 +00:00
$$ = make_spec (field_type (0), 0, 0, 0);
$$.type = append_type ($$.type, $2.type);
}
;
optional_tag
: tag
| /* empty */ { $$ = 0; }
;
tag : NAME ;
enum_specifier
: ENUM tag optional_enum_list
{
2011-03-07 08:49:01 +00:00
$$ = make_spec ($3->type, 0, 0, 0);
if (!$3->table)
symtab_addsymbol (current_symtab, $3);
}
| ENUM enum_list
{
2011-03-07 08:49:01 +00:00
$$ = make_spec ($2->type, 0, 0, 0);
if (!$2->table)
symtab_addsymbol (current_symtab, $2);
}
2006-12-11 09:26:07 +00:00
;
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;
(void) ($<symbol>3);
}
;
enumerator
: identifier { add_enum ($<symbol>0, $1, 0); }
| identifier '=' expr { add_enum ($<symbol>0, $1, $3); }
;
struct_specifier
: STRUCT optional_tag '{'
{
symbol_t *sym;
sym = find_struct ($1, $2, 0);
if (!sym->table) {
symtab_addsymbol (current_symtab, sym);
} else {
if (!sym->type) {
internal_error (0, "broken structure symbol?");
}
if (sym->type->meta == ty_enum
|| (sym->type->meta == ty_struct && sym->type->t.symtab)) {
error (0, "%s %s redefined",
$1 == 's' ? "struct" : "union", $2->name);
$1 = 0;
} else if (sym->type->meta != ty_struct) {
internal_error (0, "%s is not a struct or union",
$2->name);
}
}
current_symtab = new_symtab (current_symtab, stab_local);
}
struct_defs '}'
{
symbol_t *sym;
symtab_t *symtab = current_symtab;
current_symtab = symtab->parent;
if ($1) {
sym = build_struct ($1, $2, symtab, 0);
$$ = make_spec (sym->type, 0, 0, 0);
if (!sym->table)
symtab_addsymbol (current_symtab, sym);
}
}
| STRUCT tag
{
symbol_t *sym;
sym = find_struct ($1, $2, 0);
2011-03-07 08:49:01 +00:00
$$ = make_spec (sym->type, 0, 0, 0);
if (!sym->table)
symtab_addsymbol (current_symtab, sym);
}
;
struct_defs
: struct_def_list ';'
| DEFS '(' identifier ')'
2002-05-10 00:00:23 +00:00
{
$3 = check_undefined ($3);
if (!$3->type || !is_class ($3->type)) {
error (0, "`%s' is not a class", $3->name);
2002-05-10 00:00:23 +00:00
} 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);
2002-05-10 00:00:23 +00:00
}
}
;
struct_def_list
: struct_def
| struct_def_list ';' struct_def
;
struct_def
: type struct_decl_list
| type
{
if ($1.sym && $1.sym->type != $1.type) {
// a type name (id, typedef, etc) was used as a field name.
// this is allowed in C
if (!use_type_name ($1)) {
error (0, "duplicate field `%s'", $1.sym->name);
}
} else if (is_anonymous_struct ($1)) {
// anonymous struct/union
// 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);
}
} else {
// bare type
warning (0, "declaration does not declare anything");
}
$$ = $1;
}
2001-06-12 19:44:26 +00:00
;
struct_decl_list
: struct_decl_list ',' { $<spec>$ = $<spec>0; }
struct_decl { (void) ($<spec>3); }
| struct_decl
2006-12-02 06:49:55 +00:00
;
struct_decl
: function_decl
{
$<spec>0 = default_type ($<spec>-0, $1);
$1->type = find_type (append_type ($1->type, $<spec>0.type));
$1->sy_type = sy_var;
$1->visibility = current_visibility;
symtab_addsymbol (current_symtab, $1);
if (!$1->table) {
error (0, "duplicate field `%s'", $1->name);
}
}
| var_decl
{
$<spec>0 = default_type ($<spec>-0, $1);
$1->type = find_type (append_type ($1->type, $<spec>0.type));
$1->sy_type = sy_var;
$1->visibility = current_visibility;
symtab_addsymbol (current_symtab, $1);
if (!$1->table) {
error (0, "duplicate field `%s'", $1->name);
}
}
| var_decl ':' expr %prec COMMA {}
| ':' expr %prec COMMA {}
;
new_name
: NAME %prec COMMA
2006-12-02 06:49:55 +00:00
{
$$ = $1;
// due to the way declarations work, we need a new symbol at all
// times. redelcarations will be checked later
if ($$->table)
$$ = new_symbol ($1->name);
}
;
var_decl
: new_name
{
$$ = $1;
$$->type = 0;
}
| var_decl function_params
{
$$->type = append_type ($$->type, parse_params (0, $2));
2002-01-21 19:03:29 +00:00
}
| var_decl array_decl
{
$$->type = append_type ($$->type, array_type (0, $2));
}
| '*' var_decl %prec UNARY
{
$$ = $2;
$$->type = append_type ($$->type, pointer_type (0));
}
| '(' var_decl ')' { $$ = $2; }
2002-01-21 19:03:29 +00:00
;
function_decl
: '*' function_decl
{
$$ = $2;
$$->type = append_type ($$->type, pointer_type (0));
}
| function_decl array_decl
{
$$ = $1;
$$->type = append_type ($$->type, array_type (0, $2));
}
| '(' function_decl ')' { $$ = $2; }
| function_decl function_params
{
$$ = $1;
$$->params = $2;
$$->type = append_type ($$->type, parse_params (0, $2));
}
| NAME function_params
{
$$ = $1;
$$->params = $2;
$$->type = parse_params (0, $2);
}
;
function_params
: '(' ')' { $$ = 0; }
| '(' ps var_list ')' { $$ = check_params ($3); }
;
qc_func_params
: '(' ')' { $$ = 0; }
| '(' ps qc_var_list ')' { $$ = check_params ($3); }
2011-02-13 08:37:11 +00:00
| '(' ps TYPE ')'
{
if (!is_void ($3))
2011-02-13 08:37:11 +00:00
PARSE_ERROR;
$$ = 0;
}
;
ps : ;
var_list
: param_declaration
| var_list ',' param_declaration
{
param_t *p;
for (p = $1; p->next; p = p->next)
;
p->next = $3;
$$ = $1;
}
2001-11-14 07:16:58 +00:00
;
qc_var_list
: qc_param_decl
| qc_var_list ',' qc_param_decl
{
param_t *p;
for (p = $1; p->next; p = p->next)
;
p->next = $3;
$$ = $1;
}
;
param_declaration
: type var_decl
{
$1 = default_type ($1, $2);
$2->type = find_type (append_type ($2->type, $1.type));
$$ = new_param (0, $2->type, $2->name);
}
| abstract_decl { $$ = new_param (0, $1->type, $1->name); }
| ELLIPSIS { $$ = new_param (0, 0, 0); }
;
abstract_decl
: type abs_decl
{
// abs_decl's symbol is just a carrier for the type
if ($2->name) {
bug (0, "unexpected name in abs_decl");
}
if ($1.sym) {
$1.sym = new_symbol ($1.sym->name);
$1.sym->type = $2->type;
$2 = $1.sym;
}
$$ = $2;
$1 = default_type ($1, $2);
$$->type = find_type (append_type ($$->type, $1.type));
}
| error { $$ = new_symbol (0); }
;
qc_param_decl
: type new_name
{
$2->type = find_type ($1.type);
$$ = new_param (0, $2->type, $2->name);
}
| type qc_func_params new_name
{
type_t **type;
// .float () foo; is a field holding a function variable rather
// than a function that returns a float field.
for (type = &$1.type; *type && (*type)->type == ev_field;
type = &(*type)->t.fldptr.type)
;
*type = parse_params (*type, $2);
$3->type = find_type ($1.type);
if ($3->type->type != ev_field)
$3->params = $2;
$$ = new_param (0, $3->type, $3->name);
}
| ELLIPSIS { $$ = new_param (0, 0, 0); }
;
abs_decl
: /* empty */ { $$ = new_symbol (0); }
| '(' abs_decl ')' function_params
2002-08-18 04:08:02 +00:00
{
$$ = $2;
2012-05-22 23:20:56 +00:00
$$->type = append_type ($$->type, parse_params (0, $4));
}
| '*' abs_decl
{
$$ = $2;
$$->type = append_type ($$->type, pointer_type (0));
}
| abs_decl array_decl
{
$$ = $1;
$$->type = append_type ($$->type, array_type (0, $2));
}
| '(' abs_decl ')'
{
$$ = $2;
2002-08-18 04:08:02 +00:00
}
;
array_decl
: '[' expr ']'
{
if (!is_int_val ($2) || expr_int ($2) < 1) {
error (0, "invalid array size");
$$ = 0;
} else {
$$ = expr_int ($2);
}
}
| '[' ']' { $$ = 0; }
;
local_specifiers
: LOCAL specifiers { $$ = $2; }
| specifiers { $$ = $1; }
;
2011-02-07 23:16:36 +00:00
param_list
: param { $$ = $1; }
| param_list ',' { $<param>$ = $<param>1; } param
{
$$ = $4;
(void) ($<symbol>3);
}
;
param
: type identifier
{
2011-01-25 11:21:41 +00:00
$2 = check_redefined ($2);
$$ = param_append_identifiers ($<param>0, $2, $1.type);
}
;
local_decl_list
: decl_list
| qc_func_params
{
type_t **type;
specifier_t spec = $<spec>0; // copy spec bits and storage
// .float () foo; is a field holding a function variable rather
// than a function that returns a float field.
for (type = &spec.type; *type && (*type)->type == ev_field;
type = &(*type)->t.fldptr.type)
;
*type = parse_params (*type, $1);
spec.type = find_type (spec.type);
$<spec>$ = spec;
}
func_def_list
{
(void) ($<spec>2);
}
;
decl_list
: decl_list ',' { $<spec>$ = $<spec>0; } decl { (void) ($<spec>3); }
| decl
;
decl
: function_decl {}
| var_decl opt_initializer
{
specifier_t spec = default_type ($<spec>0, $1);
storage_class_t sc = spec.storage;
struct defspace_s *space = current_symtab->space;
2011-02-07 12:38:58 +00:00
if (sc == sc_static)
space = pr.near_data;
$1->type = find_type (append_type ($1->type, spec.type));
initialize_def ($1, $2, space, sc, current_symtab);
if ($1->s.def)
$1->s.def->nosave |= spec.nosave;
}
;
function_def_list
: func_def_list comma func_def_list_term
| func_def_list_term
;
func_def_list
: func_def_list comma func_def { $$ = $2; }
| func_def { $$ = $<spec>0; }
;
/* This rule is used only to get an action before both func_def and
func_def_list_term so that the function spec can be inherited.
*/
comma: ',' { $$ = $<spec>0; }
func_def_list_term
: non_code_funcion ';'
| code_function ';'
| code_function %prec IFX
;
func_def
: non_code_funcion
| code_function
;
code_function
: overloaded_identifier code_func
;
non_code_funcion
: overloaded_identifier non_code_func
;
overloaded_identifier
: identifier
{
$$ = $1;
$$->type = $<spec>0.type;
if (!local_expr && $$->type->type != ev_field) {
$$ = function_symbol ($$, $<spec>0.is_overload, 1);
$$->params = $<spec>0.params;
}
}
;
non_code_func
: '=' '#' expr
{
build_builtin_function ($<symbol>0, $3, 0, $<spec>-1.storage);
}
| '=' expr
{
if (local_expr) {
symbol_t *sym = $<symbol>0;
specifier_t spec = $<spec>-1;
initialize_def (sym, $2, current_symtab->space, spec.storage,
current_symtab);
if (sym->s.def)
sym->s.def->nosave |= spec.nosave;
} else {
if (is_int_val ($2) || is_float_val ($2)) {
error (0, "invalid function initializer."
" did you forget #?");
} else {
error (0, "cannot create global function variables");
}
}
}
| /* emtpy */
{
symbol_t *sym = $<symbol>0;
specifier_t spec = $<spec>-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;
}
}
;
code_func
: '=' optional_state_expr
save_storage
{
$<symtab>$ = current_symtab;
current_func = begin_function ($<symbol>0, 0, current_symtab, 0,
$<spec>-1.storage);
current_symtab = current_func->locals;
current_storage = sc_local;
}
compound_statement
{
build_code_function ($<symbol>0, $2, $5);
current_symtab = $<symtab>4;
current_storage = $3.storage;
current_func = 0;
}
;
opt_initializer
: /*empty*/ { $$ = 0; }
| var_initializer { $$ = $1; }
;
var_initializer
: '=' expr { $$ = $2; }
| '=' compound_init
{
if (!$2 && is_scalar ($<spec>-1.type)) {
error (0, "empty scalar initializer");
}
$$ = $2 ? $2 : new_nil_expr ();
}
;
compound_init
: '{' element_list optional_comma '}' { $$ = $2; }
| '{' '}' { $$ = 0; }
;
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;
}
2001-10-24 06:39:49 +00:00
}
;
pop_scope
: /* empty */
2001-10-24 06:39:49 +00:00
{
if (!options.traditional)
current_symtab = current_symtab->parent;
}
2001-06-12 19:44:26 +00:00
;
compound_statement
: '{' push_scope statements '}' pop_scope { $$ = $3; }
;
2001-06-12 19:44:26 +00:00
statements
: /*empty*/
{
$$ = new_block_expr ();
}
| statements statement
{
$$ = append_expr ($1, $2);
}
2001-06-12 19:44:26 +00:00
;
local_def
: local_specifiers
{
2011-03-07 08:49:01 +00:00
if (!$1.storage)
$1.storage = sc_local;
$<spec>$ = $1;
local_expr = new_block_expr ();
}
local_decl_list ';'
{
$$ = local_expr;
local_expr = 0;
(void) ($<spec>2);
}
| specifiers ';'
{
check_specifiers ($1);
$$ = 0;
}
2012-11-20 08:11:39 +00:00
;
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); }
| 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 ';' opt_expr ';' opt_expr ')' statement pop_scope
{
if ($6) {
$6 = build_block_expr ($6);
}
$$ = build_for_statement ($6, $8, $10, $12,
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;
}
2001-06-12 19:44:26 +00:00
;
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
: comma_expr
| type set_spec_storage init_var_decl_list { $$ = $3; }
| /* empty */
{
$$ = 0;
}
;
init_var_decl_list
: init_var_decl
{
$$ = $1;
}
| init_var_decl_list ',' { $<spec>$ = $<spec>0; } init_var_decl
{
$4->next = $1;
$$ = $4;
}
;
init_var_decl
: var_decl opt_initializer
{
specifier_t spec = $<spec>0;
$1->type = find_type (append_type ($1->type, spec.type));
$1->sy_type = sy_var;
initialize_def ($1, 0, current_symtab->space, spec.storage,
current_symtab);
$$ = assign_expr (new_symbol_expr ($1), $2);
}
;
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, 0); }
2010-01-13 06:31:41 +00:00
| '*' cast_expr %prec UNARY { $$ = pointer_expr ($2); }
| SIZEOF unary_expr %prec UNARY { $$ = sizeof_expr ($2, 0); }
| SIZEOF '(' abstract_decl ')' %prec HYPERUNARY
{
$$ = sizeof_expr (0, $3->type);
}
| vector_expr { $$ = new_vector_list ($1); }
| obj_expr { $$ = $1; }
;
ident_expr
: OBJECT { $$ = new_symbol_expr ($1); }
| CLASS_NAME { $$ = new_symbol_expr ($1); }
| TYPE_NAME { $$ = new_symbol_expr ($1); }
;
vector_expr
: '[' expr ',' expr_list ']'
{
expr_t *t = $4;
while (t->next)
t = t->next;
t->next = $2;
$$ = $4;
}
;
cast_expr
: '(' abstract_decl ')' cast_expr
{
$$ = cast_expr ($2->type, $4);
}
| unary_expr %prec LOW
;
2001-06-12 19:44:26 +00:00
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); }
;
2007-05-13 08:39:07 +00:00
texpr
: expr { $$ = convert_bool ($1, 1); }
2007-05-13 08:39:07 +00:00
;
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; }
2001-06-12 19:44:26 +00:00
;
arg_list
: arg_expr
| arg_list ',' arg_expr
{
$3->next = $1;
$$ = $3;
}
2001-06-12 19:44:26 +00:00
;
arg_expr
: expr
| compound_init
;
const
: VALUE
| NIL { $$ = new_nil_expr (); }
| string
;
string
: STRING
| string STRING { $$ = binary_expr ('+', $1, $2); }
;
identifier
: NAME
| OBJECT
| CLASS_NAME
| TYPE_NAME
;
// Objective-QC stuff
2002-05-08 21:24:24 +00:00
obj_def
2002-11-15 17:12:16 +00:00
: classdef { }
| classdecl
| protocoldecl
| protocoldef
2002-05-15 19:10:23 +00:00
| { if (!current_class) PARSE_ERROR; } methoddef
| END
2002-05-15 19:10:23 +00:00
{
if (!current_class)
PARSE_ERROR;
else
class_finish (current_class);
2002-05-15 19:10:23 +00:00
current_class = 0;
}
;
2002-05-08 21:24:24 +00:00
identifier_list
: identifier
2002-05-08 21:24:24 +00:00
{
$$ = append_expr (new_block_expr (), new_symbol_expr ($1));
2002-05-08 21:24:24 +00:00
}
| identifier_list ',' identifier
2002-05-08 21:24:24 +00:00
{
$$ = append_expr ($1, new_symbol_expr ($3));
2002-05-08 21:24:24 +00:00
}
;
2002-05-10 00:00:23 +00:00
classdecl
2002-08-20 21:13:18 +00:00
: CLASS identifier_list ';'
2002-05-08 21:24:24 +00:00
{
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);
}
2002-05-08 21:24:24 +00:00
}
;
2002-08-18 04:08:02 +00:00
class_name
: identifier %prec CLASS_NOT_CATEGORY
2002-05-08 21:24:24 +00:00
{
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);
2002-05-10 00:00:23 +00:00
$$ = get_class (0, 1);
} else {
$$ = $1->type->t.class;
2002-05-10 00:00:23 +00:00
}
2002-05-08 21:24:24 +00:00
}
2002-05-10 00:00:23 +00:00
;
new_class_name
: identifier
2002-05-08 21:24:24 +00:00
{
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);
2002-05-10 00:00:23 +00:00
}
$$->interface_declared = 1;
current_class = &$$->class_type;
if (!$1->table)
symtab_addsymbol (current_symtab, $1);
2002-05-08 21:24:24 +00:00
}
2002-05-30 21:18:58 +00:00
;
2002-05-10 00:00:23 +00:00
2002-05-15 19:10:23 +00:00
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);
2002-05-15 19:10:23 +00:00
$$ = $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;
2002-05-15 19:10:23 +00:00
$1->super_class = $3;
$$ = $1;
}
;
category_name
: identifier '(' identifier ')'
2002-05-15 19:10:23 +00:00
{
$$ = get_category ($1, $3->name, 0);
2002-05-15 19:10:23 +00:00
if (!$$) {
error (0, "undefined category `%s (%s)'", $1->name, $3->name);
2002-05-15 19:10:23 +00:00
$$ = get_category (0, 0, 1);
}
}
;
new_category_name
: identifier '(' identifier ')'
2002-05-15 19:10:23 +00:00
{
if (current_class) {
warning (0, "‘@end’ missing in implementation context");
class_finish (current_class);
current_class = 0;
}
$$ = get_category ($1, $3->name, 1);
2002-05-15 19:10:23 +00:00
if ($$->defined) {
error (0, "redefinition of category `%s (%s)'",
$1->name, $3->name);
2002-05-15 19:10:23 +00:00
$$ = get_category (0, 0, 1);
}
current_class = &$$->class_type;
2002-05-15 19:10:23 +00:00
}
;
class_reference
: identifier
{
emit_class_ref ($1->name);
}
;
category_reference
: identifier '(' identifier ')'
{
emit_category_ref ($1->name, $3->name);
}
;
2002-05-10 00:00:23 +00:00
protocol_name
: identifier
2002-05-08 21:24:24 +00:00
{
$$ = get_protocol ($1->name, 0);
if ($$ && $$->methods) {
error (0, "redefinition of protocol %s", $1->name);
2002-05-10 00:00:23 +00:00
$$ = get_protocol (0, 1);
}
if (!$$) {
$$ = get_protocol ($1->name, 1);
2002-05-10 00:00:23 +00:00
}
$$->methods = new_methodlist ();
current_class = &$$->class_type;
2002-05-08 21:24:24 +00:00
}
2002-05-30 21:18:58 +00:00
;
2002-05-10 00:00:23 +00:00
2002-05-15 19:10:23 +00:00
classdef
: INTERFACE new_class_name
2006-12-01 08:17:55 +00:00
protocolrefs { class_add_protocols ($2, $3); }
'{' { $<class>$ = $2; }
ivar_decl_list '}'
{
class_add_ivars ($2, $7);
$<class>$ = $2;
}
2002-11-14 18:17:43 +00:00
methodprotolist { class_add_methods ($2, $10); }
2006-12-01 08:17:55 +00:00
END
{
current_class = 0;
(void) ($<class>6);
(void) ($<class>9);
}
| 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); }
2006-12-01 08:17:55 +00:00
END
{
current_class = 0;
(void) ($<class>5);
}
| INTERFACE new_class_with_super
protocolrefs { class_add_protocols ($2, $3);}
'{' { $<class>$ = $2; }
ivar_decl_list '}'
{
class_add_ivars ($2, $7);
$<class>$ = $2;
}
2002-11-14 18:17:43 +00:00
methodprotolist { class_add_methods ($2, $10); }
2006-12-01 08:17:55 +00:00
END
{
current_class = 0;
(void) ($<class>6);
(void) ($<class>9);
}
| 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); }
2006-12-01 08:17:55 +00:00
END
{
current_class = 0;
(void) ($<class>5);
}
| INTERFACE new_category_name
protocolrefs
{
category_add_protocols ($2, $3);
$<class>$ = $2->class;
}
2002-11-14 18:17:43 +00:00
methodprotolist { category_add_methods ($2, $5); }
2006-12-01 08:17:55 +00:00
END
{
current_class = 0;
(void) ($<class>4);
}
2002-11-14 18:17:43 +00:00
| IMPLEMENTATION class_name { class_begin (&$2->class_type); }
'{' { $<class>$ = $2; }
2006-12-01 08:17:55 +00:00
ivar_decl_list '}'
{
class_check_ivars ($2, $6);
(void) ($<class>5);
}
2002-11-14 18:17:43 +00:00
| IMPLEMENTATION class_name { class_begin (&$2->class_type); }
| IMPLEMENTATION class_with_super { class_begin (&$2->class_type); }
'{' { $<class>$ = $2; }
2006-12-01 08:17:55 +00:00
ivar_decl_list '}'
{
class_check_ivars ($2, $6);
(void) ($<class>5);
}
2002-11-14 18:17:43 +00:00
| 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;
}
}
;
2002-05-10 00:00:23 +00:00
protocoldef
: protocol
protocol_name
protocolrefs { protocol_add_protocols ($2, $3); $<class>$ = 0; }
methodprotolist { protocol_add_methods ($2, $5); }
END
{
current_class = $<class_type>1;
(void) ($<class>4);
}
;
protocol
: PROTOCOL { $<class_type>$ = current_class; }
;
2002-05-10 00:00:23 +00:00
protocolrefs
: /* emtpy */ { $$ = 0; }
| LT { $<protocol_list>$ = new_protocol_list (); }
2006-12-01 08:17:55 +00:00
protocol_list GT { $$ = $3; (void) ($<protocol_list>2); }
2003-07-25 20:34:24 +00:00
;
protocol_list
: identifier
2003-07-25 20:34:24 +00:00
{
$$ = add_protocol ($<protocol_list>0, $1->name);
2003-07-25 20:34:24 +00:00
}
| protocol_list ',' identifier
2003-07-25 20:34:24 +00:00
{
$$ = add_protocol ($1, $3->name);
2003-07-25 20:34:24 +00:00
}
;
2002-05-10 00:00:23 +00:00
ivar_decl_list
: /* empty */
{
symtab_t *tab, *ivars;
ivars = class_new_ivars ($<class>0);
for (tab = ivars; tab->parent; tab = tab->parent)
;
2011-02-14 12:25:55 +00:00
if (tab == current_symtab)
internal_error (0, "ivars already linked to parent scope");
$<symtab>$ = tab;
tab->parent = current_symtab;
current_symtab = ivars;
2002-05-17 17:58:57 +00:00
current_visibility = vis_protected;
}
ivar_decl_list_2
2002-05-15 19:10:23 +00:00
{
symtab_t *tab = $<symtab>1;
$$ = current_symtab;
current_symtab = tab->parent;
tab->parent = 0;
tab = $$->parent; // preserve the ivars inheritance chain
build_struct ('s', 0, $$, 0);
$$->parent = tab;
current_visibility = vis_public;
2002-05-15 19:10:23 +00:00
}
;
ivar_decl_list_2
: ivar_decl_list_2 visibility_spec ivar_decls
| ivar_decls
;
2002-05-10 00:00:23 +00:00
visibility_spec
: PRIVATE { current_visibility = vis_private; }
| PROTECTED { current_visibility = vis_protected; }
| PUBLIC { current_visibility = vis_public; }
;
2002-05-10 00:00:23 +00:00
ivar_decls
: /* empty */
| ivar_decls ivar_decl ';'
;
2002-05-10 00:00:23 +00:00
ivar_decl
: type ivars
| type
{
if (is_anonymous_struct ($1)) {
// anonymous struct/union
// 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);
}
} else {
// bare type
warning (0, "declaration does not declare anything");
}
}
;
2002-05-10 00:00:23 +00:00
ivars
: struct_decl
| ivars ',' { $<spec>$ = $<spec>0; } struct_decl { (void) ($<spec>3); }
;
2002-05-08 21:24:24 +00:00
methoddef
2011-02-04 12:19:52 +00:00
: ci methoddecl optional_state_expr
{
method_t *method = $2;
method->instance = $1;
$2 = method = class_find_method (current_class, method);
$<symbol>$ = method_symbol (current_class, method);
}
save_storage
2011-02-04 12:19:52 +00:00
{
method_t *method = $2;
const char *nicename = method_name (method);
symbol_t *sym = $<symbol>4;
symtab_t *ivar_scope;
2011-02-04 12:19:52 +00:00
$<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);
2011-02-15 13:27:13 +00:00
method->func = sym->s.func;
2011-02-04 12:19:52 +00:00
method->def = sym->s.func->def;
current_symtab = current_func->locals;
current_storage = sc_local;
2011-02-04 12:19:52 +00:00
}
compound_statement
{
build_code_function ($<symbol>4, $3, $7);
current_symtab = $<symtab>6;
current_storage = $5.storage;
current_func = 0;
2011-02-04 12:19:52 +00:00
}
| ci methoddecl '=' '#' const ';'
2011-02-04 10:50:24 +00:00
{
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;
2011-02-04 10:50:24 +00:00
method->def = sym->s.func->def;
}
;
2002-05-17 18:35:54 +00:00
ci
: '+' { $$ = 0; }
| '-' { $$ = 1; }
;
2002-05-08 21:24:24 +00:00
methodprotolist
2002-05-10 00:00:23 +00:00
: /* emtpy */ { $$ = 0; }
| methodprotolist2
;
2002-05-08 21:24:24 +00:00
methodprotolist2
2002-11-14 18:17:43 +00:00
: { } methodproto
2002-05-10 00:00:23 +00:00
{
$$ = new_methodlist ();
2002-11-14 18:17:43 +00:00
add_method ($$, $2);
2002-05-10 00:00:23 +00:00
}
| methodprotolist2 methodproto
{
add_method ($1, $2);
$$ = $1;
}
;
2002-05-08 21:24:24 +00:00
methodproto
: '+' methoddecl ';'
2002-05-08 21:24:24 +00:00
{
$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 ';'
2002-05-08 21:24:24 +00:00
{
$2->instance = 1;
$$ = $2;
}
;
2002-05-08 21:24:24 +00:00
methoddecl
: '(' abstract_decl ')' unaryselector
{ $$ = new_method ($2->type, $4, 0); }
| unaryselector
2002-05-08 21:24:24 +00:00
{ $$ = new_method (&type_id, $1, 0); }
| '(' abstract_decl ')' keywordselector optional_param_list
{ $$ = new_method ($2->type, $4, $5); }
| keywordselector optional_param_list
2002-05-08 21:24:24 +00:00
{ $$ = new_method (&type_id, $1, $2); }
;
optional_param_list
2002-05-10 00:00:23 +00:00
: /* empty */ { $$ = 0; }
| ',' ELLIPSIS { $$ = new_param (0, 0, 0); }
| ',' param_list { $$ = $2; }
2002-05-08 23:12:49 +00:00
| ',' param_list ',' ELLIPSIS
{
$$ = param_append_identifiers ($2, 0, 0);
2002-05-08 23:12:49 +00:00
}
;
2002-05-08 21:24:24 +00:00
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 { $$ = new_symbol (qc_yytext); }
| TYPE { $$ = new_symbol (qc_yytext); }
| TYPE_NAME { $$ = $1; }
2002-05-08 23:12:49 +00:00
| 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 ':' '(' abstract_decl ')' identifier
{ $$ = new_param ($1->name, $4->type, $6->name); }
| selector ':' identifier
{ $$ = new_param ($1->name, &type_id, $3->name); }
| ':' '(' abstract_decl ')' identifier
{ $$ = new_param ("", $3->type, $5->name); }
| ':' identifier
{ $$ = new_param ("", &type_id, $2->name); }
;
2002-05-08 23:12:49 +00:00
obj_expr
: obj_messageexpr
| SELECTOR '(' selectorarg ')' { $$ = selector_expr ($3); }
| PROTOCOL '(' identifier ')' { $$ = protocol_expr ($3->name); }
| ENCODE '(' abstract_decl ')' { $$ = encode_expr ($3->type); }
2002-05-08 23:12:49 +00:00
| obj_string /* FIXME string object? */
;
2002-05-08 23:12:49 +00:00
obj_messageexpr
: '[' receiver messageargs ']' { $$ = message_expr ($2, $3); }
;
2002-05-08 23:12:49 +00:00
receiver
: expr
| CLASS_NAME
{
$$ = new_symbol_expr ($1);
}
;
2002-05-08 23:12:49 +00:00
messageargs
: selector { $$ = new_keywordarg ($1->name, 0); }
2002-05-08 23:12:49 +00:00
| keywordarglist
;
2002-05-08 23:12:49 +00:00
keywordarglist
: keywordarg
| keywordarglist keywordarg
{
$2->next = $1;
$$ = $2;
}
;
2002-05-08 23:12:49 +00:00
keywordarg
: selector ':' arg_list { $$ = new_keywordarg ($1->name, $3); }
| ':' arg_list { $$ = new_keywordarg ("", $2); }
;
2002-05-08 23:12:49 +00:00
selectorarg
: selector { $$ = new_keywordarg ($1->name, 0); }
| keywordnamelist
;
2002-05-08 23:12:49 +00:00
keywordnamelist
: keywordname
| keywordnamelist keywordname
2002-05-08 23:12:49 +00:00
{
$2->next = $1;
$$ = $2;
}
;
2002-05-08 23:12:49 +00:00
keywordname
: selector ':' { $$ = new_keywordarg ($1->name, new_nil_expr ()); }
| ':' { $$ = new_keywordarg ("", new_nil_expr ()); }
;
2002-05-08 23:12:49 +00:00
obj_string
: '@' STRING
2002-05-08 23:12:49 +00:00
{
//FIXME string object
$$ = $2;
2002-05-08 23:12:49 +00:00
}
;
2001-06-12 19:44:26 +00:00
%%