Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 44 additions & 0 deletions cobj/codegen.c
Original file line number Diff line number Diff line change
Expand Up @@ -3549,6 +3549,47 @@ static void joutput_ferror_stmt(struct cb_statement *p, int code) {
// joutput_line("// %s:%d: %s", p->file, p->line, p->comment);
// }

static void joutput_switch(struct cb_switch *sw,
enum joutput_stmt_type output_type) {
joutput_prefix();
joutput("switch(");
joutput_param(sw->test, -1);
joutput(".getInt()) {");
joutput_newline();
joutput_indent_level += 2;
cb_tree case_tree;
for (case_tree = sw->case_list; case_tree; case_tree = CB_CHAIN(case_tree)) {
cb_tree whens = CB_VALUE(case_tree);
cb_tree stmt = CB_VALUE(whens);
int flag_other_exists = 1;
for (; whens; whens = CB_CHAIN(whens)) {
cb_tree objs = CB_VALUE(whens);
for (; objs; objs = CB_CHAIN(objs)) {
cb_tree obj = CB_VALUE(objs);
if (obj && CB_LIST_P(obj)) {
flag_other_exists = 0;
}
if (obj && CB_PAIR_P(obj)) {
cb_tree when_target = CB_PAIR_Y(obj);
struct cb_literal *primary_target =
CB_LITERAL(CB_PAIR_X(when_target));
int dummy;
int label =
cb_literal_to_int_for_switch_label(primary_target, &dummy);
joutput_line("case %d:", label);
}
}
}
if (flag_other_exists) {
joutput_line("default:");
}
joutput_stmt(stmt, output_type);
joutput_line("break;");
}
joutput_indent_level -= 2;
joutput_line("}");
}

static void joutput_stmt(cb_tree x, enum joutput_stmt_type output_type) {
struct cb_statement *p;
struct cb_label *lp;
Expand Down Expand Up @@ -3948,6 +3989,9 @@ static void joutput_stmt(cb_tree x, enum joutput_stmt_type output_type) {
joutput_prefix();
joutput(";\n");
break;
case CB_TAG_SWITCH:
joutput_switch(CB_SWITCH(x), output_type);
break;
case CB_TAG_LIST:
if (x && CB_TREE_TAG(CB_VALUE(x)) == CB_TAG_PERFORM) {
putParen = 0;
Expand Down
13 changes: 13 additions & 0 deletions cobj/tree.c
Original file line number Diff line number Diff line change
Expand Up @@ -2511,6 +2511,19 @@ cb_tree cb_build_continue(void) {
return CB_TREE(p);
}

/*
* SWITCH
*/

cb_tree cb_build_switch(cb_tree test, cb_tree case_list) {
struct cb_switch *p;

p = make_tree(CB_TAG_SWITCH, CB_CATEGORY_UNKNOWN, sizeof(struct cb_switch));
p->test = test;
p->case_list = case_list;
return CB_TREE(p);
}

/*
* FUNCTION
*/
Expand Down
20 changes: 20 additions & 0 deletions cobj/tree.h
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,8 @@ enum cb_tag {

CB_TAG_JAVA_CONTINUE,
CB_TAG_JAVA_BREAK,

CB_TAG_SWITCH,
};

enum cb_alphabet_name_type {
Expand Down Expand Up @@ -1118,6 +1120,24 @@ extern cb_tree cb_build_perform(int type);
extern cb_tree cb_build_perform_varying(cb_tree name, cb_tree from,
cb_tree step, cb_tree until);

/*
* switch (evaluate)
*/

struct cb_switch {
struct cb_tree_common common;
cb_tree test;
cb_tree case_list;
};

#define CB_SWITCH(x) (CB_TREE_CAST(CB_TAG_SWITCH, struct cb_switch, x))
#define CB_SWITCH_P(x) (CB_TREE_TAG(x) == CB_TAG_SWITCH)

extern cb_tree cb_build_switch(cb_tree test, cb_tree case_tree);

extern int cb_literal_to_int_for_switch_label(struct cb_literal *lit,
int *result);

/*
* SORT
*/
Expand Down
148 changes: 147 additions & 1 deletion cobj/typeck.c
Original file line number Diff line number Diff line change
Expand Up @@ -3673,8 +3673,154 @@ static cb_tree build_evaluate(cb_tree subject_list, cb_tree case_list) {
}
}

int cb_literal_to_int_for_switch_label(struct cb_literal *lit, int *result) {
const char *min_num_abs =
"2147483648"; // -1 * (the minimum value of int of Java)
const char *max_num = "2147483647"; // the maximum value of int of Java
const int len_boundary =
10; // string length of min_num_abs (== string length of max_num)
if (lit->size > len_boundary) {
// the value of the literal is out of range
*result = 0;
return 0;
}

size_t i;
const char *boundary = lit->sign < 0 ? min_num_abs : max_num;
if (lit->size == len_boundary) {
for (i = 0; i < len_boundary; ++i) {
if (boundary[i] < lit->data[i]) {
// the value of the literal is out of range
*result = 0;
return 0;
}
}
}

int ret = 0;
for (i = 0; i < lit->size; ++i) {
ret = ret * 10 + lit->data[i] - '0';
}
*result = 1;
return lit->sign < 0 ? -ret : ret;
}

// Determine a given label exists in the list of existing labels
// If a given label exists in the list, return 1. Otherwise return 0;
static int is_switch_label_duplicate(int label, const int *existing_label_list,
int list_len) {
int i = 0;
for (; i < list_len; ++i) {
if (existing_label_list[i] == label) {
return 1;
}
}
return 0;
}

static cb_tree convert_evaluate_stmt_to_switch_stmt(cb_tree subject_list,
cb_tree case_list) {
cb_tree subjs;
cb_tree whens;
cb_tree objs;

cb_tree case_tree = case_list;
int when_count = 0;
int other_exists = 0;

// The length of subject_list must be 1
if (subject_list == NULL || CB_CHAIN(subject_list)) {
return NULL;
}

// Determine if a given evaluate statement can be converted to a switch
// statement
int case_count = 0;
for (; case_tree; case_tree = CB_CHAIN(case_tree), ++case_count) {
whens = CB_VALUE(case_tree);
/* for each WHEN sequence */
int flag_other_exists = 1;
for (; whens; whens = CB_CHAIN(whens)) {
for (subjs = subject_list, objs = CB_VALUE(whens); subjs && objs;
subjs = CB_CHAIN(subjs), objs = CB_CHAIN(objs)) {
cb_tree subj = CB_VALUE(subjs);
cb_tree obj = CB_VALUE(objs);
if (!subj || CB_TREE_CATEGORY(subj) != CB_CATEGORY_NUMERIC) {
return NULL;
}
if (obj) {
if (CB_LIST_P(obj)) {
flag_other_exists = 0;
}
if (CB_STATEMENT_P(obj)) {
continue;
}
if (CB_PAIR_P(obj)) {
// WHEN NOT
if (CB_PURPOSE_INT(obj)) {
return NULL;
}
cb_tree when_target = CB_PAIR_Y(obj);
if (CB_PAIR_P(when_target)) {
cb_tree primary_target = CB_PAIR_X(when_target);
cb_tree thru_target = CB_PAIR_Y(when_target);
if (!CB_LITERAL_P(primary_target) || thru_target) {
return NULL;
}
struct cb_literal *lit = CB_LITERAL(primary_target);
if (lit->data == NULL || lit->scale != 0) {
return NULL;
}
++when_count;
}
}
}
}
}
other_exists |= flag_other_exists;
}
// Build Switch statement
int *cases = malloc(when_count * sizeof(int));
int case_index = 0;
for (case_tree = case_list; case_tree; case_tree = CB_CHAIN(case_tree)) {
whens = CB_VALUE(case_tree);
/* for each WHEN sequence */
for (; whens; whens = CB_CHAIN(whens)) {
for (objs = CB_VALUE(whens); objs; objs = CB_CHAIN(objs)) {
cb_tree obj = CB_VALUE(objs);
if (obj && CB_PAIR_P(obj)) {
cb_tree when_target = CB_PAIR_Y(obj);
struct cb_literal *primary_target =
CB_LITERAL(CB_PAIR_X(when_target));
int result;
int label =
cb_literal_to_int_for_switch_label(primary_target, &result);
// If calculating a switch label fails, the evaluate statement cannot
// be converted to switch statement
if (!result) {
return NULL;
}
// If a duplicate label is found, the evaluate statement cannot be
// converted to switch statement
if (is_switch_label_duplicate(label, cases, case_index)) {
return NULL;
}
cases[case_index++] = label;
}
}
}
}
return cb_build_switch(CB_VALUE(subject_list), case_list);
}

void cb_emit_evaluate(cb_tree subject_list, cb_tree case_list) {
cb_emit(build_evaluate(subject_list, case_list));
cb_tree switch_stmt =
convert_evaluate_stmt_to_switch_stmt(subject_list, case_list);
if (switch_stmt) {
cb_emit(switch_stmt);
} else {
cb_emit(build_evaluate(subject_list, case_list));
}
}

/*
Expand Down
3 changes: 2 additions & 1 deletion tests/Makefile.am
Original file line number Diff line number Diff line change
Expand Up @@ -217,7 +217,8 @@ misc_DEPENDENCIES = \
misc.src/record-key-duplicates-error.at \
misc.src/copy-comments.at \
misc.src/read_prev_after_start.at \
misc.src/japanese-char-section-var.at
misc.src/japanese-char-section-var.at \
misc.src/evaluate-switch.at

EXTRA_DIST = $(srcdir)/package.m4 \
$(TESTS) \
Expand Down
3 changes: 2 additions & 1 deletion tests/Makefile.in
Original file line number Diff line number Diff line change
Expand Up @@ -758,7 +758,8 @@ misc_DEPENDENCIES = \
misc.src/record-key-duplicates-error.at \
misc.src/copy-comments.at \
misc.src/read_prev_after_start.at \
misc.src/japanese-char-section-var.at
misc.src/japanese-char-section-var.at \
misc.src/evaluate-switch.at

EXTRA_DIST = $(srcdir)/package.m4 \
$(TESTS) \
Expand Down
1 change: 1 addition & 0 deletions tests/misc.at
Original file line number Diff line number Diff line change
Expand Up @@ -47,3 +47,4 @@ m4_include([record-key-duplicates-error.at])
#m4_include([copy-comments.at])
m4_include([read_prev_after_start.at])
m4_include([japanese-char-section-var.at])
m4_include([evaluate-switch.at])
Loading