Basic objects.
This commit is contained in:
parent
031d23bcc3
commit
c8b8dd820e
6 changed files with 131 additions and 2 deletions
30
examples/objects_basic.fun
Executable file
30
examples/objects_basic.fun
Executable file
|
|
@ -0,0 +1,30 @@
|
|||
#!/usr/bin/env fun
|
||||
|
||||
// Objects via maps: bracket access and method calls with explicit self
|
||||
|
||||
// Construct an object as a map (attach methods later)
|
||||
obj = { "x": 1 }
|
||||
|
||||
// Property read
|
||||
print(obj["x"]) // -> 1
|
||||
|
||||
// Property write
|
||||
obj["x"] = 5
|
||||
print(obj["x"]) // -> 5
|
||||
|
||||
// Define a method function (expects self as first parameter)
|
||||
fun inc(self, d)
|
||||
// Update a field using bracket notation
|
||||
self["x"] = self["x"] + d
|
||||
return self["x"]
|
||||
|
||||
// Attach method and call it (explicit self)
|
||||
obj["inc"] = inc
|
||||
inc(obj, 3)
|
||||
print(obj["x"]) // -> 8
|
||||
|
||||
/* Expected output:
|
||||
1
|
||||
5
|
||||
8
|
||||
*/
|
||||
89
src/parser.c
89
src/parser.c
|
|
@ -852,9 +852,11 @@ static int emit_primary(Bytecode *bc, const char *src, size_t len, size_t *pos)
|
|||
printf("compile: CALL %s with %d arg(s)\n", name, argc);
|
||||
#endif
|
||||
bytecode_add_instruction(bc, OP_CALL, argc);
|
||||
/* postfix indexing or slice */
|
||||
/* postfix indexing, slice, and dot access/method calls */
|
||||
for (;;) {
|
||||
skip_spaces(src, len, pos);
|
||||
|
||||
/* index/slice */
|
||||
if (*pos < len && src[*pos] == '[') {
|
||||
(*pos)++;
|
||||
if (!emit_expression(bc, src, len, pos)) { parser_fail(*pos, "Expected start expression"); free(name); return 0; }
|
||||
|
|
@ -877,6 +879,20 @@ static int emit_primary(Bytecode *bc, const char *src, size_t len, size_t *pos)
|
|||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
/* dot property access: obj.field -> obj["field"] */
|
||||
if (*pos < len && src[*pos] == '.') {
|
||||
(*pos)++; /* '.' */
|
||||
skip_spaces(src, len, pos);
|
||||
char *mname = NULL;
|
||||
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;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
free(name);
|
||||
|
|
@ -888,9 +904,11 @@ static int emit_primary(Bytecode *bc, const char *src, size_t len, size_t *pos)
|
|||
int gi = sym_index(name);
|
||||
bytecode_add_instruction(bc, OP_LOAD_GLOBAL, gi);
|
||||
}
|
||||
/* postfix indexing or slice */
|
||||
/* postfix indexing, slice, and dot access/method calls */
|
||||
for (;;) {
|
||||
skip_spaces(src, len, pos);
|
||||
|
||||
/* index/slice */
|
||||
if (*pos < len && src[*pos] == '[') {
|
||||
(*pos)++;
|
||||
if (!emit_expression(bc, src, len, pos)) { parser_fail(*pos, "Expected start expression"); free(name); return 0; }
|
||||
|
|
@ -913,6 +931,20 @@ static int emit_primary(Bytecode *bc, const char *src, size_t len, size_t *pos)
|
|||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
/* dot property access: obj.field -> obj["field"] */
|
||||
if (*pos < len && src[*pos] == '.') {
|
||||
(*pos)++;
|
||||
skip_spaces(src, len, pos);
|
||||
char *mname = NULL;
|
||||
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;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
free(name);
|
||||
|
|
@ -1404,6 +1436,59 @@ static void parse_simple_statement(Bytecode *bc, const char *src, size_t len, si
|
|||
int gi = (lidx < 0) ? sym_index(name) : -1;
|
||||
skip_spaces(src, len, &local_pos);
|
||||
|
||||
/* object field assignment: name.field = expr (only if '=' follows) */
|
||||
if (local_pos < len && src[local_pos] == '.') {
|
||||
size_t stmt_start = *pos; /* for expression fallback */
|
||||
size_t look = local_pos + 1; /* point after '.' */
|
||||
skip_spaces(src, len, &look);
|
||||
char *fname = NULL;
|
||||
if (!read_identifier_into(src, len, &look, &fname)) {
|
||||
parser_fail(look, "Expected field name after '.'");
|
||||
free(name);
|
||||
return;
|
||||
}
|
||||
skip_spaces(src, len, &look);
|
||||
if (look >= len || src[look] != '=') {
|
||||
/* Not an assignment: treat as expression statement (e.g., obj.method(...)) */
|
||||
free(fname);
|
||||
free(name);
|
||||
size_t expr_pos = stmt_start;
|
||||
if (emit_expression(bc, src, len, &expr_pos)) {
|
||||
bytecode_add_instruction(bc, OP_POP, 0);
|
||||
}
|
||||
*pos = expr_pos;
|
||||
skip_to_eol(src, len, pos);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Confirmed assignment: emit container, key, value, then INDEX_SET */
|
||||
/* Load container variable */
|
||||
if (lidx >= 0) {
|
||||
bytecode_add_instruction(bc, OP_LOAD_LOCAL, lidx);
|
||||
} else {
|
||||
bytecode_add_instruction(bc, OP_LOAD_GLOBAL, gi);
|
||||
}
|
||||
/* Push key */
|
||||
int kci = bytecode_add_constant(bc, make_string(fname));
|
||||
free(fname);
|
||||
bytecode_add_instruction(bc, OP_LOAD_CONST, kci);
|
||||
|
||||
/* Advance local_pos to after '=' and parse value expression */
|
||||
local_pos = look + 1; /* skip '=' */
|
||||
if (!emit_expression(bc, src, len, &local_pos)) {
|
||||
parser_fail(local_pos, "Expected expression after '='");
|
||||
free(name);
|
||||
return;
|
||||
}
|
||||
/* perform set: pops value, key, container (in that order) */
|
||||
bytecode_add_instruction(bc, OP_INDEX_SET, 0);
|
||||
|
||||
free(name);
|
||||
*pos = local_pos;
|
||||
skip_to_eol(src, len, pos);
|
||||
return;
|
||||
}
|
||||
|
||||
/* array element assignment: name[expr] = expr */
|
||||
if (local_pos < len && src[local_pos] == '[') {
|
||||
/* load array variable */
|
||||
|
|
|
|||
|
|
@ -1,6 +1,10 @@
|
|||
case OP_INDEX_GET: {
|
||||
Value idx = pop_value(vm);
|
||||
Value container = pop_value(vm);
|
||||
#ifdef FUN_DEBUG
|
||||
fprintf(stderr, "DEBUG INDEX_GET: container.type=%d idx.type=%d\n",
|
||||
container.type, idx.type);
|
||||
#endif
|
||||
if (container.type == VAL_ARRAY) {
|
||||
if (idx.type != VAL_INT) { fprintf(stderr, "INDEX_GET index must be int for array\n"); exit(1); }
|
||||
Value elem;
|
||||
|
|
|
|||
|
|
@ -2,6 +2,10 @@ case OP_INDEX_SET: {
|
|||
Value v = pop_value(vm);
|
||||
Value idx = pop_value(vm);
|
||||
Value container = pop_value(vm);
|
||||
#ifdef FUN_DEBUG
|
||||
fprintf(stderr, "DEBUG INDEX_SET: container.type=%d idx.type=%d value.type=%d\n",
|
||||
container.type, idx.type, v.type);
|
||||
#endif
|
||||
if (container.type == VAL_ARRAY) {
|
||||
if (idx.type != VAL_INT) { fprintf(stderr, "INDEX_SET index must be int for array\n"); exit(1); }
|
||||
if (!array_set(&container, (int)idx.i, v)) {
|
||||
|
|
|
|||
|
|
@ -4,6 +4,9 @@ case OP_LOAD_GLOBAL: {
|
|||
fprintf(stderr, "Runtime error: global index out of range\n");
|
||||
exit(1);
|
||||
}
|
||||
#ifdef FUN_DEBUG
|
||||
fprintf(stderr, "DEBUG LOAD_GLOBAL[%d]: type=%d\n", idx, vm->globals[idx].type);
|
||||
#endif
|
||||
push_value(vm, copy_value(&vm->globals[idx]));
|
||||
break;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,6 +5,9 @@ case OP_STORE_GLOBAL: {
|
|||
exit(1);
|
||||
}
|
||||
Value v = pop_value(vm);
|
||||
#ifdef FUN_DEBUG
|
||||
fprintf(stderr, "DEBUG STORE_GLOBAL[%d]: new.type=%d\n", idx, v.type);
|
||||
#endif
|
||||
free_value(vm->globals[idx]);
|
||||
vm->globals[idx] = v;
|
||||
break;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue