Skip to content

Commit f733914

Browse files
gh-9: Add pointers, gitignore.
1 parent 01e33ea commit f733914

10 files changed

Lines changed: 214 additions & 6 deletions

File tree

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
prefix.exe

src/ast.c

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -318,6 +318,9 @@ void free_expr(Expr* expr) {
318318
case EXPR_STR:
319319
free(expr->as.str_value);
320320
break;
321+
case EXPR_PTR:
322+
free(expr->as.ptr_name);
323+
break;
321324
case EXPR_TNS:
322325
free_expr_list(&expr->as.tns_items);
323326
break;
@@ -412,4 +415,13 @@ void free_stmt(Stmt* stmt) {
412415
break;
413416
}
414417
free(stmt);
418+
}
419+
420+
Expr* expr_ptr(char* name, int line, int column) {
421+
Expr* expr = ast_alloc(sizeof(Expr));
422+
expr->type = EXPR_PTR;
423+
expr->line = line;
424+
expr->column = column;
425+
expr->as.ptr_name = name;
426+
return expr;
415427
}

src/ast.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ typedef enum {
1919
EXPR_INT,
2020
EXPR_FLT,
2121
EXPR_STR,
22+
EXPR_PTR,
2223
EXPR_IDENT,
2324
EXPR_CALL,
2425
EXPR_TNS,
@@ -43,6 +44,7 @@ struct Expr {
4344
double flt_value;
4445
char* str_value;
4546
char* ident;
47+
char* ptr_name;
4648
struct {
4749
Expr* callee;
4850
ExprList args;
@@ -130,6 +132,7 @@ struct Stmt {
130132
Expr* expr_int(int64_t value, int line, int column);
131133
Expr* expr_flt(double value, int line, int column);
132134
Expr* expr_str(char* value, int line, int column);
135+
Expr* expr_ptr(char* name, int line, int column);
133136
Expr* expr_ident(char* name, int line, int column);
134137
Expr* expr_call(Expr* callee, int line, int column);
135138
Expr* expr_tns(int line, int column);

src/env.c

Lines changed: 101 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,55 @@ Env* env_create(Env* parent) {
2323
return env;
2424
}
2525

26+
// Forward declaration for lookup helper used below
27+
static EnvEntry* env_find_local(Env* env, const char* name);
28+
29+
bool env_set_alias(Env* env, const char* name, const char* target_name, DeclType type, bool declare_if_missing) {
30+
if (!env || !name || !target_name) return false;
31+
// Ensure the target exists
32+
EnvEntry* target = env_get_entry(env, target_name);
33+
if (!target) return false;
34+
35+
// Resolve final target through alias chain and ensure no cycle to 'name'
36+
EnvEntry* cur = target;
37+
int depth = 0;
38+
while (cur && cur->alias_target) {
39+
if (depth++ > 256) return false;
40+
if (strcmp(cur->alias_target, name) == 0) return false; // would create cycle
41+
cur = env_get_entry(env, cur->alias_target);
42+
}
43+
if (!cur) return false;
44+
45+
// Disallow aliasing to frozen/permafrozen target
46+
if (cur->frozen || cur->permafrozen) return false;
47+
48+
// Find or create local entry
49+
EnvEntry* entry = env_find_local(env, name);
50+
if (!entry) {
51+
if (!declare_if_missing) return false;
52+
if (!env_define(env, name, type)) return false;
53+
entry = env_find_local(env, name);
54+
}
55+
56+
// Respect frozen state on the entry itself
57+
if (entry->frozen || entry->permafrozen) return false;
58+
59+
// Type compatibility
60+
if (type != TYPE_UNKNOWN && type != cur->decl_type) return false;
61+
entry->decl_type = cur->decl_type;
62+
63+
// Clear any stored value and set alias
64+
if (entry->initialized) {
65+
value_free(entry->value);
66+
entry->initialized = false;
67+
entry->value = value_null();
68+
}
69+
if (entry->alias_target) { free(entry->alias_target); entry->alias_target = NULL; }
70+
entry->alias_target = strdup(cur->name);
71+
entry->initialized = true; // alias is considered an initialized binding
72+
return true;
73+
}
74+
2675
void env_free(Env* env) {
2776
if (!env) return;
2877
for (size_t i = 0; i < env->count; i++) {
@@ -69,6 +118,7 @@ bool env_define(Env* env, const char* name, DeclType type) {
69118
entry->initialized = false;
70119
entry->frozen = false;
71120
entry->permafrozen = false;
121+
entry->alias_target = NULL;
72122
entry->value = value_null();
73123
return true;
74124
}
@@ -77,10 +127,26 @@ bool env_assign(Env* env, const char* name, Value value, DeclType type, bool dec
77127
for (Env* e = env; e != NULL; e = e->parent) {
78128
EnvEntry* entry = env_find_local(e, name);
79129
if (entry) {
80-
// Respect frozen/permanent-frozen bindings
130+
// If this binding is an alias, route assignment to the alias target
131+
if (entry->alias_target) {
132+
const char* target_name = entry->alias_target;
133+
if (!target_name) return false;
134+
EnvEntry* target = env_get_entry(env, target_name);
135+
if (!target) return false;
136+
// Respect freezing on the target
137+
if (target->frozen || target->permafrozen) return false;
138+
if (target->initialized) value_free(target->value);
139+
target->value = value_copy(value);
140+
target->initialized = true;
141+
return true;
142+
}
143+
144+
// Respect frozen/permanent-frozen bindings for normal assignments
81145
if (entry->frozen || entry->permafrozen) {
82146
return false;
83147
}
148+
149+
// Normal assignment: respect frozen/permafrozen bindings
84150
if (entry->initialized) {
85151
value_free(entry->value);
86152
}
@@ -101,9 +167,17 @@ bool env_get(Env* env, const char* name, Value* out_value, DeclType* out_type, b
101167
for (Env* e = env; e != NULL; e = e->parent) {
102168
EnvEntry* entry = env_find_local(e, name);
103169
if (entry) {
104-
if (out_value) *out_value = value_copy(entry->value);
105-
if (out_type) *out_type = entry->decl_type;
106-
if (out_initialized) *out_initialized = entry->initialized;
170+
// If this entry is an alias, follow chain to final target
171+
EnvEntry* cur = entry;
172+
int depth = 0;
173+
while (cur && cur->alias_target) {
174+
if (depth++ > 256) return false; // cycle or too deep
175+
cur = env_get_entry(env, cur->alias_target);
176+
}
177+
if (!cur) return false;
178+
if (out_value) *out_value = value_copy(cur->value);
179+
if (out_type) *out_type = cur->decl_type;
180+
if (out_initialized) *out_initialized = cur->initialized;
107181
return true;
108182
}
109183
}
@@ -121,6 +195,7 @@ bool env_delete(Env* env, const char* name) {
121195
if (entry->initialized) {
122196
value_free(entry->value);
123197
}
198+
if (entry->alias_target) { free(entry->alias_target); entry->alias_target = NULL; }
124199
entry->initialized = false;
125200
entry->value = value_null();
126201
return true;
@@ -166,10 +241,31 @@ int env_permafrozen(Env* env, const char* name) {
166241
return entry->permafrozen ? 1 : 0;
167242
}
168243

244+
// EnvEntry accessors
245+
bool env_entry_initialized(EnvEntry* entry) {
246+
if (!entry) return false;
247+
return entry->initialized;
248+
}
249+
250+
Value env_entry_value_copy(EnvEntry* entry) {
251+
if (!entry) return value_null();
252+
return value_copy(entry->value);
253+
}
254+
255+
int env_entry_frozen_state_local(EnvEntry* entry) {
256+
if (!entry) return 0;
257+
if (entry->permafrozen) return -1;
258+
if (entry->frozen) return 1;
259+
return 0;
260+
}
261+
169262
bool env_exists(Env* env, const char* name) {
170263
for (Env* e = env; e != NULL; e = e->parent) {
171264
EnvEntry* entry = env_find_local(e, name);
172265
if (entry && entry->initialized) return true;
173266
}
174267
return false;
175-
}
268+
}
269+
270+
// Forward declaration for local lookup helper (defined later)
271+
static EnvEntry* env_find_local(Env* env, const char* name);

src/env.h

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,15 @@
33

44
#include "value.h"
55

6-
typedef struct {
6+
typedef struct EnvEntry {
77
char* name;
88
DeclType decl_type;
99
Value value;
1010
bool initialized;
1111
bool frozen;
1212
bool permafrozen;
13+
// If non-NULL, this entry is an alias to another binding name in the environment
14+
char* alias_target;
1315
} EnvEntry;
1416

1517
typedef struct Env {
@@ -31,6 +33,18 @@ bool env_exists(Env* env, const char* name);
3133
// Caller must NOT free the returned pointer. Returns NULL if not found.
3234
EnvEntry* env_get_entry(Env* env, const char* name);
3335

36+
// Create or update an alias (pointer) binding: `name` will become an alias to `target_name`.
37+
// If declare_if_missing is true, `name` will be defined if absent. Returns true on success.
38+
bool env_set_alias(Env* env, const char* name, const char* target_name, DeclType type, bool declare_if_missing);
39+
40+
// Accessors for EnvEntry opaque use from other translation units
41+
// Returns true if the entry is initialized
42+
bool env_entry_initialized(EnvEntry* entry);
43+
// Returns a copy of the entry's value (caller owns the returned Value)
44+
Value env_entry_value_copy(EnvEntry* entry);
45+
// Returns frozen state: -1 permafrozen, 1 frozen, 0 not frozen or not found
46+
int env_entry_frozen_state_local(EnvEntry* entry);
47+
3448
// Symbol freezing API
3549
// Returns 0 on success, -1 if the identifier was not found.
3650
int env_freeze(Env* env, const char* name);

src/interpreter.c

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -315,6 +315,29 @@ Value eval_expr(Interpreter* interp, Expr* expr, Env* env) {
315315
}
316316
return v;
317317
}
318+
319+
case EXPR_PTR: {
320+
// Evaluate pointer literal by returning the pointed-to binding's value (dereference-on-read)
321+
const char* name = expr->as.ptr_name;
322+
Value v; DeclType dt; bool initialized;
323+
if (!env_get(env, name, &v, &dt, &initialized)) {
324+
char buf[128];
325+
snprintf(buf, sizeof(buf), "Undefined identifier '%s'", name);
326+
interp->error = strdup(buf);
327+
interp->error_line = expr->line;
328+
interp->error_col = expr->column;
329+
return value_null();
330+
}
331+
if (!initialized) {
332+
char buf[128];
333+
snprintf(buf, sizeof(buf), "Identifier '%s' declared but not initialized", name);
334+
interp->error = strdup(buf);
335+
interp->error_line = expr->line;
336+
interp->error_col = expr->column;
337+
return value_null();
338+
}
339+
return v;
340+
}
318341

319342
case EXPR_CALL: {
320343
// Get the callee
@@ -923,6 +946,28 @@ static ExecResult exec_stmt(Interpreter* interp, Stmt* stmt, Env* env, LabelMap*
923946
}
924947

925948
case STMT_ASSIGN: {
949+
// Special-case: RHS is a pointer literal -> create alias binding on LHS (no PTR Value type)
950+
if (stmt->as.assign.value && stmt->as.assign.value->type == EXPR_PTR && stmt->as.assign.target == NULL) {
951+
const char* tgt = stmt->as.assign.value->as.ptr_name;
952+
if (!tgt) return make_error("Invalid pointer literal", stmt->line, stmt->column);
953+
if (stmt->as.assign.has_type) {
954+
DeclType expected = stmt->as.assign.decl_type;
955+
env_define(env, stmt->as.assign.name, expected);
956+
if (!env_set_alias(env, stmt->as.assign.name, tgt, expected, true)) {
957+
char buf[256];
958+
snprintf(buf, sizeof(buf), "Cannot create alias '%s' -> '%s'", stmt->as.assign.name, tgt);
959+
return make_error(buf, stmt->line, stmt->column);
960+
}
961+
} else {
962+
if (!env_set_alias(env, stmt->as.assign.name, tgt, TYPE_UNKNOWN, false)) {
963+
char buf[256];
964+
snprintf(buf, sizeof(buf), "Cannot create alias '%s' -> '%s'", stmt->as.assign.name, tgt);
965+
return make_error(buf, stmt->line, stmt->column);
966+
}
967+
}
968+
return make_ok(value_null());
969+
}
970+
926971
Value v = eval_expr(interp, stmt->as.assign.value, env);
927972
if (interp->error) {
928973
ExecResult err = make_error(interp->error, interp->error_line, interp->error_col);

src/parser.c

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,15 @@ static Expr* parse_primary(Parser* parser) {
107107
if (match(parser, TOKEN_STRING)) {
108108
return expr_str(token.literal, token.line, token.column);
109109
}
110+
if (match(parser, TOKEN_AT)) {
111+
if (parser->current_token.type != TOKEN_IDENT) {
112+
report_error(parser, "Expected identifier after '@'");
113+
return NULL;
114+
}
115+
Token id = parser->current_token;
116+
advance(parser);
117+
return expr_ptr(id.literal, id.line, id.column);
118+
}
110119
if (match(parser, TOKEN_IDENT)) {
111120
return expr_ident(token.literal, token.line, token.column);
112121
}

src/value.c

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,10 @@ Value value_func(struct Func* func) {
2828
Value val; val.type = VAL_FUNC; val.as.func = func; return val;
2929
}
3030

31+
// Create a pointer value referring to a binding name
32+
// Pointer values removed: aliasing is handled at EnvEntry level
33+
34+
3135
static size_t compute_strides(const size_t* shape, size_t ndim, size_t* out_strides) {
3236
size_t len = 1;
3337
for (size_t i = ndim; i-- > 0;) {

src/value.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33

44
#include "ast.h"
55

6+
struct EnvEntry; // forward declare for pointer values
7+
68
typedef enum {
79
VAL_NULL,
810
VAL_INT,
@@ -69,6 +71,7 @@ Value value_int(int64_t v);
6971
Value value_flt(double v);
7072
Value value_str(const char* s);
7173
Value value_func(struct Func* func);
74+
// Note: pointer semantics are implemented at the EnvEntry (alias) level; no PTR Value type.
7275

7376
Value value_copy(Value v);
7477
Value value_deep_copy(Value v);

test2.pre

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,27 @@ MAP: m2 = <"a" = 101, "b" = 1010>
7777
ASSERT(EQ(KEYS(m2), ["a", "b"]))
7878
ASSERT(EQ(VALUES(m2), [101, 1010]))
7979

80+
# Pointer tests
81+
PRINT("Testing pointers...")
82+
INT: x = 1
83+
INT: y = @x
84+
ASSERT(EQ(y, 1))
85+
x = 10
86+
ASSERT(EQ(y, 10))
87+
y = 0
88+
ASSERT(EQ(x, 0))
89+
FREEZE(x)
90+
FREEZE(y)
91+
TRY{
92+
y = @x
93+
ASSERT(0)
94+
}CATCH{}
95+
THAW(x)
96+
THAW(y)
97+
DEL(x)
98+
DEL(y)
99+
PRINT("Pointers: PASS\n")
100+
80101
# MATCH tests
81102
MAP: tpl = <"foo" = 101>
82103
ASSERT(MATCH(m, tpl))

0 commit comments

Comments
 (0)