aboutsummaryrefslogtreecommitdiffstats
Commit message (Collapse)AuthorAgeFilesLines
* Write a library's record as the app does in every hint, and post its rows ↵eunhhu2026-10-043-12/+34
| | | | | | | | | 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.
* Say what library records showed (002)eunhhu2026-10-042-4/+16
| | | | | | | The task list's module is the same with its record in a library (2,509 bytes); one record of a library's three goes into a module using one; a migration of the same library moves what the app stored; eleven diagnostics. The example compiles.
* Let a library declare records, written LIBRARY.NAME (005's 4)eunhhu2026-10-042-28/+246
| | | | | | | | | | | | | 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.
* Say how a library declares records, and what it must show (005's 4)eunhhu2026-10-041-0/+42
| | | | | | | | | A library declares records as it does fns: an app writes store.User, which is User in its module (Pure's record names are identifiers, one namespace with states), so a record moved into a library leaves the module as it was and another module reading the same store has the same record. Only the library records an app's lists and rows use go into its module; a record value is matched to its declaration.
* Give aup an i64: exact integers, and literals typed by what they meeteunhhu2026-09-305-126/+493
| | | | | | | | | | | | | | 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-308-136/+328
| | | | | | | | | | | | | | | | | | | | | | | 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-303-4/+25
| | | | | | | | | | 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-303-10/+219
| | | | | | | | | | | | | | | | | | | | | | | | 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-303-2/+56
| | | | | | | | | | | | 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.
* Allow 16 records and 128 record fieldseunhhu2026-09-282-2/+2
|
* Slice a keyed list by its keyseunhhu2026-09-283-5/+51
| | | | | | | | | | 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-283-8/+19
| | | | | | | | 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-283-3/+109
| | | | | | | | | | | 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-283-4/+119
| | | | | | | | | | | | | | | | 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-283-14/+235
| | | | | | | | | | | | | | | | | | | 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.
* Compile a query against an app (aup query)eunhhu2026-09-281-11/+58
| | | | | | | | aup query APP QUERY -o OUT compiles APP's declarations (records, states, inputs, handlers) with the views QUERY adds, as one module: the node checks that its states are the app's and runs the query's view on a snapshot of the app (PUT and GET /queries/NAME in pure-serve). QUERY may be - for standard input.
* Let a fold in a column read the column's roweunhhu2026-09-282-6/+6
| | | | | | | | | | 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-272-1175/+4
| | | | | | | 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.
* Name aup's one part: compiler/eunhhu2026-09-274-2/+3
| | | | | | As in Pure and Vega, the top level directories are the parts; aup has one, the compiler, checked end to end by tests/check.py.
* Build Pure's hosts from its top leveleunhhu2026-09-273-15/+15
| | | | | | | 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-272-3/+149
| | | | | | | | | 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-273-23/+135
| | | | | | | | | | | 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-273-21/+151
| | | | | | | | | | | | | | | | | | 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.
* Raise the program limits to Pure's: 65,535 instructions, 4,096 stringseunhhu2026-09-262-7/+11
| | | | | | The limit's diagnostic now comes from fns that double what they inline, and a module of some 12,000 instructions (200 KB) must compile and run.
* Check served timers and the time events seeeunhhu2026-09-261-0/+51
| | | | | | | A served app ticking every 50 ms counts its beats and stamps a posted event with the server's clock, on one thread and with an owner, and a restarted server goes on counting from the stored beats.
* Add time: now, every timers, floor and %eunhhu2026-09-263-19/+144
| | | | | | | | | | now is the time of the event a handler runs for (milliseconds since 1970, UTC), which a host gives and a script can replay; views cannot read it. input tick = every 1 second declares a timer (ms, second, minute or hour; 1 ms to a day). floor(x) and x % y, taking the divisor's sign as Python does, take times apart. Checked against a Python model over 3,600 random steps; nine diagnostics.
* Declare number and text message fields; check JSON bodieseunhhu2026-09-263-11/+126
| | | | | | | | | input add = message { title: text, points: number } now compiles, with variable fields anywhere and several of them. The served-app checks post 300 random JSON objects (escapes, control bytes, Korean, surrogate pairs, members in any order) and read them back as Python's json module does, with a list of bodies that must be refused and why.
* Check the served app with an owner thread tooeunhhu2026-09-261-12/+17
| | | | | | | The random HTTP requests against the model and the kill -9 while clients post also run with three connection threads handing requests to the app's owner.
* Check an app served by its views and inputseunhhu2026-09-261-0/+174
| | | | | | | | | A keyed-list app is served by pure-serve and driven by random POSTs (a message with query fields and a body, a text, a key) and GETs (a number, a text, pages of a table): statuses, fault names, bodies and JSON rows match a Python model, and every acknowledged event survives a restart and a kill -9 while six clients post.
* Add keyed lists and text comparisoneunhhu2026-09-265-14/+323
| | | | | | | | | | | | | state xs: list of R by field keeps its items in that field's order, each key once: xs.add(R { ... }) places an item (a taken key fails the event with exists), xs[key] finds one by halving (its field, or its index), xs.has(key) asks, xs.remove(key) removes. append and insert are refused on a keyed list, add on another, and a key field is never assigned. Texts compare with == != < <= > >= by their bytes. Checked against a Python model over 3,600 random steps with a text-keyed and a number-keyed list; 13 diagnostics; examples/tally.aup.
* Add aup watch: build again when what a build read changeseunhhu2026-09-264-22/+223
| | | | | | | | | | A build records every path it opened or looked for and a digest of the bytes there, so a rebuild happens exactly when one of them reads differently: the source, a library (the one with the error too), or a library appearing where one was searched. A change is built once it reads the same twice in a row, so a file caught halfway through being written is never built; the module is replaced whole through a rename.
* Find an item with first(t in tasks where ...)eunhhu2026-09-263-15/+28
| | | | | | | | | | first(item in list [where condition]) is the index of the first item the condition keeps, typed as an index of that list: at = first(...), a field through first(...).title, or its .position. With no such item a view has no value and a handler fails not_found. A fold's result now takes member access like a call's. The random model check jumps to the first open task and shows its position; one diagnostic joins.
* Filter a table view with whereeunhhu2026-09-263-7/+38
| | | | | | | | | | | view open_list = each t in tasks where !t.done { ... } has a row for each item the condition keeps, in the list's order; the condition compiles to the table's filter block, generated like a column so it reads the row's item, and the view entry names the list in the byte after its type. A count, sum, max or min inside the condition is a compile error, as Pure refuses a fold in a filter. The random model check now compares a filtered table too; two diagnostics join.
* Add count, sum, max and min over a listeunhhu2026-09-263-5/+229
| | | | | | | | | | | | | | | count(t in tasks where !t.done), sum(t.points for t in tasks), and max and min in the same form compile to Pure's FOLD: the value and the where condition become a block run once per item, as a column of the list, so views count and add without a copy of the list or a state kept in step. max(a, b), min(a, b) and fns of the same names stay calls, told apart by the `for` after the first expression or the `name in` after count(. Inside the block only its item and states can be read; a parameter of the fn outside, the enclosing column's row, dt and a fold inside a fold are compile errors that say why. The checks run 3,600 random steps against a Python model and add seven diagnostics.
* Declare marks: cursors that stay before inserted texteunhhu2026-09-264-7/+17
| | | | | | | | `state from: mark in note` is a cursor that stays before text inserted at its position; `cursor in` goes after it, as a caret does. Everything a cursor takes, a mark takes too. The notes example's mark is now one, and its reference model agrees: typing at the mark leaves it in place.
* Check that lent answers count against the budgeteunhhu2026-09-261-2/+44
| | | | | | | Two readers that stop reading keep one 1 MiB answer each waiting on their sockets; a third read must get 503 payload_budget until one of them goes.
* Check that reads lend a stable versioneunhhu2026-09-261-2/+35
| | | | | | A 1 MiB read sent over a slow socket must carry the version it read while the document is replaced five times meanwhile.
* Check the module server on three shardseunhhu2026-09-261-10/+13
| | | | | | Server parity and the kill -9 durability check also run with pure-serve --threads 3.
* Check pure-serve, pipelined connections includedeunhhu2026-09-262-7/+66
| | | | | | | | The server checks run pure-serve, which replaced the module mode of the old document server. A keep-alive client and pipelined requests (sent in one write, and in writes that cut heads and bodies) must get the same answers, in order, as separate connections.
* Check the module server and saving in burstseunhhu2026-09-262-5/+155
| | | | | | | | | | The HTTP document server running editor.aup must answer random requests exactly as the server running editor.pure (status, revision, error and body); concurrent edits must share commits; and every edit it acknowledged must survive kill -9. Every other stored run of the list app and the text editor saves in bursts and still matches its reference model across restarts.
* Check declared state across restartseunhhu2026-09-253-1/+66
| | | | | | | | The list app and the text editor run on a tree store through pure-run --store, restarting at random points, and must keep matching their reference models. The language notes state that a declared state is a stored state when the host gives a store.
* Record Vega's first component in the planeunhhu2026-09-252-2/+2
|
* Add fns and librarieseunhhu2026-09-259-51/+427
| | | | | | | | | | | | | | | | fn name(params) = value declares one expression, inlined where it is called: arguments are computed once onto the stack and the body reads them with PICK, so a fn costs no runtime call and keeps Pure's bound on work. Parameters take any kind of value; recursion is a compile error. use name reads name.aup beside the source or from AUP_PATH. A library declares only fns and uses, sees only its parameters, and is called as name.fn(...). Diagnostics now carry the file they are in, and the compiler takes a loader so that sources come from the host. examples/words.aup is such a library; examples/tasks.aup uses it and still matches its reference model.
* Add conditional expressions and table viewseunhhu2026-09-256-30/+270
| | | | | | | | | | | | | | | | | if c { a } else { b }, with else-if chains, is an expression anywhere a value goes. Only the chosen branch runs, every branch gives the same type, and nesting that would pass Pure's limit of 8 value-carrying joins is a compile error. view name = each row in list { column = value ... } declares a table with a row per item; columns read their row as an index of the list. An index now reads and writes its item's fields directly (at.title, at.done = 1), so 'position' cannot name a record field. examples/tasks.aup gains conditional views and a board table and still matches its reference model; a new check confirms the unchosen branch is never computed.
* Emit Pure module format v5eunhhu2026-09-252-5/+5
| | | | | | | Pure now loads only v5 modules, where a view entry's type is one byte followed by the table it belongs to. Plain views encode as before, so the output changes only in its magic. The view limit follows Pure's to 64.
* Record the input limit of 32 in the language noteseunhhu2026-09-251-1/+1
|