Skip to content

Commit a3c4352

Browse files
gh-53: Add SELF.
1 parent 5bbbf69 commit a3c4352

File tree

6 files changed

+132
-10
lines changed

6 files changed

+132
-10
lines changed

docs/SPECIFICATION.html

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -192,6 +192,8 @@
192192
- `<"foo" = 1, "bar" = 10>` is a 1-level map with string keys.
193193
- `<"outer" = <"inner" = 1>, "other" = 0>` nests a map value under key `"outer"`.
194194
195+
A special literal `SELF` is recognized inside map literals. When used as a value within a map literal, `SELF` denotes a pointer (alias) to the enclosing `MAP` being constructed; storing `SELF` creates an entry that references the map itself so mutations via that reference affect the same map instance.
196+
195197
Map indexing uses angle brackets and a comma-separated key list: `m<k1>` or `m<k1,k2>` to lookup nested values. Keys MUST be scalar values of type `INT`, `FLT`, or `STR` (tensor values are not permitted as keys). The number of keys supplied MAY vary between lookups and different keys need not share the same depth: maps are sparse and do not require rectangular shapes.
196198
197199
Maps preserve insertion order: keys are stored in the order they are inserted (left-to-right for keys written in a literal and in the order of creation for keys added later). Iteration and serialized representations of a map reflect this insertion order.

src/builtins.c

Lines changed: 55 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3502,8 +3502,44 @@ static Value builtin_lcm(Interpreter* interp, Value* args, int argc, Expr** arg_
35023502

35033503
// ============ Comparison operators ============
35043504

3505-
// Recursive deep equality helper for Values (returns 1 if equal, 0 otherwise)
3506-
static int value_deep_eq(Value a, Value b) {
3505+
typedef struct {
3506+
const void* a;
3507+
const void* b;
3508+
ValueType type;
3509+
} EqSeenPair;
3510+
3511+
typedef struct {
3512+
EqSeenPair* items;
3513+
size_t count;
3514+
size_t cap;
3515+
} EqSeenSet;
3516+
3517+
static int eq_seen_contains(EqSeenSet* seen, const void* a, const void* b, ValueType type) {
3518+
if (!seen) return 0;
3519+
for (size_t i = 0; i < seen->count; i++) {
3520+
EqSeenPair* p = &seen->items[i];
3521+
if (p->type != type) continue;
3522+
if ((p->a == a && p->b == b) || (p->a == b && p->b == a)) return 1;
3523+
}
3524+
return 0;
3525+
}
3526+
3527+
static void eq_seen_add(EqSeenSet* seen, const void* a, const void* b, ValueType type) {
3528+
if (!seen) return;
3529+
if (seen->count + 1 > seen->cap) {
3530+
size_t new_cap = seen->cap == 0 ? 16 : seen->cap * 2;
3531+
EqSeenPair* grown = realloc(seen->items, sizeof(EqSeenPair) * new_cap);
3532+
if (!grown) { fprintf(stderr, "Out of memory\n"); exit(1); }
3533+
seen->items = grown;
3534+
seen->cap = new_cap;
3535+
}
3536+
seen->items[seen->count].a = a;
3537+
seen->items[seen->count].b = b;
3538+
seen->items[seen->count].type = type;
3539+
seen->count++;
3540+
}
3541+
3542+
static int value_deep_eq_impl(Value a, Value b, EqSeenSet* seen) {
35073543
if (a.type != b.type) return 0;
35083544
switch (a.type) {
35093545
case VAL_INT:
@@ -3519,27 +3555,33 @@ static int value_deep_eq(Value a, Value b) {
35193555
Tensor* ta = a.as.tns;
35203556
Tensor* tb = b.as.tns;
35213557
if (ta == NULL || tb == NULL) return (ta == tb) ? 1 : 0;
3558+
if (ta == tb) return 1;
3559+
if (eq_seen_contains(seen, ta, tb, VAL_TNS)) return 1;
3560+
eq_seen_add(seen, ta, tb, VAL_TNS);
35223561
if (ta->elem_type != tb->elem_type) return 0;
35233562
if (ta->ndim != tb->ndim) return 0;
35243563
for (size_t i = 0; i < ta->ndim; i++) {
35253564
if (ta->shape[i] != tb->shape[i]) return 0;
35263565
}
35273566
if (ta->length != tb->length) return 0;
35283567
for (size_t i = 0; i < ta->length; i++) {
3529-
if (!value_deep_eq(ta->data[i], tb->data[i])) return 0;
3568+
if (!value_deep_eq_impl(ta->data[i], tb->data[i], seen)) return 0;
35303569
}
35313570
return 1;
35323571
}
35333572
case VAL_MAP: {
35343573
Map* ma = a.as.map;
35353574
Map* mb = b.as.map;
35363575
if (ma == NULL || mb == NULL) return (ma == mb) ? 1 : 0;
3576+
if (ma == mb) return 1;
3577+
if (eq_seen_contains(seen, ma, mb, VAL_MAP)) return 1;
3578+
eq_seen_add(seen, ma, mb, VAL_MAP);
35373579
if (ma->count != mb->count) return 0;
35383580
for (size_t i = 0; i < ma->count; i++) {
35393581
int found = 0;
35403582
for (size_t j = 0; j < mb->count; j++) {
3541-
if (value_deep_eq(ma->items[i].key, mb->items[j].key)) {
3542-
if (!value_deep_eq(ma->items[i].value, mb->items[j].value)) return 0;
3583+
if (value_deep_eq_impl(ma->items[i].key, mb->items[j].key, seen)) {
3584+
if (!value_deep_eq_impl(ma->items[i].value, mb->items[j].value, seen)) return 0;
35433585
found = 1;
35443586
break;
35453587
}
@@ -3555,6 +3597,14 @@ static int value_deep_eq(Value a, Value b) {
35553597
}
35563598
}
35573599

3600+
// Recursive deep equality helper for Values (returns 1 if equal, 0 otherwise)
3601+
static int value_deep_eq(Value a, Value b) {
3602+
EqSeenSet seen = {0};
3603+
int out = value_deep_eq_impl(a, b, &seen);
3604+
free(seen.items);
3605+
return out;
3606+
}
3607+
35583608
static Value builtin_eq(Interpreter* interp, Value* args, int argc, Expr** arg_nodes, Env* env, int line, int col) {
35593609
(void)arg_nodes; (void)env; (void)interp;
35603610

src/interpreter.c

Lines changed: 47 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1406,11 +1406,53 @@ Value eval_expr(Interpreter* interp, Expr* expr, Env* env) {
14061406
interp->error_col = expr->column;
14071407
return value_null();
14081408
}
1409-
Value v = eval_expr(interp, vexpr, env);
1410-
if (interp->error) { value_free(k); value_free(mv); return value_null(); }
1411-
value_map_set(&mv, k, v);
1412-
value_free(k);
1413-
value_free(v);
1409+
// Special-cases for SELF usage inside map literal:
1410+
// - `SELF` as a bare identifier -> alias pointing to enclosing map
1411+
// - `SELF<...>` -> lookup into the enclosing map using the provided key(s)
1412+
if (vexpr->type == EXPR_IDENT && vexpr->as.ident && strcmp(vexpr->as.ident, "SELF") == 0) {
1413+
value_map_set_self(&mv, k);
1414+
value_free(k);
1415+
} else if (vexpr->type == EXPR_INDEX && vexpr->as.index.target && vexpr->as.index.target->type == EXPR_IDENT
1416+
&& vexpr->as.index.target->as.ident && strcmp(vexpr->as.index.target->as.ident, "SELF") == 0) {
1417+
// Evaluate index key(s) and perform lookup on the partially-constructed map `mv`.
1418+
ExprList* idxs = &vexpr->as.index.indices;
1419+
// Only single-key lookup is supported here (map keys are scalars). If multiple indices provided,
1420+
// resolve nested map access by descending.
1421+
Value cur = value_copy(mv);
1422+
bool err = false;
1423+
Value final_val = value_null();
1424+
for (size_t ii = 0; ii < idxs->count; ii++) {
1425+
Expr* ik = idxs->items[ii];
1426+
Value ikv = eval_expr(interp, ik, env);
1427+
if (interp->error) { value_free(ikv); value_free(cur); err = true; break; }
1428+
if (!(ikv.type == VAL_INT || ikv.type == VAL_STR || ikv.type == VAL_FLT)) {
1429+
value_free(ikv); value_free(cur);
1430+
interp->error = strdup("Map index must be INT, FLT or STR");
1431+
interp->error_line = ik->line;
1432+
interp->error_col = ik->column;
1433+
err = true; break;
1434+
}
1435+
int found = 0;
1436+
Value got = value_map_get(cur, ikv, &found);
1437+
value_free(ikv);
1438+
if (!found) { value_free(cur); final_val = value_null(); break; }
1439+
value_free(cur);
1440+
cur = got; // continue descending (got is a copy)
1441+
if (ii + 1 == idxs->count) final_val = value_copy(cur);
1442+
}
1443+
if (err) { value_free(k); value_free(mv); return value_null(); }
1444+
// final_val holds the value to insert (may be null)
1445+
value_map_set(&mv, k, final_val);
1446+
value_free(k);
1447+
value_free(final_val);
1448+
value_free(cur);
1449+
} else {
1450+
Value v = eval_expr(interp, vexpr, env);
1451+
if (interp->error) { value_free(k); value_free(mv); return value_null(); }
1452+
value_map_set(&mv, k, v);
1453+
value_free(k);
1454+
value_free(v);
1455+
}
14141456
}
14151457
return mv;
14161458
}

src/value.c

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -346,6 +346,26 @@ void value_map_delete(Value* mapval, Value key) {
346346
m->count--;
347347
}
348348

349+
void value_map_set_self(Value* mapval, Value key) {
350+
if (!mapval || mapval->type != VAL_MAP) return;
351+
Map* m = mapval->as.map;
352+
int idx = map_find_index(m, key);
353+
if (idx >= 0) {
354+
value_free(m->items[idx].value);
355+
m->items[idx].value = value_alias(*mapval); // alias points to the same Map
356+
return;
357+
}
358+
if (m->count + 1 > m->capacity) {
359+
size_t newc = m->capacity == 0 ? 8 : m->capacity * 2;
360+
m->items = realloc(m->items, sizeof(MapEntry) * newc);
361+
if (!m->items) { fprintf(stderr, "Out of memory\n"); exit(1); }
362+
m->capacity = newc;
363+
}
364+
m->items[m->count].key = value_copy(key);
365+
m->items[m->count].value = value_alias(*mapval);
366+
m->count++;
367+
}
368+
349369
Value* value_map_get_ptr(Value* mapval, Value key, bool create_if_missing) {
350370
if (!mapval || mapval->type != VAL_MAP) return NULL;
351371
Map* m = mapval->as.map;

src/value.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,9 @@ void value_map_set(Value* mapval, Value key, Value val);
8484
Value value_map_get(Value mapval, Value key, int* found);
8585
void value_map_delete(Value* mapval, Value key);
8686

87+
// Set map entry value to an alias pointing to the map itself (SELF semantics)
88+
void value_map_set_self(Value* mapval, Value key);
89+
8790
// Pointer helpers (for lvalue/indexed assignment)
8891
// Returns a pointer to the stored value for key, optionally creating a missing entry with NULL value.
8992
// Returned pointer is owned by the map; do NOT free it.

tests/test2.pre

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -159,6 +159,11 @@ MAP: m2 = <"a" = 101, "b" = 1010>
159159
ASSERT(EQ(KEYS(m2), ["a", "b"]))
160160
ASSERT(EQ(VALUES(m2), [101, 1010]))
161161

162+
MAP: m3 = <"a" = 1, "b" = SELF<"a">, "c" = SELF>
163+
ASSERT(EQ(m3<"b">, 1))
164+
ASSERT(EQ(m3, m3<"c">))
165+
DEL(m3)
166+
162167
! Pointer tests
163168
PRINT("Testing pointers...")
164169
INT: x = 1

0 commit comments

Comments
 (0)