aboutsummaryrefslogtreecommitdiffstats
path: root/compiler
diff options
context:
space:
mode:
authoreunhhu <[email protected]>2026-10-04 14:43:11 +0100
committereunhhu <[email protected]>2026-10-04 14:43:11 +0100
commitda7361aae4e896edaad37dbcf404c5005a7f8596 (patch)
treeb01bde38a23df0f92093bd19dfc9ec9e39424b62 /compiler
parent7340ba9af757b6f135dff9c51a193ba527f061b3 (diff)
downloadaup-da7361aae4e896edaad37dbcf404c5005a7f8596.tar.gz
aup-da7361aae4e896edaad37dbcf404c5005a7f8596.zip
Give a view a body that computes and returns its value (Pure 018)
Diffstat (limited to 'compiler')
-rw-r--r--compiler/compile.c127
1 files changed, 86 insertions, 41 deletions
diff --git a/compiler/compile.c b/compiler/compile.c
index 25a761b..69bfdb5 100644
--- a/compiler/compile.c
+++ b/compiler/compile.c
@@ -80,7 +80,7 @@ typedef struct Expr {
} Expr;
typedef enum { S_ASSIGN, S_IF, S_REPLACE, S_REPLY, S_REQUIRE, S_INSERT, S_REMOVE, S_APPEND, S_ADD, S_SET, S_SEND, S_FOR,
- S_LET, S_COUNT, S_BREAK } StmtKind;
+ S_LET, S_COUNT, S_BREAK, S_RETURN } StmtKind;
typedef struct Stmt {
StmtKind kind;
int line, column;
@@ -180,6 +180,7 @@ typedef struct {
/* Views in order; a table (no value) is followed by its columns. */
char view_names[VIEWS][NAME], view_row[VIEWS][NAME], view_list[VIEWS][NAME];
Expr *view_value[VIEWS];
+ Stmt *view_body[VIEWS]; /* a view that computes: its statements, return last (view_value) */
Expr *view_filter[VIEWS]; /* a table's where condition, or NULL */
unsigned char view_byte[VIEWS]; /* a filtered table's list + 1 */
int view_table[VIEWS]; /* a column's table + 1, or 0 */
@@ -207,6 +208,9 @@ typedef struct {
struct { char name[NAME]; int list, item; Scope *scope; } loops[NESTING];
unsigned loop_count;
unsigned nested; /* blocks within the handler's own (an if's, a for's) */
+ bool handling; /* statements of a handler (else of a view's body: they change no state) */
+ Kind returned; /* the value a view's body returned, once it has */
+ bool has_returned;
/* Libraries: module 0 is the main source; each module lists the libraries it uses. */
struct { char name[NAME], path[AUP_PATH]; bool uses[MODULES], parsing; } modules[MODULES];
unsigned module_count;
@@ -232,7 +236,7 @@ static const char *const reserved[] = {"state", "input", "view", "on", "if", "el
"dt", "min", "max", "clamp", "message", "by", "bytes", "u32",
"reply", "require", "record", "each", "fn", "use", "now", "every",
"floor", "output", "send", "for", "let", "true", "false",
- "transient", "break"};
+ "transient", "break", "return"};
/* An error in the module being parsed or inlined; inside a fn it names the call. */
static _Noreturn void fail_at(Compiler *c, int line, int column, const char *format, ...) {
@@ -1026,6 +1030,11 @@ static Stmt *statement(Compiler *c, unsigned depth) {
s->kind = S_BREAK;
return s;
}
+ if (!strcmp(token.text, "return")) { /* a view's body's last line: its value */
+ s->kind = S_RETURN;
+ s->value = expression(c, 0, false);
+ return s;
+ }
if (!strcmp(token.text, "for")) {
/* for item in list [where condition] { ... }: the block runs once for every item the list has */
s->kind = S_FOR;
@@ -1807,10 +1816,19 @@ static void declaration(Compiler *c) {
if (c->views == VIEWS) fail_at(c, name.line, name.column, "at most 64 views are allowed");
unsigned index = c->views++;
if (is(c, "(")) view_arguments(c, index, &name);
- expect(c, "=", "'='");
memcpy(c->view_names[index], name.text, NAME);
- if (!word(c, "each")) c->view_value[index] = expression(c, 0, false);
- else table_declaration(c, index);
+ if (is(c, "{")) { /* view name(arguments) { statements ... return value }: a view that computes */
+ Stmt *body = block(c, 0), *last = body;
+ while (last && last->next) last = last->next;
+ if (!last || last->kind != S_RETURN)
+ fail_at(c, name.line, name.column, "a view's body ends with return and its value, as in return total");
+ c->view_body[index] = body;
+ c->view_value[index] = last->value;
+ } else {
+ expect(c, "=", "'='");
+ if (!word(c, "each")) c->view_value[index] = expression(c, 0, false);
+ else table_declaration(c, index);
+ }
} else if (!strcmp(kind.text, "on")) {
if (c->handler_count == HANDLERS) fail_at(c, kind.line, kind.column, "at most 64 handlers are allowed");
Token event = expect_name(c, "frame or an input");
@@ -2635,7 +2653,7 @@ static Kind value(Compiler *c, const Expr *e, bool handler, int height) {
/* Bytes, or text sent as its UTF-8 bytes. */
static void bytes_value(Compiler *c, const Expr *e, int height, const char *what) {
- Kind kind = value(c, e, true, height);
+ Kind kind = value(c, e, c->handling, height);
if (kind.type == TEXT) emit(c, e->line, e->column, BYTES_FROM, 0, 0);
else if (kind.type != BYTES) fail_at(c, e->line, e->column, "%s takes bytes or text, not %s", what, type_name(kind.type));
}
@@ -2737,7 +2755,7 @@ static void record_fields(Compiler *c, const Expr *e, int list, int height) {
record_values(c, e, list, values);
for (unsigned k = 0; k < r->fields; ++k) {
int at = height + (int)k;
- if (values[k]) expect_kind(c, values[k], wanted(c, values[k], true, at, field_kind(r, k)), field_kind(r, k));
+ if (values[k]) expect_kind(c, values[k], wanted(c, values[k], c->handling, at, field_kind(r, k)), field_kind(r, k));
else if (at + 1 > STACK) fail_at(c, e->line, e->column, "expression needs more than 64 stack values");
else if (r->field_kinds[k] == TEXT) emit(c, e->line, e->column, TEXT_CONST, r->field_constant[k], 0);
else if (r->field_kinds[k] == I64) emit_integer(c, e->line, e->column, r->field_integer[k]);
@@ -2774,6 +2792,12 @@ static void send_values(Compiler *c, const Input *out, const Expr *group, const
}
}
+/* A view's body changes only its lets and its own lists (a let's list): its states are a snapshot's. */
+static void view_changes(Compiler *c, const Stmt *s, int list, const char *name) {
+ if (!c->handling && (list < 0 || !c->state_event[list]))
+ fail_at(c, s->line, s->column, "a view's body changes only its lets and its own lists, not '%s'", name);
+}
+
static void statements(Compiler *c, const Stmt *s) {
unsigned scope = c->local_count; /* a block's lets end with it */
for (; s; s = s->next) {
@@ -2800,7 +2824,7 @@ static void statements(Compiler *c, const Stmt *s) {
break;
}
if (c->local_slots == LOCALS) fail_at(c, s->line, s->column, "at most %d lets are allowed in a handler", LOCALS);
- Kind held = value(c, s->value, true, 0);
+ Kind held = value(c, s->value, c->handling, 0);
if (held.type != NUM && held.type != BOOL && held.type != I64 && held.type != INDEX)
fail_at(c, s->value->line, s->value->column, "a let holds a number, a bool, an i64 or an index, not %s",
type_name(held.type));
@@ -2818,7 +2842,7 @@ static void statements(Compiler *c, const Stmt *s) {
if (local >= 0 && c->locals[local].counter)
fail_at(c, s->line, s->column, "'%s' is what this for counts; it is not assigned", s->name);
if (local >= 0) { /* a let takes a new value of its kind */
- Kind kind = wanted(c, s->value, true, 0, c->locals[local].kind.type);
+ Kind kind = wanted(c, s->value, c->handling, 0, c->locals[local].kind.type);
if (kind.type != c->locals[local].kind.type || (kind.type == INDEX && kind.owner != c->locals[local].kind.owner))
fail_at(c, s->value->line, s->value->column, "'%s' holds %s", s->name,
c->locals[local].kind.type == INDEX ? "an index of its list" : type_name(c->locals[local].kind.type));
@@ -2831,29 +2855,31 @@ static void statements(Compiler *c, const Stmt *s) {
if (kind) fail_at(c, s->line, s->column, "cannot assign to %s '%s'", kind, s->name);
fail_at(c, s->line, s->column, "unknown state '%s'", s->name);
}
+ view_changes(c, s, -1, s->name);
if (c->state_types[index] == STATE_I64) {
- integer(c, s->value, true, 0);
+ integer(c, s->value, c->handling, 0);
emit(c, s->line, s->column, STORE, (uint32_t)index, 0);
} else if (c->state_types[index] == STATE_NUM || c->state_types[index] == STATE_BOOL) {
- expect_kind(c, s->value, value(c, s->value, true, 0), c->state_types[index] == STATE_BOOL ? BOOL : NUM);
+ expect_kind(c, s->value, value(c, s->value, c->handling, 0), c->state_types[index] == STATE_BOOL ? BOOL : NUM);
emit(c, s->line, s->column, STORE, (uint32_t)index, 0);
} else if (c->state_types[index] == STATE_LIST) {
fail_at(c, s->line, s->column, "a list changes by append, insert, remove and %s[index].field = value", s->name);
} else if (c->state_types[index] == STATE_INDEX) {
- index_of(c, s->value, true, 0, cursor_owner(c, index));
+ index_of(c, s->value, c->handling, 0, cursor_owner(c, index));
emit(c, s->line, s->column, INDEX_STORE, (uint32_t)index, 0);
} else if (c->state_types[index] == STATE_TEXT) {
- expect_kind(c, s->value, value(c, s->value, true, 0), TEXT);
+ expect_kind(c, s->value, value(c, s->value, c->handling, 0), TEXT);
emit(c, s->line, s->column, TEXT_SET, (uint32_t)index, 0);
} else {
int owner = cursor_owner(c, index);
- cursor_of(c, s->value, true, 0, owner, c->state_names[owner]);
+ cursor_of(c, s->value, c->handling, 0, owner, c->state_names[owner]);
emit(c, s->line, s->column, CUR_STORE, (uint32_t)index, 0);
}
break;
}
case S_INSERT: case S_REMOVE: {
int list = state_index(c, s->name);
+ view_changes(c, s, list >= 0 && c->state_types[list] == STATE_LIST ? list : -1, s->name);
if (list >= 0 && c->state_types[list] == STATE_LIST) {
if (s->last) fail_at(c, s->last->line, s->last->column, "a list removes one item, as in %s.remove(index)", s->name);
if (s->kind == S_INSERT && walked(c, list))
@@ -2863,27 +2889,28 @@ static void statements(Compiler *c, const Stmt *s) {
if (s->kind == S_INSERT && keyed_by(c, list))
fail_at(c, s->line, s->column, "'%s' is keyed by %s: %s.add(%s { ... }) places an item by its key", s->name,
keyed_by(c, list), s->name, record_shown(c, c->module, record_of(c, list)));
- if (s->kind == S_REMOVE) item_of(c, s->first, true, 0, list);
- else index_of(c, s->first, true, 0, list);
+ if (s->kind == S_REMOVE) item_of(c, s->first, c->handling, 0, list);
+ else index_of(c, s->first, c->handling, 0, list);
if (s->kind == S_INSERT) record_fields(c, s->value, list, 1);
emit(c, s->line, s->column, s->kind == S_INSERT ? LIST_INSERT : LIST_REMOVE, (uint32_t)list, 0);
break;
}
int text = text_state(c, s->name, s->line, s->column);
- cursor_of(c, s->first, true, 0, text, s->name);
+ cursor_of(c, s->first, c->handling, 0, text, s->name);
if (s->kind == S_INSERT) {
- expect_kind(c, s->value, value(c, s->value, true, 1), TEXT);
+ expect_kind(c, s->value, value(c, s->value, c->handling, 1), TEXT);
emit(c, s->line, s->column, TEXT_INSERT, (uint32_t)text, 0);
break;
}
if (!s->last) fail_at(c, s->line, s->column, "a text removes a range, as in %s.remove(first .. last)", s->name);
- cursor_of(c, s->last, true, 1, text, s->name);
+ cursor_of(c, s->last, c->handling, 1, text, s->name);
emit(c, s->line, s->column, TEXT_REMOVE, (uint32_t)text, 0);
break;
}
case S_APPEND: case S_ADD: {
/* A keyed list places an added item by its key (it fails if the key is there); others append. */
int list = list_state(c, s->name, s->line, s->column, NULL);
+ view_changes(c, s, list, s->name);
if (walked(c, list))
fail_at(c, s->line, s->column, "a for does not add to the list it walks; add to another list, or after the for");
const char *key = keyed_by(c, list), *record = record_shown(c, c->module, record_of(c, list));
@@ -2900,6 +2927,7 @@ static void statements(Compiler *c, const Stmt *s) {
case S_SET: {
int list = *s->name ? list_state(c, s->name, s->line, s->column, s->first) : -1;
if (!s->member[0]) { /* tasks[at] = Task { ... }: each field set, the key the same */
+ view_changes(c, s, list, s->name);
const Record *r = record_of(c, list);
const Expr *values[FIELDS_PER_RECORD];
record_values(c, s->value, list, values);
@@ -2917,40 +2945,43 @@ static void statements(Compiler *c, const Stmt *s) {
same->args[0] = item;
same->args[1] = (Expr *)values[key];
same->count = 2;
- truth(c, same, true, 0, "require");
+ truth(c, same, c->handling, 0, "require");
emit(c, s->line, s->column, REQUIRE, 0, 0);
}
for (unsigned k = 0; k < r->fields; ++k) {
if ((int)k == key) continue;
- item_of(c, s->first, true, 0, list);
- expect_kind(c, values[k], wanted(c, values[k], true, 1, field_kind(r, k)), field_kind(r, k));
+ item_of(c, s->first, c->handling, 0, list);
+ expect_kind(c, values[k], wanted(c, values[k], c->handling, 1, field_kind(r, k)), field_kind(r, k));
emit(c, s->line, s->column, ITEM_SET, (uint32_t)list | k << 16, 0);
}
break;
}
if (list < 0) {
- Kind index = value(c, s->first, true, 0);
+ Kind index = value(c, s->first, c->handling, 0);
expect_kind(c, s->first, index, INDEX);
list = index.owner;
}
+ view_changes(c, s, list, c->state_names[list]);
const Record *r = record_of(c, list);
unsigned field = record_field(c, r, s->member, s->line, s->column);
if (c->list_key[list] == (int)field + 1)
fail_at(c, s->line, s->column, "the key of '%s' does not change; remove the item and add it again", c->state_names[list]);
- if (*s->name) item_of(c, s->first, true, 0, list);
- expect_kind(c, s->value, wanted(c, s->value, true, 1, field_kind(r, field)), field_kind(r, field));
+ if (*s->name) item_of(c, s->first, c->handling, 0, list);
+ expect_kind(c, s->value, wanted(c, s->value, c->handling, 1, field_kind(r, field)), field_kind(r, field));
emit(c, s->line, s->column, ITEM_SET, (uint32_t)list | field << 16, 0);
break;
}
case S_REPLACE: {
+ view_changes(c, s, -1, s->name);
uint32_t target = collection(c, s->name, s->line, s->column);
- number(c, s->first, true, 0);
- number(c, s->last, true, 1);
+ number(c, s->first, c->handling, 0);
+ number(c, s->last, c->handling, 1);
bytes_value(c, s->value, 2, "replace");
emit(c, s->line, s->column, REPLACE, target, 0);
break;
}
case S_SEND: {
+ if (!c->handling) fail_at(c, s->line, s->column, "a view sends nothing; a handler sends");
int output = input_index(c, s->name);
if (output < 0 || c->inputs[output].kind != KIND_OUTPUT) {
const char *kind = declared_kind(c, s->name);
@@ -2968,7 +2999,7 @@ static void statements(Compiler *c, const Stmt *s) {
if (!given[f]) fail_at(c, s->line, s->column, "send %s needs '%s'", s->name, out->field_names[f]);
int type = out->field_types[f];
if (type == FIELD_BYTES) bytes_value(c, given[f], (int)f, "a bytes field");
- else expect_kind(c, given[f], wanted(c, given[f], true, (int)f, message_kind(type)), message_kind(type));
+ else expect_kind(c, given[f], wanted(c, given[f], c->handling, (int)f, message_kind(type)), message_kind(type));
}
emit(c, s->line, s->column, SEND, (uint32_t)output, 0);
break;
@@ -2981,7 +3012,8 @@ static void statements(Compiler *c, const Stmt *s) {
emit(c, s->line, s->column, REPLY, 0, 0);
break;
case S_REQUIRE:
- truth(c, s->value, true, 0, "require");
+ if (!c->handling) fail_at(c, s->line, s->column, "a view requires nothing; its value says what it found");
+ truth(c, s->value, c->handling, 0, "require");
emit(c, s->line, s->column, REQUIRE, 0, 0);
break;
case S_FOR: {
@@ -2997,8 +3029,8 @@ static void statements(Compiler *c, const Stmt *s) {
if (key != NUM && key != I64)
fail_at(c, s->first->line, s->first->column, "'%s' is not keyed by a number or an i64: a slice takes one",
s->member);
- expect_kind(c, s->low, wanted(c, s->low, true, 0, key), key);
- expect_kind(c, s->high, wanted(c, s->high, true, 1, key), key);
+ expect_kind(c, s->low, wanted(c, s->low, c->handling, 0, key), key);
+ expect_kind(c, s->high, wanted(c, s->high, c->handling, 1, key), key);
}
unsigned at = c->code_count, jump = 0, loop = c->loop_count++;
emit(c, s->line, s->column, EACH, 0, 0);
@@ -3007,7 +3039,7 @@ static void statements(Compiler *c, const Stmt *s) {
c->loops[loop].item = -1;
c->loops[loop].scope = c->scope;
if (s->value) {
- truth(c, s->value, true, 0, "where");
+ truth(c, s->value, c->handling, 0, "where");
jump = c->code_count;
emit(c, s->line, s->column, JZ, 0, 0);
}
@@ -3034,7 +3066,7 @@ static void statements(Compiler *c, const Stmt *s) {
break;
}
case S_IF: {
- truth(c, s->value, true, 0, "if");
+ truth(c, s->value, c->handling, 0, "if");
unsigned jump = c->code_count;
emit(c, s->line, s->column, JZ, 0, 0);
++c->nested;
@@ -3059,10 +3091,10 @@ static void statements(Compiler *c, const Stmt *s) {
fail_at(c, s->line, s->column, "'%s' is already a let or a for's name here", s->name);
if (c->local_slots == LOCALS || c->local_count == LOCALS)
fail_at(c, s->line, s->column, "at most %d lets are allowed in a handler", LOCALS);
- bool integer = probe(c, s->low, true, 0) == I64 || probe(c, s->high, true, 0) == I64;
+ bool integer = probe(c, s->low, c->handling, 0) == I64 || probe(c, s->high, c->handling, 0) == I64;
Type type = integer ? I64 : NUM;
- expect_kind(c, s->low, wanted(c, s->low, true, 0, type), type);
- expect_kind(c, s->high, wanted(c, s->high, true, 1, type), type);
+ expect_kind(c, s->low, wanted(c, s->low, c->handling, 0, type), type);
+ expect_kind(c, s->high, wanted(c, s->high, c->handling, 1, type), type);
unsigned scope = c->local_count, local = c->local_count++, loop = c->loop_count++, at = c->code_count;
memcpy(c->locals[local].name, s->name, NAME);
c->locals[local].slot = c->local_slots++;
@@ -3086,6 +3118,12 @@ static void statements(Compiler *c, const Stmt *s) {
if (!c->loop_count) fail_at(c, s->line, s->column, "break ends a for; this one is outside any");
emit(c, s->line, s->column, BREAK, 0, 0);
break;
+ case S_RETURN: /* a view's body's value, its last line */
+ if (c->handling) fail_at(c, s->line, s->column, "return gives a view's body its value; a handler returns nothing");
+ if (s->next || c->nested) fail_at(c, s->line, s->column, "return is the last line of a view's body");
+ c->returned = value(c, s->value, false, 0);
+ c->has_returned = true;
+ break;
}
}
c->local_count = scope;
@@ -3133,8 +3171,8 @@ static uint32_t intern(unsigned char *strings, size_t *used, const char *text) {
* blocks, which hold one record keyed one way; no two handlers of one event
* have one, so each begins empty. */
static void event_lists(Compiler *c) {
- for (unsigned h = 0; h < c->handler_count; ++h)
- for (const Stmt *s = c->handlers[h].body; s; s = s->next) {
+ for (unsigned h = 0; h < c->handler_count + c->views; ++h) /* the handlers', then the views' bodies' */
+ for (const Stmt *s = h < c->handler_count ? c->handlers[h].body : c->view_body[h - c->handler_count]; s; s = s->next) {
if (s->kind != S_LET || !s->listed) continue;
char path[2 * NAME];
snprintf(path, sizeof(path), "let.%s", s->name);
@@ -3148,7 +3186,7 @@ static void event_lists(Compiler *c) {
strcmp(c->state_key[found], s->key))
fail_at(c, s->line, s->column, "'%s' is a let's list of another record or key in another handler; one name "
"holds one kind of list", s->name);
- for (unsigned k = 0; k < h; ++k) {
+ for (unsigned k = 0; h < c->handler_count && k < h; ++k) {
if (strcmp(c->handlers[k].input, c->handlers[h].input) || c->handlers[k].edge != c->handlers[h].edge) continue;
for (const Stmt *t = c->handlers[k].body; t; t = t->next)
if (t->kind == S_LET && t->listed && !strcmp(t->name, s->name))
@@ -3337,7 +3375,13 @@ static void generate(Compiler *c, unsigned char **module, size_t *size) {
c->row = c->view_table[i] - 1;
c->arguments = c->view_arguments[c->row >= 0 ? (unsigned)c->row : i] - 1;
c->view_code[i][0] = c->code_count;
- Kind kind = value(c, c->view_value[i], false, 0);
+ Kind kind;
+ if (c->view_body[i]) { /* statements as a handler's, changing only its lets and lists, and the value returned */
+ c->local_count = c->local_slots = c->loop_count = c->nested = 0;
+ c->handling = c->has_returned = false;
+ statements(c, c->view_body[i]);
+ kind = c->returned;
+ } else kind = value(c, c->view_value[i], false, 0);
if (kind.type != NUM && kind.type != BOOL && kind.type != I64 && kind.type != TEXT)
fail_at(c, c->view_value[i]->line, c->view_value[i]->column, "a %s is a number, a bool, an i64 or text, not %s",
c->row >= 0 ? "column" : "view", type_name(kind.type));
@@ -3347,10 +3391,11 @@ static void generate(Compiler *c, unsigned char **module, size_t *size) {
c->row = -1;
c->arguments = -1;
c->fold_list = -1;
+ c->handling = true;
for (unsigned i = 0; i < c->handler_count; ++i) {
int input = handler_input[i];
c->message = input >= 0 && c->inputs[input].kind != KIND_KEY ? input : -1;
- c->local_count = c->local_slots = 0;
+ c->local_count = c->local_slots = c->loop_count = c->nested = 0;
c->handler_code[i][0] = c->code_count;
statements(c, c->handlers[i].body);
c->handler_code[i][1] = c->code_count - c->handler_code[i][0];