codegen almost finished
This commit is contained in:
parent
ed0ad1d095
commit
870cf8f0b4
15 changed files with 523 additions and 126 deletions
370
codegen.c
370
codegen.c
|
|
@ -447,25 +447,105 @@ void gen_expr(FILE *fp, ast_node *expr)
|
||||||
gen_expr(fp, expr->expr.cast.value);
|
gen_expr(fp, expr->expr.cast.value);
|
||||||
break;
|
break;
|
||||||
case NODE_VAR_DECL: {
|
case NODE_VAR_DECL: {
|
||||||
int offset;
|
int offset = 0;
|
||||||
if (expr->expr.var_decl.type && expr->expr.var_decl.type->expr_type) {
|
type *var_type = expr->expr_type;
|
||||||
usize var_size = expr->expr.var_decl.type->expr_type->size;
|
if (!var_type && expr->expr.var_decl.type) {
|
||||||
|
var_type = expr->expr.var_decl.type->expr_type;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool is_inline_slice = false;
|
||||||
|
if (var_type && var_type->tag == TYPE_SLICE && expr->expr.var_decl.value &&
|
||||||
|
(expr->expr.var_decl.value->type == NODE_STRUCT_INIT ||
|
||||||
|
expr->expr.var_decl.value->type == NODE_RANGE)) {
|
||||||
|
is_inline_slice = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!is_inline_slice) {
|
||||||
|
if (var_type && var_type->size > 0) {
|
||||||
offset = get_var_offset_sized(expr->expr.var_decl.name,
|
offset = get_var_offset_sized(expr->expr.var_decl.name,
|
||||||
expr->expr.var_decl.name_len, var_size);
|
expr->expr.var_decl.name_len, var_type->size);
|
||||||
} else {
|
} else {
|
||||||
offset = get_var_offset(expr->expr.var_decl.name, expr->expr.var_decl.name_len);
|
offset = get_var_offset(expr->expr.var_decl.name, expr->expr.var_decl.name_len);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (expr->expr.var_decl.value) {
|
if (expr->expr.var_decl.value) {
|
||||||
if (expr->expr.var_decl.value->type == NODE_STRUCT_INIT) {
|
if (expr->expr.var_decl.value->type == NODE_RANGE && var_type && var_type->tag == TYPE_SLICE) {
|
||||||
|
ast_node *range = expr->expr.var_decl.value;
|
||||||
|
if (range->expr.binary.left->type == NODE_INTEGER &&
|
||||||
|
range->expr.binary.right->type == NODE_INTEGER) {
|
||||||
|
i64 start = range->expr.binary.left->expr.integer;
|
||||||
|
i64 end = range->expr.binary.right->expr.integer;
|
||||||
|
i64 count = end - start + 1;
|
||||||
|
|
||||||
|
type *element_type = var_type->data.slice.child;
|
||||||
|
usize element_size = element_type ? element_type->size : 8;
|
||||||
|
|
||||||
|
usize data_size = count * element_size;
|
||||||
|
usize aligned_data_size = (data_size + 7) & ~7;
|
||||||
|
stack_offset += aligned_data_size;
|
||||||
|
int data_offset = stack_offset;
|
||||||
|
|
||||||
|
stack_offset += 16;
|
||||||
|
offset = stack_offset;
|
||||||
|
|
||||||
|
char *var_name = strndup(expr->expr.var_decl.name, expr->expr.var_decl.name_len);
|
||||||
|
shput(variables, var_name, offset);
|
||||||
|
|
||||||
|
for (i64 i = 0; i < count; i++) {
|
||||||
|
i64 value = start + i;
|
||||||
|
int element_offset = data_offset - (i * element_size);
|
||||||
|
fprintf(fp, "mov $%ld, %%rax\n", value);
|
||||||
|
if (element_size == 4) {
|
||||||
|
fprintf(fp, "mov %%eax, -%d(%%rbp)\n", element_offset);
|
||||||
|
} else if (element_size == 2) {
|
||||||
|
fprintf(fp, "mov %%ax, -%d(%%rbp)\n", element_offset);
|
||||||
|
} else if (element_size == 1) {
|
||||||
|
fprintf(fp, "mov %%al, -%d(%%rbp)\n", element_offset);
|
||||||
|
} else {
|
||||||
|
fprintf(fp, "mov %%rax, -%d(%%rbp)\n", element_offset);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fprintf(fp, "lea -%d(%%rbp), %%rax\n", data_offset);
|
||||||
|
fprintf(fp, "mov %%rax, -%d(%%rbp)\n", offset);
|
||||||
|
fprintf(fp, "mov $%ld, %%rax\n", count);
|
||||||
|
fprintf(fp, "mov %%rax, -%d(%%rbp)\n", offset - 8);
|
||||||
|
}
|
||||||
|
} else if (expr->expr.var_decl.value->type == NODE_STRING && var_type && var_type->tag == TYPE_SLICE) {
|
||||||
|
ast_node *str = expr->expr.var_decl.value;
|
||||||
|
usize str_len = str->expr.string.len;
|
||||||
|
char *str_data = str->expr.string.start;
|
||||||
|
|
||||||
|
usize aligned_data_size = (str_len + 7) & ~7;
|
||||||
|
stack_offset += aligned_data_size;
|
||||||
|
int data_offset = stack_offset;
|
||||||
|
|
||||||
|
stack_offset += 16;
|
||||||
|
offset = stack_offset;
|
||||||
|
|
||||||
|
char *var_name = strndup(expr->expr.var_decl.name, expr->expr.var_decl.name_len);
|
||||||
|
shput(variables, var_name, offset);
|
||||||
|
|
||||||
|
for (usize i = 0; i < str_len; i++) {
|
||||||
|
int byte_offset = data_offset - i;
|
||||||
|
if ((unsigned char)str_data[i] == '\\' && (unsigned char)str_data[i+1] == 'n') {
|
||||||
|
fprintf(fp, "movb $%d, -%d(%%rbp)\n", (unsigned char)'\n', byte_offset);
|
||||||
|
i += 1;
|
||||||
|
} else {
|
||||||
|
fprintf(fp, "movb $%d, -%d(%%rbp)\n", (unsigned char)str_data[i], byte_offset);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fprintf(fp, "lea -%d(%%rbp), %%rax\n", data_offset);
|
||||||
|
fprintf(fp, "mov %%rax, -%d(%%rbp)\n", offset);
|
||||||
|
fprintf(fp, "mov $%lu, %%rax\n", str_len);
|
||||||
|
fprintf(fp, "mov %%rax, -%d(%%rbp)\n", offset - 8);
|
||||||
|
} else if (expr->expr.var_decl.value->type == NODE_STRUCT_INIT) {
|
||||||
ast_node *member_list = expr->expr.var_decl.value->expr.struct_init.members;
|
ast_node *member_list = expr->expr.var_decl.value->expr.struct_init.members;
|
||||||
ast_node *current = member_list;
|
ast_node *current = member_list;
|
||||||
|
|
||||||
type *struct_type = expr->expr_type;
|
if (var_type && var_type->tag == TYPE_STRUCT) {
|
||||||
if (!struct_type && expr->expr.var_decl.type) {
|
|
||||||
struct_type = expr->expr.var_decl.type->expr_type;
|
|
||||||
}
|
|
||||||
|
|
||||||
while (current && current->type == NODE_UNIT) {
|
while (current && current->type == NODE_UNIT) {
|
||||||
ast_node *assignment = current->expr.unit_node.expr;
|
ast_node *assignment = current->expr.unit_node.expr;
|
||||||
if (assignment && assignment->type == NODE_BINARY &&
|
if (assignment && assignment->type == NODE_BINARY &&
|
||||||
|
|
@ -473,12 +553,11 @@ void gen_expr(FILE *fp, ast_node *expr)
|
||||||
ast_node *field = assignment->expr.binary.left;
|
ast_node *field = assignment->expr.binary.left;
|
||||||
ast_node *value = assignment->expr.binary.right;
|
ast_node *value = assignment->expr.binary.right;
|
||||||
|
|
||||||
if (field->type == NODE_IDENTIFIER && struct_type &&
|
if (field->type == NODE_IDENTIFIER) {
|
||||||
struct_type->tag == TYPE_STRUCT) {
|
|
||||||
char *field_name = strndup(field->expr.string.start,
|
char *field_name = strndup(field->expr.string.start,
|
||||||
field->expr.string.len);
|
field->expr.string.len);
|
||||||
|
|
||||||
member *m = struct_type->data.structure.members;
|
member *m = var_type->data.structure.members;
|
||||||
int field_offset = -1;
|
int field_offset = -1;
|
||||||
while (m) {
|
while (m) {
|
||||||
if (m->name_len == field->expr.string.len &&
|
if (m->name_len == field->expr.string.len &&
|
||||||
|
|
@ -492,7 +571,7 @@ void gen_expr(FILE *fp, ast_node *expr)
|
||||||
if (field_offset >= 0) {
|
if (field_offset >= 0) {
|
||||||
gen_expr(fp, value);
|
gen_expr(fp, value);
|
||||||
|
|
||||||
type *field_type = shget(struct_type->data.structure.member_types, field_name);
|
type *field_type = shget(var_type->data.structure.member_types, field_name);
|
||||||
|
|
||||||
if (field_type && field_type->size == 4) {
|
if (field_type && field_type->size == 4) {
|
||||||
fprintf(fp, "mov %%eax, -%d(%%rbp)\n", offset + field_offset);
|
fprintf(fp, "mov %%eax, -%d(%%rbp)\n", offset + field_offset);
|
||||||
|
|
@ -510,11 +589,100 @@ void gen_expr(FILE *fp, ast_node *expr)
|
||||||
}
|
}
|
||||||
current = current->expr.unit_node.next;
|
current = current->expr.unit_node.next;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
else if (var_type && (var_type->tag == TYPE_PTR || var_type->tag == TYPE_SLICE)) {
|
||||||
|
usize element_size = 8;
|
||||||
|
type *element_type = NULL;
|
||||||
|
|
||||||
|
if (var_type->tag == TYPE_PTR && var_type->data.ptr.child) {
|
||||||
|
element_type = var_type->data.ptr.child;
|
||||||
|
element_size = element_type->size;
|
||||||
|
} else if (var_type->tag == TYPE_SLICE && var_type->data.slice.child) {
|
||||||
|
element_type = var_type->data.slice.child;
|
||||||
|
element_size = element_type->size;
|
||||||
|
}
|
||||||
|
|
||||||
|
int element_count = 0;
|
||||||
|
ast_node *count_node = current;
|
||||||
|
while (count_node && count_node->type == NODE_UNIT) {
|
||||||
|
element_count++;
|
||||||
|
count_node = count_node->expr.unit_node.next;
|
||||||
|
}
|
||||||
|
if (var_type->tag == TYPE_SLICE) {
|
||||||
|
usize data_size = element_count * element_size;
|
||||||
|
usize aligned_data_size = (data_size + 7) & ~7;
|
||||||
|
stack_offset += aligned_data_size;
|
||||||
|
int data_offset = stack_offset;
|
||||||
|
stack_offset += 16;
|
||||||
|
offset = stack_offset;
|
||||||
|
|
||||||
|
char *var_name = strndup(expr->expr.var_decl.name, expr->expr.var_decl.name_len);
|
||||||
|
shput(variables, var_name, offset);
|
||||||
|
|
||||||
|
int index = 0;
|
||||||
|
while (current && current->type == NODE_UNIT) {
|
||||||
|
ast_node *value = current->expr.unit_node.expr;
|
||||||
|
if (value) {
|
||||||
|
gen_expr(fp, value);
|
||||||
|
|
||||||
|
int element_offset = data_offset - (index * element_size);
|
||||||
|
|
||||||
|
if (element_type && element_type->size == 4) {
|
||||||
|
fprintf(fp, "mov %%eax, -%d(%%rbp)\n", element_offset);
|
||||||
|
} else if (element_type && element_type->size == 2) {
|
||||||
|
fprintf(fp, "mov %%ax, -%d(%%rbp)\n", element_offset);
|
||||||
|
} else if (element_type && element_type->size == 1) {
|
||||||
|
fprintf(fp, "mov %%al, -%d(%%rbp)\n", element_offset);
|
||||||
|
} else {
|
||||||
|
fprintf(fp, "mov %%rax, -%d(%%rbp)\n", element_offset);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
index++;
|
||||||
|
current = current->expr.unit_node.next;
|
||||||
|
}
|
||||||
|
|
||||||
|
fprintf(fp, "lea -%d(%%rbp), %%rax\n", data_offset);
|
||||||
|
fprintf(fp, "mov %%rax, -%d(%%rbp)\n", offset);
|
||||||
|
fprintf(fp, "mov $%d, %%rax\n", element_count);
|
||||||
|
fprintf(fp, "mov %%rax, -%d(%%rbp)\n", offset - 8);
|
||||||
|
} else {
|
||||||
|
int index = 0;
|
||||||
|
while (current && current->type == NODE_UNIT) {
|
||||||
|
ast_node *value = current->expr.unit_node.expr;
|
||||||
|
if (value) {
|
||||||
|
gen_expr(fp, value);
|
||||||
|
|
||||||
|
int element_offset = offset + (index * element_size);
|
||||||
|
|
||||||
|
if (element_type && element_type->size == 4) {
|
||||||
|
fprintf(fp, "mov %%eax, -%d(%%rbp)\n", element_offset);
|
||||||
|
} else if (element_type && element_type->size == 2) {
|
||||||
|
fprintf(fp, "mov %%ax, -%d(%%rbp)\n", element_offset);
|
||||||
|
} else if (element_type && element_type->size == 1) {
|
||||||
|
fprintf(fp, "mov %%al, -%d(%%rbp)\n", element_offset);
|
||||||
|
} else {
|
||||||
|
fprintf(fp, "mov %%rax, -%d(%%rbp)\n", element_offset);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
index++;
|
||||||
|
current = current->expr.unit_node.next;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
gen_expr(fp, expr->expr.var_decl.value);
|
gen_expr(fp, expr->expr.var_decl.value);
|
||||||
|
|
||||||
|
// If assigning a slice value, copy the 16-byte structure
|
||||||
|
if (var_type && var_type->tag == TYPE_SLICE) {
|
||||||
|
fprintf(fp, "mov (%%rax), %%rcx\n"); // Load ptr field
|
||||||
|
fprintf(fp, "mov 8(%%rax), %%rdx\n"); // Load len field
|
||||||
|
fprintf(fp, "mov %%rcx, -%d(%%rbp)\n", offset); // Store ptr
|
||||||
|
fprintf(fp, "mov %%rdx, -%d(%%rbp)\n", offset - 8); // Store len
|
||||||
|
} else {
|
||||||
fprintf(fp, "mov %%rax, -%d(%%rbp)\n", offset);
|
fprintf(fp, "mov %%rax, -%d(%%rbp)\n", offset);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case NODE_RETURN: {
|
case NODE_RETURN: {
|
||||||
|
|
@ -598,12 +766,24 @@ void gen_expr(FILE *fp, ast_node *expr)
|
||||||
|
|
||||||
if (base->type == NODE_IDENTIFIER) {
|
if (base->type == NODE_IDENTIFIER) {
|
||||||
int base_offset = get_var_offset(base->expr.string.start, base->expr.string.len);
|
int base_offset = get_var_offset(base->expr.string.start, base->expr.string.len);
|
||||||
|
type *base_type = base->expr_type;
|
||||||
|
|
||||||
type *struct_type = base->expr_type;
|
if (base_type && base_type->tag == TYPE_SLICE && member_node->type == NODE_IDENTIFIER) {
|
||||||
|
char *field_name = strndup(member_node->expr.string.start, member_node->expr.string.len);
|
||||||
|
|
||||||
if (member_node->type == NODE_IDENTIFIER && struct_type &&
|
if (strcmp(field_name, "ptr") == 0) {
|
||||||
struct_type->tag == TYPE_STRUCT) {
|
fprintf(fp, "mov -%d(%%rbp), %%rax\n", base_offset);
|
||||||
member *m = struct_type->data.structure.members;
|
} else if (strcmp(field_name, "len") == 0) {
|
||||||
|
fprintf(fp, "mov -%d(%%rbp), %%rax\n", base_offset - 8);
|
||||||
|
} else {
|
||||||
|
fprintf(fp, "# ERROR: slice field '%s' not found\n", field_name);
|
||||||
|
}
|
||||||
|
|
||||||
|
free(field_name);
|
||||||
|
}
|
||||||
|
else if (member_node->type == NODE_IDENTIFIER && base_type &&
|
||||||
|
base_type->tag == TYPE_STRUCT) {
|
||||||
|
member *m = base_type->data.structure.members;
|
||||||
int field_offset = -1;
|
int field_offset = -1;
|
||||||
while (m) {
|
while (m) {
|
||||||
if (m->name_len == member_node->expr.string.len &&
|
if (m->name_len == member_node->expr.string.len &&
|
||||||
|
|
@ -620,17 +800,165 @@ void gen_expr(FILE *fp, ast_node *expr)
|
||||||
fprintf(fp, "# ERROR: field not found\n");
|
fprintf(fp, "# ERROR: field not found\n");
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
fprintf(fp, "# ERROR: not a struct type\n");
|
fprintf(fp, "# ERROR: not a struct or slice type\n");
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
fprintf(fp, "# ERROR: complex struct access not implemented\n");
|
fprintf(fp, "# ERROR: complex struct access not implemented\n");
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
case NODE_RANGE: {
|
||||||
|
if (expr->expr.binary.left->type == NODE_INTEGER &&
|
||||||
|
expr->expr.binary.right->type == NODE_INTEGER) {
|
||||||
|
i64 start = expr->expr.binary.left->expr.integer;
|
||||||
|
i64 end = expr->expr.binary.right->expr.integer;
|
||||||
|
i64 count = end - start + 1;
|
||||||
|
|
||||||
|
usize element_size = 8;
|
||||||
|
usize data_size = count * element_size;
|
||||||
|
usize aligned_data_size = (data_size + 7) & ~7;
|
||||||
|
stack_offset += aligned_data_size;
|
||||||
|
int data_offset = stack_offset;
|
||||||
|
|
||||||
|
for (i64 i = 0; i < count; i++) {
|
||||||
|
i64 value = start + i;
|
||||||
|
int element_offset = data_offset - (i * element_size);
|
||||||
|
fprintf(fp, "mov $%ld, %%rax\n", value);
|
||||||
|
fprintf(fp, "mov %%rax, -%d(%%rbp)\n", element_offset);
|
||||||
|
}
|
||||||
|
|
||||||
|
stack_offset += 16;
|
||||||
|
int slice_offset = stack_offset;
|
||||||
|
|
||||||
|
fprintf(fp, "lea -%d(%%rbp), %%rax\n", data_offset);
|
||||||
|
fprintf(fp, "mov %%rax, -%d(%%rbp)\n", slice_offset);
|
||||||
|
fprintf(fp, "mov $%ld, %%rax\n", count);
|
||||||
|
fprintf(fp, "mov %%rax, -%d(%%rbp)\n", slice_offset - 8);
|
||||||
|
|
||||||
|
fprintf(fp, "lea -%d(%%rbp), %%rax\n", slice_offset);
|
||||||
|
} else {
|
||||||
|
fprintf(fp, "# ERROR: range expression requires constant bounds\n");
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
case NODE_STRUCT_INIT: {
|
case NODE_STRUCT_INIT: {
|
||||||
fprintf(fp, "# ERROR: struct init outside of variable declaration\n");
|
fprintf(fp, "# ERROR: struct init outside of variable declaration\n");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
case NODE_ARRAY_SUBSCRIPT: {
|
||||||
|
usize element_size = 8;
|
||||||
|
type *base_type = expr->expr.subscript.expr->expr_type;
|
||||||
|
bool is_slice = false;
|
||||||
|
|
||||||
|
if (base_type) {
|
||||||
|
if (base_type->tag == TYPE_PTR && base_type->data.ptr.child) {
|
||||||
|
element_size = base_type->data.ptr.child->size;
|
||||||
|
} else if (base_type->tag == TYPE_SLICE && base_type->data.slice.child) {
|
||||||
|
element_size = base_type->data.slice.child->size;
|
||||||
|
is_slice = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (expr->expr.subscript.index->type == NODE_RANGE) {
|
||||||
|
if (expr->expr.subscript.expr->type == NODE_IDENTIFIER) {
|
||||||
|
int base_offset = get_var_offset(expr->expr.subscript.expr->expr.string.start,
|
||||||
|
expr->expr.subscript.expr->expr.string.len);
|
||||||
|
|
||||||
|
fprintf(fp, "mov -%d(%%rbp), %%rcx\n", base_offset);
|
||||||
|
|
||||||
|
gen_expr(fp, expr->expr.subscript.index->expr.binary.left);
|
||||||
|
fprintf(fp, "push %%rax\n");
|
||||||
|
|
||||||
|
gen_expr(fp, expr->expr.subscript.index->expr.binary.right);
|
||||||
|
fprintf(fp, "mov %%rax, %%rdx\n"); // rdx = end
|
||||||
|
fprintf(fp, "pop %%rax\n"); // rax = start
|
||||||
|
fprintf(fp, "mov %%rdx, %%r8\n");
|
||||||
|
fprintf(fp, "sub %%rax, %%r8\n");
|
||||||
|
fprintf(fp, "inc %%r8\n"); // r8 = new length
|
||||||
|
|
||||||
|
if (element_size != 1) {
|
||||||
|
fprintf(fp, "imul $%lu, %%rax\n", element_size);
|
||||||
|
}
|
||||||
|
fprintf(fp, "add %%rcx, %%rax\n"); // rax = new ptr
|
||||||
|
|
||||||
|
// Allocate temporary slice struct (16 bytes)
|
||||||
|
stack_offset += 16;
|
||||||
|
fprintf(fp, "mov %%rax, -%d(%%rbp)\n", stack_offset); // Store ptr
|
||||||
|
fprintf(fp, "mov %%r8, -%d(%%rbp)\n", stack_offset - 8); // Store len
|
||||||
|
fprintf(fp, "lea -%d(%%rbp), %%rax\n", stack_offset); // Return address of temp slice
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (expr->expr.subscript.expr->type == NODE_IDENTIFIER && is_slice) {
|
||||||
|
int base_offset = get_var_offset(expr->expr.subscript.expr->expr.string.start,
|
||||||
|
expr->expr.subscript.expr->expr.string.len);
|
||||||
|
|
||||||
|
fprintf(fp, "mov -%d(%%rbp), %%rcx\n", base_offset);
|
||||||
|
|
||||||
|
gen_expr(fp, expr->expr.subscript.index);
|
||||||
|
|
||||||
|
if (element_size != 1) {
|
||||||
|
fprintf(fp, "imul $%lu, %%rax\n", element_size);
|
||||||
|
}
|
||||||
|
|
||||||
|
fprintf(fp, "add %%rcx, %%rax\n");
|
||||||
|
|
||||||
|
if (expr->expr_type && expr->expr_type->size == 4) {
|
||||||
|
fprintf(fp, "movl (%%rax), %%eax\n");
|
||||||
|
} else if (expr->expr_type && expr->expr_type->size == 2) {
|
||||||
|
fprintf(fp, "movzwl (%%rax), %%eax\n");
|
||||||
|
} else if (expr->expr_type && expr->expr_type->size == 1) {
|
||||||
|
fprintf(fp, "movzbl (%%rax), %%eax\n");
|
||||||
|
} else {
|
||||||
|
fprintf(fp, "mov (%%rax), %%rax\n");
|
||||||
|
}
|
||||||
|
} else if (expr->expr.subscript.expr->type == NODE_IDENTIFIER) {
|
||||||
|
int base_offset = get_var_offset(expr->expr.subscript.expr->expr.string.start,
|
||||||
|
expr->expr.subscript.expr->expr.string.len);
|
||||||
|
|
||||||
|
gen_expr(fp, expr->expr.subscript.index);
|
||||||
|
|
||||||
|
if (element_size != 1) {
|
||||||
|
fprintf(fp, "imul $%lu, %%rax\n", element_size);
|
||||||
|
}
|
||||||
|
|
||||||
|
fprintf(fp, "add $%d, %%rax\n", base_offset);
|
||||||
|
fprintf(fp, "neg %%rax\n");
|
||||||
|
fprintf(fp, "add %%rbp, %%rax\n");
|
||||||
|
|
||||||
|
if (expr->expr_type && expr->expr_type->size == 4) {
|
||||||
|
fprintf(fp, "movl (%%rax), %%eax\n");
|
||||||
|
} else if (expr->expr_type && expr->expr_type->size == 2) {
|
||||||
|
fprintf(fp, "movzwl (%%rax), %%eax\n");
|
||||||
|
} else if (expr->expr_type && expr->expr_type->size == 1) {
|
||||||
|
fprintf(fp, "movzbl (%%rax), %%eax\n");
|
||||||
|
} else {
|
||||||
|
fprintf(fp, "mov (%%rax), %%rax\n");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
gen_expr(fp, expr->expr.subscript.expr);
|
||||||
|
fprintf(fp, "push %%rax\n");
|
||||||
|
|
||||||
|
gen_expr(fp, expr->expr.subscript.index);
|
||||||
|
|
||||||
|
if (element_size != 1) {
|
||||||
|
fprintf(fp, "imul $%lu, %%rax\n", element_size);
|
||||||
|
}
|
||||||
|
|
||||||
|
fprintf(fp, "pop %%rcx\n");
|
||||||
|
fprintf(fp, "add %%rcx, %%rax\n");
|
||||||
|
|
||||||
|
if (expr->expr_type && expr->expr_type->size == 4) {
|
||||||
|
fprintf(fp, "movl (%%rax), %%eax\n");
|
||||||
|
} else if (expr->expr_type && expr->expr_type->size == 2) {
|
||||||
|
fprintf(fp, "movzwl (%%rax), %%eax\n");
|
||||||
|
} else if (expr->expr_type && expr->expr_type->size == 1) {
|
||||||
|
fprintf(fp, "movzbl (%%rax), %%eax\n");
|
||||||
|
} else {
|
||||||
|
fprintf(fp, "mov (%%rax), %%rax\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
case NODE_CALL: {
|
case NODE_CALL: {
|
||||||
const char *arg_regs[] = {"%rdi", "%rsi", "%rdx", "%rcx", "%r8", "%r9"};
|
const char *arg_regs[] = {"%rdi", "%rsi", "%rdx", "%rcx", "%r8", "%r9"};
|
||||||
|
|
||||||
|
|
@ -676,6 +1004,10 @@ void gen_expr(FILE *fp, ast_node *expr)
|
||||||
|
|
||||||
void gen_function(FILE *fp, ast_node *fn)
|
void gen_function(FILE *fp, ast_node *fn)
|
||||||
{
|
{
|
||||||
|
if (fn->expr.function.is_extern || fn->expr.function.body == NULL) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
ast_node *current = fn->expr.function.body;
|
ast_node *current = fn->expr.function.body;
|
||||||
|
|
||||||
stack_offset = 0;
|
stack_offset = 0;
|
||||||
|
|
|
||||||
0
done.txt
0
done.txt
43
parser.c
43
parser.c
|
|
@ -1089,6 +1089,7 @@ static ast_node *parse_function(parser *p)
|
||||||
{
|
{
|
||||||
ast_node *fn = arena_alloc(p->allocator, sizeof(ast_node));
|
ast_node *fn = arena_alloc(p->allocator, sizeof(ast_node));
|
||||||
fn->type = NODE_FUNCTION;
|
fn->type = NODE_FUNCTION;
|
||||||
|
fn->expr.function.is_extern = false;
|
||||||
fn->expr.function.type = parse_type(p);
|
fn->expr.function.type = parse_type(p);
|
||||||
fn->expr.function.name = peek(p)->lexeme;
|
fn->expr.function.name = peek(p)->lexeme;
|
||||||
fn->expr.function.name_len = peek(p)->lexeme_len;
|
fn->expr.function.name_len = peek(p)->lexeme_len;
|
||||||
|
|
@ -1097,7 +1098,14 @@ static ast_node *parse_function(parser *p)
|
||||||
advance(p);
|
advance(p);
|
||||||
|
|
||||||
if (match(p, TOKEN_RPAREN)) {
|
if (match(p, TOKEN_RPAREN)) {
|
||||||
fn->expr.function.body = parse_compound(p);;
|
// Check if this is an extern declaration (semicolon) or definition (body)
|
||||||
|
if (match_peek(p, TOKEN_SEMICOLON)) {
|
||||||
|
// Extern function - no body, just consume semicolon
|
||||||
|
advance(p);
|
||||||
|
fn->expr.function.body = NULL;
|
||||||
|
} else {
|
||||||
|
fn->expr.function.body = parse_compound(p);
|
||||||
|
}
|
||||||
fn->expr.function.parameters = NULL;
|
fn->expr.function.parameters = NULL;
|
||||||
fn->expr.function.parameters_len = 0;
|
fn->expr.function.parameters_len = 0;
|
||||||
return fn;
|
return fn;
|
||||||
|
|
@ -1110,8 +1118,14 @@ static ast_node *parse_function(parser *p)
|
||||||
if (!match(p, TOKEN_RPAREN)) {
|
if (!match(p, TOKEN_RPAREN)) {
|
||||||
error(p, "expected `,`.");
|
error(p, "expected `,`.");
|
||||||
return NULL;
|
return NULL;
|
||||||
|
} else {
|
||||||
|
// Check if this is an extern declaration (semicolon) or definition (body)
|
||||||
|
if (match_peek(p, TOKEN_SEMICOLON)) {
|
||||||
|
advance(p);
|
||||||
|
fn->expr.function.body = NULL;
|
||||||
} else {
|
} else {
|
||||||
fn->expr.function.body = parse_compound(p);
|
fn->expr.function.body = parse_compound(p);
|
||||||
|
}
|
||||||
return fn;
|
return fn;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1132,7 +1146,14 @@ static ast_node *parse_function(parser *p)
|
||||||
|
|
||||||
prev = current;
|
prev = current;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Check if this is an extern declaration (semicolon) or definition (body)
|
||||||
|
if (match_peek(p, TOKEN_SEMICOLON)) {
|
||||||
|
advance(p);
|
||||||
|
fn->expr.function.body = NULL;
|
||||||
|
} else {
|
||||||
fn->expr.function.body = parse_compound(p);
|
fn->expr.function.body = parse_compound(p);
|
||||||
|
}
|
||||||
|
|
||||||
return fn;
|
return fn;
|
||||||
}
|
}
|
||||||
|
|
@ -1140,6 +1161,13 @@ static ast_node *parse_function(parser *p)
|
||||||
static ast_node *parse_statement(parser *p)
|
static ast_node *parse_statement(parser *p)
|
||||||
{
|
{
|
||||||
token *cur = peek(p);
|
token *cur = peek(p);
|
||||||
|
|
||||||
|
/* Check for extern function declaration */
|
||||||
|
bool is_extern = false;
|
||||||
|
if (match(p, TOKEN_EXTERN)) {
|
||||||
|
is_extern = true;
|
||||||
|
}
|
||||||
|
|
||||||
ast_node *type = parse_type(p);
|
ast_node *type = parse_type(p);
|
||||||
if (type && type->type == NODE_STRUCT && type->expr.structure.name_len > 0) {
|
if (type && type->type == NODE_STRUCT && type->expr.structure.name_len > 0) {
|
||||||
goto skip_struct;
|
goto skip_struct;
|
||||||
|
|
@ -1148,9 +1176,20 @@ static ast_node *parse_statement(parser *p)
|
||||||
if (p->tokens->next && p->tokens->next->type == TOKEN_LPAREN) {
|
if (p->tokens->next && p->tokens->next->type == TOKEN_LPAREN) {
|
||||||
/* Function definition. */
|
/* Function definition. */
|
||||||
p->tokens = cur;
|
p->tokens = cur;
|
||||||
return parse_function(p);
|
if (is_extern) {
|
||||||
|
advance(p); // Skip TOKEN_EXTERN
|
||||||
|
}
|
||||||
|
ast_node *fn = parse_function(p);
|
||||||
|
if (fn && is_extern) {
|
||||||
|
fn->expr.function.is_extern = true;
|
||||||
|
fn->expr.function.body = NULL;
|
||||||
|
}
|
||||||
|
return fn;
|
||||||
}
|
}
|
||||||
p->tokens = cur;
|
p->tokens = cur;
|
||||||
|
if (is_extern) {
|
||||||
|
advance(p); // Skip TOKEN_EXTERN for non-function case
|
||||||
|
}
|
||||||
/* Variable declaration. */
|
/* Variable declaration. */
|
||||||
ast_node *node = arena_alloc(p->allocator, sizeof(ast_node));
|
ast_node *node = arena_alloc(p->allocator, sizeof(ast_node));
|
||||||
node->type = NODE_VAR_DECL;
|
node->type = NODE_VAR_DECL;
|
||||||
|
|
|
||||||
1
parser.h
1
parser.h
|
|
@ -230,6 +230,7 @@ typedef struct _ast_node {
|
||||||
usize name_len;
|
usize name_len;
|
||||||
struct _ast_node *type;
|
struct _ast_node *type;
|
||||||
struct _ast_node *body;
|
struct _ast_node *body;
|
||||||
|
bool is_extern;
|
||||||
} function;
|
} function;
|
||||||
struct {
|
struct {
|
||||||
variant *variants;
|
variant *variants;
|
||||||
|
|
|
||||||
80
sema.c
80
sema.c
|
|
@ -152,17 +152,18 @@ static type *get_type(sema *s, ast_node *n)
|
||||||
return t;
|
return t;
|
||||||
case NODE_PTR_TYPE:
|
case NODE_PTR_TYPE:
|
||||||
t = malloc(sizeof(type));
|
t = malloc(sizeof(type));
|
||||||
t->size = sizeof(usize);
|
|
||||||
t->alignment = sizeof(usize);
|
t->alignment = sizeof(usize);
|
||||||
if (n->expr.ptr_type.flags & PTR_RAW) {
|
if (n->expr.ptr_type.flags & PTR_RAW) {
|
||||||
t->name = "ptr";
|
t->name = "ptr";
|
||||||
t->tag = TYPE_PTR;
|
t->tag = TYPE_PTR;
|
||||||
|
t->size = sizeof(usize);
|
||||||
t->data.ptr.child = get_type(s, n->expr.ptr_type.type);
|
t->data.ptr.child = get_type(s, n->expr.ptr_type.type);
|
||||||
t->data.ptr.is_const = (n->expr.ptr_type.flags & PTR_CONST) != 0;
|
t->data.ptr.is_const = (n->expr.ptr_type.flags & PTR_CONST) != 0;
|
||||||
t->data.ptr.is_volatile = (n->expr.ptr_type.flags & PTR_VOLATILE) != 0;
|
t->data.ptr.is_volatile = (n->expr.ptr_type.flags & PTR_VOLATILE) != 0;
|
||||||
} else {
|
} else {
|
||||||
t->name = "slice";
|
t->name = "slice";
|
||||||
t->tag = TYPE_SLICE;
|
t->tag = TYPE_SLICE;
|
||||||
|
t->size = sizeof(usize) * 2; // ptr + len = 16 bytes
|
||||||
t->data.slice.child = get_type(s, n->expr.ptr_type.type);
|
t->data.slice.child = get_type(s, n->expr.ptr_type.type);
|
||||||
t->data.slice.is_const = (n->expr.ptr_type.flags & PTR_CONST) != 0;
|
t->data.slice.is_const = (n->expr.ptr_type.flags & PTR_CONST) != 0;
|
||||||
t->data.slice.is_volatile = (n->expr.ptr_type.flags & PTR_VOLATILE) != 0;
|
t->data.slice.is_volatile = (n->expr.ptr_type.flags & PTR_VOLATILE) != 0;
|
||||||
|
|
@ -377,8 +378,8 @@ static ast_node *get_def(sema *s, char *name)
|
||||||
static type *get_string_type(sema *s, ast_node *node)
|
static type *get_string_type(sema *s, ast_node *node)
|
||||||
{
|
{
|
||||||
type *string_type = arena_alloc(s->allocator, sizeof(type));
|
type *string_type = arena_alloc(s->allocator, sizeof(type));
|
||||||
string_type->tag = TYPE_PTR;
|
string_type->tag = TYPE_SLICE;
|
||||||
string_type->size = sizeof(usize);
|
string_type->size = sizeof(usize) * 2; // ptr + len = 16 bytes
|
||||||
string_type->alignment = sizeof(usize);
|
string_type->alignment = sizeof(usize);
|
||||||
string_type->name = "slice";
|
string_type->name = "slice";
|
||||||
string_type->data.slice.child = shget(type_reg, "u8");
|
string_type->data.slice.child = shget(type_reg, "u8");
|
||||||
|
|
@ -409,6 +410,33 @@ static type *get_access_type(sema *s, ast_node *node)
|
||||||
ast_node *member = node->expr.access.member;
|
ast_node *member = node->expr.access.member;
|
||||||
char *name_start = member->expr.string.start;
|
char *name_start = member->expr.string.start;
|
||||||
usize name_len = member->expr.string.len;
|
usize name_len = member->expr.string.len;
|
||||||
|
|
||||||
|
// Handle slice field access
|
||||||
|
if (t && t->tag == TYPE_SLICE) {
|
||||||
|
char *name = intern_string(s, name_start, name_len);
|
||||||
|
if (strcmp(name, "ptr") == 0) {
|
||||||
|
// Return pointer to element type
|
||||||
|
type *ptr_type = arena_alloc(s->allocator, sizeof(type));
|
||||||
|
ptr_type->tag = TYPE_PTR;
|
||||||
|
ptr_type->size = 8;
|
||||||
|
ptr_type->alignment = 8;
|
||||||
|
ptr_type->name = "ptr";
|
||||||
|
ptr_type->data.ptr.child = t->data.slice.child;
|
||||||
|
ptr_type->data.ptr.is_const = t->data.slice.is_const;
|
||||||
|
ptr_type->data.ptr.is_volatile = t->data.slice.is_volatile;
|
||||||
|
free(name);
|
||||||
|
return ptr_type;
|
||||||
|
} else if (strcmp(name, "len") == 0) {
|
||||||
|
// Return usize type
|
||||||
|
free(name);
|
||||||
|
return shget(type_reg, "usize");
|
||||||
|
} else {
|
||||||
|
error(node, "slice doesn't have that field");
|
||||||
|
free(name);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (!t || (t->tag != TYPE_STRUCT && t->tag != TYPE_UNION)) {
|
if (!t || (t->tag != TYPE_STRUCT && t->tag != TYPE_UNION)) {
|
||||||
error(node, "invalid expression.");
|
error(node, "invalid expression.");
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
@ -557,6 +585,36 @@ static type *get_expression_type(sema *s, ast_node *node)
|
||||||
return t;
|
return t;
|
||||||
case NODE_ARRAY_SUBSCRIPT:
|
case NODE_ARRAY_SUBSCRIPT:
|
||||||
t = get_expression_type(s, node->expr.subscript.expr);
|
t = get_expression_type(s, node->expr.subscript.expr);
|
||||||
|
|
||||||
|
// Check if this is range subscripting (creates a slice)
|
||||||
|
if (node->expr.subscript.index && node->expr.subscript.index->type == NODE_RANGE) {
|
||||||
|
type *element_type = NULL;
|
||||||
|
switch (t->tag) {
|
||||||
|
case TYPE_SLICE:
|
||||||
|
element_type = t->data.slice.child;
|
||||||
|
break;
|
||||||
|
case TYPE_PTR:
|
||||||
|
element_type = t->data.ptr.child;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
error(node, "only pointers and slices can be indexed.");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return a slice type
|
||||||
|
type *slice_type = arena_alloc(s->allocator, sizeof(type));
|
||||||
|
slice_type->tag = TYPE_SLICE;
|
||||||
|
slice_type->size = sizeof(usize) * 2;
|
||||||
|
slice_type->alignment = sizeof(usize);
|
||||||
|
slice_type->data.slice.child = element_type;
|
||||||
|
slice_type->data.slice.is_const = false;
|
||||||
|
slice_type->data.slice.len = 0;
|
||||||
|
|
||||||
|
node->expr_type = slice_type;
|
||||||
|
return slice_type;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Regular subscript - return element type
|
||||||
switch (t->tag) {
|
switch (t->tag) {
|
||||||
case TYPE_SLICE:
|
case TYPE_SLICE:
|
||||||
t = t->data.slice.child;
|
t = t->data.slice.child;
|
||||||
|
|
@ -732,6 +790,18 @@ static void check_statement(sema *s, ast_node *node)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (t->tag == TYPE_STRUCT) {
|
if (t->tag == TYPE_STRUCT) {
|
||||||
|
// Struct initialization with NODE_STRUCT_INIT is allowed
|
||||||
|
} else if (node->expr.var_decl.value && node->expr.var_decl.value->type == NODE_STRUCT_INIT &&
|
||||||
|
(t->tag == TYPE_SLICE || t->tag == TYPE_PTR)) {
|
||||||
|
// Array/slice initialization with NODE_STRUCT_INIT is allowed
|
||||||
|
} else if (node->expr.var_decl.value && node->expr.var_decl.value->type == NODE_RANGE &&
|
||||||
|
t->tag == TYPE_SLICE) {
|
||||||
|
// Range initialization for slices is allowed
|
||||||
|
get_expression_type(s, node->expr.var_decl.value);
|
||||||
|
} else if (node->expr.var_decl.value && node->expr.var_decl.value->type == NODE_STRING &&
|
||||||
|
t->tag == TYPE_SLICE) {
|
||||||
|
// String literal can be assigned to slice
|
||||||
|
get_expression_type(s, node->expr.var_decl.value);
|
||||||
} else if (!can_cast(get_expression_type(s, node->expr.var_decl.value), t) && !match(t, get_expression_type(s, node->expr.var_decl.value))) {
|
} else if (!can_cast(get_expression_type(s, node->expr.var_decl.value), t) && !match(t, get_expression_type(s, node->expr.var_decl.value))) {
|
||||||
error(node, "type mismatch (decl).");
|
error(node, "type mismatch (decl).");
|
||||||
}
|
}
|
||||||
|
|
@ -763,11 +833,14 @@ static void check_function(sema *s, ast_node *f)
|
||||||
param = param->next;
|
param = param->next;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Skip body checking for extern functions
|
||||||
|
if (!f->expr.function.is_extern && f->expr.function.body) {
|
||||||
ast_node *current = f->expr.function.body;
|
ast_node *current = f->expr.function.body;
|
||||||
while (current && current->type == NODE_UNIT) {
|
while (current && current->type == NODE_UNIT) {
|
||||||
check_statement(s, current->expr.unit_node.expr);
|
check_statement(s, current->expr.unit_node.expr);
|
||||||
current = current->expr.unit_node.next;
|
current = current->expr.unit_node.next;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pop_scope(s);
|
pop_scope(s);
|
||||||
}
|
}
|
||||||
|
|
@ -820,6 +893,7 @@ void sema_init(parser *p, arena *a)
|
||||||
register_type(s, "u16", create_integer(s, "u16", 16, false));
|
register_type(s, "u16", create_integer(s, "u16", 16, false));
|
||||||
register_type(s, "u32", create_integer(s, "u32", 32, false));
|
register_type(s, "u32", create_integer(s, "u32", 32, false));
|
||||||
register_type(s, "u64", create_integer(s, "u64", 64, false));
|
register_type(s, "u64", create_integer(s, "u64", 64, false));
|
||||||
|
register_type(s, "usize", create_integer(s, "usize", 64, false));
|
||||||
register_type(s, "i8", create_integer(s, "i8", 8, true));
|
register_type(s, "i8", create_integer(s, "i8", 8, true));
|
||||||
register_type(s, "i16", create_integer(s, "i16", 16, true));
|
register_type(s, "i16", create_integer(s, "i16", 16, true));
|
||||||
register_type(s, "i32", create_integer(s, "i32", 32, true));
|
register_type(s, "i32", create_integer(s, "i32", 32, true));
|
||||||
|
|
|
||||||
BIN
test
BIN
test
Binary file not shown.
22
test.l
22
test.l
|
|
@ -1,10 +1,20 @@
|
||||||
struct point {
|
extern i64 write(i32 fd, *u8 buf, u64 count);
|
||||||
i32 x,
|
extern void exit(i32 code);
|
||||||
i32 y
|
extern *u8 malloc(usize size);
|
||||||
}
|
|
||||||
|
|
||||||
i32 main()
|
i32 main()
|
||||||
{
|
{
|
||||||
point result = .{ x = 2, y = 1 };
|
[u8] message = "Hello world!\n";
|
||||||
return (result.y) + 2;
|
*u8 message_heap = malloc(message.len);
|
||||||
|
[u8] new_message = message_heap[0..13];
|
||||||
|
u32 i = 0;
|
||||||
|
|
||||||
|
loop while i < message.len {
|
||||||
|
new_message[i] = message[i];
|
||||||
|
i = i + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
write(1, new_message.ptr, new_message.len);
|
||||||
|
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
20
test.s
20
test.s
|
|
@ -1,20 +0,0 @@
|
||||||
.section .text
|
|
||||||
.global main
|
|
||||||
main:
|
|
||||||
push %rbp
|
|
||||||
mov %rsp, %rbp
|
|
||||||
sub $256, %rsp
|
|
||||||
mov $2, %rax
|
|
||||||
mov %eax, -8(%rbp)
|
|
||||||
mov $1, %rax
|
|
||||||
mov %eax, -12(%rbp)
|
|
||||||
mov -12(%rbp), %rax
|
|
||||||
mov %rax, %rcx
|
|
||||||
mov $2, %rax
|
|
||||||
add %rcx, %rax
|
|
||||||
mov %rbp, %rsp
|
|
||||||
pop %rbp
|
|
||||||
ret
|
|
||||||
mov %rbp, %rsp
|
|
||||||
pop %rbp
|
|
||||||
ret
|
|
||||||
BIN
test_control
BIN
test_control
Binary file not shown.
|
|
@ -1,23 +0,0 @@
|
||||||
i32 main()
|
|
||||||
{
|
|
||||||
i32 x = 0;
|
|
||||||
i32 i = 0;
|
|
||||||
|
|
||||||
// Test while loop with break
|
|
||||||
while (i < 10) {
|
|
||||||
i = i + 1;
|
|
||||||
if (i == 5) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
x = x + i;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Test goto and label
|
|
||||||
if (x == 10) {
|
|
||||||
goto skip;
|
|
||||||
}
|
|
||||||
x = 999;
|
|
||||||
|
|
||||||
skip:
|
|
||||||
return x;
|
|
||||||
}
|
|
||||||
BIN
test_simple
BIN
test_simple
Binary file not shown.
|
|
@ -1,13 +0,0 @@
|
||||||
i32 main()
|
|
||||||
{
|
|
||||||
i32 x = 0;
|
|
||||||
|
|
||||||
while (x < 10) {
|
|
||||||
x = x + 1;
|
|
||||||
if (x == 5) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return x;
|
|
||||||
}
|
|
||||||
2
todo.cfg
2
todo.cfg
|
|
@ -1,2 +0,0 @@
|
||||||
export TODO_DIR="."
|
|
||||||
export TODO_FILE="$TODO_DIR/todo.txt"
|
|
||||||
1
todo.txt
1
todo.txt
|
|
@ -1 +0,0 @@
|
||||||
implement dominator tree for control flow
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue