mirror of
https://git.code.sf.net/p/quake/quakeforge
synced 2025-01-18 06:51:47 +00:00
switch statement. could do with lots of improvements, but this should be a
good start.
This commit is contained in:
parent
09405469f6
commit
f029687ff0
7 changed files with 191 additions and 7 deletions
|
@ -15,7 +15,7 @@ X pre- and post- increment operators (++ and --)
|
|||
X break/continue keywords for switch() and for(;;)
|
||||
X full scoping
|
||||
I gut out old parser
|
||||
o switch/case, for any type
|
||||
X switch/case, for any type
|
||||
o short circuit logic for && and || (optional?)
|
||||
o warn for local shaddowing parameter
|
||||
o quaternion type. Tricky: requires 4 word args/return
|
||||
|
|
16
tools/qfcc/include/switch.h
Normal file
16
tools/qfcc/include/switch.h
Normal file
|
@ -0,0 +1,16 @@
|
|||
typedef struct switch_block_s {
|
||||
expr_t *test;
|
||||
struct hashtab_s *labels;
|
||||
} switch_block_t;
|
||||
|
||||
typedef struct case_label_s {
|
||||
struct expr_s *label;
|
||||
struct expr_s *value;
|
||||
} case_label_t;
|
||||
|
||||
struct expr_s *case_label_expr (switch_block_t *switch_block,
|
||||
struct expr_s *value);
|
||||
switch_block_t *new_switch_block (void);
|
||||
struct expr_s *switch_expr (switch_block_t *switch_block,
|
||||
struct expr_s *break_label,
|
||||
struct expr_s *statements);
|
|
@ -33,5 +33,5 @@ YFLAGS = -d
|
|||
|
||||
bin_PROGRAMS= qfcc
|
||||
|
||||
qfcc_SOURCES= cmdlib.c debug.c pr_comp.c pr_def.c pr_imm.c pr_lex.c pr_opcode.c qfcc.c qc-parse.y qc-lex.l emit.c expr.c
|
||||
qfcc_SOURCES= cmdlib.c debug.c pr_comp.c pr_def.c pr_imm.c pr_lex.c pr_opcode.c qfcc.c qc-parse.y qc-lex.l emit.c expr.c switch.c
|
||||
qfcc_LDADD= -lQFgamecode -lQFutil
|
||||
|
|
|
@ -1275,8 +1275,8 @@ expr_t *
|
|||
conditional_expr (expr_t *cond, expr_t *e1, expr_t *e2)
|
||||
{
|
||||
expr_t *block = new_block_expr ();
|
||||
type_t *type1 = types[extract_type (e1)];
|
||||
type_t *type2 = types[extract_type (e2)];
|
||||
type_t *type1 = get_type (e1);
|
||||
type_t *type2 = get_type (e2);
|
||||
expr_t *tlabel = new_label_expr ();
|
||||
expr_t *elabel = new_label_expr ();
|
||||
|
||||
|
|
|
@ -223,6 +223,9 @@ static keyword_t keywords[] = {
|
|||
{"for", FOR, 0, PROG_ID_VERSION},
|
||||
{"break", BREAK, 0, PROG_ID_VERSION},
|
||||
{"continue", CONTINUE, 0, PROG_ID_VERSION},
|
||||
{"switch", SWITCH, 0, PROG_ID_VERSION},
|
||||
{"case", CASE, 0, PROG_ID_VERSION},
|
||||
{"default", DEFAULT, 0, PROG_ID_VERSION},
|
||||
{"NIL", NIL, 0, PROG_ID_VERSION},
|
||||
};
|
||||
|
||||
|
|
|
@ -35,6 +35,8 @@ static const char rcsid[] =
|
|||
#include <QF/hash.h>
|
||||
#include <QF/sys.h>
|
||||
|
||||
#include "switch.h"
|
||||
|
||||
#define YYDEBUG 1
|
||||
#define YYERROR_VERBOSE 1
|
||||
|
||||
|
@ -84,6 +86,7 @@ typedef struct {
|
|||
float vector_val[3];
|
||||
float quaternion_val[4];
|
||||
function_t *function;
|
||||
struct switch_block_s *switch_block;
|
||||
}
|
||||
|
||||
%right <op> '=' ASX
|
||||
|
@ -105,6 +108,7 @@ typedef struct {
|
|||
%token <quaternion_val> QUATERNION_VAL
|
||||
|
||||
%token LOCAL RETURN WHILE DO IF ELSE FOR BREAK CONTINUE ELIPSIS NIL
|
||||
%token SWITCH CASE DEFAULT
|
||||
%token <type> TYPE
|
||||
|
||||
%type <type> type maybe_func
|
||||
|
@ -114,6 +118,7 @@ typedef struct {
|
|||
%type <expr> break_label continue_label
|
||||
%type <function> begin_function
|
||||
%type <def_list> save_inits
|
||||
%type <switch_block> switch_block
|
||||
|
||||
%expect 1
|
||||
|
||||
|
@ -126,6 +131,7 @@ function_t *current_func;
|
|||
expr_t *local_expr;
|
||||
expr_t *break_label;
|
||||
expr_t *continue_label;
|
||||
switch_block_t *switch_block;
|
||||
|
||||
def_t *pr_scope; // the function being parsed, or NULL
|
||||
string_t s_file; // filename for function definition
|
||||
|
@ -410,17 +416,39 @@ statement
|
|||
}
|
||||
| BREAK ';'
|
||||
{
|
||||
$$ = 0;
|
||||
if (break_label)
|
||||
$$ = new_unary_expr ('g', break_label);
|
||||
else
|
||||
$$ = error (0, "break outside of loop or switch");
|
||||
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");
|
||||
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
|
||||
{
|
||||
|
@ -528,7 +556,8 @@ statement
|
|||
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
|
||||
| FOR break_label continue_label
|
||||
'(' opt_expr ';' opt_expr ';' opt_expr ')' save_inits statement
|
||||
{
|
||||
expr_t *l1 = new_label_expr ();
|
||||
expr_t *l2 = break_label;
|
||||
|
@ -571,6 +600,13 @@ continue_label
|
|||
continue_label = new_label_expr ();
|
||||
}
|
||||
|
||||
switch_block
|
||||
: /* empty */
|
||||
{
|
||||
$$ = switch_block;
|
||||
switch_block = new_switch_block ();
|
||||
}
|
||||
|
||||
save_inits
|
||||
: /* empty */
|
||||
{
|
||||
|
|
129
tools/qfcc/source/switch.c
Normal file
129
tools/qfcc/source/switch.c
Normal file
|
@ -0,0 +1,129 @@
|
|||
/*
|
||||
switch.c
|
||||
|
||||
qc switch statement support
|
||||
|
||||
Copyright (C) 2001 Bill Currie <bill@taniwha.org>
|
||||
|
||||
Author: Bill Currie <bill@taniwha.org>
|
||||
Date: 2001/10/24
|
||||
|
||||
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$";
|
||||
|
||||
#include <QF/hash.h>
|
||||
#include <QF/sys.h>
|
||||
|
||||
#include "qfcc.h"
|
||||
#include "switch.h"
|
||||
#include "scope.h"
|
||||
#include "qc-parse.h"
|
||||
|
||||
static unsigned long
|
||||
get_hash (void *_cl, void *unused)
|
||||
{
|
||||
case_label_t *cl = (case_label_t*)_cl;
|
||||
|
||||
return (unsigned long)cl->value;
|
||||
}
|
||||
|
||||
static int
|
||||
compare (void *_cla, void *_clb, void *unused)
|
||||
{
|
||||
case_label_t *cla = (case_label_t*)_cla;
|
||||
case_label_t *clb = (case_label_t*)_clb;
|
||||
|
||||
return cla->value == clb->value;
|
||||
}
|
||||
|
||||
struct expr_s *
|
||||
case_label_expr (switch_block_t *switch_block, expr_t *value)
|
||||
{
|
||||
case_label_t *cl = malloc (sizeof (case_label_t));
|
||||
|
||||
if (!cl)
|
||||
Sys_Error ("case_label_expr: Memory Allocation Failure\n");
|
||||
|
||||
if (value && value->type < ex_string) {
|
||||
error (value, "non-constant case value");
|
||||
free (cl);
|
||||
return 0;
|
||||
}
|
||||
if (!switch_block) {
|
||||
error (value, "%s outside of switch", value ? "case" : "default");
|
||||
free (cl);
|
||||
return 0;
|
||||
}
|
||||
cl->value = value;
|
||||
if (Hash_FindElement (switch_block->labels, cl)) {
|
||||
error (value, "duplicate %s", value ? "case" : "default");
|
||||
free (cl);
|
||||
}
|
||||
cl->label = new_label_expr ();
|
||||
Hash_AddElement (switch_block->labels, cl);
|
||||
return cl->label;
|
||||
}
|
||||
|
||||
switch_block_t *
|
||||
new_switch_block (void)
|
||||
{
|
||||
switch_block_t *switch_block = malloc (sizeof (switch_block_t));
|
||||
if (!switch_block)
|
||||
Sys_Error ("new_switch_block: Memory Allocation Failure\n");
|
||||
switch_block->labels = Hash_NewTable (127, 0, 0, 0);
|
||||
Hash_SetHashCompare (switch_block->labels, get_hash, compare);
|
||||
switch_block->test = 0;
|
||||
return switch_block;
|
||||
}
|
||||
|
||||
struct expr_s *
|
||||
switch_expr (switch_block_t *switch_block, expr_t *break_label,
|
||||
expr_t *statements)
|
||||
{
|
||||
case_label_t **labels, **l;
|
||||
case_label_t _default_label;
|
||||
case_label_t *default_label = &_default_label;
|
||||
expr_t *sw = new_block_expr ();
|
||||
type_t *type = get_type (switch_block->test);
|
||||
expr_t *temp = new_temp_def_expr (type);
|
||||
|
||||
default_label->value = 0;
|
||||
default_label = Hash_FindElement (switch_block->labels, default_label);
|
||||
labels = (case_label_t **)Hash_GetList (switch_block->labels);
|
||||
|
||||
append_expr (sw, binary_expr ('=', temp, switch_block->test));
|
||||
for (l = labels; *l; l++) {
|
||||
if ((*l)->value) {
|
||||
expr_t *cmp = binary_expr (EQ, temp, (*l)->value);
|
||||
append_expr (sw,
|
||||
new_binary_expr ('i',
|
||||
test_expr (cmp, 1), (*l)->label));
|
||||
}
|
||||
}
|
||||
if (default_label)
|
||||
append_expr (sw, new_unary_expr ('g', default_label->label));
|
||||
else
|
||||
append_expr (sw, new_unary_expr ('g', break_label));
|
||||
append_expr (sw, statements);
|
||||
append_expr (sw, break_label);
|
||||
return sw;
|
||||
}
|
Loading…
Reference in a new issue