aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authoreunhhu <[email protected]>2026-10-19 07:11:50 +0100
committereunhhu <[email protected]>2026-10-19 07:11:50 +0100
commit11960e4e2353dc63660362660901183a2585cb08 (patch)
tree127d8a857819dcc11e5c9fbc6a8ea8dc5e2f7c8c
parent24e3994b4c845831f867430cc4bb4c6e1e564a6d (diff)
downloadaup-11960e4e2353dc63660362660901183a2585cb08.tar.gz
aup-11960e4e2353dc63660362660901183a2585cb08.zip
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
-rw-r--r--compiler/compile.c141
-rw-r--r--tests/check.py51
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