Skip to content

Commit 7a2e267

Browse files
gh-5: Add MAP literals.
1 parent 83ed35a commit 7a2e267

7 files changed

Lines changed: 272 additions & 9 deletions

File tree

src/ast.c

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,20 @@ Expr* expr_tns(int line, int column) {
6969
return expr;
7070
}
7171

72+
Expr* expr_map(int line, int column) {
73+
Expr* expr = ast_alloc(sizeof(Expr));
74+
expr->type = EXPR_MAP;
75+
expr->line = line;
76+
expr->column = column;
77+
expr->as.map_items.keys.items = NULL;
78+
expr->as.map_items.keys.count = 0;
79+
expr->as.map_items.keys.capacity = 0;
80+
expr->as.map_items.values.items = NULL;
81+
expr->as.map_items.values.count = 0;
82+
expr->as.map_items.values.capacity = 0;
83+
return expr;
84+
}
85+
7286
Expr* expr_index(Expr* target, int line, int column) {
7387
Expr* expr = ast_alloc(sizeof(Expr));
7488
expr->type = EXPR_INDEX;
@@ -297,6 +311,10 @@ void free_expr(Expr* expr) {
297311
case EXPR_TNS:
298312
free_expr_list(&expr->as.tns_items);
299313
break;
314+
case EXPR_MAP:
315+
free_expr_list(&expr->as.map_items.keys);
316+
free_expr_list(&expr->as.map_items.values);
317+
break;
300318
case EXPR_INDEX:
301319
free_expr(expr->as.index.target);
302320
free_expr_list(&expr->as.index.indices);

src/ast.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ typedef enum {
2222
EXPR_IDENT,
2323
EXPR_CALL,
2424
EXPR_TNS,
25+
EXPR_MAP,
2526
EXPR_INDEX,
2627
EXPR_RANGE,
2728
EXPR_WILDCARD
@@ -54,6 +55,10 @@ struct Expr {
5455
Expr* start;
5556
Expr* end;
5657
} range;
58+
struct {
59+
ExprList keys;
60+
ExprList values;
61+
} map_items;
5762
ExprList tns_items;
5863
} as;
5964
};
@@ -126,6 +131,7 @@ Expr* expr_str(char* value, int line, int column);
126131
Expr* expr_ident(char* name, int line, int column);
127132
Expr* expr_call(Expr* callee, int line, int column);
128133
Expr* expr_tns(int line, int column);
134+
Expr* expr_map(int line, int column);
129135
Expr* expr_index(Expr* target, int line, int column);
130136
Expr* expr_range(Expr* start, Expr* end, int line, int column);
131137
Expr* expr_wildcard(int line, int column);

src/interpreter.c

Lines changed: 78 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -591,19 +591,37 @@ Value eval_expr(Interpreter* interp, Expr* expr, Env* env) {
591591
free(shape);
592592
return value_null();
593593
}
594+
case EXPR_MAP: {
595+
// Evaluate map literal: keys and values
596+
Expr* mp = expr;
597+
Value mv = value_map_new();
598+
size_t count = mp->as.map_items.keys.count;
599+
for (size_t i = 0; i < count; i++) {
600+
Expr* kexpr = mp->as.map_items.keys.items[i];
601+
Expr* vexpr = mp->as.map_items.values.items[i];
602+
Value k = eval_expr(interp, kexpr, env);
603+
if (interp->error) { value_free(k); value_free(mv); return value_null(); }
604+
if (!(k.type == VAL_INT || k.type == VAL_STR || k.type == VAL_FLT)) {
605+
value_free(k);
606+
value_free(mv);
607+
interp->error = strdup("Map keys must be INT, FLT or STR");
608+
interp->error_line = expr->line;
609+
interp->error_col = expr->column;
610+
return value_null();
611+
}
612+
Value v = eval_expr(interp, vexpr, env);
613+
if (interp->error) { value_free(k); value_free(mv); return value_null(); }
614+
value_map_set(&mv, k, v);
615+
value_free(k);
616+
value_free(v);
617+
}
618+
return mv;
619+
}
594620
case EXPR_INDEX: {
595621
// Evaluate target
596622
Expr* target = expr->as.index.target;
597623
Value tval = eval_expr(interp, target, env);
598624
if (interp->error) return value_null();
599-
if (tval.type != VAL_TNS) {
600-
interp->error = strdup("Indexing is supported only on tensors");
601-
interp->error_line = expr->line;
602-
interp->error_col = expr->column;
603-
value_free(tval);
604-
return value_null();
605-
}
606-
Tensor* t = tval.as.tns;
607625
size_t nidx = expr->as.index.indices.count;
608626
if (nidx == 0) {
609627
value_free(tval);
@@ -613,6 +631,58 @@ Value eval_expr(Interpreter* interp, Expr* expr, Env* env) {
613631
return value_null();
614632
}
615633

634+
if (tval.type == VAL_MAP) {
635+
// map indexing: support nested lookups m<k1,k2>
636+
Value cur = tval;
637+
for (size_t i = 0; i < nidx; i++) {
638+
Expr* it = expr->as.index.indices.items[i];
639+
Value key = eval_expr(interp, it, env);
640+
if (interp->error) { value_free(cur); return value_null(); }
641+
if (!(key.type == VAL_INT || key.type == VAL_STR || key.type == VAL_FLT)) {
642+
value_free(key); value_free(cur);
643+
interp->error = strdup("Map index must be INT, FLT or STR");
644+
interp->error_line = it->line;
645+
interp->error_col = it->column;
646+
return value_null();
647+
}
648+
int found = 0;
649+
Value got = value_map_get(cur, key, &found);
650+
value_free(key);
651+
// If not found, return null (missing key)
652+
if (!found) { value_free(cur); return value_null(); }
653+
// If this is last index, return the found value
654+
if (i + 1 == nidx) {
655+
// free original map container if it was a copy
656+
if (cur.type == VAL_MAP) value_free(cur);
657+
return got;
658+
}
659+
// Otherwise, continue descending; found must be a map
660+
if (got.type != VAL_MAP) {
661+
value_free(got);
662+
value_free(cur);
663+
interp->error = strdup("Attempted nested map indexing on non-map value");
664+
interp->error_line = it->line;
665+
interp->error_col = it->column;
666+
return value_null();
667+
}
668+
// replace cur with got and continue
669+
if (cur.type == VAL_MAP) value_free(cur);
670+
cur = got;
671+
}
672+
// Shouldn't reach here
673+
value_free(cur);
674+
return value_null();
675+
}
676+
677+
if (tval.type != VAL_TNS) {
678+
interp->error = strdup("Indexing is supported only on tensors and maps");
679+
interp->error_line = expr->line;
680+
interp->error_col = expr->column;
681+
value_free(tval);
682+
return value_null();
683+
}
684+
Tensor* t = tval.as.tns;
685+
616686
// Check whether all indices are simple integer indexes
617687
bool all_int = true;
618688
for (size_t i = 0; i < nidx; i++) {

src/parser.c

Lines changed: 47 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -130,14 +130,37 @@ static Expr* parse_primary(Parser* parser) {
130130
consume(parser, TOKEN_RBRACKET, "Expected ']' after tensor literal");
131131
return tns;
132132
}
133+
if (match(parser, TOKEN_LANGLE)) {
134+
Token lb = parser->previous_token; // the '<' token
135+
Expr* mp = expr_map(lb.line, lb.column);
136+
if (parser->current_token.type == TOKEN_RANGLE) {
137+
report_error(parser, "Empty map literal is not allowed");
138+
return NULL;
139+
}
140+
do {
141+
// parse key
142+
Expr* key = parse_expression(parser);
143+
if (!key) return NULL;
144+
if (!match(parser, TOKEN_EQUALS)) {
145+
report_error(parser, "Expected '=' in map literal");
146+
return NULL;
147+
}
148+
Expr* val = parse_expression(parser);
149+
if (!val) return NULL;
150+
expr_list_add(&mp->as.map_items.keys, key);
151+
expr_list_add(&mp->as.map_items.values, val);
152+
} while (match(parser, TOKEN_COMMA));
153+
consume(parser, TOKEN_RANGLE, "Expected '>' after map literal");
154+
return mp;
155+
}
133156
report_error(parser, "Expected expression");
134157
return NULL;
135158
}
136159

137160
static Expr* parse_call(Parser* parser) {
138161
Expr* expr = parse_primary(parser);
139162
if (!expr) return NULL;
140-
while (parser->current_token.type == TOKEN_LPAREN || parser->current_token.type == TOKEN_LBRACKET) {
163+
while (parser->current_token.type == TOKEN_LPAREN || parser->current_token.type == TOKEN_LBRACKET || parser->current_token.type == TOKEN_LANGLE) {
141164
if (parser->current_token.type == TOKEN_LPAREN) {
142165
int line = parser->current_token.line;
143166
int column = parser->current_token.column;
@@ -200,6 +223,29 @@ static Expr* parse_call(Parser* parser) {
200223
expr = idx;
201224
continue;
202225
}
226+
227+
// Handle angle-bracket indexing for maps: '<' ... '>'
228+
if (parser->current_token.type == TOKEN_LANGLE) {
229+
int line = parser->current_token.line;
230+
int column = parser->current_token.column;
231+
advance(parser); // consume '<'
232+
Expr* idx = expr_index(expr, line, column);
233+
if (parser->current_token.type == TOKEN_RANGLE) {
234+
report_error(parser, "Empty index list");
235+
return NULL;
236+
}
237+
while (parser->current_token.type != TOKEN_RANGLE && parser->current_token.type != TOKEN_EOF) {
238+
Expr* start = parse_expression(parser);
239+
if (!start) return NULL;
240+
expr_list_add(&idx->as.index.indices, start);
241+
242+
if (parser->current_token.type == TOKEN_COMMA) { advance(parser); continue; }
243+
break;
244+
}
245+
consume(parser, TOKEN_RANGLE, "Expected '>' after index list");
246+
expr = idx;
247+
continue;
248+
}
203249
}
204250
return expr;
205251
}

src/value.c

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -200,6 +200,73 @@ Value value_tns_slice(Value v, const int64_t* starts, const int64_t* ends, size_
200200
return out;
201201
}
202202

203+
// Map implementation
204+
Value value_map_new(void) {
205+
Value v; v.type = VAL_MAP;
206+
Map* m = malloc(sizeof(Map));
207+
if (!m) { fprintf(stderr, "Out of memory\n"); exit(1); }
208+
m->items = NULL;
209+
m->count = 0;
210+
m->capacity = 0;
211+
v.as.map = m;
212+
return v;
213+
}
214+
215+
static int map_find_index(Map* m, Value key) {
216+
for (size_t i = 0; i < m->count; i++) {
217+
MapEntry* e = &m->items[i];
218+
if (e->key.type == key.type) {
219+
if (key.type == VAL_INT && e->key.as.i == key.as.i) return (int)i;
220+
if (key.type == VAL_STR && e->key.as.s && key.as.s && strcmp(e->key.as.s, key.as.s) == 0) return (int)i;
221+
if (key.type == VAL_FLT && e->key.as.f == key.as.f) return (int)i;
222+
}
223+
}
224+
return -1;
225+
}
226+
227+
void value_map_set(Value* mapval, Value key, Value val) {
228+
if (!mapval || mapval->type != VAL_MAP) return;
229+
Map* m = mapval->as.map;
230+
int idx = map_find_index(m, key);
231+
if (idx >= 0) {
232+
// replace
233+
value_free(m->items[idx].value);
234+
m->items[idx].value = value_copy(val);
235+
return;
236+
}
237+
if (m->count + 1 > m->capacity) {
238+
size_t newc = m->capacity == 0 ? 8 : m->capacity * 2;
239+
m->items = realloc(m->items, sizeof(MapEntry) * newc);
240+
if (!m->items) { fprintf(stderr, "Out of memory\n"); exit(1); }
241+
m->capacity = newc;
242+
}
243+
m->items[m->count].key = value_copy(key);
244+
m->items[m->count].value = value_copy(val);
245+
m->count++;
246+
}
247+
248+
Value value_map_get(Value mapval, Value key, int* found) {
249+
Value out = value_null();
250+
if (!mapval.as.map) { if (found) *found = 0; return out; }
251+
Map* m = mapval.as.map;
252+
int idx = map_find_index(m, key);
253+
if (idx < 0) { if (found) *found = 0; return out; }
254+
if (found) *found = 1;
255+
return value_copy(m->items[idx].value);
256+
}
257+
258+
void value_map_delete(Value* mapval, Value key) {
259+
if (!mapval || mapval->type != VAL_MAP) return;
260+
Map* m = mapval->as.map;
261+
int idx = map_find_index(m, key);
262+
if (idx < 0) return;
263+
value_free(m->items[idx].key);
264+
value_free(m->items[idx].value);
265+
// compact
266+
for (size_t i = (size_t)idx; i + 1 < m->count; i++) m->items[i] = m->items[i+1];
267+
m->count--;
268+
}
269+
203270
Value value_copy(Value v) {
204271
Value out = v;
205272
if (v.type == VAL_STR && v.as.s) {
@@ -216,6 +283,18 @@ Value value_copy(Value v) {
216283
t2->data = malloc(sizeof(Value) * t2->length);
217284
for (size_t i = 0; i < t2->length; i++) t2->data[i] = value_copy(t->data[i]);
218285
out.as.tns = t2;
286+
} else if (v.type == VAL_MAP && v.as.map) {
287+
Map* m = v.as.map;
288+
Map* m2 = malloc(sizeof(Map));
289+
if (!m2) { fprintf(stderr, "Out of memory\n"); exit(1); }
290+
m2->count = m->count;
291+
m2->capacity = m->count;
292+
m2->items = malloc(sizeof(MapEntry) * (m2->capacity ? m2->capacity : 1));
293+
for (size_t i = 0; i < m->count; i++) {
294+
m2->items[i].key = value_copy(m->items[i].key);
295+
m2->items[i].value = value_copy(m->items[i].value);
296+
}
297+
out.as.map = m2;
219298
}
220299
return out;
221300
}
@@ -233,12 +312,24 @@ void value_free(Value v) {
233312
if (t->strides) free(t->strides);
234313
free(t);
235314
}
315+
else if (v.type == VAL_MAP && v.as.map) {
316+
Map* m = v.as.map;
317+
if (m->items) {
318+
for (size_t i = 0; i < m->count; i++) {
319+
value_free(m->items[i].key);
320+
value_free(m->items[i].value);
321+
}
322+
free(m->items);
323+
}
324+
free(m);
325+
}
236326
}
237327

238328
const char* value_type_name(Value v) {
239329
switch (v.type) {
240330
case VAL_INT: return "INT";
241331
case VAL_FLT: return "FLT";
332+
case VAL_MAP: return "MAP";
242333
case VAL_TNS: return "TNS";
243334
case VAL_STR: return "STR";
244335
case VAL_FUNC: return "FUNC";

src/value.h

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ typedef enum {
99
VAL_FLT,
1010
VAL_STR,
1111
VAL_TNS,
12+
VAL_MAP,
1213
VAL_FUNC
1314
} ValueType;
1415

@@ -33,15 +34,33 @@ typedef struct Value {
3334
char* s;
3435
struct Func* func;
3536
struct Tensor* tns;
37+
struct Map* map;
3638
} as;
3739
} Value;
3840

41+
typedef struct MapEntry {
42+
Value key;
43+
Value value;
44+
} MapEntry;
45+
46+
typedef struct Map {
47+
MapEntry* items;
48+
size_t count;
49+
size_t capacity;
50+
} Map;
51+
3952
// Tensor helpers
4053
Value value_tns_new(DeclType elem_type, size_t ndim, const size_t* shape);
4154
Value value_tns_from_values(DeclType elem_type, size_t ndim, const size_t* shape, Value* items, size_t item_count);
4255
Value value_tns_get(Value t, const size_t* idxs, size_t nidxs);
4356
Value value_tns_slice(Value t, const int64_t* starts, const int64_t* ends, size_t n);
4457

58+
// Map helpers
59+
Value value_map_new(void);
60+
void value_map_set(Value* mapval, Value key, Value val);
61+
Value value_map_get(Value mapval, Value key, int* found);
62+
void value_map_delete(Value* mapval, Value key);
63+
4564

4665
Value value_null(void);
4766
Value value_int(int64_t v);

0 commit comments

Comments
 (0)