aboutsummaryrefslogtreecommitdiffstats
path: root/tests
Commit message (Collapse)AuthorAgeFilesLines
* Targets together compiled straight when they can be: S_TOGETHER holds the ↵eunhhu29 hours1-2/+13
| | | | targets with their values; when no value may read what a target before it changes (a target's name, a fn's call, a parameter or a binding, an index or a for's item of a changed list), each target takes its value in order with no lets, else the values go through lets first as before; layout.aup's boxes 1,525.9 instructions a node through lets, 1,492.8 straight (the hand-written 1,493.9); frictions reads a changed item through a let's index and a for's item (a compiler always straight fails it)
* Lists declared together: three names or more made the statements a cycle ↵eunhhu29 hours1-2/+4
| | | | (each copy took the first's next, the copy before it), so the compiler never ended (found writing linebreak.aup's nine lists as two lines); frictions declares four
* Hex literals are numbers where numbers go without a core change (140's UAX ↵eunhhu29 hours1-2/+10
| | | | #14 friction): 0x... compared with a number, in its arithmetic, in min and max, given to a number's state, let or field is that number, past 2^53 refused; alone or meeting an i64 it is an i64 as before, bit operations as before; frictions in make check, two diagnostics; 002 records the frictions written away; no module of aup's, Pure's or Vega's changes
* abs(x) and a timer's period without a core change (140's RFC 6298 ↵eunhhu29 hours1-2/+16
| | | | frictions): abs of a number or an i64, x once (a PICK of it, its negation, MAX: -0's is +0, i64's least overflows); tick.period, an every input's milliseconds as now counts them, a constant anywhere; frictions in make check, a diagnostic; no module of aup's, Pure's or Vega's changes
* A list given a value whole without a core change (140's frictions): xs = a ↵eunhhu29 hours1-11/+40
| | | | 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
* Names together without a core change (140's frictions): lets' lists declared ↵eunhhu29 hours1-0/+83
| | | | 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
* A message's rows read by position where they are, not from a copy of them: ↵eunhhu30 hours1-0/+58
| | | | input.rows[k] made the rows a list value (LIST_VALUE copies every row) and read the copy, so each read cost the rows' count (140's box layout: 99,067 ns a node, 4,737 now, the answers the same); rows_at in make check (a literal, a computed and a looped position, through a let and a fn's argument, past the last failing the event); no other module of aup's, Pure's or Vega's changes
* Bytes alone as a state (Pure 028): state x: bytes (= "..." its UTF-8), held ↵eunhhu32 hours1-0/+69
| | | | as a text state is and written to the module as type 6, its name read as a bytes value and x = ... setting it whole (a string meets bytes), cursors and marks in it, its insert and remove, its start and end refused with a bytes state's diagnostic, a text never set from it; test bytes_state (300 steps of random bytes with 0x00 and 0xff added and cut, started again from the store 15 times, against Python; the same name as a text from the store and by a replace: UTF-8 bytes kept, others anew, and back to bytes what the text held), three diagnostics; the 60 sources the same modules; 002
* Lets' lists in inner blocks and xs = [...] (after Pure 140's inflate): a ↵eunhhu32 hours1-2/+78
| | | | let's list in an if's, a for's, a while's or a match's block (a handler's, a fn's or a view's body's) begins empty each time its let is run, one state let.NAME a name as before (gathered from inner blocks now), emptied there a item at a time (while xs.count > 0 remove the last: a list of numbers' by its place count - 1, others' .last; Pure has no op that empties a list); xs = [...] empties a let's list and gives it its elements ([] none), a stored list refused; a list literal in an expression keeps its elements (it dropped them: a field's [1, 2] was empty); folds and filters hold no let's list; test inner_lists (100 random events: a for's list, a keyed one, a fn's, a view body's, xs = [...] and [] against Python), a diagnostic retired (an inner block's list now reads as unknown past it), one added; the 60 sources the same modules; 002
* Bytes compared (Pure's TEXT_COMPARE with PURE_COMPARE_BYTES): ==, !=, <, <=, ↵eunhhu32 hours1-0/+50
| | | | >, >= between bytes by their bytes; a string (a literal, or one with formats) where bytes are what it meets, compared or joined, is its UTF-8 bytes, as 1 is an i64 where i64s go, values of text and bytes mixing in neither (bytes(t) or text(b)); test bytes_compared (400 random pairs with 0x00 and 0xff in the six relations, literals on either side, a formatted string, joins with literals, against Python), two diagnostics; the 60 sources compiled before the same modules; 002
* Lists bare as values (after Pure 140's inflate and HTTP): a let's list or a ↵eunhhu33 hours1-1/+84
| | | | message's rows named where a value goes is a list value (Pure's LIST_VALUE, a copy of its items), given to fns, returned (several at once too), held by a let or put in a field; a list value (a parameter's, a let's from a fn, a field's) moves by its number uncopied and is read through a register (for, a fold at its height, [i], [i].field, .count, .first, .last); changing one says it is a list value, a stored list is still no value; a list where a number goes says to walk it or read .count; test bare_lists (200 random lists through fns that fold, walk, read by position and give back two lists and a list of numbers, against Python), two diagnostics (one changed: o.rows where a number goes); the 60 sources compiled before are the same modules byte for byte; 002
* Pure 027's 5d: a number, a bool and an i64 carried over each other when ↵eunhhu34 hours1-0/+36
| | | | exact, else the new default: states 5 and 2.5 (numbers), 7 and 2^53 + 1 (i64s) and a stored list's rows 3, 2.5, -0 and 1e300 (a number field) moved to i64, i64, number and bool states and an i64 field: 5, -2 (its default), 7, false (its default), rows 3, 9, 0, 9; alike on a replace while running and on two starts from the store the old module wrote (moved, then stored as moved); back to the old module, false is the i64 0
* Pure 027's 5c: {cents:.2d}, an i64 (or a number) in minor units written with ↵eunhhu35 hours1-7/+15
| | | | its last N digits after a point (1234 at .2d is 12.34, -5 is -0.05), not Python's form, so the random formats test it against Decimal(v).scaleb(-N) written as .Nf (digits on two in five d columns); digits refused off f and d (the diagnostic names .2d), an i64's .2f pointing at .2d; 002's formats say so
* Pure 027's 5b: an i64 sum exact: the largest i64, 1, -2 and 2 appended to a ↵eunhhu35 hours1-0/+10
| | | | list of i64 and its sum seen after each: the largest, overflow (no view), the largest less one (past the range midway, the answer), overflow
* Pure 027's 5a: // a floored quotient (Pure's FLOORDIV for numbers, I64_DIV ↵eunhhu35 hours1-5/+22
| | | | for i64s), at /'s precedence; / between i64s refused (an i64 divides by //, or converts: number(a) / number(b)); the three that divided i64s by / (an accepted form, the i64 app, the random programs' i64 expressions) now by //; a check of 200 random a // b and a % b against Python's, bit for bit (-0's sign too); a diagnostic (305)
* Pure 027's 4f-4: a source of any size (files and standard input read whole), ↵eunhhu36 hours1-1/+15
| | | | the compiler's tables raised to Pure's widths (4,096 states, 1,024 inputs, 4,096 views and handlers, 1,024 records, 16,384 record fields; 15 MB calloc'd, its unused rooms never touched) and its diagnostics saying so; at most 255 outputs (Pure's store counts them in a byte), a walked or folded list among the first 256 states (its argument's byte), said before Pure would refuse; a wide program checked: 301 states, 40 inputs, 70 views and 20 records in 48 KB of source, run to the last of each
* Pure 027's 4e: views of any value: view x = a list state (its items as they ↵eunhhu38 hours1-8/+33
| | | | are, their fields its columns), each ... where ... without columns (filtered as they are), a view's body returning a list it built (a state's list told to be a view as it is), bytes views and columns, list columns ({ tags: t.tags }); a view written as Pure's 20 bytes, its type a record field's (number, text, bool, i64, list, bytes); a pointing for a view of a list state as it is too; diagnostics on a view's type say bytes and lists; stored_nested checks the board's list views (as it is, filtered, a map with a list and a bytes column, a list a body built) against the model, in memory and on a store restarting; an accepted form of them
* Module format v7 (Pure 027's 4b-2): keyed documents gone from the language: ↵eunhhu41 hours1-104/+18
| | | | no bytes by key, message by key, name[key] or .replace(first .. last, value), the opcodes DOC, DOC_SIZE and REPLACE (those after them moved up), the header 38 bytes; examples/editor.aup and bounded-editor.aup gone (examples/documents.aup does their work in a keyed list), and with them the parity with the earlier editor (documents_model checks the new one); diagnostics on documents rewritten for plain messages or gone (304); a module's records counted at their v7 place; a field changed by '=' said so where a call was written
* Bytes in a record field (Pure 027's 4b-1): body: bytes (= "..." its ↵eunhhu42 hours1-1/+70
| | | | default's bytes), x.body.bytes its length (LEN), a text going there only as bytes(t); no list keyed by bytes or by a list; examples/documents.aup: keyed byte documents as a keyed list of records, each edit checked against the revision it was made on; documents_model: random shows and edits (stale revisions, past the end, any bytes, up to 5,000 at once) against a model, in memory and on a store restarting six times (half of the runs in bursts), four seeds alike (1,280 steps); four diagnostics
* A message's rows hold lists (Pure 027's 4a): a record holding lists is a ↵eunhhu42 hours1-2/+2
| | | | message's rows as a let's, a stored and a transient list's; appending into the list of a message's row is refused (a message's rows are read alone), its diagnostic saying so; diagnostic 37 now that one
* Stored nested state (Pure 027's stage 3): stored and transient lists of ↵eunhhu43 hours1-1/+136
| | | | records holding lists (a message's rows not yet); xs[i].items.append(...) into a stored item; xs[i].field = a let's list, [] or a list value; an item of the same record appended as it is (ys.append(y)); a test against a model: columns of cards of tags under random steps in memory and on a store restarting eight times (half of the runs saving in bursts), six seeds, every step's fault, states and a view reading the lists alike (2,400 snapshots, 54 restarts); the stored list diagnostic now the message's rows'
* Fns that call themselves, blocks, several values, records' values and ↵eunhhu44 hours1-3/+152
| | | | characters (Pure 027's stage 2c): a fn with a block (statements, return a, b; return alone gives nothing) or one a cycle of calls goes through runs as Pure's function (CALL), once for each kinds of arguments it is called with, in code of its own after the handlers' and views'; its parameters held by name as it begins, its let's lists, held values and registers each call's own (Pure's); -> types where it calls itself before a return says what it gives, a return's values wanted as those types; f(x) as a statement where it gives nothing, let a, b = f(x) and a, b = f(x) (lets and states) where it gives several; a view, a count, sum, max or min calls none; records' and variants' values: R { ... }, V.case(x), V.case and calls giving one onto the stack field by field, held by a name (name.field lets), passed and given back, matched (a case's value named, read only), appended (a call's value held first), an item and a record under an item's field passed as values (a function has its own lists), a list a value holds walked through a register, a list in a local (Pure's local type 4), R { ... } written where a let, an assignment, a return or an argument takes a value; never-run lines after a return or a break refused, no jump past an else whose then stops, the last arm of a match that names every case taken without asking; for ch in t: a text's characters as code points (a let's, a parameter's, a state's, a record's field, a message's text field), char(n); examples/json_tree.aup by recursive descent and a depth-first walk; 13 diagnostics, 2 accepted forms, a functions test against Python (40 random trees built and summed recursively, deepest and leaves as two values, 60 random texts' characters, a call without end failing as memory with nothing changed); the 2b compiler's modules for 49 sources byte for byte the same (json_tree.aup's match aside); three diagnostics' wording follows records being values
* Records in records, variants, lists in records and match (Pure 027's stage ↵eunhhu7 days1-3/+53
| | | | 2b): a field may hold a record or a variant (flattened into paths: at.x; a variant's case a field of its own, its value at name.case) or a list of anything (list of Json, a variant holding itself through a list); variant V { case, case(type) } and V.case(value) where items are made; match item { case(name) => ... _ => ... } over an item of a variant or a variant in a field (the binding a field, a list read through a register, or fields under it; missing cases refused); x.items.append(...), xs.last.items.append, xs[i].items.append; for, folds, count, first, last and [i] over a list in a field; xs[i] an item of an unkeyed list; number(t) of a text; examples/json_tree.aup; 16 diagnostics, an accepted program, a nested values test
* Formats, joins, slices, lets of bytes and text, while and && and || that ↵eunhhu7 days1-2/+103
| | | | decide alone (Pure 027's stage 2a): {v:08x}, {p:.2f} (Python's mini-language, [0][width][.digits][d x X b o f]), a + b of texts or of bytes, b[a .. c] of bytes and of texts (a cut inside a character failing as encoding), byte(n), let s = bytes or text (16 of each a handler, s = s + e appending in place), while c { } as a count to 2^53 that breaks when c is false, && and || computing their right side only where it decides (an AND or OR still where it cannot fail: comparisons, states and fields, a walked item's fields, a number plus a small literal, what the left side computed; every module of the apps and benchmarks byte for byte the same but json's); examples/json.aup, examples/sha256.aup in formats and joined bytes; 14 diagnostics, one accepted form, 144 random formats against Python's format() and an app cutting at commas
* Bits, bytes and lists of numbers (Pure 027's stage 1): hex literals (i64s), ↵eunhhu7 days1-0/+135
| | | | & | ^ ~ << >> on i64s by Python's precedence (a shift's count a number or an i64), b[i] the byte at a position of bytes (a field's, an argument's, a parameter's, bytes(t)), list of i64 (number, bool, text) as records of one field whose items read as their values, a let's list begun with [elements], xs[i] read and written by position (not_found past the end), bytes(xs), and a, b = x, y and let a, b = x, y given at once; examples/sha256.aup; 12 diagnostics, one accepted form, 192 random bit expressions against Python's integers and an app of bytes and lists
* Give a view a body that computes and returns its value (Pure 018)eunhhu2026-10-041-0/+21
|
* Count with for i in a .. b, break, nest fors and let an event's own lists ↵eunhhu2026-10-041-2/+117
| | | | (Pure 018)
* Declare transient state: held while the app runs, never stored (Pure 018)eunhhu2026-10-041-0/+61
|
* Write a library's record as the app does in every hint, and post its rows ↵eunhhu2026-10-041-7/+29
| | | | | | | | | over HTTP The hints of keyed lists, has and a whole item named a library's record by its plain name, which the app cannot write: they say store.User now. library_records posts a message whose rows are a library's records to pure-serve (make check builds it) and reads them back.
* Let a library declare records, written LIBRARY.NAME (005's 4)eunhhu2026-10-041-1/+87
| | | | | | | | | | | | | A library declares records as it declares fns; the app writes them store.User in its lists, rows fields and record values, and they are User in its module: Pure's record names are identifiers, one namespace with states. So the task list with its record moved into a library builds the same module, byte for byte, and a migration module of the same library reads and changes what the app stored. Only the library records an app's lists and rows hold go into its module, after the app's own, library by library in declaration order; a record value is matched to its declaration, not its name, and two records of one name in a module are refused.
* Give aup an i64: exact integers, and literals typed by what they meeteunhhu2026-09-301-22/+140
| | | | | | | | | | | | | | state, record, message, output and argument fields, lets, views, keys and slices can be i64 (Pure 016 stage 2). An integer literal becomes an i64 where the other side of an operation, a comparison or an assignment is one; a literal-only side follows the other, found by compiling it on trial and rolling back. + - * and negation fail the event with overflow, / and % floor, and i64(x) and number(n) are the only way between the two, so a mix is a diagnostic that says so. The check adds 18 i64 diagnostics, i64 accepted forms (with a 30-deep chain that must stay linear), an event-by-event program past 2^53 and at the edges, and i64 values in the generated programs.
* Give aup a bool: true and false, and conditions that take oneeunhhu2026-09-301-48/+166
| | | | | | | | | | | | | | | | | | | | | | | A condition was any number, non-zero being true: done was a number field, a view showed 1 for true, and done + 1 was no type error. Now bool is its own type (Pure 016, module format v6): - true and false; comparisons, text comparisons, has and ! && || give a bool. Bools are equal or not; they are no numbers and have no order. - if, conditional expressions, require, where (folds, walks, filtered tables) take a bool. Given a number, the diagnostic says how to make one: "if takes a bool, not a number: compare it, as in n != 0". - Bool states (state on = false), record fields (done = false), message, output and argument fields (ok: bool), lets, views and columns. Interpolation writes true or false. A list is not keyed by a bool. - The examples' flags are bools (charge's holding, toggle's visible, tasks' done). Checks: 20 bool diagnostics, a form using bools everywhere, an app whose every output shows true and false (states, items, views, columns, text) and whose replacement turns a number into a bool and back. Models hold bools and compare them strictly: Python's True == 1 no longer hides a 1 where a true belongs. Generated programs are typed, numbers and bools.
* Let declared fields be apart by commas, as a value's areeunhhu2026-09-301-0/+7
| | | | | | | | | | A message's, a group's and a record's fields took a newline or a semicolon after each, while a value's (Kept { id: 1, t: "a" }, send) take commas, and the language's own examples declared message { title: text, points: number } and to: { city: text, zip: u32 }, which did not compile. Now a comma, a newline or a semicolon ends a declared field too (one after the last as well); without one it says so.
* Spread an item or a message into a record or a send, and set an item wholeeunhhu2026-09-301-0/+42
| | | | | | | | | | | | | | | | | | | | | | | | 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.
* Write a message once for its output and the input that takes iteunhhu2026-09-301-0/+18
| | | | | | | | | | | | Growing a board two nodes share (Pure's research 105), one field took five places, two of them the same list: the output a node sends its changes on and the input it takes the other's with. A message may now take the fields of a message input or output declared before, in their order: input remote = message like changed. The module is the same as with the fields written out, byte for byte (checked). An unknown name, one that is no message, the message itself, and an output taking an input's rows are diagnosed.
* Slice a keyed list by its keyseunhhu2026-09-281-0/+8
| | | | | | | | | | list[low .. high], as a fold's or a for's list, is the items whose keys are from low up to before high (a list keyed by a number), read by key: distinct(l.item for l in lines[o * 16 .. o * 16 + 16] where stock[l.item].quantity < t) reads one order's lines, whatever its test does outside them. It compiles to Pure's sliced folds and walks; an empty for over a slice still computes its keys. Two diagnostics and an app of slices are checked.
* Count distinct valueseunhhu2026-09-281-1/+7
| | | | | | | | distinct(value for item in list [where condition]) counts the different values the included items give, compiled to Pure's fold of kind 5 (SQL's count(distinct ...)). An accepted app walks and folds a keyed list in a range of its key (where l.id >= start && l.id < start + 16), which Pure reads by key.
* Name a number or an index with leteunhhu2026-09-281-0/+21
| | | | | | | | | | | let name = value, in a handler, names a number or an index for the rest of its block; name = value gives it a new value of the same kind; the walks and folds inside read it. It compiles to Pure's locals (a slot an event, 0 when it begins, never saved), a slot a let in a handler, so an order's loop sums into a let and takes stock through an index let without finding the item again. A let may not be a declared name, another let in scope or the walked item, and holds no text. Nine diagnostics and an app of lets are checked.
* Give views argumentseunhhu2026-09-281-0/+20
| | | | | | | | | | | | | | | | view name(argument: type, ...) = ... declares a view that takes u32, number, bytes or text arguments, read by name in the view, in a table's filter and columns and in the folds inside them (compiled to FIELD of an arguments input named name.args that names its view, Pure's kind 6). A host gives them as the request's query: GET /views/balance?aid=42, and a table pages as before with from and count. A query compiled with aup query may take arguments too. An argument's name may not be a state or another declaration, nor the table's row, nor from or count (which page a table). Eight diagnostics and an app of views with arguments (a keyed lookup, a fold, a text, a table filtered by two of them whose column folds with one, a handler's fold reading its message) are checked.
* Walk lists with for, and take a message's rowseunhhu2026-09-281-1/+26
| | | | | | | | | | | | | | | | | | | for item in list [where condition] { ... } runs its block in a handler once for every item the list has, in order, the item an index of the list (read, changed, removed: a removed item does not skip the next). It compiles to Pure's EACH; a fold inside reads the walk's item as a fold in a column reads its row. A for adds nothing to the list it walks, holds no other for and does not reply, so it ends. A message field may be a list of a record (lines: list of Line): its rows arrive as a JSON array of objects and fill a list of their own, named order.lines in the module, which the handler walks, folds and counts (order.lines.count) and which is emptied when the event ends. So a transaction over many rows (an order taking stock line by line, all or nothing) and a bulk insert are one event. An output sends no rows. Nine diagnostics and an accepted app (rows in a group, where, a fold over the rows reading the walk's item, a walk that removes) are checked.
* Let a fold in a column read the column's roweunhhu2026-09-281-2/+2
| | | | | | | | | | A count, sum, max or min inside a table's column may now name the row the column is evaluated for: per branch, the sum of its accounts (sum(a.balance for a in accounts where a.bid == b.bid)), or per account, how many are richer. The column's row inside the fold compiles to OUTER_ROW, which Pure's verifier accepts only in a fold that runs in a column; the fold's own item stays ROW. This is a correlated subquery, the form SQL writes GROUP BY and self-joins with.
* Leave pure-serve's checks to Pureeunhhu2026-09-271-1173/+2
| | | | | | | aup's check keeps what it owns: the language, the compiler, and apps written in aup matching their references on Pure's loader. The HTTP host's checks moved to Pure's tests/check.py, which builds its apps with this compiler.
* Build Pure's hosts from its top leveleunhhu2026-09-271-6/+6
| | | | | | | Pure now keeps the app model, the store and pure-serve in core/, store/ and serve/ with one top-level make; the earlier document server the parity checks compare against stays in prototypes/connections.
* Check a payments API's answers coming back as events exactly onceeunhhu2026-09-271-3/+146
| | | | | | | | | A Python payments API answers 200 or 402 and repeats a key's first answer; the sender's output is wired to it and its answers to the charged input. With the sender killed (-9) while clients buy, the app's paid and declined counts and paid total match the API's answers one per key. The language notes say how answers arrive.
* Check delivery to a third-party HTTPS webhookeunhhu2026-09-271-0/+87
| | | | | | | | | A Python HTTPS server with its own certificate stands for a webhook: five orders arrive in order with the output's JSON shape and the sender's headers, one answered 503 at first arrives again, and a sender that does not trust the certificate delivers nothing and says so.
* Check that orders cross between served apps exactly onceeunhhu2026-09-271-0/+148
| | | | | | | | | | Two served apps, the sender's output wired to the receiver's input (pure-serve --send): four clients order while the receiver and then the sender are killed (-9) and started again on their stores; the receiver's count and total end equal to what the sender saved, an order the receiver refuses is delivered once and holds nothing back, and every acknowledged order is among the saved ones.
* Declare outputs and send them: output ordered = message { ... }eunhhu2026-09-271-1/+16
| | | | | | | | | | | An output is a message the app sends, with fields and groups as a message has; send ordered { item: x, to: { city: y } } gives every field once, in any order, groups in their own braces, and compiles to Pure's SEND. Diagnostics cover a missing field, a group given a value, an unknown field, a mistyped value, a send to a message, a handler for an output, a field given twice and reading an output as a state; kinds in messages now carry their article (an output, a key input).
* Group message fields: to: { city: text }, read as ship.to.cityeunhhu2026-09-271-1/+73
| | | | | | | | | | | | | | | | | | A message's field block may hold groups, nested as deep as the 31 characters of a path allow; the compiler flattens them into fields named by their path (to.city), which Pure's module format takes, so groups cost nothing at run time. A member chain on a message names a field by its path; one that stops at a group, or names a field the group lacks, is diagnosed, and a field's own member (m.text.bytes) still reads the field. A module may have 64 message fields. The JSON server check posts 100 random bodies with groups two deep (members shuffled at every level, strings with escapes, control bytes, Korean, emoji and U+2028) to pure-serve, which reads them in place, and compares the answers and the table with a Python model; a group's fixed field from the query and ten malformed group bodies are checked too. Six diagnostics cover groups.
* Check reads from snapshots while a served app is killedeunhhu2026-09-261-4/+25
| | | | | | | | | | | | pure-serve now always runs an app with states on an owner thread and shows views from the snapshot of its last save (Pure 080), so the server check expects an owner with one connection thread too and views answered from snapshots. While six clients post and the server is killed, two readers on other connections now check that each read sees every point acknowledged before it was asked, that the total never goes back, and that the total after the restart is at least any total they saw: nothing unsaved was shown.
* Check lent views: whole versions, and every loan given backeunhhu2026-09-261-0/+89
| | | | | | | | | | Views of a 1 MiB note are pipelined on one connection, more than the kernel takes, while the note is replaced: each answer is one whole version, the store mapping the waiting answers hold stays (through a compaction too) and is unmapped once they are read, or once the reader leaves without reading, and nothing stays charged. On one thread and with an owner.