From 24e3994b4c845831f867430cc4bb4c6e1e564a6d Mon Sep 17 00:00:00 2001 From: eunhhu Date: Mon, 19 Oct 2026 07:05:44 +0100 Subject: Names together without a core change (140's frictions): lets' lists declared together (let a, b: list of R, a let each, empty as it begins; given elements refused), and targets together with fields and items among them (xs[i].x, xs[i].y = a, b; a, xs[i] = ...): the values first, each into a let of its own (a literal straight), then each target left to right as Python's; names alone are S_ASSIGNS as before; frictions in make check, two diagnostics; no module of aup's, Pure's or Vega's changes --- compiler/compile.c | 144 +++++++++++++++++++++++++++++++++++++++++++++++++---- tests/check.py | 83 ++++++++++++++++++++++++++++++ 2 files changed, 217 insertions(+), 10 deletions(-) diff --git a/compiler/compile.c b/compiler/compile.c index 5c88b97..eab1b3c 100644 --- a/compiler/compile.c +++ b/compiler/compile.c @@ -1162,7 +1162,7 @@ static bool path_append(Compiler *c, Stmt *s, const Token *name) { } /* , b, c = x, y, z after the first name: every name, then as many values on the line. */ -static void several(Compiler *c, Stmt *s, const char *first) { +static void several_names(Compiler *c, Stmt *s, const char *first) { s->names = allocate(c, STACK * sizeof(*s->names)); memcpy(s->names[s->name_count++], first, NAME); while (is(c, ",")) { @@ -1174,6 +1174,10 @@ static void several(Compiler *c, Stmt *s, const char *first) { if (!strcmp(s->names[i], name.text)) fail_at(c, name.line, name.column, "'%s' is named twice", name.text); memcpy(s->names[s->name_count++], name.text, NAME); } +} + +/* = x, y (after the names): every name's value, or one call that gives them all. */ +static void several_values(Compiler *c, Stmt *s) { expect(c, "=", "'='"); Expr **tail = &s->value; unsigned values = 0; @@ -1189,6 +1193,99 @@ static void several(Compiler *c, Stmt *s, const char *first) { fail_at(c, s->line, s->column, "%u names take %u values, not %u", s->name_count, s->name_count, values); } +/* A target of a, xs[i].x = ...: a name (S_ASSIGN), a list's item's field or its item at a position (S_SET), or an + * index's field (at.x, S_SET); its value comes later. */ +static Stmt *target(Compiler *c, Token name) { + Stmt *t = allocate(c, sizeof(*t)); + t->line = name.line, t->column = name.column; + memcpy(t->name, name.text, NAME); + if (is(c, "[")) { + take(c); + t->kind = S_SET; + t->first = expression(c, 0, true); + newlines(c); + expect(c, "]", "']'"); + if (is(c, ".")) { + take(c); + Token field = expect_name(c, "a field"); + memcpy(t->member, field.text, NAME); + } + return t; + } + if (is(c, ".")) { + take(c); + Token field = expect_name(c, "a field"); + t->kind = S_SET; + t->first = node(c, E_NAME, name.line, name.column); + memcpy(t->first->name, name.text, NAME); + t->name[0] = 0; + memcpy(t->member, field.text, NAME); + return t; + } + if (reserved_name(name.text)) fail_at(c, name.line, name.column, "'%s' is reserved", name.text); + t->kind = S_ASSIGN; + 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; + Expr *values[STACK]; + unsigned count = 0; + for (Expr *v = s->value; v; v = v->next) values[count++] = v; + Stmt *last = s; + Expr **link = &s->value; + unsigned kept = 0; + for (unsigned i = 0; i < n; ++i) { + Expr *v = count == n ? values[i] : NULL; /* NULL: one call gives them all */ + if (v && (v->kind == E_NUMBER || v->kind == E_BOOL || v->kind == E_TEXT)) { + v->next = NULL; /* a literal reads nothing: straight to its target */ + targets[i]->value = v; + } else { + memcpy(s->names[kept++], s->names[i], NAME); + Expr *given = node(c, E_NAME, s->line, s->column); + memcpy(given->name, s->names[i], NAME); + targets[i]->value = given; + if (v) *link = v, link = &v->next; + } + last = last->next = targets[i]; + } + if (count == n) *link = NULL; + s->name_count = kept; + if (kept == 1) { /* one let: as let x = value */ + s->kind = S_LET; + memcpy(s->name, s->names[0], NAME); + s->names = NULL, s->name_count = 0; + } + return kept ? s : s->next; /* every value a literal: no lets */ +} + static Stmt *statement(Compiler *c, unsigned depth) { Token token = expect_name(c, "a statement or '}'"); Stmt *s = allocate(c, sizeof(*s)); @@ -1219,10 +1316,13 @@ static Stmt *statement(Compiler *c, unsigned depth) { memcpy(s->name, name.text, NAME); s->line = name.line; s->column = name.column; - if (is(c, ",")) { /* let a, b = x, y */ - s->kind = S_LETS; - several(c, s, name.text); - return s; + if (is(c, ",")) { /* let a, b = x, y, or let a, b: list of R (a list each, empty as it begins) */ + several_names(c, s, name.text); + if (!is(c, ":")) { + s->kind = S_LETS; + several_values(c, s); + return s; + } } if (is(c, ":")) { /* let name: list of R [by field]: the event's list, empty as it begins */ take(c); @@ -1239,6 +1339,20 @@ static Stmt *statement(Compiler *c, unsigned depth) { s->key_column = key.column; } s->listed = true; + if (s->name_count > 1) { /* the names after the first: lets of their own, of the same list, after it */ + Stmt *last = s; + for (unsigned i = 1; i < s->name_count; ++i) { + Stmt *other = allocate(c, sizeof(*other)); + *other = *s; + other->names = NULL, other->name_count = 0; + memcpy(other->name, s->names[i], NAME); + last = last->next = other; + } + s->names = NULL, s->name_count = 0; + if (is(c, "=")) + fail_at(c, c->token.line, c->token.column, "lists declared together begin empty: give each its elements apart"); + return s; + } if (is(c, "=")) { /* = [a, b, ...]: what it holds as it begins, appended in order */ take(c); Token open = expect(c, "[", "'[' and the list's elements"); @@ -1435,6 +1549,9 @@ static Stmt *statement(Compiler *c, unsigned depth) { } if (is(c, "(")) fail_at(c, field.line, field.column, "a field changes by '=', as in %s[at].%s = value", s->name, field.text); memcpy(s->member, field.text, NAME); + if (is(c, ",")) return together(c, s); /* xs[i].x, xs[i].y = a, b */ + } else if (is(c, ",")) { + return together(c, s); /* xs[i], ys[j] = a, b */ } else if (is(c, "=")) { /* tasks[at] = Task { ... }: every field of the item (no member); xs[i] = 5 */ take(c); s->value = inserted_value(c, false); @@ -1450,6 +1567,14 @@ static Stmt *statement(Compiler *c, unsigned depth) { if (is(c, ".")) { take(c); Token method = expect_name(c, "insert, remove, append or a field"); + if (is(c, ",")) { /* at.x, at.y = a, b */ + s->kind = S_SET; + s->first = node(c, E_NAME, token.line, token.column); + memcpy(s->first->name, s->name, NAME); + s->name[0] = 0; + memcpy(s->member, method.text, NAME); + return together(c, s); + } if (is(c, "=")) { /* at.field = value: a field of the item an index names. */ take(c); @@ -1488,10 +1613,9 @@ static Stmt *statement(Compiler *c, unsigned depth) { expect(c, ")", "')'"); return s; } - if (is(c, ",")) { /* a, b = b, a */ - s->kind = S_ASSIGNS; - several(c, s, token.text); - return s; + if (is(c, ",")) { /* a, b = b, a; a, xs[i].x = 1, 2 */ + s->kind = S_ASSIGN; + return together(c, s); } s->kind = S_ASSIGN; expect(c, "=", "'='"); @@ -1508,7 +1632,7 @@ static Stmt *block(Compiler *c, unsigned depth) { while (!is(c, "}")) { if (c->token.kind == T_EOF) fail_at(c, c->token.line, c->token.column, "expected '}'"); *tail = statement(c, depth); - tail = &(*tail)->next; + while (*tail) tail = &(*tail)->next; /* let a, b: list of R is a let each */ end(c); } take(c); diff --git a/tests/check.py b/tests/check.py index e299911..72dee99 100644 --- a/tests/check.py +++ b/tests/check.py @@ -77,6 +77,9 @@ DIAGNOSTICS = [ ('state y: i64\nview v = 0xffffffffffffffff + y\n', '2:10', "the number is past an i64's range"), ('state a = 0\nstate b = 0\non frame {\n a, b = 1\n}\n', '4:3', '2 names take 2 values, not 1'), ('state a = 0\non frame {\n a, a = 1, 2\n}\n', '3:6', "'a' is named twice"), + ('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"), ('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'), @@ -645,6 +648,53 @@ on m { } ''' +FRICTIONS = '''record P { + x = 0 +} +record B { + x = 0 + y = 0 +} +state total = 0 +state n = 0 +state bs: list of B +state a = 0 +state b = 0 +state i = 0 +input go = message { k: u32 } +on go { + if bs.count == 0 { bs.append(B {}) } + bs.append(B {}) + bs[0].x, bs[0].y = go.k, go.k * 2 + a, bs[1].x = bs[0].y, a + 1 + i, bs[i].y = 1, 7 + a, b = b, a + let ns: list of number + ns.append(0) + ns.append(0) + ns[0], ns[1] = 3, go.k + 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 }) + } + if go.k > 1 { + let inner, outer: list of number + inner.append(go.k) + outer.append(1) + n = inner.count * 10 + outer.count + inner[0] + } + let t = 0 + for v in xs { t = t + v } + for v in ys { t = t + v * 100 } + for p in ps { t = t + p.x * 10000 } + total = t + qs.count +} +''' + INNER_LISTS = '''record Seen { node = 0 depth = 0 @@ -1732,6 +1782,38 @@ def rows_at(aup, pure_run, work, events=200): return events +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.""" + import random + import struct + path = work / 'frictions.pmod' + (work / 'frictions.aup').write_text(FRICTIONS) + compile_module(aup, work / 'frictions.aup', path) + rng = random.Random(140) + asked = [rng.randrange(0, 9) for _ in range(events)] + lines = run(pure_run, path, stdin=''.join(f"message go {struct.pack(' 1: n = 10 + 1 + k + bs += [{'x': 0, 'y': 0}] * (2 if not bs else 1) + bs = [dict(x) for x in bs] + bs[0]['x'], bs[0]['y'] = k, k * 2 + a, bs[1]['x'] = bs[0]['y'], a + 1 + i, bs[i]['y'] = 1, 7 + a, b = b, a + 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} + assert state['fault'] == 'none' and {key: state['states'][key] for key in want} == want, (k, state['states']) + return events + + def bytes_compared(aup, pure_run, work, events=400): """Bytes compared (Pure's TEXT_COMPARE with PURE_COMPARE_BYTES, after 140's DNS and HTTP): random bytes, 0x00 and 0xff among them, in the six relations, against literals on either side and a string with a format (a string where @@ -2927,6 +3009,7 @@ def main(): report['bytes_compared'] = bytes_compared(aup, pure_run, work) report['inner_lists'] = inner_lists(aup, pure_run, work) report['rows_at'] = rows_at(aup, pure_run, work) + report['frictions'] = frictions(aup, pure_run, work) report['bytes_state'] = bytes_state(aup, pure_run, work) report['stored_nested'] = stored_nested(aup, pure_run, work, range(6)) report['documents'] = documents_model(aup, pure_run, work, range(4)) -- cgit v1.2.3