diff options
| author | eunhhu <[email protected]> | 2026-09-30 06:41:41 +0100 |
|---|---|---|
| committer | eunhhu <[email protected]> | 2026-09-30 06:41:41 +0100 |
| commit | c69682e1f1bc73e2b1f4a22b8eb3ec463be588e3 (patch) | |
| tree | c181b50fedf6b306b4a53d06d305d4be24bcb94e /compiler/compile.c | |
| parent | aa279fb959024134416fc33f90f648d8332e9af4 (diff) | |
| download | aup-c69682e1f1bc73e2b1f4a22b8eb3ec463be588e3.tar.gz aup-c69682e1f1bc73e2b1f4a22b8eb3ec463be588e3.zip | |
Spread an item or a message into a record or a send, and set an item whole
Adding one field to the board two nodes share (Pure's research 105)
still meant the record, the output, three sends and the code applying
what the other node sent: the sends and the apply named every field.
..source, last in a record value's or a send's braces, gives the fields
not written from the source's fields of the same names: an item (an
index, a let, the item a for walks, list[key]) or the handler's message
(grouped fields by their paths). A record value gives what the source
lacks its default; a send still needs every field. list[i] = Record {
... } sets every field of the item, all of them given; a keyed list's
key does not change, so a value with another key fails the event
(limit) and changes nothing.
Both are the fields written out: the checks compare the modules byte
for byte, and run the key rule in pure-run. Seven diagnostics: a spread
not last, a source that is no item nor this handler's message (also
another handler's message), a spread inside a send's group, a send
field left empty, a whole item missing a field, a value that is no
record. The board now sends { ..at } and applies cards[k] = Card {
..remote }: a new field is written in two places, the record and the
output.
Diffstat (limited to 'compiler/compile.c')
| -rw-r--r-- | compiler/compile.c | 150 |
1 files changed, 142 insertions, 8 deletions
diff --git a/compiler/compile.c b/compiler/compile.c index 4e49b4b..86259dd 100644 --- a/compiler/compile.c +++ b/compiler/compile.c @@ -796,6 +796,22 @@ parsed: } /* Record { field: value, ... }: fields end at a comma, semicolon or line end. */ +/* ..source, last in a record's or a send's braces: an E_FIELD with no + * member, its value the source (an item, or the handler's message) whose + * fields of the same names give those not written. */ +static bool spread(Compiler *c, Expr ***tail) { + if (!is(c, "..")) return false; + Token dots = take(c); + Expr *f = node(c, E_FIELD, dots.line, dots.column); + f->args[0] = expression(c, 0, false); + f->count = 1; + **tail = f; + *tail = &f->next; + while (is(c, ",") || is(c, ";") || c->token.kind == T_NL) take(c); + if (!is(c, "}")) fail_at(c, c->token.line, c->token.column, "a spread goes last, as in { field: value, ..source }"); + return true; +} + static Expr *record_value(Compiler *c) { Token name = take(c); take(c); /* { */ @@ -803,6 +819,7 @@ static Expr *record_value(Compiler *c) { memcpy(r->name, name.text, NAME); separators(c); while (!is(c, "}")) { + if (spread(c, &tail)) break; Token field = expect_name(c, "a field name"); expect(c, ":", "':'"); Expr *f = node(c, E_FIELD, field.line, field.column); @@ -826,6 +843,7 @@ static Expr *send_fields(Compiler *c) { Expr *r = node(c, E_RECORD, open.line, open.column), **tail = &r->args[0]; separators(c); while (!is(c, "}")) { + if (spread(c, &tail)) break; Token field = expect_name(c, "a field name"); expect(c, ":", "':'"); Expr *f = node(c, E_FIELD, field.line, field.column); @@ -943,8 +961,13 @@ static Stmt *statement(Compiler *c, unsigned depth) { if (is(c, "(")) fail_at(c, s->first->line, s->first->column, "a collection is indexed by the message key, as in name[key]"); memcpy(s->member, field.text, NAME); - } else fail_at(c, c->token.line, c->token.column, "expected '.' and the field to change, as in %s[at].field = value", - s->name); + } else if (is(c, "=")) { /* tasks[at] = Task { ... }: every field of the item (no member) */ + take(c); + s->value = inserted(c); + return s; + } else + fail_at(c, c->token.line, c->token.column, + "expected '.' and the field to change, as in %s[at].field = value, or '=' and a record", s->name); expect(c, "=", "'='"); s->value = expression(c, 0, false); return s; @@ -2162,22 +2185,97 @@ static void bytes_value(Compiler *c, const Expr *e, int height, const char *what else if (kind.type != BYTES) fail_at(c, e->line, e->column, "%s takes bytes or text, not %s", what, type_name(kind.type)); } -/* A record's fields in order, onto the stack from height; missing fields take their default. */ -static void record_fields(Compiler *c, const Expr *e, int list, int height) { +/* The spread among a record's or a send's fields (..source), or NULL. */ +static const Expr *spread_of(const Expr *e) { + for (const Expr *f = e->args[0]; f; f = f->next) + if (!f->member[0]) return f; + return NULL; +} + +/* What a spread's source reads name from: source.name, as written out (an + * item's field, or a field of the handler's message by its path), or NULL + * when the source has no field of that name. A source is an item (an + * index, a let or the item a for walks, or list[key]) or the handler's + * message. */ +static Expr *spread_field(Compiler *c, const Expr *spread, const char *name) { + const Expr *source = spread->args[0]; + int line = spread->line, column = spread->column, list = -1; + if (source->kind == E_NAME && c->message >= 0 && !strcmp(source->name, c->inputs[c->message].name)) { + const Input *in = &c->inputs[c->message]; + bool has = false; + for (unsigned i = 0; i < in->fields && !has; ++i) has = !strcmp(in->field_names[i], name); + if (!has) return NULL; + Expr *read = node(c, E_NAME, line, column); + memcpy(read->name, source->name, NAME); + for (const char *part = name; *part;) { /* to.city: message.to.city */ + size_t length = strcspn(part, "."); + Expr *access = node(c, E_MEMBER, line, column); + access->args[0] = read; + access->count = 1; + memcpy(access->member, part, length); + read = access; + part += length + (part[length] == '.'); + } + return read; + } + if (source->kind == E_NAME) { + int local = local_named(c, source->name), state = state_index(c, source->name); + if (c->walking && !strcmp(source->name, c->walk_name)) list = c->walk_list; + else if (local >= 0 && c->locals[local].kind.type == INDEX) list = c->locals[local].kind.owner; + else if (local < 0 && state >= 0 && c->state_types[state] == STATE_INDEX) list = cursor_owner(c, state); + } else if (source->kind == E_ITEM && !source->member[0]) + list = list_state(c, source->name, source->line, source->column, source->args[0]); + if (list < 0) + fail_at(c, source->line, source->column, "a spread takes an item (an index, or list[key]) or this handler's message"); + const Record *r = record_of(c, list); + bool has = false; + for (unsigned i = 0; i < r->fields && !has; ++i) has = !strcmp(r->field_names[i], name); + if (!has) return NULL; + Expr *read; + if (source->kind == E_ITEM) { + read = node(c, E_ITEM, line, column); + memcpy(read->name, source->name, NAME); + read->args[0] = source->args[0]; + read->count = 1; + } else { + Expr *item = node(c, E_NAME, line, column); + memcpy(item->name, source->name, NAME); + read = node(c, E_MEMBER, line, column); + read->args[0] = item; + read->count = 1; + } + memcpy(read->member, name, NAME); + return read; +} + +/* A record value's fields in the record's order (values, NULL when not + * given): those written, and the rest its spread's source has by name. */ +static void record_values(Compiler *c, const Expr *e, int list, const Expr **values) { const Record *r = record_of(c, list); if (e->kind != E_RECORD) fail_at(c, e->line, e->column, "'%s' holds %s records, as in %s.append(%s { %s: ... })", c->state_names[list], r->name, c->state_names[list], r->name, r->field_names[0]); if (strcmp(e->name, r->name)) fail_at(c, e->line, e->column, "'%s' holds %s, not %s", c->state_names[list], r->name, e->name); - const Expr *given[FIELDS_PER_RECORD] = {0}; + for (unsigned k = 0; k < r->fields; ++k) values[k] = NULL; for (const Expr *f = e->args[0]; f; f = f->next) { + if (!f->member[0]) continue; /* the spread, below */ unsigned field = record_field(c, r, f->member, f->line, f->column); - if (given[field]) fail_at(c, f->line, f->column, "field '%s' is given twice", f->member); - given[field] = f; + if (values[field]) fail_at(c, f->line, f->column, "field '%s' is given twice", f->member); + values[field] = f->args[0]; } + const Expr *spreading = spread_of(e); + for (unsigned k = 0; spreading && k < r->fields; ++k) + if (!values[k]) values[k] = spread_field(c, spreading, r->field_names[k]); +} + +/* A record's fields in order, onto the stack from height; missing fields take their default. */ +static void record_fields(Compiler *c, const Expr *e, int list, int height) { + const Record *r = record_of(c, list); + const Expr *values[FIELDS_PER_RECORD]; + record_values(c, e, list, values); for (unsigned k = 0; k < r->fields; ++k) { int at = height + (int)k; - if (given[k]) expect_kind(c, given[k]->args[0], value(c, given[k]->args[0], true, at), r->field_text[k] ? TEXT : NUM); + if (values[k]) expect_kind(c, values[k], value(c, values[k], true, at), r->field_text[k] ? TEXT : NUM); else if (at + 1 > STACK) fail_at(c, e->line, e->column, "expression needs more than 64 stack values"); else if (r->field_text[k]) emit(c, e->line, e->column, TEXT_CONST, r->field_constant[k], 0); else emit(c, e->line, e->column, CONST, 0, r->field_number[k]); @@ -2187,6 +2285,10 @@ static void record_fields(Compiler *c, const Expr *e, int list, int height) { /* send's values by the output's fields, groups by their paths. */ static void send_values(Compiler *c, const Input *out, const Expr *group, const char *path, const Expr **given) { for (const Expr *f = group->args[0]; f; f = f->next) { + if (!f->member[0]) { /* the spread: the send's own braces only */ + if (*path) fail_at(c, f->line, f->column, "a spread goes in the send's own braces, not a group's"); + continue; + } char name[NAME]; int length = snprintf(name, sizeof(name), "%s%s%s", path, *path ? "." : "", f->member); if (length < 0 || length >= NAME) fail_at(c, f->line, f->column, "output '%s' has no field '%s.%s'", out->name, path, f->member); @@ -2309,6 +2411,35 @@ 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 */ + const Record *r = record_of(c, list); + const Expr *values[FIELDS_PER_RECORD]; + record_values(c, s->value, list, values); + int key = c->list_key[list] - 1; + for (unsigned k = 0; k < r->fields; ++k) + if (!values[k]) fail_at(c, s->value->line, s->value->column, "%s[...] = %s { ... } needs '%s'", s->name, + r->name, r->field_names[k]); + if (key >= 0) { /* require tasks[at].key == the value's key */ + Expr *item = node(c, E_ITEM, s->line, s->column), *same = node(c, E_BINARY, s->line, s->column); + memcpy(item->name, s->name, NAME); + memcpy(item->member, r->field_names[key], NAME); + item->args[0] = s->first; + item->count = 1; + same->op = EQ; + same->args[0] = item; + same->args[1] = (Expr *)values[key]; + same->count = 2; + number(c, same, true, 0); + 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], value(c, values[k], true, 1), r->field_text[k] ? TEXT : NUM); + 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); expect_kind(c, s->first, index, INDEX); @@ -2341,6 +2472,9 @@ static void statements(Compiler *c, const Stmt *s) { const Input *out = &c->inputs[output]; const Expr *given[FIELDS] = {0}; send_values(c, out, s->value, "", given); + const Expr *spreading = spread_of(s->value); + for (unsigned f = 0; spreading && f < out->fields; ++f) + if (!given[f]) given[f] = spread_field(c, spreading, out->field_names[f]); if (out->fields > STACK) fail_at(c, s->line, s->column, "an output of more than %d fields cannot be sent", STACK); for (unsigned f = 0; f < out->fields; ++f) { if (!given[f]) fail_at(c, s->line, s->column, "send %s needs '%s'", s->name, out->field_names[f]); |
