Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: support switch statement #137

Merged
merged 12 commits into from
Dec 10, 2023
Merged
27 changes: 27 additions & 0 deletions sample/switch.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
int small_switch(int x) {
switch (x) {
case 0:
return 2;
case 1:
return 6;
default:
return 0;
}
}

int large_switch(int x) {
switch (x) {
case 0:
return 2;
case 2:
return 6;
case 4:
return 10;
case 6:
return 14;
case -10:
return 17;
default:
return 0;
}
}
3 changes: 3 additions & 0 deletions src/ast/ast.h
Original file line number Diff line number Diff line change
Expand Up @@ -54,12 +54,15 @@ typedef enum AstType {

// statement
AST_CMPD_STMT,
AST_CASE_STMT,
AST_DEFAULT_STMT,
AST_CONTINUE_STMT,
AST_BREAK_STMT,
AST_RET_STMT,
AST_EXPR_STMT,
AST_NULL_STMT,
AST_IF_STMT,
AST_SWITCH_STMT,
AST_WHILE_STMT,
AST_FOR_STMT,

Expand Down
5 changes: 3 additions & 2 deletions src/ctoken/ctoken.c
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,9 @@ BaseType t_ctoken = {

char* ctoken_types[] = {
// non-punctuators
"break", "char", "continue", "else", "enum", "for", "if", "int", "long", "return", "sizeof", "struct", "typedef",
"unsigned", "void", "while", "identifier", "integer-constant", "character-constant", "string-literal",
"break", "case", "char", "continue", "default", "else", "enum", "for", "if", "int", "long", "return", "sizeof",
"struct", "switch", "typedef", "unsigned", "void", "while", "identifier", "integer-constant", "character-constant",
"string-literal",
// punctuators
"[", "]", "(", ")", "{", "}", ".", "->", "++", "--", "&", "*", "+", "-", "~", "!", "/", "%", "<<", ">>", "<", ">",
"<=", ">=", "==", "!=", "^", "|", "&&", "||", "?", ":", ";", "=",
Expand Down
3 changes: 3 additions & 0 deletions src/ctoken/ctoken.h
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,10 @@
typedef enum CTokenType {
// keyword
CTOKEN_KEYWORD_BREAK,
CTOKEN_KEYWORD_CASE,
CTOKEN_KEYWORD_CHAR,
CTOKEN_KEYWORD_CONTINUE,
CTOKEN_KEYWORD_DEFAULT,
CTOKEN_KEYWORD_ELSE,
CTOKEN_KEYWORD_ENUM,
CTOKEN_KEYWORD_FOR,
Expand All @@ -19,6 +21,7 @@ typedef enum CTokenType {
CTOKEN_KEYWORD_RETURN,
CTOKEN_KEYWORD_SIZEOF,
CTOKEN_KEYWORD_STRUCT,
CTOKEN_KEYWORD_SWITCH,
CTOKEN_KEYWORD_TYPEDEF,
CTOKEN_KEYWORD_UNSIGNED,
CTOKEN_KEYWORD_VOID,
Expand Down
13 changes: 13 additions & 0 deletions src/immcgen/immcgen.c
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ Immcgen* new_immcgen(Srt* srt) {
immcgen->continue_label_id = -1;
immcgen->break_label_id = -1;
immcgen->return_label_id = -1;
immcgen->case_label_values = NULL;
immcgen->label_id = -1;
return immcgen;
}
Expand Down Expand Up @@ -53,6 +54,12 @@ Vector* immcgen_generate_immcode(Immcgen* immcgen) {
case SRT_INIT:
codes = gen_initializer_immcode(immcgen);
break;
case SRT_CASE_STMT:
codes = gen_case_stmt_immcode(immcgen);
break;
case SRT_DEFAULT_STMT:
codes = gen_default_stmt_immcode(immcgen);
break;
case SRT_CMPD_STMT:
immcgen->symbol_table = symboltable_enter_scope(immcgen->symbol_table);
immcgen->tag_table = tagtable_enter_scope(immcgen->tag_table);
Expand All @@ -78,6 +85,9 @@ Vector* immcgen_generate_immcode(Immcgen* immcgen) {
case SRT_IF_STMT:
codes = gen_if_else_stmt_immcode(immcgen);
break;
case SRT_SWITCH_STMT:
codes = gen_switch_stmt_immcode(immcgen);
break;
case SRT_WHILE_STMT:
codes = gen_while_stmt_immcode(immcgen);
break;
Expand Down Expand Up @@ -175,5 +185,8 @@ void delete_immcgen(Immcgen* immcgen) {
if (immcgen->initialized_dtype != NULL) {
delete_dtype(immcgen->initialized_dtype);
}
if (immcgen->case_label_values != NULL) {
delete_vector(immcgen->case_label_values);
}
free(immcgen);
}
2 changes: 2 additions & 0 deletions src/immcgen/immcgen.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ typedef struct Immcgen {
int continue_label_id;
int break_label_id;
int return_label_id;
Vector* case_label_values;
int default_label_id;
int label_id;
} Immcgen;

Expand Down
91 changes: 91 additions & 0 deletions src/immcgen/statement.c
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#include "./statement.h"
#include "../common/type.h"
#include "../immc/immc.h"
#include "../pair/pair.h"
#include "./util.h"

#include <stdlib.h>
Expand All @@ -11,6 +12,39 @@ Vector* gen_compound_stmt_immcode(Immcgen* immcgen) {
return codes;
}

Vector* gen_case_stmt_immcode(Immcgen* immcgen) {
Vector* codes = new_vector(&t_immc);

Srt* case_value_srt = vector_at(immcgen->srt->children, 0);

immcgen->label_id++;
int case_label_id = immcgen->label_id;

Pair* label_value_pair = new_pair(&t_integer, &t_iliteral);
pair_set(label_value_pair, new_integer(case_label_id), iliteral_copy(case_value_srt->iliteral));
vector_push(immcgen->case_label_values, label_value_pair);

vector_push(codes, new_label_immc_from_id(IMMC_LABEL_NORMAL, IMMC_VIS_NONE, case_label_id));

append_child_immcode(immcgen, codes, 1);
return codes;
}

Vector* gen_default_stmt_immcode(Immcgen* immcgen) {
Vector* codes = new_vector(&t_immc);

immcgen->label_id++;
int default_label_id = immcgen->label_id;

immcgen->default_label_id = default_label_id;

vector_push(codes, new_label_immc_from_id(IMMC_LABEL_NORMAL, IMMC_VIS_NONE, default_label_id));

append_child_immcode(immcgen, codes, 0);

return codes;
}

Vector* gen_continue_stmt_immcode(Immcgen* immcgen) {
Vector* codes = new_vector(&t_immc);
ImmcOpe* continue_label = new_label_immcope_from_id(immcgen->continue_label_id);
Expand Down Expand Up @@ -126,6 +160,63 @@ Vector* gen_if_with_else_stmt_immcode(Immcgen* immcgen) {
return codes;
}

Vector* gen_switch_stmt_immcode(Immcgen* immcgen) {
Vector* codes = new_vector(&t_immc);

Vector* cases_codes = new_vector(&t_immc);
ImmcOpe* reg = gen_child_reg_immcope(immcgen, cases_codes, 0);

Vector* body_codes = new_vector(&t_immc);
immcgen->label_id++;
int break_label_id = immcgen->label_id;

Vector* original_case_label_values = immcgen->case_label_values;
int original_default_label_id = immcgen->default_label_id;
int original_break_label_id = immcgen->break_label_id;

immcgen->case_label_values = new_vector(&t_pair);
immcgen->default_label_id = -1;

immcgen->break_label_id = break_label_id;
append_child_immcode(immcgen, body_codes, 1);
immcgen->break_label_id = original_break_label_id;

int num_labels = vector_size(immcgen->case_label_values);
for (int i = 0; i < num_labels; i++) {
Pair* label_value_pair = vector_at(immcgen->case_label_values, i);
int* case_label_id_ref = pair_first(label_value_pair);
ImmcOpe* case_label = new_label_immcope_from_id(*case_label_id_ref);

IntegerLiteral* case_value_iliteral = pair_second(label_value_pair);
ImmcSuffix suffix = immcsuffix_get(iliteral_nbytes(case_value_iliteral));
ImmcOpe* case_value = new_int_immcope(suffix, iliteral_copy(case_value_iliteral));

vector_push(cases_codes, new_inst_immc(IMMC_INST_JEQ, case_label, immcope_copy(reg), case_value));
}
delete_immcope(reg);

if (immcgen->default_label_id > 0) {
ImmcOpe* default_label = new_label_immcope_from_id(immcgen->default_label_id);
vector_push(cases_codes, new_inst_immc(IMMC_INST_JMP, default_label, NULL, NULL));
} else {
ImmcOpe* break_label = new_label_immcope_from_id(break_label_id);
vector_push(cases_codes, new_inst_immc(IMMC_INST_JMP, break_label, NULL, NULL));
}

delete_vector(immcgen->case_label_values);
immcgen->case_label_values = original_case_label_values;
immcgen->default_label_id = original_default_label_id;

vector_push(body_codes, new_label_immc_from_id(IMMC_LABEL_NORMAL, IMMC_VIS_NONE, break_label_id));

vector_extend(codes, cases_codes);
delete_vector(cases_codes);
vector_extend(codes, body_codes);
delete_vector(body_codes);

return codes;
}

Vector* gen_while_stmt_immcode(Immcgen* immcgen) {
Vector* codes = new_vector(&t_immc);

Expand Down
3 changes: 3 additions & 0 deletions src/immcgen/statement.h
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,16 @@

#include "./immcgen.h"

Vector* gen_case_stmt_immcode(Immcgen* immcgen);
Vector* gen_default_stmt_immcode(Immcgen* immcgen);
Vector* gen_compound_stmt_immcode(Immcgen* immcgen);
Vector* gen_continue_stmt_immcode(Immcgen* immcgen);
Vector* gen_break_stmt_immcode(Immcgen* immcgen);
Vector* gen_return_stmt_immcode(Immcgen* immcgen);
Vector* gen_expression_stmt_immcode(Immcgen* immcgen);
Vector* gen_null_stmt_immcode(void);
Vector* gen_if_else_stmt_immcode(Immcgen* immcgen);
Vector* gen_switch_stmt_immcode(Immcgen* immcgen);
Vector* gen_while_stmt_immcode(Immcgen* immcgen);
Vector* gen_for_stmt_immcode(Immcgen* immcgen);

Expand Down
3 changes: 3 additions & 0 deletions src/lexer/util.c
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,10 @@ Map* new_keyword_map(void) {
Map* keyword_map = new_map(&t_hashable_string, &t_integer);

ctoken_map_add(keyword_map, "break", CTOKEN_KEYWORD_BREAK);
ctoken_map_add(keyword_map, "case", CTOKEN_KEYWORD_CASE);
ctoken_map_add(keyword_map, "char", CTOKEN_KEYWORD_CHAR);
ctoken_map_add(keyword_map, "continue", CTOKEN_KEYWORD_CONTINUE);
ctoken_map_add(keyword_map, "default", CTOKEN_KEYWORD_DEFAULT);
ctoken_map_add(keyword_map, "else", CTOKEN_KEYWORD_ELSE);
ctoken_map_add(keyword_map, "enum", CTOKEN_KEYWORD_ENUM);
ctoken_map_add(keyword_map, "for", CTOKEN_KEYWORD_FOR);
Expand All @@ -21,6 +23,7 @@ Map* new_keyword_map(void) {
ctoken_map_add(keyword_map, "return", CTOKEN_KEYWORD_RETURN);
ctoken_map_add(keyword_map, "sizeof", CTOKEN_KEYWORD_SIZEOF);
ctoken_map_add(keyword_map, "struct", CTOKEN_KEYWORD_STRUCT);
ctoken_map_add(keyword_map, "switch", CTOKEN_KEYWORD_SWITCH);
ctoken_map_add(keyword_map, "typedef", CTOKEN_KEYWORD_TYPEDEF);
ctoken_map_add(keyword_map, "unsigned", CTOKEN_KEYWORD_UNSIGNED);
ctoken_map_add(keyword_map, "void", CTOKEN_KEYWORD_VOID);
Expand Down
21 changes: 21 additions & 0 deletions src/literal/iliteral.c
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,11 @@ int iliteral_isunsigned(IntegerLiteral* iliteral) {
return iliteral_type_isunsigned(iliteral->type);
}

int iliteral_nbytes(IntegerLiteral* iliteral) {
// TODO: consider range of integer literal value
return iliteral_type_nbytes(iliteral->type);
}

char* iliteral_display_string(IntegerLiteral* iliteral) {
char* display_string = malloc(50 * sizeof(char));
if (iliteral_isunsigned(iliteral)) {
Expand All @@ -63,6 +68,22 @@ int iliteral_type_isunsigned(IntegerLiteralType type) {
return type == INTEGER_UNSIGNED_INT || type == INTEGER_UNSIGNED_LONG || type == INTEGER_UNSIGNED_LONGLONG;
}

int iliteral_type_nbytes(IntegerLiteralType type) {
switch (type) {
case INTEGER_INT:
case INTEGER_UNSIGNED_INT:
return 4;
case INTEGER_LONG:
case INTEGER_UNSIGNED_LONG:
return 8;
case INTEGER_LONGLONG:
case INTEGER_UNSIGNED_LONGLONG:
return 8;
default:
return 0;
}
}

IntegerLiteralType iliteral_type_get(int scalar_rank, int is_unsigned) {
if (scalar_rank < 3) {
return is_unsigned ? INTEGER_UNSIGNED_INT : INTEGER_INT;
Expand Down
2 changes: 2 additions & 0 deletions src/literal/iliteral.h
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,12 @@ IntegerLiteral* new_signed_iliteral(IntegerLiteralType type, long long value);
IntegerLiteral* new_unsigned_iliteral(IntegerLiteralType type, unsigned long long value);
IntegerLiteral* iliteral_copy(IntegerLiteral* iliteral);
int iliteral_isunsigned(IntegerLiteral* iliteral);
int iliteral_nbytes(IntegerLiteral* iliteral);
char* iliteral_display_string(IntegerLiteral* iliteral);
void delete_iliteral(IntegerLiteral* iliteral);

int iliteral_type_isunsigned(IntegerLiteralType type);
int iliteral_type_nbytes(IntegerLiteralType type);
IntegerLiteralType iliteral_type_get(int rank, int is_unsigned);

#endif
5 changes: 5 additions & 0 deletions src/pair/pair.c
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,11 @@ struct Pair {
void* second;
};

BaseType t_pair = {
.copy_object = (void* (*)(void*))pair_copy,
.delete_object = (void (*)(void*))delete_pair,
};

Pair* new_pair(BaseType* t_first, BaseType* t_second) {
Pair* pair = malloc(sizeof(Pair));
pair->t_first = t_first;
Expand Down
2 changes: 2 additions & 0 deletions src/pair/pair.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@

typedef struct Pair Pair;

extern BaseType t_pair;

Pair* new_pair(BaseType* t_first, BaseType* t_second);
Pair* pair_copy(Pair* pair);
void* pair_first(Pair* pair);
Expand Down
Loading