From 11960e4e2353dc63660362660901183a2585cb08 Mon Sep 17 00:00:00 2001 From: eunhhu Date: Mon, 19 Oct 2026 07:11:50 +0100 Subject: A list given a value whole without a core change (140's frictions): xs = a list value (a fn's, a let's) copies it first, empties xs and gives it the copy's items (added by key when keyed); xs = ys walks ys where it is, xs = xs leaves it; xs = [...] for a stored list too; xs, j = f(x) with a list among the names; a value of another kind or record refused by name ('xs' takes a list of F, not G); frictions in make check, diagnostics updated; no module of aup's, Pure's or Vega's changes --- compiler/compile.c | 141 ++++++++++++++++++++++++++++++++++++++++------------- tests/check.py | 51 ++++++++++++++----- 2 files changed, 148 insertions(+), 44 deletions(-) diff --git a/compiler/compile.c b/compiler/compile.c index eab1b3c..b11acec 100644 --- a/compiler/compile.c +++ b/compiler/compile.c @@ -124,6 +124,7 @@ typedef struct Stmt { * the values linked from value by next. */ char (*names)[NAME]; unsigned name_count; + int refills; /* S_LET of xs = value's copy: xs's list + 1 (its value must be a list of its record), else 0 */ struct Stmt *then, *otherwise, *next; } Stmt; @@ -1227,35 +1228,12 @@ static Stmt *target(Compiler *c, Token name) { return t; } -/* a, xs[i].x = x, y after its first target: every target its value at once, the values first, in order. Names alone - * are S_ASSIGNS; with a field or an item among them, the values go to lets of their own first (a literal, which - * reads nothing, goes straight), then each target takes its own: a let each and a statement each, linked. */ -static Stmt *together(Compiler *c, Stmt *first) { - Stmt *targets[STACK]; - unsigned n = 0; - bool names = first->kind == S_ASSIGN; - targets[n++] = first; - while (is(c, ",")) { - take(c); - Token name = expect_name(c, "a name"); - if (n == STACK) fail_at(c, name.line, name.column, "at most %d names take values at once", STACK); - Stmt *t = target(c, name); - for (unsigned i = 0; t->kind == S_ASSIGN && i < n; ++i) - if (targets[i]->kind == S_ASSIGN && !strcmp(targets[i]->name, t->name)) - fail_at(c, name.line, name.column, "'%s' is named twice", name.text); - names = names && t->kind == S_ASSIGN; - targets[n++] = t; - } - Stmt *s = allocate(c, sizeof(*s)); - s->line = first->line, s->column = first->column; - s->names = allocate(c, STACK * sizeof(*s->names)); - s->name_count = n; - for (unsigned i = 0; i < n; ++i) - if (names) memcpy(s->names[i], targets[i]->name, NAME); - else snprintf(s->names[i], NAME, ".%d.%d.%u", s->line, s->column, i); /* no name of the source's */ - s->kind = names ? S_ASSIGNS : S_LETS; - several_values(c, s); - if (names) return s; +/* s (its names' count and its values, a, xs[i].x = x, y's) as lets and a statement for each target: the values + * first, each into a let of its own (a literal, which reads nothing, straight), then each target, left to right. */ +static Stmt *through_lets(Compiler *c, Stmt *s, Stmt **targets) { + unsigned n = s->name_count; + for (unsigned i = 0; i < n; ++i) snprintf(s->names[i], NAME, "$%d$%d$%u", s->line, s->column, i); /* none of the source's (nor a path) */ + s->kind = S_LETS; Expr *values[STACK]; unsigned count = 0; for (Expr *v = s->value; v; v = v->next) values[count++] = v; @@ -1276,6 +1254,7 @@ static Stmt *together(Compiler *c, Stmt *first) { } last = last->next = targets[i]; } + last->next = NULL; if (count == n) *link = NULL; s->name_count = kept; if (kept == 1) { /* one let: as let x = value */ @@ -1286,6 +1265,35 @@ static Stmt *together(Compiler *c, Stmt *first) { return kept ? s : s->next; /* every value a literal: no lets */ } +/* a, xs[i].x = x, y after its first target: every target its value at once, the values first, in order. Names alone + * are S_ASSIGNS; with a field or an item among them, the values go to lets of their own first (a literal, which + * reads nothing, goes straight), then each target takes its own: a let each and a statement each, linked. */ +static Stmt *together(Compiler *c, Stmt *first) { + Stmt *targets[STACK]; + unsigned n = 0; + bool names = first->kind == S_ASSIGN; + targets[n++] = first; + while (is(c, ",")) { + take(c); + Token name = expect_name(c, "a name"); + if (n == STACK) fail_at(c, name.line, name.column, "at most %d names take values at once", STACK); + Stmt *t = target(c, name); + for (unsigned i = 0; t->kind == S_ASSIGN && i < n; ++i) + if (targets[i]->kind == S_ASSIGN && !strcmp(targets[i]->name, t->name)) + fail_at(c, name.line, name.column, "'%s' is named twice", name.text); + names = names && t->kind == S_ASSIGN; + targets[n++] = t; + } + Stmt *s = allocate(c, sizeof(*s)); + s->line = first->line, s->column = first->column; + s->names = allocate(c, STACK * sizeof(*s->names)); + s->name_count = n; + for (unsigned i = 0; names && i < n; ++i) memcpy(s->names[i], targets[i]->name, NAME); + s->kind = S_ASSIGNS; + several_values(c, s); + return names ? s : through_lets(c, s, targets); +} + static Stmt *statement(Compiler *c, unsigned depth) { Token token = expect_name(c, "a statement or '}'"); Stmt *s = allocate(c, sizeof(*s)); @@ -4538,6 +4546,41 @@ static Stmt *emptied(Compiler *c, const char *name, int line, int column) { return loop; } +/* xs = a list value (a fn's, a let's, another list's): a copy of it first (xs may be in it), then xs emptied, then + * given the copy's items in order (added by key when xs is keyed). */ +static Stmt *refilled(Compiler *c, const Stmt *s, int list) { + Stmt *copy = allocate(c, sizeof(*copy)), *each = allocate(c, sizeof(*each)), *give = allocate(c, sizeof(*give)); + Expr *item = node(c, E_NAME, s->line, s->column); + copy->kind = S_LET, copy->line = s->line, copy->column = s->column, copy->value = s->value, copy->refills = list + 1; + snprintf(copy->name, NAME, "$%d$%d$copy", s->line, s->column); + each->kind = S_FOR, each->line = s->line, each->column = s->column, each->then = give; + snprintf(each->name, NAME, "$%d$%d$item", s->line, s->column); + memcpy(each->member, copy->name, NAME); + each->first = node(c, E_NAME, s->line, s->column), each->last = node(c, E_NAME, s->line, s->column); /* where they stand */ + memcpy(each->first->name, copy->name, NAME); + memcpy(each->last->name, each->name, NAME); + memcpy(item->name, each->name, NAME); + give->kind = keyed_by(c, list) ? S_ADD : S_APPEND, give->line = s->line, give->column = s->column, give->value = item; + memcpy(give->name, s->name, NAME); + Stmt *empty = emptied(c, s->name, s->line, s->column); + empty->next = each; + int other = s->value->kind == E_NAME && !c->module && parameter(c, s->value->name) < 0 && local_named(c, s->value->name) < 0 ? + state_index(c, s->value->name) : -1; + if (other >= 0 && c->state_types[other] == STATE_LIST) { /* xs = ys: another list, walked where it is */ + if (other == list) return NULL; /* xs = xs: as it is */ + int record = module_record(c, c->state_library[list], c->state_target[list]), + given = module_record(c, c->state_library[other], c->state_target[other]); + if (record != given) + fail_at(c, s->value->line, s->value->column, "'%s' takes a list of %s, not %s", s->name, c->records[record].name, + c->records[given].name); + memcpy(each->member, s->value->name, NAME); + memcpy(each->first->name, s->value->name, NAME); + return empty; + } + copy->next = empty; + return copy; +} + static void statements(Compiler *c, const Stmt *s) { unsigned scope = c->local_count; /* a block's lets end with it */ for (const Stmt *before = NULL; s; before = s, s = s->next) { @@ -4707,6 +4750,14 @@ static void statements(Compiler *c, const Stmt *s) { Kind held = value(c, s->value, c->handling, 0); if (held.type == TUPLE) fail_at(c, s->value->line, s->value->column, "this gives several values: name each, as in let a, b = ..."); + if (s->refills) { /* xs = value: a list of xs's record */ + int list = s->refills - 1, record = module_record(c, c->state_library[list], c->state_target[list]); + const char *name = c->state_names[list]; + if (c->state_event[list] && !strncmp(name, "let.", 4)) name += 4; /* a let's list, as written */ + if (held.type != LIST || held.owner != record) + fail_at(c, s->value->line, s->value->column, "'%s' takes a list of %s, not %s", name, + c->records[record].name, held.type == LIST ? c->records[held.owner].name : type_name(held.type)); + } if (held.type == RECORD || held.type == LIST) { /* a record's value, a list's (027's 2c) */ name_value(c, s->name, held, s->line, s->column); pop_named(c, local_named(c, s->name), s->line, s->column); @@ -4769,6 +4820,31 @@ static void statements(Compiler *c, const Stmt *s) { case S_ASSIGNS: { const Expr *v = s->value; int locals[STACK], states[STACK]; + bool lists = false; + for (unsigned i = 0; i < s->name_count; ++i) { + int list = local_named(c, s->names[i]) < 0 ? state_index(c, s->names[i]) : -1; + lists = lists || (list >= 0 && c->state_types[list] == STATE_LIST); + } + if (lists) { /* xs, j = f(x): a list among the names takes its value as xs = value does, after the values */ + Stmt *spread = allocate(c, sizeof(*spread)), *targets[STACK]; + *spread = *s; + spread->next = NULL; + spread->names = allocate(c, STACK * sizeof(*spread->names)); + Expr **link = &spread->value; + for (const Expr *x = s->value; x; x = x->next) { /* copies: a fn's body is compiled for each instance */ + *link = node(c, x->kind, x->line, x->column); + **link = *x; + link = &(*link)->next; + } + *link = NULL; + for (unsigned i = 0; i < s->name_count; ++i) { + targets[i] = allocate(c, sizeof(*targets[i])); + targets[i]->kind = S_ASSIGN, targets[i]->line = s->line, targets[i]->column = s->column; + memcpy(targets[i]->name, s->names[i], NAME); + } + statements(c, through_lets(c, spread, targets)); + break; + } if (!v->next) { /* a fn's call that gives them all (027's 2c): each into a let of its kind */ Kind given = value(c, v, c->handling, 0); const Instance *in = given.type == TUPLE ? &c->instances[given.owner] : NULL; @@ -4869,8 +4945,8 @@ static void statements(Compiler *c, const Stmt *s) { } else if (c->state_types[index] == STATE_NUM || c->state_types[index] == STATE_BOOL) { 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 && c->state_event[index] && s->value->kind == E_LIST) { - statements(c, emptied(c, s->name, s->line, s->column)); /* xs = [...]: a let's list emptied, then its elements */ + } else if (c->state_types[index] == STATE_LIST && s->value->kind == E_LIST) { + statements(c, emptied(c, s->name, s->line, s->column)); /* xs = [...]: the list emptied, then its elements */ if (keyed_by(c, index) && s->value->args[0]) fail_at(c, s->value->line, s->value->column, "a keyed list adds its elements by key; add them one by one"); for (const Expr *element = s->value->args[0]; element; element = element->next) { @@ -4878,8 +4954,7 @@ static void statements(Compiler *c, const Stmt *s) { emit(c, element->line, element->column, LIST_APPEND, (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, %s[index].field = value and (a let's) " - "%s = [...]", s->name, s->name); + statements(c, refilled(c, s, index)); /* xs = a list value */ } else if (c->state_types[index] == STATE_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); diff --git a/tests/check.py b/tests/check.py index 72dee99..9f1242d 100644 --- a/tests/check.py +++ b/tests/check.py @@ -80,6 +80,10 @@ DIAGNOSTICS = [ ('state a = 0\non frame {\n let xs, ys: list of number = [1]\n}\n', '3:30', 'lists declared together begin empty: give each its elements apart'), ('state a = 0\non frame {\n let xs, xs: list of number\n}\n', '3:11', "'xs' is named twice"), + ('record F {\n v = 0\n}\nstate kept: list of F\nstate n = 0\non frame {\n kept = n\n}\n', '7:10', + "'kept' takes a list of F, not a number"), + ('record F {\n v = 0\n}\nrecord G {\n w = 0\n}\nstate kept: list of F\nstate other: list of G\non frame {\n kept = other\n}\n', + '10:10', "'kept' takes a list of F, not G"), ('state s: text\nstate a = 0\non frame {\n s, a = "x", 1\n}\n', '4:3', "'s' takes its value apart"), ('state t: text\nstate c: cursor in t\non frame {\n let a, b = 1, c\n}\n', '4:17', 'a let holds a number, a bool, an i64, an index, bytes, text, a list or a record, not a cursor'), @@ -91,9 +95,8 @@ DIAGNOSTICS = [ ('state data: bytes\ninput k = key KeyK\non k.down {\n data.insert(data.start, "x")\n}\n', '4:3', "'data' is a bytes state: it is read and set whole, no cursor in it"), ('state data: bytes\nstate t: text\ninput k = key KeyK\non k.down {\n t = data\n}\n', '5:7', 'expected text, not bytes'), - # Lets' lists in inner blocks and xs = [...] (after 140's inflate): a stored list is not given elements so. - ('record R {\n v = 0\n}\nstate rs: list of R\non frame {\n rs = []\n}\n', '6:3', - "a list changes by append, insert, remove, rs[index].field = value and (a let's) rs = [...]"), + # A list given a value whole (after 140's frictions): xs = [...] and xs = a list of its record, a stored list's too. + ('record R {\n v = 0\n}\nstate rs: list of R\non frame {\n rs = [1]\n}\n', '6:9', "'rs' holds R records, as in rs.append(R { v: ... })"), # Bytes compared (after 140's DNS and HTTP): bytes compare with bytes (a string where bytes go is its bytes), and # neither compares nor joins with a text's value. ('state t: text\ninput m = message { name: bytes }\non m {\n require m.name == t\n}\n', '4:21', @@ -275,7 +278,7 @@ DIAGNOSTICS = [ (RECORD + 'state xs: list of T\nview v = xs.size\n', '5:10', "a list has count, first and last, not 'size'"), (RECORD + 'state xs: list of T\nstate at: index of xs\nview v = at\n', '6:10', 'a view is a number, a bool, an i64, text, bytes or a list, not an index'), (RECORD + 'state xs: list of T\nstate at: index of xs\n' + ON_K + ' at = 3\n}\n', '8:8', 'expected an index, not a number'), - (RECORD + 'state xs: list of T\n' + ON_K + ' xs = 1\n}\n', '7:3', 'a list changes by append, insert, remove'), + (RECORD + 'state xs: list of T\n' + ON_K + ' xs = 1\n}\n', '7:8', "'xs' takes a list of T, not a number"), (RECORD + 'record U {\n a = 0\n}\nstate xs: list of T\n' + ON_K + ' xs.append(U { a: 1 })\n}\n', '10:13', "'xs' holds T, not U"), (RECORD + 'state xs: list of T\n' + ON_K + ' xs.append(1)\n}\n', '7:13', "'xs' holds T records"), @@ -661,8 +664,29 @@ state bs: list of B state a = 0 state b = 0 state i = 0 +state kept: list of P +state keyed: list of P by x +state j = 0 input go = message { k: u32 } +fn made(n) -> list of P, number { + let out: list of P + for m in 0 .. n { out.append(P { x: m * 10 }) } + return out, n + 100 +} +fn only(n) -> list of P { + let out: list of P + for m in 0 .. n { out.append(P { x: n - m }) } + return out +} on go { + let ts: list of P + ts.append(P { x: 1 }) + ts, j = made(go.k) + kept = ts + kept = kept + keyed = only(go.k + 1) + if go.k == 3 { kept = [] } + if go.k == 4 { keyed = kept } if bs.count == 0 { bs.append(B {}) } bs.append(B {}) bs[0].x, bs[0].y = go.k, go.k * 2 @@ -676,10 +700,10 @@ on go { b = b + ns[0] * 100 + ns[1] let xs, ys: list of number let ps, qs: list of P - for j in 0 .. go.k { - xs.append(j) - ys.append(j * 2) - ps.append(P { x: j }) + for w in 0 .. go.k { + xs.append(w) + ys.append(w * 2) + ps.append(P { x: w }) } if go.k > 1 { let inner, outer: list of number @@ -1785,8 +1809,9 @@ def rows_at(aup, pure_run, work, events=200): def frictions(aup, pure_run, work, events=100): """140's frictions written away in aup alone, no core change: lets' lists declared together (let a, b: list of R), each its own and empty as it begins, in inner blocks too; targets together with fields and items among them - (xs[i].x, xs[i].y = a, b; a, xs[i] = ...), the values first, then each target left to right as Python's. Each event - against Python's.""" + (xs[i].x, xs[i].y = a, b; a, xs[i] = ...), the values first, then each target left to right as Python's; a list + given a list value whole (xs = f(x), xs = ys, xs = xs, xs = [] for a list state too, xs, j = f(x)), added by key + when keyed. Each event against Python's.""" import random import struct path = work / 'frictions.pmod' @@ -1799,6 +1824,9 @@ def frictions(aup, pure_run, work, events=100): n, bs, a, b, i = 0, [], 0, 0, 0 for k, line in zip(asked, lines): state = json.loads(line) + kept = [] if k == 3 else [{'x': m * 10} for m in range(k)] + keyed = sorted(({'x': k + 1 - m} for m in range(k + 1)), key=lambda p: p['x']) + if k == 4: keyed = sorted(kept, key=lambda p: p['x']) if k > 1: n = 10 + 1 + k bs += [{'x': 0, 'y': 0}] * (2 if not bs else 1) bs = [dict(x) for x in bs] @@ -1809,7 +1837,8 @@ def frictions(aup, pure_run, work, events=100): ns = [0, 0] ns[0], ns[1] = 3, k b = b + ns[0] * 100 + ns[1] - want = {'total': sum(range(k)) * (1 + 200 + 10000), 'n': n, 'bs': bs, 'a': a, 'b': b, 'i': i} + want = {'total': sum(range(k)) * (1 + 200 + 10000), 'n': n, 'bs': bs, 'a': a, 'b': b, 'i': i, 'kept': kept, + 'keyed': keyed, 'j': k + 100} assert state['fault'] == 'none' and {key: state['states'][key] for key in want} == want, (k, state['states']) return events -- cgit v1.2.3