Skip to content

Commit da58602

Browse files
gh-60: Fix ASSIGN.
1 parent 8919af2 commit da58602

File tree

6 files changed

+116
-2
lines changed

6 files changed

+116
-2
lines changed

src/ast.c

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,16 @@ Expr* expr_ident(char* name, int line, int column) {
5656
return expr;
5757
}
5858

59+
Expr* expr_typed_ident(DeclType decl_type, char* name, int line, int column) {
60+
Expr* expr = ast_alloc(sizeof(Expr));
61+
expr->type = EXPR_TYPED_IDENT;
62+
expr->line = line;
63+
expr->column = column;
64+
expr->as.typed_ident.decl_type = decl_type;
65+
expr->as.typed_ident.name = name;
66+
return expr;
67+
}
68+
5969
Expr* expr_call(Expr* callee, int line, int column) {
6070
Expr* expr = ast_alloc(sizeof(Expr));
6171
expr->type = EXPR_CALL;
@@ -399,6 +409,9 @@ void free_expr(Expr* expr) {
399409
case EXPR_STR:
400410
free(expr->as.str_value);
401411
break;
412+
case EXPR_TYPED_IDENT:
413+
free(expr->as.typed_ident.name);
414+
break;
402415
case EXPR_PTR:
403416
free(expr->as.ptr_name);
404417
break;

src/ast.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ typedef enum {
3636
EXPR_STR,
3737
EXPR_PTR,
3838
EXPR_IDENT,
39+
EXPR_TYPED_IDENT,
3940
EXPR_CALL,
4041
EXPR_ASYNC,
4142
EXPR_TNS,
@@ -61,6 +62,7 @@ struct Expr {
6162
struct { double value; int base; int base_is_nan; } flt_value;
6263
char* str_value;
6364
char* ident;
65+
struct { DeclType decl_type; char* name; } typed_ident;
6466
char* ptr_name;
6567
struct { Stmt* block; } async;
6668
struct {
@@ -156,6 +158,7 @@ Expr* expr_flt(double value, int base, int base_is_nan, int line, int column);
156158
Expr* expr_str(char* value, int line, int column);
157159
Expr* expr_ptr(char* name, int line, int column);
158160
Expr* expr_ident(char* name, int line, int column);
161+
Expr* expr_typed_ident(DeclType decl_type, char* name, int line, int column);
159162
Expr* expr_call(Expr* callee, int line, int column);
160163
void call_kw_add(Expr* call, char* name, Expr* value);
161164
Expr* expr_tns(int line, int column);

src/builtins.c

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1183,6 +1183,20 @@ static void ser_expr(JsonBuf* jb, SerCtx* ctx, Interpreter* interp, Expr* expr)
11831183
jb_append_char(jb, '}');
11841184
return;
11851185
}
1186+
case EXPR_TYPED_IDENT: {
1187+
jb_append_char(jb, '{');
1188+
bool first = true;
1189+
json_obj_field(jb, &first, "n");
1190+
jb_append_json_string(jb, "TypedIdentifier");
1191+
json_obj_field(jb, &first, "loc");
1192+
ser_loc(jb, expr->line, expr->column);
1193+
json_obj_field(jb, &first, "decl_type");
1194+
jb_append_json_string(jb, decl_type_name(expr->as.typed_ident.decl_type));
1195+
json_obj_field(jb, &first, "name");
1196+
jb_append_json_string(jb, expr->as.typed_ident.name ? expr->as.typed_ident.name : "");
1197+
jb_append_char(jb, '}');
1198+
return;
1199+
}
11861200
case EXPR_PTR: {
11871201
jb_append_char(jb, '{');
11881202
bool first = true;
@@ -2039,6 +2053,13 @@ static Expr* deser_expr(JsonValue* obj, UnserCtx* ctx, Interpreter* interp, cons
20392053
const char* s = (nm && nm->type == JSON_STR) ? nm->as.str : "";
20402054
return expr_ident(strdup(s), line, col);
20412055
}
2056+
if (strcmp(name, "TypedIdentifier") == 0) {
2057+
JsonValue* typev = json_obj_get(obj, "decl_type");
2058+
JsonValue* nm = json_obj_get(obj, "name");
2059+
const char* t = (typev && typev->type == JSON_STR) ? typev->as.str : "";
2060+
const char* s = (nm && nm->type == JSON_STR) ? nm->as.str : "";
2061+
return expr_typed_ident(decl_type_from_name(t), strdup(s), line, col);
2062+
}
20422063
if (strcmp(name, "PointerExpression") == 0) {
20432064
JsonValue* nm = json_obj_get(obj, "target");
20442065
const char* s = (nm && nm->type == JSON_STR) ? nm->as.str : "";
@@ -6163,6 +6184,52 @@ static Value builtin_assign(Interpreter* interp, Value* args, int argc, Expr** a
61636184

61646185
Value rhs = args[1];
61656186

6187+
if (target->type == EXPR_TYPED_IDENT) {
6188+
const char* name = target->as.typed_ident.name;
6189+
DeclType expected = target->as.typed_ident.decl_type;
6190+
DeclType actual;
6191+
6192+
switch (rhs.type) {
6193+
case VAL_INT: actual = TYPE_INT; break;
6194+
case VAL_FLT: actual = TYPE_FLT; break;
6195+
case VAL_STR: actual = TYPE_STR; break;
6196+
case VAL_TNS: actual = TYPE_TNS; break;
6197+
case VAL_MAP: actual = TYPE_MAP; break;
6198+
case VAL_FUNC: actual = TYPE_FUNC; break;
6199+
case VAL_THR: actual = TYPE_THR; break;
6200+
default: actual = TYPE_UNKNOWN; break;
6201+
}
6202+
6203+
if (expected != actual) {
6204+
char buf[128];
6205+
snprintf(buf, sizeof(buf), "Type mismatch: expected %s but got %s",
6206+
decl_type_name(expected), value_type_name(rhs));
6207+
RUNTIME_ERROR(interp, buf, line, col);
6208+
}
6209+
6210+
EnvEntry* existing = env_get_entry(env, name);
6211+
if (existing && existing->decl_type != expected) {
6212+
char buf[128];
6213+
snprintf(buf, sizeof(buf), "Type mismatch: expected %s but got %s",
6214+
decl_type_name(existing->decl_type), decl_type_name(expected));
6215+
RUNTIME_ERROR(interp, buf, line, col);
6216+
}
6217+
6218+
Env* assign_env = env;
6219+
if (!interp->isolate_env_writes && !existing && env->parent) {
6220+
assign_env = env->parent;
6221+
}
6222+
if (!existing) {
6223+
env_define(assign_env, name, expected);
6224+
}
6225+
if (!env_assign(assign_env, name, rhs, expected, true)) {
6226+
char buf[256];
6227+
snprintf(buf, sizeof(buf), "ASSIGN: cannot assign to target '%s'", name);
6228+
RUNTIME_ERROR(interp, buf, line, col);
6229+
}
6230+
return value_copy(rhs);
6231+
}
6232+
61666233
// Identifier target
61676234
if (target->type == EXPR_IDENT) {
61686235
const char* name = target->as.ident;

src/parser.c

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -225,6 +225,20 @@ static bool parse_param_list(Parser* parser, ParamList* params) {
225225
return true;
226226
}
227227

228+
static Expr* parse_typed_ident_expr(Parser* parser) {
229+
Token type_tok = parser->current_token;
230+
DeclType dtype = parse_type_name(type_tok.literal);
231+
advance(parser);
232+
consume(parser, TOKEN_COLON, "Expected ':' after type");
233+
if (parser->current_token.type != TOKEN_IDENT) {
234+
report_error(parser, "Expected identifier name");
235+
return NULL;
236+
}
237+
char* name = parser->current_token.literal;
238+
advance(parser);
239+
return expr_typed_ident(dtype, name, type_tok.line, type_tok.column);
240+
}
241+
228242
static Expr* parse_primary(Parser* parser) {
229243
Token token = parser->current_token;
230244
// Recognize FLT literal names `INF` and `NaN` as primary expressions
@@ -393,8 +407,17 @@ static Expr* parse_call(Parser* parser) {
393407
if (parser->current_token.type != TOKEN_RPAREN) {
394408
bool seen_kw = false;
395409
do {
396-
// Keyword arg form: IDENT '=' expr
397-
if (parser->current_token.type == TOKEN_IDENT && parser->next_token.type == TOKEN_EQUALS) {
410+
if (call->as.call.callee->type == EXPR_IDENT
411+
&& strcmp(call->as.call.callee->as.ident, "ASSIGN") == 0
412+
&& call->as.call.args.count == 0
413+
&& call->as.call.kw_count == 0
414+
&& is_type_token(parser->current_token.type)
415+
&& parser->next_token.type == TOKEN_COLON) {
416+
Expr* arg = parse_typed_ident_expr(parser);
417+
if (!arg) return NULL;
418+
expr_list_add(&call->as.call.args, arg);
419+
} else if (parser->current_token.type == TOKEN_IDENT && parser->next_token.type == TOKEN_EQUALS) {
420+
// Keyword arg form: IDENT '=' expr
398421
seen_kw = true;
399422
char* name = parser->current_token.literal;
400423
advance(parser); // consume IDENT

tests/test.pre

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -157,10 +157,13 @@ FUNC INT: BUILTIN_TESTS(){
157157
! Typed first assignment returns the assigned value
158158
ASSERT( EQ( ASSIGN(INT: ax, 1), 1 ) )
159159
ASSERT( EQ( ax, 1 ) )
160+
ASSERT( EQ( ASSIGN(STR: as, "ok"), "ok" ) )
161+
ASSERT( EQ( as, "ok" ) )
160162
! Untyped assignment allowed once type exists
161163
ASSERT( EQ( ASSIGN(ax, 10), 10 ) )
162164
ASSERT( EQ( ax, 10 ) )
163165
DEL(ax)
166+
DEL(as)
164167

165168
! Indexed ASSIGN on tensor
166169
TNS: at = [0, 0]

tests/test2.pre

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -194,6 +194,11 @@ DEL(y)
194194
PRINT("Pointers: PASS\n")
195195

196196
PRINT("Testing ASSIGN inline operator...")
197+
! typed first assignment should declare, assign, and return the value
198+
ASSERT(EQ(ASSIGN(STR: as_s, "foo"), "foo"))
199+
ASSERT(EQ(as_s, "foo"))
200+
DEL(as_s)
201+
197202
! simple identifier assignment (target must already exist)
198203
INT: as_a = 0d0
199204
ASSIGN(as_a, 0d5)

0 commit comments

Comments
 (0)