Skip to content

Commit dd50145

Browse files
gh-6: Add THR, type and statement.
1 parent 64b7879 commit dd50145

File tree

8 files changed

+229
-6
lines changed

8 files changed

+229
-6
lines changed

src/ast.c

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -264,6 +264,16 @@ Stmt* stmt_continue(int line, int column) {
264264
return stmt;
265265
}
266266

267+
Stmt* stmt_thr(char* name, Stmt* body, int line, int column) {
268+
Stmt* stmt = ast_alloc(sizeof(Stmt));
269+
stmt->type = STMT_THR;
270+
stmt->line = line;
271+
stmt->column = column;
272+
stmt->as.thr_stmt.name = name;
273+
stmt->as.thr_stmt.body = body;
274+
return stmt;
275+
}
276+
267277
Stmt* stmt_try(Stmt* try_block, char* catch_name, Stmt* catch_block, int line, int column) {
268278
Stmt* stmt = ast_alloc(sizeof(Stmt));
269279
stmt->type = STMT_TRY;
@@ -426,6 +436,10 @@ void free_stmt(Stmt* stmt) {
426436
case STMT_BREAK:
427437
free_expr(stmt->as.break_stmt.value);
428438
break;
439+
case STMT_THR:
440+
free(stmt->as.thr_stmt.name);
441+
free_stmt(stmt->as.thr_stmt.body);
442+
break;
429443
case STMT_TRY:
430444
free_stmt(stmt->as.try_stmt.try_block);
431445
free(stmt->as.try_stmt.catch_name);

src/ast.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ typedef enum {
99
TYPE_STR,
1010
TYPE_TNS,
1111
TYPE_FUNC,
12+
TYPE_THR,
1213
TYPE_UNKNOWN
1314
} DeclType;
1415

@@ -81,6 +82,7 @@ typedef enum {
8182
STMT_RETURN,
8283
STMT_BREAK,
8384
STMT_CONTINUE,
85+
STMT_THR,
8486
STMT_POP,
8587
STMT_TRY,
8688
STMT_GOTO,
@@ -126,6 +128,7 @@ struct Stmt {
126128
struct { char* name; ParamList params; DeclType return_type; Stmt* body; } func_stmt;
127129
struct { Expr* value; } return_stmt;
128130
struct { Expr* value; } break_stmt;
131+
struct { char* name; Stmt* body; } thr_stmt;
129132
struct { Stmt* try_block; char* catch_name; Stmt* catch_block; } try_stmt;
130133
struct { Expr* target; } goto_stmt;
131134
struct { char* name; } pop_stmt;
@@ -159,6 +162,7 @@ Stmt* stmt_return(Expr* value, int line, int column);
159162
Stmt* stmt_pop(char* name, int line, int column);
160163
Stmt* stmt_break(Expr* value, int line, int column);
161164
Stmt* stmt_continue(int line, int column);
165+
Stmt* stmt_thr(char* name, Stmt* body, int line, int column);
162166
Stmt* stmt_try(Stmt* try_block, char* catch_name, Stmt* catch_block, int line, int column);
163167
Stmt* stmt_goto(Expr* target, int line, int column);
164168
Stmt* stmt_gotopoint(Expr* target, int line, int column);

src/builtins.c

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2687,6 +2687,7 @@ static Value builtin_signature(Interpreter* interp, Value* args, int argc, Expr*
26872687
case TYPE_STR: tname = "STR"; break;
26882688
case TYPE_TNS: tname = "TNS"; break;
26892689
case TYPE_FUNC: tname = "FUNC"; break;
2690+
case TYPE_THR: tname = "THR"; break;
26902691
default: tname = "ANY"; break;
26912692
}
26922693
if (i > 0) strcat(buf, ", ");
@@ -2731,6 +2732,7 @@ static Value builtin_signature(Interpreter* interp, Value* args, int argc, Expr*
27312732
case TYPE_STR: rname = "STR"; break;
27322733
case TYPE_TNS: rname = "TNS"; break;
27332734
case TYPE_FUNC: rname = "FUNC"; break;
2735+
case TYPE_THR: rname = "THR"; break;
27342736
default: rname = "ANY"; break;
27352737
}
27362738
strcat(buf, rname);
@@ -2760,6 +2762,7 @@ static Value builtin_signature(Interpreter* interp, Value* args, int argc, Expr*
27602762
case TYPE_STR: tname = "STR"; break;
27612763
case TYPE_TNS: tname = "TNS"; break;
27622764
case TYPE_FUNC: tname = "FUNC"; break;
2765+
case TYPE_THR: tname = "THR"; break;
27632766
default: tname = "ANY"; break;
27642767
}
27652768
if (i > 0) strcat(buf, ", ");
@@ -2804,6 +2807,7 @@ static Value builtin_signature(Interpreter* interp, Value* args, int argc, Expr*
28042807
case TYPE_STR: rname = "STR"; break;
28052808
case TYPE_TNS: rname = "TNS"; break;
28062809
case TYPE_FUNC: rname = "FUNC"; break;
2810+
case TYPE_THR: rname = "THR"; break;
28072811
default: rname = "ANY"; break;
28082812
}
28092813
strcat(buf, rname);
@@ -2823,6 +2827,7 @@ static Value builtin_signature(Interpreter* interp, Value* args, int argc, Expr*
28232827
case TYPE_STR: tname = "STR"; break;
28242828
case TYPE_TNS: tname = "TNS"; break;
28252829
case TYPE_FUNC: tname = "FUNC"; break;
2830+
case TYPE_THR: tname = "THR"; break;
28262831
default: tname = value_type_name(entry->value); break;
28272832
}
28282833
size_t len = strlen(tname) + 2 + strlen(name) + 1;
@@ -3431,6 +3436,7 @@ static Value builtin_values(Interpreter* interp, Value* args, int argc, Expr** a
34313436
else if (vt == VAL_STR) dt = TYPE_STR;
34323437
else if (vt == VAL_TNS) dt = TYPE_TNS;
34333438
else if (vt == VAL_FUNC) dt = TYPE_FUNC;
3439+
else if (vt == VAL_THR) dt = TYPE_THR;
34343440
else if (vt == VAL_MAP) dt = TYPE_TNS; // no TYPE_MAP, use TNS as container type
34353441
else RUNTIME_ERROR(interp, "VALUES: unsupported value type", line, col);
34363442

@@ -3446,6 +3452,7 @@ static Value builtin_values(Interpreter* interp, Value* args, int argc, Expr** a
34463452
else if (cur == VAL_STR) cur_dt = TYPE_STR;
34473453
else if (cur == VAL_TNS) cur_dt = TYPE_TNS;
34483454
else if (cur == VAL_FUNC) cur_dt = TYPE_FUNC;
3455+
else if (cur == VAL_THR) cur_dt = TYPE_THR;
34493456
else if (cur == VAL_MAP) cur_dt = TYPE_TNS;
34503457
if (cur_dt != dt) {
34513458
for (size_t j = 0; j < i; j++) value_free(items[j]);

src/interpreter.c

Lines changed: 106 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,39 @@
1212
static ExecResult exec_stmt(Interpreter* interp, Stmt* stmt, Env* env, LabelMap* labels);
1313
static ExecResult exec_stmt_list(Interpreter* interp, StmtList* list, Env* env, LabelMap* labels);
1414

15+
// Thread worker for THR blocks
16+
#if PREFIX_HAS_THREADS
17+
typedef struct {
18+
Interpreter* interp;
19+
Env* env;
20+
Stmt* body;
21+
Value thr_val;
22+
} ThrStart;
23+
24+
static int thr_worker(void* arg) {
25+
ThrStart* start = (ThrStart*)arg;
26+
LabelMap labels = {0};
27+
ExecResult res = exec_stmt(start->interp, start->body, start->env, &labels);
28+
29+
// Clean up labels
30+
for (size_t i = 0; i < labels.count; i++) value_free(labels.items[i].key);
31+
free(labels.items);
32+
33+
if (res.status == EXEC_RETURN || res.status == EXEC_OK || res.status == EXEC_GOTO) {
34+
value_free(res.value);
35+
}
36+
if (res.status == EXEC_ERROR && res.error) {
37+
free(res.error);
38+
}
39+
40+
value_thr_set_finished(start->thr_val, 1);
41+
value_free(start->thr_val);
42+
free(start->interp);
43+
free(start);
44+
return 0;
45+
}
46+
#endif
47+
1548
// ============ Helper functions ============
1649

1750
static void* safe_malloc(size_t size) {
@@ -182,6 +215,8 @@ int value_truthiness(Value v) {
182215
return v.as.s != NULL && v.as.s[0] != '\0';
183216
case VAL_FUNC:
184217
return 1; // Functions are always truthy
218+
case VAL_THR:
219+
return value_thr_is_running(v);
185220
default:
186221
return 0;
187222
}
@@ -196,6 +231,7 @@ static DeclType value_type_to_decl(ValueType vt) {
196231
case VAL_STR: return TYPE_STR;
197232
case VAL_TNS: return TYPE_TNS;
198233
case VAL_FUNC: return TYPE_FUNC;
234+
case VAL_THR: return TYPE_THR;
199235
default: return TYPE_UNKNOWN;
200236
}
201237
}
@@ -207,6 +243,7 @@ static ValueType decl_type_to_value(DeclType dt) {
207243
case TYPE_STR: return VAL_STR;
208244
case TYPE_TNS: return VAL_TNS;
209245
case TYPE_FUNC: return VAL_FUNC;
246+
case TYPE_THR: return VAL_THR;
210247
default: return VAL_NULL;
211248
}
212249
}
@@ -841,12 +878,12 @@ Value eval_expr(Interpreter* interp, Expr* expr, Env* env) {
841878
if (kw_used) free(kw_used);
842879

843880
// Execute function body
844-
LabelMap labels = {0};
845-
ExecResult res = exec_stmt(interp, user_func->body, call_env, &labels);
881+
LabelMap local_labels = {0};
882+
ExecResult res = exec_stmt(interp, user_func->body, call_env, &local_labels);
846883

847884
// Clean up labels
848-
for (size_t i = 0; i < labels.count; i++) value_free(labels.items[i].key);
849-
free(labels.items);
885+
for (size_t i = 0; i < local_labels.count; i++) value_free(local_labels.items[i].key);
886+
free(local_labels.items);
850887

851888
env_free(call_env);
852889

@@ -881,11 +918,21 @@ Value eval_expr(Interpreter* interp, Expr* expr, Env* env) {
881918
case TYPE_INT: return value_int(0);
882919
case TYPE_FLT: return value_flt(0.0);
883920
case TYPE_STR: return value_str("");
921+
case TYPE_TNS:
922+
interp->error = strdup("TNS-returning function must return a value");
923+
interp->error_line = expr->line;
924+
interp->error_col = expr->column;
925+
return value_null();
884926
case TYPE_FUNC:
885927
interp->error = strdup("FUNC-returning function must return a value");
886928
interp->error_line = expr->line;
887929
interp->error_col = expr->column;
888930
return value_null();
931+
case TYPE_THR:
932+
interp->error = strdup("THR-returning function must return a value");
933+
interp->error_line = expr->line;
934+
interp->error_col = expr->column;
935+
return value_null();
889936
default:
890937
return value_null();
891938
}
@@ -1507,7 +1554,10 @@ static ExecResult exec_stmt(Interpreter* interp, Stmt* stmt, Env* env, LabelMap*
15071554
snprintf(buf, sizeof(buf), "Type mismatch: expected %s but got %s",
15081555
expected == TYPE_INT ? "INT" :
15091556
expected == TYPE_FLT ? "FLT" :
1510-
expected == TYPE_STR ? "STR" : "FUNC",
1557+
expected == TYPE_STR ? "STR" :
1558+
expected == TYPE_TNS ? "TNS" :
1559+
expected == TYPE_FUNC ? "FUNC" :
1560+
expected == TYPE_THR ? "THR" : "UNKNOWN",
15111561
value_type_name(v));
15121562
value_free(v);
15131563
return make_error(buf, stmt->line, stmt->column);
@@ -1705,6 +1755,57 @@ static ExecResult exec_stmt(Interpreter* interp, Stmt* stmt, Env* env, LabelMap*
17051755
return res;
17061756
}
17071757

1758+
case STMT_THR: {
1759+
Value thr_val = value_thr_new();
1760+
Value thr_for_worker = value_copy(thr_val);
1761+
if (!env_assign(env, stmt->as.thr_stmt.name, thr_val, TYPE_THR, true)) {
1762+
value_free(thr_for_worker);
1763+
value_free(thr_val);
1764+
return make_error("Cannot assign to THR identifier", stmt->line, stmt->column);
1765+
}
1766+
value_free(thr_val);
1767+
1768+
#if PREFIX_HAS_THREADS
1769+
ThrStart* start = safe_malloc(sizeof(ThrStart));
1770+
Interpreter* thr_interp = safe_malloc(sizeof(Interpreter));
1771+
*thr_interp = (Interpreter){0};
1772+
thr_interp->global_env = interp->global_env;
1773+
thr_interp->functions = interp->functions;
1774+
thr_interp->loop_depth = 0;
1775+
thr_interp->error = NULL;
1776+
thr_interp->error_line = 0;
1777+
thr_interp->error_col = 0;
1778+
thr_interp->in_try_block = false;
1779+
thr_interp->modules = interp->modules;
1780+
thr_interp->shushed = interp->shushed;
1781+
1782+
start->interp = thr_interp;
1783+
start->env = env;
1784+
start->body = stmt->as.thr_stmt.body;
1785+
start->thr_val = thr_for_worker;
1786+
1787+
if (thrd_create(&thr_for_worker.as.thr->thread, thr_worker, start) != thrd_success) {
1788+
value_thr_set_finished(thr_for_worker, 1);
1789+
value_free(thr_for_worker);
1790+
free(thr_interp);
1791+
free(start);
1792+
return make_error("Failed to start THR", stmt->line, stmt->column);
1793+
}
1794+
#else
1795+
LabelMap local_labels = {0};
1796+
ExecResult res = exec_stmt(interp, stmt->as.thr_stmt.body, env, &local_labels);
1797+
for (size_t i = 0; i < local_labels.count; i++) value_free(local_labels.items[i].key);
1798+
free(local_labels.items);
1799+
if (res.status == EXEC_ERROR && res.error) free(res.error);
1800+
if (res.status == EXEC_RETURN || res.status == EXEC_OK || res.status == EXEC_GOTO) {
1801+
value_free(res.value);
1802+
}
1803+
value_thr_set_finished(thr_for_worker, 1);
1804+
value_free(thr_for_worker);
1805+
#endif
1806+
return make_ok(value_null());
1807+
}
1808+
17081809
case STMT_IF: {
17091810
Value cond = eval_expr(interp, stmt->as.if_stmt.condition, env);
17101811
if (interp->error) {

src/parser.c

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,7 @@ static DeclType parse_type_name(const char* name) {
6565
if (strcmp(name, "FLT") == 0) return TYPE_FLT;
6666
if (strcmp(name, "STR") == 0) return TYPE_STR;
6767
if (strcmp(name, "FUNC") == 0) return TYPE_FUNC;
68+
if (strcmp(name, "THR") == 0) return TYPE_THR;
6869
if (strcmp(name, "TNS") == 0) return TYPE_TNS;
6970
return TYPE_UNKNOWN;
7071
}
@@ -471,6 +472,20 @@ static Stmt* parse_statement(Parser* parser) {
471472
consume(parser, TOKEN_RPAREN, "Expected ')' after BREAK value");
472473
return stmt_break(expr, tok.line, tok.column);
473474
}
475+
case TOKEN_THR: {
476+
Token tok = parser->current_token;
477+
advance(parser);
478+
consume(parser, TOKEN_LPAREN, "Expected '(' after THR");
479+
if (parser->current_token.type != TOKEN_IDENT) {
480+
report_error(parser, "Expected identifier after THR(");
481+
return NULL;
482+
}
483+
char* name = parser->current_token.literal;
484+
advance(parser);
485+
consume(parser, TOKEN_RPAREN, "Expected ')' after THR identifier");
486+
Stmt* body = parse_block(parser);
487+
return stmt_thr(name, body, tok.line, tok.column);
488+
}
474489
case TOKEN_CONTINUE: {
475490
Token tok = parser->current_token;
476491
advance(parser);

src/value.c

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

31+
Value value_thr_new(void) {
32+
Thr* t = malloc(sizeof(Thr));
33+
if (!t) { fprintf(stderr, "Out of memory\n"); exit(1); }
34+
t->finished = 0;
35+
t->paused = 0;
36+
t->refcount = 1;
37+
#if PREFIX_HAS_THREADS
38+
memset(&t->thread, 0, sizeof(thrd_t));
39+
#endif
40+
Value v; v.type = VAL_THR; v.as.thr = t; return v;
41+
}
42+
43+
int value_thr_is_running(Value v) {
44+
if (v.type != VAL_THR || !v.as.thr) return 0;
45+
return v.as.thr->finished ? 0 : 1;
46+
}
47+
48+
void value_thr_set_finished(Value v, int finished) {
49+
if (v.type != VAL_THR || !v.as.thr) return;
50+
v.as.thr->finished = finished ? 1 : 0;
51+
}
52+
3153
// Create a pointer value referring to a binding name
3254
// Pointer values removed: aliasing is handled at EnvEntry level
3355

@@ -321,6 +343,10 @@ Value value_copy(Value v) {
321343
Map* m = v.as.map;
322344
m->refcount++;
323345
out.as.map = m;
346+
} else if (v.type == VAL_THR && v.as.thr) {
347+
Thr* th = v.as.thr;
348+
th->refcount++;
349+
out.as.thr = th;
324350
}
325351
return out;
326352
}
@@ -357,6 +383,11 @@ Value value_deep_copy(Value v) {
357383
}
358384
m2->refcount = 1;
359385
out.as.map = m2;
386+
} else if (v.type == VAL_THR && v.as.thr) {
387+
// Threads are not deep-copyable; preserve handle semantics (share)
388+
Thr* th = v.as.thr;
389+
th->refcount++;
390+
out.as.thr = th;
360391
}
361392
return out;
362393
}
@@ -387,6 +418,11 @@ void value_free(Value v) {
387418
}
388419
free(m);
389420
}
421+
} else if (v.type == VAL_THR && v.as.thr) {
422+
Thr* th = v.as.thr;
423+
if (--th->refcount <= 0) {
424+
free(th);
425+
}
390426
}
391427
}
392428

@@ -398,6 +434,7 @@ const char* value_type_name(Value v) {
398434
case VAL_TNS: return "TNS";
399435
case VAL_STR: return "STR";
400436
case VAL_FUNC: return "FUNC";
437+
case VAL_THR: return "THR";
401438
default: return "NULL";
402439
}
403440
}

0 commit comments

Comments
 (0)