Compare commits

..

4 Commits

Author SHA1 Message Date
zzy
1ceda90207 stage1 修复类型重复解析 修复嵌套结构体字面量无限循环 2026-07-12 14:20:41 +08:00
zzy
9b9b25ce0f stage1 禁止隐式调用 修复bug 2026-07-12 11:43:27 +08:00
zzy
118c153280 stage1 优化重构代码 2026-07-12 10:01:57 +08:00
zzy
f1b4225c92 stage1 优化重构代码 2026-07-11 23:18:06 +08:00
8 changed files with 882 additions and 1146 deletions

View File

@@ -240,6 +240,21 @@ int spl_lookup_func(spl_comp_t *ctx, const char *name) {
return -1; return -1;
} }
/* Ensure a native function is registered for NCALL dispatch.
* Returns the native index. */
int spl_ensure_native(spl_comp_t *ctx, const char *name) {
vec_for(ctx->prog.natives, ni) {
if (strcmp(vec_at(ctx->prog.natives, ni).name, name) == 0)
return (int)ni;
}
spl_native_t nat;
nat.name = strdup(name);
nat.idx_of_strtab = 0;
nat.impl_fn = NULL;
vec_push(ctx->prog.natives, nat);
return (int)vec_size(ctx->prog.natives) - 1;
}
/* ---- String/data management ---- */ /* ---- String/data management ---- */
int spl_add_string(spl_comp_t *ctx, const char *str) { int spl_add_string(spl_comp_t *ctx, const char *str) {

View File

@@ -245,6 +245,16 @@ typedef struct {
} spl_expr_result_t; } spl_expr_result_t;
spl_expr_result_t spl_parse_expr(spl_comp_t *ctx, int min_prec); spl_expr_result_t spl_parse_expr(spl_comp_t *ctx, int min_prec);
spl_expr_result_t spl_parse_struct_literal(spl_comp_t *ctx, spl_type_info_t *type);
/* Match arm pattern comparison — emit code to compare saved match value
* against a parsed pattern. Comparison logic belongs in expr layer
* so that match arms behave as "enhanced if-conditions".
* For enum: parses .VariantName, emits tag comparison, returns variant.
* For value: parses expression, emits equality comparison. */
spl_enum_variant_t *spl_emit_match_enum_cmp(spl_comp_t *ctx, spl_type_info_t *enum_type,
int val_offset);
void spl_emit_match_value_cmp(spl_comp_t *ctx, int val_offset);
/* ============================================================ /* ============================================================
* Statement functions (spl_stmt.c) * Statement functions (spl_stmt.c)
@@ -252,6 +262,7 @@ spl_expr_result_t spl_parse_expr(spl_comp_t *ctx, int min_prec);
void spl_parse_stmt(spl_comp_t *ctx); void spl_parse_stmt(spl_comp_t *ctx);
void spl_parse_block(spl_comp_t *ctx); void spl_parse_block(spl_comp_t *ctx);
spl_expr_result_t spl_parse_block_expr(spl_comp_t *ctx);
/* ============================================================ /* ============================================================
* Codegen helpers (spl_comp.c) * Codegen helpers (spl_comp.c)
@@ -268,6 +279,8 @@ void spl_patch_to_here(spl_comp_t *ctx, spl_val_t addr);
* Stack: [..., temp_addr] → [...] * Stack: [..., temp_addr] → [...]
* Copies nslots slots (each sizeof(spl_val_t) bytes) from temp offset to dest_offset. */ * Copies nslots slots (each sizeof(spl_val_t) bytes) from temp offset to dest_offset. */
void spl_emit_copy_slots(spl_comp_t *ctx, int dest_offset, usize nslots); void spl_emit_copy_slots(spl_comp_t *ctx, int dest_offset, usize nslots);
void spl_emit_ret(spl_comp_t *ctx, spl_type_info_t *ret_type);
void spl_emit_store_init(spl_comp_t *ctx, int var_offset, spl_type_info_t *var_type);
/* Variable management */ /* Variable management */
int spl_declare_var(spl_comp_t *ctx, const char *name, spl_type_info_t *type, int is_const); int spl_declare_var(spl_comp_t *ctx, const char *name, spl_type_info_t *type, int is_const);
@@ -279,6 +292,9 @@ int spl_declare_func(spl_comp_t *ctx, const char *name, spl_type_info_t *ret_typ
int is_extern, int is_pub); int is_extern, int is_pub);
int spl_lookup_func(spl_comp_t *ctx, const char *name); int spl_lookup_func(spl_comp_t *ctx, const char *name);
/* Ensure a native function is registered for NCALL dispatch, returns index */
int spl_ensure_native(spl_comp_t *ctx, const char *name);
/* String/data management */ /* String/data management */
int spl_add_string(spl_comp_t *ctx, const char *str); int spl_add_string(spl_comp_t *ctx, const char *str);
int spl_add_global_data(spl_comp_t *ctx, void *data, usize size); int spl_add_global_data(spl_comp_t *ctx, void *data, usize size);

File diff suppressed because it is too large Load Diff

View File

@@ -21,6 +21,44 @@ spl_tok_t *advance(spl_comp_t *ctx) {
/* Shared helper: register a function, declare params, parse body, end function. /* Shared helper: register a function, declare params, parse body, end function.
* Used by both top-level fn decl and methods inside type bodies. */ * Used by both top-level fn decl and methods inside type bodies. */
/* Shared helper: parse function/method parameter list: (name: type, name: type, ...)
* Returns number of params parsed. Stores names in pnames and types in ptypes
* (both must be MAX_PARAMS-sized arrays). */
enum { MAX_PARAMS = 64 };
static int parse_params_decl(spl_comp_t *ctx, char pnames[][256], spl_type_info_t *ptypes[]) {
int nparams = 0;
skip_nl(ctx);
if (peek(ctx)->type != TOK_R_PAREN) {
while (1) {
spl_tok_t *pname = advance(ctx);
spl_tok_copy_name(pname, pnames[nparams], 256);
skip_nl(ctx);
if (peek(ctx)->type == TOK_COLON) {
advance(ctx); /* : */
skip_nl(ctx);
ptypes[nparams] = spl_parse_type(ctx);
} else {
ptypes[nparams] = spl_type_basic(SPL_I32);
}
nparams++;
skip_nl(ctx);
if (peek(ctx)->type == TOK_COMMA) {
advance(ctx);
skip_nl(ctx);
continue;
}
if (peek(ctx)->type == TOK_ELLIPSIS) {
advance(ctx);
skip_nl(ctx);
}
break;
}
}
expect(ctx, TOK_R_PAREN);
return nparams;
}
static int parse_fn_body(spl_comp_t *ctx, const char *fn_name, spl_type_info_t *ret_type, static int parse_fn_body(spl_comp_t *ctx, const char *fn_name, spl_type_info_t *ret_type,
int nparams, char pnames[][256], spl_type_info_t *ptypes[], int is_pub) { int nparams, char pnames[][256], spl_type_info_t *ptypes[], int is_pub) {
int fi = spl_declare_func(ctx, fn_name, ret_type, nparams, 0, is_pub); int fi = spl_declare_func(ctx, fn_name, ret_type, nparams, 0, is_pub);
@@ -68,48 +106,14 @@ static void parse_fn_decl(spl_comp_t *ctx, int is_extern, int is_pub) {
spl_tok_t *fname_tok = advance(ctx); spl_tok_t *fname_tok = advance(ctx);
char fn_name[256]; char fn_name[256];
usize fnl = fname_tok->len < 255 ? fname_tok->len : 255; spl_tok_copy_name(fname_tok, fn_name, sizeof(fn_name));
memcpy(fn_name, fname_tok->lexeme, fnl);
fn_name[fnl] = '\0';
skip_nl(ctx); skip_nl(ctx);
expect(ctx, TOK_L_PAREN); expect(ctx, TOK_L_PAREN);
/* Parse parameters — collect names and types */ /* Parse parameters — collect names and types */
int nparams = 0;
enum { MAX_PARAMS = 64 };
char pnames[MAX_PARAMS][256]; char pnames[MAX_PARAMS][256];
spl_type_info_t *ptypes[MAX_PARAMS]; spl_type_info_t *ptypes[MAX_PARAMS];
skip_nl(ctx); int nparams = parse_params_decl(ctx, pnames, ptypes);
if (peek(ctx)->type != TOK_R_PAREN) {
while (1) {
spl_tok_t *pname = advance(ctx);
usize pnl = pname->len < 255 ? pname->len : 255;
memcpy(pnames[nparams], pname->lexeme, pnl);
pnames[nparams][pnl] = '\0';
skip_nl(ctx);
if (peek(ctx)->type == TOK_COLON) {
advance(ctx); /* : */
skip_nl(ctx);
ptypes[nparams] = spl_parse_type(ctx);
} else {
ptypes[nparams] = spl_type_basic(SPL_I32);
}
nparams++;
skip_nl(ctx);
if (peek(ctx)->type == TOK_COMMA) {
advance(ctx);
skip_nl(ctx);
continue;
}
if (peek(ctx)->type == TOK_ELLIPSIS) {
advance(ctx);
skip_nl(ctx);
}
break;
}
}
expect(ctx, TOK_R_PAREN);
skip_nl(ctx); skip_nl(ctx);
/* Return type (default: void) */ /* Return type (default: void) */
@@ -124,22 +128,7 @@ static void parse_fn_decl(spl_comp_t *ctx, int is_extern, int is_pub) {
/* Extern function: register as native, no body */ /* Extern function: register as native, no body */
if (is_extern) { if (is_extern) {
spl_declare_func(ctx, fn_name, ret_type, nparams, 1, is_pub); spl_declare_func(ctx, fn_name, ret_type, nparams, 1, is_pub);
/* Add to prog->natives for NCALL dispatch */ spl_ensure_native(ctx, fn_name);
int found = -1;
vec_for(ctx->prog.natives, ni) {
if (strcmp(vec_at(ctx->prog.natives, ni).name, fn_name) == 0) {
found = (int)ni;
break;
}
}
if (found < 0) {
spl_native_t nat;
memset(&nat, 0, sizeof(nat));
nat.name = strdup(fn_name);
nat.idx_of_strtab = 0;
nat.impl_fn = NULL; /* resolved by VM at runtime */
vec_push(ctx->prog.natives, nat);
}
if (peek(ctx)->type == TOK_SEMICOLON) if (peek(ctx)->type == TOK_SEMICOLON)
advance(ctx); advance(ctx);
return; return;
@@ -158,22 +147,117 @@ static void parse_fn_decl(spl_comp_t *ctx, int is_extern, int is_pub) {
} }
/* ============================================================ /* ============================================================
* Parse struct/union body (shared for struct and union containers) * Shared helper: parse a method declaration inside a type body.
* Used by both struct_body and enum_body parsers.
* fn name(params) ret-type { body }
* ============================================================ */
static void parse_method_decl(spl_comp_t *ctx, spl_type_info_t *container) {
advance(ctx); /* fn */
skip_nl(ctx);
spl_tok_t *mname_tok = advance(ctx);
char mname[256];
spl_tok_copy_name(mname_tok, mname, sizeof(mname));
/* Build qualified name: TypeName.method_name */
char qualified[512];
snprintf(qualified, sizeof(qualified), "%s.%s", container->name ? container->name : "anon",
mname);
skip_nl(ctx);
expect(ctx, TOK_L_PAREN);
/* Parse parameters using shared helper */
char pnames[MAX_PARAMS][256];
spl_type_info_t *ptypes[MAX_PARAMS];
int nparams = parse_params_decl(ctx, pnames, ptypes);
skip_nl(ctx);
/* Return type (default: void) */
spl_type_info_t *ret_type = spl_type_basic(SPL_VOID);
if (peek(ctx)->type != TOK_SEMICOLON && peek(ctx)->type != TOK_L_BRACE) {
ret_type = spl_parse_type(ctx);
if (!ret_type)
ret_type = spl_type_basic(SPL_VOID);
skip_nl(ctx);
}
/* Set current_type_name for short-name resolution inside method body */
const char *saved_type_name = ctx->current_type_name;
ctx->current_type_name = container->name;
int fi = parse_fn_body(ctx, qualified, ret_type, nparams, pnames, ptypes, 0);
ctx->current_type_name = saved_type_name;
{
spl_func_info_t *f = &vec_at(ctx->funcs, fi);
f->param_types = calloc(nparams, sizeof(spl_type_info_t *));
f->param_names = calloc(nparams, sizeof(char *));
for (int i = 0; i < nparams; i++) {
f->param_types[i] = ptypes[i];
f->param_names[i] = strdup(pnames[i]);
}
}
spl_type_add_method(container, mname, fi);
}
/* ============================================================
* Parse type container body (shared for struct, union, enum)
*
* For struct/union: fields are added for identifiers
* For enum: variants are added for identifiers
* *
* Body supports: * Body supports:
* var name: type; — field declarations * var name: type; — field declarations (struct/union only)
* name: type, — field declarations (old-style) * name: type, — field/variant declarations
* name: Type, — enum variant with data
* name, — simple enum variant
* type Name = ...; — nested type declarations * type Name = ...; — nested type declarations
* fn name(...) type { } — methods * fn name(...) type { } — methods
* ============================================================ */ * ============================================================ */
static void parse_struct_body(spl_comp_t *ctx, spl_type_info_t *st) { /* Skip past a nested type declaration in pass 2 without re-parsing */
static void skip_type_decl(spl_comp_t *ctx) {
advance(ctx); /* type */
advance(ctx); /* name */
skip_nl(ctx);
expect(ctx, TOK_ASSIGN);
skip_nl(ctx);
spl_tok_type_t tt = peek(ctx)->type;
if (tt == KW_STRUCT || tt == KW_UNION || tt == KW_ENUM) {
advance(ctx);
skip_nl(ctx);
}
if (peek(ctx)->type == TOK_L_BRACE) {
int bd = 1;
advance(ctx);
while (bd > 0 && ctx->tok_idx < vec_size(ctx->toks)) {
if (peek(ctx)->type == TOK_L_BRACE)
bd++;
else if (peek(ctx)->type == TOK_R_BRACE)
bd--;
advance(ctx);
}
} else {
while (peek(ctx)->type != TOK_SEMICOLON && peek(ctx)->type != TOK_EOF)
advance(ctx);
}
skip_nl(ctx);
if (peek(ctx)->type == TOK_SEMICOLON)
advance(ctx);
}
static void parse_type_body(spl_comp_t *ctx, spl_type_info_t *container, int is_enum) {
if (peek(ctx)->type != TOK_L_BRACE) if (peek(ctx)->type != TOK_L_BRACE)
return; return;
advance(ctx); /* { */ advance(ctx); /* { */
/* === Pass 1: Parse all nested type declarations first === /* === Pass 1: Parse all nested type declarations first ===
* This allows field declarations to reference types defined later in the body. */ * This allows fields/variants to reference types defined later. */
{ {
usize saved = ctx->tok_idx; usize saved = ctx->tok_idx;
int depth = 1; int depth = 1;
@@ -193,11 +277,10 @@ static void parse_struct_body(spl_comp_t *ctx, spl_type_info_t *st) {
advance(ctx); advance(ctx);
} }
} }
/* Reset to start of body for second pass */
ctx->tok_idx = saved; ctx->tok_idx = saved;
} }
/* === Pass 2: Parse fields and methods === */ /* === Pass 2: Parse fields/variants and methods === */
{ {
int depth = 1; int depth = 1;
while (depth > 0 && ctx->tok_idx < vec_size(ctx->toks)) { while (depth > 0 && ctx->tok_idx < vec_size(ctx->toks)) {
@@ -214,10 +297,9 @@ static void parse_struct_body(spl_comp_t *ctx, spl_type_info_t *st) {
} }
advance(ctx); advance(ctx);
} else if (tt == KW_TYPE && depth == 1) { } else if (tt == KW_TYPE && depth == 1) {
/* Nested type — already parsed in pass 1, skip by re-parsing */ skip_type_decl(ctx);
parse_type_decl(ctx); } else if (tt == KW_VAR && depth == 1 && !is_enum) {
} else if (tt == KW_VAR && depth == 1) { /* var name: type; (struct/union only) */
/* var name: type; */
advance(ctx); /* var */ advance(ctx); /* var */
skip_nl(ctx); skip_nl(ctx);
spl_tok_t *ftok = advance(ctx); spl_tok_t *ftok = advance(ctx);
@@ -227,178 +309,14 @@ static void parse_struct_body(spl_comp_t *ctx, spl_type_info_t *st) {
skip_nl(ctx); skip_nl(ctx);
spl_type_info_t *ftype = spl_parse_type(ctx); spl_type_info_t *ftype = spl_parse_type(ctx);
char fname[256]; char fname[256];
usize fnl = ftok->len < 255 ? ftok->len : 255; spl_tok_copy_name(ftok, fname, sizeof(fname));
memcpy(fname, ftok->lexeme, fnl); spl_type_add_field(container, fname, ftype);
fname[fnl] = '\0';
spl_type_add_field(st, fname, ftype);
} }
skip_nl(ctx); skip_nl(ctx);
if (peek(ctx)->type == TOK_SEMICOLON || peek(ctx)->type == TOK_COMMA) if (peek(ctx)->type == TOK_SEMICOLON || peek(ctx)->type == TOK_COMMA)
advance(ctx); advance(ctx);
} else if (tt == TOK_IDENT && depth == 1) { } else if (tt == TOK_IDENT && depth == 1) {
/* Old-style field: name: type, */ if (is_enum) {
spl_tok_t *ftok = advance(ctx);
skip_nl(ctx);
if (peek(ctx)->type == TOK_COLON) {
advance(ctx); /* : */
skip_nl(ctx);
spl_type_info_t *ftype = spl_parse_type(ctx);
char fname[256];
usize fnl = ftok->len < 255 ? ftok->len : 255;
memcpy(fname, ftok->lexeme, fnl);
fname[fnl] = '\0';
spl_type_add_field(st, fname, ftype);
}
skip_nl(ctx);
if (peek(ctx)->type == TOK_SEMICOLON || peek(ctx)->type == TOK_COMMA)
advance(ctx);
} else if (tt == KW_FN && depth == 1) {
/* Method — parse properly using parse_fn_body */
advance(ctx); /* fn */
skip_nl(ctx);
spl_tok_t *mname_tok = advance(ctx);
char mname[256];
usize mnl = mname_tok->len < 255 ? mname_tok->len : 255;
memcpy(mname, mname_tok->lexeme, mnl);
mname[mnl] = '\0';
/* Build qualified name: TypeName.method_name */
char qualified[512];
snprintf(qualified, sizeof(qualified), "%s.%s", st->name ? st->name : "anon",
mname);
skip_nl(ctx);
expect(ctx, TOK_L_PAREN);
/* Parse parameters */
int nparams = 0;
enum { MAX_PARAMS = 64 };
char pnames[MAX_PARAMS][256];
spl_type_info_t *ptypes[MAX_PARAMS];
skip_nl(ctx);
if (peek(ctx)->type != TOK_R_PAREN) {
while (1) {
spl_tok_t *pname = advance(ctx);
usize pnl = pname->len < 255 ? pname->len : 255;
memcpy(pnames[nparams], pname->lexeme, pnl);
pnames[nparams][pnl] = '\0';
skip_nl(ctx);
if (peek(ctx)->type == TOK_COLON) {
advance(ctx); /* : */
skip_nl(ctx);
ptypes[nparams] = spl_parse_type(ctx);
} else {
ptypes[nparams] = spl_type_basic(SPL_I32);
}
nparams++;
skip_nl(ctx);
if (peek(ctx)->type == TOK_COMMA) {
advance(ctx);
skip_nl(ctx);
continue;
}
break;
}
}
expect(ctx, TOK_R_PAREN);
skip_nl(ctx);
/* Return type (default: void) */
spl_type_info_t *ret_type = spl_type_basic(SPL_VOID);
if (peek(ctx)->type != TOK_SEMICOLON && peek(ctx)->type != TOK_L_BRACE) {
ret_type = spl_parse_type(ctx);
if (!ret_type)
ret_type = spl_type_basic(SPL_VOID);
skip_nl(ctx);
}
/* Set current_type_name for short-name resolution inside method body */
const char *saved_type_name = ctx->current_type_name;
ctx->current_type_name = st->name;
int fi = parse_fn_body(ctx, qualified, ret_type, nparams, pnames, ptypes, 0);
{
spl_func_info_t *f = &vec_at(ctx->funcs, fi);
f->param_types = calloc(nparams, sizeof(spl_type_info_t *));
f->param_names = calloc(nparams, sizeof(char *));
for (int i = 0; i < nparams; i++) {
f->param_types[i] = ptypes[i];
f->param_names[i] = strdup(pnames[i]);
}
}
ctx->current_type_name = saved_type_name;
spl_type_add_method(st, mname, fi);
} else {
advance(ctx);
}
}
}
spl_type_compute_layout(st);
}
/* ============================================================
* Parse enum body
*
* Body supports:
* Name — simple variant
* Name: Type — variant with data
* var name: type; — field-style variant
* type Name = ...; — nested type declarations
* fn name(...) type { } — methods (skipped)
* ============================================================ */
static void parse_enum_body(spl_comp_t *ctx, spl_type_info_t *et) {
if (peek(ctx)->type != TOK_L_BRACE)
return;
advance(ctx); /* { */
/* === Pass 1: Parse all nested type declarations first === */
{
usize saved = ctx->tok_idx;
int depth = 1;
while (depth > 0 && ctx->tok_idx < vec_size(ctx->toks)) {
spl_tok_type_t tt = peek(ctx)->type;
if (tt == TOK_L_BRACE) {
depth++;
advance(ctx);
} else if (tt == TOK_R_BRACE) {
depth--;
if (depth == 0)
break;
advance(ctx);
} else if (tt == KW_TYPE && depth == 1) {
parse_type_decl(ctx);
} else {
advance(ctx);
}
}
ctx->tok_idx = saved;
}
/* === Pass 2: Parse variants and methods === */
{
int depth = 1;
while (depth > 0 && ctx->tok_idx < vec_size(ctx->toks)) {
skip_nl(ctx);
spl_tok_type_t tt = peek(ctx)->type;
if (tt == TOK_L_BRACE) {
depth++;
advance(ctx);
} else if (tt == TOK_R_BRACE) {
depth--;
if (depth == 0) {
advance(ctx);
break;
}
advance(ctx);
} else if (tt == KW_TYPE && depth == 1) {
/* Already parsed in pass 1, re-parse to skip */
parse_type_decl(ctx);
} else if (tt == TOK_IDENT && depth == 1) {
/* Variant: Name or Name: Type */ /* Variant: Name or Name: Type */
spl_tok_t *vtok = advance(ctx); spl_tok_t *vtok = advance(ctx);
skip_nl(ctx); skip_nl(ctx);
@@ -407,106 +325,41 @@ static void parse_enum_body(spl_comp_t *ctx, spl_type_info_t *et) {
skip_nl(ctx); skip_nl(ctx);
spl_type_info_t *dtype = spl_parse_type(ctx); spl_type_info_t *dtype = spl_parse_type(ctx);
char vname[256]; char vname[256];
usize vnl = vtok->len < 255 ? vtok->len : 255; spl_tok_copy_name(vtok, vname, sizeof(vname));
memcpy(vname, vtok->lexeme, vnl); spl_type_add_variant(container, vname, dtype);
vname[vnl] = '\0';
spl_type_add_variant(et, vname, dtype);
} else { } else {
char vname[256]; char vname[256];
usize vnl = vtok->len < 255 ? vtok->len : 255; spl_tok_copy_name(vtok, vname, sizeof(vname));
memcpy(vname, vtok->lexeme, vnl); spl_type_add_variant(container, vname, NULL);
vname[vnl] = '\0'; }
spl_type_add_variant(et, vname, NULL); } else {
/* Old-style field: name: type, */
spl_tok_t *ftok = advance(ctx);
skip_nl(ctx);
if (peek(ctx)->type == TOK_COLON) {
advance(ctx); /* : */
skip_nl(ctx);
spl_type_info_t *ftype = spl_parse_type(ctx);
char fname[256];
spl_tok_copy_name(ftok, fname, sizeof(fname));
spl_type_add_field(container, fname, ftype);
}
} }
skip_nl(ctx); skip_nl(ctx);
if (peek(ctx)->type == TOK_SEMICOLON || peek(ctx)->type == TOK_COMMA) if (peek(ctx)->type == TOK_SEMICOLON || peek(ctx)->type == TOK_COMMA)
advance(ctx); advance(ctx);
} else if (tt == KW_FN && depth == 1) { } else if (tt == KW_FN && depth == 1) {
/* Method — parse properly using parse_fn_body */ /* Compute layout before compiling methods so
advance(ctx); /* fn */ * field offsets are correct during codegen */
skip_nl(ctx); spl_type_compute_layout(container);
spl_tok_t *mname_tok = advance(ctx); parse_method_decl(ctx, container);
char mname[256];
usize mnl = mname_tok->len < 255 ? mname_tok->len : 255;
memcpy(mname, mname_tok->lexeme, mnl);
mname[mnl] = '\0';
/* Build qualified name: TypeName.method_name */
char qualified[512];
snprintf(qualified, sizeof(qualified), "%s.%s", et->name ? et->name : "anon",
mname);
skip_nl(ctx);
expect(ctx, TOK_L_PAREN);
/* Parse parameters */
int nparams = 0;
enum { MAX_PARAMS = 64 };
char pnames[MAX_PARAMS][256];
spl_type_info_t *ptypes[MAX_PARAMS];
skip_nl(ctx);
if (peek(ctx)->type != TOK_R_PAREN) {
while (1) {
spl_tok_t *pname = advance(ctx);
usize pnl = pname->len < 255 ? pname->len : 255;
memcpy(pnames[nparams], pname->lexeme, pnl);
pnames[nparams][pnl] = '\0';
skip_nl(ctx);
if (peek(ctx)->type == TOK_COLON) {
advance(ctx); /* : */
skip_nl(ctx);
ptypes[nparams] = spl_parse_type(ctx);
} else {
ptypes[nparams] = spl_type_basic(SPL_I32);
}
nparams++;
skip_nl(ctx);
if (peek(ctx)->type == TOK_COMMA) {
advance(ctx);
skip_nl(ctx);
continue;
}
break;
}
}
expect(ctx, TOK_R_PAREN);
skip_nl(ctx);
/* Return type (default: void) */
spl_type_info_t *ret_type = spl_type_basic(SPL_VOID);
if (peek(ctx)->type != TOK_SEMICOLON && peek(ctx)->type != TOK_L_BRACE) {
ret_type = spl_parse_type(ctx);
if (!ret_type)
ret_type = spl_type_basic(SPL_VOID);
skip_nl(ctx);
}
/* Set current_type_name for short-name resolution inside method body */
const char *saved_type_name = ctx->current_type_name;
ctx->current_type_name = et->name;
int fi = parse_fn_body(ctx, qualified, ret_type, nparams, pnames, ptypes, 0);
ctx->current_type_name = saved_type_name;
{
spl_func_info_t *f = &vec_at(ctx->funcs, fi);
f->param_types = calloc(nparams, sizeof(spl_type_info_t *));
f->param_names = calloc(nparams, sizeof(char *));
for (int i = 0; i < nparams; i++) {
f->param_types[i] = ptypes[i];
f->param_names[i] = strdup(pnames[i]);
}
}
spl_type_add_method(et, mname, fi);
} else { } else {
advance(ctx); advance(ctx);
} }
} }
} }
spl_type_compute_layout(et); spl_type_compute_layout(container);
} }
/* ============================================================ /* ============================================================
@@ -521,9 +374,7 @@ void parse_type_decl(spl_comp_t *ctx) {
advance(ctx); /* type */ advance(ctx); /* type */
spl_tok_t *name_tok = advance(ctx); spl_tok_t *name_tok = advance(ctx);
char tname[256]; char tname[256];
usize tnl = name_tok->len < 255 ? name_tok->len : 255; spl_tok_copy_name(name_tok, tname, sizeof(tname));
memcpy(tname, name_tok->lexeme, tnl);
tname[tnl] = '\0';
skip_nl(ctx); skip_nl(ctx);
expect(ctx, TOK_ASSIGN); expect(ctx, TOK_ASSIGN);
@@ -535,20 +386,20 @@ void parse_type_decl(spl_comp_t *ctx) {
/* Register type early to allow self-referential fields */ /* Register type early to allow self-referential fields */
map_put(ctx->type_defs, strdup(tname), st); map_put(ctx->type_defs, strdup(tname), st);
skip_nl(ctx); skip_nl(ctx);
parse_struct_body(ctx, st); parse_type_body(ctx, st, 0);
} else if (peek(ctx)->type == KW_UNION) { } else if (peek(ctx)->type == KW_UNION) {
advance(ctx); advance(ctx);
spl_type_info_t *ut = spl_type_union(tname); spl_type_info_t *ut = spl_type_union(tname);
map_put(ctx->type_defs, strdup(tname), ut); map_put(ctx->type_defs, strdup(tname), ut);
skip_nl(ctx); skip_nl(ctx);
parse_struct_body(ctx, ut); parse_type_body(ctx, ut, 0);
} else if (peek(ctx)->type == KW_ENUM) { } else if (peek(ctx)->type == KW_ENUM) {
advance(ctx); advance(ctx);
spl_type_info_t *et = spl_type_enum(tname); spl_type_info_t *et = spl_type_enum(tname);
/* Register type early to allow self-referential variants */ /* Register type early to allow self-referential variants */
map_put(ctx->type_defs, strdup(tname), et); map_put(ctx->type_defs, strdup(tname), et);
skip_nl(ctx); skip_nl(ctx);
parse_enum_body(ctx, et); parse_type_body(ctx, et, 1);
} else if (peek(ctx)->type == TOK_IDENT || } else if (peek(ctx)->type == TOK_IDENT ||
(peek(ctx)->type >= KW_AS && peek(ctx)->type <= KW_ANY)) { (peek(ctx)->type >= KW_AS && peek(ctx)->type <= KW_ANY)) {
spl_type_info_t *base = spl_parse_type(ctx); spl_type_info_t *base = spl_parse_type(ctx);
@@ -606,7 +457,7 @@ void spl_parse_prog(spl_comp_t *ctx) {
parse_fn_decl(ctx, 1, 0); parse_fn_decl(ctx, 1, 0);
break; break;
default: { default: {
int prev = ctx->tok_idx; int prev = ctx->tok_idx; /* Safety: prevent infinite loop */
/* Try to parse as a statement */ /* Try to parse as a statement */
spl_parse_stmt(ctx); spl_parse_stmt(ctx);
/* Safety: prevent infinite loop on unrecognized tokens */ /* Safety: prevent infinite loop on unrecognized tokens */

View File

@@ -3,7 +3,6 @@
#include "spl_comp.h" #include "spl_comp.h"
#include "spl_lex_util.h" #include "spl_lex_util.h"
#include <stdlib.h> #include <stdlib.h>
#include <string.h>
/* ============================================================ /* ============================================================
* Return statement: ret expr; * Return statement: ret expr;
@@ -25,18 +24,7 @@ static void parse_ret_stmt(spl_comp_t *ctx) {
} else { } else {
spl_expr_result_t val = spl_parse_expr(ctx, PREC_MIN); spl_expr_result_t val = spl_parse_expr(ctx, PREC_MIN);
(void)val; (void)val;
spl_type_t rt = SPL_VOID; spl_emit_ret(ctx, ctx->current_ret_type);
if (ctx->current_ret_type) {
if (ctx->current_ret_type->kind == TYPE_BASIC) {
rt = ctx->current_ret_type->basic_type;
} else if (ctx->current_ret_type->kind == TYPE_PTR) {
rt = SPL_PTR;
} else if (spl_type_size(ctx->current_ret_type) <= sizeof(spl_val_t)) {
/* 1-slot struct/enum/etc: use PTR type */
rt = SPL_PTR;
}
}
spl_emit(ctx, SPL_RET, rt, 0);
} }
if (peek(ctx)->type == TOK_SEMICOLON) if (peek(ctx)->type == TOK_SEMICOLON)
advance(ctx); advance(ctx);
@@ -52,9 +40,7 @@ static void parse_var_decl(spl_comp_t *ctx, int is_const) {
spl_tok_t *name_tok = advance(ctx); spl_tok_t *name_tok = advance(ctx);
char vname[256]; char vname[256];
usize nlen = name_tok->len < 255 ? name_tok->len : 255; spl_tok_copy_name(name_tok, vname, sizeof(vname));
memcpy(vname, name_tok->lexeme, nlen);
vname[nlen] = '\0';
spl_type_info_t *var_type = NULL; spl_type_info_t *var_type = NULL;
int has_init = 0; int has_init = 0;
@@ -84,10 +70,19 @@ static void parse_var_decl(spl_comp_t *ctx, int is_const) {
/* Init expression: parse before declare to enable type inference */ /* Init expression: parse before declare to enable type inference */
spl_expr_result_t init = {0}; spl_expr_result_t init = {0};
int inline_lit = 0;
if (has_init) { if (has_init) {
skip_nl(ctx); skip_nl(ctx);
/* Inline struct/enum/slice literal: var x: Type = { .field = val }
* Don't call spl_parse_expr — { would be consumed as block expression */
if (var_type && peek(ctx)->type == TOK_L_BRACE &&
(var_type->kind == TYPE_STRUCT || var_type->kind == TYPE_ENUM ||
var_type->kind == TYPE_SLICE)) {
inline_lit = 1;
} else {
init = spl_parse_expr(ctx, PREC_MIN); init = spl_parse_expr(ctx, PREC_MIN);
} }
}
if (!var_type) { if (!var_type) {
var_type = init.type ? init.type : spl_type_basic(SPL_I32); var_type = init.type ? init.type : spl_type_basic(SPL_I32);
@@ -96,84 +91,19 @@ static void parse_var_decl(spl_comp_t *ctx, int is_const) {
/* Store init value */ /* Store init value */
if (has_init) { if (has_init) {
/* Handle slice struct literal: { .ptr = ..., .len = ... } */ if (inline_lit && (var_type->kind == TYPE_STRUCT || var_type->kind == TYPE_ENUM ||
if (var_type && var_type->kind == TYPE_SLICE && peek(ctx)->type == TOK_L_BRACE && var_type->kind == TYPE_SLICE)) {
!init.type) { /* Inline struct/enum/slice literal: var x: Type = { .field = val }
advance(ctx); /* { */ * Use spl_parse_struct_literal to handle field parsing uniformly. */
skip_nl(ctx); spl_parse_struct_literal(ctx, var_type);
while (peek(ctx)->type != TOK_R_BRACE && peek(ctx)->type != TOK_EOF) { if (var_type->kind == TYPE_SLICE) {
if (peek(ctx)->type == TOK_COMMA) { /* Slice: copy 2 temp slots to variable */
advance(ctx); spl_emit_copy_slots(ctx, offset, 2);
skip_nl(ctx);
continue;
}
if (peek(ctx)->type == TOK_DOT)
advance(ctx);
spl_tok_t *ftok = advance(ctx);
char fname[256];
usize fnl = ftok->len < 255 ? ftok->len : 255;
memcpy(fname, ftok->lexeme, fnl);
fname[fnl] = '\0';
skip_nl(ctx);
if (peek(ctx)->type == TOK_ASSIGN)
advance(ctx);
skip_nl(ctx);
spl_expr_result_t fv = spl_parse_expr(ctx, PREC_MIN);
(void)fv;
if (strcmp(fname, "ptr") == 0) {
spl_emit(ctx, SPL_LADDR, SPL_PTR, offset);
spl_emit(ctx, SPL_SWAP, SPL_VOID, 0);
spl_emit(ctx, SPL_STORE, SPL_PTR, 0);
} else if (strcmp(fname, "len") == 0) {
spl_emit(ctx, SPL_LADDR, SPL_PTR, offset + (int)sizeof(spl_val_t));
spl_emit(ctx, SPL_SWAP, SPL_VOID, 0);
spl_emit(ctx, SPL_STORE, SPL_USIZE, 0);
}
skip_nl(ctx);
}
expect(ctx, TOK_R_BRACE);
} else if (var_type && (var_type->kind == TYPE_STRUCT || var_type->kind == TYPE_ENUM)) {
/* Struct/enum initialization */
usize sz = spl_type_size(var_type);
if (sz <= sizeof(spl_val_t)) {
/* Fits in one slot: value on stack, store directly */
spl_emit(ctx, SPL_LADDR, SPL_PTR, offset);
spl_emit(ctx, SPL_SWAP, SPL_VOID, 0);
spl_emit(ctx, SPL_STORE, SPL_PTR, 0);
} else { } else {
/* Multi-slot: copy from temp addr to var, slot by slot */ spl_emit_store_init(ctx, offset, var_type);
usize nslots = (sz + sizeof(spl_val_t) - 1) / sizeof(spl_val_t);
spl_emit_copy_slots(ctx, offset, nslots);
} }
} else if (var_type && var_type->kind == TYPE_ARRAY) {
/* Array initialization: store each element in reverse stack order */
spl_type_info_t *elem = var_type->elem;
spl_type_t bt = (elem && elem->kind == TYPE_BASIC) ? elem->basic_type : SPL_I32;
usize stride = spl_type_elem_stride(elem);
for (int i = (int)var_type->array_len - 1; i >= 0; i--) {
spl_emit(ctx, SPL_LADDR, SPL_PTR, offset + (int)(i * stride));
spl_emit(ctx, SPL_SWAP, SPL_VOID, 0);
spl_emit(ctx, SPL_STORE, bt, 0);
}
} else if (var_type && var_type->kind == TYPE_SLICE) {
/* Slice initialization: stack has [ptr, len] from slice expression.
* Store len at offset+8, then ptr at offset. */
spl_emit(ctx, SPL_LADDR, SPL_PTR, offset + (int)sizeof(spl_val_t));
spl_emit(ctx, SPL_SWAP, SPL_VOID, 0);
spl_emit(ctx, SPL_STORE, SPL_USIZE, 0);
spl_emit(ctx, SPL_LADDR, SPL_PTR, offset);
spl_emit(ctx, SPL_SWAP, SPL_VOID, 0);
spl_emit(ctx, SPL_STORE, SPL_PTR, 0);
} else { } else {
/* Single value store */ spl_emit_store_init(ctx, offset, var_type);
spl_emit(ctx, SPL_LADDR, SPL_PTR, offset);
spl_type_t bt = (var_type->kind == TYPE_BASIC) ? var_type->basic_type
: (var_type->kind == TYPE_PTR) ? SPL_PTR
: SPL_I32;
spl_emit(ctx, SPL_SWAP, SPL_VOID, 0);
spl_emit(ctx, SPL_STORE, bt, 0);
} }
} }
@@ -206,6 +136,72 @@ void spl_parse_block(spl_comp_t *ctx) {
} }
} }
/* Forward declaration for block expr */
static int is_assign_op(spl_tok_type_t t);
static int lookahead_is_assign(spl_comp_t *ctx);
/* ============================================================
* Block expression: { stmts; [trailing_expr] }
* Parses a block and returns the trailing expression type.
* Like Rust/Zig: the last expression without semicolon is the block's value.
* ============================================================ */
spl_expr_result_t spl_parse_block_expr(spl_comp_t *ctx) {
advance(ctx); /* { */
spl_push_scope(ctx);
spl_expr_result_t result = {0}; /* void by default */
skip_nl(ctx);
while (peek(ctx)->type != TOK_R_BRACE && peek(ctx)->type != TOK_EOF) {
if (peek(ctx)->type == TOK_SEMICOLON || peek(ctx)->type == TOK_ENDLINE) {
advance(ctx);
skip_nl(ctx);
continue;
}
spl_tok_type_t t = peek(ctx)->type;
int is_keyword = (t == KW_RET || t == KW_VAR || t == KW_CONST || t == KW_IF ||
t == KW_WHILE || t == KW_LOOP || t == KW_FOR || t == KW_BREAK ||
t == KW_CONTINUE || t == KW_DEFER || t == KW_MATCH || t == KW_TYPE ||
t == TOK_L_BRACE || t == TOK_SHARP || t == TOK_LINE_COMMENT);
if (is_keyword) {
/* Keyword statement — produces void */
spl_parse_stmt(ctx);
result = (spl_expr_result_t){0};
} else {
/* Expression — look ahead for assignment */
int is_assign = lookahead_is_assign(ctx);
int saved_addr = ctx->addr_of_mode;
if (is_assign)
ctx->addr_of_mode = 1;
spl_expr_result_t expr = spl_parse_expr(ctx, PREC_MIN);
ctx->addr_of_mode = saved_addr;
skip_nl(ctx);
if (peek(ctx)->type == TOK_SEMICOLON || peek(ctx)->type == TOK_ENDLINE) {
/* Expression statement: drop value, consume ; */
if (!is_assign && expr.type && expr.type->kind == TYPE_BASIC &&
expr.type->basic_type != SPL_VOID)
spl_emit(ctx, SPL_DROP, SPL_VOID, 0);
while (peek(ctx)->type == TOK_SEMICOLON || peek(ctx)->type == TOK_ENDLINE)
advance(ctx);
result = (spl_expr_result_t){0};
} else {
/* Trailing expression — this is the block's value */
result = expr;
}
}
skip_nl(ctx);
}
spl_emit_defer_epilogue(ctx, ctx->scope_depth);
spl_pop_scope(ctx);
expect(ctx, TOK_R_BRACE);
return result;
}
/* ============================================================ /* ============================================================
* If statement: if expr { ... } [else { ... }] * If statement: if expr { ... } [else { ... }]
* ============================================================ */ * ============================================================ */
@@ -234,41 +230,53 @@ static void parse_if_stmt(spl_comp_t *ctx) {
} }
} }
/* ============================================================
* Loop context helpers (save/restore loop state for while/loop/for)
* ============================================================ */
typedef struct {
int saved_loop;
usize saved_continue;
usize saved_bp_count;
spl_val_t loop_start;
} spl_loop_save_t;
static void loop_enter(spl_comp_t *ctx, spl_loop_save_t *save) {
save->loop_start = vec_size(ctx->prog.insns);
save->saved_loop = ctx->in_loop;
save->saved_continue = ctx->continue_target;
save->saved_bp_count = ctx->break_patch_count;
ctx->in_loop = 1;
ctx->continue_target = save->loop_start;
}
static void loop_exit(spl_comp_t *ctx, spl_loop_save_t *save) {
spl_val_t here = vec_size(ctx->prog.insns);
spl_emit(ctx, SPL_JMP, SPL_VOID, (spl_val_t)((isize)save->loop_start - (isize)here - 1));
for (usize i = save->saved_bp_count; i < ctx->break_patch_count; i++)
spl_patch_to_here(ctx, ctx->break_patches[i]);
ctx->break_patch_count = save->saved_bp_count;
ctx->in_loop = save->saved_loop;
ctx->continue_target = save->saved_continue;
}
/* ============================================================ /* ============================================================
* While statement: while expr { ... } * While statement: while expr { ... }
* ============================================================ */ * ============================================================ */
static void parse_while_stmt(spl_comp_t *ctx) { static void parse_while_stmt(spl_comp_t *ctx) {
spl_val_t loop_start = vec_size(ctx->prog.insns);
int saved_loop = ctx->in_loop;
usize saved_continue = ctx->continue_target;
usize saved_bp_count = ctx->break_patch_count;
ctx->in_loop = 1;
ctx->continue_target = loop_start;
advance(ctx); /* while */ advance(ctx); /* while */
skip_nl(ctx); skip_nl(ctx);
spl_loop_save_t save;
loop_enter(ctx, &save);
spl_expr_result_t cond = spl_parse_expr(ctx, PREC_MIN); spl_expr_result_t cond = spl_parse_expr(ctx, PREC_MIN);
(void)cond; (void)cond;
spl_val_t bz_addr = spl_emit_bz(ctx); spl_val_t bz_addr = spl_emit_bz(ctx);
skip_nl(ctx); skip_nl(ctx);
spl_parse_block(ctx); spl_parse_block(ctx);
loop_exit(ctx, &save);
/* JMP back to loop condition — use relative offset */
spl_val_t here = vec_size(ctx->prog.insns);
spl_emit(ctx, SPL_JMP, SPL_VOID, (spl_val_t)((isize)loop_start - (isize)here - 1));
spl_patch_to_here(ctx, bz_addr); spl_patch_to_here(ctx, bz_addr);
/* Patch break statements — compute relative offset */
for (usize i = saved_bp_count; i < ctx->break_patch_count; i++) {
spl_patch_to_here(ctx, ctx->break_patches[i]);
}
ctx->break_patch_count = saved_bp_count;
ctx->in_loop = saved_loop;
ctx->continue_target = saved_continue;
} }
/* ============================================================ /* ============================================================
@@ -276,30 +284,12 @@ static void parse_while_stmt(spl_comp_t *ctx) {
* ============================================================ */ * ============================================================ */
static void parse_loop_stmt(spl_comp_t *ctx) { static void parse_loop_stmt(spl_comp_t *ctx) {
spl_val_t loop_start = vec_size(ctx->prog.insns); spl_loop_save_t save;
int saved_loop = ctx->in_loop; loop_enter(ctx, &save);
usize saved_continue = ctx->continue_target;
usize saved_bp_count = ctx->break_patch_count;
ctx->in_loop = 1;
ctx->continue_target = loop_start;
advance(ctx); /* loop */ advance(ctx); /* loop */
skip_nl(ctx); skip_nl(ctx);
spl_parse_block(ctx); spl_parse_block(ctx);
loop_exit(ctx, &save);
/* JMP back to loop start — use relative offset */
spl_val_t here = vec_size(ctx->prog.insns);
spl_emit(ctx, SPL_JMP, SPL_VOID, (spl_val_t)((isize)loop_start - (isize)here - 1));
/* Patch break statements — compute relative offset */
for (usize i = saved_bp_count; i < ctx->break_patch_count; i++) {
spl_patch_to_here(ctx, ctx->break_patches[i]);
}
ctx->break_patch_count = saved_bp_count;
ctx->in_loop = saved_loop;
ctx->continue_target = saved_continue;
} }
/* ============================================================ /* ============================================================
@@ -329,9 +319,7 @@ static void parse_for_stmt(spl_comp_t *ctx) {
spl_tok_t *ivar = advance(ctx); spl_tok_t *ivar = advance(ctx);
char iname[256]; char iname[256];
usize inl = ivar->len < 255 ? ivar->len : 255; spl_tok_copy_name(ivar, iname, sizeof(iname));
memcpy(iname, ivar->lexeme, inl);
iname[inl] = '\0';
spl_push_scope(ctx); spl_push_scope(ctx);
int ioffset = spl_declare_var(ctx, iname, spl_type_basic(SPL_USIZE), 0); int ioffset = spl_declare_var(ctx, iname, spl_type_basic(SPL_USIZE), 0);
@@ -344,21 +332,15 @@ static void parse_for_stmt(spl_comp_t *ctx) {
spl_emit(ctx, SPL_STORE, SPL_USIZE, 0); spl_emit(ctx, SPL_STORE, SPL_USIZE, 0);
/* Stack: [end] */ /* Stack: [end] */
spl_val_t loop_start = vec_size(ctx->prog.insns);
/* Condition: i < end */ /* Condition: i < end */
spl_loop_save_t save;
loop_enter(ctx, &save);
spl_emit(ctx, SPL_LADDR, SPL_PTR, ioffset); spl_emit(ctx, SPL_LADDR, SPL_PTR, ioffset);
spl_emit(ctx, SPL_LOAD, SPL_USIZE, 0); spl_emit(ctx, SPL_LOAD, SPL_USIZE, 0);
spl_emit(ctx, SPL_PICK, SPL_VOID, 1); spl_emit(ctx, SPL_PICK, SPL_VOID, 1);
spl_emit(ctx, SPL_ULT, SPL_USIZE, 0); spl_emit(ctx, SPL_ULT, SPL_USIZE, 0);
spl_val_t bz_addr = spl_emit_bz(ctx); spl_val_t bz_addr = spl_emit_bz(ctx);
int saved_loop = ctx->in_loop;
usize saved_continue = ctx->continue_target;
usize saved_bp_count = ctx->break_patch_count;
ctx->in_loop = 1;
ctx->continue_target = loop_start;
skip_nl(ctx); skip_nl(ctx);
spl_parse_block(ctx); /* body */ spl_parse_block(ctx); /* body */
@@ -370,21 +352,12 @@ static void parse_for_stmt(spl_comp_t *ctx) {
spl_emit(ctx, SPL_ADD, SPL_USIZE, 0); spl_emit(ctx, SPL_ADD, SPL_USIZE, 0);
spl_emit(ctx, SPL_STORE, SPL_USIZE, 0); spl_emit(ctx, SPL_STORE, SPL_USIZE, 0);
/* JMP back to condition */ loop_exit(ctx, &save);
spl_val_t jmp_here = vec_size(ctx->prog.insns);
spl_emit(ctx, SPL_JMP, SPL_VOID, (spl_val_t)((isize)loop_start - (isize)jmp_here - 1));
/* Exit: patch bz, drop end */ /* Exit: patch bz, drop end */
spl_patch_to_here(ctx, bz_addr); spl_patch_to_here(ctx, bz_addr);
spl_emit(ctx, SPL_DROP, SPL_VOID, 0); spl_emit(ctx, SPL_DROP, SPL_VOID, 0);
/* Patch breaks */
for (usize i = saved_bp_count; i < ctx->break_patch_count; i++)
spl_patch_to_here(ctx, ctx->break_patches[i]);
ctx->break_patch_count = saved_bp_count;
ctx->in_loop = saved_loop;
ctx->continue_target = saved_continue;
spl_emit_defer_epilogue(ctx, ctx->scope_depth); spl_emit_defer_epilogue(ctx, ctx->scope_depth);
spl_pop_scope(ctx); spl_pop_scope(ctx);
return; return;
@@ -416,18 +389,14 @@ static void parse_for_stmt(spl_comp_t *ctx) {
/* Parse val variable name */ /* Parse val variable name */
spl_tok_t *vtok = advance(ctx); spl_tok_t *vtok = advance(ctx);
char vname[256]; char vname[256];
usize vnl = vtok->len < 255 ? vtok->len : 255; spl_tok_copy_name(vtok, vname, sizeof(vname));
memcpy(vname, vtok->lexeme, vnl);
vname[vnl] = '\0';
/* Parse optional idx variable name */ /* Parse optional idx variable name */
char iname[256] = {0}; char iname[256] = {0};
if (peek(ctx)->type == TOK_COMMA) { if (peek(ctx)->type == TOK_COMMA) {
advance(ctx); advance(ctx);
spl_tok_t *itok = advance(ctx); spl_tok_t *itok = advance(ctx);
usize inl = itok->len < 255 ? itok->len : 255; spl_tok_copy_name(itok, iname, sizeof(iname));
memcpy(iname, itok->lexeme, inl);
iname[inl] = '\0';
} }
/* Stack: [slice_addr] or whatever the slice expression left */ /* Stack: [slice_addr] or whatever the slice expression left */
@@ -458,7 +427,8 @@ static void parse_for_stmt(spl_comp_t *ctx) {
spl_emit(ctx, SPL_PUSH, SPL_USIZE, 0); spl_emit(ctx, SPL_PUSH, SPL_USIZE, 0);
/* Stack: [ptr, len, idx=0] */ /* Stack: [ptr, len, idx=0] */
spl_val_t loop_start = vec_size(ctx->prog.insns); spl_loop_save_t save;
loop_enter(ctx, &save);
/* Condition: idx < len */ /* Condition: idx < len */
spl_emit(ctx, SPL_PICK, SPL_VOID, 1); /* copy len */ spl_emit(ctx, SPL_PICK, SPL_VOID, 1); /* copy len */
@@ -478,21 +448,15 @@ static void parse_for_stmt(spl_comp_t *ctx) {
/* Load slice[idx] and store to val */ /* Load slice[idx] and store to val */
spl_emit(ctx, SPL_PICK, SPL_VOID, 2); /* copy ptr: [ptr, len, idx, ptr] */ spl_emit(ctx, SPL_PICK, SPL_VOID, 2); /* copy ptr: [ptr, len, idx, ptr] */
spl_emit(ctx, SPL_PICK, SPL_VOID, 1); /* copy idx: [ptr, len, idx, ptr, idx] */ spl_emit(ctx, SPL_PICK, SPL_VOID, 1); /* copy idx: [ptr, len, idx, ptr, idx] */
usize elem_byte_size = (elem_type && elem_type->byte_size) ? elem_type->byte_size : 4; usize elem_byte_size = elem_type ? spl_type_size(elem_type) : 4;
spl_emit(ctx, SPL_PUSH, SPL_U64, elem_byte_size); spl_emit(ctx, SPL_PUSH, SPL_U64, elem_byte_size);
spl_emit(ctx, SPL_MUL, SPL_U64, 0); spl_emit(ctx, SPL_MUL, SPL_U64, 0);
spl_emit(ctx, SPL_ADD, SPL_U64, 0); spl_emit(ctx, SPL_ADD, SPL_U64, 0);
spl_emit(ctx, SPL_LOAD, SPL_I32, 0); spl_type_t elem_bt = elem_type ? spl_type_emit_type(elem_type) : SPL_I32;
spl_emit(ctx, SPL_LOAD, elem_bt, 0);
spl_emit(ctx, SPL_LADDR, SPL_PTR, val_offset); spl_emit(ctx, SPL_LADDR, SPL_PTR, val_offset);
spl_emit(ctx, SPL_SWAP, SPL_VOID, 0); spl_emit(ctx, SPL_SWAP, SPL_VOID, 0);
spl_emit(ctx, SPL_STORE, SPL_I32, 0); spl_emit(ctx, SPL_STORE, elem_bt, 0);
/* Loop context */
int saved_loop = ctx->in_loop;
usize saved_continue = ctx->continue_target;
usize saved_bp_count = ctx->break_patch_count;
ctx->in_loop = 1;
ctx->continue_target = loop_start;
skip_nl(ctx); skip_nl(ctx);
spl_parse_block(ctx); /* body */ spl_parse_block(ctx); /* body */
@@ -504,9 +468,7 @@ static void parse_for_stmt(spl_comp_t *ctx) {
spl_emit(ctx, SPL_SWAP, SPL_VOID, 0); spl_emit(ctx, SPL_SWAP, SPL_VOID, 0);
spl_emit(ctx, SPL_DROP, SPL_VOID, 0); /* replace old idx with new */ spl_emit(ctx, SPL_DROP, SPL_VOID, 0); /* replace old idx with new */
/* JMP back to condition */ loop_exit(ctx, &save);
spl_val_t jmp_here = vec_size(ctx->prog.insns);
spl_emit(ctx, SPL_JMP, SPL_VOID, (spl_val_t)((isize)loop_start - (isize)jmp_here - 1));
/* Exit: patch bz, drop idx, len, ptr */ /* Exit: patch bz, drop idx, len, ptr */
spl_patch_to_here(ctx, bz_addr); spl_patch_to_here(ctx, bz_addr);
@@ -514,13 +476,6 @@ static void parse_for_stmt(spl_comp_t *ctx) {
spl_emit(ctx, SPL_DROP, SPL_VOID, 0); /* drop len */ spl_emit(ctx, SPL_DROP, SPL_VOID, 0); /* drop len */
spl_emit(ctx, SPL_DROP, SPL_VOID, 0); /* drop ptr */ spl_emit(ctx, SPL_DROP, SPL_VOID, 0); /* drop ptr */
/* Patch breaks */
for (usize i = saved_bp_count; i < ctx->break_patch_count; i++)
spl_patch_to_here(ctx, ctx->break_patches[i]);
ctx->break_patch_count = saved_bp_count;
ctx->in_loop = saved_loop;
ctx->continue_target = saved_continue;
spl_pop_scope(ctx); spl_pop_scope(ctx);
} }
@@ -582,10 +537,88 @@ static void parse_defer_stmt(spl_comp_t *ctx) {
} }
} }
/* ============================================================
* Match statement helpers
* ============================================================ */
/* Parse an enum variant pattern in a match arm: .VariantName
* Delegates comparison to expr layer (spl_emit_match_enum_cmp).
* Returns the variant for binding parsing, or NULL on error. */
static spl_enum_variant_t *parse_match_enum_variant(spl_comp_t *ctx, spl_type_info_t *enum_type,
int val_offset) {
return spl_emit_match_enum_cmp(ctx, enum_type, val_offset);
}
/* Parse a value pattern in a match arm (non-enum).
* Delegates comparison to expr layer (spl_emit_match_value_cmp).
* Uses PREC_LOGOR internally to prevent => from being consumed as assignment. */
static void parse_match_value_pattern(spl_comp_t *ctx, int val_offset) {
spl_emit_match_value_cmp(ctx, val_offset);
}
/* Parse enum variant data bindings: (name1, name2, ...)
* Declares local variables and loads corresponding field data
* from the matched value's data area (offset 4+).
* Sets *scope_pushed = 1 if bindings declared. */
static void parse_match_enum_bindings(spl_comp_t *ctx, spl_enum_variant_t *variant, int val_offset,
int *scope_pushed) {
advance(ctx); /* ( */
skip_nl(ctx);
if (peek(ctx)->type == TOK_R_PAREN) {
expect(ctx, TOK_R_PAREN);
return;
}
spl_push_scope(ctx);
*scope_pushed = 1;
if (variant->data_type && variant->data_type->kind == TYPE_STRUCT) {
/* Multi-field struct destructuring: one binding per struct field */
int bi = 0;
for (;;) {
spl_tok_t *btok = advance(ctx);
char bname[256];
spl_tok_copy_name(btok, bname, sizeof(bname));
spl_type_info_t *btype = spl_type_basic(SPL_I32);
usize field_byte_off = 4; /* skip tag */
if ((usize)bi < vec_size(variant->data_type->fields)) {
btype = vec_at(variant->data_type->fields, bi).type;
field_byte_off = 4 + vec_at(variant->data_type->fields, bi).offset;
}
int boffset = spl_declare_var(ctx, bname, btype, 0);
spl_emit_load_to_var(ctx, val_offset, field_byte_off, btype, boffset);
bi++;
skip_nl(ctx);
if (peek(ctx)->type == TOK_COMMA) {
advance(ctx);
skip_nl(ctx);
continue;
}
break;
}
} else if (variant->data_type) {
/* Single-value binding */
spl_tok_t *btok = advance(ctx);
char bname[256];
spl_tok_copy_name(btok, bname, sizeof(bname));
spl_type_info_t *btype = variant->data_type;
int boffset = spl_declare_var(ctx, bname, btype, 0);
spl_emit_load_to_var(ctx, val_offset, 4, btype, boffset);
}
expect(ctx, TOK_R_PAREN);
}
/* ============================================================ /* ============================================================
* Match statement: * Match statement:
* Enum: match expr { .Variant(bindings) => stmt, ... } * Enum: match expr { .Variant(bindings) => stmt, ... }
* Int: match expr { literal => stmt, ..., _ => stmt } * Value: match expr { lit, lit => stmt, ..., _ => stmt }
*
* Built as enhanced if-else: each arm is a condition chain.
* Fallthrough (comma-separated patterns) uses BNZ for OR.
* ============================================================ */ * ============================================================ */
static void parse_match_stmt(spl_comp_t *ctx) { static void parse_match_stmt(spl_comp_t *ctx) {
@@ -594,11 +627,9 @@ static void parse_match_stmt(spl_comp_t *ctx) {
spl_expr_result_t expr = spl_parse_expr(ctx, PREC_MIN); spl_expr_result_t expr = spl_parse_expr(ctx, PREC_MIN);
/* Determine match type (enum or integer) */ /* Determine match type */
int is_enum_match = 0; int is_enum_match = 0;
int is_int_match = 0;
spl_type_info_t *enum_type = NULL; spl_type_info_t *enum_type = NULL;
spl_type_info_t *t = expr.type; spl_type_info_t *t = expr.type;
if (t && t->kind == TYPE_ENUM) { if (t && t->kind == TYPE_ENUM) {
is_enum_match = 1; is_enum_match = 1;
@@ -606,16 +637,12 @@ static void parse_match_stmt(spl_comp_t *ctx) {
} else if (t && t->kind == TYPE_PTR && t->elem && t->elem->kind == TYPE_ENUM) { } else if (t && t->kind == TYPE_PTR && t->elem && t->elem->kind == TYPE_ENUM) {
is_enum_match = 1; is_enum_match = 1;
enum_type = t->elem; enum_type = t->elem;
} else if (t && t->kind == TYPE_BASIC && spl_type_is_integer(t->basic_type)) { } else if (!(t && t->kind == TYPE_BASIC && spl_type_is_integer(t->basic_type))) {
is_int_match = 1;
}
if (!is_enum_match && !is_int_match) {
spl_comp_error(ctx, "match expression must be an enum or integer type"); spl_comp_error(ctx, "match expression must be an enum or integer type");
return; return;
} }
/* Save the value/address to a temp slot */ /* Save match value to temp slot */
int val_offset = ctx->current_local_bytes; int val_offset = ctx->current_local_bytes;
ctx->current_local_bytes += (int)sizeof(spl_val_t); ctx->current_local_bytes += (int)sizeof(spl_val_t);
if (ctx->current_local_bytes > ctx->peak_local_bytes) if (ctx->current_local_bytes > ctx->peak_local_bytes)
@@ -650,57 +677,23 @@ static void parse_match_stmt(spl_comp_t *ctx) {
int n_bnz = 0; int n_bnz = 0;
spl_enum_variant_t *arm_variant = NULL; spl_enum_variant_t *arm_variant = NULL;
/* --- Parse arm pattern(s): comma-separated values with fallthrough --- */ /* --- Parse arm pattern(s): comma-separated with fallthrough --- */
if (peek(ctx)->type == KW_ANY) { if (peek(ctx)->type == KW_ANY) {
/* _ default arm: always matches, no comparison */ /* _ default arm: always matches, skip comparison */
advance(ctx); advance(ctx);
} else { } else {
for (;;) { for (;;) {
if (is_enum_match) { if (is_enum_match) {
/* .VariantName */ arm_variant = parse_match_enum_variant(ctx, enum_type, val_offset);
if (peek(ctx)->type == TOK_DOT) if (ctx->has_error)
advance(ctx);
spl_tok_t *vtok = advance(ctx);
char vname[256];
usize vnl = vtok->len < 255 ? vtok->len : 255;
memcpy(vname, vtok->lexeme, vnl);
vname[vnl] = '\0';
spl_enum_variant_t *variant = NULL;
vec_for(enum_type->variants, vi) {
if (strcmp(vec_at(enum_type->variants, vi).name, vname) == 0) {
variant = &vec_at(enum_type->variants, vi);
break; break;
}
}
if (!variant) {
spl_comp_error(ctx, "unknown variant '%s' in match", vname);
break;
}
arm_variant = variant;
/* Compare tag: load [val_offset] → addr → +0 → tag */
spl_emit(ctx, SPL_LADDR, SPL_PTR, val_offset);
spl_emit(ctx, SPL_LOAD, SPL_PTR, 0);
spl_emit(ctx, SPL_PUSH, SPL_USIZE, 0);
spl_emit(ctx, SPL_ADD, SPL_USIZE, 0);
spl_emit(ctx, SPL_LOAD, SPL_I32, 0);
spl_emit(ctx, SPL_PUSH, SPL_I32, variant->value);
spl_emit(ctx, SPL_EQ, SPL_I32, 0);
/* If bindings follow, this must be the last pattern */
skip_nl(ctx); skip_nl(ctx);
if (peek(ctx)->type == TOK_L_PAREN) { if (peek(ctx)->type == TOK_L_PAREN) {
has_parens = 1; has_parens = 1;
break; break; /* bindings → must be last in fallthrough group */
} }
} else if (is_int_match) { } else {
/* Parse expression as arm value */ parse_match_value_pattern(ctx, val_offset);
spl_parse_expr(ctx, PREC_MIN);
/* Compare: load saved match value, then EQ */
spl_emit(ctx, SPL_LADDR, SPL_PTR, val_offset);
spl_emit(ctx, SPL_LOAD, SPL_PTR, 0);
spl_emit(ctx, SPL_EQ, SPL_I32, 0);
} }
/* Check for more comma-separated patterns */ /* Check for more comma-separated patterns */
@@ -708,9 +701,8 @@ static void parse_match_stmt(spl_comp_t *ctx) {
if (peek(ctx)->type == TOK_COMMA) { if (peek(ctx)->type == TOK_COMMA) {
advance(ctx); advance(ctx);
skip_nl(ctx); skip_nl(ctx);
/* If another pattern follows (not ⇒), emit BNZ for fallthrough */
if (peek(ctx)->type != TOK_ASSIGN) { if (peek(ctx)->type != TOK_ASSIGN) {
bnz_addrs[n_bnz++] = spl_emit_bnz(ctx); bnz_addrs[n_bnz++] = spl_emit_bnz(ctx); /* fallthrough to body */
continue; continue;
} }
break; break;
@@ -718,7 +710,7 @@ static void parse_match_stmt(spl_comp_t *ctx) {
break; break;
} }
/* Last pattern: BZ to skip arm if no value matched */ /* Last pattern: BZ past body if no match */
if (!ctx->has_error) { if (!ctx->has_error) {
bz_addr = spl_emit_bz(ctx); bz_addr = spl_emit_bz(ctx);
body_start = vec_size(ctx->prog.insns); body_start = vec_size(ctx->prog.insns);
@@ -727,53 +719,7 @@ static void parse_match_stmt(spl_comp_t *ctx) {
/* --- Parse enum bindings (only for last variant) --- */ /* --- Parse enum bindings (only for last variant) --- */
if (is_enum_match && has_parens && arm_variant) { if (is_enum_match && has_parens && arm_variant) {
advance(ctx); /* ( */ parse_match_enum_bindings(ctx, arm_variant, val_offset, &scope_pushed);
skip_nl(ctx);
if (peek(ctx)->type != TOK_R_PAREN) {
spl_push_scope(ctx);
scope_pushed = 1;
if (arm_variant->data_type && arm_variant->data_type->kind == TYPE_STRUCT) {
int bi = 0;
for (;;) {
spl_tok_t *btok = advance(ctx);
char bname[256];
usize bnl = btok->len < 255 ? btok->len : 255;
memcpy(bname, btok->lexeme, bnl);
bname[bnl] = '\0';
spl_type_info_t *btype = spl_type_basic(SPL_I32);
usize field_byte_off = 4;
if ((usize)bi < vec_size(arm_variant->data_type->fields)) {
btype = vec_at(arm_variant->data_type->fields, bi).type;
field_byte_off = 4 + vec_at(arm_variant->data_type->fields, bi).offset;
}
int boffset = spl_declare_var(ctx, bname, btype, 0);
spl_emit_load_to_var(ctx, val_offset, field_byte_off, btype, boffset);
bi++;
skip_nl(ctx);
if (peek(ctx)->type == TOK_COMMA) {
advance(ctx);
skip_nl(ctx);
continue;
}
break;
}
} else if (arm_variant->data_type) {
spl_tok_t *btok = advance(ctx);
char bname[256];
usize bnl = btok->len < 255 ? btok->len : 255;
memcpy(bname, btok->lexeme, bnl);
bname[bnl] = '\0';
spl_type_info_t *btype = arm_variant->data_type;
int boffset = spl_declare_var(ctx, bname, btype, 0);
spl_emit_load_to_var(ctx, val_offset, 4, btype, boffset);
}
}
expect(ctx, TOK_R_PAREN);
} }
skip_nl(ctx); skip_nl(ctx);
@@ -786,10 +732,10 @@ static void parse_match_stmt(spl_comp_t *ctx) {
} }
skip_nl(ctx); skip_nl(ctx);
/* Parse arm body statement */ /* Parse arm body (reuses stmt infrastructure) */
spl_parse_stmt(ctx); spl_parse_stmt(ctx);
/* Pop scope if we pushed one */ /* Pop scope if bindings were declared */
if (scope_pushed) { if (scope_pushed) {
spl_emit_defer_epilogue(ctx, ctx->scope_depth); spl_emit_defer_epilogue(ctx, ctx->scope_depth);
spl_pop_scope(ctx); spl_pop_scope(ctx);
@@ -803,7 +749,7 @@ static void parse_match_stmt(spl_comp_t *ctx) {
if (bz_addr) if (bz_addr)
spl_patch_to_here(ctx, bz_addr); spl_patch_to_here(ctx, bz_addr);
/* Patch BNZs to body start (fallthrough values matched) */ /* Patch BNZ fallthroughs to body start */
for (int i = 0; i < n_bnz; i++) { for (int i = 0; i < n_bnz; i++) {
spl_val_t offset = body_start - bnz_addrs[i] - 1; spl_val_t offset = body_start - bnz_addrs[i] - 1;
spl_patch(ctx, bnz_addrs[i], offset); spl_patch(ctx, bnz_addrs[i], offset);
@@ -842,9 +788,7 @@ static void parse_extern_decl(spl_comp_t *ctx) {
skip_nl(ctx); skip_nl(ctx);
spl_tok_t *fname_tok = advance(ctx); spl_tok_t *fname_tok = advance(ctx);
char fn_name[256]; char fn_name[256];
usize fnl = fname_tok->len < 255 ? fname_tok->len : 255; spl_tok_copy_name(fname_tok, fn_name, sizeof(fn_name));
memcpy(fn_name, fname_tok->lexeme, fnl);
fn_name[fnl] = '\0';
skip_nl(ctx); skip_nl(ctx);
expect(ctx, TOK_L_PAREN); expect(ctx, TOK_L_PAREN);
@@ -906,6 +850,25 @@ static int is_assign_op(spl_tok_type_t t) {
t == TOK_ASSIGN_R_SH; t == TOK_ASSIGN_R_SH;
} }
/* Scan ahead to check if the expression at current position is an assignment.
* Returns 1 if an assignment operator is found before a statement terminator
* or function-call paren. */
static int lookahead_is_assign(spl_comp_t *ctx) {
usize look = ctx->tok_idx;
while (look < vec_size(ctx->toks)) {
spl_tok_type_t t = vec_at(ctx->toks, look).type;
if (t == TOK_SEMICOLON || t == TOK_ENDLINE || t == TOK_L_BRACE || t == TOK_R_BRACE ||
t == TOK_EOF)
break;
if (t == TOK_L_PAREN)
break;
if (is_assign_op(t))
return 1;
look++;
}
return 0;
}
static void parse_expr_stmt(spl_comp_t *ctx) { static void parse_expr_stmt(spl_comp_t *ctx) {
skip_nl(ctx); skip_nl(ctx);
if (peek(ctx)->type == KW_RET) { if (peek(ctx)->type == KW_RET) {
@@ -915,24 +878,8 @@ static void parse_expr_stmt(spl_comp_t *ctx) {
usize prev = ctx->tok_idx; usize prev = ctx->tok_idx;
/* Look ahead for assignment operator (scan past expression tokens) */ /* Look ahead for assignment operator */
int is_assign = 0; int is_assign = lookahead_is_assign(ctx);
{
usize look = ctx->tok_idx;
while (look < vec_size(ctx->toks)) {
spl_tok_type_t t = vec_at(ctx->toks, look).type;
if (t == TOK_SEMICOLON || t == TOK_ENDLINE || t == TOK_L_BRACE || t == TOK_R_BRACE ||
t == TOK_EOF)
break;
if (t == TOK_L_PAREN)
break; /* function call, not assignment */
if (is_assign_op(t)) {
is_assign = 1;
break;
}
look++;
}
}
if (is_assign) if (is_assign)
ctx->addr_of_mode = 1; ctx->addr_of_mode = 1;

View File

@@ -403,9 +403,7 @@ spl_type_info_t *spl_parse_type(spl_comp_t *ctx) {
advance(ctx); /* : */ advance(ctx); /* : */
spl_type_info_t *ftype = spl_parse_type(ctx); spl_type_info_t *ftype = spl_parse_type(ctx);
char fname[256]; char fname[256];
usize fnl = ftok->len < 255 ? ftok->len : 255; spl_tok_copy_name(ftok, fname, sizeof(fname));
memcpy(fname, ftok->lexeme, fnl);
fname[fnl] = '\0';
spl_type_add_field(t, fname, ftype); spl_type_add_field(t, fname, ftype);
} }
if (peek(ctx)->type == TOK_COMMA) if (peek(ctx)->type == TOK_COMMA)
@@ -430,15 +428,11 @@ spl_type_info_t *spl_parse_type(spl_comp_t *ctx) {
advance(ctx); /* : */ advance(ctx); /* : */
spl_type_info_t *dtype = spl_parse_type(ctx); spl_type_info_t *dtype = spl_parse_type(ctx);
char vname[256]; char vname[256];
usize vnl = vtok->len < 255 ? vtok->len : 255; spl_tok_copy_name(vtok, vname, sizeof(vname));
memcpy(vname, vtok->lexeme, vnl);
vname[vnl] = '\0';
spl_type_add_variant(t, vname, dtype); spl_type_add_variant(t, vname, dtype);
} else { } else {
char vname[256]; char vname[256];
usize vnl = vtok->len < 255 ? vtok->len : 255; spl_tok_copy_name(vtok, vname, sizeof(vname));
memcpy(vname, vtok->lexeme, vnl);
vname[vnl] = '\0';
spl_type_add_variant(t, vname, NULL); spl_type_add_variant(t, vname, NULL);
} }
if (peek(ctx)->type == TOK_COMMA) if (peek(ctx)->type == TOK_COMMA)

View File

@@ -34,7 +34,7 @@ type Expr = enum {
fn main() i32 { fn main() i32 {
/* struct 方法调用 */ /* struct 方法调用 */
var p: Point = Point.init(3, 4); var p: Point = Point.init(3, 4);
p.dump(); p.dump(&p);
/* enum 方法 + match */ /* enum 方法 + match */
var expr_l := Expr { .Int = 3 }; var expr_l := Expr { .Int = 3 };

View File

@@ -156,23 +156,23 @@ fn test_nested_struct() i32 {
fn test_struct_method() i32 { fn test_struct_method() i32 {
var c: Counter = Counter { .val = 0 }; var c: Counter = Counter { .val = 0 };
/* 实例方法调用 c.inc() */ /* 实例方法调用 c.inc(&c) */
var r1: i32 = c.inc(); var r1: i32 = c.inc(&c);
if r1 != 1 { ret 1; } if r1 != 1 { ret 1; }
if c.val != 1 { ret 2; } if c.val != 1 { ret 2; }
/* 带参数方法调用 c.add(n) */ /* 带参数方法调用 c.add(&c, n) */
var r2: i32 = c.add(5); var r2: i32 = c.add(&c, 5);
if r2 != 6 { ret 3; } if r2 != 6 { ret 3; }
if c.val != 6 { ret 4; } if c.val != 6 { ret 4; }
/* 连续调用 */ /* 连续调用 (显式 self) */
c.reset(); c.reset(&c);
if c.val != 0 { ret 5; } if c.val != 0 { ret 5; }
c.add(10); c.add(&c, 10);
c.inc(); c.inc(&c);
var r3: i32 = c.get(); var r3: i32 = c.get(&c);
if r3 != 11 { ret 6; } if r3 != 11 { ret 6; }
ret 0; ret 0;