Skip to content

Commit 8c56860

Browse files
gh-80: Ban incorrect bracket kinds.
1 parent e750b1b commit 8c56860

5 files changed

Lines changed: 54 additions & 12 deletions

File tree

src/ast.c

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -109,7 +109,7 @@ Expr* expr_map(int line, int column) {
109109
return expr;
110110
}
111111

112-
Expr* expr_index(Expr* target, int line, int column) {
112+
Expr* expr_index(Expr* target, int line, int column, bool is_map) {
113113
Expr* expr = ast_alloc(sizeof(Expr));
114114
expr->type = EXPR_INDEX;
115115
expr->line = line;
@@ -118,6 +118,7 @@ Expr* expr_index(Expr* target, int line, int column) {
118118
expr->as.index.indices.items = NULL;
119119
expr->as.index.indices.count = 0;
120120
expr->as.index.indices.capacity = 0;
121+
expr->as.index.is_map = is_map;
121122
return expr;
122123
}
123124

src/ast.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,7 @@ struct Expr {
7676
struct {
7777
Expr* target;
7878
ExprList indices;
79+
bool is_map; /* true if angle-bracket indexing '<...>' was used */
7980
} index;
8081
struct {
8182
Expr* start;
@@ -164,7 +165,7 @@ void call_kw_add(Expr* call, char* name, Expr* value);
164165
Expr* expr_tns(int line, int column);
165166
Expr* expr_async(Stmt* block, int line, int column);
166167
Expr* expr_map(int line, int column);
167-
Expr* expr_index(Expr* target, int line, int column);
168+
Expr* expr_index(Expr* target, int line, int column, bool is_map);
168169
Expr* expr_range(Expr* start, Expr* end, int line, int column);
169170
Expr* expr_wildcard(int line, int column);
170171
Expr* expr_lambda(ParamList params, DeclType return_type, Stmt* body, int line, int column);

src/builtins.c

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1282,7 +1282,7 @@ static void ser_expr(JsonBuf* jb, SerCtx* ctx, Interpreter* interp, Expr* expr)
12821282
}
12831283
jb_append_char(jb, ']');
12841284
json_obj_field(jb, &first, "is_map");
1285-
jb_append_str(jb, "false");
1285+
jb_append_str(jb, expr->as.index.is_map ? "true" : "false");
12861286
jb_append_char(jb, '}');
12871287
return;
12881288
}
@@ -2092,7 +2092,10 @@ static Expr* deser_expr(JsonValue* obj, UnserCtx* ctx, Interpreter* interp, cons
20922092
}
20932093
if (strcmp(name, "IndexExpression") == 0) {
20942094
Expr* base = deser_expr(json_obj_get(obj, "base"), ctx, interp, err);
2095-
Expr* idx = expr_index(base, line, col);
2095+
JsonValue* is_map_v = json_obj_get(obj, "is_map");
2096+
bool is_map = false;
2097+
if (is_map_v && is_map_v->type == JSON_BOOL) is_map = is_map_v->as.boolean ? true : false;
2098+
Expr* idx = expr_index(base, line, col, is_map);
20962099
JsonValue* indices = json_obj_get(obj, "indices");
20972100
if (indices && indices->type == JSON_ARR) {
20982101
for (size_t i = 0; i < indices->as.arr.count; i++) {

src/interpreter.c

Lines changed: 41 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1603,7 +1603,7 @@ Value eval_expr(Interpreter* interp, Expr* expr, Env* env) {
16031603
if (vexpr->type == EXPR_IDENT && vexpr->as.ident && strcmp(vexpr->as.ident, "SELF") == 0) {
16041604
value_map_set_self(&mv, k);
16051605
value_free(k);
1606-
} else if (vexpr->type == EXPR_INDEX && vexpr->as.index.target && vexpr->as.index.target->type == EXPR_IDENT
1606+
} else if (vexpr->type == EXPR_INDEX && vexpr->as.index.is_map && vexpr->as.index.target && vexpr->as.index.target->type == EXPR_IDENT
16071607
&& vexpr->as.index.target->as.ident && strcmp(vexpr->as.index.target->as.ident, "SELF") == 0) {
16081608
// Evaluate index key(s) and perform lookup on the partially-constructed map `mv`.
16091609
ExprList* idxs = &vexpr->as.index.indices;
@@ -1660,8 +1660,27 @@ Value eval_expr(Interpreter* interp, Expr* expr, Env* env) {
16601660
interp->error_col = expr->column;
16611661
return value_null();
16621662
}
1663+
bool is_map_index = expr->as.index.is_map;
16631664

1664-
if (tval.type == VAL_MAP) {
1665+
if (is_map_index) {
1666+
if (tval.type != VAL_MAP) {
1667+
value_free(tval);
1668+
interp->error = strdup("Angle-bracket indexing '<...>' is only allowed on MAP values");
1669+
interp->error_line = expr->line;
1670+
interp->error_col = expr->column;
1671+
return value_null();
1672+
}
1673+
} else {
1674+
if (tval.type != VAL_TNS) {
1675+
value_free(tval);
1676+
interp->error = strdup("Square-bracket indexing '[...]' is only allowed on TNS values");
1677+
interp->error_line = expr->line;
1678+
interp->error_col = expr->column;
1679+
return value_null();
1680+
}
1681+
}
1682+
1683+
if (is_map_index && tval.type == VAL_MAP) {
16651684
// map indexing: support nested lookups m<k1,k2>
16661685
Value cur = tval;
16671686
for (size_t i = 0; i < nidx; i++) {
@@ -1947,9 +1966,27 @@ ExecResult assign_index_chain(Interpreter* interp, Env* env, Expr* idx_expr, Val
19471966
goto cleanup;
19481967
}
19491968

1950-
// Auto-promote NULL to MAP when assigning through indexes.
1969+
// Auto-promote NULL to MAP only when angle-bracket (map) indexing is used.
19511970
if (cur->type == VAL_NULL) {
1952-
*cur = value_map_new();
1971+
if (node->as.index.is_map) {
1972+
*cur = value_map_new();
1973+
} else {
1974+
out = make_error("Attempted tensor indexing on uninitialized value", node->line, node->column);
1975+
goto cleanup;
1976+
}
1977+
}
1978+
1979+
/* Enforce bracket kind matches the container type: angle-brackets -> MAP, square-brackets -> TNS */
1980+
if (node->as.index.is_map) {
1981+
if (cur->type != VAL_MAP) {
1982+
out = make_error("Angle-bracket indexing '<...>' used on non-map value", node->line, node->column);
1983+
goto cleanup;
1984+
}
1985+
} else {
1986+
if (cur->type != VAL_TNS) {
1987+
out = make_error("Square-bracket indexing '[...]' used on non-tensor value", node->line, node->column);
1988+
goto cleanup;
1989+
}
19531990
}
19541991

19551992
if (cur->type == VAL_TNS) {

src/parser.c

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -443,7 +443,7 @@ static Expr* parse_call(Parser* parser) {
443443
int line = parser->current_token.line;
444444
int column = parser->current_token.column;
445445
advance(parser); // consume '['
446-
Expr* idx = expr_index(expr, line, column);
446+
Expr* idx = expr_index(expr, line, column, false);
447447
if (parser->current_token.type == TOKEN_RBRACKET) {
448448
report_error(parser, "Empty index list");
449449
return NULL;
@@ -489,7 +489,7 @@ static Expr* parse_call(Parser* parser) {
489489
int line = parser->current_token.line;
490490
int column = parser->current_token.column;
491491
advance(parser); // consume '<'
492-
Expr* idx = expr_index(expr, line, column);
492+
Expr* idx = expr_index(expr, line, column, true);
493493
if (parser->current_token.type == TOKEN_RANGLE) {
494494
report_error(parser, "Empty index list");
495495
return NULL;
@@ -680,7 +680,7 @@ static Stmt* parse_statement(Parser* parser) {
680680
int line = parser->current_token.line;
681681
int column = parser->current_token.column;
682682
advance(parser); // consume '['
683-
Expr* idx = expr_index(base, line, column);
683+
Expr* idx = expr_index(base, line, column, false);
684684
if (parser->current_token.type == TOKEN_RBRACKET) {
685685
report_error(parser, "Empty index list");
686686
return NULL;
@@ -719,7 +719,7 @@ static Stmt* parse_statement(Parser* parser) {
719719
int line = parser->current_token.line;
720720
int column = parser->current_token.column;
721721
advance(parser); // consume '<'
722-
Expr* idx = expr_index(base, line, column);
722+
Expr* idx = expr_index(base, line, column, true);
723723
if (parser->current_token.type == TOKEN_RANGLE) {
724724
report_error(parser, "Empty index list");
725725
return NULL;

0 commit comments

Comments
 (0)