commit 87d90e31a1e510d28f4448672b152fcb882c3249
parent 954028b86d02fdaa21a28baceffa45c11d354041
Author: James Eversole <james@eversole.co>
Date: Mon, 19 Oct 2026 18:52:47 -0500
Prefer layout and readable pipes in public demo sources
Remove redundant let/case/do/application wrappers, use the unparenthesized IO conversation, and simplify parser/lexer and traversal pipelines. Keep required pattern, lambda and list-item grouping and intentional grouping lessons. Correct obsolete multiline advice and establish the public-example convention in AGENTS.md.
Regenerate source descriptors and native captures. All 17 source fixtures retain identical executable bundle hashes, complete results, demand controls and IO transcripts. Parser/lexer source releases advance to 0.3.1 without modifying historical releases. Validate 129 Node tests, all 16 editable demos and beginner exercises on both production WASM backends in Chromium, and nix flake check. Compiler/support fixtures remain untouched.
Diffstat:
16 files changed, 116 insertions(+), 90 deletions(-)
diff --git a/AGENTS.md b/AGENTS.md
@@ -94,6 +94,12 @@ Before authoring or rewriting `.tri`, read [the language-feature inventory](docs
Check available syntax and library protocols before proposing new machinery or
claiming a language gap; verify uncertain behavior with the selected stable compiler.
+In public examples, prefer indented continuation and unwrapped `let`, `case`, and
+`do` blocks over layout-only parentheses. Use `<|` or `|>` when they make nested
+calls clearer. Keep parentheses where syntax requires them or grouping is the
+clearest explanation; do not contort code just to eliminate them. Both pipe
+chains associate left-to-right, so repeated `<|` is not nested application.
+
```
t → Leaf (the base term)
t t → Stem Leaf
diff --git a/ext/demo-site/catalogue.json b/ext/demo-site/catalogue.json
@@ -188,8 +188,8 @@
"title": "Language reference",
"category": "Language & Runtime Explorations",
"hash": "86717d7f9d69fe730c6c6172c42c49895623aa070bf675e1b5f751adb30616c1",
- "source": "!import bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 *\n!import 7f87549d587fa8c3be88af0ea988db3adcc3b35f1ffb48973553491b075614db as Math\n!export *\n\n-- The open base import supplies contract helpers and unqualified names.\n-- Math is a qualified import: its names start with Math.\n-- A syntax sampler: independent examples, rather than one application.\n-- Start with answer / Natural number: 42. Choose a definition for its preset.\n-- Full contracts: follow For AI assistants in the footer to Language features.\n-- Each comment below gives an expression and its interpretation.\n\n-- Names, lambdas, named parameters, application and grouping.\nidentity = (x : x)\nfirst a b = a\nanswer = identity (first 42 7)\n-- identity \"hello\" / Text -> hello; first 7 9 / Natural number -> 7.\n\n-- The kernel constructors: Leaf, Stem and Fork. Use tricu for arbitrary trees.\nleaf = t\nshapes = [t (t t) (t t t)]\n-- leaf / tricu -> t; shapes / tricu shows a list containing all three shapes.\nliterals = [\"hello\" 42 [1 2 3]]\n-- literals / tricu shows mixed values. Strings support Unicode; names are ASCII.\n-- Lists use spaces. Group application items: [(identity 1) (identity 2)].\n\n-- Partial application: provide one of a function's arguments now.\naddTwo = Math.add 2\n-- addTwo 5 / Natural number -> 7.\n\n-- Ordered equations: literals, a qualified pin, exact lists, cons and wildcard.\n-- A pin (^true) compares against an existing value.\ndescribe 7 = \"seven\"\ndescribe \"hi\" = \"greeting\"\ndescribe ^true = \"true\"\ndescribe [a b] = \"two items\"\ndescribe [head | tail] = \"nonempty list\"\ndescribe [] = \"empty list\"\ndescribe _ = \"other\"\n-- Try each with Text: describe 7, describe \"hi\", describe (t t),\n-- describe [4 5], describe [1 2 3], describe [], describe (t (t t)).\n\n-- Pair patterns and whole-value capture. Construct a pair with t left right.\nkeep whole{left, right} = whole\nkeep _ = t\n-- keep (t t t) / tricu -> (t t t); keep (t t) / tricu -> t.\n\n-- Plain local bindings are sequential and nonrecursive.\nlocal x = (let\n double n = Math.multiply n 2\n in double x)\nviaWhere x = step x where step y = Math.add y 1\n-- local 6 -> 12; viaWhere 6 -> 7, both Natural number.\n\n-- Direct self-recursion, with a final fallback equation.\ncopy [] = []\ncopy [head | tail] = t head (copy tail)\ncopy _ = []\n-- copy [1 2 3] / Bytes -> [1, 2, 3]. Bytes displays naturals from 0 through 255.\n-- Use tricu for a general list; copy [] and copy (t t) both return [].\n\n-- case examines an intermediate value; arms align and finish with a fallback.\nfirstOr fallback xs = case xs of\n [head | _] = head\n _ = fallback\n-- firstOr 99 [5] -> 5; firstOr 99 [] -> 99, both Natural number.\n\n-- Local self-recursion is explicit: let rec (also available with where rec).\nlocalCopy xs = (let rec\n walk [] = []\n walk [head | tail] = t head (walk tail)\n walk _ = []\n in walk xs)\n-- localCopy [4 5] / Bytes -> [4, 5]; localCopy [] -> [].\n\n-- Parameter and result contracts check values at runtime.\nchecked x@nat? =@nat? x\n-- checked 42 / Natural number -> 42. A failed contract produces a Result.\n-- Keep annotations on plain function heads; see Contracts for Result handlers.\n\n-- Pipes and do use an explicit sequencing operator. Here a lambda passes a value\n-- to the next continuation. It is ordinary pure code; IO uses IO.bind instead.\nsequenced input = (do (value next : next value)\n value <- input |> identity\n identity value\n identity <| value)\n-- sequenced 9 / Natural number -> 9.\n-- Both pipe chains associate left-to-right; group nested applications explicitly.\n\n|- Block comments are another way to annotate source. They do not nest. -|\n-- !export * selects local definitions. Use !export name for a smaller interface.\n-- An open import uses !import MANIFEST *; a reexport uses !export * from MANIFEST.\n-- Replace MANIFEST with the full identity from Add import or the library guide.\n-- Reexports select the public interface; imports introduce names into scope.\n-- See Conversation for IO, and the docs for Maybe/Result/parser protocols.\n",
- "sourceSha256": "5d4035e52ef2f9f85278d6431fc5be43df4e7badce7393930237fc1deb1c4fb8",
+ "source": "!import bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 *\n!import 7f87549d587fa8c3be88af0ea988db3adcc3b35f1ffb48973553491b075614db as Math\n!export *\n\n-- The open base import supplies contract helpers and unqualified names.\n-- Math is a qualified import: its names start with Math.\n-- A syntax sampler: independent examples, rather than one application.\n-- Start with answer / Natural number: 42. Choose a definition for its preset.\n-- Full contracts: follow For AI assistants in the footer to Language features.\n-- Each comment below gives an expression and its interpretation.\n\n-- Names, lambdas, named parameters, application and grouping.\nidentity = (x : x)\nfirst a b = a\nanswer = identity (first 42 7)\n-- identity \"hello\" / Text -> hello; first 7 9 / Natural number -> 7.\n\n-- The kernel constructors: Leaf, Stem and Fork. Use tricu for arbitrary trees.\nleaf = t\nshapes = [t (t t) (t t t)]\n-- leaf / tricu -> t; shapes / tricu shows a list containing all three shapes.\nliterals = [\"hello\" 42 [1 2 3]]\n-- literals / tricu shows mixed values. Strings support Unicode; names are ASCII.\n-- Lists use spaces. Group application items: [(identity 1) (identity 2)].\n\n-- Partial application: provide one of a function's arguments now.\naddTwo = Math.add 2\n-- addTwo 5 / Natural number -> 7.\n\n-- Ordered equations: literals, a qualified pin, exact lists, cons and wildcard.\n-- A pin (^true) compares against an existing value.\ndescribe 7 = \"seven\"\ndescribe \"hi\" = \"greeting\"\ndescribe ^true = \"true\"\ndescribe [a b] = \"two items\"\ndescribe [head | tail] = \"nonempty list\"\ndescribe [] = \"empty list\"\ndescribe _ = \"other\"\n-- Try each with Text: describe 7, describe \"hi\", describe (t t),\n-- describe [4 5], describe [1 2 3], describe [], describe (t (t t)).\n\n-- Pair patterns and whole-value capture. Construct a pair with t left right.\nkeep whole{left, right} = whole\nkeep _ = t\n-- keep (t t t) / tricu -> (t t t); keep (t t) / tricu -> t.\n\n-- Plain local bindings are sequential and nonrecursive.\nlocal x = let\n double n = Math.multiply n 2\n in double x\nviaWhere x = step x where step y = Math.add y 1\n-- local 6 -> 12; viaWhere 6 -> 7, both Natural number.\n\n-- Direct self-recursion, with a final fallback equation.\ncopy [] = []\ncopy [head | tail] = t head <| copy tail\ncopy _ = []\n-- copy [1 2 3] / Bytes -> [1, 2, 3]. Bytes displays naturals from 0 through 255.\n-- Use tricu for a general list; copy [] and copy (t t) both return [].\n\n-- case examines an intermediate value; arms align and finish with a fallback.\nfirstOr fallback xs = case xs of\n [head | _] = head\n _ = fallback\n-- firstOr 99 [5] -> 5; firstOr 99 [] -> 99, both Natural number.\n\n-- Local self-recursion is explicit: let rec (also available with where rec).\nlocalCopy xs = let rec\n walk [] = []\n walk [head | tail] = t head <| walk tail\n walk _ = []\n in walk xs\n-- localCopy [4 5] / Bytes -> [4, 5]; localCopy [] -> [].\n\n-- Parameter and result contracts check values at runtime.\nchecked x@nat? =@nat? x\n-- checked 42 / Natural number -> 42. A failed contract produces a Result.\n-- Keep annotations on plain function heads; see Contracts for Result handlers.\n\n-- Pipes and do use an explicit sequencing operator. Here a lambda passes a value\n-- to the next continuation. It is ordinary pure code; IO uses IO.bind instead.\nsequenced input = do (value next : next value)\n value <- input |> identity\n identity value\n identity <| value\n-- sequenced 9 / Natural number -> 9.\n-- Both pipe chains associate left-to-right; group nested applications explicitly.\n\n|- Block comments are another way to annotate source. They do not nest. -|\n-- !export * selects local definitions. Use !export name for a smaller interface.\n-- An open import uses !import MANIFEST *; a reexport uses !export * from MANIFEST.\n-- Replace MANIFEST with the full identity from Add import or the library guide.\n-- Reexports select the public interface; imports introduce names into scope.\n-- See Conversation for IO, and the docs for Maybe/Result/parser protocols.\n",
+ "sourceSha256": "593b2982c50668b5e8892eb8447f18df71011cabd7f5227a43181886519f519b",
"export": "answer",
"arguments": [],
"output": "number",
@@ -378,8 +378,8 @@
"title": "A conversation",
"category": "Practical Examples",
"hash": "0811457d501113ee11194f90e279ebb4ef9d548c70cb48d146be03eb9af7505f",
- "source": "!import 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 as IO\n!export main\n\n-- A conversation built from the IO library's actions and explicit bind operator.\n-- Evaluate main in Browser IO mode, then answer in the terminal input.\n-- The returned name is also displayed as Text. Try changing the greeting.\n-- Importing IO does not grant filesystem or network access in the browser.\nmain = (IO.io (do IO.bind\n IO.putStr \"What is your name?\\n\"\n name <- IO.getLine\n IO.putStr \"Hello, \"\n IO.putStr name\n IO.putStr \"!\\n\"\n IO.pure name))\n",
- "sourceSha256": "3b9835aec1f3b987ad954cd235a7c0517170d3f7e236b15d60ce9a3a809c74c4",
+ "source": "!import 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 as IO\n!export main\n\n-- A conversation built from the IO library's actions and explicit bind operator.\n-- Evaluate main in Browser IO mode, then answer in the terminal input.\n-- The returned name is also displayed as Text. Try changing the greeting.\n-- Importing IO does not grant filesystem or network access in the browser.\nmain = IO.io\n do IO.bind\n IO.putStr \"What is your name?\\n\"\n name <- IO.getLine\n IO.putStr \"Hello, \"\n IO.putStr name\n IO.putStr \"!\\n\"\n IO.pure name\n",
+ "sourceSha256": "63ba77b14e307d836c253e9fbff5d0a04971316c616b2cb65f7a56295da5595d",
"export": "main",
"arguments": [],
"output": "string",
@@ -392,8 +392,8 @@
"title": "Tree microscope",
"category": "Language & Runtime Explorations",
"hash": "493a125b649639c0df8348b4aeb3a455b7139cae0634ba114866c48813500bc5",
- "source": "!import bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 as B\n!export shape\n!export size\n!export same\n\n-- Inspect data OR compiled program trees, not source text.\n-- Try shape (t t t), size (t t t), or same (t t) (t t).\n-- Also try shape (x : x): functions are inspectable trees too.\n-- triage dispatches directly on Leaf, Stem and Fork without visiting children.\n-- Arithmetic and Booleans come from base; traversal is the subject of this demo.\nshape tree = B.triage \"Leaf\" (child : \"Stem\") (left right : \"Fork\") tree\n\n-- Count every constructor occurrence, including repeated shared branches.\n-- This is expanded tree size, not allocated nodes or source-code length.\ncount tree acc = (B.triage\n (B.succ acc)\n (child : count child (B.succ acc))\n (left right : count right (count left (B.succ acc)))\n tree)\n\nsize tree = count tree 0\n\nsame a b = (B.triage\n (B.triage B.true (ignored : B.false) (ignored ignored2 : B.false) b)\n (x : B.triage B.false (y : same x y) (ignored ignored2 : B.false) b)\n (x y : B.triage B.false (ignored : B.false) (u v : B.and? (same x u) (same y v)) b)\n a)\n",
- "sourceSha256": "0dd0bb5444662200cf3556dd7a81e65570b5d3b34d4b3e6b17e6d9c36d3d1114",
+ "source": "!import bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 as B\n!export shape\n!export size\n!export same\n\n-- Inspect data OR compiled program trees, not source text.\n-- Try shape (t t t), size (t t t), or same (t t) (t t).\n-- Also try shape (x : x): functions are inspectable trees too.\n-- triage dispatches directly on Leaf, Stem and Fork without visiting children.\n-- Arithmetic and Booleans come from base; traversal is the subject of this demo.\nshape tree = B.triage \"Leaf\" (child : \"Stem\") (left right : \"Fork\") tree\n\n-- Count every constructor occurrence, including repeated shared branches.\n-- This is expanded tree size, not allocated nodes or source-code length.\ncount tree acc = B.triage\n (B.succ acc)\n (child : count child <| B.succ acc)\n (left right : B.succ acc |> count left |> count right)\n tree\n\nsize tree = count tree 0\n\nsame a b = B.triage\n (B.triage B.true (ignored : B.false) (ignored ignored2 : B.false) b)\n (x : B.triage B.false (y : same x y) (ignored ignored2 : B.false) b)\n (x y : B.triage B.false (ignored : B.false) (u v : B.and? (same x u) (same y v)) b)\n a\n",
+ "sourceSha256": "08c2b341d8947c9e46062dfa63f9d8810eb15620de499caf0891e313c5152cef",
"export": "size",
"arguments": [
{
@@ -469,8 +469,8 @@
"title": "Shipping policies",
"category": "Practical Examples",
"hash": "4aa9ce9f19dfce87ecb3f6bd0a2e881dafcbcd3d2e4b4075befbb378d80833ea",
- "source": "!import bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 *\n!export v1\n!export v2\n\n-- Shipping policy: arguments are destination, cart subtotal in cents, grams.\n-- Destinations: domestic, regional, international. Grams: 1..5000.\n-- Select v1/v2 with the same cart: domestic free shipping changes from $75 to $100.\n-- Otherwise domestic costs $4.99/$5.99, regional $9.99, international $19.99.\n-- Packages above 1kg add $2.50. Subtotals are bounded to $10,000.\n-- The bundle hash + export pins the policy used to reproduce a quote later.\n-- Records use [fieldName tag value]: 0 = text, 1 = natural, 2 = Boolean.\n-- Base supplies canonical-natural checks, arithmetic and Booleans.\n\nfailure message = [[\"status\" 0 \"error\"] [\"message\" 0 message]]\n\nbaseRate 1 \"domestic\" = 499\nbaseRate _ \"domestic\" = 599\nbaseRate _ \"regional\" = 999\nbaseRate _ _ = 1999\n\npaidRate version zone grams = case lte_? grams 1000 of\n ^true = baseRate version zone\n _ = add 250 (baseRate version zone)\n\nfreeThreshold 1 = 7500\nfreeThreshold _ = 10000\n\namount version zone subtotal grams = case and? (equal? zone \"domestic\") (lte_? (freeThreshold version) subtotal) of\n ^true = 0\n _ = paidRate version zone grams\n\nquote version zone subtotal grams = case and? (isNat? subtotal) (isNat? grams) of\n ^true = (case or? (equal? zone \"domestic\") (or? (equal? zone \"regional\") (equal? zone \"international\")) of\n ^true = (case and? (lte_? subtotal 1000000) (and? (lte_? 1 grams) (lte_? grams 5000)) of\n ^true = [[\"status\" 0 \"ok\"] [\"policyVersion\" 1 version]\n [\"destination\" 0 zone] [\"shippingCents\" 1 (amount version zone subtotal grams)]]\n _ = failure \"Subtotal must be <=1000000 cents; grams must be 1..5000.\")\n _ = failure \"Unknown destination.\")\n _ = failure \"Subtotal and grams must be canonical natural numbers.\"\n\nv1 zone subtotal grams = quote 1 zone subtotal grams\nv2 zone subtotal grams = quote 2 zone subtotal grams\n",
- "sourceSha256": "d6d9378ab39733a8aabaf4f6a25851350e99fa81607bf05a64266301e2712f5a",
+ "source": "!import bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 *\n!export v1\n!export v2\n\n-- Shipping policy: arguments are destination, cart subtotal in cents, grams.\n-- Destinations: domestic, regional, international. Grams: 1..5000.\n-- Select v1/v2 with the same cart: domestic free shipping changes from $75 to $100.\n-- Otherwise domestic costs $4.99/$5.99, regional $9.99, international $19.99.\n-- Packages above 1kg add $2.50. Subtotals are bounded to $10,000.\n-- The bundle hash + export pins the policy used to reproduce a quote later.\n-- Records use [fieldName tag value]: 0 = text, 1 = natural, 2 = Boolean.\n-- Base supplies canonical-natural checks, arithmetic and Booleans.\n\nfailure message = [[\"status\" 0 \"error\"] [\"message\" 0 message]]\n\nbaseRate 1 \"domestic\" = 499\nbaseRate _ \"domestic\" = 599\nbaseRate _ \"regional\" = 999\nbaseRate _ _ = 1999\n\npaidRate version zone grams = case lte_? grams 1000 of\n ^true = baseRate version zone\n _ = add 250 <| baseRate version zone\n\nfreeThreshold 1 = 7500\nfreeThreshold _ = 10000\n\namount version zone subtotal grams = case and? (equal? zone \"domestic\") (lte_? (freeThreshold version) subtotal) of\n ^true = 0\n _ = paidRate version zone grams\n\nquote version zone subtotal grams = case and? (isNat? subtotal) (isNat? grams) of\n ^true = case or? (equal? zone \"domestic\") (or? (equal? zone \"regional\") (equal? zone \"international\")) of\n ^true = case and? (lte_? subtotal 1000000) (and? (lte_? 1 grams) (lte_? grams 5000)) of\n ^true = [[\"status\" 0 \"ok\"] [\"policyVersion\" 1 version]\n [\"destination\" 0 zone] [\"shippingCents\" 1 (amount version zone subtotal grams)]]\n _ = failure \"Subtotal must be <=1000000 cents; grams must be 1..5000.\"\n _ = failure \"Unknown destination.\"\n _ = failure \"Subtotal and grams must be canonical natural numbers.\"\n\nv1 zone subtotal grams = quote 1 zone subtotal grams\nv2 zone subtotal grams = quote 2 zone subtotal grams\n",
+ "sourceSha256": "dbbb53a9da0918c294f8d3b07768a756cae7d6beafb3c780caba3e6d4c0f7d4a",
"export": "v1",
"arguments": [
{
@@ -533,8 +533,8 @@
"title": "Event adapters",
"category": "Practical Examples",
"hash": "8b51693c63658d59c8083ec1c95cf1f6565ce5799791f540e92caf508613c9fb",
- "source": "!import bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 *\n!import 3f70d95cf1bae0928ee89acd2a9e0cdd8287796efae32a9f08d33e3dd0149c55 as A\n!export vendorA\n!export vendorB\n\n-- Event adapter: two producers, one consumer-facing record schema.\n-- vendorA accepts [eventName customer totalCents]. eventName is purchase/refund.\n-- vendorB accepts [customer refunded totalCents]. refunded is a Boolean.\n-- Both return schemaVersion, eventType, customer and amountCents.\n-- Try the two exports: their presets describe the same order.created event.\n-- A producer can publish the adapter hash alongside its event schema.\n-- Records use [fieldName tag value]: 0 = text, 1 = natural, 2 = Boolean.\n-- Base supplies arithmetic/Booleans; A.hostString? checks Unicode scalar text.\n\nfailure message = [[\"status\" 0 \"error\"] [\"message\" 0 message]]\n\nnormalized event customer cents = case A.hostString? customer of\n ^true = (case and? (isNat? cents) (lte_? cents 1000000) of\n ^true = [[\"schemaVersion\" 1 1] [\"eventType\" 0 event]\n [\"customer\" 0 customer] [\"amountCents\" 1 cents]]\n _ = failure \"amountCents must be a canonical natural <=1000000.\")\n _ = failure \"customer must be scalar text.\"\n\nfromA \"purchase\" customer cents = normalized \"order.created\" customer cents\nfromA \"refund\" customer cents = normalized \"order.refunded\" customer cents\nfromA _ _ _ = failure \"Unknown vendor A event.\"\n\nfromB customer ^true cents = normalized \"order.refunded\" customer cents\nfromB customer ^false cents = normalized \"order.created\" customer cents\nfromB _ _ _ = failure \"refunded must be a canonical Boolean.\"\n\n-- Preserve list-shape diagnostics without inspecting any field before dispatch.\nunpack handler (t _) = failure \"Expected a list.\"\nunpack handler [_ | (t _)] = failure \"Expected a list.\"\nunpack handler [_ | [_ | (t _)]] = failure \"Expected a list.\"\nunpack handler [a b c] = handler a b c\nunpack _ _ = failure \"Expected exactly three fields.\"\n\nvendorA values = unpack fromA values\nvendorB values = unpack fromB values\n",
- "sourceSha256": "ca11d9a7e20bfe228545c43f50aed57089c020eead516e23b67d909fae8923de",
+ "source": "!import bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 *\n!import 3f70d95cf1bae0928ee89acd2a9e0cdd8287796efae32a9f08d33e3dd0149c55 as A\n!export vendorA\n!export vendorB\n\n-- Event adapter: two producers, one consumer-facing record schema.\n-- vendorA accepts [eventName customer totalCents]. eventName is purchase/refund.\n-- vendorB accepts [customer refunded totalCents]. refunded is a Boolean.\n-- Both return schemaVersion, eventType, customer and amountCents.\n-- Try the two exports: their presets describe the same order.created event.\n-- A producer can publish the adapter hash alongside its event schema.\n-- Records use [fieldName tag value]: 0 = text, 1 = natural, 2 = Boolean.\n-- Base supplies arithmetic/Booleans; A.hostString? checks Unicode scalar text.\n\nfailure message = [[\"status\" 0 \"error\"] [\"message\" 0 message]]\n\nnormalized event customer cents = case A.hostString? customer of\n ^true = case and? (isNat? cents) (lte_? cents 1000000) of\n ^true = [[\"schemaVersion\" 1 1] [\"eventType\" 0 event]\n [\"customer\" 0 customer] [\"amountCents\" 1 cents]]\n _ = failure \"amountCents must be a canonical natural <=1000000.\"\n _ = failure \"customer must be scalar text.\"\n\nfromA \"purchase\" customer cents = normalized \"order.created\" customer cents\nfromA \"refund\" customer cents = normalized \"order.refunded\" customer cents\nfromA _ _ _ = failure \"Unknown vendor A event.\"\n\nfromB customer ^true cents = normalized \"order.refunded\" customer cents\nfromB customer ^false cents = normalized \"order.created\" customer cents\nfromB _ _ _ = failure \"refunded must be a canonical Boolean.\"\n\n-- Preserve list-shape diagnostics without inspecting any field before dispatch.\nunpack handler (t _) = failure \"Expected a list.\"\nunpack handler [_ | (t _)] = failure \"Expected a list.\"\nunpack handler [_ | [_ | (t _)]] = failure \"Expected a list.\"\nunpack handler [a b c] = handler a b c\nunpack _ _ = failure \"Expected exactly three fields.\"\n\nvendorA values = unpack fromA values\nvendorB values = unpack fromB values\n",
+ "sourceSha256": "08a7857e4cb6ad2e4c1c9972f3ce05f26b810af8481ca974b91b1819bd4718e7",
"export": "vendorA",
"arguments": [
{
@@ -612,8 +612,8 @@
"title": "Registration validator",
"category": "Practical Examples",
"hash": "679733e17a86cc5e3f5282ca824cad4c9c0febd260e51f6a87e16dd14c02c45a",
- "source": "!import bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 *\n!import 3f70d95cf1bae0928ee89acd2a9e0cdd8287796efae32a9f08d33e3dd0149c55 as A\n!export main\n\n-- Registration validator: name, email, age, termsAccepted are separate arguments.\n-- Returns one field-specific message per input, plus an accepted Boolean.\n-- Name must be nonempty scalar text. Age must be a natural from 18 to 120.\n-- Email uses a deliberately small rule: nonempty text containing @ and a dot.\n-- Terms must be the canonical true Boolean. This is a teaching policy, not SMTP validation.\n-- Browser and server can pin this same executable instead of duplicating rules.\n-- Records use [fieldName tag value]: 0 = text, 1 = natural, 2 = Boolean.\n-- Base supplies arithmetic/Booleans; A.hostString? checks Unicode scalar text.\n\nnameMessage name = case A.hostString? name of\n ^true = (case name of\n [] = \"Name is required.\"\n _ = \"\")\n _ = \"Name must be scalar text.\"\n\nemailMessage email = case A.hostString? email of\n ^true = (case and? (contains? \"@\" email) (contains? \".\" email) of\n ^true = \"\"\n _ = \"Email must contain @ and a dot.\")\n _ = \"Email must be scalar text.\"\n\nageMessage age = case isNat? age of\n ^true = (case and? (lte_? 18 age) (lte_? age 120) of\n ^true = \"\"\n _ = \"Age must be from 18 to 120.\")\n _ = \"Age must be a canonical natural.\"\n\ntermsMessage ^true = \"\"\ntermsMessage _ = \"Accept the terms.\"\n\nreport name email age terms = [\n [\"accepted\" 2 (and? (equal? name \"\") (and? (equal? email \"\") (and? (equal? age \"\") (equal? terms \"\"))))]\n [\"nameError\" 0 name] [\"emailError\" 0 email]\n [\"ageError\" 0 age] [\"termsError\" 0 terms]]\n\nmain name email age terms = report (nameMessage name) (emailMessage email) (ageMessage age) (termsMessage terms)\n",
- "sourceSha256": "531866c6b2e7e42ecbd4ae285c502b59321ad31fd2b82b36d8a139938592ac31",
+ "source": "!import bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 *\n!import 3f70d95cf1bae0928ee89acd2a9e0cdd8287796efae32a9f08d33e3dd0149c55 as A\n!export main\n\n-- Registration validator: name, email, age, termsAccepted are separate arguments.\n-- Returns one field-specific message per input, plus an accepted Boolean.\n-- Name must be nonempty scalar text. Age must be a natural from 18 to 120.\n-- Email uses a deliberately small rule: nonempty text containing @ and a dot.\n-- Terms must be the canonical true Boolean. This is a teaching policy, not SMTP validation.\n-- Browser and server can pin this same executable instead of duplicating rules.\n-- Records use [fieldName tag value]: 0 = text, 1 = natural, 2 = Boolean.\n-- Base supplies arithmetic/Booleans; A.hostString? checks Unicode scalar text.\n\nnameMessage name = case A.hostString? name of\n ^true = case name of\n [] = \"Name is required.\"\n _ = \"\"\n _ = \"Name must be scalar text.\"\n\nemailMessage email = case A.hostString? email of\n ^true = case and? (contains? \"@\" email) (contains? \".\" email) of\n ^true = \"\"\n _ = \"Email must contain @ and a dot.\"\n _ = \"Email must be scalar text.\"\n\nageMessage age = case isNat? age of\n ^true = case and? (lte_? 18 age) (lte_? age 120) of\n ^true = \"\"\n _ = \"Age must be from 18 to 120.\"\n _ = \"Age must be a canonical natural.\"\n\ntermsMessage ^true = \"\"\ntermsMessage _ = \"Accept the terms.\"\n\nreport name email age terms = [\n [\"accepted\" 2 (and? (equal? name \"\") (and? (equal? email \"\") (and? (equal? age \"\") (equal? terms \"\"))))]\n [\"nameError\" 0 name] [\"emailError\" 0 email]\n [\"ageError\" 0 age] [\"termsError\" 0 terms]]\n\nmain name email age terms = report (nameMessage name) (emailMessage email) (ageMessage age) (termsMessage terms)\n",
+ "sourceSha256": "b15d4e60a34d839b06c813fa439ea063abc4a771bc2d0dd24cd928eb21ca7e29",
"export": "main",
"arguments": [
{
@@ -666,8 +666,8 @@
"title": "Profile migrations",
"category": "Practical Examples",
"hash": "730aeb8203ff504b7483999a6477a292b10871f82e94a77d3d5f7b49ecc3b6b3",
- "source": "!import bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 *\n!import 3f70d95cf1bae0928ee89acd2a9e0cdd8287796efae32a9f08d33e3dd0149c55 as A\n!export v1\n!export v2\n\n-- Archived profile v0: [name email]. v1: adds schemaVersion and displayName.\n-- v2: also supplies marketingOptIn=false; existing data is preserved.\n-- Select v1/v2 with the same input to compare the two target schemas.\n-- A migration receipt can record the input identity, executable hash/export,\n-- output identity and wire schema. Running this demo stays entirely local.\n-- Records use [fieldName tag value]: 0 = text, 1 = natural, 2 = Boolean.\n-- Base supplies arithmetic/Booleans; A.hostString? checks Unicode scalar text.\n\nfailure message = [[\"status\" 0 \"error\"] [\"message\" 0 message]]\n\nmigrate version name email = case and? (A.hostString? name) (A.hostString? email) of\n ^true = (case version of\n 1 = [[\"schemaVersion\" 1 1] [\"displayName\" 0 name] [\"email\" 0 email]]\n _ = [[\"schemaVersion\" 1 2] [\"displayName\" 0 name] [\"email\" 0 email]\n [\"marketingOptIn\" 2 false]])\n _ = failure \"Name and email must be scalar text.\"\n\nprofile _ (t _) = failure \"Expected a list.\"\nprofile _ [_ | (t _)] = failure \"Expected a list.\"\nprofile version [name email] = migrate version name email\nprofile _ _ = failure \"Expected [name email].\"\n\nv1 values = profile 1 values\nv2 values = profile 2 values\n",
- "sourceSha256": "66ee7bc1a9005b5f1d12f168b7866231ad7f523cf34f52b63958cb155a5c49b5",
+ "source": "!import bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 *\n!import 3f70d95cf1bae0928ee89acd2a9e0cdd8287796efae32a9f08d33e3dd0149c55 as A\n!export v1\n!export v2\n\n-- Archived profile v0: [name email]. v1: adds schemaVersion and displayName.\n-- v2: also supplies marketingOptIn=false; existing data is preserved.\n-- Select v1/v2 with the same input to compare the two target schemas.\n-- A migration receipt can record the input identity, executable hash/export,\n-- output identity and wire schema. Running this demo stays entirely local.\n-- Records use [fieldName tag value]: 0 = text, 1 = natural, 2 = Boolean.\n-- Base supplies arithmetic/Booleans; A.hostString? checks Unicode scalar text.\n\nfailure message = [[\"status\" 0 \"error\"] [\"message\" 0 message]]\n\nmigrate version name email = case and? (A.hostString? name) (A.hostString? email) of\n ^true = case version of\n 1 = [[\"schemaVersion\" 1 1] [\"displayName\" 0 name] [\"email\" 0 email]]\n _ = [[\"schemaVersion\" 1 2] [\"displayName\" 0 name] [\"email\" 0 email]\n [\"marketingOptIn\" 2 false]]\n _ = failure \"Name and email must be scalar text.\"\n\nprofile _ (t _) = failure \"Expected a list.\"\nprofile _ [_ | (t _)] = failure \"Expected a list.\"\nprofile version [name email] = migrate version name email\nprofile _ _ = failure \"Expected [name email].\"\n\nv1 values = profile 1 values\nv2 values = profile 2 values\n",
+ "sourceSha256": "a9270baef5cb5102c895c9f23434ebc82c87eebfa16c25cb415b06b4636a279e",
"export": "v1",
"arguments": [
{
@@ -732,10 +732,11 @@
"id": "parser",
"preparation": "native",
"environment": "empty",
+ "category": "Language & Runtime Explorations",
"title": "Parser combinators",
"hash": "355e41506f39e05da8c8a72a68aa810a486e062480322bac11af67e86980520e",
- "source": "!import 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 *\n!import 14282088469cc420dd014700da06998f6bb8dcf990fc885a464c0a1e1858e20d *\n!import 7f87549d587fa8c3be88af0ea988db3adcc3b35f1ffb48973553491b075614db as N\n!export parseNames\n!export parsePrefix\n\n-- Lowercase names separated by commas. Try \"red,green\", \"red,,blue\",\n-- \"red,\" and \"red green\". A comma commits to another name.\n-- parseNames requires the entire input; parsePrefix exposes any remainder.\n-- Edit the parser or labels below; valid edits compile locally.\n-- Imports pin complete library interfaces, fetched only when compiling.\nnameParser = parserLabel \"lowercase name\" (parserSome (parserSatisfy (c : N.inRange c 97 122)))\ncommaParser = parserThen (parserExpect 44) parserCommit\nnamesPrefix = parserSepBy1 nameParser commaParser\nnamesComplete = parserBefore namesPrefix (parserLabel \"end of input\" parserEof)\n\nreport reply = (parserMatchReply\n (diagnostic rest consumed committed : [\n [\"status\" 0 \"error\"]\n [\"expected\" 0 (parserDiagnosticExpected diagnostic)]\n [\"remaining\" 0 rest]\n [\"consumed\" 2 consumed]\n [\"committed\" 2 committed]])\n (names rest consumed committed : [\n [\"status\" 0 \"ok\"]\n [\"names\" 0 (intercalate \", \" names)]\n [\"count\" 1 (length names)]\n [\"remaining\" 0 rest]\n [\"consumed\" 2 consumed]\n [\"committed\" 2 committed]])\n reply)\nparseNames text = report (namesComplete text)\nparsePrefix text = report (namesPrefix text)\n",
- "sourceSha256": "18aa0a464dabb595749abdab5edb1bb11c4356c0e753cf0985a644901bb5c241",
+ "source": "!import 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 *\n!import 14282088469cc420dd014700da06998f6bb8dcf990fc885a464c0a1e1858e20d *\n!import 7f87549d587fa8c3be88af0ea988db3adcc3b35f1ffb48973553491b075614db as N\n!export parseNames\n!export parsePrefix\n\n-- Lowercase names separated by commas. Try \"red,green\", \"red,,blue\",\n-- \"red,\" and \"red green\". A comma commits to another name.\n-- parseNames requires the entire input; parsePrefix exposes any remainder.\n-- Edit the parser or labels below; valid edits compile locally.\n-- Imports pin complete library interfaces, fetched only when compiling.\nnameParser = parserSatisfy (c : N.inRange c 97 122)\n |> parserSome\n |> parserLabel \"lowercase name\"\ncommaParser = parserThen (parserExpect 44) parserCommit\nnamesPrefix = parserSepBy1 nameParser commaParser\nnamesComplete = parserBefore namesPrefix <| parserLabel \"end of input\" parserEof\n\nreport reply = parserMatchReply\n (diagnostic rest consumed committed : [\n [\"status\" 0 \"error\"]\n [\"expected\" 0 (parserDiagnosticExpected diagnostic)]\n [\"remaining\" 0 rest]\n [\"consumed\" 2 consumed]\n [\"committed\" 2 committed]])\n (names rest consumed committed : [\n [\"status\" 0 \"ok\"]\n [\"names\" 0 (intercalate \", \" names)]\n [\"count\" 1 (length names)]\n [\"remaining\" 0 rest]\n [\"consumed\" 2 consumed]\n [\"committed\" 2 committed]])\n reply\nparseNames text = namesComplete text |> report\nparsePrefix text = namesPrefix text |> report\n",
+ "sourceSha256": "9c005993cf43c07ec5c7e4f7f420b2f63c66e605d9522ab4b7e044de5e6cb8df",
"export": "parseNames",
"arguments": [
{
@@ -766,17 +767,17 @@
"output": "record",
"mode": "pure"
}
- },
- "category": "Language & Runtime Explorations"
+ }
},
{
"id": "lexer",
"preparation": "native",
"environment": "empty",
+ "category": "Language & Runtime Explorations",
"title": "Located lexer scans",
"hash": "e756a481b3056a2e76f50316ccf77bfab8b15727b7bec899580c2eb919e668b2",
- "source": "!import 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 *\n!import 14282088469cc420dd014700da06998f6bb8dcf990fc885a464c0a1e1858e20d *\n!import 816df1a32853750a1254112aff8ae8c55ca86bf70e809d226969ab1d43a6688b *\n!import 7f87549d587fa8c3be88af0ea988db3adcc3b35f1ffb48973553491b075614db as N\n!export scanWord\n!export scanWhitespace\n\n-- Positioned prefix scans, not whole-input parsers. The remainder begins\n-- at the first nonmatching character. Positions count source characters,\n-- not UTF-8 bytes or display columns: only LF advances the line.\n-- Edit a predicate below and evaluate scanWord \"hello,world\" to compare.\n-- scanWhitespace accepts space, LF and tab, but deliberately not CR.\nreport reply = (let\n payload = parserReplyPayload reply\n end = snd payload\n in [\n [\"matched\" 0 (fst payload)]\n [\"remaining\" 0 (parserReplyRest reply)]\n [\"line\" 1 (lexerPositionLine end)]\n [\"column\" 1 (lexerPositionColumn end)]\n [\"offset\" 1 (lexerPositionOffset end)]\n [\"consumed\" 2 (parserReplyConsumed reply)]])\n\nscan predicate text = report (lexerMapScan reverse\n (lexerScanWhile text lexerStart [] predicate (state character : pair character state)))\nscanWord text = scan (character : N.inRange character 97 122) text\nscanWhitespace text = scan (character : or? (equal? character 32)\n (or? (equal? character 9) (equal? character 10))) text\n",
- "sourceSha256": "ec68af4102c16ef56be01e78a3288bea57af71ca57ab35374a0d12005b5a10a1",
+ "source": "!import 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 *\n!import 14282088469cc420dd014700da06998f6bb8dcf990fc885a464c0a1e1858e20d *\n!import 816df1a32853750a1254112aff8ae8c55ca86bf70e809d226969ab1d43a6688b *\n!import 7f87549d587fa8c3be88af0ea988db3adcc3b35f1ffb48973553491b075614db as N\n!export scanWord\n!export scanWhitespace\n\n-- Positioned prefix scans, not whole-input parsers. The remainder begins\n-- at the first nonmatching character. Positions count source characters,\n-- not UTF-8 bytes or display columns: only LF advances the line.\n-- Edit a predicate below and evaluate scanWord \"hello,world\" to compare.\n-- scanWhitespace accepts space, LF and tab, but deliberately not CR.\nreport reply = let\n payload = parserReplyPayload reply\n end = snd payload\n in [\n [\"matched\" 0 (fst payload)]\n [\"remaining\" 0 (parserReplyRest reply)]\n [\"line\" 1 (lexerPositionLine end)]\n [\"column\" 1 (lexerPositionColumn end)]\n [\"offset\" 1 (lexerPositionOffset end)]\n [\"consumed\" 2 (parserReplyConsumed reply)]]\n\nscan predicate text = lexerScanWhile text lexerStart [] predicate (state character : pair character state)\n |> lexerMapScan reverse\n |> report\nscanWord text = scan (character : N.inRange character 97 122) text\nscanWhitespace text = scan (character : or? (equal? character 32)\n (or? (equal? character 9) (equal? character 10))) text\n",
+ "sourceSha256": "2a441d6d881acd6e34cd0caa8179ee1afefbce68c2e5ff1e44fb1768f69eeb3c",
"export": "scanWord",
"arguments": [
{
@@ -807,7 +808,6 @@
"output": "record",
"mode": "pure"
}
- },
- "category": "Language & Runtime Explorations"
+ }
}
]
diff --git a/ext/demo-site/demos.json b/ext/demo-site/demos.json
@@ -5,7 +5,7 @@
"compilerExport": "source_unit.compileStableModuleDefinitions",
"compilerBundleSha256": "91f78730b3623d019e33f96e9e2712d61b04837b21ef3eccec3071ac9260944d",
"compilerManifest": "c3d5c2c0efdac3641467968669031ba1bc21883db7307772a594c8dc04ef1582",
- "hostSha256": "2bb21ddb1307e69bad141eb62f4e94b71efdf900614ba7333821e2eef2204547",
+ "hostSha256": "8661383d0db249c34ac2a4b33ad606d15c416446f367df905b9da7bbf23fa15a",
"demos": {
"names-values": {
"demandCases": [],
@@ -183,8 +183,8 @@
},
"language-reference": {
"demandCases": [],
- "source": "!import bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 *\n!import 7f87549d587fa8c3be88af0ea988db3adcc3b35f1ffb48973553491b075614db as Math\n!export *\n\n-- The open base import supplies contract helpers and unqualified names.\n-- Math is a qualified import: its names start with Math.\n-- A syntax sampler: independent examples, rather than one application.\n-- Start with answer / Natural number: 42. Choose a definition for its preset.\n-- Full contracts: follow For AI assistants in the footer to Language features.\n-- Each comment below gives an expression and its interpretation.\n\n-- Names, lambdas, named parameters, application and grouping.\nidentity = (x : x)\nfirst a b = a\nanswer = identity (first 42 7)\n-- identity \"hello\" / Text -> hello; first 7 9 / Natural number -> 7.\n\n-- The kernel constructors: Leaf, Stem and Fork. Use tricu for arbitrary trees.\nleaf = t\nshapes = [t (t t) (t t t)]\n-- leaf / tricu -> t; shapes / tricu shows a list containing all three shapes.\nliterals = [\"hello\" 42 [1 2 3]]\n-- literals / tricu shows mixed values. Strings support Unicode; names are ASCII.\n-- Lists use spaces. Group application items: [(identity 1) (identity 2)].\n\n-- Partial application: provide one of a function's arguments now.\naddTwo = Math.add 2\n-- addTwo 5 / Natural number -> 7.\n\n-- Ordered equations: literals, a qualified pin, exact lists, cons and wildcard.\n-- A pin (^true) compares against an existing value.\ndescribe 7 = \"seven\"\ndescribe \"hi\" = \"greeting\"\ndescribe ^true = \"true\"\ndescribe [a b] = \"two items\"\ndescribe [head | tail] = \"nonempty list\"\ndescribe [] = \"empty list\"\ndescribe _ = \"other\"\n-- Try each with Text: describe 7, describe \"hi\", describe (t t),\n-- describe [4 5], describe [1 2 3], describe [], describe (t (t t)).\n\n-- Pair patterns and whole-value capture. Construct a pair with t left right.\nkeep whole{left, right} = whole\nkeep _ = t\n-- keep (t t t) / tricu -> (t t t); keep (t t) / tricu -> t.\n\n-- Plain local bindings are sequential and nonrecursive.\nlocal x = (let\n double n = Math.multiply n 2\n in double x)\nviaWhere x = step x where step y = Math.add y 1\n-- local 6 -> 12; viaWhere 6 -> 7, both Natural number.\n\n-- Direct self-recursion, with a final fallback equation.\ncopy [] = []\ncopy [head | tail] = t head (copy tail)\ncopy _ = []\n-- copy [1 2 3] / Bytes -> [1, 2, 3]. Bytes displays naturals from 0 through 255.\n-- Use tricu for a general list; copy [] and copy (t t) both return [].\n\n-- case examines an intermediate value; arms align and finish with a fallback.\nfirstOr fallback xs = case xs of\n [head | _] = head\n _ = fallback\n-- firstOr 99 [5] -> 5; firstOr 99 [] -> 99, both Natural number.\n\n-- Local self-recursion is explicit: let rec (also available with where rec).\nlocalCopy xs = (let rec\n walk [] = []\n walk [head | tail] = t head (walk tail)\n walk _ = []\n in walk xs)\n-- localCopy [4 5] / Bytes -> [4, 5]; localCopy [] -> [].\n\n-- Parameter and result contracts check values at runtime.\nchecked x@nat? =@nat? x\n-- checked 42 / Natural number -> 42. A failed contract produces a Result.\n-- Keep annotations on plain function heads; see Contracts for Result handlers.\n\n-- Pipes and do use an explicit sequencing operator. Here a lambda passes a value\n-- to the next continuation. It is ordinary pure code; IO uses IO.bind instead.\nsequenced input = (do (value next : next value)\n value <- input |> identity\n identity value\n identity <| value)\n-- sequenced 9 / Natural number -> 9.\n-- Both pipe chains associate left-to-right; group nested applications explicitly.\n\n|- Block comments are another way to annotate source. They do not nest. -|\n-- !export * selects local definitions. Use !export name for a smaller interface.\n-- An open import uses !import MANIFEST *; a reexport uses !export * from MANIFEST.\n-- Replace MANIFEST with the full identity from Add import or the library guide.\n-- Reexports select the public interface; imports introduce names into scope.\n-- See Conversation for IO, and the docs for Maybe/Result/parser protocols.\n",
- "sourceSha256": "5d4035e52ef2f9f85278d6431fc5be43df4e7badce7393930237fc1deb1c4fb8",
+ "source": "!import bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 *\n!import 7f87549d587fa8c3be88af0ea988db3adcc3b35f1ffb48973553491b075614db as Math\n!export *\n\n-- The open base import supplies contract helpers and unqualified names.\n-- Math is a qualified import: its names start with Math.\n-- A syntax sampler: independent examples, rather than one application.\n-- Start with answer / Natural number: 42. Choose a definition for its preset.\n-- Full contracts: follow For AI assistants in the footer to Language features.\n-- Each comment below gives an expression and its interpretation.\n\n-- Names, lambdas, named parameters, application and grouping.\nidentity = (x : x)\nfirst a b = a\nanswer = identity (first 42 7)\n-- identity \"hello\" / Text -> hello; first 7 9 / Natural number -> 7.\n\n-- The kernel constructors: Leaf, Stem and Fork. Use tricu for arbitrary trees.\nleaf = t\nshapes = [t (t t) (t t t)]\n-- leaf / tricu -> t; shapes / tricu shows a list containing all three shapes.\nliterals = [\"hello\" 42 [1 2 3]]\n-- literals / tricu shows mixed values. Strings support Unicode; names are ASCII.\n-- Lists use spaces. Group application items: [(identity 1) (identity 2)].\n\n-- Partial application: provide one of a function's arguments now.\naddTwo = Math.add 2\n-- addTwo 5 / Natural number -> 7.\n\n-- Ordered equations: literals, a qualified pin, exact lists, cons and wildcard.\n-- A pin (^true) compares against an existing value.\ndescribe 7 = \"seven\"\ndescribe \"hi\" = \"greeting\"\ndescribe ^true = \"true\"\ndescribe [a b] = \"two items\"\ndescribe [head | tail] = \"nonempty list\"\ndescribe [] = \"empty list\"\ndescribe _ = \"other\"\n-- Try each with Text: describe 7, describe \"hi\", describe (t t),\n-- describe [4 5], describe [1 2 3], describe [], describe (t (t t)).\n\n-- Pair patterns and whole-value capture. Construct a pair with t left right.\nkeep whole{left, right} = whole\nkeep _ = t\n-- keep (t t t) / tricu -> (t t t); keep (t t) / tricu -> t.\n\n-- Plain local bindings are sequential and nonrecursive.\nlocal x = let\n double n = Math.multiply n 2\n in double x\nviaWhere x = step x where step y = Math.add y 1\n-- local 6 -> 12; viaWhere 6 -> 7, both Natural number.\n\n-- Direct self-recursion, with a final fallback equation.\ncopy [] = []\ncopy [head | tail] = t head <| copy tail\ncopy _ = []\n-- copy [1 2 3] / Bytes -> [1, 2, 3]. Bytes displays naturals from 0 through 255.\n-- Use tricu for a general list; copy [] and copy (t t) both return [].\n\n-- case examines an intermediate value; arms align and finish with a fallback.\nfirstOr fallback xs = case xs of\n [head | _] = head\n _ = fallback\n-- firstOr 99 [5] -> 5; firstOr 99 [] -> 99, both Natural number.\n\n-- Local self-recursion is explicit: let rec (also available with where rec).\nlocalCopy xs = let rec\n walk [] = []\n walk [head | tail] = t head <| walk tail\n walk _ = []\n in walk xs\n-- localCopy [4 5] / Bytes -> [4, 5]; localCopy [] -> [].\n\n-- Parameter and result contracts check values at runtime.\nchecked x@nat? =@nat? x\n-- checked 42 / Natural number -> 42. A failed contract produces a Result.\n-- Keep annotations on plain function heads; see Contracts for Result handlers.\n\n-- Pipes and do use an explicit sequencing operator. Here a lambda passes a value\n-- to the next continuation. It is ordinary pure code; IO uses IO.bind instead.\nsequenced input = do (value next : next value)\n value <- input |> identity\n identity value\n identity <| value\n-- sequenced 9 / Natural number -> 9.\n-- Both pipe chains associate left-to-right; group nested applications explicitly.\n\n|- Block comments are another way to annotate source. They do not nest. -|\n-- !export * selects local definitions. Use !export name for a smaller interface.\n-- An open import uses !import MANIFEST *; a reexport uses !export * from MANIFEST.\n-- Replace MANIFEST with the full identity from Add import or the library guide.\n-- Reexports select the public interface; imports introduce names into scope.\n-- See Conversation for IO, and the docs for Maybe/Result/parser protocols.\n",
+ "sourceSha256": "593b2982c50668b5e8892eb8447f18df71011cabd7f5227a43181886519f519b",
"hash": "86717d7f9d69fe730c6c6172c42c49895623aa070bf675e1b5f751adb30616c1",
"mode": "pure",
"haskellCases": [
@@ -448,8 +448,8 @@
},
"intro": {
"demandCases": [],
- "source": "!export *\n\n-- Start here: a small tricu language guide.\n-- Lines beginning with -- are comments. Read down, then try the exports.\n-- Edit this source in place; valid edits compile after a short pause.\n-- Write a call in Evaluate tricu expression, then press Evaluate.\n-- Imports/exports declare a module; this introductory one needs no libraries.\n\n-- 1. Definitions give a name to a value: name = expression.\n-- Every value is a tree. These are the three constructor forms.\nleaf = t\nstem = t t\nfork = t t t\n-- Evaluate leaf, stem or fork with tricu · t source to see its structure.\n\n-- 2. A lambda names an input before the colon and returns an expression.\nidentity = (x : x)\n-- Try identity \"Hello!\" with Text, then identity 42 with Natural number.\n\n-- 3. Named parameters are a convenient way to write functions.\n-- This is equivalent to first = (a b : a).\nfirst a b = a\n-- Try first 7 9: it returns the first of its two arguments.\n\n-- 4. Application is written with spaces and groups to the left.\n-- first 7 9 means (first 7) 9. Parentheses group an expression explicitly.\nanswer = identity (first 42 7)\n-- Evaluate answer with Natural number: the result is 42.\n-- A name by itself needs no further arguments; [] is an empty-list value.\n\n-- 5. Numbers, quoted strings and lists are convenient tree literals.\ngreeting = \"Hello from tricu!\"\nnumbers = [1 2 3]\n-- Evaluate greeting with Text, or numbers with Bytes to display [1, 2, 3].\n-- Bytes requires a list of naturals in 0..255; use t source for other lists.\n-- Lists use spaces, not commas: try identity [4 5 6].\n\n-- 6. The result interpretation tells the playground how to read a tree.\n-- Use Text for a string, Natural number for a number, or t source for any tree.\n-- Pure value observes the result; Browser IO interprets an IO action tree.\n-- The Conversation demo shows input, output and continuations.\n\n-- Settings also offers structured arguments and stored-definition inspection.\n-- Edits stay local; Restore original returns to the published source.\n-- For an application spanning lines, put the entire call in parentheses.\n-- Other demos import libraries by immutable manifest hash, shown in their headers.\n-- Next: Tree microscope shows how functions can inspect other program trees.\n",
- "sourceSha256": "24835a93f2da82d4b16876074e18634045e750e832a6694bd6067ba78e1a5508",
+ "source": "!export *\n\n-- Start here: a small tricu language guide.\n-- Lines beginning with -- are comments. Read down, then try the exports.\n-- Edit this source in place; valid edits compile after a short pause.\n-- Write a call in Evaluate tricu expression, then press Evaluate.\n-- Imports/exports declare a module; this introductory one needs no libraries.\n\n-- 1. Definitions give a name to a value: name = expression.\n-- Every value is a tree. These are the three constructor forms.\nleaf = t\nstem = t t\nfork = t t t\n-- Evaluate leaf, stem or fork with tricu · t source to see its structure.\n\n-- 2. A lambda names an input before the colon and returns an expression.\nidentity = (x : x)\n-- Try identity \"Hello!\" with Text, then identity 42 with Natural number.\n\n-- 3. Named parameters are a convenient way to write functions.\n-- This is equivalent to first = (a b : a).\nfirst a b = a\n-- Try first 7 9: it returns the first of its two arguments.\n\n-- 4. Application is written with spaces and groups to the left.\n-- first 7 9 means (first 7) 9. Parentheses group an expression explicitly.\nanswer = identity (first 42 7)\n-- Evaluate answer with Natural number: the result is 42.\n-- A name by itself needs no further arguments; [] is an empty-list value.\n\n-- 5. Numbers, quoted strings and lists are convenient tree literals.\ngreeting = \"Hello from tricu!\"\nnumbers = [1 2 3]\n-- Evaluate greeting with Text, or numbers with Bytes to display [1, 2, 3].\n-- Bytes requires a list of naturals in 0..255; use t source for other lists.\n-- Lists use spaces, not commas: try identity [4 5 6].\n\n-- 6. The result interpretation tells the playground how to read a tree.\n-- Use Text for a string, Natural number for a number, or t source for any tree.\n-- Pure value observes the result; Browser IO interprets an IO action tree.\n-- The Conversation demo shows input, output and continuations.\n\n-- Settings also offers structured arguments and stored-definition inspection.\n-- Edits stay local; Restore original returns to the published source.\n-- Continue a call on indented lines; parentheses are only needed for grouping.\n-- Other demos import libraries by immutable manifest hash, shown in their headers.\n-- Next: Tree microscope shows how functions can inspect other program trees.\n",
+ "sourceSha256": "5572e99fed54b0fe261fee4f417ec3bad47a25f0a266a4ba478dd42368b610f1",
"hash": "64e7db3b35121efa19c7becbbb38a0ad0f884143e790e286a5ee6c236c00f9d5",
"mode": "pure",
"haskellCases": [
@@ -554,8 +554,8 @@
},
"greet": {
"demandCases": [],
- "source": "!import 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 as IO\n!export main\n\n-- A conversation built from the IO library's actions and explicit bind operator.\n-- Evaluate main in Browser IO mode, then answer in the terminal input.\n-- The returned name is also displayed as Text. Try changing the greeting.\n-- Importing IO does not grant filesystem or network access in the browser.\nmain = (IO.io (do IO.bind\n IO.putStr \"What is your name?\\n\"\n name <- IO.getLine\n IO.putStr \"Hello, \"\n IO.putStr name\n IO.putStr \"!\\n\"\n IO.pure name))\n",
- "sourceSha256": "3b9835aec1f3b987ad954cd235a7c0517170d3f7e236b15d60ce9a3a809c74c4",
+ "source": "!import 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 as IO\n!export main\n\n-- A conversation built from the IO library's actions and explicit bind operator.\n-- Evaluate main in Browser IO mode, then answer in the terminal input.\n-- The returned name is also displayed as Text. Try changing the greeting.\n-- Importing IO does not grant filesystem or network access in the browser.\nmain = IO.io\n do IO.bind\n IO.putStr \"What is your name?\\n\"\n name <- IO.getLine\n IO.putStr \"Hello, \"\n IO.putStr name\n IO.putStr \"!\\n\"\n IO.pure name\n",
+ "sourceSha256": "63ba77b14e307d836c253e9fbff5d0a04971316c616b2cb65f7a56295da5595d",
"hash": "0811457d501113ee11194f90e279ebb4ef9d548c70cb48d146be03eb9af7505f",
"mode": "io",
"haskellCases": [
@@ -598,8 +598,8 @@
"result": "22021021020202021002210210210210202102100220210202021021021002210210202102021021000"
}
],
- "source": "!import bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 as B\n!export shape\n!export size\n!export same\n\n-- Inspect data OR compiled program trees, not source text.\n-- Try shape (t t t), size (t t t), or same (t t) (t t).\n-- Also try shape (x : x): functions are inspectable trees too.\n-- triage dispatches directly on Leaf, Stem and Fork without visiting children.\n-- Arithmetic and Booleans come from base; traversal is the subject of this demo.\nshape tree = B.triage \"Leaf\" (child : \"Stem\") (left right : \"Fork\") tree\n\n-- Count every constructor occurrence, including repeated shared branches.\n-- This is expanded tree size, not allocated nodes or source-code length.\ncount tree acc = (B.triage\n (B.succ acc)\n (child : count child (B.succ acc))\n (left right : count right (count left (B.succ acc)))\n tree)\n\nsize tree = count tree 0\n\nsame a b = (B.triage\n (B.triage B.true (ignored : B.false) (ignored ignored2 : B.false) b)\n (x : B.triage B.false (y : same x y) (ignored ignored2 : B.false) b)\n (x y : B.triage B.false (ignored : B.false) (u v : B.and? (same x u) (same y v)) b)\n a)\n",
- "sourceSha256": "0dd0bb5444662200cf3556dd7a81e65570b5d3b34d4b3e6b17e6d9c36d3d1114",
+ "source": "!import bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 as B\n!export shape\n!export size\n!export same\n\n-- Inspect data OR compiled program trees, not source text.\n-- Try shape (t t t), size (t t t), or same (t t) (t t).\n-- Also try shape (x : x): functions are inspectable trees too.\n-- triage dispatches directly on Leaf, Stem and Fork without visiting children.\n-- Arithmetic and Booleans come from base; traversal is the subject of this demo.\nshape tree = B.triage \"Leaf\" (child : \"Stem\") (left right : \"Fork\") tree\n\n-- Count every constructor occurrence, including repeated shared branches.\n-- This is expanded tree size, not allocated nodes or source-code length.\ncount tree acc = B.triage\n (B.succ acc)\n (child : count child <| B.succ acc)\n (left right : B.succ acc |> count left |> count right)\n tree\n\nsize tree = count tree 0\n\nsame a b = B.triage\n (B.triage B.true (ignored : B.false) (ignored ignored2 : B.false) b)\n (x : B.triage B.false (y : same x y) (ignored ignored2 : B.false) b)\n (x y : B.triage B.false (ignored : B.false) (u v : B.and? (same x u) (same y v)) b)\n a\n",
+ "sourceSha256": "08c2b341d8947c9e46062dfa63f9d8810eb15620de499caf0891e313c5152cef",
"hash": "493a125b649639c0df8348b4aeb3a455b7139cae0634ba114866c48813500bc5",
"mode": "pure",
"haskellCases": [
@@ -854,8 +854,8 @@
"result": "222210210202021021021002202021020210210210022102020202021021002202021020210210210022102021020210210210022102102020210210210002022210202102020210210022021020202102102100220210202021021021002210210210210202102100220210202021021021000022221020210210202102100221020210202021021002210210202021021021002210210202021021021002210202020202102100221021021020202102100221020210202021021000202221021020202102021002210202102021021021002202102020202102100220202102021021021002210210210210202102100220202102021021021002210202020202102100220202102102021021002202020202021002210202020202102100220210210210202102100220202102020210210022020202020210022102102102020210210022021020202102102100221020202020210210022102021021020210210022102102020210210210022020202020210022102021021020210210022102021020210210210022102102020210210210022020210202102102100220202020202100220210202020210210022102021020202102100220202020202100221021020202021021002210202020202102100220210210210202102100221021021021020210210022021021021020210210022102020210202102100221021020202021021002210202020202102100220202102102021021002202020202021002202102102102021021002210202020202102100220202102021021021002210202102021021021002202102020210210210022102020202021021002202021021020210210022020202020210022021021021020210210022102021020210210210022102021021020210210022021020202021021002210202102020210210022021020202102102100221021020202102102100220210210210202100000"
}
],
- "source": "!import bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 *\n!export v1\n!export v2\n\n-- Shipping policy: arguments are destination, cart subtotal in cents, grams.\n-- Destinations: domestic, regional, international. Grams: 1..5000.\n-- Select v1/v2 with the same cart: domestic free shipping changes from $75 to $100.\n-- Otherwise domestic costs $4.99/$5.99, regional $9.99, international $19.99.\n-- Packages above 1kg add $2.50. Subtotals are bounded to $10,000.\n-- The bundle hash + export pins the policy used to reproduce a quote later.\n-- Records use [fieldName tag value]: 0 = text, 1 = natural, 2 = Boolean.\n-- Base supplies canonical-natural checks, arithmetic and Booleans.\n\nfailure message = [[\"status\" 0 \"error\"] [\"message\" 0 message]]\n\nbaseRate 1 \"domestic\" = 499\nbaseRate _ \"domestic\" = 599\nbaseRate _ \"regional\" = 999\nbaseRate _ _ = 1999\n\npaidRate version zone grams = case lte_? grams 1000 of\n ^true = baseRate version zone\n _ = add 250 (baseRate version zone)\n\nfreeThreshold 1 = 7500\nfreeThreshold _ = 10000\n\namount version zone subtotal grams = case and? (equal? zone \"domestic\") (lte_? (freeThreshold version) subtotal) of\n ^true = 0\n _ = paidRate version zone grams\n\nquote version zone subtotal grams = case and? (isNat? subtotal) (isNat? grams) of\n ^true = (case or? (equal? zone \"domestic\") (or? (equal? zone \"regional\") (equal? zone \"international\")) of\n ^true = (case and? (lte_? subtotal 1000000) (and? (lte_? 1 grams) (lte_? grams 5000)) of\n ^true = [[\"status\" 0 \"ok\"] [\"policyVersion\" 1 version]\n [\"destination\" 0 zone] [\"shippingCents\" 1 (amount version zone subtotal grams)]]\n _ = failure \"Subtotal must be <=1000000 cents; grams must be 1..5000.\")\n _ = failure \"Unknown destination.\")\n _ = failure \"Subtotal and grams must be canonical natural numbers.\"\n\nv1 zone subtotal grams = quote 1 zone subtotal grams\nv2 zone subtotal grams = quote 2 zone subtotal grams\n",
- "sourceSha256": "d6d9378ab39733a8aabaf4f6a25851350e99fa81607bf05a64266301e2712f5a",
+ "source": "!import bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 *\n!export v1\n!export v2\n\n-- Shipping policy: arguments are destination, cart subtotal in cents, grams.\n-- Destinations: domestic, regional, international. Grams: 1..5000.\n-- Select v1/v2 with the same cart: domestic free shipping changes from $75 to $100.\n-- Otherwise domestic costs $4.99/$5.99, regional $9.99, international $19.99.\n-- Packages above 1kg add $2.50. Subtotals are bounded to $10,000.\n-- The bundle hash + export pins the policy used to reproduce a quote later.\n-- Records use [fieldName tag value]: 0 = text, 1 = natural, 2 = Boolean.\n-- Base supplies canonical-natural checks, arithmetic and Booleans.\n\nfailure message = [[\"status\" 0 \"error\"] [\"message\" 0 message]]\n\nbaseRate 1 \"domestic\" = 499\nbaseRate _ \"domestic\" = 599\nbaseRate _ \"regional\" = 999\nbaseRate _ _ = 1999\n\npaidRate version zone grams = case lte_? grams 1000 of\n ^true = baseRate version zone\n _ = add 250 <| baseRate version zone\n\nfreeThreshold 1 = 7500\nfreeThreshold _ = 10000\n\namount version zone subtotal grams = case and? (equal? zone \"domestic\") (lte_? (freeThreshold version) subtotal) of\n ^true = 0\n _ = paidRate version zone grams\n\nquote version zone subtotal grams = case and? (isNat? subtotal) (isNat? grams) of\n ^true = case or? (equal? zone \"domestic\") (or? (equal? zone \"regional\") (equal? zone \"international\")) of\n ^true = case and? (lte_? subtotal 1000000) (and? (lte_? 1 grams) (lte_? grams 5000)) of\n ^true = [[\"status\" 0 \"ok\"] [\"policyVersion\" 1 version]\n [\"destination\" 0 zone] [\"shippingCents\" 1 (amount version zone subtotal grams)]]\n _ = failure \"Subtotal must be <=1000000 cents; grams must be 1..5000.\"\n _ = failure \"Unknown destination.\"\n _ = failure \"Subtotal and grams must be canonical natural numbers.\"\n\nv1 zone subtotal grams = quote 1 zone subtotal grams\nv2 zone subtotal grams = quote 2 zone subtotal grams\n",
+ "sourceSha256": "dbbb53a9da0918c294f8d3b07768a756cae7d6beafb3c780caba3e6d4c0f7d4a",
"hash": "4aa9ce9f19dfce87ecb3f6bd0a2e881dafcbcd3d2e4b4075befbb378d80833ea",
"mode": "pure",
"haskellCases": [
@@ -1200,8 +1200,8 @@
"result": "2222102102020210210210022020210202102102100221020202020210210022020210202102102100221020210202102102100221021020202102102100020222102021020202102100220210202021021021002202102020210210210022102102102102021021002202102020210210210000222210202102102021021002210202102020210210022102102020210210210022102102020210210210022102020202021021002210210210202021021002210202102020210210002022202102020210210210022102021020202102100220210210202021021002210202102021021021002202102102102021021002202021020202102100221020210202021021002202021020202102100220202020202100221020210210202102100221020210202102102100221021020202102102100220202102021021021002202020202021002202102020202102100221020210202021021002202020202021002210202020202102100220202020202100221021020202021021002210202020202102100220210210210202102100221021021021020210210022021021021020210210022102020210202102100221021020202021021002210202020202102100220202102102021021002202020202021002202102020202021002210210210210202102100221021021021020210210022020210210202102100221020210202021021002210202020202102100220210210210202102100220210210210202100000"
}
],
- "source": "!import bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 *\n!import 3f70d95cf1bae0928ee89acd2a9e0cdd8287796efae32a9f08d33e3dd0149c55 as A\n!export vendorA\n!export vendorB\n\n-- Event adapter: two producers, one consumer-facing record schema.\n-- vendorA accepts [eventName customer totalCents]. eventName is purchase/refund.\n-- vendorB accepts [customer refunded totalCents]. refunded is a Boolean.\n-- Both return schemaVersion, eventType, customer and amountCents.\n-- Try the two exports: their presets describe the same order.created event.\n-- A producer can publish the adapter hash alongside its event schema.\n-- Records use [fieldName tag value]: 0 = text, 1 = natural, 2 = Boolean.\n-- Base supplies arithmetic/Booleans; A.hostString? checks Unicode scalar text.\n\nfailure message = [[\"status\" 0 \"error\"] [\"message\" 0 message]]\n\nnormalized event customer cents = case A.hostString? customer of\n ^true = (case and? (isNat? cents) (lte_? cents 1000000) of\n ^true = [[\"schemaVersion\" 1 1] [\"eventType\" 0 event]\n [\"customer\" 0 customer] [\"amountCents\" 1 cents]]\n _ = failure \"amountCents must be a canonical natural <=1000000.\")\n _ = failure \"customer must be scalar text.\"\n\nfromA \"purchase\" customer cents = normalized \"order.created\" customer cents\nfromA \"refund\" customer cents = normalized \"order.refunded\" customer cents\nfromA _ _ _ = failure \"Unknown vendor A event.\"\n\nfromB customer ^true cents = normalized \"order.refunded\" customer cents\nfromB customer ^false cents = normalized \"order.created\" customer cents\nfromB _ _ _ = failure \"refunded must be a canonical Boolean.\"\n\n-- Preserve list-shape diagnostics without inspecting any field before dispatch.\nunpack handler (t _) = failure \"Expected a list.\"\nunpack handler [_ | (t _)] = failure \"Expected a list.\"\nunpack handler [_ | [_ | (t _)]] = failure \"Expected a list.\"\nunpack handler [a b c] = handler a b c\nunpack _ _ = failure \"Expected exactly three fields.\"\n\nvendorA values = unpack fromA values\nvendorB values = unpack fromB values\n",
- "sourceSha256": "ca11d9a7e20bfe228545c43f50aed57089c020eead516e23b67d909fae8923de",
+ "source": "!import bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 *\n!import 3f70d95cf1bae0928ee89acd2a9e0cdd8287796efae32a9f08d33e3dd0149c55 as A\n!export vendorA\n!export vendorB\n\n-- Event adapter: two producers, one consumer-facing record schema.\n-- vendorA accepts [eventName customer totalCents]. eventName is purchase/refund.\n-- vendorB accepts [customer refunded totalCents]. refunded is a Boolean.\n-- Both return schemaVersion, eventType, customer and amountCents.\n-- Try the two exports: their presets describe the same order.created event.\n-- A producer can publish the adapter hash alongside its event schema.\n-- Records use [fieldName tag value]: 0 = text, 1 = natural, 2 = Boolean.\n-- Base supplies arithmetic/Booleans; A.hostString? checks Unicode scalar text.\n\nfailure message = [[\"status\" 0 \"error\"] [\"message\" 0 message]]\n\nnormalized event customer cents = case A.hostString? customer of\n ^true = case and? (isNat? cents) (lte_? cents 1000000) of\n ^true = [[\"schemaVersion\" 1 1] [\"eventType\" 0 event]\n [\"customer\" 0 customer] [\"amountCents\" 1 cents]]\n _ = failure \"amountCents must be a canonical natural <=1000000.\"\n _ = failure \"customer must be scalar text.\"\n\nfromA \"purchase\" customer cents = normalized \"order.created\" customer cents\nfromA \"refund\" customer cents = normalized \"order.refunded\" customer cents\nfromA _ _ _ = failure \"Unknown vendor A event.\"\n\nfromB customer ^true cents = normalized \"order.refunded\" customer cents\nfromB customer ^false cents = normalized \"order.created\" customer cents\nfromB _ _ _ = failure \"refunded must be a canonical Boolean.\"\n\n-- Preserve list-shape diagnostics without inspecting any field before dispatch.\nunpack handler (t _) = failure \"Expected a list.\"\nunpack handler [_ | (t _)] = failure \"Expected a list.\"\nunpack handler [_ | [_ | (t _)]] = failure \"Expected a list.\"\nunpack handler [a b c] = handler a b c\nunpack _ _ = failure \"Expected exactly three fields.\"\n\nvendorA values = unpack fromA values\nvendorB values = unpack fromB values\n",
+ "sourceSha256": "08a7857e4cb6ad2e4c1c9972f3ce05f26b810af8481ca974b91b1819bd4718e7",
"hash": "8b51693c63658d59c8083ec1c95cf1f6565ce5799791f540e92caf508613c9fb",
"mode": "pure",
"haskellCases": [
@@ -1584,8 +1584,8 @@
},
"registration": {
"demandCases": [],
- "source": "!import bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 *\n!import 3f70d95cf1bae0928ee89acd2a9e0cdd8287796efae32a9f08d33e3dd0149c55 as A\n!export main\n\n-- Registration validator: name, email, age, termsAccepted are separate arguments.\n-- Returns one field-specific message per input, plus an accepted Boolean.\n-- Name must be nonempty scalar text. Age must be a natural from 18 to 120.\n-- Email uses a deliberately small rule: nonempty text containing @ and a dot.\n-- Terms must be the canonical true Boolean. This is a teaching policy, not SMTP validation.\n-- Browser and server can pin this same executable instead of duplicating rules.\n-- Records use [fieldName tag value]: 0 = text, 1 = natural, 2 = Boolean.\n-- Base supplies arithmetic/Booleans; A.hostString? checks Unicode scalar text.\n\nnameMessage name = case A.hostString? name of\n ^true = (case name of\n [] = \"Name is required.\"\n _ = \"\")\n _ = \"Name must be scalar text.\"\n\nemailMessage email = case A.hostString? email of\n ^true = (case and? (contains? \"@\" email) (contains? \".\" email) of\n ^true = \"\"\n _ = \"Email must contain @ and a dot.\")\n _ = \"Email must be scalar text.\"\n\nageMessage age = case isNat? age of\n ^true = (case and? (lte_? 18 age) (lte_? age 120) of\n ^true = \"\"\n _ = \"Age must be from 18 to 120.\")\n _ = \"Age must be a canonical natural.\"\n\ntermsMessage ^true = \"\"\ntermsMessage _ = \"Accept the terms.\"\n\nreport name email age terms = [\n [\"accepted\" 2 (and? (equal? name \"\") (and? (equal? email \"\") (and? (equal? age \"\") (equal? terms \"\"))))]\n [\"nameError\" 0 name] [\"emailError\" 0 email]\n [\"ageError\" 0 age] [\"termsError\" 0 terms]]\n\nmain name email age terms = report (nameMessage name) (emailMessage email) (ageMessage age) (termsMessage terms)\n",
- "sourceSha256": "531866c6b2e7e42ecbd4ae285c502b59321ad31fd2b82b36d8a139938592ac31",
+ "source": "!import bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 *\n!import 3f70d95cf1bae0928ee89acd2a9e0cdd8287796efae32a9f08d33e3dd0149c55 as A\n!export main\n\n-- Registration validator: name, email, age, termsAccepted are separate arguments.\n-- Returns one field-specific message per input, plus an accepted Boolean.\n-- Name must be nonempty scalar text. Age must be a natural from 18 to 120.\n-- Email uses a deliberately small rule: nonempty text containing @ and a dot.\n-- Terms must be the canonical true Boolean. This is a teaching policy, not SMTP validation.\n-- Browser and server can pin this same executable instead of duplicating rules.\n-- Records use [fieldName tag value]: 0 = text, 1 = natural, 2 = Boolean.\n-- Base supplies arithmetic/Booleans; A.hostString? checks Unicode scalar text.\n\nnameMessage name = case A.hostString? name of\n ^true = case name of\n [] = \"Name is required.\"\n _ = \"\"\n _ = \"Name must be scalar text.\"\n\nemailMessage email = case A.hostString? email of\n ^true = case and? (contains? \"@\" email) (contains? \".\" email) of\n ^true = \"\"\n _ = \"Email must contain @ and a dot.\"\n _ = \"Email must be scalar text.\"\n\nageMessage age = case isNat? age of\n ^true = case and? (lte_? 18 age) (lte_? age 120) of\n ^true = \"\"\n _ = \"Age must be from 18 to 120.\"\n _ = \"Age must be a canonical natural.\"\n\ntermsMessage ^true = \"\"\ntermsMessage _ = \"Accept the terms.\"\n\nreport name email age terms = [\n [\"accepted\" 2 (and? (equal? name \"\") (and? (equal? email \"\") (and? (equal? age \"\") (equal? terms \"\"))))]\n [\"nameError\" 0 name] [\"emailError\" 0 email]\n [\"ageError\" 0 age] [\"termsError\" 0 terms]]\n\nmain name email age terms = report (nameMessage name) (emailMessage email) (ageMessage age) (termsMessage terms)\n",
+ "sourceSha256": "b15d4e60a34d839b06c813fa439ea063abc4a771bc2d0dd24cd928eb21ca7e29",
"hash": "679733e17a86cc5e3f5282ca824cad4c9c0febd260e51f6a87e16dd14c02c45a",
"mode": "pure",
"haskellCases": [
@@ -1707,8 +1707,8 @@
},
"migration": {
"demandCases": [],
- "source": "!import bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 *\n!import 3f70d95cf1bae0928ee89acd2a9e0cdd8287796efae32a9f08d33e3dd0149c55 as A\n!export v1\n!export v2\n\n-- Archived profile v0: [name email]. v1: adds schemaVersion and displayName.\n-- v2: also supplies marketingOptIn=false; existing data is preserved.\n-- Select v1/v2 with the same input to compare the two target schemas.\n-- A migration receipt can record the input identity, executable hash/export,\n-- output identity and wire schema. Running this demo stays entirely local.\n-- Records use [fieldName tag value]: 0 = text, 1 = natural, 2 = Boolean.\n-- Base supplies arithmetic/Booleans; A.hostString? checks Unicode scalar text.\n\nfailure message = [[\"status\" 0 \"error\"] [\"message\" 0 message]]\n\nmigrate version name email = case and? (A.hostString? name) (A.hostString? email) of\n ^true = (case version of\n 1 = [[\"schemaVersion\" 1 1] [\"displayName\" 0 name] [\"email\" 0 email]]\n _ = [[\"schemaVersion\" 1 2] [\"displayName\" 0 name] [\"email\" 0 email]\n [\"marketingOptIn\" 2 false]])\n _ = failure \"Name and email must be scalar text.\"\n\nprofile _ (t _) = failure \"Expected a list.\"\nprofile _ [_ | (t _)] = failure \"Expected a list.\"\nprofile version [name email] = migrate version name email\nprofile _ _ = failure \"Expected [name email].\"\n\nv1 values = profile 1 values\nv2 values = profile 2 values\n",
- "sourceSha256": "66ee7bc1a9005b5f1d12f168b7866231ad7f523cf34f52b63958cb155a5c49b5",
+ "source": "!import bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 *\n!import 3f70d95cf1bae0928ee89acd2a9e0cdd8287796efae32a9f08d33e3dd0149c55 as A\n!export v1\n!export v2\n\n-- Archived profile v0: [name email]. v1: adds schemaVersion and displayName.\n-- v2: also supplies marketingOptIn=false; existing data is preserved.\n-- Select v1/v2 with the same input to compare the two target schemas.\n-- A migration receipt can record the input identity, executable hash/export,\n-- output identity and wire schema. Running this demo stays entirely local.\n-- Records use [fieldName tag value]: 0 = text, 1 = natural, 2 = Boolean.\n-- Base supplies arithmetic/Booleans; A.hostString? checks Unicode scalar text.\n\nfailure message = [[\"status\" 0 \"error\"] [\"message\" 0 message]]\n\nmigrate version name email = case and? (A.hostString? name) (A.hostString? email) of\n ^true = case version of\n 1 = [[\"schemaVersion\" 1 1] [\"displayName\" 0 name] [\"email\" 0 email]]\n _ = [[\"schemaVersion\" 1 2] [\"displayName\" 0 name] [\"email\" 0 email]\n [\"marketingOptIn\" 2 false]]\n _ = failure \"Name and email must be scalar text.\"\n\nprofile _ (t _) = failure \"Expected a list.\"\nprofile _ [_ | (t _)] = failure \"Expected a list.\"\nprofile version [name email] = migrate version name email\nprofile _ _ = failure \"Expected [name email].\"\n\nv1 values = profile 1 values\nv2 values = profile 2 values\n",
+ "sourceSha256": "a9270baef5cb5102c895c9f23434ebc82c87eebfa16c25cb415b06b4636a279e",
"hash": "730aeb8203ff504b7483999a6477a292b10871f82e94a77d3d5f7b49ecc3b6b3",
"mode": "pure",
"haskellCases": [
diff --git a/ext/demo-site/demos/events.tri b/ext/demo-site/demos/events.tri
@@ -15,10 +15,10 @@
failure message = [["status" 0 "error"] ["message" 0 message]]
normalized event customer cents = case A.hostString? customer of
- ^true = (case and? (isNat? cents) (lte_? cents 1000000) of
+ ^true = case and? (isNat? cents) (lte_? cents 1000000) of
^true = [["schemaVersion" 1 1] ["eventType" 0 event]
["customer" 0 customer] ["amountCents" 1 cents]]
- _ = failure "amountCents must be a canonical natural <=1000000.")
+ _ = failure "amountCents must be a canonical natural <=1000000."
_ = failure "customer must be scalar text."
fromA "purchase" customer cents = normalized "order.created" customer cents
diff --git a/ext/demo-site/demos/greet.tri b/ext/demo-site/demos/greet.tri
@@ -5,10 +5,11 @@
-- Evaluate main in Browser IO mode, then answer in the terminal input.
-- The returned name is also displayed as Text. Try changing the greeting.
-- Importing IO does not grant filesystem or network access in the browser.
-main = (IO.io (do IO.bind
- IO.putStr "What is your name?\n"
- name <- IO.getLine
- IO.putStr "Hello, "
- IO.putStr name
- IO.putStr "!\n"
- IO.pure name))
+main = IO.io
+ do IO.bind
+ IO.putStr "What is your name?\n"
+ name <- IO.getLine
+ IO.putStr "Hello, "
+ IO.putStr name
+ IO.putStr "!\n"
+ IO.pure name
diff --git a/ext/demo-site/demos/intro.tri b/ext/demo-site/demos/intro.tri
@@ -42,6 +42,6 @@ numbers = [1 2 3]
-- Settings also offers structured arguments and stored-definition inspection.
-- Edits stay local; Restore original returns to the published source.
--- For an application spanning lines, put the entire call in parentheses.
+-- Continue a call on indented lines; parentheses are only needed for grouping.
-- Other demos import libraries by immutable manifest hash, shown in their headers.
-- Next: Tree microscope shows how functions can inspect other program trees.
diff --git a/ext/demo-site/demos/language-reference.tri b/ext/demo-site/demos/language-reference.tri
@@ -45,15 +45,15 @@ keep _ = t
-- keep (t t t) / tricu -> (t t t); keep (t t) / tricu -> t.
-- Plain local bindings are sequential and nonrecursive.
-local x = (let
+local x = let
double n = Math.multiply n 2
- in double x)
+ in double x
viaWhere x = step x where step y = Math.add y 1
-- local 6 -> 12; viaWhere 6 -> 7, both Natural number.
-- Direct self-recursion, with a final fallback equation.
copy [] = []
-copy [head | tail] = t head (copy tail)
+copy [head | tail] = t head <| copy tail
copy _ = []
-- copy [1 2 3] / Bytes -> [1, 2, 3]. Bytes displays naturals from 0 through 255.
-- Use tricu for a general list; copy [] and copy (t t) both return [].
@@ -65,11 +65,11 @@ firstOr fallback xs = case xs of
-- firstOr 99 [5] -> 5; firstOr 99 [] -> 99, both Natural number.
-- Local self-recursion is explicit: let rec (also available with where rec).
-localCopy xs = (let rec
+localCopy xs = let rec
walk [] = []
- walk [head | tail] = t head (walk tail)
+ walk [head | tail] = t head <| walk tail
walk _ = []
- in walk xs)
+ in walk xs
-- localCopy [4 5] / Bytes -> [4, 5]; localCopy [] -> [].
-- Parameter and result contracts check values at runtime.
@@ -79,10 +79,10 @@ checked x@nat? =@nat? x
-- Pipes and do use an explicit sequencing operator. Here a lambda passes a value
-- to the next continuation. It is ordinary pure code; IO uses IO.bind instead.
-sequenced input = (do (value next : next value)
+sequenced input = do (value next : next value)
value <- input |> identity
identity value
- identity <| value)
+ identity <| value
-- sequenced 9 / Natural number -> 9.
-- Both pipe chains associate left-to-right; group nested applications explicitly.
diff --git a/ext/demo-site/demos/lexer.tri b/ext/demo-site/demos/lexer.tri
@@ -10,7 +10,7 @@
-- not UTF-8 bytes or display columns: only LF advances the line.
-- Edit a predicate below and evaluate scanWord "hello,world" to compare.
-- scanWhitespace accepts space, LF and tab, but deliberately not CR.
-report reply = (let
+report reply = let
payload = parserReplyPayload reply
end = snd payload
in [
@@ -19,10 +19,11 @@ report reply = (let
["line" 1 (lexerPositionLine end)]
["column" 1 (lexerPositionColumn end)]
["offset" 1 (lexerPositionOffset end)]
- ["consumed" 2 (parserReplyConsumed reply)]])
+ ["consumed" 2 (parserReplyConsumed reply)]]
-scan predicate text = report (lexerMapScan reverse
- (lexerScanWhile text lexerStart [] predicate (state character : pair character state)))
+scan predicate text = lexerScanWhile text lexerStart [] predicate (state character : pair character state)
+ |> lexerMapScan reverse
+ |> report
scanWord text = scan (character : N.inRange character 97 122) text
scanWhitespace text = scan (character : or? (equal? character 32)
(or? (equal? character 9) (equal? character 10))) text
diff --git a/ext/demo-site/demos/microscope.tri b/ext/demo-site/demos/microscope.tri
@@ -12,16 +12,16 @@ shape tree = B.triage "Leaf" (child : "Stem") (left right : "Fork") tree
-- Count every constructor occurrence, including repeated shared branches.
-- This is expanded tree size, not allocated nodes or source-code length.
-count tree acc = (B.triage
+count tree acc = B.triage
(B.succ acc)
- (child : count child (B.succ acc))
- (left right : count right (count left (B.succ acc)))
- tree)
+ (child : count child <| B.succ acc)
+ (left right : B.succ acc |> count left |> count right)
+ tree
size tree = count tree 0
-same a b = (B.triage
+same a b = B.triage
(B.triage B.true (ignored : B.false) (ignored ignored2 : B.false) b)
(x : B.triage B.false (y : same x y) (ignored ignored2 : B.false) b)
(x y : B.triage B.false (ignored : B.false) (u v : B.and? (same x u) (same y v)) b)
- a)
+ a
diff --git a/ext/demo-site/demos/migration.tri b/ext/demo-site/demos/migration.tri
@@ -14,10 +14,10 @@
failure message = [["status" 0 "error"] ["message" 0 message]]
migrate version name email = case and? (A.hostString? name) (A.hostString? email) of
- ^true = (case version of
+ ^true = case version of
1 = [["schemaVersion" 1 1] ["displayName" 0 name] ["email" 0 email]]
_ = [["schemaVersion" 1 2] ["displayName" 0 name] ["email" 0 email]
- ["marketingOptIn" 2 false]])
+ ["marketingOptIn" 2 false]]
_ = failure "Name and email must be scalar text."
profile _ (t _) = failure "Expected a list."
diff --git a/ext/demo-site/demos/parser.tri b/ext/demo-site/demos/parser.tri
@@ -9,12 +9,14 @@
-- parseNames requires the entire input; parsePrefix exposes any remainder.
-- Edit the parser or labels below; valid edits compile locally.
-- Imports pin complete library interfaces, fetched only when compiling.
-nameParser = parserLabel "lowercase name" (parserSome (parserSatisfy (c : N.inRange c 97 122)))
+nameParser = parserSatisfy (c : N.inRange c 97 122)
+ |> parserSome
+ |> parserLabel "lowercase name"
commaParser = parserThen (parserExpect 44) parserCommit
namesPrefix = parserSepBy1 nameParser commaParser
-namesComplete = parserBefore namesPrefix (parserLabel "end of input" parserEof)
+namesComplete = parserBefore namesPrefix <| parserLabel "end of input" parserEof
-report reply = (parserMatchReply
+report reply = parserMatchReply
(diagnostic rest consumed committed : [
["status" 0 "error"]
["expected" 0 (parserDiagnosticExpected diagnostic)]
@@ -28,6 +30,6 @@ report reply = (parserMatchReply
["remaining" 0 rest]
["consumed" 2 consumed]
["committed" 2 committed]])
- reply)
-parseNames text = report (namesComplete text)
-parsePrefix text = report (namesPrefix text)
+ reply
+parseNames text = namesComplete text |> report
+parsePrefix text = namesPrefix text |> report
diff --git a/ext/demo-site/demos/registration.tri b/ext/demo-site/demos/registration.tri
@@ -12,21 +12,21 @@
-- Base supplies arithmetic/Booleans; A.hostString? checks Unicode scalar text.
nameMessage name = case A.hostString? name of
- ^true = (case name of
+ ^true = case name of
[] = "Name is required."
- _ = "")
+ _ = ""
_ = "Name must be scalar text."
emailMessage email = case A.hostString? email of
- ^true = (case and? (contains? "@" email) (contains? "." email) of
+ ^true = case and? (contains? "@" email) (contains? "." email) of
^true = ""
- _ = "Email must contain @ and a dot.")
+ _ = "Email must contain @ and a dot."
_ = "Email must be scalar text."
ageMessage age = case isNat? age of
- ^true = (case and? (lte_? 18 age) (lte_? age 120) of
+ ^true = case and? (lte_? 18 age) (lte_? age 120) of
^true = ""
- _ = "Age must be from 18 to 120.")
+ _ = "Age must be from 18 to 120."
_ = "Age must be a canonical natural."
termsMessage ^true = ""
diff --git a/ext/demo-site/demos/shipping.tri b/ext/demo-site/demos/shipping.tri
@@ -20,7 +20,7 @@ baseRate _ _ = 1999
paidRate version zone grams = case lte_? grams 1000 of
^true = baseRate version zone
- _ = add 250 (baseRate version zone)
+ _ = add 250 <| baseRate version zone
freeThreshold 1 = 7500
freeThreshold _ = 10000
@@ -30,12 +30,12 @@ amount version zone subtotal grams = case and? (equal? zone "domestic") (lte_? (
_ = paidRate version zone grams
quote version zone subtotal grams = case and? (isNat? subtotal) (isNat? grams) of
- ^true = (case or? (equal? zone "domestic") (or? (equal? zone "regional") (equal? zone "international")) of
- ^true = (case and? (lte_? subtotal 1000000) (and? (lte_? 1 grams) (lte_? grams 5000)) of
+ ^true = case or? (equal? zone "domestic") (or? (equal? zone "regional") (equal? zone "international")) of
+ ^true = case and? (lte_? subtotal 1000000) (and? (lte_? 1 grams) (lte_? grams 5000)) of
^true = [["status" 0 "ok"] ["policyVersion" 1 version]
["destination" 0 zone] ["shippingCents" 1 (amount version zone subtotal grams)]]
- _ = failure "Subtotal must be <=1000000 cents; grams must be 1..5000.")
- _ = failure "Unknown destination.")
+ _ = failure "Subtotal must be <=1000000 cents; grams must be 1..5000."
+ _ = failure "Unknown destination."
_ = failure "Subtotal and grams must be canonical natural numbers."
v1 zone subtotal grams = quote 1 zone subtotal grams
diff --git a/ext/demo-site/lexer-demo.json b/ext/demo-site/lexer-demo.json
@@ -1,10 +1,10 @@
{
"id": "lexer",
- "source": "!import 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 *\n!import 14282088469cc420dd014700da06998f6bb8dcf990fc885a464c0a1e1858e20d *\n!import 816df1a32853750a1254112aff8ae8c55ca86bf70e809d226969ab1d43a6688b *\n!import 7f87549d587fa8c3be88af0ea988db3adcc3b35f1ffb48973553491b075614db as N\n!export scanWord\n!export scanWhitespace\n\n-- Positioned prefix scans, not whole-input parsers. The remainder begins\n-- at the first nonmatching character. Positions count source characters,\n-- not UTF-8 bytes or display columns: only LF advances the line.\n-- Edit a predicate below and evaluate scanWord \"hello,world\" to compare.\n-- scanWhitespace accepts space, LF and tab, but deliberately not CR.\nreport reply = (let\n payload = parserReplyPayload reply\n end = snd payload\n in [\n [\"matched\" 0 (fst payload)]\n [\"remaining\" 0 (parserReplyRest reply)]\n [\"line\" 1 (lexerPositionLine end)]\n [\"column\" 1 (lexerPositionColumn end)]\n [\"offset\" 1 (lexerPositionOffset end)]\n [\"consumed\" 2 (parserReplyConsumed reply)]])\n\nscan predicate text = report (lexerMapScan reverse\n (lexerScanWhile text lexerStart [] predicate (state character : pair character state)))\nscanWord text = scan (character : N.inRange character 97 122) text\nscanWhitespace text = scan (character : or? (equal? character 32)\n (or? (equal? character 9) (equal? character 10))) text\n",
+ "source": "!import 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 *\n!import 14282088469cc420dd014700da06998f6bb8dcf990fc885a464c0a1e1858e20d *\n!import 816df1a32853750a1254112aff8ae8c55ca86bf70e809d226969ab1d43a6688b *\n!import 7f87549d587fa8c3be88af0ea988db3adcc3b35f1ffb48973553491b075614db as N\n!export scanWord\n!export scanWhitespace\n\n-- Positioned prefix scans, not whole-input parsers. The remainder begins\n-- at the first nonmatching character. Positions count source characters,\n-- not UTF-8 bytes or display columns: only LF advances the line.\n-- Edit a predicate below and evaluate scanWord \"hello,world\" to compare.\n-- scanWhitespace accepts space, LF and tab, but deliberately not CR.\nreport reply = let\n payload = parserReplyPayload reply\n end = snd payload\n in [\n [\"matched\" 0 (fst payload)]\n [\"remaining\" 0 (parserReplyRest reply)]\n [\"line\" 1 (lexerPositionLine end)]\n [\"column\" 1 (lexerPositionColumn end)]\n [\"offset\" 1 (lexerPositionOffset end)]\n [\"consumed\" 2 (parserReplyConsumed reply)]]\n\nscan predicate text = lexerScanWhile text lexerStart [] predicate (state character : pair character state)\n |> lexerMapScan reverse\n |> report\nscanWord text = scan (character : N.inRange character 97 122) text\nscanWhitespace text = scan (character : or? (equal? character 32)\n (or? (equal? character 9) (equal? character 10))) text\n",
"hash": "e756a481b3056a2e76f50316ccf77bfab8b15727b7bec899580c2eb919e668b2",
"mode": "pure",
"compilerSha256": "bd9b236492bb6359a6cc4dcd7c988ac2bf64abea4a1cc1c7233d1d023ca0dd1e",
- "sourceSha256": "ec68af4102c16ef56be01e78a3288bea57af71ca57ab35374a0d12005b5a10a1",
+ "sourceSha256": "2a441d6d881acd6e34cd0caa8179ee1afefbce68c2e5ff1e44fb1768f69eeb3c",
"haskellCases": [
{
"export": "scanWord",
diff --git a/ext/demo-site/packages.json b/ext/demo-site/packages.json
@@ -1871,6 +1871,22 @@
"manifest": "e8bad7dcea5944336083f5bfa3ada63d7542286fbdec0f34dfeb65e9879ba2fc",
"hash": "3bd4df273ab4e4599417a18210ec3ad39e2ace2f7df6c0b7cad62999c4cb52a2",
"title": "lib/tools/format-tri.tri"
+ },
+ {
+ "name": "parser-demo",
+ "version": "0.3.1",
+ "hash": "355e41506f39e05da8c8a72a68aa810a486e062480322bac11af67e86980520e",
+ "manifest": "0c34bfdf800f8bd1b742e375a8f7c555c88d0c827268f684259433e834d94fed",
+ "title": "Parser combinators: complete input, prefixes and committed errors",
+ "demo": "parser"
+ },
+ {
+ "name": "lexer-demo",
+ "version": "0.3.1",
+ "hash": "e756a481b3056a2e76f50316ccf77bfab8b15727b7bec899580c2eb919e668b2",
+ "manifest": "ed99e054e9c862668e165f458d3afcdc56279f766eea7a3886d440aea7252b63",
+ "title": "Located scans: matched prefix, remainder and character positions",
+ "demo": "lexer"
}
]
}
diff --git a/ext/demo-site/parser-demo.json b/ext/demo-site/parser-demo.json
@@ -1,10 +1,10 @@
{
"id": "parser",
- "source": "!import 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 *\n!import 14282088469cc420dd014700da06998f6bb8dcf990fc885a464c0a1e1858e20d *\n!import 7f87549d587fa8c3be88af0ea988db3adcc3b35f1ffb48973553491b075614db as N\n!export parseNames\n!export parsePrefix\n\n-- Lowercase names separated by commas. Try \"red,green\", \"red,,blue\",\n-- \"red,\" and \"red green\". A comma commits to another name.\n-- parseNames requires the entire input; parsePrefix exposes any remainder.\n-- Edit the parser or labels below; valid edits compile locally.\n-- Imports pin complete library interfaces, fetched only when compiling.\nnameParser = parserLabel \"lowercase name\" (parserSome (parserSatisfy (c : N.inRange c 97 122)))\ncommaParser = parserThen (parserExpect 44) parserCommit\nnamesPrefix = parserSepBy1 nameParser commaParser\nnamesComplete = parserBefore namesPrefix (parserLabel \"end of input\" parserEof)\n\nreport reply = (parserMatchReply\n (diagnostic rest consumed committed : [\n [\"status\" 0 \"error\"]\n [\"expected\" 0 (parserDiagnosticExpected diagnostic)]\n [\"remaining\" 0 rest]\n [\"consumed\" 2 consumed]\n [\"committed\" 2 committed]])\n (names rest consumed committed : [\n [\"status\" 0 \"ok\"]\n [\"names\" 0 (intercalate \", \" names)]\n [\"count\" 1 (length names)]\n [\"remaining\" 0 rest]\n [\"consumed\" 2 consumed]\n [\"committed\" 2 committed]])\n reply)\nparseNames text = report (namesComplete text)\nparsePrefix text = report (namesPrefix text)\n",
+ "source": "!import 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 *\n!import 14282088469cc420dd014700da06998f6bb8dcf990fc885a464c0a1e1858e20d *\n!import 7f87549d587fa8c3be88af0ea988db3adcc3b35f1ffb48973553491b075614db as N\n!export parseNames\n!export parsePrefix\n\n-- Lowercase names separated by commas. Try \"red,green\", \"red,,blue\",\n-- \"red,\" and \"red green\". A comma commits to another name.\n-- parseNames requires the entire input; parsePrefix exposes any remainder.\n-- Edit the parser or labels below; valid edits compile locally.\n-- Imports pin complete library interfaces, fetched only when compiling.\nnameParser = parserSatisfy (c : N.inRange c 97 122)\n |> parserSome\n |> parserLabel \"lowercase name\"\ncommaParser = parserThen (parserExpect 44) parserCommit\nnamesPrefix = parserSepBy1 nameParser commaParser\nnamesComplete = parserBefore namesPrefix <| parserLabel \"end of input\" parserEof\n\nreport reply = parserMatchReply\n (diagnostic rest consumed committed : [\n [\"status\" 0 \"error\"]\n [\"expected\" 0 (parserDiagnosticExpected diagnostic)]\n [\"remaining\" 0 rest]\n [\"consumed\" 2 consumed]\n [\"committed\" 2 committed]])\n (names rest consumed committed : [\n [\"status\" 0 \"ok\"]\n [\"names\" 0 (intercalate \", \" names)]\n [\"count\" 1 (length names)]\n [\"remaining\" 0 rest]\n [\"consumed\" 2 consumed]\n [\"committed\" 2 committed]])\n reply\nparseNames text = namesComplete text |> report\nparsePrefix text = namesPrefix text |> report\n",
"hash": "355e41506f39e05da8c8a72a68aa810a486e062480322bac11af67e86980520e",
"mode": "pure",
"compilerSha256": "bd9b236492bb6359a6cc4dcd7c988ac2bf64abea4a1cc1c7233d1d023ca0dd1e",
- "sourceSha256": "18aa0a464dabb595749abdab5edb1bb11c4356c0e753cf0985a644901bb5c241",
+ "sourceSha256": "9c005993cf43c07ec5c7e4f7f420b2f63c66e605d9522ab4b7e044de5e6cb8df",
"haskellCases": [
{
"export": "parseNames",