1
0
Fork 0
forked from fun/fun

Some class fixes

This commit is contained in:
Johannes Findeisen 2025-09-26 08:06:30 +02:00
commit 30de0d396e
3 changed files with 166 additions and 11 deletions

View file

@ -3,7 +3,7 @@
BUILD_DIR ?= build
CMAKE ?= cmake
# Default to a build with extra logging disabled
CMAKE_FLAGS ?= -DFUN_DEBUG=OFF
CMAKE_FLAGS ?= -DFUN_DEBUG=ON
.PHONY: all configure build fun fun_test test_opcodes clean distclean install repl run

View file

@ -23,7 +23,7 @@
* move_fn(p, dx, dy)
*/
class Point
class Point(number x, number y)
x = 0
y = 0
fun move(this, dx, dy)
@ -42,8 +42,8 @@ class Counter
this.value = this.value + n
return this.value
// Instantiate a Point
p = Point()
// Instantiate a Point with constructor parameters
p = Point(10, -2)
// Types
print("typeof(Point) = " + typeof(Point)) // Function (factory)
@ -51,9 +51,6 @@ print("typeof(p) = " + typeof(p)) // Map (instance)
// Read/write fields
print("p.x = " + to_string(p["x"]) + ", p.y = " + to_string(p["y"]))
p["x"] = 10
p["y"] = -2
print("after set -> p.x = " + to_string(p["x"]) + ", p.y = " + to_string(p["y"]))
// Call methods directly with dot-call sugar: p.method(args) desugars to method(p, args)
print("toString(p) => " + p.toString())
@ -61,8 +58,8 @@ print("toString(p) => " + p.toString())
p.move(3, 5)
print("after move(3,5) -> " + p.toString()) // expect Point(13, 3)
// Another instance with defaults
q = Point()
// Another instance (explicit constructor args)
q = Point(0, 0)
print("q initially -> " + q.toString()) // Point(0, 0)
// Counter demo

View file

@ -1927,10 +1927,75 @@ static void parse_block(Bytecode *bc, const char *src, size_t len, size_t *pos,
}
int cgi = sym_index(cname);
/* Optional typed parameter list: class Name(type ident, ...) */
char *param_names[64]; int param_kind[64]; int pcount = 0;
memset(param_names, 0, sizeof(param_names));
memset(param_kind, 0, sizeof(param_kind));
/* kind: 1=Number (numeric types incl. boolean), 2=String, 3=Nil */
auto int map_type_kind(const char *t) {
if (!t) return 0;
if (strcmp(t, "string") == 0) return 2;
if (strcmp(t, "nil") == 0) return 3;
if (strcmp(t, "boolean") == 0) return 1;
if (strcmp(t, "number") == 0) return 1;
if (strncmp(t, "uint", 4) == 0 || strncmp(t, "sint", 4) == 0 || strncmp(t, "int", 3) == 0) return 1;
return 0; /* unknown -> treat as number-like? keep as 0 => no check */
}
skip_spaces(src, len, pos);
if (*pos < len && src[*pos] == '(') {
(*pos)++;
skip_spaces(src, len, pos);
if (*pos < len && src[*pos] != ')') {
for (;;) {
/* read type token */
char *tname = NULL;
if (!read_identifier_into(src, len, pos, &tname)) {
parser_fail(*pos, "Expected type in class parameter list");
free(cname);
return;
}
/* read param name */
skip_spaces(src, len, pos);
char *pname = NULL;
if (!read_identifier_into(src, len, pos, &pname)) {
parser_fail(*pos, "Expected parameter name after type");
free(tname);
free(cname);
return;
}
if (pcount >= (int)(sizeof(param_names)/sizeof(param_names[0]))) {
parser_fail(*pos, "Too many class parameters");
free(tname); free(pname); free(cname);
return;
}
param_names[pcount] = pname;
param_kind[pcount] = map_type_kind(tname);
free(tname);
pcount++;
skip_spaces(src, len, pos);
if (*pos < len && src[*pos] == ',') {
(*pos)++;
skip_spaces(src, len, pos);
continue;
}
break;
}
}
if (!consume_char(src, len, pos, ')')) {
parser_fail(*pos, "Expected ')' after class parameter list");
for (int i = 0; i < pcount; ++i) free(param_names[i]);
free(cname);
return;
}
}
/* end of class header line */
skip_to_eol(src, len, pos);
/* Build factory function: Name() -> instance map with fields and methods */
/* Build factory function: Name(...) -> instance map with fields and methods */
Bytecode *ctor_bc = bytecode_new();
/* local env for the factory to allow temp locals */
LocalEnv ctor_env;
@ -1938,7 +2003,90 @@ static void parse_block(Bytecode *bc, const char *src, size_t len, size_t *pos,
LocalEnv *prev_env = g_locals;
g_locals = &ctor_env;
/* instance map: __this = {} */
/* Register parameter locals first so args land at 0..pcount-1 */
for (int i = 0; i < pcount; ++i) {
local_add(param_names[i]);
}
/* Guard local to detect extra argument at index == pcount */
int l_extra = local_add("__extra");
/* Runtime checks: missing args and type checks */
for (int i = 0; i < pcount; ++i) {
/* missing arg: local i must not be Nil */
bytecode_add_instruction(ctor_bc, OP_LOAD_LOCAL, i);
bytecode_add_instruction(ctor_bc, OP_TYPEOF, 0);
int ci_nil = bytecode_add_constant(ctor_bc, make_string("Nil"));
bytecode_add_instruction(ctor_bc, OP_LOAD_CONST, ci_nil);
bytecode_add_instruction(ctor_bc, OP_EQ, 0);
int j_ok_present = bytecode_add_instruction(ctor_bc, OP_JUMP_IF_FALSE, 0);
/* then -> error */
{
char msg[128];
snprintf(msg, sizeof(msg), "TypeError: missing argument '%s' in %s()", param_names[i], cname);
int ci_msg = bytecode_add_constant(ctor_bc, make_string(msg));
bytecode_add_instruction(ctor_bc, OP_LOAD_CONST, ci_msg);
bytecode_add_instruction(ctor_bc, OP_PRINT, 0);
bytecode_add_instruction(ctor_bc, OP_HALT, 0);
}
/* patch continue if present */
bytecode_set_operand(ctor_bc, j_ok_present, ctor_bc->instr_count);
/* type check for known kinds */
int kind = param_kind[i];
if (kind == 1 || kind == 2 || kind == 3) {
/* typeof(local i) == Expected ? skip error : go to error */
bytecode_add_instruction(ctor_bc, OP_LOAD_LOCAL, i);
bytecode_add_instruction(ctor_bc, OP_TYPEOF, 0);
const char *exp = (kind == 1) ? "Number" : (kind == 2) ? "String" : "Nil";
int ci_exp = bytecode_add_constant(ctor_bc, make_string(exp));
bytecode_add_instruction(ctor_bc, OP_LOAD_CONST, ci_exp);
bytecode_add_instruction(ctor_bc, OP_EQ, 0);
/* if false -> jump to error */
int j_to_error = bytecode_add_instruction(ctor_bc, OP_JUMP_IF_FALSE, 0);
/* on success, skip error block */
int j_skip_err = bytecode_add_instruction(ctor_bc, OP_JUMP, 0);
/* error block */
{
int err_label = ctor_bc->instr_count;
bytecode_set_operand(ctor_bc, j_to_error, err_label);
char msg2[160];
snprintf(msg2, sizeof(msg2), "TypeError: %s() expects %s for '%s'", cname, exp, param_names[i]);
int ci_msg2 = bytecode_add_constant(ctor_bc, make_string(msg2));
bytecode_add_instruction(ctor_bc, OP_LOAD_CONST, ci_msg2);
bytecode_add_instruction(ctor_bc, OP_PRINT, 0);
bytecode_add_instruction(ctor_bc, OP_HALT, 0);
}
/* continue label after error block */
bytecode_set_operand(ctor_bc, j_skip_err, ctor_bc->instr_count);
}
}
/* Extra args check: guard local must be Nil; if not Nil -> error */
{
bytecode_add_instruction(ctor_bc, OP_LOAD_LOCAL, l_extra);
bytecode_add_instruction(ctor_bc, OP_TYPEOF, 0);
int ci_nil2 = bytecode_add_constant(ctor_bc, make_string("Nil"));
bytecode_add_instruction(ctor_bc, OP_LOAD_CONST, ci_nil2);
bytecode_add_instruction(ctor_bc, OP_EQ, 0);
/* if false (not Nil) -> jump to error */
int j_to_error = bytecode_add_instruction(ctor_bc, OP_JUMP_IF_FALSE, 0);
/* if true (Nil) -> skip error */
int j_skip_err = bytecode_add_instruction(ctor_bc, OP_JUMP, 0);
{
int err_label = ctor_bc->instr_count;
bytecode_set_operand(ctor_bc, j_to_error, err_label);
char msg3[128];
snprintf(msg3, sizeof(msg3), "TypeError: %s() received too many arguments", cname);
int ci_msg3 = bytecode_add_constant(ctor_bc, make_string(msg3));
bytecode_add_instruction(ctor_bc, OP_LOAD_CONST, ci_msg3);
bytecode_add_instruction(ctor_bc, OP_PRINT, 0);
bytecode_add_instruction(ctor_bc, OP_HALT, 0);
}
bytecode_set_operand(ctor_bc, j_skip_err, ctor_bc->instr_count);
}
/* instance map: __this = {} (placed after param guard) */
int l_this = local_add("__this");
bytecode_add_instruction(ctor_bc, OP_MAKE_MAP, 0);
bytecode_add_instruction(ctor_bc, OP_STORE_LOCAL, l_this);
@ -2116,6 +2264,15 @@ static void parse_block(Bytecode *bc, const char *src, size_t len, size_t *pos,
/* empty class body allowed */
}
/* Override defaults with constructor parameters: this["name"] = local i */
for (int i = 0; i < pcount; ++i) {
bytecode_add_instruction(ctor_bc, OP_LOAD_LOCAL, l_this);
int kci = bytecode_add_constant(ctor_bc, make_string(param_names[i]));
bytecode_add_instruction(ctor_bc, OP_LOAD_CONST, kci);
bytecode_add_instruction(ctor_bc, OP_LOAD_LOCAL, i);
bytecode_add_instruction(ctor_bc, OP_INDEX_SET, 0);
}
/* return instance */
bytecode_add_instruction(ctor_bc, OP_LOAD_LOCAL, l_this);
bytecode_add_instruction(ctor_bc, OP_RETURN, 0);
@ -2128,6 +2285,7 @@ static void parse_block(Bytecode *bc, const char *src, size_t len, size_t *pos,
bytecode_add_instruction(bc, OP_LOAD_CONST, cci);
bytecode_add_instruction(bc, OP_STORE_GLOBAL, cgi);
for (int i = 0; i < pcount; ++i) free(param_names[i]);
free(cname);
continue;
}