fix: str+str 运行时拼接 — malloc + strlen + memcpy

- codegen: 声明 CRT 的 malloc/strlen/memcpy
- str+str 拼接: strlen(l)+strlen(r)+1 → malloc → memcpy×2 → 返回指针
- 新增集成测试 08_str_concat.l ("Hello, " + "World!" → "Hello, World!")
- 修复自报告 §5-6 字符串拼接不工作的 bug
This commit is contained in:
2026-06-05 02:36:23 +08:00
parent 9ff2990724
commit 9e41b09318
3 changed files with 335 additions and 2 deletions
+60 -2
View File
@@ -31,6 +31,10 @@ typedef struct {
// printf 运行时支持(内置 print 函数委托给 printf
LLVMValueRef printf_fn;
LLVMTypeRef printf_ty;
// 字符串拼接运行时支持
LLVMValueRef malloc_fn;
LLVMValueRef strlen_fn;
LLVMValueRef memcpy_fn;
} CgCtx;
// === 类型映射(需要 Context===
@@ -124,8 +128,39 @@ static LLVMValueRef codegen_expr(CgCtx* ctx, AstNode* node) {
LLVMValueRef r = codegen_expr(ctx, node->as.binary.right);
if (!l || !r) return NULL;
// 字符串拼接:暂不支持运行时拼接,直接返回左操作数
if (node->type.kind == TYPE_STR) return l;
// 字符串拼接:alloc 栈缓冲区,strcpy + strcat
if (node->type.kind == TYPE_STR) {
// strlen(left)
LLVMValueRef len_l = LLVMBuildCall2(ctx->builder,
LLVMGlobalGetValueType(ctx->strlen_fn), ctx->strlen_fn,
(LLVMValueRef[]){l}, 1, "strlen_l");
// strlen(right)
LLVMValueRef len_r = LLVMBuildCall2(ctx->builder,
LLVMGlobalGetValueType(ctx->strlen_fn), ctx->strlen_fn,
(LLVMValueRef[]){r}, 1, "strlen_r");
// total = len_l + len_r + 1
LLVMValueRef total = LLVMBuildAdd(ctx->builder, len_l, len_r, "total");
total = LLVMBuildAdd(ctx->builder, total,
LLVMConstInt(LLVMInt64TypeInContext(ctx->context), 1, false), "total_1");
// char* buf = malloc(total)
LLVMValueRef buf = LLVMBuildCall2(ctx->builder,
LLVMGlobalGetValueType(ctx->malloc_fn), ctx->malloc_fn,
(LLVMValueRef[]){total}, 1, "str_buf");
// memcpy(buf, left, len_l)
LLVMBuildCall2(ctx->builder,
LLVMGlobalGetValueType(ctx->memcpy_fn), ctx->memcpy_fn,
(LLVMValueRef[]){buf, l, len_l}, 3, "");
// memcpy(buf + len_l, right, len_r + 1) -- includes null terminator
LLVMValueRef offset_ptr = LLVMBuildGEP2(ctx->builder,
LLVMInt8TypeInContext(ctx->context), buf,
(LLVMValueRef[]){len_l}, 1, "offset");
LLVMValueRef len_r1 = LLVMBuildAdd(ctx->builder, len_r,
LLVMConstInt(LLVMInt64TypeInContext(ctx->context), 1, false), "len_r1");
LLVMBuildCall2(ctx->builder,
LLVMGlobalGetValueType(ctx->memcpy_fn), ctx->memcpy_fn,
(LLVMValueRef[]){offset_ptr, r, len_r1}, 3, "");
return buf;
}
bool is_float = (node->type.kind == TYPE_F64);
@@ -352,6 +387,29 @@ LLVMModuleRef codegen_module(AstNode* ast, Arena* codegen_arena,
LLVMInt32TypeInContext(ctx.context), printf_param_types, 1, true);
ctx.printf_fn = LLVMAddFunction(ctx.module, "printf", ctx.printf_ty);
// 声明 malloc: void* malloc(size_t)
LLVMTypeRef malloc_args[] = { LLVMInt64TypeInContext(ctx.context) };
LLVMTypeRef malloc_ty = LLVMFunctionType(
LLVMPointerType(LLVMInt8TypeInContext(ctx.context), 0), malloc_args, 1, false);
ctx.malloc_fn = LLVMAddFunction(ctx.module, "malloc", malloc_ty);
// 声明 strlen: size_t strlen(const char*)
LLVMTypeRef strlen_args[] = { LLVMPointerType(LLVMInt8TypeInContext(ctx.context), 0) };
LLVMTypeRef strlen_ty = LLVMFunctionType(
LLVMInt64TypeInContext(ctx.context), strlen_args, 1, false);
ctx.strlen_fn = LLVMAddFunction(ctx.module, "strlen", strlen_ty);
// 声明 memcpy: void* memcpy(void*, const void*, size_t)
LLVMTypeRef memcpy_args[] = {
LLVMPointerType(LLVMInt8TypeInContext(ctx.context), 0),
LLVMPointerType(LLVMInt8TypeInContext(ctx.context), 0),
LLVMInt64TypeInContext(ctx.context),
};
LLVMTypeRef memcpy_ty = LLVMFunctionType(
LLVMPointerType(LLVMInt8TypeInContext(ctx.context), 0),
memcpy_args, 3, false);
ctx.memcpy_fn = LLVMAddFunction(ctx.module, "memcpy", memcpy_ty);
// 第一遍:声明所有 L 函数
for (size_t i = 0; i < ast->as.program.fn_count; i++) {
AstNode* fn = ast->as.program.functions[i];