feat: 模块系统 mod + pub — 多文件编译支持
This commit is contained in:
+17
-1
@@ -30,12 +30,14 @@ AstNode* ast_make_program(void* alloc, AstNode** fns, size_t fn_count,
|
|||||||
}
|
}
|
||||||
|
|
||||||
AstNode* ast_make_function(void* alloc, const char* name, AstNode** params, size_t pcount,
|
AstNode* ast_make_function(void* alloc, const char* name, AstNode** params, size_t pcount,
|
||||||
TypeKind ret, const char* ret_struct_name, AstNode* body, SourceLoc loc) {
|
TypeKind ret, const char* ret_struct_name, AstNode* body,
|
||||||
|
bool is_pub, SourceLoc loc) {
|
||||||
NEW(alloc, AST_FUNCTION);
|
NEW(alloc, AST_FUNCTION);
|
||||||
n->as.function.name = name; n->as.function.params = params;
|
n->as.function.name = name; n->as.function.params = params;
|
||||||
n->as.function.param_count = pcount; n->as.function.return_type = ret;
|
n->as.function.param_count = pcount; n->as.function.return_type = ret;
|
||||||
n->as.function.return_struct_type_name = ret_struct_name;
|
n->as.function.return_struct_type_name = ret_struct_name;
|
||||||
n->as.function.body = body;
|
n->as.function.body = body;
|
||||||
|
n->as.function.is_pub = is_pub;
|
||||||
return n;
|
return n;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -255,3 +257,17 @@ AstNode* ast_make_method_call(void* alloc, AstNode* receiver, const char* method
|
|||||||
n->as.method_call.arg_count = count;
|
n->as.method_call.arg_count = count;
|
||||||
return n;
|
return n;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
AstNode* ast_make_mod_decl(void* alloc, const char* name, AstNode* sub_ast, SourceLoc loc) {
|
||||||
|
NEW(alloc, AST_MOD_DECL);
|
||||||
|
n->as.mod_decl.name = name;
|
||||||
|
n->as.mod_decl.ast = sub_ast;
|
||||||
|
return n;
|
||||||
|
}
|
||||||
|
|
||||||
|
AstNode* ast_make_use_decl(void* alloc, const char* path, const char* item, SourceLoc loc) {
|
||||||
|
NEW(alloc, AST_USE_DECL);
|
||||||
|
n->as.use_decl.path = path;
|
||||||
|
n->as.use_decl.item = item;
|
||||||
|
return n;
|
||||||
|
}
|
||||||
|
|||||||
+11
-2
@@ -30,6 +30,8 @@ typedef enum {
|
|||||||
AST_ARRAY_ASSIGN_STMT,// arr[i] = expr
|
AST_ARRAY_ASSIGN_STMT,// arr[i] = expr
|
||||||
AST_IMPL_BLOCK, // impl StructName { fn method(...) ... }
|
AST_IMPL_BLOCK, // impl StructName { fn method(...) ... }
|
||||||
AST_METHOD_CALL, // receiver.method(args)
|
AST_METHOD_CALL, // receiver.method(args)
|
||||||
|
AST_MOD_DECL, // mod foo;
|
||||||
|
AST_USE_DECL, // use foo::bar;
|
||||||
} AstKind;
|
} AstKind;
|
||||||
|
|
||||||
typedef enum {
|
typedef enum {
|
||||||
@@ -65,7 +67,7 @@ struct AstNode {
|
|||||||
// AST_FUNCTION
|
// AST_FUNCTION
|
||||||
struct { const char* name; struct AstNode** params; size_t param_count;
|
struct { const char* name; struct AstNode** params; size_t param_count;
|
||||||
TypeKind return_type; const char* return_struct_type_name;
|
TypeKind return_type; const char* return_struct_type_name;
|
||||||
struct AstNode* body; } function;
|
struct AstNode* body; bool is_pub; } function;
|
||||||
// AST_PARAMETER (也用作结构体字段: name + type)
|
// AST_PARAMETER (也用作结构体字段: name + type)
|
||||||
struct { const char* name; TypeKind type; const char* struct_type_name; } parameter;
|
struct { const char* name; TypeKind type; const char* struct_type_name; } parameter;
|
||||||
// AST_BLOCK
|
// AST_BLOCK
|
||||||
@@ -120,6 +122,10 @@ struct AstNode {
|
|||||||
struct { const char* struct_name; struct AstNode** methods; size_t method_count; } impl_block;
|
struct { const char* struct_name; struct AstNode** methods; size_t method_count; } impl_block;
|
||||||
// AST_METHOD_CALL
|
// AST_METHOD_CALL
|
||||||
struct { struct AstNode* receiver; const char* method_name; struct AstNode** args; const char** arg_names; size_t arg_count; } method_call;
|
struct { struct AstNode* receiver; const char* method_name; struct AstNode** args; const char** arg_names; size_t arg_count; } method_call;
|
||||||
|
// AST_MOD_DECL
|
||||||
|
struct { const char* name; struct AstNode* ast; } mod_decl;
|
||||||
|
// AST_USE_DECL
|
||||||
|
struct { const char* path; const char* item; } use_decl;
|
||||||
} as;
|
} as;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -130,7 +136,8 @@ AstNode* ast_make_program(void* alloc, AstNode** fns, size_t fn_count,
|
|||||||
AstNode** enums, size_t enum_count,
|
AstNode** enums, size_t enum_count,
|
||||||
AstNode** impls, size_t impl_count, SourceLoc loc);
|
AstNode** impls, size_t impl_count, SourceLoc loc);
|
||||||
AstNode* ast_make_function(void* alloc, const char* name, AstNode** params, size_t pcount,
|
AstNode* ast_make_function(void* alloc, const char* name, AstNode** params, size_t pcount,
|
||||||
TypeKind ret, const char* ret_struct_name, AstNode* body, SourceLoc loc);
|
TypeKind ret, const char* ret_struct_name, AstNode* body,
|
||||||
|
bool is_pub, SourceLoc loc);
|
||||||
AstNode* ast_make_parameter(void* alloc, const char* name, TypeKind type, const char* struct_type_name, SourceLoc loc);
|
AstNode* ast_make_parameter(void* alloc, const char* name, TypeKind type, const char* struct_type_name, SourceLoc loc);
|
||||||
AstNode* ast_make_block(void* alloc, AstNode** stmts, size_t count, SourceLoc loc);
|
AstNode* ast_make_block(void* alloc, AstNode** stmts, size_t count, SourceLoc loc);
|
||||||
AstNode* ast_make_let(void* alloc, const char* name, TypeKind annot_type, bool has_type_annot,
|
AstNode* ast_make_let(void* alloc, const char* name, TypeKind annot_type, bool has_type_annot,
|
||||||
@@ -164,5 +171,7 @@ AstNode* ast_make_index_expr(void* alloc, AstNode* array, AstNode* index, Source
|
|||||||
AstNode* ast_make_array_assign(void* alloc, const char* name, AstNode* index, AstNode* value, SourceLoc loc);
|
AstNode* ast_make_array_assign(void* alloc, const char* name, AstNode* index, AstNode* value, SourceLoc loc);
|
||||||
AstNode* ast_make_impl_block(void* alloc, const char* struct_name, AstNode** methods, size_t count, SourceLoc loc);
|
AstNode* ast_make_impl_block(void* alloc, const char* struct_name, AstNode** methods, size_t count, SourceLoc loc);
|
||||||
AstNode* ast_make_method_call(void* alloc, AstNode* receiver, const char* method, AstNode** args, const char** arg_names, size_t count, SourceLoc loc);
|
AstNode* ast_make_method_call(void* alloc, AstNode* receiver, const char* method, AstNode** args, const char** arg_names, size_t count, SourceLoc loc);
|
||||||
|
AstNode* ast_make_mod_decl(void* alloc, const char* name, AstNode* sub_ast, SourceLoc loc);
|
||||||
|
AstNode* ast_make_use_decl(void* alloc, const char* path, const char* item, SourceLoc loc);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -66,6 +66,7 @@ static TokenKind check_keyword(const Token* tok) {
|
|||||||
KW("str", TOK_STR); KW("void", TOK_VOID);
|
KW("str", TOK_STR); KW("void", TOK_VOID);
|
||||||
KW("struct", TOK_STRUCT); KW("type", TOK_TYPE);
|
KW("struct", TOK_STRUCT); KW("type", TOK_TYPE);
|
||||||
KW("enum", TOK_ENUM); KW("extend", TOK_EXTEND); KW("match", TOK_MATCH);
|
KW("enum", TOK_ENUM); KW("extend", TOK_EXTEND); KW("match", TOK_MATCH);
|
||||||
|
KW("pub", TOK_PUB); KW("mod", TOK_MOD); KW("use", TOK_USE);
|
||||||
KW("_", TOK_UNDERSCORE);
|
KW("_", TOK_UNDERSCORE);
|
||||||
KW("true", TOK_TRUE); KW("false", TOK_FALSE);
|
KW("true", TOK_TRUE); KW("false", TOK_FALSE);
|
||||||
#undef KW
|
#undef KW
|
||||||
|
|||||||
@@ -6,6 +6,7 @@
|
|||||||
|
|
||||||
static const char* NAMES[] = {
|
static const char* NAMES[] = {
|
||||||
[TOK_FN] = "fn", [TOK_LET] = "let", [TOK_VAR] = "var", [TOK_IF] = "if", [TOK_GUARD] = "guard",
|
[TOK_FN] = "fn", [TOK_LET] = "let", [TOK_VAR] = "var", [TOK_IF] = "if", [TOK_GUARD] = "guard",
|
||||||
|
[TOK_PUB] = "pub", [TOK_MOD] = "mod", [TOK_USE] = "use",
|
||||||
[TOK_ELSE] = "else", [TOK_WHILE] = "while", [TOK_FOR] = "for", [TOK_IN] = "in", [TOK_RETURN] = "return",
|
[TOK_ELSE] = "else", [TOK_WHILE] = "while", [TOK_FOR] = "for", [TOK_IN] = "in", [TOK_RETURN] = "return",
|
||||||
[TOK_STRUCT] = "struct", [TOK_TYPE] = "type", [TOK_ENUM] = "enum", [TOK_EXTEND] = "extend",
|
[TOK_STRUCT] = "struct", [TOK_TYPE] = "type", [TOK_ENUM] = "enum", [TOK_EXTEND] = "extend",
|
||||||
[TOK_MATCH] = "match",
|
[TOK_MATCH] = "match",
|
||||||
|
|||||||
+1
-1
@@ -7,7 +7,7 @@
|
|||||||
typedef enum {
|
typedef enum {
|
||||||
// 关键字
|
// 关键字
|
||||||
TOK_FN, TOK_LET, TOK_VAR, TOK_IF, TOK_ELSE, TOK_WHILE, TOK_FOR, TOK_IN, TOK_RETURN, TOK_GUARD,
|
TOK_FN, TOK_LET, TOK_VAR, TOK_IF, TOK_ELSE, TOK_WHILE, TOK_FOR, TOK_IN, TOK_RETURN, TOK_GUARD,
|
||||||
TOK_STRUCT, TOK_TYPE, TOK_ENUM, TOK_EXTEND, TOK_MATCH,
|
TOK_STRUCT, TOK_TYPE, TOK_ENUM, TOK_EXTEND, TOK_MATCH, TOK_PUB, TOK_MOD, TOK_USE,
|
||||||
// 类型关键字
|
// 类型关键字
|
||||||
TOK_I32, TOK_I64, TOK_U64, TOK_F64, TOK_BOOL, TOK_CHAR, TOK_STR, TOK_VOID,
|
TOK_I32, TOK_I64, TOK_U64, TOK_F64, TOK_BOOL, TOK_CHAR, TOK_STR, TOK_VOID,
|
||||||
// 字面量
|
// 字面量
|
||||||
|
|||||||
+145
-7
@@ -1,4 +1,5 @@
|
|||||||
#include "parser.h"
|
#include "parser.h"
|
||||||
|
#include "lexer.h"
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
@@ -72,7 +73,7 @@ static AstNode* parse_expr(Parser* p, ErrorInfo* error);
|
|||||||
static AstNode* parse_expr_prec(Parser* p, Precedence prec, ErrorInfo* error);
|
static AstNode* parse_expr_prec(Parser* p, Precedence prec, ErrorInfo* error);
|
||||||
static AstNode* parse_block(Parser* p, ErrorInfo* error);
|
static AstNode* parse_block(Parser* p, ErrorInfo* error);
|
||||||
static AstNode* parse_statement(Parser* p, ErrorInfo* error);
|
static AstNode* parse_statement(Parser* p, ErrorInfo* error);
|
||||||
static AstNode* parse_function(Parser* p, ErrorInfo* error);
|
static AstNode* parse_function(Parser* p, bool is_pub, ErrorInfo* error);
|
||||||
|
|
||||||
// === 前缀解析 ===
|
// === 前缀解析 ===
|
||||||
static AstNode* parse_unary(Parser* p, ErrorInfo* error) {
|
static AstNode* parse_unary(Parser* p, ErrorInfo* error) {
|
||||||
@@ -188,11 +189,56 @@ static AstNode* parse_struct_init(Parser* p, const Token* name, ErrorInfo* error
|
|||||||
static AstNode* parse_ident_or_call(Parser* p, ErrorInfo* error) {
|
static AstNode* parse_ident_or_call(Parser* p, ErrorInfo* error) {
|
||||||
const Token* name = advance(p);
|
const Token* name = advance(p);
|
||||||
|
|
||||||
// 枚举变体引用: Name::Variant 或 Name::Variant(payload)
|
// 枚举变体或模块函数: Name::Variant 或 Name::fn
|
||||||
if (peek(p)->kind == TOK_COLON_COLON) {
|
if (peek(p)->kind == TOK_COLON_COLON) {
|
||||||
advance(p); // 跳过 ::
|
advance(p); // 跳过 ::
|
||||||
const Token* variant = expect(p, TOK_IDENT, error, "枚举变体名");
|
const Token* variant = expect(p, TOK_IDENT, error, "枚举变体名");
|
||||||
if (!variant) return NULL;
|
if (!variant) return NULL;
|
||||||
|
// Name::fn 或 Name::Variant 或 Name::Variant(payload)
|
||||||
|
if (peek(p)->kind == TOK_LPAREN) {
|
||||||
|
// 前进探测: 检查括号内是否有多参数或命名参数(→函数调用)还是单表达式(→枚举payload)
|
||||||
|
size_t probe = p->pos + 1;
|
||||||
|
int paren_depth = 1;
|
||||||
|
bool has_comma = false, has_named = false;
|
||||||
|
while (paren_depth > 0 && p->tokens[probe].kind != TOK_EOF) {
|
||||||
|
if (p->tokens[probe].kind == TOK_LPAREN) paren_depth++;
|
||||||
|
else if (p->tokens[probe].kind == TOK_RPAREN) { paren_depth--; if (paren_depth == 0) break; }
|
||||||
|
else if (paren_depth == 1 && p->tokens[probe].kind == TOK_COMMA) has_comma = true;
|
||||||
|
else if (paren_depth == 1 && p->tokens[probe].kind == TOK_COLON) has_named = true;
|
||||||
|
probe++;
|
||||||
|
}
|
||||||
|
if (has_comma || has_named) {
|
||||||
|
// 模块函数调用: Name::fn(a, b) 或 Name::fn(x: 1)
|
||||||
|
advance(p); // 跳过 '('
|
||||||
|
AstNode* args[16]; const char* arg_names[16]; int arg_count = 0;
|
||||||
|
bool seen_named = false;
|
||||||
|
while (peek(p)->kind != TOK_RPAREN && !error->message) {
|
||||||
|
if (arg_count >= 16) { error->message = "参数过多"; error->filename = p->filename; error->line = peek(p)->line; error->col = peek(p)->col; return NULL; }
|
||||||
|
if (peek(p)->kind == TOK_IDENT && (p->tokens[p->pos + 1].kind == TOK_COLON)) {
|
||||||
|
const Token* aname = advance(p); advance(p);
|
||||||
|
arg_names[arg_count] = arena_strdup_impl(p->arena, aname->start, aname->length);
|
||||||
|
seen_named = true;
|
||||||
|
} else {
|
||||||
|
if (seen_named) { error->message = "命名参数必须放在位置参数之后"; error->filename = p->filename; error->line = peek(p)->line; error->col = peek(p)->col; return NULL; }
|
||||||
|
arg_names[arg_count] = NULL;
|
||||||
|
}
|
||||||
|
args[arg_count] = parse_expr(p, error);
|
||||||
|
if (!args[arg_count]) return NULL;
|
||||||
|
arg_count++;
|
||||||
|
if (peek(p)->kind == TOK_COMMA) advance(p); else break;
|
||||||
|
}
|
||||||
|
if (!expect(p, TOK_RPAREN, error, "缺少 ')'")) return NULL;
|
||||||
|
AstNode** arg_arr = arena_alloc_impl(p->arena, arg_count * sizeof(AstNode*));
|
||||||
|
memcpy(arg_arr, args, arg_count * sizeof(AstNode*));
|
||||||
|
const char** name_arr = seen_named
|
||||||
|
? memcpy(arena_alloc_impl(p->arena, arg_count * sizeof(const char*)), arg_names, arg_count * sizeof(const char*))
|
||||||
|
: NULL;
|
||||||
|
char* full_name = arena_alloc_impl(p->arena, name->length + variant->length + 4);
|
||||||
|
sprintf(full_name, "%.*s::%.*s", name->length, name->start, variant->length, variant->start);
|
||||||
|
return ast_make_call(p->arena, full_name, arg_arr, name_arr, arg_count, tok_loc(name));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 枚举 payload: Name::Variant 或 Name::Variant(expr)
|
||||||
AstNode* payload = NULL;
|
AstNode* payload = NULL;
|
||||||
if (match(p, TOK_LPAREN)) {
|
if (match(p, TOK_LPAREN)) {
|
||||||
payload = parse_expr(p, error);
|
payload = parse_expr(p, error);
|
||||||
@@ -857,7 +903,7 @@ static AstNode* parse_statement(Parser* p, ErrorInfo* error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// === 函数解析 ===
|
// === 函数解析 ===
|
||||||
static AstNode* parse_function(Parser* p, ErrorInfo* error) {
|
static AstNode* parse_function(Parser* p, bool is_pub, ErrorInfo* error) {
|
||||||
const Token* fn_tok = advance(p); // fn
|
const Token* fn_tok = advance(p); // fn
|
||||||
const Token* name = expect(p, TOK_IDENT, error, "fn 后应为函数名");
|
const Token* name = expect(p, TOK_IDENT, error, "fn 后应为函数名");
|
||||||
if (!name) return NULL;
|
if (!name) return NULL;
|
||||||
@@ -897,7 +943,51 @@ static AstNode* parse_function(Parser* p, ErrorInfo* error) {
|
|||||||
memcpy(parr, params, pcount * sizeof(AstNode*));
|
memcpy(parr, params, pcount * sizeof(AstNode*));
|
||||||
return ast_make_function(p->arena,
|
return ast_make_function(p->arena,
|
||||||
arena_strdup_impl(p->arena, name->start, name->length),
|
arena_strdup_impl(p->arena, name->start, name->length),
|
||||||
parr, pcount, ret, ret_struct_name, body, tok_loc(fn_tok));
|
parr, pcount, ret, ret_struct_name, body, is_pub, tok_loc(fn_tok));
|
||||||
|
}
|
||||||
|
|
||||||
|
// === 模块文件加载辅助 ===
|
||||||
|
// parse 前向声明(定义在后面)
|
||||||
|
AstNode* parse(Arena* a, const Token* tokens, size_t count,
|
||||||
|
const char* filename, ErrorInfo* error);
|
||||||
|
|
||||||
|
static AstNode* load_module(Arena* a, const char* parent_file,
|
||||||
|
const char* mod_name, ErrorInfo* error) {
|
||||||
|
// 构造模块文件路径: 同目录下 mod_name.l
|
||||||
|
char mod_path[512];
|
||||||
|
const char* last_slash = strrchr(parent_file, '/');
|
||||||
|
const char* last_bs = strrchr(parent_file, '\\');
|
||||||
|
const char* dir_end = parent_file;
|
||||||
|
if (last_slash && last_slash > dir_end) dir_end = last_slash;
|
||||||
|
if (last_bs && last_bs > dir_end) dir_end = last_bs;
|
||||||
|
if (dir_end != parent_file) {
|
||||||
|
size_t dir_len = dir_end - parent_file + 1;
|
||||||
|
memcpy(mod_path, parent_file, dir_len);
|
||||||
|
snprintf(mod_path + dir_len, sizeof(mod_path) - dir_len, "%s.l", mod_name);
|
||||||
|
} else {
|
||||||
|
snprintf(mod_path, sizeof(mod_path), "%s.l", mod_name);
|
||||||
|
}
|
||||||
|
// 读取文件
|
||||||
|
FILE* f = fopen(mod_path, "rb");
|
||||||
|
if (!f) {
|
||||||
|
error->message = "无法打开模块文件"; error->filename = mod_path;
|
||||||
|
error->line = 0; error->col = 0;
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
fseek(f, 0, SEEK_END);
|
||||||
|
size_t sz = ftell(f); fseek(f, 0, SEEK_SET);
|
||||||
|
char* src = malloc(sz + 1);
|
||||||
|
if (!src) { fclose(f); return NULL; }
|
||||||
|
fread(src, 1, sz, f); src[sz] = '\0'; fclose(f);
|
||||||
|
|
||||||
|
size_t tc;
|
||||||
|
ErrorInfo lex_err = {0};
|
||||||
|
Token* toks = lex(a, src, mod_path, &tc, &lex_err);
|
||||||
|
free(src);
|
||||||
|
if (!toks) { *error = lex_err; return NULL; }
|
||||||
|
|
||||||
|
AstNode* ast = parse(a, toks, tc, mod_path, error);
|
||||||
|
return ast;
|
||||||
}
|
}
|
||||||
|
|
||||||
// === 程序入口 ===
|
// === 程序入口 ===
|
||||||
@@ -910,7 +1000,14 @@ AstNode* parse(Arena* a, const Token* tokens, size_t count,
|
|||||||
AstNode* aliases[64]; int alias_count = 0;
|
AstNode* aliases[64]; int alias_count = 0;
|
||||||
AstNode* enums[64]; int enum_count = 0;
|
AstNode* enums[64]; int enum_count = 0;
|
||||||
AstNode* impls[64]; int impl_count = 0;
|
AstNode* impls[64]; int impl_count = 0;
|
||||||
|
AstNode* mods[64]; int mod_count = 0;
|
||||||
|
AstNode* uses[64]; int use_count = 0;
|
||||||
while (peek(&p)->kind != TOK_EOF && !error->message) {
|
while (peek(&p)->kind != TOK_EOF && !error->message) {
|
||||||
|
// pub 前缀
|
||||||
|
bool is_pub = false;
|
||||||
|
if (peek(&p)->kind == TOK_PUB) {
|
||||||
|
is_pub = true; advance(&p);
|
||||||
|
}
|
||||||
if (peek(&p)->kind == TOK_STRUCT) {
|
if (peek(&p)->kind == TOK_STRUCT) {
|
||||||
if (struct_count >= 64) { error->message = "结构体过多 (最多64)"; error->filename = p.filename; error->line = peek(&p)->line; error->col = peek(&p)->col; return NULL; }
|
if (struct_count >= 64) { error->message = "结构体过多 (最多64)"; error->filename = p.filename; error->line = peek(&p)->line; error->col = peek(&p)->col; return NULL; }
|
||||||
structs[struct_count++] = parse_struct_decl(&p, error);
|
structs[struct_count++] = parse_struct_decl(&p, error);
|
||||||
@@ -975,7 +1072,7 @@ AstNode* parse(Arena* a, const Token* tokens, size_t count,
|
|||||||
while (peek(&p)->kind != TOK_RBRACE && !error->message) {
|
while (peek(&p)->kind != TOK_RBRACE && !error->message) {
|
||||||
if (mcount >= 64) { error->message = "方法过多 (最多64)"; error->filename = p.filename; error->line = peek(&p)->line; error->col = peek(&p)->col; return NULL; }
|
if (mcount >= 64) { error->message = "方法过多 (最多64)"; error->filename = p.filename; error->line = peek(&p)->line; error->col = peek(&p)->col; return NULL; }
|
||||||
if (peek(&p)->kind != TOK_FN) { error->message = "extend 块内只允许 fn"; error->filename = p.filename; error->line = peek(&p)->line; error->col = peek(&p)->col; return NULL; }
|
if (peek(&p)->kind != TOK_FN) { error->message = "extend 块内只允许 fn"; error->filename = p.filename; error->line = peek(&p)->line; error->col = peek(&p)->col; return NULL; }
|
||||||
methods[mcount++] = parse_function(&p, error);
|
methods[mcount++] = parse_function(&p, false, error);
|
||||||
}
|
}
|
||||||
if (!expect(&p, TOK_RBRACE, error, "缺少 '}'")) return NULL;
|
if (!expect(&p, TOK_RBRACE, error, "缺少 '}'")) return NULL;
|
||||||
AstNode** m_arr = arena_alloc_impl(p.arena, mcount * sizeof(AstNode*));
|
AstNode** m_arr = arena_alloc_impl(p.arena, mcount * sizeof(AstNode*));
|
||||||
@@ -984,11 +1081,52 @@ AstNode* parse(Arena* a, const Token* tokens, size_t count,
|
|||||||
impls[impl_count++] = ast_make_impl_block(p.arena,
|
impls[impl_count++] = ast_make_impl_block(p.arena,
|
||||||
arena_strdup_impl(p.arena, st_name->start, st_name->length),
|
arena_strdup_impl(p.arena, st_name->start, st_name->length),
|
||||||
m_arr, mcount, tok_loc(i_tok));
|
m_arr, mcount, tok_loc(i_tok));
|
||||||
|
} else if (peek(&p)->kind == TOK_MOD) {
|
||||||
|
advance(&p);
|
||||||
|
const Token* mn = expect(&p, TOK_IDENT, error, "mod 后应为模块名");
|
||||||
|
if (!mn) return NULL;
|
||||||
|
if (!expect(&p, TOK_SEMICOLON, error, "缺少 ';'")) return NULL;
|
||||||
|
const char* mod_name = arena_strdup_impl(p.arena, mn->start, mn->length);
|
||||||
|
AstNode* sub = load_module(a, filename, mod_name, error);
|
||||||
|
if (!sub) return NULL;
|
||||||
|
// 合并子模块项到当前文件(以 mod_name:: 为前缀)
|
||||||
|
for (size_t i = 0; i < sub->as.program.fn_count; i++) {
|
||||||
|
AstNode* fn = sub->as.program.functions[i];
|
||||||
|
if (fn->as.function.is_pub) {
|
||||||
|
char* mangled = arena_alloc_impl(p.arena, strlen(mod_name) + strlen(fn->as.function.name) + 4);
|
||||||
|
sprintf(mangled, "%s::%s", mod_name, fn->as.function.name);
|
||||||
|
fn->as.function.name = mangled;
|
||||||
|
if (fn_count >= 256) { error->message = "函数过多"; error->filename = p.filename; return NULL; }
|
||||||
|
functions[fn_count++] = fn;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (size_t i = 0; i < sub->as.program.struct_count; i++) {
|
||||||
|
if (struct_count >= 64) break;
|
||||||
|
structs[struct_count++] = sub->as.program.structs[i];
|
||||||
|
}
|
||||||
|
for (size_t i = 0; i < sub->as.program.enum_count; i++) {
|
||||||
|
if (enum_count >= 64) break;
|
||||||
|
enums[enum_count++] = sub->as.program.enums[i];
|
||||||
|
}
|
||||||
|
if (mod_count < 64) mods[mod_count++] = ast_make_mod_decl(a, mod_name, sub, tok_loc(mn));
|
||||||
|
} else if (peek(&p)->kind == TOK_USE) {
|
||||||
|
advance(&p);
|
||||||
|
const Token* path_tok = expect(&p, TOK_IDENT, error, "use 后应为模块名");
|
||||||
|
if (!path_tok) return NULL;
|
||||||
|
if (!expect(&p, TOK_COLON_COLON, error, "缺少 '::'")) return NULL;
|
||||||
|
const Token* item_tok = expect(&p, TOK_IDENT, error, "use 后应为项目名");
|
||||||
|
if (!item_tok) return NULL;
|
||||||
|
if (!expect(&p, TOK_SEMICOLON, error, "缺少 ';'")) return NULL;
|
||||||
|
if (use_count >= 64) { error->message = "use 过多 (最多64)"; error->filename = p.filename; error->line = peek(&p)->line; error->col = peek(&p)->col; return NULL; }
|
||||||
|
uses[use_count++] = ast_make_use_decl(a,
|
||||||
|
arena_strdup_impl(p.arena, path_tok->start, path_tok->length),
|
||||||
|
arena_strdup_impl(p.arena, item_tok->start, item_tok->length),
|
||||||
|
tok_loc(path_tok));
|
||||||
} else if (peek(&p)->kind == TOK_FN) {
|
} else if (peek(&p)->kind == TOK_FN) {
|
||||||
if (fn_count >= 256) { error->message = "函数过多 (最多256)"; error->filename = p.filename; error->line = peek(&p)->line; error->col = peek(&p)->col; return NULL; }
|
if (fn_count >= 256) { error->message = "函数过多 (最多256)"; error->filename = p.filename; error->line = peek(&p)->line; error->col = peek(&p)->col; return NULL; }
|
||||||
functions[fn_count++] = parse_function(&p, error);
|
functions[fn_count++] = parse_function(&p, is_pub, error);
|
||||||
} else {
|
} else {
|
||||||
error->message = "顶层只允许 fn、struct、type、enum 或 extend";
|
error->message = "顶层只允许 fn、struct、type、enum、extend、mod 或 use";
|
||||||
error->filename = p.filename;
|
error->filename = p.filename;
|
||||||
error->line = peek(&p)->line;
|
error->line = peek(&p)->line;
|
||||||
error->col = peek(&p)->col;
|
error->col = peek(&p)->col;
|
||||||
|
|||||||
@@ -0,0 +1,7 @@
|
|||||||
|
mod math_mod;
|
||||||
|
|
||||||
|
fn main() -> i64 {
|
||||||
|
let result = math_mod::add(3, 4);
|
||||||
|
print_i64(result); // 7
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
pub fn add(a: i64, b: i64) -> i64 {
|
||||||
|
return a + b;
|
||||||
|
}
|
||||||
+11
-11
@@ -12,7 +12,7 @@ void test_codegen_simple_function() {
|
|||||||
AstNode* ret = ast_make_return(&a, ast_make_literal_i64(&a, 42, loc_at(1, 1)), loc_at(1, 1));
|
AstNode* ret = ast_make_return(&a, ast_make_literal_i64(&a, 42, loc_at(1, 1)), loc_at(1, 1));
|
||||||
AstNode* stmts[] = { ret };
|
AstNode* stmts[] = { ret };
|
||||||
AstNode* body = ast_make_block(&a, stmts, 1, loc_at(1, 1));
|
AstNode* body = ast_make_block(&a, stmts, 1, loc_at(1, 1));
|
||||||
AstNode* fn = ast_make_function(&a, "main", NULL, 0, TYPE_I64, NULL, body, loc_at(1, 1));
|
AstNode* fn = ast_make_function(&a, "main", NULL, 0, TYPE_I64, NULL, body, false, loc_at(1, 1));
|
||||||
AstNode* fns[] = { fn };
|
AstNode* fns[] = { fn };
|
||||||
AstNode* prog = ast_make_program(&a, fns, 1, NULL, 0, NULL, 0, NULL, 0, NULL, 0, loc_at(1, 1));
|
AstNode* prog = ast_make_program(&a, fns, 1, NULL, 0, NULL, 0, NULL, 0, NULL, 0, loc_at(1, 1));
|
||||||
|
|
||||||
@@ -46,7 +46,7 @@ void test_codegen_if_else() {
|
|||||||
ast_make_literal_bool(&a, true, loc_at(1, 1)), then_block, else_block, loc_at(1, 1));
|
ast_make_literal_bool(&a, true, loc_at(1, 1)), then_block, else_block, loc_at(1, 1));
|
||||||
AstNode* stmts[] = { if_stmt };
|
AstNode* stmts[] = { if_stmt };
|
||||||
AstNode* body = ast_make_block(&a, stmts, 1, loc_at(1, 1));
|
AstNode* body = ast_make_block(&a, stmts, 1, loc_at(1, 1));
|
||||||
AstNode* fn = ast_make_function(&a, "main", NULL, 0, TYPE_I64, NULL, body, loc_at(1, 1));
|
AstNode* fn = ast_make_function(&a, "main", NULL, 0, TYPE_I64, NULL, body, false, loc_at(1, 1));
|
||||||
AstNode* fns[] = { fn };
|
AstNode* fns[] = { fn };
|
||||||
AstNode* prog = ast_make_program(&a, fns, 1, NULL, 0, NULL, 0, NULL, 0, NULL, 0, loc_at(1, 1));
|
AstNode* prog = ast_make_program(&a, fns, 1, NULL, 0, NULL, 0, NULL, 0, NULL, 0, loc_at(1, 1));
|
||||||
|
|
||||||
@@ -77,7 +77,7 @@ void test_codegen_binary_ops() {
|
|||||||
AstNode* ret = ast_make_return(&a, expr, loc_at(1, 1));
|
AstNode* ret = ast_make_return(&a, expr, loc_at(1, 1));
|
||||||
AstNode* stmts[] = { ret };
|
AstNode* stmts[] = { ret };
|
||||||
AstNode* body = ast_make_block(&a, stmts, 1, loc_at(1, 1));
|
AstNode* body = ast_make_block(&a, stmts, 1, loc_at(1, 1));
|
||||||
AstNode* fn = ast_make_function(&a, "main", NULL, 0, TYPE_I64, NULL, body, loc_at(1, 1));
|
AstNode* fn = ast_make_function(&a, "main", NULL, 0, TYPE_I64, NULL, body, false, loc_at(1, 1));
|
||||||
AstNode* fns[] = { fn };
|
AstNode* fns[] = { fn };
|
||||||
AstNode* prog = ast_make_program(&a, fns, 1, NULL, 0, NULL, 0, NULL, 0, NULL, 0, loc_at(1, 1));
|
AstNode* prog = ast_make_program(&a, fns, 1, NULL, 0, NULL, 0, NULL, 0, NULL, 0, loc_at(1, 1));
|
||||||
|
|
||||||
@@ -107,7 +107,7 @@ void test_codegen_while_loop() {
|
|||||||
AstNode* ret = ast_make_return(&a, ast_make_literal_i64(&a, 1, loc_at(1, 1)), loc_at(1, 1));
|
AstNode* ret = ast_make_return(&a, ast_make_literal_i64(&a, 1, loc_at(1, 1)), loc_at(1, 1));
|
||||||
AstNode* stmts[] = { while_stmt, ret };
|
AstNode* stmts[] = { while_stmt, ret };
|
||||||
AstNode* fn_body = ast_make_block(&a, stmts, 2, loc_at(1, 1));
|
AstNode* fn_body = ast_make_block(&a, stmts, 2, loc_at(1, 1));
|
||||||
AstNode* fn = ast_make_function(&a, "main", NULL, 0, TYPE_I64, NULL, fn_body, loc_at(1, 1));
|
AstNode* fn = ast_make_function(&a, "main", NULL, 0, TYPE_I64, NULL, fn_body, false, loc_at(1, 1));
|
||||||
AstNode* fns[] = { fn };
|
AstNode* fns[] = { fn };
|
||||||
AstNode* prog = ast_make_program(&a, fns, 1, NULL, 0, NULL, 0, NULL, 0, NULL, 0, loc_at(1, 1));
|
AstNode* prog = ast_make_program(&a, fns, 1, NULL, 0, NULL, 0, NULL, 0, NULL, 0, loc_at(1, 1));
|
||||||
|
|
||||||
@@ -160,7 +160,7 @@ void test_codegen_struct_decl() {
|
|||||||
AstNode* ret = ast_make_return(&a, field_x, loc_at(1, 1));
|
AstNode* ret = ast_make_return(&a, field_x, loc_at(1, 1));
|
||||||
AstNode* stmts[] = { let_stmt, ret };
|
AstNode* stmts[] = { let_stmt, ret };
|
||||||
AstNode* body = ast_make_block(&a, stmts, 2, loc_at(1, 1));
|
AstNode* body = ast_make_block(&a, stmts, 2, loc_at(1, 1));
|
||||||
AstNode* fn = ast_make_function(&a, "main", NULL, 0, TYPE_I64, NULL, body, loc_at(1, 1));
|
AstNode* fn = ast_make_function(&a, "main", NULL, 0, TYPE_I64, NULL, body, false, loc_at(1, 1));
|
||||||
AstNode* fns[] = { fn };
|
AstNode* fns[] = { fn };
|
||||||
|
|
||||||
AstNode* prog = ast_make_program(&a, fns, 1, structs, 1, NULL, 0, NULL, 0, NULL, 0, loc_at(1, 1));
|
AstNode* prog = ast_make_program(&a, fns, 1, structs, 1, NULL, 0, NULL, 0, NULL, 0, loc_at(1, 1));
|
||||||
@@ -215,7 +215,7 @@ void test_codegen_struct_field_access() {
|
|||||||
AstNode* ret = ast_make_return(&a, field_y, loc_at(1, 1));
|
AstNode* ret = ast_make_return(&a, field_y, loc_at(1, 1));
|
||||||
AstNode* stmts[] = { let_stmt, ret };
|
AstNode* stmts[] = { let_stmt, ret };
|
||||||
AstNode* body = ast_make_block(&a, stmts, 2, loc_at(1, 1));
|
AstNode* body = ast_make_block(&a, stmts, 2, loc_at(1, 1));
|
||||||
AstNode* fn = ast_make_function(&a, "main", NULL, 0, TYPE_I64, NULL, body, loc_at(1, 1));
|
AstNode* fn = ast_make_function(&a, "main", NULL, 0, TYPE_I64, NULL, body, false, loc_at(1, 1));
|
||||||
AstNode* fns[] = { fn };
|
AstNode* fns[] = { fn };
|
||||||
|
|
||||||
AstNode* prog = ast_make_program(&a, fns, 1, structs, 1, NULL, 0, NULL, 0, NULL, 0, loc_at(1, 1));
|
AstNode* prog = ast_make_program(&a, fns, 1, structs, 1, NULL, 0, NULL, 0, NULL, 0, loc_at(1, 1));
|
||||||
@@ -265,7 +265,7 @@ void test_codegen_enum() {
|
|||||||
|
|
||||||
AstNode* stmts[] = { let_stmt, print_call, ret };
|
AstNode* stmts[] = { let_stmt, print_call, ret };
|
||||||
AstNode* body = ast_make_block(&a, stmts, 3, loc_at(1, 1));
|
AstNode* body = ast_make_block(&a, stmts, 3, loc_at(1, 1));
|
||||||
AstNode* fn = ast_make_function(&a, "main", NULL, 0, TYPE_I64, NULL, body, loc_at(1, 1));
|
AstNode* fn = ast_make_function(&a, "main", NULL, 0, TYPE_I64, NULL, body, false, loc_at(1, 1));
|
||||||
AstNode* fns[] = { fn };
|
AstNode* fns[] = { fn };
|
||||||
|
|
||||||
AstNode* prog = ast_make_program(&a, fns, 1, NULL, 0, NULL, 0, enums, 1, NULL, 0, loc_at(1, 1));
|
AstNode* prog = ast_make_program(&a, fns, 1, NULL, 0, NULL, 0, enums, 1, NULL, 0, loc_at(1, 1));
|
||||||
@@ -330,7 +330,7 @@ void test_codegen_array() {
|
|||||||
|
|
||||||
AstNode* stmts[] = { let_stmt, arr_assign, print_call, ret };
|
AstNode* stmts[] = { let_stmt, arr_assign, print_call, ret };
|
||||||
AstNode* body = ast_make_block(&a, stmts, 4, loc_at(1, 1));
|
AstNode* body = ast_make_block(&a, stmts, 4, loc_at(1, 1));
|
||||||
AstNode* fn = ast_make_function(&a, "main", NULL, 0, TYPE_I64, NULL, body, loc_at(1, 1));
|
AstNode* fn = ast_make_function(&a, "main", NULL, 0, TYPE_I64, NULL, body, false, loc_at(1, 1));
|
||||||
AstNode* fns[] = { fn };
|
AstNode* fns[] = { fn };
|
||||||
AstNode* prog = ast_make_program(&a, fns, 1, NULL, 0, NULL, 0, NULL, 0, NULL, 0, loc_at(1, 1));
|
AstNode* prog = ast_make_program(&a, fns, 1, NULL, 0, NULL, 0, NULL, 0, NULL, 0, loc_at(1, 1));
|
||||||
|
|
||||||
@@ -373,7 +373,7 @@ void test_codegen_method_call() {
|
|||||||
AstNode* ret_body = ast_make_return(&a, field_x, loc_at(1, 1));
|
AstNode* ret_body = ast_make_return(&a, field_x, loc_at(1, 1));
|
||||||
AstNode* ret_stmts[] = { ret_body };
|
AstNode* ret_stmts[] = { ret_body };
|
||||||
AstNode* body = ast_make_block(&a, ret_stmts, 1, loc_at(1, 1));
|
AstNode* body = ast_make_block(&a, ret_stmts, 1, loc_at(1, 1));
|
||||||
AstNode* get_x_fn = ast_make_function(&a, "Point$get_x", params, 1, TYPE_I64, NULL, body, loc_at(1, 1));
|
AstNode* get_x_fn = ast_make_function(&a, "Point$get_x", params, 1, TYPE_I64, NULL, body, false, loc_at(1, 1));
|
||||||
|
|
||||||
/* fn main() -> i64 {
|
/* fn main() -> i64 {
|
||||||
let p = Point { x: 42, y: 0 };
|
let p = Point { x: 42, y: 0 };
|
||||||
@@ -399,7 +399,7 @@ void test_codegen_method_call() {
|
|||||||
AstNode* ret_main = ast_make_return(&a, method_call, loc_at(1, 1));
|
AstNode* ret_main = ast_make_return(&a, method_call, loc_at(1, 1));
|
||||||
AstNode* main_stmts[] = { let_stmt, ret_main };
|
AstNode* main_stmts[] = { let_stmt, ret_main };
|
||||||
AstNode* main_body = ast_make_block(&a, main_stmts, 2, loc_at(1, 1));
|
AstNode* main_body = ast_make_block(&a, main_stmts, 2, loc_at(1, 1));
|
||||||
AstNode* main_fn = ast_make_function(&a, "main", NULL, 0, TYPE_I64, NULL, main_body, loc_at(1, 1));
|
AstNode* main_fn = ast_make_function(&a, "main", NULL, 0, TYPE_I64, NULL, main_body, false, loc_at(1, 1));
|
||||||
|
|
||||||
AstNode* fns[] = { get_x_fn, main_fn };
|
AstNode* fns[] = { get_x_fn, main_fn };
|
||||||
AstNode* prog = ast_make_program(&a, fns, 2, structs, 1, NULL, 0, NULL, 0, NULL, 0, loc_at(1, 1));
|
AstNode* prog = ast_make_program(&a, fns, 2, structs, 1, NULL, 0, NULL, 0, NULL, 0, loc_at(1, 1));
|
||||||
@@ -486,7 +486,7 @@ void test_codegen_match() {
|
|||||||
|
|
||||||
AstNode* main_stmts[] = { let_stmt, outer_if };
|
AstNode* main_stmts[] = { let_stmt, outer_if };
|
||||||
AstNode* main_body = ast_make_block(&a, main_stmts, 2, loc_at(1, 1));
|
AstNode* main_body = ast_make_block(&a, main_stmts, 2, loc_at(1, 1));
|
||||||
AstNode* main_fn = ast_make_function(&a, "main", NULL, 0, TYPE_I64, NULL, main_body, loc_at(1, 1));
|
AstNode* main_fn = ast_make_function(&a, "main", NULL, 0, TYPE_I64, NULL, main_body, false, loc_at(1, 1));
|
||||||
|
|
||||||
AstNode* fns[] = { main_fn };
|
AstNode* fns[] = { main_fn };
|
||||||
AstNode* prog = ast_make_program(&a, fns, 1, NULL, 0, NULL, 0, enums, 1, NULL, 0, loc_at(1, 1));
|
AstNode* prog = ast_make_program(&a, fns, 1, NULL, 0, NULL, 0, enums, 1, NULL, 0, loc_at(1, 1));
|
||||||
|
|||||||
Reference in New Issue
Block a user