feat: 字符串类型 + 字面量 + print_str

- 新增 TYPE_STR 类型 (i8* 指针)
- lexer: 双引号字符串字面量 + str 关键字
- parser: TOK_STR_LIT → AST_LITERAL_EXPR(str_val)
- sema: print_str 内置函数注册 + 字符串拼接类型检查
- codegen: GlobalStringPtr 生成字符串常量,print_str → printf("%s")
- 新增集成测试 07_hello_str.l

基于 Codex 分析报告 P0 建议。
This commit is contained in:
2026-06-05 00:47:53 +08:00
parent bd02a4989e
commit 9a53d97274
10 changed files with 84 additions and 11 deletions
+14 -1
View File
@@ -59,7 +59,8 @@ static TokenKind check_keyword(const Token* tok) {
KW("if", TOK_IF); KW("else", TOK_ELSE);
KW("while", TOK_WHILE); KW("return", TOK_RETURN);
KW("i64", TOK_I64); KW("f64", TOK_F64);
KW("bool", TOK_BOOL); KW("void", TOK_VOID);
KW("bool", TOK_BOOL); KW("str", TOK_STR);
KW("void", TOK_VOID);
KW("true", TOK_TRUE); KW("false", TOK_FALSE);
#undef KW
return TOK_IDENT;
@@ -90,6 +91,18 @@ Token* lex(Arena* a, const char* source, const char* filename,
char c = peek(&l);
if (isdigit(c)) { tokens[idx++] = lex_number(&l); }
else if (c == '"') {
advance(&l); // 跳过开头的 "
int start = l.pos;
while (peek(&l) != '"' && peek(&l) != '\0' && peek(&l) != '\n') advance(&l);
int len = l.pos - start;
if (peek(&l) != '"') {
*error = (ErrorInfo){.message="未闭合的字符串", .filename=filename, .line=line, .col=col};
return NULL;
}
advance(&l); // 跳过结尾的 "
tokens[idx++] = make_token(&l, TOK_STR_LIT, start, len);
}
else if (isalpha(c) || c == '_') { tokens[idx++] = lex_ident_or_keyword(&l); }
else if (c == '+' && peek_next(&l) != '=') { tokens[idx++] = make_token(&l, TOK_PLUS, l.pos, 1); advance(&l); }
else if (c == '-' && peek_next(&l) != '>') { tokens[idx++] = make_token(&l, TOK_MINUS, l.pos, 1); advance(&l); }