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

1393 lines
29 KiB
Text
Raw Normal View History

2001-06-12 19:44:26 +00:00
%{
2001-09-28 07:09:38 +00:00
/*
#FILENAME#
#DESCRIPTION#
Copyright (C) 2001 #AUTHOR#
Author: #AUTHOR#
Date: #DATE#
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
*/
static const char rcsid[] =
"$Id$";
2002-06-01 04:41:25 +00:00
#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 "class.h"
2002-06-04 21:23:39 +00:00
#include "debug.h"
#include "def.h"
#include "emit.h"
2002-06-01 05:30:16 +00:00
#include "expr.h"
#include "function.h"
2002-06-04 18:44:03 +00:00
#include "immediate.h"
2002-05-08 21:24:24 +00:00
#include "method.h"
2002-06-04 18:44:03 +00:00
#include "options.h"
#include "qfcc.h"
#include "reloc.h"
#include "struct.h"
#include "switch.h"
2002-01-17 16:59:00 +00:00
#include "type.h"
#define YYDEBUG 1
#define YYERROR_VERBOSE 1
extern char *yytext;
2001-06-12 20:24:02 +00:00
void
yyerror (const char *s)
2001-06-12 20:24:02 +00:00
{
error (0, "%s %s\n", yytext, s);
2001-06-12 20:24:02 +00:00
}
2002-05-15 19:10:23 +00:00
void
parse_error (void)
{
error (0, "parse error before %s", yytext);
}
#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
hashtab_t *save_local_inits (scope_t *scope);
hashtab_t *merge_local_inits (hashtab_t *dl_1, hashtab_t *dl_2);
void restore_local_inits (hashtab_t *def_list);
void free_local_inits (hashtab_t *def_list);
2001-06-12 19:44:26 +00:00
%}
2001-06-12 20:24:02 +00:00
%union {
int op;
2002-06-04 21:23:39 +00:00
struct def_s *def;
struct hashtab_s *def_list;
type_t *type;
expr_t *expr;
int integer_val;
float float_val;
const char *string_val;
float vector_val[3];
float quaternion_val[4];
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-05-08 21:24:24 +00:00
struct protocol_s *protocol;
2002-05-08 23:12:49 +00:00
struct keywordarg_s *keywordarg;
2002-05-10 00:00:23 +00:00
struct methodlist_s *methodlist;
2001-06-12 20:24:02 +00:00
}
%right <op> '=' ASX PAS /* pointer assign */
%right '?' ':'
%left OR AND
2001-06-12 20:24:02 +00:00
%left EQ NE LE GE LT GT
%left SHL SHR %left '+' '-'
%left '*' '/' '&' '|' '^' '%'
%right <op> UNARY INCOP
%right '(' '['
%left '.'
2001-06-12 19:44:26 +00:00
2002-08-18 04:08:02 +00:00
%token <string_val> CLASS_NAME NAME STRING_VAL
Initial integer type support. qfcc /is/ partially broken when it comes to integer constants and float function args/return values. pr_comp.h: o add the integer opcodes to pr_opcode_e pr_edict.c: o add "quaternion" and "integer" to type_name[] o support quatnernion and integers types when printing values o support the integer opcodes when bounds checking pr_exec.c o enable the integer opcodes pr_opcode: o add the integer opcodes to the opcode table o logical operators all result in an integer rather than a value expr.h: o rename int_val to integer_val qfcc.h: o kill another magic number expr.c: o move the opcode to string conversion out of type_mismatch and into get_op_string o rename int_val to integer_val o general integer type support. o generate an internal comipiler error for null opcodes rather than segging. pr_imm.c: o rename int_val to integer_val o support integer constants, converting to float when needed. pr_lex.c: o magic number death and support quaternions and integers in type_size[] qc-lex.l o rename int_val to integer_val o support quaternion and integer type keywords qc-parse.y: o rename int_val to integer_val o use binary_expr instead of new_binary_expr for local initialized variables builtins.c: o rename int_val to integer_val o fix most (all?) of the INT related FIXMEs defs.qc: o use integer instead of float where it makes sense main.c: o read_result is now integer rather than float main.qc: o float -> integer where appropriate o new test for int const to float arg
2001-07-23 01:31:22 +00:00
%token <integer_val> INT_VAL
%token <float_val> FLOAT_VAL
%token <vector_val> VECTOR_VAL
%token <quaternion_val> QUATERNION_VAL
2001-06-12 19:44:26 +00:00
%token LOCAL RETURN WHILE DO IF ELSE FOR BREAK CONTINUE ELLIPSIS NIL
%token IFBE IFB IFAE IFA
2002-06-20 20:28:01 +00:00
%token SWITCH CASE DEFAULT STRUCT UNION ENUM TYPEDEF SUPER SELF THIS
%token ARGC ARGV EXTERN STATIC SYSTEM SIZEOF
%token ELE_START
%token <type> TYPE
%token CLASS DEFS ENCODE END IMPLEMENTATION INTERFACE PRIVATE PROTECTED
%token PROTOCOL PUBLIC SELECTOR
%type <type> type non_field_type type_name
%type <param> function_decl
%type <param> param param_list
%type <def> def_name
%type <def> def_item def_list
2002-01-18 08:26:37 +00:00
%type <expr> const opt_expr expr arg_list element_list element_list1 element
2002-06-13 04:40:09 +00:00
%type <expr> string_val opt_state_expr array_decl
%type <expr> statement statements statement_block
2002-01-21 19:03:29 +00:00
%type <expr> break_label continue_label enum_list enum
%type <function> begin_function
%type <def_list> save_inits
%type <switch_block> switch_block
2001-06-12 19:44:26 +00:00
2002-08-18 04:08:02 +00:00
%type <string_val> selector reserved_word maybe_class
2002-05-08 21:24:24 +00:00
%type <param> optparmlist unaryselector keyworddecl keywordselector
%type <method> methodproto methoddecl
2002-05-08 23:12:49 +00:00
%type <expr> obj_expr identifier_list obj_messageexpr obj_string receiver
2002-05-10 00:00:23 +00:00
%type <expr> protocolrefs
2002-05-08 23:12:49 +00:00
%type <keywordarg> messageargs keywordarg keywordarglist selectorarg
%type <keywordarg> keywordnamelist keywordname
2002-05-15 19:10:23 +00:00
%type <class> class_name new_class_name class_with_super new_class_with_super
%type <class> category_name new_category_name
2002-05-10 00:00:23 +00:00
%type <protocol> protocol_name
%type <methodlist> methodprotolist methodprotolist2
2002-05-15 19:10:23 +00:00
%type <type> ivar_decl_list
2002-08-18 04:08:02 +00:00
%expect 3 // statement : if | if else, class_name : maybe_class | category_name : maybe_class '(' maybe_class ')', type_name : TYPE | expr : TYPE '(' expr ')'
2001-06-12 19:44:26 +00:00
%{
type_t *current_type;
def_t *current_def;
function_t *current_func;
param_t *current_params;
2002-05-08 21:24:24 +00:00
expr_t *current_init;
class_t *current_class;
expr_t *local_expr;
expr_t *break_label;
expr_t *continue_label;
switch_block_t *switch_block;
type_t *struct_type;
2002-05-10 00:00:23 +00:00
visibility_t current_visibility;
2002-05-15 19:10:23 +00:00
type_t *current_ivars;
scope_t *current_scope;
storage_class_t current_storage;
int element_flag;
%}
2001-06-12 19:44:26 +00:00
%%
defs
: /* empty */
2002-05-15 19:10:23 +00:00
| defs {if (current_class) PARSE_ERROR;} def ';'
2002-05-08 21:24:24 +00:00
| defs obj_def
2001-06-12 19:44:26 +00:00
;
def
: type { current_storage = st_global; current_type = $1; } def_list
| storage_class type { current_type = $2; } def_list
| storage_class '{' simple_defs '}'
| STRUCT NAME
{ struct_type = new_struct ($2); } '=' '{' struct_defs '}'
2002-06-20 20:28:01 +00:00
| UNION NAME
{ struct_type = new_union ($2); } '=' '{' struct_defs '}'
2002-01-23 20:50:25 +00:00
| ENUM '{' enum_list opt_comma '}'
2002-01-21 19:03:29 +00:00
{ process_enum ($3); }
2002-01-23 20:50:25 +00:00
| TYPEDEF type NAME
{ new_typedef ($3, $2); }
| TYPEDEF ENUM '{' enum_list opt_comma '}' NAME
{
process_enum ($4);
new_typedef ($7, &type_integer);
}
;
simple_defs
: /* empty */
| simple_defs simple_def ';'
;
simple_def
: type { current_type = $1; } def_list
;
storage_class
: EXTERN { current_storage = st_extern; }
| STATIC { current_storage = st_static; }
| SYSTEM { current_storage = st_system; }
;
struct_defs
: /* empty */
| struct_defs struct_def ';'
2002-05-10 00:00:23 +00:00
| DEFS '(' NAME ')'
{
class_t *class = get_class ($3, 0);
if (class) {
copy_struct_fields (struct_type, class->ivars);
} else {
error (0, "undefined symbol `%s'", $3);
}
}
;
struct_def
: type { current_type = $1; } struct_def_list
2001-06-12 19:44:26 +00:00
;
2002-01-21 19:03:29 +00:00
enum_list
: enum
| enum_list ',' enum
{
if ($3) {
$3->next = $1;
$$ = $3;
} else {
$$ = $1;
}
}
;
enum
: NAME { $$ = new_name_expr ($1); }
| NAME '=' expr
{
$$ = 0;
if ($3->type < ex_string) {
error ($3, "non-constant initializer");
} else if ($3->type != ex_integer) {
error ($3, "invalid initializer type");
} else {
$$ = new_binary_expr ('=', new_name_expr ($1), $3);
}
}
;
type
: '.' type { $$ = field_type ($2); }
| non_field_type { $$ = $1; }
| non_field_type function_decl
{
current_params = $2;
$$ = parse_params ($1, $2);
}
| non_field_type array_decl
{
2002-06-13 04:40:09 +00:00
if ($2)
$$ = array_type ($1, $2->e.integer_val);
else
$$ = pointer_type ($1);
}
;
non_field_type
: '(' type ')' { $$ = $2; }
| type_name { $$ = $1; }
2001-11-14 07:16:58 +00:00
;
type_name
: TYPE { $$ = $1; }
2002-08-18 04:08:02 +00:00
| CLASS_NAME
{
class_t *class = get_class ($1, 0);
if (!class) {
error (0, "undefined symbol `%s'", $1);
class = get_class (0, 1);
}
$$ = class->type;
}
;
function_decl
2002-05-10 00:00:23 +00:00
: '(' param_list ')' { $$ = reverse_params ($2); }
| '(' param_list ',' ELLIPSIS ')'
{
$$ = new_param (0, 0, 0);
2002-05-10 00:00:23 +00:00
$$->next = $2;
$$ = reverse_params ($$);
}
2002-05-10 00:00:23 +00:00
| '(' ELLIPSIS ')' { $$ = new_param (0, 0, 0); }
| '(' ')'
{
$$ = 0;
}
;
param_list
: param
| param_list ',' param
{
$3->next = $1;
$$ = $3;
}
;
param
: type NAME { $$ = new_param (0, $1, $2); }
;
array_decl
: '[' const ']'
{
if ($2->type != ex_integer || $2->e.integer_val < 1) {
error (0, "invalid array size");
$$ = 0;
} else {
2002-06-13 04:40:09 +00:00
$$ = $2;
}
}
| '[' ']' { $$ = 0; }
;
struct_def_list
: struct_def_list ',' struct_def_item
| struct_def_item
;
struct_def_item
2002-05-10 00:00:23 +00:00
: NAME { new_struct_field (struct_type, current_type, $1, vis_public); }
;
def_list
: def_list ',' def_item
| def_item
;
def_item
: def_name opt_initializer
{
$$ = $1;
2002-09-12 18:30:24 +00:00
if ($$ && !$$->local
&& $$->type->type != ev_func)
def_initialized ($$);
}
;
def_name
: NAME
{
if (current_scope->type == sc_local
&& current_scope->parent->type == sc_params) {
def_t *def = get_def (0, $1, current_scope, st_none);
if (def) {
if (def->scope->type == sc_params)
warning (0, "local %s shadows param %s", $1, def->name);
}
}
$$ = get_def (current_type, $1, current_scope, current_storage);
current_def = $$;
}
2001-06-12 19:44:26 +00:00
;
opt_initializer
: /*empty*/
| { element_flag = current_type->type != ev_func; } var_initializer
{ element_flag = 0; }
;
var_initializer
: '=' expr
{
if (current_scope->type == sc_local) {
2002-05-22 17:18:48 +00:00
append_expr (local_expr,
assign_expr (new_def_expr (current_def), $2));
def_initialized (current_def);
} else {
if ($2->type >= ex_string) {
if (current_def->type->type == ev_func) {
PARSE_ERROR;
} else {
current_def = ReuseConstant ($2, current_def);
}
} else {
error ($2, "non-constant expression used for initializer");
}
}
}
| '=' ELE_START { current_init = new_block_expr (); } element_list '}'
2002-01-18 08:26:37 +00:00
{
init_elements (current_def, $4);
current_init = 0;
}
| '=' '#' const
{
2002-05-17 18:35:54 +00:00
build_builtin_function (current_def, $3);
}
| '=' opt_state_expr begin_function statement_block end_function
{
build_function ($3);
if ($2) {
$2->next = $4;
emit_function ($3, $2);
} else {
emit_function ($3, $4);
}
finish_function ($3);
}
;
opt_state_expr
: /* emtpy */
{
$$ = 0;
}
| '[' const ',' { $<def>$ = current_def; }
def_name { current_def = $<def>4; } ']'
{
if ($2->type == ex_integer)
convert_int ($2);
if ($2->type != ex_float)
error ($2, "invalid type for frame number");
if ($5->type->type != ev_func)
error ($2, "invalid type for think");
2002-05-22 17:18:48 +00:00
$$ = new_binary_expr ('s', $2, new_def_expr ($5));
}
;
element_list
: /* empty */
{
$$ = new_block_expr ();
}
| element_list1 opt_comma
{
$$ = current_init;
}
;
element_list1
: element
{
append_expr (current_init, $1);
}
| element_list1 ',' element
{
append_expr (current_init, $3);
}
;
element
: '{'
{
$$ = current_init;
current_init = new_block_expr ();
}
element_list
{
current_init = $<expr>2;
}
'}'
{
$$ = $3;
}
| expr
{
$$ = $1;
}
;
opt_comma
: /* empty */
| ','
;
begin_function
: /*empty*/
{
$$ = current_func = new_function (current_def->name);
$$->def = current_def;
$$->refs = new_reloc ($$->def->ofs, rel_def_func);
$$->code = pr.code->size;
2001-10-26 06:43:56 +00:00
if (options.code.debug) {
pr_lineno_t *lineno = new_lineno ();
$$->aux->source_line = $$->def->line;
$$->aux->line_info = lineno - pr.linenos;
$$->aux->local_defs = pr.num_locals;
lineno->fa.func = $$->aux - pr.auxfunctions;
}
build_scope ($$, current_def, current_params);
current_scope = $$->scope;
}
;
end_function
: /*empty*/
{
current_scope = current_scope->parent;
}
2001-06-12 19:44:26 +00:00
;
statement_block
2001-10-24 06:39:49 +00:00
: '{'
{
scope_t *scope = new_scope (sc_local, current_scope->space,
current_scope);
current_scope = scope;
2001-10-24 06:39:49 +00:00
}
statements '}'
{
def_t *defs = current_scope->head;
int num_defs = current_scope->num_defs;
2001-10-24 06:39:49 +00:00
flush_scope (current_scope, 1);
2001-10-24 06:39:49 +00:00
current_scope = current_scope->parent;
current_scope->num_defs += num_defs;
*current_scope->tail = defs;
while (*current_scope->tail) {
current_scope->tail = &(*current_scope->tail)->def_next;
}
2001-10-24 06:39:49 +00:00
$$ = $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
;
statement
: ';' { $$ = 0; }
| statement_block { $$ = $1; }
| RETURN expr ';'
{
2001-07-23 02:27:46 +00:00
$$ = return_expr (current_func, $2);
}
| RETURN ';'
{
2001-07-23 02:27:46 +00:00
$$ = return_expr (current_func, 0);
}
| BREAK ';'
{
$$ = 0;
if (break_label)
$$ = new_unary_expr ('g', break_label);
else
error (0, "break outside of loop or switch");
}
| CONTINUE ';'
{
$$ = 0;
if (continue_label)
$$ = new_unary_expr ('g', continue_label);
else
error (0, "continue outside of loop");
}
| CASE expr ':'
{
$$ = case_label_expr (switch_block, $2);
}
| DEFAULT ':'
{
$$ = case_label_expr (switch_block, 0);
}
| SWITCH break_label switch_block '(' expr ')'
{
switch_block->test = $5;
}
save_inits statement_block
{
restore_local_inits ($8);
free_local_inits ($8);
$$ = switch_expr (switch_block, break_label, $9);
switch_block = $3;
break_label = $2;
}
| WHILE break_label continue_label '(' expr ')' save_inits statement
{
2001-06-26 02:46:02 +00:00
expr_t *l1 = new_label_expr ();
expr_t *l2 = break_label;
expr_t *e;
restore_local_inits ($7);
free_local_inits ($7);
$$ = new_block_expr ();
e = new_binary_expr ('n', test_expr ($5, 1), l2);
e->line = $5->line;
e->file = $5->file;
append_expr ($$, e);
append_expr ($$, l1);
append_expr ($$, $8);
append_expr ($$, continue_label);
e = new_binary_expr ('i', test_expr ($5, 1), l1);
e->line = $5->line;
e->file = $5->file;
append_expr ($$, e);
append_expr ($$, l2);
break_label = $2;
continue_label = $3;
}
| DO break_label continue_label statement WHILE '(' expr ')' ';'
{
2001-06-26 02:46:02 +00:00
expr_t *l1 = new_label_expr ();
$$ = new_block_expr ();
append_expr ($$, l1);
append_expr ($$, $4);
append_expr ($$, continue_label);
append_expr ($$, new_binary_expr ('i', test_expr ($7, 1), l1));
append_expr ($$, break_label);
break_label = $2;
continue_label = $3;
}
| LOCAL type
{
current_storage = st_local;
current_type = $2;
local_expr = new_block_expr ();
}
def_list ';'
{
$$ = local_expr;
local_expr = 0;
}
| IF '(' expr ')' save_inits statement
{
2001-06-26 02:46:02 +00:00
expr_t *l1 = new_label_expr ();
expr_t *e;
$$ = new_block_expr ();
restore_local_inits ($5);
free_local_inits ($5);
e = new_binary_expr ('n', test_expr ($3, 1), l1);
e->line = $3->line;
e->file = $3->file;
append_expr ($$, e);
append_expr ($$, $6);
append_expr ($$, l1);
}
| IF '(' expr ')' save_inits statement ELSE
{
$<def_list>$ = save_local_inits (current_scope);
restore_local_inits ($5);
}
statement
{
expr_t *l1 = new_label_expr ();
expr_t *l2 = new_label_expr ();
expr_t *e;
hashtab_t *merged;
hashtab_t *else_ini;
$$ = new_block_expr ();
else_ini = save_local_inits (current_scope);
restore_local_inits ($5);
free_local_inits ($5);
e = new_binary_expr ('n', test_expr ($3, 1), l1);
e->line = $3->line;
e->file = $3->file;
append_expr ($$, e);
append_expr ($$, $6);
e = new_unary_expr ('g', l2);
append_expr ($$, e);
append_expr ($$, l1);
append_expr ($$, $9);
append_expr ($$, l2);
merged = merge_local_inits ($<def_list>8, else_ini);
restore_local_inits (merged);
free_local_inits (merged);
free_local_inits (else_ini);
free_local_inits ($<def_list>8);
}
| FOR break_label continue_label
'(' opt_expr ';' opt_expr ';' opt_expr ')' save_inits statement
{
2001-06-26 02:46:02 +00:00
expr_t *l1 = new_label_expr ();
expr_t *l2 = break_label;
restore_local_inits ($11);
free_local_inits ($11);
$$ = new_block_expr ();
append_expr ($$, $5);
if ($7)
append_expr ($$, new_binary_expr ('n', test_expr ($7, 1), l2));
append_expr ($$, l1);
append_expr ($$, $12);
append_expr ($$, continue_label);
append_expr ($$, $9);
if ($5)
append_expr ($$, new_binary_expr ('i', test_expr ($7, 1), l1));
append_expr ($$, l2);
break_label = $2;
continue_label = $3;
}
| expr ';'
{
$$ = $1;
}
2001-06-12 19:44:26 +00:00
;
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 ();
}
;
save_inits
: /* empty */
{
$$ = save_local_inits (current_scope);
}
;
opt_expr
: expr
| /* empty */
{
$$ = 0;
}
;
2001-06-12 19:44:26 +00:00
expr
: expr '=' expr { $$ = assign_expr ($1, $3); }
| expr ASX expr { $$ = asx_expr ($2, $1, $3); }
| expr '?' expr ':' expr { $$ = conditional_expr ($1, $3, $5); }
| expr AND expr { $$ = binary_expr (AND, $1, $3); }
| expr OR expr { $$ = binary_expr (OR, $1, $3); }
| 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 '(' arg_list ')' { $$ = function_expr ($1, $3); }
| expr '(' ')' { $$ = function_expr ($1, 0); }
| TYPE '(' expr ')' { $$ = cast_expr ($1, $3); }
| expr '[' expr ']' { $$ = array_expr ($1, $3); }
| expr '.' expr { $$ = binary_expr ('.', $1, $3); }
| '+' expr %prec UNARY { $$ = $2; }
| '-' expr %prec UNARY { $$ = unary_expr ('-', $2); }
| '!' expr %prec UNARY { $$ = unary_expr ('!', $2); }
| '~' expr %prec UNARY { $$ = unary_expr ('~', $2); }
| '&' expr %prec UNARY { $$ = address_expr ($2, 0, 0); }
| INCOP expr { $$ = incop_expr ($1, $2, 0); }
| expr INCOP { $$ = incop_expr ($2, $1, 1); }
| obj_expr { $$ = $1; }
2002-01-21 19:03:29 +00:00
| NAME { $$ = new_name_expr ($1); }
| ARGC { $$ = new_name_expr (".argc"); }
| ARGV { $$ = new_name_expr (".argv"); }
| SELF { $$ = new_self_expr (); }
| THIS { $$ = new_this_expr (); }
2002-08-18 04:08:02 +00:00
| SIZEOF '(' expr ')' { $$ = sizeof_expr ($3, 0); }
| SIZEOF '(' type ')' { $$ = sizeof_expr (0, $3); }
| const { $$ = $1; }
| '(' expr ')' { $$ = $2; $$->paren = 1; }
2001-06-12 19:44:26 +00:00
;
arg_list
: expr
2001-06-12 19:44:26 +00:00
| arg_list ',' expr
{
$3->next = $1;
$$ = $3;
}
2001-06-12 19:44:26 +00:00
;
const
: FLOAT_VAL { $$ = new_float_expr ($1); }
| string_val { $$ = $1; }
| VECTOR_VAL { $$ = new_vector_expr ($1); }
| QUATERNION_VAL { $$ = new_quaternion_expr ($1); }
| INT_VAL { $$ = new_integer_expr ($1); }
| NIL { $$ = new_nil_expr (); }
;
string_val
: STRING_VAL { $$ = new_string_expr ($1); }
| string_val STRING_VAL
{
$$ = binary_expr ('+', $1, new_string_expr ($2));
}
;
2002-05-08 21:24:24 +00:00
obj_def
: classdef
| classdecl
| 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
: NAME
{
$$ = new_block_expr ();
append_expr ($$, new_name_expr ($1));
}
| identifier_list ',' NAME
2002-05-08 21:24:24 +00:00
{
append_expr ($1, new_name_expr ($3));
$$ = $1;
}
;
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)
2002-05-10 00:00:23 +00:00
get_class (e->e.string_val, 1);
2002-05-08 21:24:24 +00:00
}
;
2002-08-18 04:08:02 +00:00
maybe_class
2002-05-10 00:00:23 +00:00
: NAME
2002-08-18 04:08:02 +00:00
| CLASS_NAME
;
class_name
: maybe_class
2002-05-08 21:24:24 +00:00
{
2002-05-10 00:00:23 +00:00
$$ = get_class ($1, 0);
if (!$$) {
error (0, "undefined symbol `%s'", $1);
$$ = get_class (0, 1);
}
2002-05-08 21:24:24 +00:00
}
2002-05-10 00:00:23 +00:00
;
new_class_name
2002-08-18 04:08:02 +00:00
: maybe_class
2002-05-08 21:24:24 +00:00
{
2002-05-10 00:00:23 +00:00
$$ = get_class ($1, 1);
if ($$->defined) {
error (0, "redefinition of `%s'", $1);
$$ = get_class (0, 1);
}
current_class = $$;
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->class_name, $1->class_name);
$$ = $1;
}
;
new_class_with_super
: new_class_name ':' class_name
{
$1->super_class = $3;
$$ = $1;
current_class = $$;
2002-05-15 19:10:23 +00:00
}
;
category_name
2002-08-18 04:08:02 +00:00
: maybe_class '(' maybe_class ')'
2002-05-15 19:10:23 +00:00
{
$$ = get_category ($1, $3, 0);
if (!$$) {
error (0, "undefined category `%s (%s)'", $1, $3);
$$ = get_category (0, 0, 1);
}
}
;
new_category_name
2002-08-18 04:08:02 +00:00
: maybe_class '(' maybe_class ')'
2002-05-15 19:10:23 +00:00
{
$$ = get_category ($1, $3, 1);
if ($$->defined) {
error (0, "redefinition of category `%s (%s)'", $1, $3);
$$ = get_category (0, 0, 1);
}
current_class = $$;
2002-05-15 19:10:23 +00:00
}
;
2002-05-10 00:00:23 +00:00
protocol_name
2002-08-18 04:08:02 +00:00
: maybe_class
2002-05-08 21:24:24 +00:00
{
2002-05-15 19:10:23 +00:00
$$ = get_protocol ($1, 0);
if ($$) {
2002-05-10 00:00:23 +00:00
error (0, "redefinition of %s", $1);
$$ = get_protocol (0, 1);
2002-05-15 19:10:23 +00:00
} else {
$$ = get_protocol ($1, 1);
2002-05-10 00:00:23 +00:00
}
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
protocolrefs { class_add_protocol_methods ($2, $3);}
'{' ivar_decl_list '}' { class_add_ivars ($2, $6); }
methodprotolist { class_add_methods ($2, $9); }
2002-05-16 22:33:11 +00:00
END { current_class = 0; }
| INTERFACE new_class_name
protocolrefs { class_add_protocol_methods ($2, $3);}
methodprotolist { class_add_methods ($2, $5); }
2002-05-16 22:33:11 +00:00
END { current_class = 0; }
| INTERFACE new_class_with_super
protocolrefs { class_add_protocol_methods ($2, $3);}
'{' ivar_decl_list '}' { class_add_ivars ($2, $6); }
methodprotolist { class_add_methods ($2, $9); }
2002-05-16 22:33:11 +00:00
END { current_class = 0; }
| INTERFACE new_class_with_super
protocolrefs { class_add_protocol_methods ($2, $3);}
methodprotolist { class_add_methods ($2, $5); }
2002-05-16 22:33:11 +00:00
END { current_class = 0; }
| INTERFACE new_category_name
protocolrefs { class_add_protocol_methods ($2, $3);}
methodprotolist { class_add_methods ($2, $5); }
2002-05-16 22:33:11 +00:00
END { current_class = 0; }
| IMPLEMENTATION class_name { class_begin ($2); }
'{' ivar_decl_list '}' { class_check_ivars ($2, $5); }
| IMPLEMENTATION class_name { class_begin ($2); }
| IMPLEMENTATION class_with_super { class_begin ($2); }
'{' ivar_decl_list '}' { class_check_ivars ($2, $5); }
| IMPLEMENTATION class_with_super { class_begin ($2); }
| IMPLEMENTATION category_name { class_begin ($2); }
;
2002-05-10 00:00:23 +00:00
protocoldef
: PROTOCOL protocol_name
protocolrefs { protocol_add_protocol_methods ($2, $3); }
methodprotolist { protocol_add_methods ($2, $5); }
END
;
2002-05-10 00:00:23 +00:00
protocolrefs
: /* emtpy */ { $$ = 0; }
| LT identifier_list GT { $$ = $2->e.block.head; }
;
2002-05-10 00:00:23 +00:00
ivar_decl_list
: /* */
{
2002-05-17 17:58:57 +00:00
current_visibility = vis_protected;
current_ivars = new_struct (0);
if (current_class->super_class)
new_struct_field (current_ivars,
current_class->super_class->ivars, 0,
vis_private);
}
ivar_decl_list_2
2002-05-15 19:10:23 +00:00
{
$$ = current_ivars;
current_ivars = 0;
}
;
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
2002-05-15 19:10:23 +00:00
: type { current_type = $1; } ivars
;
2002-05-10 00:00:23 +00:00
ivars
: ivar_declarator
| ivars ',' ivar_declarator
;
2002-05-10 00:00:23 +00:00
ivar_declarator
: NAME
{
2002-05-15 19:10:23 +00:00
new_struct_field (current_ivars, current_type,
2002-05-10 00:00:23 +00:00
$1, current_visibility);
}
;
2002-05-08 21:24:24 +00:00
methoddef
: '+' methoddecl
2002-05-08 21:24:24 +00:00
{
$2->instance = 0;
$2 = class_find_method (current_class, $2);
}
opt_state_expr
{
2002-05-15 19:10:23 +00:00
current_def = $2->def = method_def (current_class, $2);
2002-05-08 21:24:24 +00:00
current_params = $2->params;
}
begin_function statement_block end_function
2002-05-08 21:24:24 +00:00
{
$2->func = $6;
build_function ($6);
if ($4) {
$4->next = $7;
emit_function ($6, $4);
2002-05-08 21:24:24 +00:00
} else {
emit_function ($6, $7);
2002-05-08 21:24:24 +00:00
}
finish_function ($6);
2002-05-08 21:24:24 +00:00
}
2002-05-17 18:35:54 +00:00
| '+' methoddecl '=' '#' const ';'
{
$2->instance = 0;
$2 = class_find_method (current_class, $2);
$2->def = method_def (current_class, $2);
$2->func = build_builtin_function ($2->def, $5);
2002-05-17 18:35:54 +00:00
}
| '-' methoddecl
2002-05-08 21:24:24 +00:00
{
$2->instance = 1;
$2 = class_find_method (current_class, $2);
}
opt_state_expr
{
2002-05-15 19:10:23 +00:00
current_def = $2->def = method_def (current_class, $2);
2002-05-08 21:24:24 +00:00
current_params = $2->params;
}
begin_function statement_block end_function
2002-05-08 21:24:24 +00:00
{
$2->func = $6;
build_function ($6);
if ($4) {
$4->next = $7;
emit_function ($6, $4);
2002-05-08 21:24:24 +00:00
} else {
emit_function ($6, $7);
2002-05-08 21:24:24 +00:00
}
finish_function ($6);
2002-05-08 21:24:24 +00:00
}
2002-05-17 18:35:54 +00:00
| '-' methoddecl '=' '#' const ';'
{
2002-05-21 22:51:46 +00:00
$2->instance = 1;
2002-05-17 18:35:54 +00:00
$2 = class_find_method (current_class, $2);
$2->def = method_def (current_class, $2);
$2->func = build_builtin_function ($2->def, $5);
2002-05-17 18:35:54 +00:00
}
;
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-05-10 00:00:23 +00:00
: methodproto
{
$$ = new_methodlist ();
add_method ($$, $1);
}
| 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->params->type = &type_Class;
2002-05-08 21:24:24 +00:00
$$ = $2;
}
| '-' methoddecl ';'
2002-05-08 21:24:24 +00:00
{
$2->instance = 1;
if (current_class)
$2->params->type = current_class->type;
2002-05-08 21:24:24 +00:00
$$ = $2;
}
;
2002-05-08 21:24:24 +00:00
methoddecl
: '(' type ')' unaryselector
2002-05-08 21:24:24 +00:00
{ $$ = new_method ($2, $4, 0); }
| unaryselector
2002-05-08 21:24:24 +00:00
{ $$ = new_method (&type_id, $1, 0); }
| '(' type ')' keywordselector optparmlist
{ $$ = new_method ($2, $4, $5); }
| keywordselector optparmlist
{ $$ = new_method (&type_id, $1, $2); }
;
2002-05-08 21:24:24 +00:00
optparmlist
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
{
$$ = new_param (0, 0, 0);
$$->next = $2;
}
;
2002-05-08 21:24:24 +00:00
unaryselector
2002-05-10 00:00:23 +00:00
: selector { $$ = new_param ($1, 0, 0); }
;
keywordselector
: keyworddecl
| keywordselector keyworddecl { $2->next = $1; $$ = $2; }
;
selector
2002-08-18 04:08:02 +00:00
: maybe_class
| TYPE { $$ = save_string (yytext); }
2002-05-08 23:12:49 +00:00
| reserved_word
;
reserved_word
: LOCAL { $$ = save_string (yytext); }
| RETURN { $$ = save_string (yytext); }
| WHILE { $$ = save_string (yytext); }
| DO { $$ = save_string (yytext); }
| IF { $$ = save_string (yytext); }
| ELSE { $$ = save_string (yytext); }
| FOR { $$ = save_string (yytext); }
| BREAK { $$ = save_string (yytext); }
| CONTINUE { $$ = save_string (yytext); }
| SWITCH { $$ = save_string (yytext); }
| CASE { $$ = save_string (yytext); }
| DEFAULT { $$ = save_string (yytext); }
| NIL { $$ = save_string (yytext); }
| STRUCT { $$ = save_string (yytext); }
| UNION { $$ = save_string (yytext); }
| ENUM { $$ = save_string (yytext); }
| TYPEDEF { $$ = save_string (yytext); }
| SUPER { $$ = save_string (yytext); }
;
keyworddecl
: selector ':' '(' type ')' NAME
{ $$ = new_param ($1, $4, $6); }
| selector ':' NAME
2002-05-08 21:24:24 +00:00
{ $$ = new_param ($1, &type_id, $3); }
| ':' '(' type ')' NAME
2002-05-08 21:24:24 +00:00
{ $$ = new_param ("", $3, $5); }
| ':' NAME
2002-05-08 21:24:24 +00:00
{ $$ = new_param ("", &type_id, $2); }
;
2002-05-08 23:12:49 +00:00
obj_expr
: obj_messageexpr
| SELECTOR '(' selectorarg ')' { $$ = selector_expr ($3); }
2002-08-18 04:08:02 +00:00
| PROTOCOL '(' maybe_class ')' { $$ = protocol_expr ($3); }
2002-05-08 23:12:49 +00:00
| ENCODE '(' type ')' { $$ = encode_expr ($3); }
| 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_name_expr ($1); }
| SUPER { $$ = new_name_expr ("super"); }
;
2002-05-08 23:12:49 +00:00
messageargs
: selector { $$ = new_keywordarg ($1, 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, $3); }
| ':' arg_list { $$ = new_keywordarg ("", $2); }
;
2002-05-08 23:12:49 +00:00
selectorarg
: selector { $$ = new_keywordarg ($1, 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, 0); }
| ':' { $$ = new_keywordarg ("", 0); }
;
2002-05-08 23:12:49 +00:00
obj_string
: '@' STRING_VAL { $$ = new_string_expr ($2); }
| obj_string '@' STRING_VAL
2002-05-08 23:12:49 +00:00
{
$$ = binary_expr ('+', $1, new_string_expr ($3));
2002-05-08 23:12:49 +00:00
}
;
2001-06-12 19:44:26 +00:00
%%
2002-06-05 21:13:29 +00:00
typedef struct def_state_s {
struct def_state_s *next;
def_t *def;
int state;
} def_state_t;
2002-06-05 21:13:29 +00:00
static def_state_t *free_def_states;
static const char *
get_key (void *_d, void *unused)
{
return ((def_state_t *)_d)->def->name;
}
static void
free_key (void *_d, void *unused)
{
2002-06-05 21:13:29 +00:00
def_state_t *d = (def_state_t *)_d;
d->next = free_def_states;
free_def_states = d;
}
static void
scan_scope (hashtab_t *tab, scope_t *scope)
{
def_t *def;
if (scope->type == sc_local)
scan_scope (tab, scope->parent);
for (def = scope->head; def; def = def->def_next) {
if (def->name && !def->removed) {
2002-06-05 21:13:29 +00:00
def_state_t *ds;
ALLOC (1024, def_state_t, def_states, ds);
ds->def = def;
ds->state = def->initialized;
Hash_Add (tab, ds);
}
}
}
hashtab_t *
save_local_inits (scope_t *scope)
{
hashtab_t *tab = Hash_NewTable (61, get_key, free_key, 0);
scan_scope (tab, scope);
return tab;
}
hashtab_t *
merge_local_inits (hashtab_t *dl_1, hashtab_t *dl_2)
{
hashtab_t *tab = Hash_NewTable (61, get_key, free_key, 0);
def_state_t **ds_list = (def_state_t **)Hash_GetList (dl_1);
def_state_t **ds;
def_state_t *d;
def_state_t *nds;
for (ds = ds_list; *ds; ds++) {
d = Hash_Find (dl_2, (*ds)->def->name);
(*ds)->def->initialized = (*ds)->state;
2002-06-05 21:13:29 +00:00
ALLOC (1024, def_state_t, def_states, nds);
nds->def = (*ds)->def;
nds->state = (*ds)->state && d->state;
Hash_Add (tab, nds);
}
free (ds_list);
return tab;
}
void
restore_local_inits (hashtab_t *def_list)
{
def_state_t **ds_list = (def_state_t **)Hash_GetList (def_list);
def_state_t **ds;
for (ds = ds_list; *ds; ds++)
(*ds)->def->initialized = (*ds)->state;
free (ds_list);
}
void
free_local_inits (hashtab_t *def_list)
{
Hash_DelTable (def_list);
}