stage1 优化代码 完成16测试
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
/* spl_expr.c — Expression parser + codegen (Pratt parser / precedence climbing) */
|
||||
|
||||
#include "spl_comp.h"
|
||||
#include <ctype.h>
|
||||
#include "spl_lex_util.h"
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
@@ -10,32 +10,6 @@
|
||||
* Token helpers
|
||||
* ============================================================ */
|
||||
|
||||
/* Token helpers — using shared peek/advance from spl_parser.c */
|
||||
|
||||
static int match(spl_comp_t *ctx, spl_tok_type_t type) {
|
||||
if (peek(ctx)->type == type) {
|
||||
advance(ctx);
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int expect(spl_comp_t *ctx, spl_tok_type_t type) {
|
||||
if (peek(ctx)->type == type) {
|
||||
advance(ctx);
|
||||
return 1;
|
||||
}
|
||||
spl_comp_error(ctx, "expected '%s', got '%s'", spl_tok_type_name(type),
|
||||
spl_tok_type_name(peek(ctx)->type));
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Skip newline tokens */
|
||||
static void skip_nl(spl_comp_t *ctx) {
|
||||
while (peek(ctx)->type == TOK_ENDLINE)
|
||||
advance(ctx);
|
||||
}
|
||||
|
||||
/* Check if current token starts a statement */
|
||||
static int is_stmt_start(spl_comp_t *ctx) {
|
||||
spl_tok_type_t t = peek(ctx)->type;
|
||||
@@ -155,8 +129,8 @@ static void emit_slice_index(spl_comp_t *ctx, spl_type_info_t *elem) {
|
||||
* Uses qualified name (TypeName.Member) first for namespace isolation,
|
||||
* then falls back to simple name lookup.
|
||||
* Returns 1 if resolved. */
|
||||
static int spl_resolve_type_member(spl_comp_t *ctx, spl_type_info_t *type,
|
||||
const char *field, spl_expr_result_t *result) {
|
||||
static int spl_resolve_type_member(spl_comp_t *ctx, spl_type_info_t *type, const char *field,
|
||||
spl_expr_result_t *result) {
|
||||
/* For enum: check variants first */
|
||||
if (type->kind == TYPE_ENUM) {
|
||||
vec_for(type->variants, i) {
|
||||
@@ -198,17 +172,28 @@ static int spl_resolve_type_member(spl_comp_t *ctx, spl_type_info_t *type,
|
||||
/* Map assignment operator token (e.g. +=) to corresponding binary operator (e.g. +) */
|
||||
static spl_tok_type_t assign_to_binop(spl_tok_type_t t) {
|
||||
switch (t) {
|
||||
case TOK_ASSIGN_ADD: return TOK_ADD;
|
||||
case TOK_ASSIGN_SUB: return TOK_SUB;
|
||||
case TOK_ASSIGN_MUL: return TOK_MUL;
|
||||
case TOK_ASSIGN_DIV: return TOK_DIV;
|
||||
case TOK_ASSIGN_MOD: return TOK_MOD;
|
||||
case TOK_ASSIGN_AND: return TOK_AND;
|
||||
case TOK_ASSIGN_OR: return TOK_OR;
|
||||
case TOK_ASSIGN_XOR: return TOK_XOR;
|
||||
case TOK_ASSIGN_L_SH: return TOK_L_SH;
|
||||
case TOK_ASSIGN_R_SH: return TOK_R_SH;
|
||||
default: return (spl_tok_type_t)-1;
|
||||
case TOK_ASSIGN_ADD:
|
||||
return TOK_ADD;
|
||||
case TOK_ASSIGN_SUB:
|
||||
return TOK_SUB;
|
||||
case TOK_ASSIGN_MUL:
|
||||
return TOK_MUL;
|
||||
case TOK_ASSIGN_DIV:
|
||||
return TOK_DIV;
|
||||
case TOK_ASSIGN_MOD:
|
||||
return TOK_MOD;
|
||||
case TOK_ASSIGN_AND:
|
||||
return TOK_AND;
|
||||
case TOK_ASSIGN_OR:
|
||||
return TOK_OR;
|
||||
case TOK_ASSIGN_XOR:
|
||||
return TOK_XOR;
|
||||
case TOK_ASSIGN_L_SH:
|
||||
return TOK_L_SH;
|
||||
case TOK_ASSIGN_R_SH:
|
||||
return TOK_R_SH;
|
||||
default:
|
||||
return (spl_tok_type_t)-1;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -398,10 +383,165 @@ static spl_expr_result_t parse_array_literal(spl_comp_t *ctx) {
|
||||
return (spl_expr_result_t){arr_type, 0};
|
||||
}
|
||||
|
||||
/* Parse a type name with optional .field suffix for enum variants:
|
||||
* TypeName
|
||||
* TypeName.field
|
||||
*/
|
||||
/* Determine correct STORE type for a type (handles ptr = 8 bytes). */
|
||||
static spl_type_t spl_store_type(spl_type_info_t *type) {
|
||||
if (!type)
|
||||
return SPL_I32;
|
||||
if (type->kind == TYPE_BASIC)
|
||||
return type->basic_type;
|
||||
if (type->kind == TYPE_PTR)
|
||||
return SPL_PTR;
|
||||
if (spl_type_slot_count(type) <= 1)
|
||||
return SPL_PTR;
|
||||
return SPL_I32;
|
||||
}
|
||||
|
||||
/* Parse struct/enum literal: Type { .field = val, ... }
|
||||
* Allocates temp slots for the value, returns lvalue (addr on stack).
|
||||
* For single-slot types, pushes the packed value directly. */
|
||||
static spl_expr_result_t parse_struct_literal(spl_comp_t *ctx, spl_type_info_t *type) {
|
||||
usize sc = spl_type_slot_count(type);
|
||||
int base_slot = ctx->current_local_slot;
|
||||
ctx->current_local_slot += (int)sc;
|
||||
|
||||
advance(ctx); /* { */
|
||||
skip_nl(ctx);
|
||||
|
||||
if (type->kind == TYPE_STRUCT) {
|
||||
while (peek(ctx)->type != TOK_R_BRACE && peek(ctx)->type != TOK_EOF) {
|
||||
if (peek(ctx)->type == TOK_COMMA) {
|
||||
advance(ctx);
|
||||
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);
|
||||
|
||||
vec_for(type->fields, fi) {
|
||||
spl_field_t *f = &vec_at(type->fields, fi);
|
||||
if (strcmp(f->name, fname) == 0) {
|
||||
spl_emit(ctx, SPL_LADDR, SPL_PTR, base_slot);
|
||||
if (f->offset > 0) {
|
||||
spl_emit(ctx, SPL_PUSH, SPL_USIZE, f->offset);
|
||||
spl_emit(ctx, SPL_ADD, SPL_USIZE, 0);
|
||||
}
|
||||
spl_expr_result_t fv = spl_parse_expr(ctx, PREC_MIN);
|
||||
(void)fv;
|
||||
spl_emit(ctx, SPL_STORE, spl_store_type(f->type), 0);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
skip_nl(ctx);
|
||||
}
|
||||
} else if (type->kind == TYPE_ENUM) {
|
||||
/* Parse .Variant [= value] */
|
||||
if (peek(ctx)->type == TOK_DOT)
|
||||
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';
|
||||
skip_nl(ctx);
|
||||
if (peek(ctx)->type == TOK_ASSIGN)
|
||||
advance(ctx);
|
||||
skip_nl(ctx);
|
||||
|
||||
int found = 0;
|
||||
vec_for(type->variants, vi) {
|
||||
spl_enum_variant_t *v = &vec_at(type->variants, vi);
|
||||
if (strcmp(v->name, vname) == 0) {
|
||||
found = 1;
|
||||
/* Store tag at offset 0 */
|
||||
spl_emit(ctx, SPL_LADDR, SPL_PTR, base_slot);
|
||||
spl_emit(ctx, SPL_PUSH, SPL_I32, v->value);
|
||||
spl_emit(ctx, SPL_STORE, SPL_I32, 0);
|
||||
|
||||
/* Store variant data at offset 4 */
|
||||
const usize DATA_OFFSET = 4;
|
||||
if (v->data_type) {
|
||||
if (v->data_type->kind == TYPE_STRUCT && peek(ctx)->type == TOK_L_BRACE) {
|
||||
/* Struct data: { .field = val, ... } */
|
||||
advance(ctx); /* { */
|
||||
skip_nl(ctx);
|
||||
while (peek(ctx)->type != TOK_R_BRACE && peek(ctx)->type != TOK_EOF) {
|
||||
if (peek(ctx)->type == TOK_COMMA) {
|
||||
advance(ctx);
|
||||
skip_nl(ctx);
|
||||
continue;
|
||||
}
|
||||
if (peek(ctx)->type == TOK_DOT)
|
||||
advance(ctx);
|
||||
spl_tok_t *sftok = advance(ctx);
|
||||
char sfname[256];
|
||||
usize sfnl = sftok->len < 255 ? sftok->len : 255;
|
||||
memcpy(sfname, sftok->lexeme, sfnl);
|
||||
sfname[sfnl] = '\0';
|
||||
skip_nl(ctx);
|
||||
if (peek(ctx)->type == TOK_ASSIGN)
|
||||
advance(ctx);
|
||||
skip_nl(ctx);
|
||||
|
||||
vec_for(v->data_type->fields, sfi) {
|
||||
spl_field_t *sf = &vec_at(v->data_type->fields, sfi);
|
||||
if (strcmp(sf->name, sfname) == 0) {
|
||||
spl_emit(ctx, SPL_LADDR, SPL_PTR, base_slot);
|
||||
usize byte_off = DATA_OFFSET + sf->offset;
|
||||
if (byte_off > 0) {
|
||||
spl_emit(ctx, SPL_PUSH, SPL_USIZE, byte_off);
|
||||
spl_emit(ctx, SPL_ADD, SPL_USIZE, 0);
|
||||
}
|
||||
spl_expr_result_t sfv = spl_parse_expr(ctx, PREC_MIN);
|
||||
(void)sfv;
|
||||
spl_emit(ctx, SPL_STORE, spl_store_type(sf->type), 0);
|
||||
break;
|
||||
}
|
||||
}
|
||||
skip_nl(ctx);
|
||||
}
|
||||
expect(ctx, TOK_R_BRACE);
|
||||
} else {
|
||||
/* Simple data: parse expression */
|
||||
spl_expr_result_t dv = spl_parse_expr(ctx, PREC_MIN);
|
||||
(void)dv;
|
||||
spl_emit(ctx, SPL_LADDR, SPL_PTR, base_slot);
|
||||
spl_emit(ctx, SPL_PUSH, SPL_USIZE, DATA_OFFSET);
|
||||
spl_emit(ctx, SPL_ADD, SPL_USIZE, 0);
|
||||
spl_type_t bt = spl_store_type(v->data_type);
|
||||
spl_emit(ctx, SPL_SWAP, SPL_VOID, 0);
|
||||
spl_emit(ctx, SPL_STORE, bt, 0);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!found)
|
||||
spl_comp_error(ctx, "unknown enum variant '%s'", vname);
|
||||
}
|
||||
|
||||
skip_nl(ctx);
|
||||
expect(ctx, TOK_R_BRACE);
|
||||
|
||||
/* Return: for single-slot, push packed value. For multi-slot, push address. */
|
||||
if (sc == 1) {
|
||||
spl_emit(ctx, SPL_LADDR, SPL_PTR, base_slot);
|
||||
spl_emit(ctx, SPL_LOAD, SPL_PTR, 0);
|
||||
return (spl_expr_result_t){type, 0}; /* value on stack */
|
||||
} else {
|
||||
spl_emit(ctx, SPL_LADDR, SPL_PTR, base_slot);
|
||||
return (spl_expr_result_t){type, 1}; /* address on stack */
|
||||
}
|
||||
}
|
||||
static spl_type_info_t *parse_qualified_type(spl_comp_t *ctx, const char *name) {
|
||||
/* Check if it's a known type */
|
||||
spl_type_info_t *t = spl_resolve_type(ctx, name);
|
||||
@@ -436,6 +576,12 @@ static spl_expr_result_t parse_ident(spl_comp_t *ctx) {
|
||||
/* Check if it's a function call: ident(...) */
|
||||
if (peek(ctx)->type == TOK_L_PAREN) {
|
||||
int fi = spl_lookup_func(ctx, name);
|
||||
/* Fallback: try qualified name for short-name resolution inside methods */
|
||||
if (fi < 0 && ctx->current_type_name) {
|
||||
char qualified[512];
|
||||
snprintf(qualified, sizeof(qualified), "%s.%s", ctx->current_type_name, name);
|
||||
fi = spl_lookup_func(ctx, qualified);
|
||||
}
|
||||
if (fi < 0) {
|
||||
spl_comp_error(ctx, "unknown function '%s'", name);
|
||||
spl_expr_result_t r = {0};
|
||||
@@ -525,7 +671,12 @@ static spl_expr_result_t parse_ident(spl_comp_t *ctx) {
|
||||
/* Check if it's a type name (for enum variant access like Color.Red) */
|
||||
spl_type_info_t *ttype = spl_resolve_type(ctx, name);
|
||||
if (ttype) {
|
||||
spl_expr_result_t r = {ttype, 1};
|
||||
/* Struct/enum literal: Type { .field = val, ... } */
|
||||
if (peek(ctx)->type == TOK_L_BRACE &&
|
||||
(ttype->kind == TYPE_STRUCT || ttype->kind == TYPE_ENUM)) {
|
||||
return parse_struct_literal(ctx, ttype);
|
||||
}
|
||||
spl_expr_result_t r = {ttype, 0};
|
||||
return r;
|
||||
}
|
||||
|
||||
@@ -669,9 +820,7 @@ spl_expr_result_t spl_parse_expr(spl_comp_t *ctx, int min_prec) {
|
||||
break;
|
||||
}
|
||||
char bname[256];
|
||||
usize bnlen = tok->len < 255 ? tok->len : 255;
|
||||
memcpy(bname, tok->lexeme, bnlen);
|
||||
bname[bnlen] = '\0';
|
||||
spl_tok_copy_name(tok, bname, sizeof bname);
|
||||
advance(ctx); /* consume builtin name */
|
||||
|
||||
if (strcmp(bname, "dbg") == 0) {
|
||||
@@ -746,6 +895,73 @@ spl_expr_result_t spl_parse_expr(spl_comp_t *ctx, int min_prec) {
|
||||
}
|
||||
}
|
||||
|
||||
/* Method call on type/instance: left.field(args) */
|
||||
if (left.type && peek(ctx)->type == TOK_L_PAREN) {
|
||||
/* Determine the type that owns methods (deref pointer if needed) */
|
||||
spl_type_info_t *methods_type = left.type;
|
||||
int is_ptr_self = 0;
|
||||
if (methods_type->kind == TYPE_PTR && methods_type->elem &&
|
||||
(methods_type->elem->kind == TYPE_STRUCT ||
|
||||
methods_type->elem->kind == TYPE_ENUM)) {
|
||||
is_ptr_self = 1;
|
||||
methods_type = methods_type->elem;
|
||||
}
|
||||
|
||||
int found_method = 0;
|
||||
vec_for(methods_type->methods, mi) {
|
||||
if (strcmp(vec_at(methods_type->methods, mi).name, fname) == 0) {
|
||||
spl_method_info_t *method = &vec_at(methods_type->methods, mi);
|
||||
spl_func_info_t *func = &vec_at(ctx->funcs, method->func_idx);
|
||||
|
||||
advance(ctx); /* ( */
|
||||
found_method = 1;
|
||||
|
||||
int nargs = 0;
|
||||
|
||||
/* Check if instance method: first param is self: *Type */
|
||||
int is_instance = 0;
|
||||
if (func->nparams > 0 && func->param_types[0] &&
|
||||
func->param_types[0]->kind == TYPE_PTR &&
|
||||
func->param_types[0]->elem == methods_type) {
|
||||
is_instance = 1;
|
||||
}
|
||||
|
||||
if (is_instance) {
|
||||
if (!left.is_lvalue && !is_ptr_self) {
|
||||
spl_comp_error(ctx, "cannot call instance method '%s' on type",
|
||||
fname);
|
||||
}
|
||||
nargs = 1; /* self already on stack */
|
||||
}
|
||||
|
||||
/* Parse remaining arguments */
|
||||
if (peek(ctx)->type != TOK_R_PAREN) {
|
||||
for (;;) {
|
||||
spl_expr_result_t arg = spl_parse_expr(ctx, PREC_MIN);
|
||||
(void)arg;
|
||||
nargs++;
|
||||
if (peek(ctx)->type == TOK_COMMA) {
|
||||
advance(ctx);
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
expect(ctx, TOK_R_PAREN);
|
||||
|
||||
spl_val_t addr = vec_at(ctx->prog.funcs, func->func_idx).address;
|
||||
spl_emit(ctx, SPL_PUSH, SPL_PTR, addr);
|
||||
spl_emit(ctx, SPL_CALL, SPL_VOID, nargs);
|
||||
|
||||
left = (spl_expr_result_t){func->ret_type, 0};
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (found_method)
|
||||
continue;
|
||||
/* If method not found, fall through to field access below */
|
||||
}
|
||||
|
||||
/* Struct field access */
|
||||
if (left.type && left.type->kind == TYPE_STRUCT) {
|
||||
/* The struct address is on stack (as lvalue or from previous computation) */
|
||||
@@ -998,12 +1214,12 @@ static spl_expr_result_t parse_infix(spl_comp_t *ctx, spl_expr_result_t left, sp
|
||||
spl_emit(ctx, SPL_STORE, bt, 0);
|
||||
} else {
|
||||
/* Compound: left = left op right — stack: [addr, rhs] */
|
||||
spl_emit(ctx, SPL_PICK, SPL_VOID, 1); /* [addr, rhs, addr] */
|
||||
spl_emit(ctx, SPL_LOAD, bt, 0); /* [addr, rhs, old_val] */
|
||||
spl_emit(ctx, SPL_SWAP, SPL_VOID, 0); /* [addr, old_val, rhs] */
|
||||
spl_emit(ctx, SPL_PICK, SPL_VOID, 1); /* [addr, rhs, addr] */
|
||||
spl_emit(ctx, SPL_LOAD, bt, 0); /* [addr, rhs, old_val] */
|
||||
spl_emit(ctx, SPL_SWAP, SPL_VOID, 0); /* [addr, old_val, rhs] */
|
||||
int sop = binop_to_sir(assign_to_binop(op), bt);
|
||||
if (sop >= 0)
|
||||
spl_emit(ctx, sop, bt, 0); /* [addr, result] */
|
||||
spl_emit(ctx, sop, bt, 0); /* [addr, result] */
|
||||
spl_emit(ctx, SPL_STORE, bt, 0);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user