Skip to content

Commit 56e8400

Browse files
gh-5: Add TNS literals.
1 parent a16a97d commit 56e8400

10 files changed

Lines changed: 1171 additions & 414 deletions

File tree

build.ps1

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ Push-Location $buildDir
3636
try {
3737
# Build in temp dir; cl will place outputs in the current dir
3838
$fe = "/Fe:prefix.exe"
39-
$args = @("/std:c17", "/Gd", "/O2", "/W4", "/WX", "/nologo", $fe)
39+
$args = @("/std:c17", "/Gd", "/O2", "/Gy", "/GF", "/GL", "/W4", "/WX", "/MP", "/nologo", $fe)
4040
$args += $cFiles
4141

4242
Write-Host "Invoking: cl.exe $($args -join ' ')"
@@ -71,4 +71,4 @@ Write-Host "Cleaning up temp build dir: $buildDir"
7171
Remove-Item -Recurse -Force $buildDir -ErrorAction SilentlyContinue
7272

7373
Write-Host "Build succeeded and exe copied to: $(Join-Path $script 'prefix.exe')"
74-
exit 0
74+
exit 0

src/ast.c

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,47 @@ Expr* expr_call(Expr* callee, int line, int column) {
5858
return expr;
5959
}
6060

61+
Expr* expr_tns(int line, int column) {
62+
Expr* expr = ast_alloc(sizeof(Expr));
63+
expr->type = EXPR_TNS;
64+
expr->line = line;
65+
expr->column = column;
66+
expr->as.tns_items.items = NULL;
67+
expr->as.tns_items.count = 0;
68+
expr->as.tns_items.capacity = 0;
69+
return expr;
70+
}
71+
72+
Expr* expr_index(Expr* target, int line, int column) {
73+
Expr* expr = ast_alloc(sizeof(Expr));
74+
expr->type = EXPR_INDEX;
75+
expr->line = line;
76+
expr->column = column;
77+
expr->as.index.target = target;
78+
expr->as.index.indices.items = NULL;
79+
expr->as.index.indices.count = 0;
80+
expr->as.index.indices.capacity = 0;
81+
return expr;
82+
}
83+
84+
Expr* expr_range(Expr* start, Expr* end, int line, int column) {
85+
Expr* expr = ast_alloc(sizeof(Expr));
86+
expr->type = EXPR_RANGE;
87+
expr->line = line;
88+
expr->column = column;
89+
expr->as.range.start = start;
90+
expr->as.range.end = end;
91+
return expr;
92+
}
93+
94+
Expr* expr_wildcard(int line, int column) {
95+
Expr* expr = ast_alloc(sizeof(Expr));
96+
expr->type = EXPR_WILDCARD;
97+
expr->line = line;
98+
expr->column = column;
99+
return expr;
100+
}
101+
61102
void expr_list_add(ExprList* list, Expr* expr) {
62103
if (list->count + 1 > list->capacity) {
63104
size_t new_cap = list->capacity == 0 ? 4 : list->capacity * 2;
@@ -253,6 +294,17 @@ void free_expr(Expr* expr) {
253294
case EXPR_STR:
254295
free(expr->as.str_value);
255296
break;
297+
case EXPR_TNS:
298+
free_expr_list(&expr->as.tns_items);
299+
break;
300+
case EXPR_INDEX:
301+
free_expr(expr->as.index.target);
302+
free_expr_list(&expr->as.index.indices);
303+
break;
304+
case EXPR_RANGE:
305+
free_expr(expr->as.range.start);
306+
free_expr(expr->as.range.end);
307+
break;
256308
case EXPR_IDENT:
257309
free(expr->as.ident);
258310
break;

src/ast.h

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ typedef enum {
77
TYPE_INT,
88
TYPE_FLT,
99
TYPE_STR,
10+
TYPE_TNS,
1011
TYPE_FUNC,
1112
TYPE_UNKNOWN
1213
} DeclType;
@@ -19,7 +20,11 @@ typedef enum {
1920
EXPR_FLT,
2021
EXPR_STR,
2122
EXPR_IDENT,
22-
EXPR_CALL
23+
EXPR_CALL,
24+
EXPR_TNS,
25+
EXPR_INDEX,
26+
EXPR_RANGE,
27+
EXPR_WILDCARD
2328
} ExprType;
2429

2530
typedef struct {
@@ -41,6 +46,15 @@ struct Expr {
4146
Expr* callee;
4247
ExprList args;
4348
} call;
49+
struct {
50+
Expr* target;
51+
ExprList indices;
52+
} index;
53+
struct {
54+
Expr* start;
55+
Expr* end;
56+
} range;
57+
ExprList tns_items;
4458
} as;
4559
};
4660

@@ -111,6 +125,10 @@ Expr* expr_flt(double value, int line, int column);
111125
Expr* expr_str(char* value, int line, int column);
112126
Expr* expr_ident(char* name, int line, int column);
113127
Expr* expr_call(Expr* callee, int line, int column);
128+
Expr* expr_tns(int line, int column);
129+
Expr* expr_index(Expr* target, int line, int column);
130+
Expr* expr_range(Expr* start, Expr* end, int line, int column);
131+
Expr* expr_wildcard(int line, int column);
114132
void expr_list_add(ExprList* list, Expr* expr);
115133

116134
Stmt* stmt_block(int line, int column);

src/builtins.c

Lines changed: 142 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -612,25 +612,48 @@ static Value builtin_lcm(Interpreter* interp, Value* args, int argc, Expr** arg_
612612

613613
// ============ Comparison operators ============
614614

615-
static Value builtin_eq(Interpreter* interp, Value* args, int argc, Expr** arg_nodes, Env* env, int line, int col) {
616-
(void)arg_nodes; (void)env; (void)interp;
617-
618-
if (args[0].type != args[1].type) {
619-
return value_int(0);
620-
}
621-
622-
switch (args[0].type) {
615+
// Recursive deep equality helper for Values (returns 1 if equal, 0 otherwise)
616+
static int value_deep_eq(Value a, Value b) {
617+
if (a.type != b.type) return 0;
618+
switch (a.type) {
623619
case VAL_INT:
624-
return value_int(args[0].as.i == args[1].as.i ? 1 : 0);
620+
return a.as.i == b.as.i ? 1 : 0;
625621
case VAL_FLT:
626-
return value_int(args[0].as.f == args[1].as.f ? 1 : 0);
622+
return a.as.f == b.as.f ? 1 : 0;
627623
case VAL_STR:
628-
return value_int(strcmp(args[0].as.s, args[1].as.s) == 0 ? 1 : 0);
624+
if (a.as.s == NULL || b.as.s == NULL) return (a.as.s == b.as.s) ? 1 : 0;
625+
return strcmp(a.as.s, b.as.s) == 0 ? 1 : 0;
629626
case VAL_FUNC:
630-
return value_int(args[0].as.func == args[1].as.func ? 1 : 0);
627+
return a.as.func == b.as.func ? 1 : 0;
628+
case VAL_TNS: {
629+
Tensor* ta = a.as.tns;
630+
Tensor* tb = b.as.tns;
631+
if (ta == NULL || tb == NULL) return (ta == tb) ? 1 : 0;
632+
if (ta->elem_type != tb->elem_type) return 0;
633+
if (ta->ndim != tb->ndim) return 0;
634+
for (size_t i = 0; i < ta->ndim; i++) {
635+
if (ta->shape[i] != tb->shape[i]) return 0;
636+
}
637+
if (ta->length != tb->length) return 0;
638+
for (size_t i = 0; i < ta->length; i++) {
639+
if (!value_deep_eq(ta->data[i], tb->data[i])) return 0;
640+
}
641+
return 1;
642+
}
631643
default:
632-
return value_int(0);
644+
return 0;
645+
}
646+
}
647+
648+
static Value builtin_eq(Interpreter* interp, Value* args, int argc, Expr** arg_nodes, Env* env, int line, int col) {
649+
(void)arg_nodes; (void)env; (void)interp;
650+
651+
// If types differ, not equal
652+
if (args[0].type != args[1].type) {
653+
return value_int(0);
633654
}
655+
656+
return value_int(value_deep_eq(args[0], args[1]) ? 1 : 0);
634657
}
635658

636659
static Value builtin_gt(Interpreter* interp, Value* args, int argc, Expr** arg_nodes, Env* env, int line, int col) {
@@ -956,39 +979,109 @@ static Value builtin_join(Interpreter* interp, Value* args, int argc, Expr** arg
956979

957980
static Value builtin_split(Interpreter* interp, Value* args, int argc, Expr** arg_nodes, Env* env, int line, int col) {
958981
(void)arg_nodes; (void)env;
959-
// SPLIT returns a tensor in Python but for now we can't return tensors
960-
// For Stage 3, we'll skip tensor-returning operations
961-
RUNTIME_ERROR(interp, "SPLIT requires TNS support (Stage 4)", line, col);
982+
// SPLIT(str, sep?) -> 1-D TNS of STR
983+
EXPECT_STR(args[0], "SPLIT", interp, line, col);
984+
const char* sep = NULL;
985+
if (argc >= 2) {
986+
EXPECT_STR(args[1], "SPLIT", interp, line, col);
987+
sep = args[1].as.s;
988+
}
989+
const char* s = args[0].as.s;
990+
// simple separator: if sep==NULL split on whitespace, else split on sep exactly
991+
char* copy = strdup(s);
992+
char* saveptr = NULL;
993+
char* token;
994+
size_t cap = 8;
995+
size_t count = 0;
996+
Value* items = malloc(sizeof(Value) * cap);
997+
if (!items) { free(copy); RUNTIME_ERROR(interp, "Out of memory", line, col); }
998+
if (sep == NULL) {
999+
// whitespace split
1000+
token = strtok_s(copy, " \t\r\n", &saveptr);
1001+
if (token) {
1002+
if (count + 1 > cap) { cap *= 2; items = realloc(items, sizeof(Value) * cap); }
1003+
items[count++] = value_str(token);
1004+
}
1005+
} else {
1006+
// split on sep: iterate
1007+
size_t seplen = strlen(sep);
1008+
char* cur = copy;
1009+
char* found;
1010+
while ((found = strstr(cur, sep)) != NULL) {
1011+
size_t len = (size_t)(found - cur);
1012+
char* piece = malloc(len + 1);
1013+
memcpy(piece, cur, len);
1014+
piece[len] = '\0';
1015+
if (count + 1 > cap) { cap *= 2; items = realloc(items, sizeof(Value) * cap); }
1016+
items[count++] = value_str(piece);
1017+
free(piece);
1018+
cur = found + seplen;
1019+
}
1020+
// last piece
1021+
if (*cur != '\0') {
1022+
if (count + 1 > cap) { cap *= 2; items = realloc(items, sizeof(Value) * cap); }
1023+
items[count++] = value_str(cur);
1024+
}
1025+
free(copy);
1026+
if (count == 0) {
1027+
free(items);
1028+
return value_tns_new(TYPE_STR, 1, (const size_t[]){0});
1029+
}
1030+
size_t shape[1] = { count };
1031+
Value out = value_tns_from_values(TYPE_STR, 1, shape, items, count);
1032+
for (size_t i = 0; i < count; i++) value_free(items[i]);
1033+
free(items);
1034+
return out;
1035+
}
1036+
1037+
while ((token = strtok_s(NULL, " \t\r\n", &saveptr)) != NULL) {
1038+
if (count + 1 > cap) { cap *= 2; items = realloc(items, sizeof(Value) * cap); }
1039+
items[count++] = value_str(token);
1040+
}
1041+
free(copy);
1042+
if (count == 0) {
1043+
free(items);
1044+
return value_tns_new(TYPE_STR, 1, (const size_t[]){0});
1045+
}
1046+
size_t shape[1] = { count };
1047+
Value out = value_tns_from_values(TYPE_STR, 1, shape, items, count);
1048+
for (size_t i = 0; i < count; i++) value_free(items[i]);
1049+
free(items);
1050+
return out;
9621051
}
9631052

9641053
static Value builtin_slice(Interpreter* interp, Value* args, int argc, Expr** arg_nodes, Env* env, int line, int col) {
9651054
(void)arg_nodes; (void)env;
966-
EXPECT_STR(args[0], "SLICE", interp, line, col);
967-
EXPECT_INT(args[1], "SLICE", interp, line, col);
968-
EXPECT_INT(args[2], "SLICE", interp, line, col);
969-
970-
const char* s = args[0].as.s;
971-
size_t len = strlen(s);
972-
int64_t start = args[1].as.i;
973-
int64_t end = args[2].as.i;
974-
975-
// Convert 1-based to 0-based, handle negative indices
976-
if (start < 0) start = (int64_t)len + start + 1;
977-
if (end < 0) end = (int64_t)len + end + 1;
978-
start--; // Convert to 0-based
979-
980-
if (start < 0) start = 0;
981-
if (end > (int64_t)len) end = (int64_t)len;
982-
if (start >= end) return value_str("");
983-
984-
size_t result_len = (size_t)(end - start);
985-
char* result = malloc(result_len + 1);
986-
memcpy(result, s + start, result_len);
987-
result[result_len] = '\0';
988-
989-
Value v = value_str(result);
990-
free(result);
991-
return v;
1055+
// SLICE can operate on STR or TNS for now. Syntax: SLICE(target, start, end)
1056+
if (args[0].type == VAL_STR) {
1057+
EXPECT_INT(args[1], "SLICE", interp, line, col);
1058+
EXPECT_INT(args[2], "SLICE", interp, line, col);
1059+
const char* s = args[0].as.s;
1060+
size_t len = strlen(s);
1061+
int64_t start = args[1].as.i;
1062+
int64_t end = args[2].as.i;
1063+
if (start < 0) start = (int64_t)len + start + 1;
1064+
if (end < 0) end = (int64_t)len + end + 1;
1065+
start--;
1066+
if (start < 0) start = 0;
1067+
if (end > (int64_t)len) end = (int64_t)len;
1068+
if (start >= end) return value_str("");
1069+
size_t result_len = (size_t)(end - start);
1070+
char* result = malloc(result_len + 1);
1071+
memcpy(result, s + start, result_len);
1072+
result[result_len] = '\0';
1073+
Value v = value_str(result);
1074+
free(result);
1075+
return v;
1076+
} else if (args[0].type == VAL_TNS) {
1077+
// slice along first axis using 1-based inclusive indices
1078+
EXPECT_INT(args[1], "SLICE", interp, line, col);
1079+
EXPECT_INT(args[2], "SLICE", interp, line, col);
1080+
int64_t starts[1] = { args[1].as.i };
1081+
int64_t ends[1] = { args[2].as.i };
1082+
return value_tns_slice(args[0], starts, ends, 1);
1083+
}
1084+
RUNTIME_ERROR(interp, "SLICE expects STR or TNS", line, col);
9921085
}
9931086

9941087
static Value builtin_replace(Interpreter* interp, Value* args, int argc, Expr** arg_nodes, Env* env, int line, int col) {
@@ -1496,7 +1589,13 @@ static Value builtin_len(Interpreter* interp, Value* args, int argc, Expr** arg_
14961589
if (args[0].type == VAL_STR) {
14971590
return value_int((int64_t)strlen(args[0].as.s));
14981591
}
1499-
RUNTIME_ERROR(interp, "LEN requires TNS support for non-string arguments (Stage 4)", line, col);
1592+
if (args[0].type == VAL_TNS) {
1593+
Tensor* t = args[0].as.tns;
1594+
if (!t) return value_int(0);
1595+
if (t->ndim == 0) return value_int(0);
1596+
return value_int((int64_t)t->shape[0]);
1597+
}
1598+
RUNTIME_ERROR(interp, "LEN expects STR or TNS", line, col);
15001599
}
15011600

15021601
// Main, OS

0 commit comments

Comments
 (0)