diff options
| author | nsfisis <nsfisis@gmail.com> | 2025-08-02 14:19:07 +0900 |
|---|---|---|
| committer | nsfisis <nsfisis@gmail.com> | 2025-08-15 10:06:21 +0900 |
| commit | bb484d2310acf5c31792cfc16b887f4746a64816 (patch) | |
| tree | f3c4f588210edb3f19a114f8b9606896c7b69f1d | |
| parent | 000e9d54435081bd40f877b319658e44dc45c7e0 (diff) | |
| download | ducc-bb484d2310acf5c31792cfc16b887f4746a64816.tar.gz ducc-bb484d2310acf5c31792cfc16b887f4746a64816.tar.zst ducc-bb484d2310acf5c31792cfc16b887f4746a64816.zip | |
feat: implement postfix increment/decrement operator
| -rw-r--r-- | codegen.c | 15 | ||||
| -rw-r--r-- | parse.c | 54 | ||||
| -rw-r--r-- | tests/080.sh | 30 |
3 files changed, 99 insertions, 0 deletions
@@ -335,6 +335,19 @@ void codegen_gvar(CodeGen* g, AstNode* ast, GenMode gen_mode) { printf(" push rax\n"); } +void codegen_composite_expr(CodeGen* g, AstNode* ast) { + // Standard C does not have composite expression, but ducc internally has. + int i; + for (i = 0; i < ast->node_len; ++i) { + AstNode* expr = ast->node_items + i; + codegen_expr(g, expr, GenMode_rval); + if (i != ast->node_len - 1) { + // TODO: the expression on the stack can be more than 8 bytes. + printf(" pop rax\n"); + } + } +} + void codegen_expr(CodeGen* g, AstNode* ast, GenMode gen_mode) { if (ast->kind == AstNodeKind_int_expr) { codegen_int_expr(g, ast); @@ -358,6 +371,8 @@ void codegen_expr(CodeGen* g, AstNode* ast, GenMode gen_mode) { codegen_lvar(g, ast, gen_mode); } else if (ast->kind == AstNodeKind_gvar) { codegen_gvar(g, ast, gen_mode); + } else if (ast->kind == AstNodeKind_list) { + codegen_composite_expr(g, ast); } else { unreachable(); } @@ -120,6 +120,28 @@ int add_lvar(Parser* p, String* name, Type* ty, int is_param) { return stack_offset; } +String* generate_temporary_lvar_name(Parser* p) { + String* ret = calloc(1, sizeof(String)); + ret->data = calloc(256, sizeof(char)); + int i; + for (i = 1;; ++i) { + ret->len = sprintf(ret->data, "__%d", i); + if (find_lvar(p, ret) == -1) { + return ret; + } + } +} + +AstNode* generate_temporary_lvar(Parser* p, Type* ty) { + String* name = generate_temporary_lvar_name(p); + int stack_offset = add_lvar(p, name, ty, 0); + AstNode* lvar = ast_new(AstNodeKind_lvar); + lvar->name = *name; + lvar->node_stack_offset = stack_offset; + lvar->ty = ty; + return lvar; +} + int find_gvar(Parser* p, const String* name) { int i; for (i = 0; i < p->n_gvars; ++i) { @@ -272,6 +294,32 @@ AstNode* parse_arg_list(Parser* p) { return list; } +// e++ +// tmp1 = &e; tmp2 = *tmp1; *tmp1 += 1; tmp2 +// e-- +// tmp1 = &e; tmp2 = *tmp1; *tmp1 -= 1; tmp2 +AstNode* create_new_postfix_inc_or_dec(Parser* p, AstNode* e, TokenKind op) { + AstNode* tmp1_lvar = generate_temporary_lvar(p, type_new_ptr(e->ty)); + AstNode* tmp2_lvar = generate_temporary_lvar(p, e->ty); + + AstNode* expr1 = ast_new_assign_expr(TokenKind_assign, tmp1_lvar, ast_new_ref_expr(e)); + AstNode* expr2 = ast_new_assign_expr(TokenKind_assign, tmp2_lvar, ast_new_deref_expr(tmp1_lvar)); + AstNode* expr3; + if (op == TokenKind_plusplus) { + expr3 = ast_new_assign_add_expr(ast_new_deref_expr(tmp1_lvar), ast_new_int(1)); + } else { + expr3 = ast_new_assign_sub_expr(ast_new_deref_expr(tmp1_lvar), ast_new_int(1)); + } + AstNode* expr4 = tmp2_lvar; + + AstNode* ret = ast_new_list(4); + ast_append(ret, expr1); + ast_append(ret, expr2); + ast_append(ret, expr3); + ast_append(ret, expr4); + return ret; +} + AstNode* parse_postfix_expr(Parser* p) { AstNode* ret = parse_primary_expr(p); String* name; @@ -296,6 +344,12 @@ AstNode* parse_postfix_expr(Parser* p) { next_token(p); name = parse_ident(p); ret = ast_new_member_access_expr(ret, name); + } else if (tk == TokenKind_plusplus) { + next_token(p); + ret = create_new_postfix_inc_or_dec(p, ret, tk); + } else if (tk == TokenKind_minusminus) { + next_token(p); + ret = create_new_postfix_inc_or_dec(p, ret, tk); } else { break; } diff --git a/tests/080.sh b/tests/080.sh new file mode 100644 index 0000000..ed649da --- /dev/null +++ b/tests/080.sh @@ -0,0 +1,30 @@ +set -e + +cat <<'EOF' > expected +44 +44 +46 +46 +44 +42 +42 +EOF + +bash ../../test_diff.sh <<'EOF' +int printf(); + +int main() { + int a = 42; + ++a; + a++; + printf("%d\n", a); + printf("%d\n", a++); + printf("%d\n", ++a); + printf("%d\n", a); + --a; + a--; + printf("%d\n", a--); + printf("%d\n", --a); + printf("%d\n", a); +} +EOF |
