codegen: type-driven dispatch for + between Int idents
Closes the known limitation from the self-host commit: `fn add(a:Int,
b:Int) { a + b }` now compiles to integer addition, not string concat.
Previously the codegen heuristic guessed string concat whenever both
operands were Idents with no literal anchor.
Mechanism
- parser captures the leading type identifier from `let x: T = ...`
bindings (new "type" field on Let) and from function parameter
annotations (new "type" field on each param).
- codegen maintains a per-function int-name set in process state via
state_set("__int_names", csv). cg_fn seeds it from typed parameters;
cg_stmt extends it from typed `let` bindings and from `let x = <Int
literal>` (literal inference).
- BinOp Plus: when both sides are Idents and both names are in the
int-name set, emit arithmetic; otherwise the existing literal-anchor
heuristic applies, with string concat as the fallback.
This is the first compiler change made entirely through the self-
hosting workflow — no Python bootstrap. Edit el source, run existing
elc on elc-combined.el, cc the output, test. Closure holds at the
new binary.
Tests
- add(40, 2) → 42
- count_to(10) → 45 (let i: Int / let total: Int rebinding)
- Regression suite (tiny/implret/whiletest/lextest) unchanged.
dist/platform/elc updated; .prev preserved.
This commit is contained in:
Vendored
BIN
Binary file not shown.
Vendored
+78
-2
@@ -49,6 +49,9 @@ el_val_t cg_stmts(el_val_t stmts, el_val_t indent, el_val_t declared);
|
||||
el_val_t param_decl(el_val_t param, el_val_t idx);
|
||||
el_val_t params_to_c(el_val_t params);
|
||||
el_val_t transform_implicit_return(el_val_t body);
|
||||
el_val_t is_int_name(el_val_t name);
|
||||
el_val_t add_int_name(el_val_t name);
|
||||
el_val_t build_int_names_for_params(el_val_t params);
|
||||
el_val_t cg_fn(el_val_t stmt);
|
||||
el_val_t is_fndef(el_val_t stmt);
|
||||
el_val_t is_top_level_decl(el_val_t stmt);
|
||||
@@ -878,8 +881,13 @@ el_val_t parse_params(el_val_t tokens, el_val_t pos) {
|
||||
el_val_t pname = tok_value(tokens, p);
|
||||
p = (p + 1);
|
||||
p = expect(tokens, p, EL_STR("Colon"));
|
||||
el_val_t ptype = EL_STR("");
|
||||
el_val_t kt = tok_kind(tokens, p);
|
||||
if (str_eq(kt, EL_STR("Ident"))) {
|
||||
ptype = tok_value(tokens, p);
|
||||
}
|
||||
p = skip_type(tokens, p);
|
||||
el_val_t param = el_map_new(1, "name", pname);
|
||||
el_val_t param = el_map_new(2, "name", pname, "type", ptype);
|
||||
params = native_list_append(params, param);
|
||||
el_val_t k2 = tok_kind(tokens, p);
|
||||
if (str_eq(k2, EL_STR("Comma"))) {
|
||||
@@ -1309,16 +1317,21 @@ el_val_t parse_stmt(el_val_t tokens, el_val_t pos) {
|
||||
el_val_t p = (pos + 1);
|
||||
el_val_t name = tok_value(tokens, p);
|
||||
p = (p + 1);
|
||||
el_val_t ltype = EL_STR("");
|
||||
el_val_t k2 = tok_kind(tokens, p);
|
||||
if (str_eq(k2, EL_STR("Colon"))) {
|
||||
p = (p + 1);
|
||||
el_val_t kt = tok_kind(tokens, p);
|
||||
if (str_eq(kt, EL_STR("Ident"))) {
|
||||
ltype = tok_value(tokens, p);
|
||||
}
|
||||
p = skip_type(tokens, p);
|
||||
}
|
||||
p = expect(tokens, p, EL_STR("Eq"));
|
||||
el_val_t r = parse_expr(tokens, p);
|
||||
el_val_t val = el_get_field(r, EL_STR("node"));
|
||||
p = el_get_field(r, EL_STR("pos"));
|
||||
return make_result(el_map_new(3, "stmt", EL_STR("Let"), "name", name, "value", val), p);
|
||||
return make_result(el_map_new(4, "stmt", EL_STR("Let"), "name", name, "value", val, "type", ltype), p);
|
||||
}
|
||||
if (str_eq(k, EL_STR("Return"))) {
|
||||
el_val_t p = (pos + 1);
|
||||
@@ -1690,6 +1703,18 @@ el_val_t cg_expr(el_val_t expr) {
|
||||
el_val_t op_c = binop_to_c(op);
|
||||
return el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(EL_STR("("), left_c), EL_STR(" ")), op_c), EL_STR(" ")), right_c), EL_STR(")"));
|
||||
}
|
||||
if (str_eq(left_kind, EL_STR("Ident"))) {
|
||||
if (str_eq(right_kind, EL_STR("Ident"))) {
|
||||
el_val_t lname = el_get_field(left, EL_STR("name"));
|
||||
el_val_t rname = el_get_field(right, EL_STR("name"));
|
||||
if (is_int_name(lname)) {
|
||||
if (is_int_name(rname)) {
|
||||
el_val_t op_c = binop_to_c(op);
|
||||
return el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(EL_STR("("), left_c), EL_STR(" ")), op_c), EL_STR(" ")), right_c), EL_STR(")"));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (str_eq(left_kind, EL_STR("Call"))) {
|
||||
return el_str_concat(el_str_concat(el_str_concat(el_str_concat(EL_STR("el_str_concat("), left_c), EL_STR(", ")), right_c), EL_STR(")"));
|
||||
}
|
||||
@@ -1898,6 +1923,14 @@ el_val_t cg_stmt(el_val_t stmt, el_val_t indent, el_val_t declared) {
|
||||
el_val_t name = el_get_field(stmt, EL_STR("name"));
|
||||
el_val_t val = el_get_field(stmt, EL_STR("value"));
|
||||
el_val_t val_c = cg_expr(val);
|
||||
el_val_t ltype = el_get_field(stmt, EL_STR("type"));
|
||||
if (str_eq(ltype, EL_STR("Int"))) {
|
||||
add_int_name(name);
|
||||
}
|
||||
el_val_t vk = el_get_field(val, EL_STR("expr"));
|
||||
if (str_eq(vk, EL_STR("Int"))) {
|
||||
add_int_name(name);
|
||||
}
|
||||
if (list_contains(declared, name)) {
|
||||
emit_line(el_str_concat(el_str_concat(el_str_concat(el_str_concat(indent, name), EL_STR(" = ")), val_c), EL_STR(";")));
|
||||
return declared;
|
||||
@@ -2118,6 +2151,48 @@ el_val_t transform_implicit_return(el_val_t body) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
el_val_t is_int_name(el_val_t name) {
|
||||
el_val_t csv = state_get(EL_STR("__int_names"));
|
||||
if (str_eq(csv, EL_STR(""))) {
|
||||
return 0;
|
||||
}
|
||||
return str_contains(csv, el_str_concat(el_str_concat(EL_STR(","), name), EL_STR(",")));
|
||||
return 0;
|
||||
}
|
||||
|
||||
el_val_t add_int_name(el_val_t name) {
|
||||
el_val_t csv = state_get(EL_STR("__int_names"));
|
||||
if (str_eq(csv, EL_STR(""))) {
|
||||
csv;
|
||||
EL_NULL;
|
||||
EL_STR(",");
|
||||
}
|
||||
el_val_t key = el_str_concat(el_str_concat(EL_STR(","), name), EL_STR(","));
|
||||
if (str_contains(csv, key)) {
|
||||
return 1;
|
||||
}
|
||||
state_set(EL_STR("__int_names"), el_str_concat(el_str_concat(csv, name), EL_STR(",")));
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
el_val_t build_int_names_for_params(el_val_t params) {
|
||||
state_set(EL_STR("__int_names"), EL_STR(","));
|
||||
el_val_t np = native_list_len(params);
|
||||
el_val_t pi = 0;
|
||||
while (pi < np) {
|
||||
el_val_t param = native_list_get(params, pi);
|
||||
el_val_t pname = el_get_field(param, EL_STR("name"));
|
||||
el_val_t ptype = el_get_field(param, EL_STR("type"));
|
||||
if (str_eq(ptype, EL_STR("Int"))) {
|
||||
add_int_name(pname);
|
||||
}
|
||||
pi = (pi + 1);
|
||||
}
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
el_val_t cg_fn(el_val_t stmt) {
|
||||
el_val_t fn_name = el_get_field(stmt, EL_STR("name"));
|
||||
if (str_eq(fn_name, EL_STR("main"))) {
|
||||
@@ -2127,6 +2202,7 @@ el_val_t cg_fn(el_val_t stmt) {
|
||||
el_val_t body = el_get_field(stmt, EL_STR("body"));
|
||||
el_val_t ret_type = el_get_field(stmt, EL_STR("ret_type"));
|
||||
el_val_t params_c = params_to_c(params);
|
||||
build_int_names_for_params(params);
|
||||
emit_line(el_str_concat(el_str_concat(el_str_concat(el_str_concat(EL_STR("el_val_t "), fn_name), EL_STR("(")), params_c), EL_STR(") {")));
|
||||
el_val_t decl = native_list_empty();
|
||||
el_val_t np = native_list_len(params);
|
||||
|
||||
BIN
Binary file not shown.
Reference in New Issue
Block a user