1
0
Fork 0
forked from fun/fun

Some more class sugar...

This commit is contained in:
Johannes Findeisen 2025-09-26 07:53:12 +02:00
commit c7c4b467d5
2 changed files with 74 additions and 21 deletions

View file

@ -55,24 +55,19 @@ p["x"] = 10
p["y"] = -2
print("after set -> p.x = " + to_string(p["x"]) + ", p.y = " + to_string(p["y"]))
// Call methods by first loading them into identifiers, then passing 'p' as first argument
to_str = p["toString"]
print("toString(p) => " + to_str(p))
// Call methods directly with dot-call sugar: p.method(args) desugars to method(p, args)
print("toString(p) => " + p.toString())
move_fn = p["move"]
move_fn(p, 3, 5)
print("after move(3,5) -> " + to_str(p)) // expect Point(13, 3)
p.move(3, 5)
print("after move(3,5) -> " + p.toString()) // expect Point(13, 3)
// Another instance with defaults
q = Point()
print("q initially -> " + to_str(q)) // Point(0, 0)
print("q initially -> " + q.toString()) // Point(0, 0)
// Counter demo
c = Counter()
inc = c["inc"]
add = c["add"]
print("counter inc -> " + to_string(inc(c))) // 1
print("counter inc -> " + to_string(inc(c))) // 2
print("counter add(5) -> " + to_string(add(c, 5))) // 7
print("counter inc -> " + to_string(c.inc())) // 1
print("counter inc -> " + to_string(c.inc())) // 2
print("counter add(5) -> " + to_string(c.add(5))) // 7
print("counter current value = " + to_string(c["value"]))

View file

@ -995,7 +995,7 @@ static int emit_primary(Bytecode *bc, const char *src, size_t len, size_t *pos)
}
}
/* dot property access: obj.field -> obj["field"] */
/* dot property access and method-call sugar: obj.field or obj.method(...) */
if (*pos < len && src[*pos] == '.') {
(*pos)++; /* '.' */
skip_spaces(src, len, pos);
@ -1003,9 +1003,39 @@ static int emit_primary(Bytecode *bc, const char *src, size_t len, size_t *pos)
if (!read_identifier_into(src, len, pos, &mname)) { parser_fail(*pos, "Expected identifier after '.'"); free(name); return 0; }
int kci = bytecode_add_constant(bc, make_string(mname));
free(mname);
bytecode_add_instruction(bc, OP_LOAD_CONST, kci);
bytecode_add_instruction(bc, OP_INDEX_GET, 0);
continue;
/* Peek for immediate call: obj.method( ... ) */
size_t callp = *pos;
skip_spaces(src, len, &callp);
if (callp < len && src[callp] == '(') {
/* Prepare: stack has <obj>. Duplicate it to preserve 'this' across INDEX_GET */
bytecode_add_instruction(bc, OP_DUP, 0);
bytecode_add_instruction(bc, OP_LOAD_CONST, kci);
bytecode_add_instruction(bc, OP_INDEX_GET, 0); /* -> stack: obj, func */
bytecode_add_instruction(bc, OP_SWAP, 0); /* -> stack: func, obj (this) */
/* Consume '(' and parse args */
*pos = callp + 1;
int argc = 0;
skip_spaces(src, len, pos);
if (*pos < len && src[*pos] != ')') {
do {
if (!emit_expression(bc, src, len, pos)) { parser_fail(*pos, "Expected expression as method argument"); free(name); return 0; }
argc++;
skip_spaces(src, len, pos);
} while (*pos < len && src[*pos] == ',' && (++(*pos), skip_spaces(src, len, pos), 1));
}
if (!consume_char(src, len, pos, ')')) { parser_fail(*pos, "Expected ')' after arguments"); free(name); return 0; }
/* Call with implicit 'this' (+1 arg) */
bytecode_add_instruction(bc, OP_CALL, argc + 1);
continue;
} else {
/* Plain property get: obj["field"] */
bytecode_add_instruction(bc, OP_LOAD_CONST, kci);
bytecode_add_instruction(bc, OP_INDEX_GET, 0);
continue;
}
}
break;
@ -1047,7 +1077,7 @@ static int emit_primary(Bytecode *bc, const char *src, size_t len, size_t *pos)
}
}
/* dot property access: obj.field -> obj["field"] */
/* dot property access and method-call sugar */
if (*pos < len && src[*pos] == '.') {
(*pos)++;
skip_spaces(src, len, pos);
@ -1055,9 +1085,37 @@ static int emit_primary(Bytecode *bc, const char *src, size_t len, size_t *pos)
if (!read_identifier_into(src, len, pos, &mname)) { parser_fail(*pos, "Expected identifier after '.'"); free(name); return 0; }
int kci = bytecode_add_constant(bc, make_string(mname));
free(mname);
bytecode_add_instruction(bc, OP_LOAD_CONST, kci);
bytecode_add_instruction(bc, OP_INDEX_GET, 0);
continue;
/* Peek for call */
size_t callp = *pos;
skip_spaces(src, len, &callp);
if (callp < len && src[callp] == '(') {
/* Stack has obj: duplicate to preserve 'this' */
bytecode_add_instruction(bc, OP_DUP, 0);
bytecode_add_instruction(bc, OP_LOAD_CONST, kci);
bytecode_add_instruction(bc, OP_INDEX_GET, 0); /* -> obj, func */
bytecode_add_instruction(bc, OP_SWAP, 0); /* -> func, obj */
*pos = callp + 1;
int argc = 0;
skip_spaces(src, len, pos);
if (*pos < len && src[*pos] != ')') {
do {
if (!emit_expression(bc, src, len, pos)) { parser_fail(*pos, "Expected expression as method argument"); free(name); return 0; }
argc++;
skip_spaces(src, len, pos);
} while (*pos < len && src[*pos] == ',' && (++(*pos), skip_spaces(src, len, pos), 1));
}
if (!consume_char(src, len, pos, ')')) { parser_fail(*pos, "Expected ')' after arguments"); free(name); return 0; }
bytecode_add_instruction(bc, OP_CALL, argc + 1);
continue;
} else {
/* plain property get */
bytecode_add_instruction(bc, OP_LOAD_CONST, kci);
bytecode_add_instruction(bc, OP_INDEX_GET, 0);
continue;
}
}
break;