switch statement. could do with lots of improvements, but this should be a

good start.
This commit is contained in:
Bill Currie 2001-10-25 06:41:52 +00:00
parent 09405469f6
commit f029687ff0
7 changed files with 191 additions and 7 deletions

View file

@ -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

View 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);

View file

@ -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

View file

@ -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 ();

View file

@ -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},
};

View file

@ -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
View 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;
}