packages feed

phino 0.0.157 → 0.0.158

raw patch · 132 files changed

+6684/−6892 lines, 132 filesdep −hspec-corePVP: major bump suggested

API removals or changes: PVP suggests a major version bump

Dependencies removed: hspec-core

API changes (from Hackage documentation)

+ Deps: EvBoxed :: Int -> Expression -> Evaluation
+ Deps: [_parts] :: Nesting -> Map Int Int
+ Deps: fragment :: Text
+ Deps: namedFormation :: Expression -> a -> Named a -> Named a
+ Lambdas: Dataized :: Meta -> Meta -> Line
+ Yaml: shiftYamlError :: String -> String
- Bytes: btsToNum :: Bytes -> Either Int Double
+ Bytes: btsToNum :: Bytes -> Double
- CST: EX_NUMBER :: Either Int Double -> TAB -> [Argument] -> EXPRESSION
+ CST: EX_NUMBER :: Double -> TAB -> [Argument] -> EXPRESSION
- Deps: Nesting :: Int -> [(Int, Int)] -> [(Int, String)] -> Named Expression -> Map Int Int -> Map Int Int -> Maybe (Either Int Bytes, String) -> [(Int, (Judgment, Expression))] -> [(Int, Judgment, Expression)] -> [(Int, Expression, String)] -> Map Attribute Expression -> Map [Maybe Attribute] [(Expression, [Binding])] -> Int -> Nesting
+ Deps: Nesting :: Int -> [(Int, Int)] -> [(Int, String)] -> Named Expression -> Map Int Int -> Map Int Int -> Map Int Int -> Maybe (Either Int Bytes, String) -> [(Int, (Judgment, Expression))] -> [(Int, Judgment, Expression)] -> [(Int, Expression, String)] -> Map Attribute Expression -> Map [Maybe Attribute] [(Expression, [Binding])] -> Int -> Nesting
- Deps: Protocol :: Int -> Named Text -> [(Int, Int)] -> Bool -> Named Expression -> Map Int Int -> Map Int Int -> Map Int Int -> Maybe (Either Int Bytes, String) -> [(Int, Maybe String, String, Bool)] -> [(Int, Expression, String)] -> Protocol
+ Deps: Protocol :: Int -> Named Text -> [(Int, Int)] -> Bool -> Named Expression -> Map Int Int -> Map Int Int -> Map Int Int -> Maybe (Either Int Bytes, String) -> [(Int, Maybe (Judgment, String), String, Bool)] -> [(Int, Expression, String)] -> Protocol
- Deps: [_blocks] :: Protocol -> [(Int, Maybe String, String, Bool)]
+ Deps: [_blocks] :: Protocol -> [(Int, Maybe (Judgment, String), String, Bool)]
- Morph: Undataizable :: Expression -> State -> ReduceException
+ Morph: Undataizable :: Expression -> NonEmpty Rewritten -> State -> ReduceException

Files

README.md view
@@ -34,2676 +34,793 @@  ```bash cabal update-cabal install --overwrite-policy=always phino-0.0.156-phino --version-```--Or download binary from the internet using [curl](https://curl.se/) or-[wget](https://en.wikipedia.org/wiki/Wget):--```bash-sudo curl -o /usr/local/bin/phino http://phino.objectionary.com/releases/macos-15/phino-latest-sudo chmod +x /usr/local/bin/phino-phino --version-```--Download paths are:--* Ubuntu 22.04: <http://phino.objectionary.com/releases/ubuntu-22.04/phino-latest>-* Ubuntu 24.04: <http://phino.objectionary.com/releases/ubuntu-24.04/phino-latest>-* MacOS (ARM): <http://phino.objectionary.com/releases/macos-15/phino-latest>-* MacOS (Intel): <http://phino.objectionary.com/releases/macos-14-large/phino-latest>-* Windows: <http://phino.objectionary.com/releases/windows-2022/phino-latest.exe>--In a [GitHub Actions][gha] workflow, one step downloads the binary-for the runner and puts it on the `PATH`:--```yaml-- uses: objectionary/phino@0.0.156-  with:-    version-file: phino-version.txt-- run: phino --version-```--The file holds the version to install, such as `0.0.150`, and the-whitespace around it is ignored.-Give `version: 0.0.150` instead of `version-file` to write the version-in the workflow itself.-The step fails if no binary is released for the runner, such as-Linux on ARM, or if `phino --version` prints another version.--## Build--To build `phino` from source, clone this repository:--```bash-git clone git@github.com:objectionary/phino.git-cd phino-```--Then, run the following command (ensure you have [Cabal][cabal] installed):--```bash-cabal build all-```--Next, run this command to install `phino` system-wide:--```bash-sudo cp "$(cabal list-bin phino)" /usr/local/bin/phino-```--Verify that `phino` is installed correctly:--```bash-$ phino --version-0.0.0-```--You can ensure scripts are run with a specific version of `phino` using-the `--pin` global option. It exits with an error when the version supplied-doesn't match the installed one:--```bash-phino --pin=0.0.0.67 dataize hello.phi-```--The `--pin-file` global option does the same, but reads the version from-a file, ignoring the whitespace around it:--```bash-phino --pin-file=version.txt dataize hello.phi-```--## Dataize--Then, you dataize the expression:--```bash-$ phino dataize hello.phi-68-65-6C-6C-6F-```--### Symbolic λ functions--Which λ functions exist is a property of the object model being dataized, not-of the calculus, so `phino` implements none of them. They come from a YAML file-given with `--symbolic`, one entry per λ function:--```yaml-- λ: L_number_(plus|times)-  dataize:-    𝛿1: $.ρ-    𝛿2: $.x-  𝑛: Φ.number( φ ↦ ⟦ λ ⤍ 𝜎 ⟧ )-```--The `λ` of an entry is the λ names it answers for, as a regular expression, so-the one above stands for `L_number_plus` and `L_number_times`. Under `dataize`-stand the operands it brings down to data through 𝔻, each binding a bytes meta-`𝛿1`, `𝛿2` and so on; under `morph` the operands it reduces to a normal-form through 𝕄, each binding an expression meta `𝑛1`, `𝑛2`. Both blocks are-terms of the calculus, read against the formation being fired, so `ξ` is that-formation and `$.x` its `x`, while `Φ` is the universe. Every entry numbers its-own metas from `𝛿1` and `𝑛1`, and the entry is what tells two `𝛿1` apart.-An operand whose reduction gets stuck, is cut or runs out of steps gets the-firing stuck right there: no later block runs, and `--partial` parks the-firing as a whole instead of handing the operand on as it was written.--The term under `𝑛` is what the firing answers with. `phino` normalizes it-exactly as it normalizes anything else, so `--protocol`, `--partial` and-`--max-steps` work on it unchanged. It may name any meta the entry bound,-those of the two blocks below among them.--An entry reads no key but `λ`, `dataize`, `morph`, `evaluate`, `defer`, `stay`,-`rewrite`, `symbolize`, `join` and `𝑛`, and any other key, such as a-misspelled `joinn`, refuses the whole file, so a typo never drops a block-without a word.--### Firing what an operand came to--𝕄 fires a λ function only where a dispatch reads the formation carrying it, so-an operand reduced under `morph` may come to a formation whose λ function-nobody fired, such as a call the program passed as an argument and never read.-There is a third block, `evaluate`, and each line of it fires such a formation-through 𝔼 and binds an expression meta of its own to the answer:--```yaml-- λ: L_apply-  morph:-    𝑛1: $.f-  evaluate:-    𝑛2: 𝑛1-  𝑛: 𝑛2-```--The right-hand side of a line names a meta bound by `morph` or by another line-of the entry, and nothing else. A formation with a λ function is fired by the-entry its λ name matches, the way 𝕄 fires it, and the line binds its meta to-the normal form of the answer. A formation with no λ function is no call and-stays as it is, and so does `⊥`. So does a bare symbol, `⟦ λ ⤍ 𝜎1 ⟧`, since it-already stands for a value nobody worked out and no entry answers it. A λ name-no entry answers gets stuck, as it-does anywhere else, and `--partial` parks it. Any other term, such as a-dispatch a `rewrite` line, described below, took out of its formation, gets an-entry stuck when it computes an `𝑛`, while an entry that joins passes the term-on as it is, since a join compares whatever its sides come to. An answer is-not fired again,-even when it is one more call: firing it takes one more line, so how many-calls an entry makes is written in its file and not guessed by `phino`. Each-firing of a line is one step deeper, the way a frame of 𝕄 or 𝔻 is, so a-recursion running through `evaluate` lines alone spends `--max-steps` and is-cut by `--acyclic` like any other.--The protocol writes the firing of a line as a block of its own, commented with-the meta it fired, `𝔼(L_second):  # 𝔼(𝑛2)`, and the line binding the answer-the same way, `𝑛3·1 := 𝑛·3·2  # 𝔼(𝑛2)`.--### Deferring a copy over an unknown--A term 𝕄 leaves may be a copy of an object of the world, such as-`Φ.step( tup ↦ Φ.items.tail )`, one of whose arguments reads to a bare symbol-`⟦ λ ⤍ 𝜎1 ⟧`. Nothing can be worked out about such a copy: its `φ` reads-`tup.next`, an attribute of `𝜎1` nobody knows, so morphing it gets stuck where-the symbol stands. There is a fourth block, `defer`, and each line of it reads-a meta and binds another, the way a line of `evaluate` does. When the term is a-copy one of whose arguments, `φ` and `ρ` aside, reads to a bare symbol, the-line binds its meta to a fresh symbol and writes what the symbol stands for:--```yaml-- λ: L_step-  morph:-    𝑛1: $.x-  defer:-    𝑛2: 𝑛1-  𝑛: 𝑛2-```--```text-deferred 𝜎3 := Φ.step( tup ↦ 𝜎1:λ )  # 𝕄(𝑛1.φ)-```--The markup writes it as `<deferred symbol="𝜎3" … of="Φ.step">`, holding a-`<with>` element with a `<symbol attr="tup">𝜎1</symbol>` per argument. When-the world declares no object the copy was made of, `of` names the `<formed>`-element that spells the copy out, and there is no `<with>`. Any other term,-a copy whose arguments read to no bare symbol among them, is bound as it-stands, as `𝑛2·1 := 𝑛·1·1  # 𝑛1`. A `defer` line asks nothing of the `φ` of a-copy, so it defers one whose `φ` reads nothing of its own object too, such as-`⟦ φ ↦ Φ.bool( … ) ⟧`.--An argument that is no bare symbol, such as `w ↦ Φ.inc( ⟦ Δ ⤍ 2A- ⟧ )`, is-worked out before the line is written. The entry morphs it in the copy, fires-what that comes to, the way an `evaluate` line does, and writes every step to-the protocol under `𝕄(𝑛1.w):`. The line then names the object the firing-answered, `Φ.box( x ↦ 𝜎1:λ, w ↦ 𝑛·2·2 )`, and so does the markup,-`<meta attr="w">𝑛·2·3</meta>`, with a meta of its own count. A reader follows-the meta to the application that made the object. An argument whose morph or-firing gets stuck, is cut or runs out of steps gets the firing stuck right-there, as an operand does, and no `deferred` line is written.--A `join` block, described below, defers nothing by itself. An entry that-joins defers a side with a `defer` line of its own, reading the meta the side-was morphed to, so a side that is such a copy comes to a fresh symbol rather-than getting stuck inside the unknown. A side written as a path from `$` is-morphed and taken as it comes, a copy over a bare symbol included.--### Joining on a condition nobody decided--A branching λ function answers neither of its branches. Which one the program-takes is decided by a value nobody worked out, so handing one of them through-would drop the branch point from the program altogether, and joining two-different terms into one would make up a value no run of the program ever has.-`join` is the fifth block: it names the condition and the two sides, and the-entry answers the one term both sides join into, or gets stuck when they do-not join.--```yaml-- λ: L_fork-  dataize:-    𝛿1: $.φ-  join:-    on: 𝛿1-    left: $.left-    right: $.right-```--`on` names the condition, a bytes meta the `dataize` block bound or a path-from `$`, described below, and `left` and `right` are paths from `$`, read-against the formation being fired the way-an operand is, or metas the `morph` block or an `evaluate`, `stay`, `rewrite`,-`symbolize` or `defer` line bound, as described below. An entry-with a `join` block has no `𝑛`, since the term it joins is what it answers-with: an entry joins or it computes, never both. A block missing one of the-three keys, holding another but `otherwise`, described below, or naming under-`on` anything but a meta of `dataize` or a path from `$` is refused when the-file is read, and-so is a side reading a meta inside a term, such as `𝑛1.x`, or naming a meta-that none of `morph`, `evaluate`, `stay`, `rewrite`, `symbolize` and `defer`-binds.--A join morphs both of its sides first, each where it stands in the universe,-under a block of its own, `𝕄(Φ.a🌵3):` for the left side and `𝕄(Φ.a🌵4):` for-the right one. A side that is a meta was morphed already, by the `morph`-block, so the join takes the term the meta is bound to as it is and morphs-nothing again. When the two come to the same term, nothing depends on the-condition: the join mints nothing and answers that term, and the line binding-it is commented with both sides, `𝑛·2·5 := 𝑛·2·3  # 𝕄(ξ.left), 𝕄(ξ.right)`.-When the two differ in their symbols alone, such as `Φ.number( φ ↦ 𝜎3:λ )` and-`Φ.number( φ ↦ 𝜎4:λ )`, the join makes one term of them: every pair of symbols-standing at the same place gets a fresh symbol, which a line of its own says-is the left one where the condition holds and the right one where it does not:--```text-joined 𝜎5 := 𝛿1·2 ? 𝜎3 : 𝜎4-```--The condition is named by the meta its `dataize` operand bound, `𝛿1·2`,-since the join reads the datum that line holds and runs no 𝔻 of its own. A-pair met twice gets the same fresh symbol both times. Nothing is bound to the-condition, which stays the unknown it was.--Two sides that differ in anything else, a datum, a λ name or the shape of a-term, do not join, and neither do a side holding a datum and a side holding a-symbol, unless a `symbolize` line turned that datum into a symbol first, as-described below. Such a firing closes with a line naming the two sides it-could not join, the left one first:--```text-diverged 𝑛·2·3 vs 𝑛·2·5  # they differ at .x, and the join names no otherwise-```--A side is named by the line that bound it: the line its morph wrote, or the-line of `morph`, `evaluate`, `stay`, `rewrite`, `symbolize` or `defer` whose-meta it reads, such as `𝑛1·1`. A side that is a bare symbol is-written as that symbol, and a side no line named, such as a datum its morph-left as it was, as the term it is. Past-that line the firing gets stuck the way a λ function no entry answers does:-`--partial` parks the site the entry was fired at, and without it the command-fails with `No entry of --symbolic answers the λ function 'L_fork'`. A side-whose morph gets stuck or is cut gets the firing stuck at once, the way an-operand of `morph` does.--The comment of the line tells where the two sides differ and why the join-could not make one formation of them, so a reader knows what to change in-the file without rebuilding both sides by hand. The place is a path of-attributes, such as `.x` or `.φ.Δ`. It walks down both sides through the-formations they share and ends at the first attribute they bind differently,-or that only one of them binds. It stops at an application or a dispatch, and-the comment leaves it out when the two sides differ as a whole, such as a-formation and a dispatch. A ρ counts like any other attribute, so the comment-may name a `.ρ` that `--hide-rho` hides from the terms. The reason is one of-three: the join names no `otherwise`, described below, a side is no-formation, as in `the right side is no formation`, or a side has a Δ or a λ,-as in `the left side has λ`.--Two formations of different shapes may still stand for one object, when-whoever reads it asks it only for attributes both sides lead to alike. A-block may name a λ function under a fourth key, `otherwise`, and then such a-firing answers a formation binding every attribute of both sides to a copy-of that function, which asks a copy of each side for the attribute:--```yaml-  join:-    on: 𝛿1-    left: 𝑛1-    right: 𝑛2-    otherwise: L_join-```--```text-⟦-  x ↦ ∅,-  left🌵 ↦ 𝑛1( x ↦ ξ.x ),-  right🌵 ↦ 𝑛2,-  φ ↦ ⟦-    ρ ↦ ∅, λ ⤍ L_join, on🌵 ↦ ⟦ Δ ⤍ 𝛿1 ⟧,-    left🌵 ↦ ρ.left🌵.φ, right🌵 ↦ ρ.right🌵.φ-  ⟧,-  if ↦ ⟦-    ρ ↦ ∅, λ ⤍ L_join, left ↦ ∅, right ↦ ∅, on🌵 ↦ ⟦ Δ ⤍ 𝛿1 ⟧,-    left🌵 ↦ ρ.left🌵.if( left ↦ ξ.left, right ↦ ξ.right ),-    right🌵 ↦ ρ.right🌵.if( left ↦ ξ.left, right ↦ ξ.right )-  ⟧-⟧-```--Here `𝑛1` and `𝑛2` stand for the two sides, `x` for a void of `𝑛1`, `left`-and `right` for the voids of the formations both sides bind `if` to, and-`𝛿1` for the bytes the condition came down to. When the condition is a-symbol, such as `𝜎7`, `on🌵` holds that symbol, `⟦ λ ⤍ 𝜎7 ⟧`, and not the 42-every symbol is dataized to, so every copy still names the unknown it decides-by. So asking the formation for an attribute means the same as asking-`on🌵.if( left🌵, right🌵 )` for it. Every name the formation adds carries-the 🌵 mark, as the names phino makes up do, such as `a🌵14`, since no-program can spell it: an attribute a side binds, such as the `left` and-`right` voids of the `if` of a bool in EO, never clashes with them.--The formation leaves void every attribute either side leaves void, once even-when both sides leave it void, and holds each side under `left🌵` or-`right🌵` applied to its own voids. An argument handed to the formation thus-reaches the side through `ξ`, while the side keeps the ρ it binds, which its-own attributes may read. Every copy of the function asks a side for an-attribute through its ρ, which the formation fills. The ρ of either side gets-no copy of the function, nor does an attribute whose name ends with 🌵, since-a side may be a formation a join fabricated. An attribute only one side binds-gets its copy too, since the other side may reach it through `φ`. A side-holding a datum or a λ, such as a bare symbol `⟦ λ ⤍ 𝜎7 ⟧`, keeps what it-stands for where no attribute reaches it, so no such formation means the same-as the two sides, and the firing writes `diverged` as it does with no-`otherwise` at all, its comment naming the side and what it has, as in-`the left side has Δ`.--A copy of the function leaves void every void of the formations the sides-bind its attribute to, once even when both leave it void, and those of the-left side first, so an argument handed over by position reaches the same void-on both sides. It applies a side binding the attribute to a formation to the-voids of that formation alone, since applying a formation to an attribute it-lacks gives ⊥, and any other side to every void it leaves, since phino sees-no void of a term it has not morphed. A method of the formation thus takes-its arguments, as in `if( left ↦ …, right ↦ … )`, and hands them to both-sides.--The firing writes a line naming the formation by the two sides, in place of-`diverged`, and spells the formation out in its comment:--```text-fabricated 𝑛·2·8 := 𝑛1·1 and 𝑛2·1  # ⟦ left🌵 ↦ 𝑛1·1, …, φ ↦ ⟦ … ⟧, … ⟧-```--phino implements no `L_join`: the file defines it, as an entry that joins-`$.left🌵` and `$.right🌵` on `$.on🌵`:--```yaml-- λ: L_join-  join:-    on: $.on🌵-    left: $.left🌵-    right: $.right🌵-    otherwise: L_join-```--A path under `on` dataizes nothing: the firing reads the bytes or the symbol-the attribute holds off the formation it fires, so the condition is not-brought down a second time, and it gets stuck when the path leads to neither.-Its `joined`-line names the condition by the path, as `joined 𝜎4 := ξ.on🌵 ? 𝜎2 : 𝜎3`. A-side that is no formation, or a formation holding a datum, which no attribute-reaches, still gets the firing stuck.--A side may raise. EO raises with an object, a copy of-`⟦ message ↦ ∅, λ ⤍ L_throw ⟧`, whose λ function the file leaves out on-purpose, so that an error of the program stays an error at runtime. Such a-side is a term like any other, and it joins with no value the other side-has, so the firing gets stuck and `--partial` parks it:--```bash-$ cat raise.phi-⟦-  bytes ↦ ⟦ φ ↦ ∅ ⟧,-  number ↦ ⟦ φ ↦ ∅ ⟧,-  error ↦ ⟦ message ↦ ∅, λ ⤍ L_throw ⟧,-  bool ↦ ⟦ if ↦ ∅ ⟧,-  check ↦ ⟦ λ ⤍ L_check ⟧,-  demo ↦ ⟦ a ↦ Φ.check.if( 7, Φ.error( "negative" ) ) ⟧-⟧-$ cat raise.yaml-- λ: L_check-  𝑛: Φ.bool( if ↦ ⟦ λ ⤍ L_fork, then ↦ ∅, else ↦ ∅, φ ↦ ⟦ λ ⤍ 𝜎 ⟧ ⟧ )-- λ: L_fork-  dataize:-    𝛿1: $.φ-  join:-    on: 𝛿1-    left: $.then-    right: $.else-$ phino dataize --symbolic=raise.yaml --locator=Q.demo.a --partial \-    --sweet --hide-rho raise.phi-⟦ λ ⤍ L_fork, then ↦ 7, else ↦ Φ.error( "negative" ), φ ↦ 𝜎1:λ ⟧-```--The `then` side comes to the bytes of 7 and the `else` side to the copy of-`error`, so the protocol ends in `diverged 𝑛·2·1 vs 𝑛·2·2`, commented with-`they differ at .φ, and the join names no otherwise`, and the command prints-the `if` it parked.-Without `--partial` the command fails there, with-`No entry of --symbolic answers the λ function 'L_fork'`.--### Dropping the ρ of a dispatch--There is a sixth block, `stay`, and it reduces nothing. Each line of it takes-the term another meta of the entry is bound to and binds an expression meta of-its own to that term with every `( ρ ↦ … )` dropped that is applied to a-dispatch:--```yaml-- λ: L_fork-  dataize:-    𝛿1: $.φ-  morph:-    𝑛1: $.then-    𝑛2: $.else-  stay:-    𝑛3: 𝑛1-    𝑛4: 𝑛2-  join:-    on: 𝛿1-    left: 𝑛3-    right: 𝑛4-```--The right-hand side of a line names a meta bound by `morph` or by another line-of the entry, and nothing else. Every position of the term is walked, the-methods and the `ρ` of a formation among them, and `𝑒.𝜏( ρ ↦ 𝑒2 )` becomes-`𝑒.𝜏`. So does a dispatch heading a chain of applications that bind no `ρ`:-`𝑒.𝜏( φ ↦ 𝑒3 )( ρ ↦ 𝑒2 )` becomes `𝑒.𝜏( φ ↦ 𝑒3 )`. A `ρ` applied to anything-else, such as a formation, stays.--Dropping it changes no value. The `dot` rule answers a dispatch as-`𝑛1( ρ ↦ ⟦…⟧ )`, and then `copy` fills a void `ρ` of the body, `stay` keeps a-bound one and `skip` drops it where the body declares none. So once `𝑒.𝜏`-comes to a formation, a later `( ρ ↦ 𝑒2 )` meets a `ρ` that is bound or not-there, and `stay` or `skip` drops it. The block only drops it earlier, before-the dispatch is reduced.--This is how a join brings two numbers to one shape. In EO `bytes.as-number`-reads its `data` as `ρ`, so a branch computed through it comes to-`Φ.number( φ ↦ Φ.bytes( φ ↦ 𝜎1 )( ρ ↦ ⟦ … ⟧ ) )`, with the copy hung on the-bytes as their `ρ`, while a branch made of no object comes to-`Φ.number( φ ↦ Φ.bytes( φ ↦ 𝜎2 ) )`. The join compares the two as written,-finds a shape apart, and gets stuck; after `stay` they differ in their-symbols alone, and the join makes one term of them. The protocol writes each-line of the block. A line that dropped a `ρ` is commented `stay(…)` with the-meta it took, so a reader sees that the term changed and what changed it, while-a line that dropped nothing is commented with the meta alone:--```text-𝑛3·1 := Φ.number( φ ↦ Φ.bytes( φ ↦ 𝜎1:λ ) ):φ  # stay(𝑛1)-𝑛4·1 := 𝑛2·1  # 𝑛2-joined 𝜎4 := 𝛿1·1 ? 𝜎1 : 𝜎2-```--### Standing a symbol for a datum--There is a seventh block, `symbolize`, and it reduces nothing either. Each line-of it takes the term another meta of the entry is bound to and binds an-expression meta of its own to that term with every datum it holds turned into-a fresh symbol:--```yaml-- λ: L_fork-  dataize:-    𝛿1: $.φ-  morph:-    𝑛1: $.then-    𝑛2: $.else-  symbolize:-    𝑛3: 𝑛1-    𝑛4: 𝑛2-  join:-    on: 𝛿1-    left: 𝑛3-    right: 𝑛4-```--The right-hand side of a line names a meta bound by `morph` or by another line-of the entry, and nothing else. The whole term is walked, the way a `rewrite`-line walks it: every binding of every formation, `ρ` and methods among them,-every argument of every application, named or positional, and the target of-every dispatch. A `Δ ⤍ 𝑏` met anywhere becomes `λ ⤍ 𝜎4`, a symbol minted-fresh, the way a bare `𝜎` of an answer is. A datum off the `φ` chain counts-too, since a value may sit there: the bool a `rewrite` line makes of a literal-holds its datum inside the formation bound to `if`.--This is how a join makes one term of a literal and a value some firing-computed. In EO `x.gt( 0 ).if( 1, x )` has one side that is the literal `1`, a-number whose bytes hold the datum `3F-F0-…`, and another side that is a number-whose bytes hold a symbol. The two are numbers of one shape and differ in the-value alone, yet a datum against a symbol is no pair of symbols, so the two do-not join. After `symbolize` both sides hold a symbol where the value stands,-and the join makes one term of them. The protocol writes every symbol the-block minted with the datum it stands for, so a reader of the joined term can-tell that `𝜎4` was `1` on the left side. A line that turned a datum into a-symbol is commented `symbolize(…)` with the meta it took, while a line whose-term held no datum is commented with the meta alone:--```text-symbolized 𝜎4 := 3F-F0-00-00-00-00-00-00  # 1.0-𝑛3·1 := Φ.number( φ ↦ Φ.bytes( φ ↦ 𝜎4:λ ) ):φ  # symbolize(𝑛1)-𝑛4·1 := 𝑛2·1  # 𝑛2-joined 𝜎5 := 𝛿1·1 ? 𝜎4 : 𝜎2-```--A line sees its own side alone, so a datum both sides share becomes two-symbols and then one joined symbol, and the joined term loses that datum. A-file that wants to keep it leaves the block out, and an entry without-`symbolize` writes the same protocol it wrote before.--The blocks chain, and an entry that joins reads as a pipeline, every step of-it on its own line. A side may go through a `stay` line, then an `evaluate`-line, then a `symbolize` line, then a `defer` line, so that a call 𝕄 left-unfired is fired, a copy over a bare symbol is deferred, and two sides of one-shape still join:--```yaml-- λ: L_fork-  dataize:-    𝛿1: $.φ-  morph:-    𝑛1: $.then-    𝑛2: $.else-  stay:-    𝑛3: 𝑛1-    𝑛4: 𝑛2-  evaluate:-    𝑛5: 𝑛3-    𝑛6: 𝑛4-  symbolize:-    𝑛7: 𝑛5-    𝑛8: 𝑛6-  defer:-    𝑛9: 𝑛7-    𝑛10: 𝑛8-  join:-    on: 𝛿1-    left: 𝑛9-    right: 𝑛10-```--An `evaluate` line here works as it does in an entry that computes, except-that it passes on a term that is no formation instead of getting stuck. In EO-`bool.and` is `flag.if( FF-.eq( x ), false )`, so one side morphs down to the-`eq` of bytes with its λ function unfired, which could never join with the-bool on the other side. The line fires it, and the side comes to a bool too,-while the other side is a bool already, a formation with no λ function, and-its line leaves it as it is. A `defer` line here works as it does in an entry-that computes too: it answers a copy over a bare symbol with a fresh symbol,-writes the `deferred …` line sited at the meta it read, such as `# 𝕄(𝑛8.φ)`,-and binds any other term as it stands.--### Rewriting a term with rules--There is an eighth block, `rewrite`, and it reduces nothing either. Each line of-it takes the term another meta of the entry is bound to, named under `of`,-applies the rules listed under `rules`, spelled with the `pattern`, `result`,-`when`, `where` and meta variables a rule file uses, and binds an expression-meta of its own to the outcome:--```yaml-- λ: L_fork-  dataize:-    𝛿1: $.c-  morph:-    𝑛1: $.a-    𝑛2: $.b-  rewrite:-    𝑛3:-      of: 𝑛1-      rules: &bool-        - name: true-literal-          pattern: Φ.true-          result: Φ.bool( if ↦ ⟦ Δ ⤍ 01- ⟧ )-        - name: false-literal-          pattern: Φ.false-          result: Φ.bool( if ↦ ⟦ Δ ⤍ 00- ⟧ )-    𝑛4:-      of: 𝑛2-      rules: *bool-  join:-    on: 𝛿1-    left: 𝑛3-    right: 𝑛4-```--The meta under `of` is one bound by `morph` or by another line of the entry,-and nothing else. A line reads no key but `of` and `rules`, and two lines may-share their rules through a YAML anchor, as above. A rule is tried at every-position of the term, the outermost first. The first rule whose pattern-matches a position rewrites it, and a rewritten position is not walked into-again, so a rule whose result carries its own pattern never loops. Nothing is-normalized afterwards: a rewrite is a substitution the entry vouches for and-not a reduction, exactly as an answer is. A rule writing a symbol such as `𝜎4`-into its result, or reading a meta its pattern never binds, is refused when the-file is read, since only a firing mints a symbol.--This is how a join compares a literal with a bool a computation answered. In-EO the literal `Φ.true` stands where the other side holds `Φ.bool( … )`, the-same value in another shape, and the join gets stuck on the two as written.-After `rewrite` both sides are bools. A line that changed its term is-commented with the block and the meta it read, while a line that changed-nothing writes the plain line of a meta bound to another:--```text-𝑛3·1 := ⟦ φ ↦ 𝜎2:λ, kq ↦ Φ.bool( if ↦ 01-:Δ ) ⟧  # rewrite(𝑛1)-𝑛4·1 := 𝑛2·1  # 𝑛2-joined 𝜎4 := 𝛿1·1 ? 𝜎2 : 𝜎3-```--The markup writes both lines as a plain `<bound>`.--### Morphing what a line made--An operand of `morph` may name a meta another line binds instead of a path-from `$`. It reduces the term that meta is bound to through 𝕄, the way an-operand reading `$` is reduced, and binds a meta of its own to the normal-form:--```yaml-- λ: L_fork-  dataize:-    𝛿1: $.c-  morph:-    𝑛1: $.a-    𝑛2: $.b-    𝑛4: 𝑛3-  rewrite:-    𝑛3:-      of: 𝑛2-      rules:-        - name: unwrap-          pattern: ⟦ φ ↦ 𝑒1 ⟧-          result: 𝑒1-  join:-    on: 𝛿1-    left: 𝑛1-    right: 𝑛4-```--Such an operand is a line of the entry, so it runs once the meta it names is-bound, as described below. It takes the term as the other line bound it and-does not read it against the formation being fired. An operand reading a meta-inside a term, such as `𝑛1.x`, is refused when the file is read, and so is-one naming a meta nothing binds.--This is how a join compares a side that `rewrite` made with a side in its-normal form. Above, `$.b` comes down to `⟦ φ ↦ Φ.t ⟧`, and the rule `unwrap`-takes out `Φ.t`, a path the join cannot compare with the object on the left.-The morph of `𝑛3` brings the path down to the object it reads. In EO the same-happens to the `Φ.bool( … )` a `rewrite` line writes in place of `Φ.false`.-The protocol comments the line with the morph of the meta it read:--```text-𝑛3·1 := Φ.t  # rewrite(𝑛2)-𝑛4·1 := 𝜎3:λ:φ  # 𝕄(𝑛3)-joined 𝜎4 := 𝛿1·1 ? 𝜎2 : 𝜎3-```--### The order of the lines--The lines of `morph`, `evaluate`, `defer`, `stay`, `rewrite` and `symbolize`-run in the order of the metas they read, not in the order of their blocks. A-line of `morph` is an operand naming a meta. A line runs once every meta it-reads is bound, so this entry fires what `x` came to and then fires the answer,-one more call, although the line doing so stands first:--```yaml-- λ: L_twice-  morph:-    𝑛1: $.x-  evaluate:-    𝑛3: 𝑛2-    𝑛2: 𝑛1-  𝑛: 𝑛3-```--The line binding `𝑛3` reads `𝑛2`, which the line under it binds, so that line-runs first. The `dataize` block and the operands of `morph` reading `$` read-no meta and always run before all the others. Where no meta orders two lines,-they run in the order the list above gives their blocks, so a line of `morph`-runs right after the line binding the meta it names, and the lines of one-block run in the order of their metas, `𝑛2` before `𝑛10`. A line reading a-meta nothing binds is refused when the file is read, and so is a line that-reads the meta it binds, directly or through other lines, since it could never-run.--### Symbols--An entry answers, it never computes. The job of these functions is symbolic-morphing: what `5.plus( 6 )` comes to is the arithmetic of the object model and-not `phino`'s, so an entry answers a term carrying a symbol standing for a-value nobody worked out, and the data its `dataize` operands came down to is-not its to read. An answer mentioning a `𝛿` is refused where the file is read.--`𝜎` is a meta of the calculus, beside `𝑛`, `𝛿` and `𝑓`, and it stands where-a λ name stands. In a term, `𝜎1` is a concrete symbol: a λ function nothing-answers, which is what makes the value the term carries unknown. Firing it is-therefore the same question as firing a λ name the `--symbolic` file does not-carry, and gets the same answer: 𝔼 stops there, the protocol records the site as-`unanswered 𝜎1`, and `--partial` leaves the term where it stands. Dispatching-an attribute off a symbol — `⟦ λ ⤍ 𝜎1 ⟧.plus( 5 )` — therefore taints its own-binding and nothing else; what stands beside it still computes. In an answer, a-bare `𝜎` asks for a fresh one, minted as the firing happens and numbered by the-run, so no two unknowns are ever spelled alike. Minting starts after the symbols-the program already carries, so a run over the 𝜑-program an earlier run wrote-never spells a fresh symbol like one already standing there.--Dataizing a symbol never gets stuck. It answers a fixed datum, 42-(`40-45-00-00-00-00-00-00`), the same one for every symbol, so 𝔻 always-answers, a `𝛿` always holds concrete data and no firing ever declines for the-lack of it:--```bash-$ cat sum.phi-⟦-  bytes ↦ ⟦ φ ↦ ∅ ⟧,-  number ↦ ⟦ φ ↦ ∅, plus(ρ, x) ↦ ⟦ λ ⤍ L_number_plus ⟧ ⟧,-  φ ↦ 5.plus( 6 )-⟧-$ phino dataize --symbolic=atoms.yaml --sweet --hide-rho sum.phi-40-45-00-00-00-00-00-00-```--### The keys of the file--Each `λ` is a regular expression, and it must match the whole λ name, so a-plain name such as `L_number_plus` means that one function and nothing else,-while `L_number_.*` stands for every function of `number`. The keys are unique:-nothing tells two entries under one key apart, so a second entry under a key is-unreachable and the file is refused rather than merely redundant.--A λ name no key matches has no λ function at all, so 𝔼 gets stuck on it.-Without `--symbolic` there is no entry at all and every λ function gets stuck:--```bash-$ phino dataize --sweet --hide-rho sum.phi-[ERROR]: No entry of --symbolic answers the λ function 'L_number_plus'-```--The file is read before anything is parsed or reduced, so a key that is no-regular expression, an operand that is no meta of the kind its block binds, or-an answer the calculus cannot read fails the run up front rather than half-way-through a derivation.--### Recording what fired--Every λ function fired on the way to the answer may be recorded in a-machine-readable protocol, with the `--protocol` option. The protocol is a-tree: the run at the top, one block per firing under it, and inside the block-the operands the firing bound and the term it answered with. A judgment started-at a new site opens a block too, and what it does stands under it.--<!-- markdownlint-disable MD013 -->--```bash-$ phino dataize --symbolic=atoms.yaml --protocol=atoms.txt --quiet \-    --sweet --hide-rho sum.phi-$ cat atoms.txt-𝔻(Φ):-  𝕄(Φ):-    𝑛·0·1 := 5-    𝑛·0·2 := 𝑛·0·1.plus( x ↦ 6 )-  𝔼(L_number_plus):-    𝔻(Φ.a🌵0):-      𝕄(Φ.a🌵0):-        𝑛·1·1 := 5-        𝑛·1·2 := Φ.bytes( φ ↦ 40-14-00-00-00-00-00-00:Δ )-      𝛿·1·1 := 40-14-00-00-00-00-00-00  # 5.0-    𝛿1·1 := 𝛿·1·1  # 𝔻(ξ.ρ)-    𝔻(Φ.a🌵1):-      𝕄(Φ.a🌵1):-        𝑛·1·3 := 6-        𝑛·1·4 := Φ.bytes( φ ↦ 40-18-00-00-00-00-00-00:Δ )-      𝛿·1·2 := 40-18-00-00-00-00-00-00  # 6.0-    𝛿2·1 := 𝛿·1·2  # 𝔻(ξ.x)-    𝑛·1·5 := Φ.number( φ ↦ 𝜎1:λ )  # 𝑛-    𝕄(𝑛·1·5):-      𝑛·1·6 := Φ.number( φ ↦ 𝜎1:λ )-    𝑛·1·7 := 𝑛·1·6  # 𝕄(𝑛·1·5)-```--<!-- markdownlint-enable MD013 -->--`𝔻(…)` is the run and the term it was aimed at, `𝕄(…)` where the run is a-morphing, and `𝔼(…)` is one firing, named by the entry that answered it. A line-standing in a block needs no comment to say which judgment made it and where:-the block it stands in says that already. A comment is written only where it-says something the block does not, such as the term an operand was reduced-from, `𝔻(ξ.ρ)`, or the meta an answer was read from, `𝑛`, and every comment-naming a judgment is of that shape: a judgment applied to a term, which is the-intent the value beside it came from. The firings are numbered across the-whole run, in the order they open, so `𝛿1·2` is the value bound to `𝛿1` by-the second firing of the run, whichever λ function that was, `𝑛1·2` the same-for a `morph` meta, and `𝑛·3·2` the answer of the third firing, so-`𝑛1·2 := 𝑛·3·2` reads "the `𝑛1` of this firing is what the third firing-answered". One firing binds a meta once and no two firings-share a number, so every one of these names stands on exactly one line of the-file and a line naming another one points at it and no other.--A nested `𝔻(…):` or `𝕄(…):` line opens a block where a judgment starts at a-site other than the one of the block it stands in, and names that site. What-the judgment does stands one level deeper under it: the objects its-normalization makes, the firings it demands, and the judgments it starts in-turn. A reader therefore sees which judgment made each line and at which site,-rather than a flat list of lines each saying so. 𝔻 of a term that is no-formation asks 𝕄 for the normal form of the same term first, through its-`norm` rule, so a `𝕄(…):` block often stands right inside a `𝔻(…):` block of-the same site, and `𝑛·0·1 := 5` above is made by that 𝕄 and not by 𝔻. A-judgment started at the site of the block it stands in, and by the same-judgment, opens nothing, and what it does joins that block. A block is written-only once a line stands in it, so a judgment that made nothing worth a line-leaves no empty heading behind. The line is no firing: it binds no meta and-takes no number, so the metas of the firings under it are numbered as if it-were not there. A `𝔻(…):` block ends with the datum the `delta` rule found,-such as `𝛿·1·1 := 40-14-00-00-00-00-00-00`, named by the firing it stands in-and a counter of its own, and the operand line right after the block names it.-A datum of eight bytes is commented with the number those bytes hold as an-IEEE 754 double, `# 5.0` here, since that is how a number keeps its data.-In the run above 𝔻 gets into each number the entry brings down, and through-its `φ` into the bytes that number holds, in the same block, since the site is-the same.--An answer stands on two lines and not one. A firing answers the term its entry-wrote and `phino` morphs that term before standing it back into the program, so-`𝑛·1·5` is what the entry wrote, with the symbols this firing minted already in-it, commented with `𝑛` to name the key it was read from, and `𝑛·1·7` is the-normal form 𝕄 made of it, commented with `𝕄(𝑛·1·5)` to say where it came from.-That morphing opens a block of its own, `𝕄(𝑛·1·5):`, named after the term it-started from, since that term stands nowhere in the program a locator could-reach. It is the same morphing every other term goes through, and writing only its-outcome would have the formation of `number` appear in place of the three-tokens the entry wrote with nothing saying why. Whatever that morphing fires-or applies writes its lines between the two, exactly where a firing an operand-took opens its block, so the order the lines come in is the order the work was-done in.--`𝑛·1·6 := Φ.number( φ ↦ 𝜎1:λ )` is an object an application made. The `copy`-rule of normalization fills a void of a formation with the argument it was-given, and so makes a new object, whatever judgment is running and wherever in-the term the application stands. The line binds that object to a fresh `𝑛` and-spells the application: the object applied on the left and the argument it got-on the right, so `5` is `Φ.number( φ ↦ … )` in sugar. The `ρ` a dispatch gives-the attribute it takes writes no line. The block the line stands in names the-judgment that normalized and the site it stood at. The name is counted with the-metas of the firing the line stands in, `𝑛·1·5` before it and `𝑛·1·7` after it,-and an object made outside every firing is counted under `𝑛·0`. From then on-every line spells the object by that name rather than as the formation it is,-and the application by the same name, since `copy` makes the same object of the-same application wherever it stands: `𝑛·1·1` is the object `5` made,-`𝑛·0·1.plus( x ↦ 6 )` is the `plus` of the first `5` applied to `6`, one line-for one call, and `𝑛·1·7 := 𝑛·1·6` says the firing answered with the object 𝕄-made of what the entry wrote. Such a line names its head and its argument that-way, each on its own, and never the application as a whole, so a line spells out-every application, even one making an object an earlier one already made, the-way `𝑛·1·1 := 5` does, and a later line names the latest of them. Only an object-still standing as it was made is named: once a rule rewrote a part of it, it is-another object and it is spelled out again.--Where an operand came down to the datum a symbol stands for, the protocol writes-`𝔻(𝜎1:λ)` in place of that 42 (`𝜎1:λ` is the formation `⟦ λ ⤍ 𝜎1 ⟧`, in the-sugar every sweet term is written with), so a reader sees that the value was-manufactured rather than read out of the program. A `𝜎` is the name of a λ-function and no term of its own, so 𝔻 is applied to the formation carrying it-and never to the name alone.--A `join` block writes every symbol it minted,-`joined 𝜎5 := 𝛿1·2 ? 𝜎3 : 𝜎4`, naming the condition by the meta of its-operand line, and nothing is bound to the condition, which stays the unknown-it was. A comment carries the letter of a judgment exactly where a judgment-made the value and the block around the line does not already say so.--The site of a firing is a locator, and the block a firing stands in names it,-under the letter of the judgment that asked for the firing: 𝔼 is fired by the-`ml` rule of morphing and by the `fire` rule of dataization, so a firing in the-block `𝕄(Φ.demo.a.φ):` is a λ function fired while 𝕄 was reducing that-binding, and one standing right in `𝔻(Φ):` was fired because dataization-demanded data of `Φ`. A chain such as `5.plus( 6 ).plus( 7 )` writes both: the-inner call is fired inside the `𝕄(Φ):` block, while 𝕄 reduces the head of the-outer dispatch, and the outer one right in `𝔻(Φ):`, because 𝔻 asked for the-data. A line standing right in a firing has no block of its own around it, so-a line there that a judgment wrote at a site no block names, such as-`starved …` below, carries that judgment and site as its comment. The site-itself is where in the program the firing-belongs: the term the run was aimed at, so `Φ` for a run that was aimed at-nothing in particular. An operand of a firing is reduced bound to a synthetic-attribute of the universe (see `--inside` below), so a λ function fired while-it came down is written under that attribute and not under the site of the-firing that asked for it, and a formation an `evaluate` line fires is written-under the meta it fired, `𝔼(𝑛2)`.--An operand line ends in the judgment that reduced it and the term it was-reduced from, written as a comment after two spaces and `#`. The value alone-says what the meta was bound to and neither what it was bound from nor what-was done to it, so `𝛿1·1 := 𝛿·1·1  # 𝔻(ξ.ρ)` reads "the `𝛿1` of this firing-is the `ρ` of the formation brought down through 𝔻, and it came down to the-datum `𝛿·1·1` above", where a `morph` operand reads `𝑛1·5 := 𝑛·3·4  # 𝕄(ξ.then)`-and says that the `then` of the formation reached its normal form through 𝕄.-Which of the two judgments ran is the whole difference between a line ending-in data and one ending in a term. It is the very term the entry wrote under-that meta, spelled the way the calculus reads it — `$` is read as `ξ` — so a-reader never has to open the `--symbolic` file beside the protocol and match-every line by λ name and meta number.--`unanswered …` is a λ name no entry answers, standing where the block of its-firing would have stood. Nothing fired, so nothing opens under it. The line is-commented with the judgment that asked and the formation it was asking about,-`𝕄(L_none:λ)`, which no block names, the way an operand line is commented-with the term it was reduced from: 𝔼 is fired by the `ml` rule of morphing-and by the `fire` rule of dataization, so the letter says where in the-reduction the site stands and the term says which object the λ function that-could not fire belongs to. It is written whether or not `--partial` goes on-to park the run, since the protocol records what 𝔼 was asked for, and a-question it could not answer belongs there as much-as one it could — once per site and not once per attempt, since a site-`--partial` parks stays in the residue, where a later step may meet it again:--<!-- markdownlint-disable MD013 -->--```bash-$ phino dataize --symbolic=atoms.yaml --protocol=atoms.txt --quiet \-    --sweet --hide-rho stuck.phi-[ERROR]: No entry of --symbolic answers the λ function 'L_number_nope'-$ cat atoms.txt-𝔻(Φ):-  𝕄(Φ):-    𝑛·0·1 := 5-    𝑛·0·2 := 𝑛·0·1.plus( x ↦ 6 )-    𝔼(L_number_plus):-      𝔻(Φ.a🌵0):-        𝕄(Φ.a🌵0):-          𝑛·1·1 := 5-          𝑛·1·2 := Φ.bytes( φ ↦ 40-14-00-00-00-00-00-00:Δ )-        𝛿·1·1 := 40-14-00-00-00-00-00-00  # 5.0-      𝛿1·1 := 𝛿·1·1  # 𝔻(ξ.ρ)-      𝔻(Φ.a🌵1):-        𝕄(Φ.a🌵1):-          𝑛·1·3 := 6-          𝑛·1·4 := Φ.bytes( φ ↦ 40-18-00-00-00-00-00-00:Δ )-        𝛿·1·2 := 40-18-00-00-00-00-00-00  # 6.0-      𝛿2·1 := 𝛿·1·2  # 𝔻(ξ.x)-      𝑛·1·5 := Φ.number( φ ↦ 𝜎1:λ )  # 𝑛-      𝕄(𝑛·1·5):-        𝑛·1·6 := Φ.number( φ ↦ 𝜎1:λ )-      𝑛·1·7 := 𝑛·1·6  # 𝕄(𝑛·1·5)-  unanswered L_number_nope  # 𝔻(L_number_nope:λ)-```--<!-- markdownlint-enable MD013 -->--The very same file comes back with `--partial`, where the run answers the-residue instead of failing: what `phino` could not decide is a property of the-program and not of the option that decides what to do about it.--Three more lines say why a firing gave no answer.-`stuck L_outer` is the last line of a firing that got stuck, naming the λ-function it got stuck on, which is seldom the one `unanswered …` names: that-one is written where it was asked for, and this one closes every firing the-failure passed on its way out. A join that could not join closes its own-firing with `diverged …` instead, as described above, and the firings around-it with `stuck`.-`stall L_outer` stands under a firing that `--acyclic=plausible` answered-with the stall an earlier firing of the same formation kept, so a told stall-never reads as a fresh firing that wrote nothing.-`starved 3` is where `--max-steps=3` ran out, whether or not `--partial` goes-on to park it. It stands in the block of the judgment and the site the-reduction stood at, and where the budget ran out before that block could open,-right in a firing, it is commented with them instead, `starved 3  # 𝔻(Φ.a🌵2)`:--```text-𝕄(Φ.x):-  𝕄(Φ.x.arg):-    𝔼(L_outer):-      𝔻(Φ.a🌵0):-        starved 3-      stuck L_outer-  𝔼(L_outer):-    𝔻(Φ.a🌵1):-      𝔼(L_outer):-        starved 3  # 𝔻(Φ.a🌵2)-    stuck L_outer-```--The markup spells them `<stuck λ="L_outer"/>`, `<stalled λ="L_outer"/>` and-`<starved limit="3" by="dataize" at="Φ.a🌵2"/>`.--`timeout 5` is where `--max-seconds=5` ran out, placed and commented the same-way and written at the first step the deadline refused.-The run ends there, with or without `--partial`, so it is always the last line-of the protocol.-The markup spells it `<timeout limit="5" by="morph" at="…"/>`.--`spent 3` is where `--max-firings=3` ran out, placed and commented the same-way and written where the firing it refused would have started.-The markup spells it `<spent limit="3" by="dataize" at="…"/>`.--Every term is 𝜑 on a single line, whatever `--output` and `--flat` say about-the result of the run, so a program reading the protocol back never has to know-what the run printed. The file is truncated at the beginning of every run, so-it always holds the firings of exactly one run.--The blocks come in the order the reduction walks the term, and that order is-not what the dependencies are read from — the symbols are. Take a comparison-nobody can decide, a join branching on it, and an `atoms.yaml` of three-entries, the λ functions named briefly to keep the lines below short:--```yaml-- λ: L_plus-  dataize:-    𝛿1: $.ρ-    𝛿2: $.x-  𝑛: Φ.number( φ ↦ ⟦ λ ⤍ 𝜎 ⟧ )-- λ: L_gt-  dataize:-    𝛿1: $.ρ-    𝛿2: $.x-  𝑛: Φ.bool( if ↦ ⟦ λ ⤍ L_fork, then ↦ ∅, else ↦ ∅, φ ↦ ⟦ λ ⤍ 𝜎 ⟧ ⟧ )-- λ: L_fork-  dataize:-    𝛿1: $.φ-  join:-    on: 𝛿1-    left: $.then-    right: $.else-```--<!-- markdownlint-disable MD013 -->--```bash-$ cat fork.phi-⟦-  bytes ↦ ⟦ φ ↦ ∅ ⟧,-  bool ↦ ⟦ if ↦ ∅ ⟧,-  number ↦ ⟦ φ ↦ ∅, plus(ρ, x) ↦ ⟦ λ ⤍ L_plus ⟧, gt(ρ, x) ↦ ⟦ λ ⤍ L_gt ⟧ ⟧,-  foo(x) ↦ ⟦-    φ ↦ ξ.x.gt( 0 ).if( ξ.x.plus( 1 ).plus( ξ.x ), ξ.x.plus( 2 ).plus( ξ.x ) ).plus( 5 )-  ⟧,-  demo ↦ ⟦ a ↦ Φ.foo( Φ.number( φ ↦ ⟦ λ ⤍ 𝜎1 ⟧ ) ) ⟧-⟧-$ phino dataize --symbolic=atoms.yaml --locator=Q.demo.a \-    --protocol=fork.txt --quiet --sweet --hide-rho fork.phi-$ cat fork.txt-𝔻(Φ.demo.a):-  𝕄(Φ.demo.a):-    𝑛·0·1 := Φ.foo( x ↦ Φ.number( φ ↦ 𝜎1:λ ) )-    𝑛·0·2 := Φ.number( φ ↦ 𝜎1:λ )-    𝑛·0·3 := 𝑛·0·2.gt( x ↦ 0 )-    𝔼(L_gt):-      𝔻(Φ.a🌵0):-        𝕄(Φ.a🌵0):-          𝑛·1·1 := Φ.number( φ ↦ 𝜎1:λ )-      𝛿1·1 := 𝔻(𝜎1:λ)  # 𝔻(ξ.ρ)-      𝔻(Φ.a🌵1):-        𝕄(Φ.a🌵1):-          𝑛·1·2 := 0-          𝑛·1·3 := Φ.bytes( φ ↦ 00-00-00-00-00-00-00-00:Δ )-        𝛿·1·1 := 00-00-00-00-00-00-00-00  # 0.0-      𝛿2·1 := 𝛿·1·1  # 𝔻(ξ.x)-      𝑛·1·4 := Φ.bool( if(then, else) ↦ ⟦ λ ⤍ L_fork, φ ↦ 𝜎2:λ ⟧ )  # 𝑛-      𝕄(𝑛·1·4):-        𝑛·1·5 := Φ.bool( if(then, else) ↦ ⟦ λ ⤍ L_fork, φ ↦ 𝜎2:λ ⟧ )-      𝑛·1·6 := 𝑛·1·5  # 𝕄(𝑛·1·4)-    𝑛·0·4 := ⟦ λ ⤍ L_fork, then ↦ ∅, else ↦ ∅, φ ↦ 𝜎2:λ ⟧( then ↦ 𝑛·1·1.plus( 1 ).plus( 𝑛·1·1 ) )-    𝑛·0·5 := 𝑛·0·4( else ↦ 𝑛·1·1.plus( 2 ).plus( 𝑛·1·1 ) )-    𝔼(L_fork):-      𝛿1·2 := 𝔻(𝜎2:λ)  # 𝔻(ξ.φ)-      𝕄(Φ.a🌵3):-        𝑛·2·1 := Φ.number( φ ↦ 𝜎1:λ )-        𝑛·2·2 := 𝑛·2·1.plus( x ↦ 1 )-        𝔼(L_plus):-          𝔻(Φ.a🌵4):-            𝕄(Φ.a🌵4):-              𝑛·3·1 := Φ.number( φ ↦ 𝜎1:λ )-          𝛿1·3 := 𝔻(𝜎1:λ)  # 𝔻(ξ.ρ)-          𝔻(Φ.a🌵5):-            𝕄(Φ.a🌵5):-              𝑛·3·2 := 1-              𝑛·3·3 := Φ.bytes( φ ↦ 3F-F0-00-00-00-00-00-00:Δ )-            𝛿·3·1 := 3F-F0-00-00-00-00-00-00  # 1.0-          𝛿2·3 := 𝛿·3·1  # 𝔻(ξ.x)-          𝑛·3·4 := Φ.number( φ ↦ 𝜎3:λ )  # 𝑛-          𝕄(𝑛·3·4):-            𝑛·3·5 := Φ.number( φ ↦ 𝜎3:λ )-          𝑛·3·6 := 𝑛·3·5  # 𝕄(𝑛·3·4)-        𝑛·2·3 := 𝑛·3·5.plus( x ↦ 𝑛·3·1 )-      𝕄(Φ.a🌵6):-        𝑛·2·4 := Φ.number( φ ↦ 𝜎1:λ )-        𝑛·2·5 := 𝑛·2·4.plus( x ↦ 2 )-        𝔼(L_plus):-          𝔻(Φ.a🌵7):-            𝕄(Φ.a🌵7):-              𝑛·4·1 := Φ.number( φ ↦ 𝜎1:λ )-          𝛿1·4 := 𝔻(𝜎1:λ)  # 𝔻(ξ.ρ)-          𝔻(Φ.a🌵8):-            𝕄(Φ.a🌵8):-              𝑛·4·2 := 2-              𝑛·4·3 := Φ.bytes( φ ↦ 40-00-00-00-00-00-00-00:Δ )-            𝛿·4·1 := 40-00-00-00-00-00-00-00  # 2.0-          𝛿2·4 := 𝛿·4·1  # 𝔻(ξ.x)-          𝑛·4·4 := Φ.number( φ ↦ 𝜎4:λ )  # 𝑛-          𝕄(𝑛·4·4):-            𝑛·4·5 := Φ.number( φ ↦ 𝜎4:λ )-          𝑛·4·6 := 𝑛·4·5  # 𝕄(𝑛·4·4)-        𝑛·2·6 := 𝑛·4·5.plus( x ↦ 𝑛·4·1 )-      joined 𝜎5 := 𝛿1·2 ? 𝜎3 : 𝜎4-      𝑛·2·7 := ⟦ x ↦ 𝑛·4·1, λ ⤍ L_plus ⟧  # 𝕄(ξ.then), 𝕄(ξ.else)-    𝔼(L_plus):-      𝔻(Φ.a🌵9):-        𝕄(Φ.a🌵9):-          𝑛·5·1 := Φ.number( φ ↦ 𝜎5:λ )-      𝛿1·5 := 𝔻(𝜎5:λ)  # 𝔻(ξ.ρ)-      𝔻(Φ.a🌵10):-        𝕄(Φ.a🌵10):-          𝑛·5·2 := Φ.number( φ ↦ 𝜎1:λ )-      𝛿2·5 := 𝔻(𝜎1:λ)  # 𝔻(ξ.x)-      𝑛·5·3 := Φ.number( φ ↦ 𝜎6:λ )  # 𝑛-      𝕄(𝑛·5·3):-        𝑛·5·4 := Φ.number( φ ↦ 𝜎6:λ )-      𝑛·5·5 := 𝑛·5·4  # 𝕄(𝑛·5·3)-    𝑛·0·6 := 𝑛·5·4.plus( x ↦ 5 )-  𝔼(L_plus):-    𝔻(Φ.a🌵11):-      𝕄(Φ.a🌵11):-        𝑛·6·1 := Φ.number( φ ↦ 𝜎6:λ )-    𝛿1·6 := 𝔻(𝜎6:λ)  # 𝔻(ξ.ρ)-    𝔻(Φ.a🌵12):-      𝕄(Φ.a🌵12):-        𝑛·6·2 := 5-        𝑛·6·3 := Φ.bytes( φ ↦ 40-14-00-00-00-00-00-00:Δ )-      𝛿·6·1 := 40-14-00-00-00-00-00-00  # 5.0-    𝛿2·6 := 𝛿·6·1  # 𝔻(ξ.x)-    𝑛·6·4 := Φ.number( φ ↦ 𝜎7:λ )  # 𝑛-    𝕄(𝑛·6·4):-      𝑛·6·5 := Φ.number( φ ↦ 𝜎7:λ )-    𝑛·6·6 := 𝑛·6·5  # 𝕄(𝑛·6·4)-```--<!-- markdownlint-enable MD013 -->--`𝜎2` is minted by the first firing and consumed by the join, which morphs-both branches where they stand in the universe, `𝕄(Φ.a🌵3):` and-`𝕄(Φ.a🌵6):`, and each morph fires the `plus` its branch starts with. `𝜎3` is-minted by the `plus( 1 )` of `then` and `𝜎4` by the `plus( 2 )` of `else`, and-nothing else tells the two branches apart, so the join mints `𝜎5` and writes,-on the `joined …` line, that `𝜎5` is `𝜎3` where `𝛿1·2` holds and `𝜎4`-where it does not, `𝛿1·2` being the operand line that brought `𝜎2` down. A-reader who knows the entry knows that `𝜎3` belongs to `then`. Nothing is-assigned to a `𝜎`, it being the name of a λ function, so the decision nobody-made stands on a line of its own. The `plus` both branches end in reads the-joined symbol as `𝔻(𝜎5:λ)`, and the `plus( 5 )` after it reads `𝔻(𝜎6:λ)`.--Two branches that differ in more than their symbols do not join. Had `else`-been `ξ.x.plus( 2 )`, its morph would come to `𝑛·2·4.plus( x ↦ 2 )`, holding-a datum where `then` holds a symbol, and the firing of `L_fork` would end in-`diverged 𝑛·2·3 vs 𝑛·2·5`, naming the two branches by the lines their morphs-wrote, since one term for both would make `foo` read as a program that-computes a condition, computes both branches and then makes up a value-neither of them has. The comment of that line says that the two differ at-`.x`, the argument each branch hands to its last `plus`, and that the join-names no `otherwise`.--A firing that happened while an operand of another was being reduced stands one-level deeper, under the firing that asked for it. The `plus( 1 )` of `then` is-fired while the join morphs that branch, so it stands inside `𝔼(L_fork)`. The-`plus` both branches end in reaches the join as a call 𝕄 left unfired, and is-fired once the join answered, by the morph of `Φ.demo.a`, so it stands beside-`𝔼(L_fork)` rather than inside it. The last `𝔼(L_plus)` is the one the outer-`𝔻` asked for after the morph.--### The protocol as XML--The name of the file decides which of the two formats `--protocol` writes: a-name ending in `.xml` gets the same tree as markup, every other name gets the-indented text above. There is no option for it, since a caller who asks for a-file called `atoms.xml` and gets text back has been told nothing useful. Here-is the run at the top of this section again:--<!-- markdownlint-disable MD013 -->--```bash-$ phino dataize --symbolic=atoms.yaml --protocol=atoms.xml --quiet \-    --sweet --hide-rho sum.phi-$ cat atoms.xml-<?xml version="1.0" encoding="UTF-8"?>-<protocol>-  <dataize at="Φ">-    <morph at="Φ">-      <applied meta="𝑛·0·1" of="Φ.bytes"><datum attr="φ">40-14-00-00-00-00-00-00</datum></applied>-      <applied meta="𝑛·0·2" by="morph" at="Φ" of="Φ.number"><meta attr="φ">𝑛·0·1</meta></applied>-      <applied meta="𝑛·0·3" of="Φ.bytes"><datum attr="φ">40-18-00-00-00-00-00-00</datum></applied>-      <applied meta="𝑛·0·4" of="Φ.number"><meta attr="φ">𝑛·0·3</meta></applied>-      <applied meta="𝑛·0·5" by="morph" at="Φ" of="𝑛·0·2.plus"><meta attr="x">𝑛·0·4</meta></applied>-      <morphed meta="𝑛·0·6">𝑛·0·5</morphed>-    </morph>-    <evaluate λ="L_number_plus" by="dataize" at="Φ">-      <dataize at="Φ.a🌵0">-        <morph at="Φ.a🌵0">-          <applied meta="𝑛·1·1" by="morph" at="Φ.a🌵0" of="Φ.number"><meta attr="φ">𝑛·0·1</meta></applied>-          <morphed meta="𝑛·1·2">𝑛·1·1</morphed>-          <applied meta="𝑛·1·3" by="morph" at="Φ.a🌵0" of="Φ.bytes"><datum attr="φ">40-14-00-00-00-00-00-00</datum></applied>-          <morphed meta="𝑛·1·4">𝑛·1·3</morphed>-        </morph>-        <dataized meta="𝛿·1·1" number="5.0">40-14-00-00-00-00-00-00</dataized>-      </dataize>-      <bound meta="𝛿1·1">𝛿·1·1</bound>-      <dataize at="Φ.a🌵1">-        <morph at="Φ.a🌵1">-          <applied meta="𝑛·1·5" by="morph" at="Φ.a🌵1" of="Φ.number"><meta attr="φ">𝑛·0·3</meta></applied>-          <morphed meta="𝑛·1·6">𝑛·1·5</morphed>-          <applied meta="𝑛·1·7" by="morph" at="Φ.a🌵1" of="Φ.bytes"><datum attr="φ">40-18-00-00-00-00-00-00</datum></applied>-          <morphed meta="𝑛·1·8">𝑛·1·7</morphed>-        </morph>-        <dataized meta="𝛿·1·2" number="6.0">40-18-00-00-00-00-00-00</dataized>-      </dataize>-      <bound meta="𝛿2·1">𝛿·1·2</bound>-      <minted symbol="𝜎1">40-14-00-00-00-00-00-00 40-18-00-00-00-00-00-00</minted>-      <applied meta="𝑛·1·9" of="Φ.number"><symbol attr="φ">𝜎1</symbol></applied>-      <built meta="𝑛·1·10">𝑛·1·9</built>-      <morph at="𝑛·1·10">-        <applied meta="𝑛·1·11" by="morph" at="𝑛·1·10" of="Φ.number"><symbol attr="φ">𝜎1</symbol></applied>-        <morphed meta="𝑛·1·12">𝑛·1·11</morphed>-      </morph>-      <answered meta="𝑛·1·13">𝑛·1·11</answered>-    </evaluate>-    <manufactured meta="𝛿·0·1">𝜎1</manufactured>-  </dataize>-</protocol>-```--<!-- markdownlint-enable MD013 -->--The root is `<protocol>`, holding the run in one element, named after the-judgment it ran — `<dataize>` for a 𝔻, `<morph>` for a 𝕄 — with `at` naming-the term it was aimed at, which is what the text format opens with as-`𝔻(Φ)`. After the run the root holds `<msec>`, `<firings>`-and `<fps>`: how long the command took, how many λ functions it fired, and how-many it fired per second. The text format ends with the same three as-`msec …`, `firings …` and `fps …`.-The examples here leave them out, since the time differs from run to run.-`<evaluate>` is one firing of 𝔼, `λ` naming the entry that answered it, `by`-naming the judgment that asked for the firing — the same word the run is named-after and an `<unanswered>` carries —-and `at` naming the site it was fired at. The text format leaves those two to-the block the firing stands in, `𝔻(Φ):`, while the markup keeps them on every-element, so a program reading it never has to track the elements around one.-A nested `<dataize at="Φ.a🌵0">` or `<morph at="Φ.a🌵0">` is a judgment started-at a new site, which the text format writes as `𝔻(Φ.a🌵0):` or `𝕄(Φ.a🌵0):`,-and `at` names that site. Whatever the judgment does is written inside the-element, so it closes where the text format drops back to the indentation it-opened at, and like the text line it counts nothing and names no meta.-`<dataized>` closes the `<dataize>` element with the datum the `delta` rule-found, `meta` naming it the way the text format does, and the `<bound>` after-the element holds that name in place of the data. A datum of eight bytes also-carries `number`, the double the text format writes as the comment of its line.-`<bound>` is one meta the firing bound, `meta` naming it the same way the text-format names it, counter and all, and the element holding the value it took:-the name of the term where the operand was reduced with 𝕄, the datum itself-where a `dataize` operand came down to data. `<manufactured>` is the other-thing a dataization may come to, the datum manufactured for an unknown. It-closes the `<dataize>` element the way `<dataized>` does, `meta` naming it with-the same counter, and holds the symbol of that unknown rather than the 42-standing for it, `𝜎2` where the text format writes the formation `𝜎2:λ`. The-`<bound>` after the element holds that name, as it does after a `<dataized>`,-while the text format writes the whole line as `𝛿1·2 := 𝔻(𝜎2:λ)`. The name of-the element is what tells a manufactured datum from data, the way `𝔻(…)` does-in the text format, so a reader never looks at the text to tell the two apart.-`<answered>` holds the name of the term the firing answered with, named the same-way by its own `meta`, and `<built>` before it holds the name of the term the-entry wrote, the one 𝕄 made that answer of: two elements rather than two-attributes of one, for the same reason `<manufactured>` is no `<dataized>`.-`<morphed>` closes the `<morph>` element with the name of the term the 𝕄 came-to, named the same way by its own `meta`, so-`<morphed meta="𝑛·1·12">𝑛·1·11</morphed>` says the 𝕄 at `𝑛·1·10` came to the-object `𝑛·1·11`. Only the 𝕄 that opened the element writes one: an operand a-rule of 𝕄 morphs on its way stands inside the same element and closes nothing,-and a 𝕄 that was parked or cut came to nothing and writes none. Two runs of 𝕄-at one site stand in one element, the way the text format merges two blocks of-one heading, and each closes with its own `<morphed>`, so the last one is what-the element came to: inside `<morph at="Φ.a🌵0">` the first run came to the-`Φ.number` of `𝑛·1·1` and the second to the `Φ.bytes` of `𝑛·1·3`, which holds-the datum. The text format writes no line for it.--`<applied meta="𝑛·1·11" by="morph" at="𝑛·1·10" of="Φ.number">` is an object-an application made, which the text format writes as `𝑛·1·6 := …` in the block-`𝕄(𝑛·1·5):`: `meta` names the object, counted with the `<built>`, the-`<morphed>` and the `<answered>` of the firing it stands in, `by` and `at`-name the judgment that normalized and the site it stood at, and `of` names the-object applied. The argument stands in an element named after the kind of its-value, here a `<symbol>`, with `name` naming the attribute it fills and the-text holding the value. An application nothing reduced gets an `<applied>`-too, with no `by` and no `at`, right before the element that holds it:-`<applied meta="𝑛·0·1" of="Φ.bytes">` is `Φ.bytes( 40-14-… )`, the argument of-the `Φ.number` that `𝑛·0·2` applies. A later element holding an object holds-its name instead, so `<answered meta="𝑛·1·13">𝑛·1·11</answered>` says the-firing answered with it and `<morph at="𝑛·1·10">` that 𝕄 got into what the-entry built.--The markup is for a program, which never has to parse a term of 𝜑-calculus to-read it. Every value the markup holds, in an attribute or as the text of an-element, is one of a few kinds: a symbol such as `𝜎1`, a datum such as-`40-14-00-00-00-00-00-00`, a meta such as `𝑛·0·2` or `𝛿·1·1`, a path of-attribute names off `Φ`, `ξ`, `ρ` or a meta, such as `𝑛·0·2.plus`, a name such-as `tail` or `L_fork`, or `⊥`. Inside `<applied>`, `<formed>` and `<with>`, the-element holding a value is named after its kind, so a reader never looks at the-text to tell a symbol from a meta: `<symbol>`, `<datum>`, `<meta>`, `<ref>` for-a path, and the empty `<bottom attr="x"/>` for `⊥`. A term of any other kind is-named by an element of its own, written before the first element that holds-it, and is its meta from then on: an application by an `<applied>`, and a-formation by a `<formed>`. A formation that copies an object of the world,-binding its `φ` to `𝜎196`, is-`<formed meta="𝑛·3·9" of="Φ.string"><symbol attr="φ">𝜎196</symbol>`: `of`-names the object by its path, and every element inside is an attribute the-copy binds to a value other than the one the object has. A copy that binds-nothing new is named by its path alone. A formation that copies no object of-the world has no `of` and holds all its bindings: an element for each bound-attribute, named after the kind of its value, a `<void attr="x"/>` for each-void one, and its `λ` or `Δ`, if it has one, as an attribute of the element,-such as `<formed meta="𝑛·1·1" λ="L_fork">`. A term-the markup can name in none of these ways, such as an argument of a deferred-call that nothing worked out, is an expression meta, `𝑒1`: the same term gets-the same `𝑒` throughout the file and another term gets another, so a reader can-tell two holes apart, while what the term is stays in the text format alone.-Since the markup names terms the text format writes out, the two count their-metas each on their own: `𝑛·1·11` of the markup above is `𝑛·1·6` of the-text.--A join writes `<joined symbol="𝜎5" on="𝛿1·2" left="𝜎3" right="𝜎4"/>`,-one element per fresh symbol, which the text format writes as-`joined 𝜎5 := 𝛿1·2 ? 𝜎3 : 𝜎4`: the fresh symbol stands in `symbol`, the-meta of the condition in `on`, named the way `<bound>` names it, and the two-it stands for in `left` and `right`. The two `<morph>` elements before it are-the sides being morphed where they stand in the universe. Two sides that do-not join write-`<diverged left="𝑛·2·3" right="𝑛·2·6" at=".x" reason="the join names no otherwise"/>`-in its place, which the text format writes as-`diverged 𝑛·2·3 vs 𝑛·2·5  # they differ at .x, and the join names no otherwise`,-naming each side the way the markup names any other value. `at` holds the-path where the two sides differ, left out when they differ as a whole, and-`reason` says why the join made no formation of them.--`<symbolized symbol="𝜎4" number="1.0">3F-F0-00-00-00-00-00-00</symbolized>` is-one symbol a `symbolize` line minted, which the text format writes as-`symbolized 𝜎4 := 3F-F0-00-00-00-00-00-00  # 1.0`: the fresh symbol stands in-`symbol` and the datum it stands for is the text, with the double of eight-bytes in `number`, as in `<dataized>`. The elements stand ahead of the-`<bound>` of the line, and the markup binds the meta of that line as it binds-any other.--`<minted symbol="𝜎1">40-14-… 40-18-…</minted>` is one symbol the firing-minted, one element per bare `𝜎` the entry wrote its answer with, standing-inside the block ahead of the `<built>` carrying them. The symbol stands in-`symbol` and the text is what is known about it: the values the `dataize`-lines of the entry took, in the order the entry declares them, each spelled as-its own line spells it — the bytes for a datum, `𝜎1` for a symbol — and-separated by a space. With the `λ` of the block the element reads as the fact-`𝔻(𝜎1:λ) == L_number_plus(40-14-…, 40-18-…)`, and a firing of an entry with-no `dataize` line writes `<minted symbol="𝜎1"/>`. The symbol is also the edge-a reader joins on: a later `<manufactured meta="𝛿·5·1">𝜎2</manufactured>` names-the symbol the firing that wrote `<minted symbol="𝜎2">` handed out. A firing-minting two symbols writes two elements and one minting none writes none,-which no attribute on the answer could say: a term may carry several symbols,-or carry one where the value it stands for is not a symbol at all. In the join-above, `𝔼(L_gt)` writes `<minted symbol="𝜎2">` although `𝜎2` sits under `if`-and not where the value of the term is, while `𝔼(L_fork)` writes none at all,-since the one symbol it mints is written as `<joined>`.--A λ name no entry answers is `<unanswered λ="…">`, standing where its-`<evaluate>` would have stood with the name of the formation 𝔼 was fired-against as its text, such as `𝑛·1·11.nope` below, and the judgment that asked-in its `by` attribute, where the text format writes the letter of it. A firing-that happened while an operand of another was being reduced is an `<evaluate>`-inside the one that asked, which is what the deeper indentation means in the-text. Elements are written as the run goes and-the open ones are closed when it ends, so a run that fails still leaves a-well-formed document behind:--<!-- markdownlint-disable MD013 -->--```bash-$ phino dataize --symbolic=atoms.yaml --protocol=atoms.xml --quiet \-    --sweet --hide-rho stuck.phi-[ERROR]: No entry of --symbolic answers the λ function 'L_number_nope'-$ cat atoms.xml-<?xml version="1.0" encoding="UTF-8"?>-<protocol>-  <dataize at="Φ">-    <morph at="Φ">-      <applied meta="𝑛·0·1" of="Φ.bytes"><datum attr="φ">40-14-00-00-00-00-00-00</datum></applied>-      <applied meta="𝑛·0·2" by="morph" at="Φ" of="Φ.number"><meta attr="φ">𝑛·0·1</meta></applied>-      <applied meta="𝑛·0·3" of="Φ.bytes"><datum attr="φ">40-18-00-00-00-00-00-00</datum></applied>-      <applied meta="𝑛·0·4" of="Φ.number"><meta attr="φ">𝑛·0·3</meta></applied>-      <applied meta="𝑛·0·5" by="morph" at="Φ" of="𝑛·0·2.plus"><meta attr="x">𝑛·0·4</meta></applied>-      <evaluate λ="L_number_plus" by="morph" at="Φ">-        <dataize at="Φ.a🌵0">-          <morph at="Φ.a🌵0">-            <applied meta="𝑛·1·1" by="morph" at="Φ.a🌵0" of="Φ.number"><meta attr="φ">𝑛·0·1</meta></applied>-            <morphed meta="𝑛·1·2">𝑛·1·1</morphed>-            <applied meta="𝑛·1·3" by="morph" at="Φ.a🌵0" of="Φ.bytes"><datum attr="φ">40-14-00-00-00-00-00-00</datum></applied>-            <morphed meta="𝑛·1·4">𝑛·1·3</morphed>-          </morph>-          <dataized meta="𝛿·1·1" number="5.0">40-14-00-00-00-00-00-00</dataized>-        </dataize>-        <bound meta="𝛿1·1">𝛿·1·1</bound>-        <dataize at="Φ.a🌵1">-          <morph at="Φ.a🌵1">-            <applied meta="𝑛·1·5" by="morph" at="Φ.a🌵1" of="Φ.number"><meta attr="φ">𝑛·0·3</meta></applied>-            <morphed meta="𝑛·1·6">𝑛·1·5</morphed>-            <applied meta="𝑛·1·7" by="morph" at="Φ.a🌵1" of="Φ.bytes"><datum attr="φ">40-18-00-00-00-00-00-00</datum></applied>-            <morphed meta="𝑛·1·8">𝑛·1·7</morphed>-          </morph>-          <dataized meta="𝛿·1·2" number="6.0">40-18-00-00-00-00-00-00</dataized>-        </dataize>-        <bound meta="𝛿2·1">𝛿·1·2</bound>-        <minted symbol="𝜎1">40-14-00-00-00-00-00-00 40-18-00-00-00-00-00-00</minted>-        <applied meta="𝑛·1·9" of="Φ.number"><symbol attr="φ">𝜎1</symbol></applied>-        <built meta="𝑛·1·10">𝑛·1·9</built>-        <morph at="𝑛·1·10">-          <applied meta="𝑛·1·11" by="morph" at="𝑛·1·10" of="Φ.number"><symbol attr="φ">𝜎1</symbol></applied>-          <morphed meta="𝑛·1·12">𝑛·1·11</morphed>-        </morph>-        <answered meta="𝑛·1·13">𝑛·1·11</answered>-      </evaluate>-      <morphed meta="𝑛·0·6">𝑛·1·11.nope</morphed>-    </morph>-    <unanswered λ="L_number_nope" by="dataize">𝑛·1·11.nope</unanswered>-  </dataize>-</protocol>-```--<!-- markdownlint-enable MD013 -->--### Abridging the protocol--A formation carrying a whole object is written flat on one line, so a real-run fills the protocol with lines tens of thousands of characters long. The-`--abridged` option shortens every term the text protocol writes: a formation-longer than sixty-four characters keeps its `φ`, `Δ` and `λ` bindings and folds-the rest into a count. The markup writes no terms, only their names, so the-option leaves it as it is. The width is a value of the option,-`--abridged=120`, for a run that can read longer lines. The result the run-prints stays whole, and the option is refused without `--protocol`.-Every byte string the protocol writes stays whole too, since a reader may need-the data a firing came down to:--<!-- markdownlint-disable MD013 -->--```bash-$ cat wide.phi-⟦-  t ↦ ⟦-    φ ↦ ⟦ Δ ⤍ 48-65-6C-6C-6F-2C-20-77-6F-72-6C-64 ⟧,-    left ↦ ξ.right,-    right ↦ ξ.left,-    middle ↦ ξ.left-  ⟧-⟧-$ phino dataize --locator=Q.t --protocol=wide.txt --abridged --quiet \-    --sweet --hide-rho wide.phi-$ cat wide.txt-𝔻(Φ.t):-  𝛿·0·1 := 48-65-6C-6C-6F-2C-20-77-6F-72-6C-64-```--<!-- markdownlint-enable MD013 -->--The `--abridged-data` option cuts the data too: a byte string longer than eight-bytes keeps its first two bytes and its last two, with the count of the bytes-cut out between them. A formation folded into a count loses its data either-way, and the option is refused without `--abridged`:--<!-- markdownlint-disable MD013 -->--```bash-$ phino dataize --locator=Q.t --protocol=wide.txt --abridged \-    --abridged-data --quiet --sweet --hide-rho wide.phi-$ cat wide.txt-𝔻(Φ.t):-  𝛿·0·1 := 48-65-..(8b)..-6C-64-```--<!-- markdownlint-enable MD013 -->--### Reducing a term inside a universe--A term that is no part of the program may still be reduced against it, with the-`--inside` option: the expression it names is bound to a synthetic attribute-prepended to the input expression, which the run takes as the universe Φ,-normalized there and then reduced.--```bash-$ cat universe.phi-⟦-  bytes ↦ ⟦ φ ↦ ∅ ⟧,-  number ↦ ⟦ φ ↦ ∅, plus(ρ, x) ↦ ⟦ λ ⤍ L_number_plus ⟧ ⟧-⟧-$ phino dataize --symbolic=atoms.yaml --inside='5.plus( 6 )' universe.phi-40-45-00-00-00-00-00-00-```--This is the very trick `phino` plays internally to reduce the operands of a-firing, made available to whoever asks it to reduce a term the program does not-hold. The option cannot be combined with `--locator`, since it aims the run at-the binding it mints itself. Both `dataize` and `morph` take `--symbolic` and-`--inside`.--### Partial evaluation--A λ function no entry of the `--symbolic` file answers fails the run. This is-what happens when an operation is deliberately left out — an input the object-model has not declared yet, or an operation whose answer is not known. With-`--partial`, dataization becomes partial evaluation instead: what the known-inputs decide is computed, the rest survives as the residual program, which is-printed in place of the bytes, and the run ends successfully:--```bash-$ cat partial.phi-⟦-  bytes ↦ ⟦ φ ↦ ∅ ⟧,-  number ↦ ⟦-    φ ↦ ∅,-    plus(ρ, x) ↦ ⟦ λ ⤍ L_number_plus ⟧,-    times(ρ, x) ↦ ⟦ λ ⤍ L_number_times ⟧,-    as-bool ↦ ⟦ λ ⤍ L_number_as_bool ⟧-  ⟧,-  φ ↦ 2.times( 3 ).plus( 4 ).as-bool-⟧-$ phino dataize --symbolic=atoms.yaml --sweet --hide-rho partial.phi-[ERROR]: No entry of --symbolic answers the λ function 'L_number_as_bool'-$ phino dataize --symbolic=atoms.yaml --partial --sweet --hide-rho partial.phi-L_number_as_bool:λ-```--Here `2.times( 3 ).plus( 4 )` was answered by the entries the file carries, so-it was reduced — the symbol it came to sits in the hidden `ρ` of the residual-program — while `as-bool` names a λ function no entry answers, so it stays in-place as a normal-form subterm. A stuck site opens no block in the-`--protocol` file, since nothing fired there, and stands in it as-`unanswered …`:--<!-- markdownlint-disable MD013 -->--```bash-$ phino dataize --symbolic=atoms.yaml --partial --protocol=atoms.txt --quiet \-    --sweet --hide-rho partial.phi-$ cat atoms.txt-𝔻(Φ):-  𝕄(Φ):-    𝑛·0·1 := 2-    𝑛·0·2 := 𝑛·0·1.times( x ↦ 3 )-    𝔼(L_number_times):-      𝔻(Φ.a🌵0):-        𝕄(Φ.a🌵0):-          𝑛·1·1 := 2-          𝑛·1·2 := Φ.bytes( φ ↦ 40-00-00-00-00-00-00-00:Δ )-        𝛿·1·1 := 40-00-00-00-00-00-00-00  # 2.0-      𝛿1·1 := 𝛿·1·1  # 𝔻(ξ.ρ)-      𝔻(Φ.a🌵1):-        𝕄(Φ.a🌵1):-          𝑛·1·3 := 3-          𝑛·1·4 := Φ.bytes( φ ↦ 40-08-00-00-00-00-00-00:Δ )-        𝛿·1·2 := 40-08-00-00-00-00-00-00  # 3.0-      𝛿2·1 := 𝛿·1·2  # 𝔻(ξ.x)-      𝑛·1·5 := Φ.number( φ ↦ 𝜎1:λ )  # 𝑛-      𝕄(𝑛·1·5):-        𝑛·1·6 := Φ.number( φ ↦ 𝜎1:λ )-      𝑛·1·7 := 𝑛·1·6  # 𝕄(𝑛·1·5)-    𝑛·0·3 := 𝑛·1·6.plus( x ↦ 4 )-    𝔼(L_number_plus):-      𝔻(Φ.a🌵2):-        𝕄(Φ.a🌵2):-          𝑛·2·1 := Φ.number( φ ↦ 𝜎1:λ )-      𝛿1·2 := 𝔻(𝜎1:λ)  # 𝔻(ξ.ρ)-      𝔻(Φ.a🌵3):-        𝕄(Φ.a🌵3):-          𝑛·2·2 := 4-          𝑛·2·3 := Φ.bytes( φ ↦ 40-10-00-00-00-00-00-00:Δ )-        𝛿·2·1 := 40-10-00-00-00-00-00-00  # 4.0-      𝛿2·2 := 𝛿·2·1  # 𝔻(ξ.x)-      𝑛·2·4 := Φ.number( φ ↦ 𝜎2:λ )  # 𝑛-      𝕄(𝑛·2·4):-        𝑛·2·5 := Φ.number( φ ↦ 𝜎2:λ )-      𝑛·2·6 := 𝑛·2·5  # 𝕄(𝑛·2·4)-  unanswered L_number_as_bool  # 𝔻(L_number_as_bool:λ)-```--<!-- markdownlint-enable MD013 -->--Evaluation stays demand-driven, as the calculus prescribes: an argument-that nothing asked for before the run got stuck is left as it is in the-residual program, for the next iteration.--An operand of a firing that reaches the terminator `⊥`, or a term no-dataization rule matches, such as a formation whose `φ` is a void nothing-filled, never comes down to data either, and `--partial` parks that firing-the same way, writing the dead end into the protocol as `unanswered ⊥` with the-term that could not be dataized beside it. Dataization aimed at `⊥` itself still-fails, with or without `--partial`, since there is no firing to park.--The nested morphing and dataization recursion is bounded by the-`--max-steps` option (default `1000`): when the budget is exhausted, the run-fails with `Dataization did not finish before reaching the limit of steps`.-This guards against non-terminating terms, which used to loop forever before-the bound was introduced:--```bash-$ phino dataize --max-steps=50 problem.phi-[ERROR]: Dataization did not finish before reaching the limit of steps: --max-steps=50-```--That budget bounds how deep one branch goes, not how much the whole run does.-An entry that reduces two operands, each firing it again, doubles its work at-every level, so twenty levels stay far inside `--max-steps` and still fire a-million times. The `--max-firings` option counts every λ function the run-fires and fails the run once the count is spent; `--partial` parks it instead,-the way it parks a spent `--max-steps`. There is no limit unless the option is-given:--```bash-$ cat split.yaml-- λ: L_split-  morph:-    𝑛1: Φ.s.foo-    𝑛2: Φ.s.foo-  𝑛: ⟦ l ↦ 𝑛1, r ↦ 𝑛2 ⟧-$ cat split.phi-⟦ s ↦ ⟦ λ ⤍ L_split ⟧, x ↦ Φ.s.foo ⟧-$ phino morph --symbolic=split.yaml --locator=Q.x --max-firings=64 split.phi-[ERROR]: Evaluation did not finish before reaching the limit of firings: --max-firings=64-```--Both budgets count work, so a run inside both of them may still take longer-than its caller can wait, and a caller that kills it gets a protocol nobody-closed. The `--max-seconds` option stops the run by the clock instead: once-that many seconds have passed since the command started, the next step the-run is about to take, whether it fires a λ function or not, fails it with-`Evaluation did not finish before reaching the limit of seconds`, and so does-a check of `--acyclic` that is still comparing formations by then.-`--partial` does not park it, since a run out of time has no site to park and-nothing left to go on with. The protocol is closed as usual and its last line-says where the time ran out. There is no limit unless the option is given.-Here a step budget of a hundred thousand lets the recursion of `split.phi`-run longer than five seconds:--```bash-$ phino morph --symbolic=split.yaml --locator=Q.x --partial \-    --max-steps=100000 --max-seconds=5 --protocol=split.txt --sweet --flat \-    split.phi-[ERROR]: Evaluation did not finish before reaching the limit of seconds: --max-seconds=5-$ grep -o 'timeout.*' split.txt-timeout 5-```--## Morph--Dataization insists on bytes. Morphing 𝕄 asks a different question: evaluate-as far as the object model allows, without demanding data. It resolves Φ-against the universe, peels dispatches and applications through-normalization, fires whichever λ functions sit under a dispatch, and stops at-the first formation it reaches, handing that formation back untouched. The-`morph` command runs 𝕄 on its own:--```bash-$ cat two.phi-⟦-  bytes ↦ ⟦ φ ↦ ∅ ⟧,-  number ↦ ⟦ φ ↦ ∅, plus(ρ, x) ↦ ⟦ λ ⤍ L_number_plus ⟧ ⟧,-  φ ↦ 5.plus( 6 ).plus( 7 )-⟧-$ phino dataize --symbolic=atoms.yaml --sweet --hide-rho two.phi-40-45-00-00-00-00-00-00-$ phino morph --symbolic=atoms.yaml --locator=Q.φ --sweet --hide-rho two.phi-⟦ x ↦ 7, λ ⤍ L_number_plus ⟧-```--The inner `5.plus( 6 )` fires, because `.plus` is dispatched on its result,-and the symbol it answered with lands in the `ρ` hidden by `--hide-rho`. The-outer application is saturated but bare, so 𝕄 returns it and is finished;-firing it is dataization's job and takes `dataize` on to a datum.--The default locator `Q` morphs the whole top formation, which 𝕄 returns-unchanged, so `--locator` is how one aims 𝕄 at a subterm, exactly as in-`dataize`. Unlike 𝔻, 𝕄 is total: where no formation is reachable the answer-is the terminator `⊥`, printed rather than reported as a failed run:--```bash-$ phino morph --locator=Q.x <<< '⟦ x ↦ ξ ⟧'-⊥-```--𝕄 maps normal forms to formations and `morph` does not normalize what it is-given, so a term that is not a normal form, such as a dispatch off a formation-with neither `φ` nor `λ`, is reported as a failed run rather than answered:--```bash-$ phino morph --locator=Q.t <<< '⟦ t ↦ ⟦ x ↦ ⟦⟧ ⟧.x ⟧'-[ERROR]: Morphing expects a normal form, but no morphing rule matches: ⟦⟧:x.x-```--The whole `dataize` option surface applies unchanged — `--symbolic`,-`--inside`, `--sequence`, `--headers`, `--steps-dir`, `--protocol`,-`--partial`, `--max-steps`, `--shuffle`/`--seed`, `--output`, `--focus` and the-rest.--### Acyclic morphing--Whether a program terminates is the object model's business, not the-calculus's, so phino prevents no recursion of its own and `--max-steps` is what-ends a run that never finishes. An entry answering with a firing of itself-therefore spends the whole budget before it fails, and what it fails on is the-limit rather than the loop:--```bash-$ cat loop.yaml-- λ: L_loop-  𝑛: ⟦ λ ⤍ L_loop ⟧-$ cat loop.phi-⟦ x ↦ ⟦ λ ⤍ L_loop ⟧.foo ⟧-$ phino morph --symbolic=loop.yaml --locator='Q.x' --max-steps=40 loop.phi-[ERROR]: Dataization did not finish before reaching the limit of steps: --max-steps=40-```--The `--acyclic=<mode>` option makes a reduction notice. Every frame of 𝕄 and-of 𝔻 remembers the formations the frames above it have entered, and only four-places enter one: `fire` of 𝔻, `ml` of 𝕄 and an `evaluate` line of an entry,-which fire the λ function of a formation, and `box` of 𝔻, which gets into the-`φ` body of a formation carrying no λ and no `Δ`. A frame about to enter a-formation one of the frames above it has already entered is asking a question-only ever answered by asking it again, so the option stops there and parks the-site the way `--partial` parks a λ function that cannot fire: the answer is the-term the spine had reached, left where it stood, and the command exits-successfully.--```bash-$ phino morph --symbolic=loop.yaml --locator='Q.x' --acyclic=proven \-    --max-steps=40 --hide-rho loop.phi-⟦ λ ⤍ L_loop ⟧.foo-```--𝕄 and 𝔻 share that one memory. They call each other on the very term they-were asked about, and that handover is no loop, but it enters no formation-either, so it is never remembered and never mistaken for one. A body-dispatching the object it stands in is a loop 𝔻 walks round on its own — 𝕄-stops at a formation every round, and `box` gets into it again — so `dataize`-takes the option too, and so does the run of 𝔻 a λ function's `dataize` operand-is brought down with:--```bash-$ cat cyc.phi-⟦ cyc ↦ ⟦ x ↦ ∅, φ ↦ Φ.cyc( ξ.x ) ⟧, t ↦ Φ.cyc( ⟦⟧ ) ⟧-$ phino dataize --locator='Q.t' --acyclic=proven --partial \-    --sweet --hide-rho --flat cyc.phi-⟦ cyc(x) ↦ Φ.cyc( x ):φ, t ↦ Φ.cyc( ⟦⟧ ) ⟧-```--𝔻 insists on bytes and a parked term carries none, so under `dataize` the option-wants `--partial` to have something to print: the residual program, exactly the-one it prints for a λ function that cannot fire. Without it the run stops on-the loop all the same, naming the formation it entered again instead of running-the budget down. Under `morph` nothing is asked for: 𝕄 always has a term to-answer with, and a loop 𝔻 meets under a firing parks the site the firing-stands at.--The mode says what "the same formation" means, and there is no default, since-no answer is right for every run. Under `proven` it means the same up to a-renaming of symbols: two-formations are one where some one-to-one pairing of the symbols of the first-with those of the second makes them equal, so `𝜎5` may stand where `𝜎3` stood-as long as it does so everywhere and no other symbol stands there too. Data,-attribute names and λ names must still match exactly. That is what catches a-recursion over an unknown, which never repeats a term: every round mints fresh-symbols, so the formation it enters on the second round is the one it entered-on the first with new names for the unknowns. The renaming is sound because a-symbol is an opaque value nobody worked out — every one of them dataizes to the-same manufactured datum and no entry of `--symbolic` answers one — so a-formation entered again with nothing but its symbols renamed replays the round-it is inside forever. Data still tells rounds apart: a formation entered with-`n ↦ 3` and then with `n ↦ 2` is two formations, so a recursion over data is-not cut while it goes on computing. Take an entry that answers a call of its-own object over a fresh symbol, so every round fires the same λ function over-an unknown it has never seen:--```bash-$ cat down.yaml-- λ: L_down-  dataize:-    𝛿1: $.x-  𝑛: Φ.down( x ↦ ⟦ λ ⤍ 𝜎 ⟧ )-$ cat down.phi-⟦-  down ↦ ⟦ x ↦ ∅, λ ⤍ L_down ⟧,-  y ↦ Φ.down( x ↦ ⟦ λ ⤍ 𝜎1 ⟧ ).φ-⟧-$ phino morph --symbolic=down.yaml --locator=Q.y --acyclic=proven \-    --protocol=down.txt --sweet --hide-rho down.phi-⟦ x ↦ 𝜎1:λ, λ ⤍ L_down ⟧.φ-$ cat down.txt-𝕄(Φ.y):-  𝑛·0·1 := Φ.down( x ↦ 𝜎1:λ )-  𝔼(L_down):-    𝛿1·1 := 𝔻(𝜎1:λ)  # 𝔻(ξ.x)-    𝑛·1·1 := Φ.down( x ↦ 𝜎2:λ )  # 𝑛-    𝕄(𝑛·1·1):-      𝑛·1·2 := Φ.down( x ↦ 𝜎2:λ )-    𝑛·1·3 := 𝑛·1·2  # 𝕄(𝑛·1·1)-  looped 𝑛·0·1  # proven-```--The first round fires `down` with `x ↦ 𝜎1` and answers it with `x ↦ 𝜎2`, and-the `φ` dispatched off that answer would fire it again: the formation the first-round entered, `𝜎2` standing where `𝜎1` stood, so the firing is cut before it-opens. The cut is the `looped …` line, standing where the block of the cut-firing would have stood: the block it stands in names the judgment the frame-belonged to and the site it was cut at, and the comment names the mode that-cut it. What it carries is the formation the frame above entered, named by the-line that made it, `𝑛·0·1`, so the two are paired by their terms and no reader-has to rename symbols by eye or find the cut in the residue. Nothing runs under-a cut, so no block opens under the line. In the XML protocol it is-`<looped by="morph" match="proven" at="Φ.y" of="𝑛·0·1"/>`, with the site, the-mode and the name of the formation as attributes. Without the option-the same run nests one round inside another until `--max-steps` runs out.--What a frame remembers is the branch from the run down to it, never everything-the run has touched, so two siblings entering one formation enter it twice and-only a formation entered from inside itself is a loop. The cut costs one lookup-and fires on the turn the repeat appears, so raising `--max-steps` from 40 to a-million changes neither the answer nor the time.--What `proven` cannot see is a recursion that never comes back to the same-formation: one whose arguments grow by a wrapper every round. Take a factorial-with an accumulator it builds a `pair` onto, with `facta.yaml` answering-`L_zero` and `L_dec` with a fresh symbol each and `L_if` joining on its-condition. Under `proven` the morph of every `right` fires the `if` inside it,-nesting one level deeper than the last until `--max-steps` runs out, since no-renaming of symbols turns a longer chain of pairs into a shorter one. Under-`plausible` it is cut on the first round:--<!-- markdownlint-disable MD013 -->--```bash-$ cat facta.phi-⟦-  if ↦ ⟦ c ↦ ∅, left ↦ ∅, right ↦ ∅, λ ⤍ L_if ⟧,-  zero ↦ ⟦ x ↦ ∅, λ ⤍ L_zero ⟧,-  dec ↦ ⟦ x ↦ ∅, λ ⤍ L_dec ⟧,-  box ↦ ⟦ v ↦ ∅, φ ↦ ξ.v ⟧,-  pair ↦ ⟦ head ↦ ∅, tail ↦ ∅, φ ↦ ξ.head ⟧,-  fact ↦ ⟦ n ↦ ∅, acc ↦ ∅, φ ↦ Φ.if( c ↦ Φ.zero( x ↦ ξ.n ), left ↦ ξ.acc, right ↦ Φ.fact( n ↦ Φ.dec( x ↦ ξ.n ), acc ↦ Φ.pair( head ↦ ξ.n, tail ↦ ξ.acc ) ).φ ).φ ⟧,-  x ↦ Φ.fact( n ↦ ⟦ λ ⤍ 𝜎1 ⟧, acc ↦ Φ.box( v ↦ ⟦ Δ ⤍ 00- ⟧ ) ).φ-⟧-$ cat facta.yaml-- λ: L_zero-  dataize:-    𝛿1: $.x-  𝑛: ⟦ λ ⤍ 𝜎 ⟧-- λ: L_dec-  dataize:-    𝛿1: $.x-  𝑛: ⟦ λ ⤍ 𝜎 ⟧-- λ: L_if-  dataize:-    𝛿1: $.c-  join:-    on: 𝛿1-    left: $.left-    right: $.right-$ phino morph --acyclic=plausible --partial --sweet --hide-rho --flat \-    --symbolic=facta.yaml --locator='Q.x' --protocol=facta.txt facta.phi-Φ.if( c ↦ Φ.zero( x ↦ 𝜎1:λ ), left ↦ Φ.box( v ↦ 00-:Δ ), right ↦ Φ.fact( n ↦ Φ.dec( x ↦ 𝜎1:λ ), acc ↦ Φ.pair( head ↦ 𝜎1:λ, tail ↦ Φ.box( v ↦ 00-:Δ ) ) ).φ ).φ-$ grep 'looped\|diverged' facta.txt-      looped 𝑛·0·5  # plausible-```--<!-- markdownlint-enable MD013 -->--Under `plausible` the same formation means one the formation entered earlier-is embedded in: the two have the same attributes, data and λ function at the-top, and every term the earlier one bound there is found again in the later-one, as it stands or somewhere below a wrapper it gained. Any symbol stands for-any other, and nothing is looked for under a `ρ`, which holds the object a term-came from rather than a term it grew into. The first `if` morphs both of its-sides before it joins them, and its `right` holds a second `if`, which holds-the first under one more `pair` and one more `dec`, so that morph is cut, and-its `looped …` line says `plausible`. A side cut that way gets the firing of-the first `if` stuck before it joins anything, so no `diverged …` line is-written, and the cut parks the `if` where it stands. A call-nested in its own operand, such as a sum of sums, is never cut, since the-inner call is smaller than the outer one and cannot hold it.-The mode is not sound, though: a recursion whose argument grows on its way to-stopping is cut too, which is why the line names the mode that made the cut.--The mode also fires every formation once. Every use of a binding copies the-term bound to it, so a program reading `truncated ↦ ρ.abs.floor` in five-places fires `abs` and `floor` five times over and mints five symbols for one-value, and a guard written that way spends its whole budget saying the same-thing again. 𝔼 is a function of the formation it fires: the entry that-answers is found by the λ name the formation carries, every operand is-reduced from its bindings inside the one universe of the run, and the answer-is built from what they came down to. So a run under `plausible` keeps what-every firing answered, by the formation it fired, and a later firing of the-same formation takes that answer, with the very symbols the first one minted,-instead of making it again. Two arguments spelling one term then come to one-symbol, here through an entry `L_pair` that fires both with `evaluate` lines:--```bash-$ cat twins.yaml-- λ: L_number_plus-  dataize:-    𝛿1: $.ρ-    𝛿2: $.x-  𝑛: Φ.number( φ ↦ ⟦ λ ⤍ 𝜎 ⟧ )-- λ: L_pair-  morph:-    𝑛1: $.a-    𝑛2: $.b-  evaluate:-    𝑛3: 𝑛1-    𝑛4: 𝑛2-  𝑛: ⟦ φ ↦ ⟦ a ↦ 𝑛3, b ↦ 𝑛4 ⟧ ⟧-$ cat twins.phi-⟦-  bytes ↦ ⟦ φ ↦ ∅ ⟧,-  number ↦ ⟦ φ ↦ ∅, plus(ρ, x) ↦ ⟦ λ ⤍ L_number_plus ⟧ ⟧,-  pair ↦ ⟦ a ↦ ∅, b ↦ ∅, λ ⤍ L_pair ⟧,-  t ↦ Φ.pair( 7.plus( 5.plus( 6 ) ), 7.plus( 5.plus( 6 ) ) ).φ-⟧-$ phino morph --symbolic=twins.yaml --acyclic=plausible --locator=Q.t \-    --sweet --hide-rho twins.phi-⟦-  a ↦ ⟦ φ ↦ 𝜎2:λ, plus(x) ↦ L_number_plus:λ ⟧,-  b ↦ ⟦ φ ↦ 𝜎2:λ, plus(x) ↦ L_number_plus:λ ⟧-⟧-```--Under `proven` `b` lands on `𝜎4`, since its `evaluate` line fires the outer sum-and the inner one once more. The protocol of the run above holds three firings-of `L_number_plus`, two for `a` and one for `b`, since it records where 𝔼 was-asked and what it answered there; the one for `b` carries the answer lines of-the outer one for `a` and no operand line, since nothing was reduced for it,-and it is not charged to `--max-firings`, which counts the firings the run-made. A join answered that way writes no `joined` line, since no line of its-own binds the condition and the first firing already said what every symbol-it minted stands for.-The formation is compared with everything it carries, `ρ` included, so-a firing on another object is another firing, yet it may take the same answer-all the same: the run keeps every answer by the λ name and what the operands-came down to as well, and a firing whose operands come down to the data, the-symbols and the normal forms of one already answered takes that answer once-they are down, minting nothing. An attribute read from the `φ` of an object-and then reached as a binding of it is read through two `ξ`, each the object-minus the attribute being read, so it is two formations, but one firing, and-so one symbol. A symbol counts as the symbol it is, not as the datum every-symbol manufactures, so two firings over two symbols stay two. The protocol-writes such a firing with its operand lines and the answer lines of the one-it took the answer from, and `--max-firings` charges it like a firing made,-since a firing is charged as it starts bringing its operands down, which is-the only place a recursion widening inside its operands can be cut. A firing-cut by the mode on its way to an answer keeps the cut, and the next firing of-the same formation is cut at its own site without reducing anything first. A-firing that got stuck keeps the λ function it got stuck on, and the next-firing of the same formation gets stuck at its own site the same way, with-nothing reduced under it, as long as nothing new was answered in between.-Once something was, the formation is fired again, since an operand that could-not be brought down the first time may come down now.--The `--max-loops=<n>` option makes either mode more patient. A frame is cut-only once more than `n` of the formations the frames above it entered are the-same as the one it is about to enter, so a recursion that stops within `n`-rounds is never cut, and one that goes on is cut `n` rounds later. The default-is `0`, which cuts on the first repeat. The option needs `--acyclic`, since-nothing else cuts a loop. Here the second `f` holds `Φ.start`, which the first-`f` is bound to, under a `Φ.stop`, so `plausible` cuts it, even though `stop`-answers data at once:--<!-- markdownlint-disable MD013 -->--```bash-$ cat once.phi-⟦-  f ↦ ⟦ x ↦ ∅, φ ↦ ξ.x.again ⟧,-  start ↦ ⟦ again ↦ Φ.f( x ↦ Φ.stop( v ↦ Φ.start ) ) ⟧,-  stop ↦ ⟦ v ↦ ∅, again ↦ ⟦ Δ ⤍ 01- ⟧ ⟧,-  t ↦ Φ.f( x ↦ Φ.start )-⟧-$ phino dataize --locator=Q.t --acyclic=plausible once.phi-[ERROR]: Reduction entered a formation it is already inside: ⟦ x ↦ Φ.stop( v ↦ Φ.start ), φ ↦ x.again ⟧-$ phino dataize --locator=Q.t --acyclic=plausible --max-loops=1 once.phi-01--```--<!-- markdownlint-enable MD013 -->--## Rewrite--You can rewrite this expression with the help of [rules](#rule-structure)-defined in the `my-rule.yml` YAML file (here, the `!d1` and `!B1` are capturing-groups, similar to regular expressions):--```yaml-name: My custom rule-pattern: ⟦ Δ ⤍ !d1, !B1 ⟧-result: ⟦ Δ ⤍ 62-79-65, !B1 ⟧-```--Then, rewrite:--```bash-$ phino rewrite --rule=my-rule.yml hello.phi-⟦ φ ↦ ⟦ Δ ⤍ 62-79-65 ⟧, t ↦ ξ.k, k ↦ ⟦⟧ ⟧-```--If you want to use many rules, just use `--rule` as many times as you need:--```bash-phino rewrite --rule=rule1.yaml --rule=rule2.yaml ...-```--You can also use [built-in rules](resources), which are designed-to normalize expressions:--```bash-phino rewrite --normalize hello.phi-```--Both flags may be combined, so that your own rules are applied-alongside the built-in ones, in a single rewriting session:--```bash-phino rewrite --normalize --rule=my-rule.yaml hello.phi-```--Some rules mint fresh synthetic names via the `random-string` built-in. To-keep the output reproducible across runs, `phino` seeds the random generator-deterministically with `0` by default. Use `--seed` to pick a different seed:--```bash-phino rewrite --seed=42 --rule=my-rule.yml hello.phi-```--If no input file is provided, the 𝜑-expression is taken from `stdin`:--```bash-$ echo '⟦ φ ↦ ⟦ Δ ⤍ 68-65-6C-6C-6F ⟧ ⟧' | phino rewrite --rule=my-rule.yml-⟦ φ ↦ ⟦ Δ ⤍ 62-79-65 ⟧ ⟧-```--You're able to pass [`XMIR`][xmir] as input. Use `--input=xmir` and `phino`-will parse given `XMIR` from file or `stdin` and convert it to `phi` AST.--```bash-phino rewrite --rule=my-rule.yaml --input=xmir file.xmir-```--Also `phino` supports 𝜑-expressions in-[ASCII](https://en.wikipedia.org/wiki/ASCII) format and with-syntax sugar. The `rewrite` command also allows you to desugar the expression-and print it in canonical syntax:--```bash-$ echo '[[ @ -> Q.io.stdout("hello") ]]' | phino rewrite-⟦-  φ ↦ Φ.io.stdout( α0 ↦ Φ.string( φ ↦ Φ.bytes( φ ↦ ⟦ Δ ⤍ 68-65-6C-6C-6F ⟧ ) ) )-⟧-```--The two styles may be mixed in one expression, so `[[ x -> ∅ ]]` reads the-same as `⟦ x ↦ ∅ ⟧`. The `--monoform` option forbids that: the first symbol-that has two spellings picks the style of the whole expression, and a later-symbol of the other style is an error. Under `--monoform`, `[[ x -> ∅ ]]`-fails, since `∅` at 1:9 mixes with the ASCII `[[` at 1:1. The option is-accepted by `rewrite`, `dataize`, `morph`, `merge` and `match`.--A formation of a single binding may be written as its asset, a colon,-and the attribute the asset is bound to:--```text-⟦ Δ ⤍ FF-AA ⟧    = FF-AA:Δ    = FF-AA:D-⟦ λ ⤍ 𝜎1 ⟧       = 𝜎1:λ       = !S1:L-⟦ a ↦ ∅ ⟧        = ∅:a        = ?:a-⟦ φ ↦ ξ.a ⟧      = ξ.a:φ      = $.a:@-```--The colon binds as tightly as a dot, so `ξ.a:φ.b` is `⟦ φ ↦ ξ.a ⟧.b`.-With `--sweet`, `phino` prints every such formation this way, so-`⟦ x ↦ ⟦ φ ↦ ξ.a ⟧ ⟧` comes out as `a:φ:x` and `⟦ x(a) ↦ ⟦ φ ↦ a ⟧ ⟧`-as `⟦ x(a) ↦ a:φ ⟧`: the formation inline voids open takes the sugar,-while the one holding the voids keeps its brackets, and so does-every formation in the salty syntax and in [LaTeX][latex].--A formation has a receiver `ρ` only when it declares one among its voids, the-way EO declares `^`: `⟦ ρ ↦ ∅, t ↦ ξ.ρ.k ⟧`, or `a(ρ) ↦ ⟦ t ↦ ξ.ρ.k ⟧` with-the void inline. `phino` adds none of its own, and a dispatch into a formation-that declares none hands it no `ρ` (the `skip` rule), so `ξ.ρ` there is `⊥`:--<!-- markdownlint-disable MD013 -->--```bash-$ echo '⟦ x ↦ ⟦ k ↦ ⟦ Δ ⤍ 01- ⟧, a ↦ ⟦ ρ ↦ ∅, t ↦ ξ.ρ.k ⟧ ⟧.a.t ⟧' | phino rewrite --normalize --sweet-01-:Δ:x-$ echo '⟦ x ↦ ⟦ k ↦ ⟦ Δ ⤍ 01- ⟧, b ↦ ⟦ t ↦ ξ.ρ.k ⟧ ⟧.b.t ⟧' | phino rewrite --normalize --sweet-⊥:x-```--<!-- markdownlint-enable MD013 -->--## Merge--You can merge several 𝜑-expressions into a single one by merging their-top level formations:--```bash-$ cat bytes.phi-⟦ bytes ↦ ⟦ φ ↦ ∅ ⟧ ⟧-$ cat number.phi-⟦-  number ↦ ⟦-    φ ↦ ∅,-    plus(x) ↦ ⟦ λ ⤍ L_number_plus ⟧-  ⟧-⟧-$ cat minus.phi-⟦ number ↦ ⟦ minus(x) ↦ ⟦ λ ⤍ L_number_minus ⟧ ⟧ ⟧-$ phino merge bytes.phi number.phi minus.phi --sweet-⟦-  bytes(φ) ↦ ⟦⟧,-  number(φ) ↦ ⟦ plus(x) ↦ L_number_plus:λ, minus(x) ↦ L_number_minus:λ ⟧-⟧-```--## Match--You can test the 𝜑-expression matches against the [rule](#rule-structure)-pattern. The result output contains matched substitutions:--```bash-$ phino match --pattern='⟦ Δ ⤍ !d, !B ⟧' hello.phi-B >> ⟦⟧-d >> 68-65-6C-6C-6F-```--## Compile--By default, `phino` reads its rules from YAML and interprets them at every-step. The `compile` command turns the rules into Haskell instead. Then a-second build of `phino` runs them as plain functions:--```bash-phino compile-cabal build all-```--The command writes the module `compiled/generated/Compiled.hs`, which git-ignores. It compiles the built-in rules of normalization, the-contextualization function 𝒞, and every file you pass with `--rule`. The-`--target` option writes the module somewhere else.--A build links the module in only when the Cabal flag `compiled` is on. If-there is no `cabal.project.local`, `compile` creates one that turns the flag-on. If the file already exists, `compile` leaves it alone and prints the two-lines to add to it:--```text-package phino-  flags: +compiled-```--The compiled rules take exactly the same steps as the YAML ones, so the-output and every `--sequence` stay the same. A few things are still read from-YAML at runtime:--* the rules of morphing (𝕄) and dataization (𝔻);-* a `--rule` file that changed after `compile`;-* the pattern of `match` and the `rewrite:` blocks of the `--symbolic` file.--The `explain` command also reads the rules from YAML.--`compile` refuses a rule it cannot turn into Haskell and names the reason.-For example, it refuses a rule with `having`, a `where` function other than-`contextualize` or `named`, or the conditions `matches` and `part-of`.--A binary built this way refuses to run if the built-in rules changed after-the last `compile`, since it would run rules nobody wrote. Run `compile`-again and rebuild. To test the whole suite against the compiled rules, run:--```bash-make compiled-```--## Explain--You can _explain_ the built-in rules by printing them in [LaTeX][latex]-format. Pass exactly one of `--normalize`, `--morph`, `--dataize` or-`--contextualize` for the rewriting, morphing (𝕄), dataization (𝔻) or-contextualization (𝒞) rules (or `--rule` for a custom rule file):--```bash-$ phino explain --normalize-\begin{tabular}{rl}-\phinoNormalizationRule{alpha}-  { [[ B_1, \tau -> ?, B_2 ]] ( \phiTerminal{\alpha_{i}} -> e ) }-  { [[ B_1, \tau -> ?, B_2 ]] ( \tau -> e ) }-  { $ i = \vert \overline{ B_1 } \vert $ }-  { }-\phinoNormalizationRule{dc}-  { T ( \tau -> e ) }-  { T }-  { }-  { }-...-\phinoNormalizationRule{stop}-  { [[ B ]] . \tau }-  { T }-  { $ [ \tau \char44{} @ \char44{} L ] \cap B = \emptyset $ }-  { }-\end{tabular}-```--The morphing and dataization rules are printed the same way:--```bash-$ phino explain --morph-\begin{tabular}{rl}-\phinoMorphingRule{mf}-  { \mathbb{M}( [[ B ]], e ) }-  { [[ B ]] }-  { }-  { }-...-\phinoMorphingRule{universe}-  { \mathbb{M}( Q, e ) }-  { \mathbb{M}( \phinoNormalize{ e }, e ) }-  { $ e \not= Q $ }-  { }-\end{tabular}-```--```bash-$ phino explain --dataize-\begin{tabular}{rl}-\phinoDataizationRule{delta}-  { \phinoDataize{ [[ B_1, D> \delta_0, B_2 ]] } }-  { \delta_0 }-  { }-  { }-...-\phinoDataizationRule{norm}-  { \phinoDataize{ n } }-  { \phinoDataize{ \mathbb{M}( n, e ) } }-  { }-  { }-\end{tabular}-```--```bash-$ phino explain --contextualize-\begin{phinoContextualizationInference}-  \phinoName{cxi}-  \phinoConclusion{ \phinoContextualize{ \phiTerminal{\xi} }{ k }{ k } }-\end{phinoContextualizationInference}-...-\begin{phinoContextualizationInference}-  \phinoName{cd}-  \phinoPremise{ \phinoContextualize{ n }{ k }{ n_1 } }-  \phinoConclusion{ \phinoContextualize{ n . \tau }{ k }{ n_1 . \tau } }-\end{phinoContextualizationInference}-```--For more details, use `phino [COMMAND] --help` option.--## Rule structure--This is BNF-like yaml rule structure. Here types ended with-apostrophe, like `Attribute'` are built types from 𝜑-expression [AST](src/AST.hs)--```bnfc-Rule:-  name: String-  pattern: String-  result: String-  when: Condition?       # predicate, works with substitutions before extension-  where: [Extension]?    # substitution extensions-  having: Condition?     # predicate, works with substitutions after extension--Condition:-  = and: [Condition]     # logical AND-  | or:  [Condition]     # logical OR-  | not: Condition       # logical NOT-  | eq:                  # compare two comparable objects-      - Comparable-      - Comparable-  | in:                  # returns True if every given attribute exists in-      - Attribute' | [Attribute'] # the union of the given bindings-      - Binding' | [Binding']-  | nf: Expression'      # returns True if given expression in normal form-                         # which means that no more other normalization rules-                         # can be applied-  | absolute: Expression' # returns True if given expression is xi-free, i.e.-                         # there is no ξ outside of a formation: it is Φ, a-                         # formation, a dispatch with a xi-free subject, or an-                         # application with a xi-free subject and argument.-                         # Combined with a normal-form check by the '𝑘'/'!k'-                         # meta variable, which ranges over the absolute-                         # expressions 𝒦 ⊆ 𝒩, used by the Rcopy rule.-  | matches:             # returns True if given expression after dataization-      - String           # matches to given regex-      - Expression-  | part-of:             # returns True if given expression is attached to any-      - Expression'      # attribute in ginve bindings-      - BiMeta'-  | formation:           # returns True if given expression is a formation-      Expression'        # (an abstraction ⟦…⟧); used by morphing 'md'-                         # as 'not (formation 𝑛)', so a non-formation head is-                         # morphed and a formation head is left to 'ml'-  | gt:                  # returns True if the first comparable object is-      - Comparable       # greater than the second one-      - Comparable-  | disjoint:            # returns True if none of the given attributes exists-      - [Attribute']     # in the given bindings-      - Binding'--Comparable:              # comparable object that may be used in 'eq' condition-  = Attribute'-  | Number-  | Expression'--Number:                  # comparable number-  = Integer              # just regular integer-  | IndexMeta'           # 𝑖 (or !i), the index captured by an α𝑖 argument-  | length: BiMeta'      # calculate length of bindings by given meta binding-  | domain: BiMeta'      # calculate number of unique attributes in given-                         # meta binding (excluding 'assets')--Extension:               # substitutions extension used to introduce new meta variables-  meta: [ExtArgument]    # new introduced meta variable-  function: String       # name of the function-  args: [ExtArgument]    # arguments of the function--ExtArgument-  = Bytes'               # !d-  | Binding'             # !B-  | Expression'          # !e-  | Attribute'           # !t-```--Here's list of functions that are supported for extensions:--* `contextualize` - function of two arguments, that rewrites given expression-  depending on provided context according to the contextualization-  [rules](assets/contextualize.jpg)-* `random-tau` - creates an attribute with a random unique name. It accepts no-  arguments; uniqueness is guaranteed across all names already taken in the-  document.-* `dataize` - dataizes given expression and returns bytes.-* `concat` - accepts bytes or dataizable expressions as arguments,-  concatenates them into single sequence and convert it to expression-  that can be pretty printed as human readable string:-  `Φ.string(Φ.bytes⟦ Δ ⤍ !d ⟧)`.-* `sed` - pattern replacer, works like unix `sed` function.-  Accepts two arguments: target expression and pattern.-  Pattern must start with `s/`, consists of three parts-  separated by `/`, for example, this pattern `s/\\s+//g`-  replaces all the spaces with empty string. To escape braces and slashes-  in pattern and replacement parts - use them with `\\`,-  e.g. `s/\\(.+\\)//g`.-* `random-string` - accepts dataizable expression or bytes as pattern.-  Replaces `%x` and `%d` formatters with random hex numbers and-  decimals accordingly. Uniqueness is guaranteed during one-  execution of `phino`.-* `size` - accepts exactly one meta binding and returns size of it and-  `Φ.number`.-* `tau` - accepts `Φ.string`, dataizes it and converts it to attribute.-  If dataized string can't be converted to attribute - an error is thrown.-* `string` - accepts `Φ.string` or `Φ.number` or attribute and converts it-  to `Φ.string`.-* `number` - accepts `Φ.string` and converts it `Φ.number`-* `sum` - accepts list of `Φ.number` or `Φ.bytes` and returns sum of them as `Φ.number`-* `join` - accepts list of bindings and returns list of joined bindings. Duplicated-  `ρ`, `Δ` and `λ` attributes are ignored, all other duplicated attributes are replaced-  with unique attributes using `random-tau` function.--## Meta variables--The `phino` supports meta variables to write 𝜑-expression patterns for-capturing attributes, bindings, etc.--This is the list of supported meta variables:--* `!t` || `𝜏` - attribute-* `!i` || `𝑖` - the index of a positional (α) application argument,-                captured by writing `α𝑖` (or `~!i`)-* `!e` || `𝑒` - any expression-* `!n` || `𝑛` - any expression that is already in normal form (behaves like-                `!e`/`𝑒`, but only binds a sub-expression in NF, so no explicit-                `nf:` guard is needed)-* `!k` || `𝑘` - any expression that is absolute, i.e. xi-free and in normal-                form (ranges over `𝒦 ⊆ 𝒩`); behaves like `!e`/`𝑒` but only-                binds an absolute sub-expression, so no explicit `absolute:`-                or `nf:` guard is needed-* `!B` || `𝐵` - list of bindings-* `!d` || `𝛿` - bytes in meta delta binding-* `!F` || `𝑓` - function name in meta lambda binding-* `!S` || `𝜎` - a symbol standing where a λ name stands (see-                [Symbols](#symbols)). It is spelled the way a meta variable is-                spelled but is a name and no capture: `𝜎1` is one concrete-                symbol, which no substitution ever binds, and a bare `𝜎` in the-                answer of a `--symbolic` entry asks for a fresh one--A meta variable carries a suffix, like `!B1` or `𝜏2`, to name what it-captured, so that the `result`, `when`, `where` and `having` of a rule can-read it back. An index starts with one: a suffix of `0`, as in `!B0` or-`𝜏0`, is refused where it is written, because it is a first index spelled-wrong and no name. A positional argument keeps counting from zero, though,-since `α0` is an index of the calculus and no meta variable.--Written bare, with no suffix at all, a meta variable is anonymous: it matches-whatever term stands in its place, every occurrence on its own, and binds no-name. Two anonymous metas of one kind are therefore two different captures,-which is what lets a pattern ask for any two attributes without inventing-names for them:--```yaml-name: two-attributes-pattern: '⟦ 𝜏 ↦ 𝑒, 𝜏 ↦ 𝑒 ⟧'-result: '⟦ x ↦ ⟦ Δ ⤍ 2A- ⟧ ⟧'-```--Spelled with suffixes, that pattern would read `⟦ 𝜏1 ↦ 𝑒1, 𝜏2 ↦ 𝑒2 ⟧` and-name four captures the result never mentions, while `⟦ 𝜏1 ↦ 𝑒1, 𝜏1 ↦ 𝑒1 ⟧`-would be rejected as a duplicated attribute.--Nothing can refer to an anonymous meta, since it has no name to be referred to-by. Writing one outside a `pattern` (or the `match`, `universe` and `c-match` of-an inference rule) is a mistake in the rule and is reported as the rule loads.--A positional (α) application argument is written as `α0`, `~0` (ASCII), or-`α𝑖`/`~!i` when its index is captured by an `!i`/`𝑖` meta variable.--Incorrect usage of meta variables in 𝜑-expression patterns leads to-parsing errors.--## Benchmark--To run performance benchmarks, you need [Java 8+][java] and [curl][curl].-Maven is downloaded automatically on first run via `benchmark/mvnw`.--The benchmark uses the compiled [`Native`][jna-native] class from-[JNA][jna] — a large real-world Java class — as its test input.-On first run, `make bench` downloads the class, disassembles it to-[XMIR][xmir] via [jeo-maven-plugin][jeo], converts it to 𝜑 using-`phino rewrite`, and caches the results in `benchmark/tmp/`.-Subsequent runs skip straight to the benchmarks.--Besides parsing, printing and rewriting that class, the suite morphs-symbolically. `benchmark/demo.phi` is a small world whose entries name the λ-functions of `benchmark/atoms.yaml`, and each entry is a case of its own, so-that a slowdown of one of them is a line of the report rather than a share of-a single total. The smallest entry — one λ function fired against one unknown-— is timed twice, over the demo world alone and over the same world merged-into the class, and the two numbers say between them what the world around an-entry costs (see [#1291][issue-1291]).--A case whose single run is measured in seconds gets fewer warmups and fewer-batches than a case measured in microseconds, since the whole suite runs-inside one job; the report says how many of each a case was given.--```bash-make bench-```--<!-- benchmark_begin -->--```text-=== parse/phi ===-  warmup:     3 iterations-  batches:    10 x 1-  total:      2384353.897 μs-  avg:        238435.390 μs-  min:        215363.988 μs-  max:        278245.089 μs-  std dev:    23622.956 μs-=== parse/xmir ===-  warmup:     3 iterations-  batches:    10 x 1-  total:      9841499.835 μs-  avg:        984149.984 μs-  min:        914833.083 μs-  max:        1089305.395 μs-  std dev:    63059.625 μs-=== rewrite/normalize ===-  warmup:     3 iterations-  batches:    10 x 1-  total:      21.060 μs-  avg:        2.106 μs-  min:        1.743 μs-  max:        2.835 μs-  std dev:    0.353 μs-=== print/sweet/multiline ===-  warmup:     3 iterations-  batches:    10 x 1-  total:      5903216.355 μs-  avg:        590321.635 μs-  min:        558729.133 μs-  max:        647658.702 μs-  std dev:    30079.890 μs-=== print/sweet/flat ===-  warmup:     3 iterations-  batches:    10 x 1-  total:      725031.333 μs-  avg:        72503.133 μs-  min:        65432.404 μs-  max:        87819.934 μs-  std dev:    8921.233 μs-=== print/salty/multiline ===-  warmup:     3 iterations-  batches:    10 x 1-  total:      19159507.382 μs-  avg:        1915950.738 μs-  min:        1869384.073 μs-  max:        1995426.872 μs-  std dev:    37221.957 μs-=== morph/symbolic/demo/e1 ===-  warmup:     3 iterations-  batches:    10 x 8-  total:      10829.586 μs-  avg:        135.370 μs-  min:        128.488 μs-  max:        150.356 μs-  std dev:    6.143 μs-=== morph/symbolic/demo/e2 ===-  warmup:     3 iterations-  batches:    10 x 127-  total:      172246.803 μs-  avg:        135.627 μs-  min:        132.999 μs-  max:        140.016 μs-  std dev:    2.239 μs-=== morph/symbolic/demo/e3 ===-  warmup:     3 iterations-  batches:    10 x 132-  total:      180903.978 μs-  avg:        137.048 μs-  min:        135.244 μs-  max:        140.700 μs-  std dev:    1.678 μs-=== morph/symbolic/demo/e4 ===-  warmup:     3 iterations-  batches:    10 x 122-  total:      164908.704 μs-  avg:        135.171 μs-  min:        132.237 μs-  max:        139.304 μs-  std dev:    1.887 μs-=== morph/symbolic/demo/e5 ===-  warmup:     3 iterations-  batches:    10 x 124-  total:      165931.498 μs-  avg:        133.816 μs-  min:        132.648 μs-  max:        135.746 μs-  std dev:    0.863 μs-=== morph/symbolic/native/e5 ===-  warmup:     3 iterations-  batches:    10 x 5-  total:      61089.359 μs-  avg:        1221.787 μs-  min:        1040.609 μs-  max:        2303.375 μs-  std dev:    370.371 μs-=== morph/symbolic/accum/0 ===-  warmup:     3 iterations-  batches:    10 x 84-  total:      94035.149 μs-  avg:        111.947 μs-  min:        110.083 μs-  max:        113.534 μs-  std dev:    1.157 μs-=== morph/symbolic/accum/400 ===-  warmup:     3 iterations-  batches:    10 x 69-  total:      92238.501 μs-  avg:        133.679 μs-  min:        130.178 μs-  max:        137.564 μs-  std dev:    2.813 μs-```--The results were calculated in [this GHA job][benchmark-gha]-on 2026-10-21 at 16:09,-on Linux with 4 CPUs.--<!-- benchmark_end -->--## How to Contribute--Fork repository, make changes, then send us a [pull request][guidelines].-We will review your changes and apply them to the `master` branch shortly,-provided they don't violate our quality standards. To avoid frustration,-before sending us your pull request please make sure all your tests pass:--```bash-make all-```--To generate a local coverage report for development, run:--```bash-make coverage-```--To build a `phino` executable into the root of the repository, run:--```bash-make phino-```--This produces an executable `phino` (or `phino.exe` on Windows) in the-project root, which you can run directly for quick local testing:--```bash-./phino --version-```--You will need [GHC ≥ 9.6.7][GHC] and [Cabal ≥ 3.0 (recommended)][cabal]-or [Stack ≥ 3.0][stack] installed.--[cabal]: https://www.haskell.org/cabal/-[stack]: https://docs.haskellstack.org/en/stable/install_and_upgrade/-[GHC]: https://www.haskell.org/ghc/-[guidelines]: https://www.yegor256.com/2014/04/15/github-guidelines.html-[xmir]: https://news.eolang.org/2022-11-25-xmir-guide.html-[latex]: https://en.wikipedia.org/wiki/LaTeX-[java]: https://www.java.com/en/download/-[curl]: https://curl.se/-[gha]: https://docs.github.com/en/actions-[jna]: https://github.com/java-native-access/jna-[jna-native]: https://github.com/java-native-access/jna/blob/master/src/com/sun/jna/Native.java-[jeo]: https://github.com/objectionary/jeo-maven-plugin-[issue-1291]: https://github.com/objectionary/phino/issues/1291-[benchmark-gha]: https://github.com/objectionary/phino/actions/runs/39630420210+cabal install --overwrite-policy=always phino-0.0.157+phino --version+```++Or download binary from the internet using [curl](https://curl.se/) or+[wget](https://en.wikipedia.org/wiki/Wget):++```bash+sudo curl -o /usr/local/bin/phino http://phino.objectionary.com/releases/macos-15/phino-latest+sudo chmod +x /usr/local/bin/phino+phino --version+```++Download paths are:++* Ubuntu 22.04: <http://phino.objectionary.com/releases/ubuntu-22.04/phino-latest>+* Ubuntu 24.04: <http://phino.objectionary.com/releases/ubuntu-24.04/phino-latest>+* MacOS (ARM): <http://phino.objectionary.com/releases/macos-15/phino-latest>+* MacOS (Intel): <http://phino.objectionary.com/releases/macos-14-large/phino-latest>+* Windows: <http://phino.objectionary.com/releases/windows-2022/phino-latest.exe>++In a [GitHub Actions][gha] workflow, one step downloads the binary+for the runner and puts it on the `PATH`:++```yaml+- uses: objectionary/phino@0.0.157+  with:+    version-file: phino-version.txt+- run: phino --version+```++The file holds the version to install, such as `0.0.150`.+Give `version: 0.0.150` instead of `version-file` to write the version+in the workflow itself.++## Build++To build `phino` from source, clone this repository and build it with+[Cabal][cabal]:++```bash+git clone git@github.com:objectionary/phino.git+cd phino+cabal build all+sudo cp "$(cabal list-bin phino)" /usr/local/bin/phino+```++The global `--pin` option fails the run when the installed version is not+the one given, and `--pin-file` reads that version from a file:++```bash+phino --pin=0.0.0.67 dataize hello.phi+```++## Dataize++Then, you dataize the expression:++```bash+$ phino dataize hello.phi+68-65-6C-6C-6F+```++### Symbolic λ functions++`phino` implements no λ function.+They come from a YAML file given with `--symbolic`, one entry per λ function.+An entry never computes a value.+It answers with a term that holds a symbol, `𝜎`, which names a value nobody+worked out.+Here is `atoms.yaml`, which the examples below use:++```yaml+- λ: L_number_(plus|times)+  dataize:+    𝛿1: $.ρ+    𝛿2: $.x+  𝑛: Φ.number( φ ↦ ⟦ λ ⤍ 𝜎 ⟧ )+```++```bash+$ cat sum.phi+⟦+  bytes ↦ ⟦ φ ↦ ∅ ⟧,+  number ↦ ⟦ φ ↦ ∅, plus(ρ, x) ↦ ⟦ λ ⤍ L_number_plus ⟧ ⟧,+  φ ↦ 5.plus( 6 )+⟧+$ phino dataize --symbolic=atoms.yaml --sweet --hide-rho sum.phi+40-45-00-00-00-00-00-00+```++Dataizing a symbol always gives the same bytes, the number 42, as above.++Inside an entry, `$` is the formation being fired and `Φ` is the universe.+A meta is a named placeholder: `𝛿1`, `𝛿2` and so on hold bytes, while `𝑛1`,+`𝑛2` and so on hold terms.+An entry may hold only the keys below, and any other key refuses the whole+file:++* `λ` is a regular expression that matches the whole λ name.+  No two entries may match the same name.+* `dataize` binds a bytes meta to the data a term comes down to through+  dataization, 𝔻.+  The term is a path from `$`, as in `𝛿1: $.ρ`, or a meta another line binds,+  as in `𝛿2: 𝑛1`.+* `morph` binds a term meta to the normal form a term reaches through+  morphing, 𝕄.+  The term is a path from `$`, as in `𝑛1: $.x`, or a meta another line binds,+  as in `𝑛4: 𝑛3`.+* `evaluate` binds a term meta to the answer of firing the formation another+  meta holds, through evaluation, 𝔼, as in `𝑛2: 𝑛1`.+* `defer` binds a term meta to a fresh symbol when another meta holds a copy+  of an object whose argument is a bare symbol, `⟦ λ ⤍ 𝜎1 ⟧`, and to that+  term as it is otherwise.+* `stay` binds a term meta to the term another meta holds, with every+  `( ρ ↦ … )` applied to a dispatch dropped.+* `symbolize` binds a term meta to the term another meta holds, with every+  datum turned into a fresh symbol.+* `rewrite` binds a term meta to the term the meta under `of` holds, with the+  rules under `rules` applied and nothing normalized afterwards.+  The rules are written as in a [rule file](#rule-structure).+* `𝑛` is the term the entry answers with.+  A bare `𝜎` in it stands for a fresh symbol, and no `𝛿` may appear in it.+* `join` answers with one term made of two sides, in place of `𝑛`.+  It holds `on`, the condition, which is a bytes meta of `dataize` or a path+  from `$`.+  It holds `left` and `right`, the two sides, each a path from `$` or a term+  meta.+  It may hold `otherwise`, a λ name that joins two formations of different+  shapes one attribute at a time, and the file must answer that name too.++An entry holds either `𝑛` or `join`, never both.+The lines of all blocks run in the order of the metas they read, not in the+order of the blocks.+A line that reads a meta nothing binds refuses the file.++### Partial evaluation++A λ name that no entry answers stops the run with an error.+With `--partial`, the run prints the residual program instead: what the known+inputs decide is computed, and the rest stays in place:++```bash+$ cat partial.phi+⟦+  bytes ↦ ⟦ φ ↦ ∅ ⟧,+  number ↦ ⟦+    φ ↦ ∅,+    plus(ρ, x) ↦ ⟦ λ ⤍ L_number_plus ⟧,+    times(ρ, x) ↦ ⟦ λ ⤍ L_number_times ⟧,+    as-bool ↦ ⟦ λ ⤍ L_number_as_bool ⟧+  ⟧,+  φ ↦ 2.times( 3 ).plus( 4 ).as-bool+⟧+$ phino dataize --symbolic=atoms.yaml --sweet --hide-rho partial.phi+[ERROR]: No entry of --symbolic answers the λ function 'L_number_as_bool'+$ phino dataize --symbolic=atoms.yaml --partial --sweet --hide-rho partial.phi+L_number_as_bool:λ+```++Here `2.times( 3 ).plus( 4 )` was computed, and the symbol it came to sits in+the hidden `ρ`, while `as-bool` stays where it was.+An operand that reaches the terminator `⊥` is left in place the same way.++### Reducing a term inside a universe++The `--inside` option reduces a term the program does not hold, taking the+program as the universe Φ.+It cannot be combined with `--locator`:++```bash+$ cat universe.phi+⟦+  bytes ↦ ⟦ φ ↦ ∅ ⟧,+  number ↦ ⟦ φ ↦ ∅, plus(ρ, x) ↦ ⟦ λ ⤍ L_number_plus ⟧ ⟧+⟧+$ phino dataize --symbolic=atoms.yaml --inside='5.plus( 6 )' universe.phi+40-45-00-00-00-00-00-00+```++### Limits++Three options stop a run that goes on for too long:++* `--max-steps` bounds how deep morphing and dataization may nest,+  `1000` by default;+* `--max-firings` bounds how many λ functions the whole run may fire;+* `--max-seconds` bounds how many seconds the whole run may take.++`--partial` leaves a site in place when it runs out of steps or firings, but+a run out of time always fails.++## Morph++Morphing, 𝕄, evaluates as far as the object model allows without asking+for data, and stops at the first formation it reaches:++```bash+$ cat two.phi+⟦+  bytes ↦ ⟦ φ ↦ ∅ ⟧,+  number ↦ ⟦ φ ↦ ∅, plus(ρ, x) ↦ ⟦ λ ⤍ L_number_plus ⟧ ⟧,+  φ ↦ 5.plus( 6 ).plus( 7 )+⟧+$ phino dataize --symbolic=atoms.yaml --sweet --hide-rho two.phi+40-45-00-00-00-00-00-00+$ phino morph --symbolic=atoms.yaml --locator=Q.φ --sweet --hide-rho two.phi+⟦ x ↦ 7.0, λ ⤍ L_number_plus ⟧+```++The inner `5.plus( 6 )` fires, because `.plus` is dispatched on its result,+while the outer call is left for dataization.+Where no formation is reachable, `morph` prints `⊥`.+It fails on a term that is not a normal form.+It takes every option of `dataize`.++### Acyclic morphing++`phino` stops no recursion by itself, so a run that never ends fails once+`--max-steps` runs out.+The `--acyclic=<mode>` option makes the run notice a loop instead.+When a reduction is about to enter a formation it is already inside, the run+stops there and leaves the term in place, as `--partial` does.+The mode says when two formations are the same:++* `proven` means equal up to a renaming of symbols;+* `plausible` also means that the later one holds the earlier one under+  wrappers it gained, such as a growing accumulator, so it may cut a+  recursion that would have stopped.++Under `plausible`, a formation is fired only once, and a later firing of it+takes the answer of the first one.+The `--max-loops=<n>` option lets a reduction enter such a formation `n` more+times before it is cut.+`dataize` takes `--acyclic` too, and fails on a cut unless `--partial` is+given, since a cut term holds no data to print.++## Protocol++The `--protocol` option writes every λ function the run fired into a file,+as a tree.+A file whose name ends in `.xml` gets the tree as XML, for a program to read,+and any other name gets indented text.+Here, `L_down` answers with a call of its own object over a fresh symbol, so+without `--acyclic` the run would go on until `--max-steps` runs out:++```bash+$ cat down.yaml+- λ: L_down+  dataize:+    𝛿1: $.x+  𝑛: Φ.down( x ↦ ⟦ λ ⤍ 𝜎 ⟧ )+$ cat down.phi+⟦+  down ↦ ⟦ x ↦ ∅, λ ⤍ L_down ⟧,+  y ↦ Φ.down( x ↦ ⟦ λ ⤍ 𝜎1 ⟧ ).φ+⟧+$ phino morph --symbolic=down.yaml --locator=Q.y --acyclic=proven \+    --protocol=down.txt --sweet --hide-rho down.phi+⟦ x ↦ 𝜎1:λ, λ ⤍ L_down ⟧.φ+$ cat down.txt+𝕄(Φ.y):+  𝑛·0·1 := Φ.down( x ↦ 𝜎1:λ )+  𝔼(L_down):+    𝔻(ξ.x):+      𝛿·1·1 := 𝔻(𝜎1:λ)+    𝛿1·1 := 𝛿·1·1  # 𝔻(ξ.x)+    𝑛·1·1 := Φ.down( x ↦ 𝜎2:λ )  # 𝑛+    𝕄(𝑛·1·1):+      𝑛·1·2 := Φ.down( x ↦ 𝜎2:λ )+      morphed 𝑛·1·3 := 𝑛·1·2+    𝑛·1·4 := 𝑛·1·2  # 𝕄(𝑛·1·1)+  looped 𝑛·0·1  # proven+```++In a name such as `𝑛·1·2`, the first number counts the firings, `0` outside+every firing, and the second counts the names within that firing.+The name `𝛿1·1` is the meta `𝛿1` of the first firing.+A protocol may hold these lines:++* `𝔻(…):` and `𝕄(…):` open a block where dataization or morphing starts at a+  new site, and what it does stands under it.+* `𝔼(…):` opens one firing, named by its λ function.+* `𝑛·0·1 := …` names an object an application made.+* `𝛿1·1 := …` binds a meta of the entry, and the comment names the judgment+  and the term it came from.+* `𝑛·1·1 := …  # 𝑛` is the term the entry answered with, and the line+  commented with `𝕄(𝑛·1·1)` is its normal form.+* `boxed …` is dataization stepping into the `φ` of a formation.+* `𝛿·1·1 := …` closes a `𝔻` block with the data it found, or with+  `𝔻(𝜎1:λ)` where it came down to a symbol.+* `morphed …` closes a `𝕄` block with the term it came to.+* `joined …`, `fabricated …` and `diverged …` are what a `join` made of its+  two sides, or why it could not join them.+* `deferred …` and `symbolized …` are the symbols a `defer` or a `symbolize`+  line minted.+* `unanswered …` is a λ name no entry answers.+* `stuck …` closes a firing that got stuck, and `stall …` stands under one+  that `--acyclic=plausible` answered with an earlier stall.+* `looped …` is where `--acyclic` cut a loop.+* `starved …`, `spent …` and `timeout …` are where `--max-steps`,+  `--max-firings` and `--max-seconds` ran out.+* `msec …`, `firings …` and `fps …` close the file, and the listing above+  leaves them out, since they differ from run to run.++The `--abridged` option folds every formation longer than sixty-four+characters, or than `--abridged=WIDTH`, into its `φ`, `Δ` and `λ` and a count+of the rest.+The `--abridged-data` option also cuts every byte string longer than eight+bytes down to its first two bytes and its last two.++## Rewrite++You can rewrite this expression with the help of [rules](#rule-structure)+defined in the `my-rule.yml` YAML file (here, the `!d1` and `!B1` are capturing+groups, similar to regular expressions):++```yaml+name: My custom rule+pattern: ⟦ Δ ⤍ !d1, !B1 ⟧+result: ⟦ Δ ⤍ 62-79-65, !B1 ⟧+```++Then, rewrite:++```bash+$ phino rewrite --rule=my-rule.yml hello.phi+⟦ φ ↦ ⟦ Δ ⤍ 62-79-65 ⟧, t ↦ ξ.k, k ↦ ⟦⟧ ⟧+```++If you want to use many rules, just use `--rule` as many times as you need:++```bash+phino rewrite --rule=rule1.yaml --rule=rule2.yaml ...+```++You can also use [built-in rules](resources), which are designed+to normalize expressions, alone or together with your own rules:++```bash+phino rewrite --normalize --rule=my-rule.yaml hello.phi+```++Some rules mint fresh names with the `random-string` function, and `--seed`+picks the seed of its random generator, `0` by default.++If no input file is provided, the 𝜑-expression is taken from `stdin`:++```bash+$ echo '⟦ φ ↦ ⟦ Δ ⤍ 68-65-6C-6C-6F ⟧ ⟧' | phino rewrite --rule=my-rule.yml+⟦ φ ↦ ⟦ Δ ⤍ 62-79-65 ⟧ ⟧+```++You're able to pass [`XMIR`][xmir] as input. Use `--input=xmir` and `phino`+will parse given `XMIR` from file or `stdin` and convert it to `phi` AST.++```bash+phino rewrite --rule=my-rule.yaml --input=xmir file.xmir+```++Also `phino` supports 𝜑-expressions in+[ASCII](https://en.wikipedia.org/wiki/ASCII) format and with+syntax sugar. The `rewrite` command also allows you to desugar the expression+and print it in canonical syntax:++```bash+$ echo '[[ @ -> Q.io.stdout("hello") ]]' | phino rewrite+⟦+  φ ↦ Φ.io.stdout( α0 ↦ Φ.string( φ ↦ Φ.bytes( φ ↦ ⟦ Δ ⤍ 68-65-6C-6C-6F ⟧ ) ) )+⟧+```++The two styles may be mixed in one expression, unless `--monoform` is given.++A formation of a single binding may be written as its asset, a colon,+and the attribute the asset is bound to:++```text+⟦ Δ ⤍ FF-AA ⟧    = FF-AA:Δ    = FF-AA:D+⟦ λ ⤍ 𝜎1 ⟧       = 𝜎1:λ       = !S1:L+⟦ a ↦ ∅ ⟧        = ∅:a        = ?:a+⟦ φ ↦ ξ.a ⟧      = ξ.a:φ      = $.a:@+```++The colon binds as tightly as a dot, so `ξ.a:φ.b` is `⟦ φ ↦ ξ.a ⟧.b`.+With `--sweet`, `phino` prints every such formation this way.++A formation has a receiver `ρ` only when it declares one among its voids, the+way EO declares `^`, as in `⟦ ρ ↦ ∅, t ↦ ξ.ρ.k ⟧`.+`phino` adds none of its own, so `ξ.ρ` in a formation that declares none+is `⊥`.++## Merge++You can merge several 𝜑-expressions into a single one by merging their+top level formations:++```bash+$ cat bytes.phi+⟦ bytes ↦ ⟦ φ ↦ ∅ ⟧ ⟧+$ cat number.phi+⟦+  number ↦ ⟦+    φ ↦ ∅,+    plus(x) ↦ ⟦ λ ⤍ L_number_plus ⟧+  ⟧+⟧+$ cat minus.phi+⟦ number ↦ ⟦ minus(x) ↦ ⟦ λ ⤍ L_number_minus ⟧ ⟧ ⟧+$ phino merge bytes.phi number.phi minus.phi --sweet+⟦+  bytes(φ) ↦ ⟦⟧,+  number(φ) ↦ ⟦ plus(x) ↦ L_number_plus:λ, minus(x) ↦ L_number_minus:λ ⟧+⟧+```++## Match++You can test the 𝜑-expression matches against the [rule](#rule-structure)+pattern. The result output contains matched substitutions:++```bash+$ phino match --pattern='⟦ Δ ⤍ !d, !B ⟧' hello.phi+B >> ⟦⟧+d >> 68-65-6C-6C-6F+```++## Compile++By default, `phino` reads its rules from YAML and interprets them at every+step.+The `compile` command turns the built-in rules of normalization,+contextualization, morphing and dataization, and every `--rule` file, into+the Haskell module `compiled/generated/Compiled.hs`, and a second build links+it in:++```bash+phino compile+cabal build all+```++The build links the module in only when the Cabal flag `compiled` is on.+If there is no `cabal.project.local`, `compile` creates one that turns the+flag on, and otherwise it prints the lines to add to it.+The compiled rules take exactly the same steps as the YAML ones.+`compile` refuses a rule it cannot turn into Haskell and names the reason.+A binary built this way refuses to run once the built-in rules changed after+the last `compile`.+To test the whole suite against the compiled rules, run `make compiled`.++## Explain++You can _explain_ the built-in rules by printing them in [LaTeX][latex]+format. Pass exactly one of `--normalize`, `--morph`, `--dataize` or+`--contextualize` for the rewriting, morphing (𝕄), dataization (𝔻) or+contextualization (𝒞) rules (or `--rule` for a custom rule file):++```bash+phino explain --normalize+```++For more details, use `phino [COMMAND] --help` option.++## Rule structure++This is BNF-like yaml rule structure. Here types ended with+apostrophe, like `Attribute'` are built types from 𝜑-expression [AST](src/AST.hs)++```bnfc+Rule:+  name: String+  pattern: String+  result: String+  when: Condition?       # predicate, works with substitutions before extension+  where: [Extension]?    # substitution extensions+  having: Condition?     # predicate, works with substitutions after extension++Condition:+  = and: [Condition]     # logical AND+  | or:  [Condition]     # logical OR+  | not: Condition       # logical NOT+  | eq:                  # compare two comparable objects+      - Comparable+      - Comparable+  | in:                  # returns True if every given attribute exists in+      - Attribute' | [Attribute'] # the union of the given bindings+      - Binding' | [Binding']+  | nf: Expression'      # returns True if given expression in normal form+                         # which means that no more other normalization rules+                         # can be applied+  | absolute: Expression' # returns True if given expression is xi-free, i.e.+                         # there is no ξ outside of a formation: it is Φ, a+                         # formation, a dispatch with a xi-free subject, or an+                         # application with a xi-free subject and argument.+                         # Combined with a normal-form check by the '𝑘'/'!k'+                         # meta variable, which ranges over the absolute+                         # expressions 𝒦 ⊆ 𝒩, used by the Rcopy rule.+  | matches:             # returns True if given expression after dataization+      - String           # matches to given regex+      - Expression+  | part-of:             # returns True if given expression is attached to any+      - Expression'      # attribute in ginve bindings+      - BiMeta'+  | formation:           # returns True if given expression is a formation+      Expression'        # (an abstraction ⟦…⟧); used by morphing 'md'+                         # as 'not (formation 𝑛)', so a non-formation head is+                         # morphed and a formation head is left to 'ml'+  | gt:                  # returns True if the first comparable object is+      - Comparable       # greater than the second one+      - Comparable+  | disjoint:            # returns True if none of the given attributes exists+      - [Attribute']     # in the given bindings+      - Binding'++Comparable:              # comparable object that may be used in 'eq' condition+  = Attribute'+  | Number+  | Expression'++Number:                  # comparable number+  = Integer              # just regular integer+  | IndexMeta'           # 𝑖 (or !i), the index captured by an α𝑖 argument+  | length: BiMeta'      # calculate length of bindings by given meta binding+  | domain: BiMeta'      # calculate number of unique attributes in given+                         # meta binding (excluding 'assets')++Extension:               # substitutions extension used to introduce new meta variables+  meta: [ExtArgument]    # new introduced meta variable+  function: String       # name of the function+  args: [ExtArgument]    # arguments of the function++ExtArgument+  = Bytes'               # !d+  | Binding'             # !B+  | Expression'          # !e+  | Attribute'           # !t+```++Here's list of functions that are supported for extensions:++* `contextualize` - function of two arguments, that rewrites given expression+  depending on provided context according to the contextualization+  [rules](assets/contextualize.jpg)+* `random-tau` - creates an attribute with a random unique name. It accepts no+  arguments; uniqueness is guaranteed across all names already taken in the+  document.+* `dataize` - dataizes given expression and returns bytes.+* `concat` - accepts bytes or dataizable expressions as arguments,+  concatenates them into single sequence and convert it to expression+  that can be pretty printed as human readable string:+  `Φ.string(Φ.bytes⟦ Δ ⤍ !d ⟧)`.+* `sed` - pattern replacer, works like unix `sed` function.+  Accepts two arguments: target expression and pattern.+  Pattern must start with `s/`, consists of three parts+  separated by `/`, for example, this pattern `s/\\s+//g`+  replaces all the spaces with empty string. To escape braces and slashes+  in pattern and replacement parts - use them with `\\`,+  e.g. `s/\\(.+\\)//g`.+* `random-string` - accepts dataizable expression or bytes as pattern.+  Replaces `%x` and `%d` formatters with random hex numbers and+  decimals accordingly. Uniqueness is guaranteed during one+  execution of `phino`.+* `size` - accepts exactly one meta binding and returns size of it and+  `Φ.number`.+* `tau` - accepts `Φ.string`, dataizes it and converts it to attribute.+  If dataized string can't be converted to attribute - an error is thrown.+* `string` - accepts `Φ.string` or `Φ.number` or attribute and converts it+  to `Φ.string`.+* `number` - accepts `Φ.string` and converts it `Φ.number`+* `sum` - accepts list of `Φ.number` or `Φ.bytes` and returns sum of them as `Φ.number`+* `join` - accepts list of bindings and returns list of joined bindings. Duplicated+  `ρ`, `Δ` and `λ` attributes are ignored, all other duplicated attributes are replaced+  with unique attributes using `random-tau` function.++## Meta variables++The `phino` supports meta variables to write 𝜑-expression patterns for+capturing attributes, bindings, etc.++This is the list of supported meta variables:++* `!t` || `𝜏` - attribute+* `!i` || `𝑖` - the index of a positional (α) application argument,+                captured by writing `α𝑖` (or `~!i`)+* `!e` || `𝑒` - any expression+* `!n` || `𝑛` - any expression that is already in normal form (behaves like+                `!e`/`𝑒`, but only binds a sub-expression in NF, so no explicit+                `nf:` guard is needed)+* `!k` || `𝑘` - any expression that is absolute, i.e. xi-free and in normal+                form (ranges over `𝒦 ⊆ 𝒩`); behaves like `!e`/`𝑒` but only+                binds an absolute sub-expression, so no explicit `absolute:`+                or `nf:` guard is needed+* `!B` || `𝐵` - list of bindings+* `!d` || `𝛿` - bytes in meta delta binding+* `!F` || `𝑓` - function name in meta lambda binding+* `!S` || `𝜎` - a symbol standing where a λ name stands (see+                [Symbolic λ functions](#symbolic-λ-functions)). It is spelled+                the way a meta variable is spelled but is a name and no+                capture: `𝜎1` is one concrete symbol, which no substitution+                ever binds, and a bare `𝜎` in the answer of a `--symbolic`+                entry asks for a fresh one++A meta variable carries a suffix, like `!B1` or `𝜏2`, to name what it+captured, so that the `result`, `when`, `where` and `having` of a rule can+read it back.+A suffix starts with one, so `!B0` is refused.+Without a suffix, a meta variable is anonymous: it matches any term on its+own, binds no name, and is refused anywhere but in a pattern, so+`⟦ 𝜏 ↦ 𝑒, 𝜏 ↦ 𝑒 ⟧` matches any two attributes.++A positional (α) application argument is written as `α0`, `~0` (ASCII), or+`α𝑖`/`~!i` when its index is captured by an `!i`/`𝑖` meta variable.++## Benchmark++To run performance benchmarks, you need [Java 8+][java] and [curl][curl]:++```bash+make bench+```++On the first run, `make bench` downloads the compiled [`Native`][jna-native]+class of [JNA][jna], turns it into [XMIR][xmir] with+[jeo-maven-plugin][jeo] and then into 𝜑, and caches the results in+`benchmark/tmp/`.+Besides parsing, printing and rewriting that class, the suite morphs the+entries of `benchmark/demo.phi` symbolically, one case per entry.++<!-- benchmark_begin -->++```text+=== parse/phi ===+  warmup:     3 iterations+  batches:    10 x 1+  total:      1409532.338 μs+  avg:        140953.234 μs+  min:        123104.580 μs+  max:        169752.210 μs+  std dev:    19431.803 μs+=== parse/xmir ===+  warmup:     3 iterations+  batches:    10 x 1+  total:      6269190.661 μs+  avg:        626919.066 μs+  min:        570410.014 μs+  max:        708906.404 μs+  std dev:    53168.480 μs+=== rewrite/normalize ===+  warmup:     3 iterations+  batches:    10 x 1+  total:      10.204 μs+  avg:        1.020 μs+  min:        0.842 μs+  max:        1.442 μs+  std dev:    0.179 μs+=== print/sweet/multiline ===+  warmup:     3 iterations+  batches:    10 x 1+  total:      3999871.155 μs+  avg:        399987.116 μs+  min:        357813.221 μs+  max:        448393.174 μs+  std dev:    27725.936 μs+=== print/sweet/flat ===+  warmup:     3 iterations+  batches:    10 x 1+  total:      455172.450 μs+  avg:        45517.245 μs+  min:        40716.412 μs+  max:        55514.792 μs+  std dev:    6068.706 μs+=== print/salty/multiline ===+  warmup:     3 iterations+  batches:    10 x 1+  total:      11916902.966 μs+  avg:        1191690.297 μs+  min:        1124787.007 μs+  max:        1324046.797 μs+  std dev:    62960.748 μs+=== morph/symbolic/demo/e1 ===+  warmup:     3 iterations+  batches:    10 x 15+  total:      8063.096 μs+  avg:        53.754 μs+  min:        52.409 μs+  max:        56.184 μs+  std dev:    0.955 μs+=== morph/symbolic/demo/e2 ===+  warmup:     3 iterations+  batches:    10 x 294+  total:      163954.216 μs+  avg:        55.767 μs+  min:        54.415 μs+  max:        56.517 μs+  std dev:    0.716 μs+=== morph/symbolic/demo/e3 ===+  warmup:     3 iterations+  batches:    10 x 277+  total:      161047.794 μs+  avg:        58.140 μs+  min:        57.025 μs+  max:        58.757 μs+  std dev:    0.553 μs+=== morph/symbolic/demo/e4 ===+  warmup:     3 iterations+  batches:    10 x 271+  total:      156357.626 μs+  avg:        57.697 μs+  min:        57.150 μs+  max:        58.248 μs+  std dev:    0.363 μs+=== morph/symbolic/demo/e5 ===+  warmup:     3 iterations+  batches:    10 x 260+  total:      160590.902 μs+  avg:        61.766 μs+  min:        59.089 μs+  max:        79.533 μs+  std dev:    5.932 μs+=== morph/symbolic/native/e5 ===+  warmup:     3 iterations+  batches:    10 x 5+  total:      35947.906 μs+  avg:        718.958 μs+  min:        682.191 μs+  max:        764.860 μs+  std dev:    22.764 μs+=== morph/symbolic/accum/0 ===+  warmup:     3 iterations+  batches:    10 x 142+  total:      68966.685 μs+  avg:        48.568 μs+  min:        47.428 μs+  max:        49.936 μs+  std dev:    0.778 μs+=== morph/symbolic/accum/400 ===+  warmup:     3 iterations+  batches:    10 x 112+  total:      63083.756 μs+  avg:        56.325 μs+  min:        55.561 μs+  max:        57.281 μs+  std dev:    0.537 μs+```++The results were calculated in [this GHA job][benchmark-gha]+on 2026-10-26 at 12:05,+on Linux with 4 CPUs.++<!-- benchmark_end -->++## How to Contribute++Fork repository, make changes, then send us a [pull request][guidelines].+We will review your changes and apply them to the `master` branch shortly,+provided they don't violate our quality standards. To avoid frustration,+before sending us your pull request please make sure all your tests pass:++```bash+make all+```++To generate a local coverage report for development, run:++```bash+make coverage+```++To build a `phino` executable into the root of the repository, run:++```bash+make phino+```++You will need [GHC ≥ 9.6.7][GHC] and [Cabal ≥ 3.0 (recommended)][cabal]+or [Stack ≥ 3.0][stack] installed.++[cabal]: https://www.haskell.org/cabal/+[stack]: https://docs.haskellstack.org/en/stable/install_and_upgrade/+[GHC]: https://www.haskell.org/ghc/+[guidelines]: https://www.yegor256.com/2014/04/15/github-guidelines.html+[xmir]: https://news.eolang.org/2022-11-25-xmir-guide.html+[latex]: https://en.wikipedia.org/wiki/LaTeX+[java]: https://www.java.com/en/download/+[curl]: https://curl.se/+[gha]: https://docs.github.com/en/actions+[jna]: https://github.com/java-native-access/jna+[jna-native]: https://github.com/java-native-access/jna/blob/master/src/com/sun/jna/Native.java+[jeo]: https://github.com/objectionary/jeo-maven-plugin+[benchmark-gha]: https://github.com/objectionary/phino/actions/runs/40319368788
phino.cabal view
@@ -1,6 +1,6 @@ cabal-version: 3.0 name: phino-version: 0.0.157+version: 0.0.158 license: MIT synopsis: Command-Line Manipulator of 𝜑-Calculus Expressions description: Please see the README on GitHub at <https://github.com/objectionary/phino#readme>@@ -219,9 +219,7 @@     directory >=1.3.7 && <1.4,     filepath >=1.4.200 && <1.6,     hspec >=2.11.0 && <2.12,-    hspec-core >=2.11.0 && <2.12,     megaparsec >=9.5 && <9.9,-    optparse-applicative >=0.18 && <0.20,     phino,     QuickCheck >=2.14 && <2.20,     random >=1.2 && <1.4,
src/Bytes.hs view
@@ -82,21 +82,12 @@       | n < 10 = chr (fromIntegral n + ord '0')       | otherwise = chr (fromIntegral n + ord 'A' - 10) -btsToNum :: Bytes -> Either Int Double+btsToNum :: Bytes -> Double btsToNum hx =   let bytes = btsToWord8 hx    in if length bytes /= 8         then throw (InvalidNumberLength (length bytes))-        else-          let word = toWord64BE bytes-              val = wordToDouble word-           in if isNaN val || isInfinite val || isNegativeZero val-                then Right val-                else case properFraction val :: (Integer, Double) of-                  (n, 0.0)-                    | n >= toInteger (minBound :: Int) && n <= toInteger (maxBound :: Int) ->-                        Left (fromInteger n)-                  _ -> Right val+        else wordToDouble (toWord64BE bytes)   where     toWord64BE :: [Word8] -> Word64     toWord64BE [a, b, c, d, e, f, g, h] =
src/CLI/Helpers.hs view
@@ -24,7 +24,7 @@ import Data.Maybe import qualified Data.Text as T import qualified Data.Yaml as Yaml-import Deps (Evaluation (EvRun), Judgment, SaveEvalFunc, SaveStepFunc, dontSaveEval, emptyNesting, emptyProgress, emptyProtocol, endEval, endEvalXml, progressed, saveEval, saveEvalXml, saveStep)+import Deps (Evaluation (EvRun), Judgment, SaveEvalFunc, SaveStepFunc, dontSaveEval, emptyNesting, emptyProgress, emptyProtocol, endEval, endEvalXml, progressed, resited, saveEval, saveEvalXml, saveStep) import Encoding import Engine (Engine, fresh, yaml) import Files (ensuredFile, overwrite)@@ -165,8 +165,8 @@   held <- newIORef []   (universe, aiming) <- insideUniverse target expr ctx{_saveEval = \record -> modifyIORef' held (record :)}   heading ctx._saveEval printCtx judgment aiming._locator-  mapM_ ctx._saveEval . reverse =<< readIORef held-  pure (universe, aiming{_saveEval = ctx._saveEval})+  mapM_ (ctx._saveEval . resited ctx._site aiming._locator) . reverse =<< readIORef held+  pure (universe, aiming{_site = aiming._locator, _saveEval = ctx._saveEval}) aimed _ _ (Just _) expr _ =   invalidCLIArguments     (printf "The option --inside requires the input expression to be a formation, but given: %s" (P.printExpression expr))@@ -294,7 +294,7 @@   where     reportInvalidRule :: Yaml.ParseException -> IO Y.Rule     reportInvalidRule err =-      invalidCLIArguments (printf "Could not load rule from '%s'\n%s" file (Yaml.prettyPrintParseException err))+      invalidCLIArguments (printf "Could not load rule from '%s'\n%s" file (Y.shiftYamlError (Yaml.prettyPrintParseException err)))  validateRewriteRule :: Y.Rule -> IO Y.Rule validateRewriteRule rule =
src/CLI/Parsers.hs view
@@ -305,7 +305,9 @@               \keeps its φ, Δ and λ bindings and folds the rest into a count, as '+34', while every byte string \               \it writes stays whole; the width is 64 characters unless given as --abridged=WIDTH"         )-        <|> option auto (long "abridged" <> metavar "WIDTH" <> internal)+        <|> option+          (boundedIntOption >>= validateIntOption (> 0) "--abridged must be positive")+          (long "abridged" <> metavar "WIDTH" <> internal)     )  optAbridgedData :: Parser Bool@@ -323,7 +325,7 @@ optSeed :: Parser Int optSeed =   option-    auto+    boundedIntOption     ( long "seed"         <> metavar "SEED"         <> help "Seed for the random generator that mints fresh synthetic names, making output reproducible across runs"
src/CLI/Runners.hs view
@@ -78,6 +78,7 @@   save <- saveStepFunc _stepsDir printCtx included excluded   let steps = map (stepOf linked) rules   (rewrittens, exceeded) <- rewrite expr steps (RewriteContext loc _maxDepth _maxCycles _depthSensitive Nothing (building linked) linked._normal (every steps) _must _breakpoint save dontSaveMade)+  when (isJust _breakpoint && NE.length rewrittens == 1) (clearedStepsDir _stepsDir)   rewrittens' <- exclude (if _sequence then NE.toList rewrittens else [NE.last rewrittens]) >>= include   logDebug (printf "Printing rewritten 𝜑-expression as %s" (show _outputFormat))   exprs <- printRewrittens printCtx (rewrittens', exceeded)@@ -214,9 +215,11 @@       when (isJust _abridged && isNothing _protocol) (invalidCLIArguments "The option --abridged requires --protocol, since only the protocol is abridged")       when (_abridgedData && isNothing _abridged) (invalidCLIArguments "The option --abridged-data requires --abridged, since only an abridged protocol cuts its data")       when (_maxLoops > 0 && isNothing _acyclic) (invalidCLIArguments "The option --max-loops requires --acyclic, since only --acyclic cuts a loop")-      when-        (isJust _inside && _locator /= "Q")-        (invalidCLIArguments "The options --inside and --locator cannot be used together, since --inside aims the run at the binding it mints")+      when (isJust _inside) $ do+        parsed <- parseExpressionThrows _locator+        when+          (parsed /= ExRoot)+          (invalidCLIArguments "The options --inside and --locator cannot be used together, since --inside aims the run at the binding it mints")     toPrintCtx :: Atoms -> Expression -> PrintContext     toPrintCtx atoms focus =       PrintCtx@@ -299,9 +302,11 @@       when (isJust _abridged && isNothing _protocol) (invalidCLIArguments "The option --abridged requires --protocol, since only the protocol is abridged")       when (_abridgedData && isNothing _abridged) (invalidCLIArguments "The option --abridged-data requires --abridged, since only an abridged protocol cuts its data")       when (_maxLoops > 0 && isNothing _acyclic) (invalidCLIArguments "The option --max-loops requires --acyclic, since only --acyclic cuts a loop")-      when-        (isJust _inside && _locator /= "Q")-        (invalidCLIArguments "The options --inside and --locator cannot be used together, since --inside aims the run at the binding it mints")+      when (isJust _inside) $ do+        parsed <- parseExpressionThrows _locator+        when+          (parsed /= ExRoot)+          (invalidCLIArguments "The options --inside and --locator cannot be used together, since --inside aims the run at the binding it mints")     toPrintCtx :: Atoms -> Expression -> PrintContext     toPrintCtx atoms focus =       PrintCtx
src/CST.hs view
@@ -177,7 +177,7 @@   | EX_DISPATCH {expr :: EXPRESSION, space :: SPACE, attr :: ATTRIBUTE}   | EX_APPLICATION {expr :: EXPRESSION, space :: SPACE, eol :: EOL, tab :: TAB, argument :: APP_ARGUMENT, eol' :: EOL, tab' :: TAB, indent :: Int}   | EX_STRING {str :: String, tab :: TAB, rhos :: [Argument]}-  | EX_NUMBER {num :: Either Int Double, tab :: TAB, rhos :: [Argument]}+  | EX_NUMBER {num :: Double, tab :: TAB, rhos :: [Argument]}   | EX_NONFINITE {global :: GLOBAL, nonfinite :: NonFinite, tab :: TAB, rhos :: [Argument]}   | EX_META {meta :: META}   | EX_PHI_MEET {prefix :: Maybe String, idx :: Int, expr :: EXPRESSION}@@ -268,7 +268,7 @@ sweetNumber bts   | btsSize bts /= 8 = False sweetNumber bts = case btsToNum bts of-  Right dbl | isNaN dbl || isInfinite dbl -> isJust (btsToNonFinite bts)+  dbl | isNaN dbl || isInfinite dbl -> isJust (btsToNonFinite bts)   _ -> True  sweetString :: Bytes -> Bool
src/Dataize.hs view
@@ -16,7 +16,7 @@ import qualified Data.List.NonEmpty as NE import Data.Maybe (listToMaybe) import qualified Data.Text as T-import Deps (Evaluation (..), Judgment (..), State (..))+import Deps (Evaluation (..), Judgment (..), State (..), resited) import Engine (Engine (..)) import qualified Inference as In import Locator (locatedExpression)@@ -41,8 +41,13 @@     Left (StuckAt func seq parked) | _partial -> residual seq parked{_stuck = Just func}     Left (OutOfStepsAt _ seq parked) | _partial -> residual seq parked     Left (LoopingAt _ seq parked) | _partial -> residual seq parked+    Left (Undataizable term seq parked) | _partial -> do+      unless (dead `elem` _parked) (_saveEval (EvStuck _nesting dead Dataization term))+      residual seq parked{_stuck = Just dead}     Left failure -> throwIO (failure :: ReduceException)   where+    dead :: T.Text+    dead = "⊥"     residual :: NonEmpty Rewritten -> State -> IO (Outcome, [Rewritten], State)     residual seq parked = do       residue <- locatedExpression _locator (fst (NE.head seq))@@ -61,10 +66,17 @@           seq' <- leadsTo seq step (ExBytes bts) ctx           pure ((bts, NE.toList seq'), state'{_manufactured = Nothing})         Just (In.Onward way built world, state') -> do-          (dataizable, state'') <- onward seq state' way built ctx-          dataize' dataizable world state'' ctx-        Nothing -> throwIO (Undataizable expr state)+          ctx' <- boxing way built ctx+          (dataizable, state'') <- onward seq state' way built ctx'+          dataize' dataizable world state'' ctx'+        Nothing -> throwIO (Undataizable expr seq state)   where+    boxing :: In.Way -> Expression -> ReduceContext -> IO ReduceContext+    boxing way built ctx+      | way == In.Normalized (Dataization, "box") = do+          ctx._saveEval (EvBoxed ctx._nesting built)+          pure ctx{_saveEval = ctx._saveEval . resited ctx._site built}+      | otherwise = pure ctx     unknown :: Expression -> Maybe Int     unknown (ExFormation bds) = listToMaybe [idx | BiLambda (FnSymbol idx) <- bds]     unknown _ = Nothing@@ -77,16 +89,9 @@ reduction :: ReductionFunc reduction univ ctx expr state = do   (universe, aiming) <- insideUniverse expr univ ctx-  result <- try (dataize universe state aiming)-  case result of-    Right (outcome, _, state') -> pure (reached outcome, state')-    Left (Undataizable term state') | ctx._partial -> do-      unless (dead `elem` ctx._parked) (ctx._saveEval (EvStuck ctx._nesting dead Dataization term))-      pure (Nothing, state'{_stuck = Just dead})-    Left failure -> throwIO failure+  (outcome, _, state') <- dataize universe state aiming+  pure (reached outcome, state')   where-    dead :: T.Text-    dead = "⊥"     reached :: Outcome -> Maybe Bytes     reached (Dataized bytes) = Just bytes     reached (Residual _) = Nothing
src/Deps.hs view
@@ -119,6 +119,7 @@   | EvStarted Int Judgment Expression   | EvDelta Int Bytes   | EvManufactured Int Int+  | EvBoxed Int Expression   | EvLooped Int Judgment Acyclic Expression Expression   | EvStuck Int T.Text Judgment Expression   | EvStall Int T.Text@@ -165,7 +166,7 @@  ieee :: Bytes -> Maybe String ieee bytes-  | btsSize bytes == 8 = Just (show (either fromIntegral id (btsToNum bytes) :: Double))+  | btsSize bytes == 8 = Just (show (btsToNum bytes))   | otherwise = Nothing  tier :: Evaluation -> Int@@ -174,6 +175,7 @@ tier (EvStarted depth _ _) = depth tier (EvDelta depth _) = depth tier (EvManufactured depth _) = depth+tier (EvBoxed depth _) = depth tier (EvLooped depth _ _ _ _) = depth tier (EvStuck depth _ _ _) = depth tier (EvStall depth _) = depth@@ -209,6 +211,11 @@     renamed :: Maybe [(Expression, a)] -> Maybe [(Expression, a)]     renamed entries = Just ((term, naming) : filter ((/= term) . fst) (fromMaybe [] entries)) +namedFormation :: Expression -> a -> Named a -> Named a+namedFormation term@(ExFormation bds) naming names+  | all ((`notElem` [Just AtLambda, Just AtDelta]) . attributeFromBinding) bds = namedInsert term naming names+namedFormation _ _ names = names+ abbreviated :: Named Expression -> Expression -> Expression abbreviated names term   | Map.null names = term@@ -235,7 +242,7 @@   , _built :: Map.Map Int Int   , _deltas :: Map.Map Int Int   , _found :: Maybe (Either Int Bytes, String)-  , _blocks :: [(Int, Maybe String, String, Bool)]+  , _blocks :: [(Int, Maybe (Judgment, String), String, Bool)]   , _answered :: [(Int, Expression, String)]   } @@ -248,6 +255,7 @@   , _closing :: [(Int, String)]   , _objects :: Named Expression   , _numbered :: Map.Map Int Int+  , _parts :: Map.Map Int Int   , _datums :: Map.Map Int Int   , _held :: Maybe (Either Int Bytes, String)   , _heads :: [(Int, (Judgment, Expression))]@@ -261,7 +269,7 @@ type Naming = StateT (Nesting, [String]) IO  emptyNesting :: Nesting-emptyNesting = Nesting 0 [] [] Map.empty Map.empty Map.empty Nothing [] [] [] Map.empty Map.empty 0+emptyNesting = Nesting 0 [] [] Map.empty Map.empty Map.empty Map.empty Nothing [] [] [] Map.empty Map.empty 0  saveEval :: Handle -> IORef Protocol -> (Expression -> IO String) -> (Expression -> IO String) -> SaveEvalFunc saveEval handle cursor printed printed' report = do@@ -270,6 +278,7 @@   where     forgotten :: Evaluation -> Protocol -> Protocol     forgotten EvDelta{} protocol = protocol+    forgotten EvManufactured{} protocol = protocol     forgotten EvWorld{} protocol = protocol     forgotten _ protocol = protocol{_found = Nothing}     beside :: Int -> Evaluation -> Protocol -> Protocol@@ -289,7 +298,7 @@     located :: Protocol -> Expression -> IO String     located protocol site = case [naming | (_, built, naming) <- protocol._answered, built == site] of       naming : _ -> pure naming-      [] -> render site+      [] -> salted site     context :: Protocol -> Judgment -> Expression -> IO [String]     context protocol judgment site = do       locator <- located protocol site@@ -297,7 +306,7 @@       pure [said | innermost protocol /= Just said]     innermost :: Protocol -> Maybe String     innermost protocol = case protocol._blocks of-      (_, heading, _, _) : _ -> heading+      (_, heading, _, _) : _ -> snd <$> heading       [] -> Nothing     remarked :: String -> [String] -> String     remarked line [] = line@@ -312,7 +321,7 @@       logDebug (printf "Saved one line of the protocol: %s" (dropWhile (== ' ') line))     written :: Int -> Evaluation -> Protocol -> IO (Protocol, Maybe String)     written _ (EvRun judgment locator) protocol =-      pure (protocol{_begun = True, _blocks = [(0, Just heading, heading ++ ":", True)]}, Just (heading ++ ":"))+      pure (protocol{_begun = True, _blocks = [(0, Just (judgment, heading), heading ++ ":", True)]}, Just (heading ++ ":"))       where         heading :: String         heading = printf "%s(%s)" (letter judgment) (T.unpack locator)@@ -334,15 +343,20 @@       let heading = printf "%s(%s)" (letter judgment) locator           inner = dropWhile (\(level, _, _, _) -> level > at) protocol._blocks       pure $ case inner of-        (level, Just heading', _, _) : _ | level == at && heading' == heading -> (protocol{_blocks = inner}, Nothing)-        _ -> (protocol{_blocks = (at, Just heading, indented at (heading ++ ":"), False) : dropWhile (\(level, _, _, _) -> level >= at) inner}, Nothing)+        (level, Just heading', _, _) : _ | level == at && heading' == (judgment, heading) -> (protocol{_blocks = inner}, Nothing)+        _ -> (protocol{_blocks = (at, Just (judgment, heading), indented at (heading ++ ":"), False) : dropWhile (\(level, _, _, _) -> level >= at) inner}, Nothing)     written at (EvDelta _ bytes) protocol = do       datum <- render (ExBytes bytes)-      let index = maybe 1 (+ 1) (Map.lookup (opener protocol) protocol._deltas)-          naming :: String-          naming = printf "%s·%d" (labelled protocol sigil) index-      pure (protocol{_deltas = Map.insert (opener protocol) index protocol._deltas, _found = Just (Right bytes, naming)}, Just (indented at (remarked (printf "%s := %s" naming datum) (maybe [] pure (ieee bytes)))))-    written _ EvManufactured{} protocol = pure (protocol, Nothing)+      let (naming, protocol') = stored (Right bytes) protocol+      pure (protocol', Just (indented at (remarked (printf "%s := %s" naming datum) (maybe [] pure (ieee bytes)))))+    written at (EvManufactured _ symbol) protocol = do+      datum <- spelt (Left symbol)+      let (naming, protocol') = stored (Left symbol) protocol+      pure (protocol', Just (indented at (printf "%s := %s" naming datum)))+    written at (EvBoxed _ term) protocol = do+      let (index, counted) = numbered protocol+      taken <- printed (abbreviatedInside protocol._made term)+      pure (counted{_made = namedInsert term (alias (opener protocol) index) counted._made}, Just (indented at (printf "boxed %s·%d := %s" (labelled protocol answer) index taken)))     written at (EvLooped _ judgment mode self site) protocol = do       form <- render self       remarks <- context protocol judgment site@@ -409,8 +423,15 @@           naming :: String           naming = printf "%s·%d" stem index       (protocol', value) <- valued counted naming term-      pure (protocol', Just (indented at (printf "%s := %s  # 𝕄(%s·%d)" naming value stem (Map.findWithDefault 1 (opener protocol) protocol._built))))-    written _ EvMorphed{} protocol = pure (protocol, Nothing)+      pure (protocol'{_made = namedFormation term (alias (opener protocol) index) protocol'._made}, Just (indented at (printf "%s := %s  # 𝕄(%s·%d)" naming value stem (Map.findWithDefault 1 (opener protocol) protocol._built))))+    written at (EvMorphed _ term) protocol = do+      let (index, counted) = numbered protocol+          naming :: String+          naming = printf "%s·%d" (labelled protocol answer) index+      (protocol', value) <- case (namedLookup term protocol._made, namedLookup term protocol._named) of+        (Nothing, Nothing) -> valued counted naming term+        _ -> (,) counted <$> borrowed protocol term+      pure (protocol'{_made = namedFormation term (alias (opener protocol) index) protocol'._made}, Just (indented at (printf "morphed %s := %s" naming value)))     written at (EvJoined _ condition symbol (left, right)) protocol = do       on <- either (pure . labelled protocol) salted condition       pure (protocol, Just (indented at (printf "joined %s := %s ? %s : %s" (printFunction (FnSymbol symbol)) on (printFunction (FnSymbol left)) (printFunction (FnSymbol right)))))@@ -430,8 +451,8 @@           naming = printf "%s·%d" (labelled protocol answer) index       (protocol', value) <- valued counted naming term       remarks <- mapM sourced sides-      pure (protocol', Just (indented at (remarked (printf "%s := %s" naming value) remarks)))-    written _ EvWorld{} protocol = pure (protocol, Nothing)+      pure (protocol'{_made = namedFormation term (alias (opener protocol) index) protocol'._made}, Just (indented at (remarked (printf "%s := %s" naming value) remarks)))+    written _ (EvWorld world) protocol = pure (protocol{_made = namedInsert world ExRoot protocol._made}, Nothing)     spelt :: Either Int Bytes -> IO String     spelt (Left symbol) = printf "𝔻(%s)" <$> render (standing symbol)     spelt (Right bytes) = render (ExBytes bytes)@@ -465,6 +486,13 @@     labelled protocol spelling = printf "%s·%d" (T.unpack spelling) (opener protocol)     opener :: Protocol -> Int     opener protocol = maybe 0 snd (listToMaybe protocol._open)+    stored :: Either Int Bytes -> Protocol -> (String, Protocol)+    stored value protocol = (naming, protocol{_deltas = Map.insert (opener protocol) index protocol._deltas, _found = Just (value, naming)})+      where+        index :: Int+        index = maybe 1 (+ 1) (Map.lookup (opener protocol) protocol._deltas)+        naming :: String+        naming = printf "%s·%d" (labelled protocol sigil) index     numbered :: Protocol -> (Int, Protocol)     numbered protocol = (index, protocol{_counted = Map.insert (opener protocol) index protocol._counted})       where@@ -573,6 +601,12 @@     elements at (EvManufactured _ symbol) nesting = placed at nesting' (pure (printf "<manufactured meta=\"%s\">%s</manufactured>" (escapeXML naming) (sigma symbol)))       where         (naming, nesting') = stored (Left symbol) nesting+    elements at (EvBoxed depth term) nesting =+      placed at nesting $ do+        taken <- coined depth term >>= lift . spelled+        (naming, aliased) <- counted+        modify (first (\nesting' -> nesting'{_objects = namedInsert term aliased nesting'._objects}))+        pure (printf "<boxed meta=\"%s\">%s</boxed>" (escapeXML naming) (escapeXMLText taken))     elements at (EvData _ spelling _ value) nesting =       placed at nesting $ case (found value nesting._held, value) of         (Just naming, _) -> pure (bound (escapeXMLText naming))@@ -665,8 +699,10 @@       pure (printf "%s·%d" (labelled nesting answer) index, alias (opener nesting) index)     recorded :: Expression -> (String -> String) -> Naming Expression     recorded term element = do-      (naming, aliased) <- counted-      modify (bimap (\nesting -> nesting{_objects = namedInsert term aliased nesting._objects}) (element (escapeXML naming) :))+      (nesting, records) <- get+      let index = maybe 1 (+ 1) (Map.lookup (opener nesting) nesting._parts)+          aliased = ExMeta (T.pack (printf "e·%d·%d" (opener nesting) index))+      put (nesting{_parts = Map.insert (opener nesting) index nesting._parts, _objects = namedInsert term aliased nesting._objects}, element (escapeXML (printf "%s·%d" (labelled nesting fragment) index)) : records)       pure aliased     opaque :: Expression -> Naming Expression     opaque term = do@@ -677,11 +713,12 @@     located :: Int -> Expression -> Naming String     located depth site = do       (nesting, _) <- get-      maybe (leaf depth site) pure (listToMaybe [naming | (_, built, naming) <- nesting._sources, built == site])+      maybe (titled depth site >>= lift . traced) pure (listToMaybe [naming | (_, built, naming) <- nesting._sources, built == site])     concluded :: String -> Int -> Expression -> Naming String     concluded tag depth term = do       body <- leaf depth term-      (naming, _) <- counted+      (naming, aliased) <- counted+      modify (first (\nesting -> nesting{_objects = namedFormation term aliased nesting._objects}))       pure (printf "<%s meta=\"%s\">%s</%s>" tag (escapeXML naming) (escapeXMLText body) tag)     bind :: Int -> Nesting -> T.Text -> Expression -> Naming String     bind depth nesting spelling term = printf "<bound meta=\"%s\">%s</bound>" (escapeXML (labelled nesting spelling)) . escapeXMLText <$> leaf depth term@@ -793,7 +830,7 @@     traced :: Expression -> IO String     traced ExXi = pure "ξ"     traced (ExDispatch inner attr) = printf "%s.%s" <$> traced inner <*> pure (printAttribute attr)-    traced term = printed term+    traced term = spelled term     sigmatic :: Expression -> Bool     sigmatic (ExFormation [BiLambda (FnSymbol _)]) = True     sigmatic (ExDispatch inner _) = sigmatic inner@@ -904,6 +941,9 @@  sigil :: T.Text sigil = "𝛿"++fragment :: T.Text+fragment = "𝑒"  found :: Either Int Bytes -> Maybe (Either Int Bytes, String) -> Maybe String found value (Just (held, naming))
src/Emit.hs view
@@ -204,7 +204,7 @@       universe = if "universe" `elem` tokens body then "universe" else "_"   Right     ( unlines-        [ printf "-- The rule '%s'." rule.name+        [ printf "-- The rule '%s'." (unwords (lines rule.name))         , printf "step%s :: Ru.Step" name         , printf "step%s = R.direct %s %s rewrite%s" name (show rule.name) (show (redex rule)) name         , ""@@ -255,7 +255,7 @@       arg var = if var `elem` tokens body then var else "_"   Right     ( unlines-        [ printf "-- The contextualization rule '%s'." rule.name+        [ printf "-- The contextualization rule '%s'." (unwords (lines rule.name))         , printf "contextualize%s :: Expression -> Expression -> [(String, Either C.ContextualizeException Expression)]" name         , printf "contextualize%s %s %s =" name (arg "term") (arg "context")         , body@@ -301,7 +301,7 @@       arg var = if var `elem` tokens body then var else "_"   Right     ( unlines-        [ printf "-- The %s rule '%s'." kind rule.name+        [ printf "-- The %s rule '%s'." kind (unwords (lines rule.name))         , printf "%s :: Expression -> Expression -> [In.Premises %s]" name answer         , printf "%s %s %s =" name (arg "term") (arg "universe")         , body
src/Evaluate.hs view
@@ -97,6 +97,7 @@     keyed side@(ExMeta _) = pure side     keyed side = operand side     line :: ReduceContext -> Ending -> (Subst, State) -> Line -> IO (Subst, State)+    line ctx _ held (Dataized meta source) = lowered ctx held meta source     line ctx _ held (Morphed meta source) = reduced ctx held meta source     line ctx ending held (Evaluated meta source) = evaluated ctx ending held meta source     line ctx _ (bound, state') (Deferred meta source) = (,state') <$> deferred ctx state' meta source bound@@ -139,26 +140,33 @@       throwIO (Stuck name)     down :: ReduceContext -> (Subst, State, [Either Int Bytes]) -> (Meta, Expression) -> IO (Subst, State, [Either Int Bytes])     down ctx (bound, state', conditions) (meta, term) = do-      placed <- operand term-      (value, state'') <- unparked (ctx._reduce univ ctx placed state'{_manufactured = Nothing, _stuck = Nothing})+      (datum, bound', state'') <- brought ctx bound state' meta term =<< operand term+      pure (bound', state'', datum : conditions)+    lowered :: ReduceContext -> (Subst, State) -> Meta -> Meta -> IO (Subst, State)+    lowered ctx (bound, state') meta source = do+      (_, bound', state'') <- brought ctx bound state' meta (ExMeta source._name) =<< buildExpressionThrows (ExMeta source._name) bound+      pure (bound', state'')+    brought :: ReduceContext -> Subst -> State -> Meta -> Expression -> Expression -> IO (Either Int Bytes, Subst, State)+    brought ctx bound state' meta origin term = do+      (value, state'') <- unparked (ctx._reduce univ ctx{_site = origin} term state'{_manufactured = Nothing, _stuck = Nothing})       case value of         Nothing -> throwIO (Stuck (fromMaybe func state''._stuck))         Just bytes -> do           let datum = maybe (Right bytes) Left state''._manufactured-          ctx._saveEval (EvData ctx._nesting meta._spelling term datum)+          ctx._saveEval (EvData ctx._nesting meta._spelling origin datum)           bound' <- bind meta (MvBytes bytes) bound-          pure (bound', state'', datum : conditions)+          pure (datum, bound', state'')     through :: ReduceContext -> (Subst, State, [Expression]) -> (Meta, Expression) -> IO (Subst, State, [Expression])     through ctx (bound, state', normals) (meta, term) = do       placed <- operand term-      (normal, state'') <- morphing univ ctx placed state'+      (normal, state'') <- morphing univ ctx{_site = term} placed state'       ctx._saveEval (EvTerm ctx._nesting meta._spelling Morphing term normal)       bound' <- bind meta (MvExpression normal) bound       pure (bound', state'', normal : normals)     reduced :: ReduceContext -> (Subst, State) -> Meta -> Meta -> IO (Subst, State)     reduced ctx (bound, state') meta source = do       term <- buildExpressionThrows (ExMeta source._name) bound-      (normal, state'') <- morphing univ ctx term state'+      (normal, state'') <- morphing univ ctx{_site = ExMeta source._name} term state'       ctx._saveEval (EvTerm ctx._nesting meta._spelling Morphing (ExMeta source._name) normal)       bound' <- bind meta (MvExpression normal) bound       pure (bound', state'')@@ -353,7 +361,7 @@         walked term = Just term     reached :: ReduceContext -> Subst -> Expression -> State -> IO (Expression, State)     reached _ bound (ExMeta name) state' = (,state') <$> buildExpressionThrows (ExMeta name) bound-    reached ctx _ side state' = (\placed -> morphing univ ctx placed state') =<< operand side+    reached ctx _ side state' = (\placed -> morphing univ ctx{_site = side} placed state') =<< operand side     mint :: Subst -> (Slot, Function) -> IO Subst     mint bound (slot, fresh) = case combine (substSlot slot (MvFunction fresh)) bound of       Just bound' -> pure bound'
src/Filter.hs view
@@ -47,10 +47,12 @@       Just attrs -> maybe (throwIO (CanNotFindObjectByLocator fqn)) pure (includedFormation expr attrs)       _ -> throwIO (InvalidLocatorProvided fqn)     mergeForms :: [Expression] -> Expression-    mergeForms forms =-      let bds = concat [bs | ExFormation bs <- forms]-          bds' = filter (\bd -> attributeFromBinding bd /= Just AtRho) bds-       in ExFormation bds'+    mergeForms forms = ExFormation (foldl merged [] (filter ((/= Just AtRho) . attributeFromBinding) (concat [bs | ExFormation bs <- forms])))+    merged :: [Binding] -> Binding -> [Binding]+    merged acc bd = case break ((== attributeFromBinding bd) . attributeFromBinding) acc of+      (before, BiTau at (ExFormation old) : after) | BiTau _ (ExFormation new) <- bd -> before ++ BiTau at (mergeForms [ExFormation old, ExFormation new]) : after+      (before, _ : after) -> before ++ bd : after+      _ -> acc ++ [bd]     includedFormation :: Expression -> [Attribute] -> Maybe Expression     includedFormation (ExFormation bindings) [at] =       let bs = [bd | bd <- bindings, attributeFromBinding bd == Just at]@@ -61,6 +63,7 @@         includedBindings ((BiTau at' form@(ExFormation _)) : bs) as@(at'' : rs)           | at' == at'' = includedFormation form rs >>= Just . BiTau at'           | otherwise = includedBindings bs as+        includedBindings (_ : bs) as = includedBindings bs as         includedBindings _ _ = Nothing     includedFormation _ _ = Nothing 
src/Functions.hs view
@@ -21,7 +21,6 @@ import Deps import Logger (logDebug) import Matcher-import Misc import Parser (ParserException (CouldNotParseAttribute), parseAttributeThrows, parseNumberThrows) import Printer (printAttribute, printExpression, printExtraArg) import Random (randomString)@@ -67,12 +66,24 @@ argToString arg subst = argToBytes arg subst <&> btsToUnescapedStr  argToNumber :: Y.ExtraArgument -> Subst -> IO Double-argToNumber arg subst = do-  bts <- argToBytes arg subst-  case btsSize bts of-    8 -> pure (either toDouble id (btsToNum bts))-    _ -> throwIO (userError (printf "Expected 8 bytes for a number, got %d" (btsSize bts)))+argToNumber (Y.ArgBytes bytes) subst = buildBytesThrows bytes subst >>= number+argToNumber arg@(Y.ArgExpression expr) subst = do+  expr' <- buildExpressionThrows expr subst+  case expr' of+    DataNumber _ -> dataized+    DataObject "bytes" _ -> dataized+    _ -> throwIO (userError "Only numbers and bytes are supported by 'sum' function")+  where+    dataized = do+      TeBytes bts <- _dataize [arg] subst+      number bts+argToNumber arg _ = throwIO (userError (printf "Can't extract a number from given argument: %s" (printExtraArg arg))) +number :: Bytes -> IO Double+number bts = case btsSize bts of+  8 -> pure (btsToNum bts)+  _ -> throwIO (userError (printf "Expected 8 bytes for a number, got %d" (btsSize bts)))+ _contextualize :: BuildTermMethod _contextualize = contextualizing contextualize @@ -147,11 +158,10 @@           let (pat, rest, separator) = nextUntilSlash body B.empty False            in if separator                 then-                  let (rep, flag, _) = nextUntilSlash rest B.empty True-                   in case flag of-                        "g" -> pure (pat, rep, True)-                        "" -> pure (pat, rep, False)-                        _ -> throwIO (userError "sed pattern must be in format s/pat/rep/[g]")+                  let (rep, flag, sealed) = nextUntilSlash rest B.empty True+                   in if sealed && flag `elem` ["", "g"]+                        then pure (pat, rep, flag == "g")+                        else throwIO (userError "sed pattern must be in format s/pat/rep/[g]")                 else throwIO (userError "sed pattern must be in format s/pat/rep/[g]")         _ -> throwIO (userError "sed pattern must start with s/")     nextUntilSlash :: B.ByteString -> B.ByteString -> Bool -> (B.ByteString, B.ByteString, Bool)@@ -196,7 +206,7 @@     DataNumber bts       | btsSize bts /= 8 ->           throwIO (userError (printf "Expected 8 bytes for a number, got %d" (btsSize bts)))-      | otherwise -> pure (DataString (strToBts (either show show (btsToNum bts))))+      | otherwise -> pure (DataString (strToBts (spelled (btsToNum bts))))     DataString bts -> pure (DataString bts)     ex ->       throwIO@@ -207,6 +217,13 @@             )         )   pure (TeExpression str)+  where+    spelled :: Double -> String+    spelled num+      | isNaN num || isInfinite num || isNegativeZero num = show num+      | otherwise = case properFraction num :: (Integer, Double) of+          (whole, 0.0) | whole >= toInteger (minBound :: Int) && whole <= toInteger (maxBound :: Int) -> show whole+          _ -> show num _string [Y.ArgAttribute attr] subst = do   attr' <- buildAttributeThrows attr subst   pure (TeExpression (DataString (strToBts (printAttribute attr'))))
src/Lambdas.hs view
@@ -61,7 +61,8 @@   }  data Line-  = Morphed Meta Meta+  = Dataized Meta Meta+  | Morphed Meta Meta   | Evaluated Meta Meta   | Deferred Meta Meta   | Stayed Meta Meta@@ -94,15 +95,15 @@   parseJSON = withObject "Lambda" $ \entry -> do     key <- entry .:? "λ" >>= maybe (fail "The entry has no 'λ' key") pure     unknown (T.unpack key) entry-    dataized <- operands key bytesMeta entry "dataize"+    (dataized, weighing) <- partitionEithers <$> reductions key bytesMeta Dataized entry "dataize"     ending <- concluded (T.unpack key) dataized entry-    (morphed, reading) <- partitionEithers <$> reductions key entry+    (morphed, reading) <- partitionEithers <$> reductions key expressionMeta Morphed entry "morph"     lambda <-       Lambda key dataized morphed-        <$> (written (T.unpack key) entry >>= ordered (T.unpack key) (map (_name . fst) morphed) . (reading ++))+        <$> (written (T.unpack key) entry >>= ordered (T.unpack key) (map (_name . fst) morphed) . ((weighing ++ reading) ++))         <*> pure ending     once (T.unpack key) lambda-    mapM_ (metaless (T.unpack key) "dataize" "a path from '$'") lambda._dataized+    mapM_ (metaless (T.unpack key) "dataize" "a path from '$' or a meta alone") lambda._dataized     mapM_ (metaless (T.unpack key) "morph" "a path from '$' or a meta alone") lambda._morphed     answered (T.unpack key) lambda     pure lambda@@ -137,7 +138,7 @@           condition spelling = case (parseBytes (T.unpack spelling), parseExpression (T.unpack spelling)) of             (Right (BtMeta name), _) | name `elem` map (_name . fst) dataized -> pure (Left (Meta spelling name))             (_, Right path) | pathed path -> pure (Right path)-            _ -> fail (printf "The 'on' of the 'join' block of λ function '%s' is '%s', while it must name a bytes meta of 'dataize', such as '𝛿1', or be a path from '$', such as '$.on'" key (T.unpack spelling))+            _ -> fail (printf "The 'on' of the 'join' block of λ function '%s' is '%s', while it must name a bytes meta of 'dataize' reading a path from '$', such as '𝛿1', or be a path from '$', such as '$.on'" key (T.unpack spelling))           pathed :: Expression -> Bool           pathed ExXi = True           pathed (ExDispatch inner _) = pathed inner@@ -151,27 +152,18 @@           fallback spelling = case parseBinding (printf "λ ⤍ %s" (T.unpack spelling)) of             Right (BiLambda func@(Function _)) -> pure func             _ -> fail (printf "The 'otherwise' of the 'join' block of λ function '%s' is '%s', while it must name a λ function, such as 'L_join'" key (T.unpack spelling))-      operands :: Text -> (Text -> Yaml.Parser Meta) -> Object -> Key -> Yaml.Parser [(Meta, Expression)]-      operands key kind entry name = do-        mapping <- entry .:? name .!= (Map.empty :: Map Text Expression)-        mapM bound (numbered mapping)-        where-          bound :: (Text, Expression) -> Yaml.Parser (Meta, Expression)-          bound (meta, term) = do-            referenceless (T.unpack key) (T.unpack meta) term-            kind meta >>= \named -> pure (named, term)-      reductions :: Text -> Object -> Yaml.Parser [Either (Meta, Expression) Line]-      reductions key entry = do-        mapping <- entry .:? "morph" .!= (Map.empty :: Map Text Text)+      reductions :: Text -> (Text -> Yaml.Parser Meta) -> (Meta -> Meta -> Line) -> Object -> Key -> Yaml.Parser [Either (Meta, Expression) Line]+      reductions key spelled kind entry block = do+        mapping <- entry .:? block .!= (Map.empty :: Map Text Text)         mapM reduction (numbered mapping)         where           reduction :: (Text, Text) -> Yaml.Parser (Either (Meta, Expression) Line)           reduction (meta, source) = do-            named <- expressionMeta meta+            named <- spelled meta             case parseExpression (T.unpack source) of-              Right (ExMeta name) -> pure (Right (Morphed named (Meta source name)))+              Right (ExMeta name) -> pure (Right (kind named (Meta source name)))               Right term -> Left (named, term) <$ referenceless (T.unpack key) (T.unpack meta) term-              Left failure -> fail failure <?> Key (Key.fromText meta) <?> Key "morph"+              Left failure -> fail failure <?> Key (Key.fromText meta) <?> Key block       expressionMeta :: Text -> Yaml.Parser Meta       expressionMeta meta = case parseExpression (T.unpack meta) of         Right (ExMeta name) -> pure (Meta meta name)@@ -236,6 +228,7 @@               feeding :: Meta -> [Meta]               feeding meta = [source line | line <- pending, meta._name == (binding line)._name, (source line)._name `notElem` known]           source :: Line -> Meta+          source (Dataized _ meta) = meta           source (Morphed _ meta) = meta           source (Evaluated _ meta) = meta           source (Deferred _ meta) = meta@@ -243,6 +236,7 @@           source (Rewritten _ meta _) = meta           source (Symbolized _ meta) = meta       binding :: Line -> Meta+      binding (Dataized meta _) = meta       binding (Morphed meta _) = meta       binding (Evaluated meta _) = meta       binding (Deferred meta _) = meta@@ -343,7 +337,7 @@   pure (Lambdas registered)   where     broken :: Yaml.ParseException -> IO [Lambda]-    broken failure = throwIO (BrokenLambdas path (Yaml.prettyPrintParseException failure))+    broken failure = throwIO (BrokenLambdas path (Y.shiftYamlError (Yaml.prettyPrintParseException failure)))     unique :: [Lambda] -> Lambda -> IO ()     unique entries entry       | length (filter ((== entry._key) . (._key)) entries) == 1 = pure ()@@ -447,16 +441,16 @@   (masked, (spent', known)) -> (masked, reverse known, spent')   where     goExpr :: Expression -> State Minting Expression-    goExpr (ExFormation bds) = ExFormation <$> mapM goBinding bds+    goExpr (ExFormation bds) = ExFormation <$> mapM (goBinding (Just AtLambda `elem` map attributeFromBinding bds)) bds     goExpr (ExApplication expr arg) = ExApplication <$> goExpr expr <*> goArgument arg     goExpr (ExDispatch expr attr) = (`ExDispatch` attr) <$> goExpr expr     goExpr (ExPhiMeet prefix idx expr) = ExPhiMeet prefix idx <$> goExpr expr     goExpr (ExPhiAgain prefix idx expr) = ExPhiAgain prefix idx <$> goExpr expr     goExpr expr = pure expr-    goBinding :: Binding -> State Minting Binding-    goBinding (BiDelta bts) = state (\(count, known) -> (BiLambda (FnSymbol (succ count)), (succ count, (succ count, bts) : known)))-    goBinding (BiTau attr expr) = BiTau attr <$> goExpr expr-    goBinding bd = pure bd+    goBinding :: Bool -> Binding -> State Minting Binding+    goBinding _ (BiDelta bts) = state (\(count, known) -> (BiLambda (FnSymbol (succ count)), (succ count, (succ count, bts) : known)))+    goBinding atom (BiTau attr expr) | atom || attr == AtPhi = BiTau attr <$> goExpr expr+    goBinding _ bd = pure bd     goArgument :: Argument -> State Minting Argument     goArgument (ArTau attr expr) = ArTau attr <$> goExpr expr     goArgument (ArAlpha alpha expr) = ArAlpha alpha <$> goExpr expr
src/Margin.hs view
@@ -34,7 +34,7 @@      in if lengthOf single + extra <= margin then single else ex'   withMargin' _ num@EX_NUMBER{} = num   withMargin' _ str@EX_STRING{} = str-  withMargin' cfg EX_DISPATCH{..} = EX_DISPATCH (withMargin' cfg expr) space attr+  withMargin' (extra, margin) EX_DISPATCH{..} = EX_DISPATCH (withMargin' (extra, margin - lengthOf attr - 1) expr) space attr   withMargin' cfg EX_PHI_AGAIN{..} = EX_PHI_AGAIN prefix idx (withMargin' cfg expr)   withMargin' _ EX_PHI_MEET{..} = EX_PHI_MEET prefix idx (toSingleLine expr)   withMargin' cfg@(extra, margin) ex@EX_SINGLE{pair = PA_TAU{..}, ..} =@@ -50,7 +50,7 @@         singleArg = toSingleLine arg'      in if           | lengthOf single + extra <= margin -> single-          | lengthOf singleMain + extra <= margin -> EX_APPLICATION singleMain space EOL tab arg' EOL tab' indent+          | lengthOf singleMain + extra + 1 <= margin -> EX_APPLICATION singleMain space EOL tab arg' EOL tab' indent           | lengthOf singleArg + extra' <= margin -> EX_APPLICATION main space NO_EOL TAB' singleArg NO_EOL TAB' indent           | otherwise -> EX_APPLICATION main space EOL tab arg' EOL tab' indent   withMargin' _ ex = ex@@ -73,12 +73,20 @@   withMargin' _ args = args  instance WithMargin BINDING where-  withMargin' cfg BI_PAIR{..} = BI_PAIR (withMargin' cfg pair) (withMargin' cfg bindings) tab+  withMargin' cfg BI_PAIR{..} = BI_PAIR (withMargin' (commas cfg) pair) (withMargin' cfg bindings) tab+    where+      commas (extra, margin) = case bindings of+        BDS_EMPTY{} -> (extra, margin)+        _ -> (extra + 1, margin)   withMargin' cfg BI_META{..} = BI_META meta (withMargin' cfg bindings) tab   withMargin' _ bd = bd  instance WithMargin BINDINGS where-  withMargin' cfg BDS_PAIR{..} = BDS_PAIR EOL tab (withMargin' cfg pair) (withMargin' cfg bindings)+  withMargin' cfg BDS_PAIR{..} = BDS_PAIR EOL tab (withMargin' (commas cfg) pair) (withMargin' cfg bindings)+    where+      commas (extra, margin) = case bindings of+        BDS_EMPTY{} -> (extra, margin)+        _ -> (extra + 1, margin)   withMargin' cfg BDS_META{..} = BDS_META EOL tab meta (withMargin' cfg bindings)   withMargin' _ bds = bds 
src/Morph.hs view
@@ -128,7 +128,7 @@   | Diverged T.Text   | Looping Expression   | LoopingAt Expression (NonEmpty Rewritten) State-  | Undataizable Expression State+  | Undataizable Expression (NonEmpty Rewritten) State   | Unmorphable Expression   deriving anyclass (Exception) @@ -147,8 +147,8 @@   show (Diverged func) = printf "The λ function '%s' could not join its two sides" (T.unpack func)   show (Looping term) = printf "Reduction entered a formation it is already inside: %s" (printExpression term)   show (LoopingAt term _ _) = show (Looping term)-  show (Undataizable ExTermination _) = "dataization reached the terminator ⊥, which signals an error and cannot be dataized"-  show (Undataizable term _) = printf "no dataization rule matched, while dataization expects a normal form: %s" (printExpression term)+  show (Undataizable ExTermination _ _) = "dataization reached the terminator ⊥, which signals an error and cannot be dataized"+  show (Undataizable term _ _) = printf "no dataization rule matched, while dataization expects a normal form: %s" (printExpression term)   show (Unmorphable term) = printf "Morphing expects a normal form, but no morphing rule matches: %s" (printExpression term)  deeper :: ReduceContext -> IO ReduceContext@@ -427,7 +427,7 @@ insideUniverse expr univ ctx@ReduceContext{_buildTerm = buildTerm} = case univ of   ExFormation bds -> do     (TeAttribute attr) <- buildTerm "random-tau" [] substEmpty-    let aiming = ctx{_locator = ExDispatch ExRoot attr, _site = ExDispatch ExRoot attr}+    let aiming = ctx{_locator = ExDispatch ExRoot attr}         synthetic = ExFormation (BiTau attr expr : bds)     (normal, _) <- normalized expr ((synthetic, Nothing) :| []) aiming     pure (ExFormation (BiTau attr normal : bds), aiming{_universe = extended attr normal})
src/Must.hs view
@@ -3,6 +3,7 @@  module Must (Must (..), inRange, exceedsUpperBound, validateMust) where +import Data.Char (isDigit) import Data.List (isInfixOf) import Text.Printf (printf) import Text.Read (readMaybe)@@ -27,11 +28,19 @@     | ".." `isInfixOf` s = parseRange s     | otherwise = parseExact s     where+      readInt :: String -> Maybe Int+      readInt str+        | null str || not (all isDigit str) = Nothing+        | otherwise = do+            num <- readMaybe str :: Maybe Integer+            if num > toInteger (maxBound :: Int) || num < toInteger (minBound :: Int)+              then Nothing+              else Just (fromInteger num)       parseRange :: String -> [(Must, String)]       parseRange str = case break (== '.') str of         (loStr, '.' : '.' : hiStr) ->-          let loPart = if null loStr then Nothing else readMaybe loStr-              hiPart = if null hiStr then Nothing else readMaybe hiStr+          let loPart = if null loStr then Nothing else readInt loStr+              hiPart = if null hiStr then Nothing else readInt hiStr            in case (loPart, hiPart, null loStr, null hiStr) of                 (Nothing, Nothing, False, False) -> []                 (Nothing, Nothing, True, True) -> [(MtRange Nothing Nothing, "")]@@ -44,7 +53,7 @@                 _ -> []         _ -> []       parseExact :: String -> [(Must, String)]-      parseExact str = case readMaybe str of+      parseExact str = case readInt str of         Just n | n >= 0 -> [(if n == 0 then MtDisabled else MtExact n, "")]         Just _ -> []         Nothing -> []
src/Regexp.hs view
@@ -20,7 +20,7 @@   pure (Regex first continuation)   where     compileWith option = do-      compiled <- R.compile R.compUTF8 option pat+      compiled <- R.compile R.compUTF8 option (B.pack "(*UCP)" `B.append` pat)       case compiled of         Left (_, err) -> throwIO (userError ("Regex compilation failed: " ++ err))         Right regex -> pure regex@@ -67,9 +67,9 @@                     Just idx -> fromMaybe (B.pack ('$' : digits)) (safeIndex idx groups)                in val : go afterDigits     go (c : rest) = B.singleton c : go rest-    safeIndex :: Int -> [B.ByteString] -> Maybe B.ByteString+    safeIndex :: Integer -> [B.ByteString] -> Maybe B.ByteString     safeIndex i xs-      | i >= 0 && i < length xs = Just (xs !! i)+      | i >= 0 && i < toInteger (length xs) = Just (xs !! fromInteger i)       | otherwise = Nothing  replaceFirst :: Regex -> B.ByteString -> B.ByteString -> IO B.ByteString
src/Render.hs view
@@ -211,7 +211,9 @@   render EX_DISPATCH{..} = render expr <> render space <> "." <> render space <> render attr   render EX_APPLICATION{..} = render expr <> render space <> "(" <> render eol <> render tab <> render argument <> render eol' <> render tab' <> ")"   render EX_STRING{..} = "\"" <> render str <> "\""-  render EX_NUMBER{..} = either (T.pack . show) (T.pack . show) num+  render EX_NUMBER{..} = case properFraction num :: (Int, Double) of+    (whole, 0.0) | abs num < 1.0e7 && not (isNegativeZero num) -> T.pack (show whole) <> ".0"+    _ -> T.pack (show num)   render EX_NONFINITE{..} = render global <> "." <> nonFiniteName nonfinite   render EX_META{..} = render meta   render EX_PHI_MEET{..} = "\\phinoMeet{" <> maybe "" (\p -> T.pack p <> ":") prefix <> render idx <> "}{ " <> render expr <> " }"
src/Rewriter.hs view
@@ -13,7 +13,7 @@  import AST import Builder-import Control.Exception (Exception, throwIO)+import Control.Exception (Exception, IOException, catch, displayException, throwIO) import Data.List.NonEmpty (NonEmpty (..)) import qualified Data.List.NonEmpty as NE import qualified Data.Map.Strict as Map@@ -130,6 +130,7 @@ fast (ExFormation _pbds@(pbd : pbds)) (ExFormation _rbds@(rbd : rbds)) =   startsAndEndsWithMeta _pbds     && startsAndEndsWithMeta _rbds+    && not (null (init pbds))     && pbd == rbd     && last pbds == last rbds     && not (hasMetaBindings (init pbds))@@ -237,10 +238,27 @@  applicable :: Expression -> [Step] -> RewriteContext -> IO Bool applicable _ [] _ = pure False-applicable expression (rule : rest) ctx@RewriteContext{..} =-  _applied rule (RuleContext _buildTerm _universe _normal) expression >>= \case-    Just (changed, _) | changed /= expression -> pure True-    _ -> applicable expression rest ctx+applicable expression (rule : rest) ctx@RewriteContext{..} = do+  applies <- (probe rule `catch` onIOException) `catch` onBuildException+  case applies of+    True -> pure True+    False -> applicable expression rest ctx+  where+    probe :: Step -> IO Bool+    probe rule =+      _applied rule (RuleContext _buildTerm _universe _normal) expression >>= \case+        Just (changed, _) | changed /= expression -> pure True+        _ -> pure False+    onIOException :: IOException -> IO Bool+    onIOException = raised+    onBuildException :: BuildException -> IO Bool+    onBuildException = raised+    raised :: Exception e => e -> IO Bool+    raised err+      | _depthSensitive = throwIO err+      | otherwise = do+          logDebug (printf "Rule '%s' raised and was treated as not applicable: %s" (_name rule) (displayException err))+          applicable expression rest ctx  rewrite :: Expression -> [Step] -> RewriteContext -> IO Rewrittens rewrite expr rules ctx@RewriteContext{..} = do@@ -254,13 +272,10 @@       | count == _maxCycles && not (inRange _must count) = throwIO (MustBeGoing _must count)       | count == _maxCycles = do           logDebug (printf "Max amount of rewriting cycles for all rules (%d) has been reached, rewriting is stopped" _maxCycles)-          if _depthSensitive-            then do-              exhausted <- applicable expression rules ctx-              if exhausted-                then throwIO (StoppedOnLimit "max-cycles" _maxCycles)-                else pure (rewrittens, False)-            else pure (rewrittens, True)+          applies <- applicable expression rules ctx+          if _depthSensitive && applies+            then throwIO (StoppedOnLimit "max-cycles" _maxCycles)+            else pure (rewrittens, applies)       | otherwise = do           logDebug (printf "Starting rewriting cycle for all rules: %d out of %d" count _maxCycles)           rewrite' state (zip [0 ..] rules) count ctx >>= \case
src/Rule.hs view
@@ -17,7 +17,7 @@   , buildExpressionThrows   ) import Bytes (btsToUnescapedStr)-import Control.Exception (Exception (displayException))+import Control.Exception (Exception (displayException), throwIO) import Control.Exception.Base (SomeException, try) import Control.Monad (when) import Data.Foldable (foldlM)@@ -95,12 +95,18 @@ normalHeld test expr = test expr  _or :: [Y.Condition] -> Subst -> RuleContext -> IO [Subst]-_or [] _ _ = pure []-_or (cond : rest) subst ctx = do-  met <- meetCondition' cond subst ctx-  if null met-    then _or rest subst ctx-    else pure met+_or conds subst ctx = loop Nothing conds+  where+    loop :: Maybe SomeException -> [Y.Condition] -> IO [Subst]+    loop raised [] = maybe (pure []) throwIO raised+    loop raised (cond : rest) = do+      met <- try (meetCondition' cond subst ctx) :: IO (Either SomeException [Subst])+      case met of+        Right [] -> loop raised rest+        Right mets -> pure mets+        Left err -> do+          logDebug (printf "Condition %s raised and was treated as not met: %s" (show cond) (displayException err))+          loop (Just err) rest  _and :: [Y.Condition] -> Subst -> RuleContext -> IO [Subst] _and [] subst _ = pure [subst]
src/Sugar.hs view
@@ -12,7 +12,6 @@ import AST import Bytes (nonFiniteBts, numToBts, strToBts, unescapeStr) import CST-import Misc (toDouble)  withSugarType :: (ToSalty a) => SugarType -> a -> a withSugarType SWEET node = node@@ -39,6 +38,9 @@     goExpr EX_SINGLE{..}       | isRho pair = goExpr formation       | otherwise = EX_SINGLE (goPair pair) space (goExpr formation)+    goExpr EX_NUMBER{..} = EX_NUMBER num tab (filter (not . isRhoArgument) rhos)+    goExpr EX_STRING{..} = EX_STRING str tab (filter (not . isRhoArgument) rhos)+    goExpr EX_NONFINITE{..} = EX_NONFINITE global nonfinite tab (filter (not . isRhoArgument) rhos)     goExpr expr = expr     goBinding :: BINDING -> BINDING     goBinding empty@BI_EMPTY{} = empty@@ -115,6 +117,9 @@     isRho PA_TAU{attr = AT_RHO _} = True     isRho PA_FORMATION{attr = AT_RHO _} = True     isRho _ = False+    isRhoArgument :: Argument -> Bool+    isRhoArgument (ArTau AtRho _) = True+    isRhoArgument _ = False  revealXi :: SugarType -> EXPRESSION -> EXPRESSION revealXi _ = goExpr@@ -189,7 +194,7 @@     saltifyPrimitive       (toCST (BaseObject "number") (indent + 1, EOL))       (toCST (BaseObject "bytes") (indent + 2, EOL))-      (toCST (ExFormation [BiDelta (numToBts (either toDouble id num))]) (indent + 2, EOL))+      (toCST (ExFormation [BiDelta (numToBts num)]) (indent + 2, EOL))       tab       rhos   toSalty EX_NONFINITE{nonfinite, tab = tab@TAB{..}, rhos} =
src/XMIR.hs view
@@ -127,7 +127,7 @@         , if _omitComments || btsSize bytes /= 8             then [bts]             else-              [ NodeComment (T.pack (either show show (btsToNum bytes)))+              [ NodeComment (T.pack (show (btsToNum bytes)))               , bts               ]         )
src/Yaml.hs view
@@ -32,7 +32,15 @@ import Parser import Slots import Text.Printf (printf)+import Text.Regex.PCRE ((=~)) +shiftYamlError :: String -> String+shiftYamlError msg =+  case (msg =~ ("^YAML parse exception at line ([0-9]+), column ([0-9]+)([:,])" :: String)) :: (String, String, String, [String]) of+    (before, _, after, [ln, col, sep]) ->+      printf "%sYAML parse exception at line %d, column %d%s%s" before (read ln + 1 :: Int) (read col + 1 :: Int) sep after+    _ -> msg+ validateYamlObject :: (MonadFail a) => Object -> [String] -> a () validateYamlObject v keys   | length current > 1 = fail ("Exactly one condition type is expected, when multiple condition types specified: " ++ show current)@@ -216,6 +224,7 @@     referenceless rule.name "where" rule.where_     referenceless rule.name "having" rule.having     targets rule (metas rule.pattern) (fromMaybe [] rule.where_)+    unread rule (named (metas rule.pattern)) "when" (metas rule.when)     steps rule (named (metas rule.pattern)) (fromMaybe [] rule.where_)     pure rule     where@@ -236,7 +245,6 @@       steps :: Rule -> [Text] -> [Extra] -> Parser ()       steps rule known [] = do         unread rule known "result" (metas rule.result)-        unread rule known "when" (metas rule.when)         unread rule known "having" (metas rule.having)       steps rule known (extra : rest) = do         unread rule known "where" (metas extra.args)
test-resources/cst/printing-packs/jeo.yaml view
@@ -28,9 +28,9 @@   ⟧ result: |-   Φ.x(-    Φ.jeo.opcode.lconst_0( 9 ),-    Φ.jeo.opcode.lstore( 55, 1 ),-    Φ.jeo.opcode.iconst_0( 3 ),-    Φ.jeo.opcode.istore( 54, 3 ),+    Φ.jeo.opcode.lconst_0( 9.0 ),+    Φ.jeo.opcode.lstore( 55.0, 1.0 ),+    Φ.jeo.opcode.iconst_0( 3.0 ),+    Φ.jeo.opcode.istore( 54.0, 3.0 ),     Φ.jeo.label( Φ.bytes( 4C-31-32-38-34-34-34-35-32-36-37:Δ ) )   ):x
test-resources/cst/printing-packs/primitives-without-rhos.yaml view
@@ -8,4 +8,4 @@     y -> Q.string(~0 -> Q.bytes([[ D> 68-65-79 ]]), ^ -> Q.x.y.z)   ]] result: |--  ⟦ x ↦ 1, y ↦ "hey" ⟧+  ⟦ x ↦ 1.0, y ↦ "hey" ⟧
test-resources/cst/printing-packs/simple.yaml view
@@ -10,4 +10,4 @@     ]]   ]] result: |--  ⟦ φ ↦ ρ.z, bar ↦ Φ.y( 5, "foo" ) ⟧:foo+  ⟦ φ ↦ ρ.z, bar ↦ Φ.y( 5.0, "foo" ) ⟧:foo
test-resources/cst/printing-packs/sugar-after-inline-voids.yaml view
@@ -5,4 +5,4 @@ expression: |   [[ x(y) -> [[ a -> 42 ]] ]] result: |--  ⟦ x(y) ↦ 42:a ⟧+  ⟦ x(y) ↦ 42.0:a ⟧
test-resources/cst/printing-packs/voids-after-delta.yaml view
@@ -5,4 +5,4 @@ expression: |   [[ x -> [[ D> 01-, a -> ?, ^ -> ?, b -> 2 ]] ]] result: |--  ⟦ x(a, ρ) ↦ ⟦ Δ ⤍ 01-, b ↦ 2 ⟧ ⟧+  ⟦ x(a, ρ) ↦ ⟦ Δ ⤍ 01-, b ↦ 2.0 ⟧ ⟧
test-resources/cst/printing-packs/with-void-rho-between.yaml view
@@ -5,4 +5,4 @@ expression: |   [[ x -> [[ ^ -> ?, x -> ?, y -> 5 ]], y -> [[ a -> ?, b -> 3, m -> ?, ^ -> ? ]] ]] result: |--  ⟦ x(ρ, x) ↦ 5:y, y(a) ↦ ⟦ b ↦ 3, m ↦ ∅, ρ ↦ ∅ ⟧ ⟧+  ⟦ x(ρ, x) ↦ 5.0:y, y(a) ↦ ⟦ b ↦ 3.0, m ↦ ∅, ρ ↦ ∅ ⟧ ⟧
test-resources/cst/to-singleline-packs/application.yaml view
@@ -14,4 +14,4 @@     )   ]] result: |--  Φ.y( z ↦ "hey", b ↦ 1 ).z( a ↦ Φ.a:ρ:q ):x+  Φ.y( z ↦ "hey", b ↦ 1.0 ).z( a ↦ Φ.a:ρ:q ):x
test-resources/cst/to-singleline-packs/with-inlined-voids.yaml view
@@ -7,4 +7,4 @@     x -> [[ y -> ?, a -> 1, ^ -> ?, q -> ? ]]   ]] result: |--  ⟦ x(y) ↦ ⟦ a ↦ 1, ρ ↦ ∅, q ↦ ∅ ⟧ ⟧+  ⟦ x(y) ↦ ⟦ a ↦ 1.0, ρ ↦ ∅, q ↦ ∅ ⟧ ⟧
test-resources/evaluate-packs/answers-a-symbol-nobody-has-worked-out.yaml view
@@ -20,22 +20,34 @@ result: '𝜎1:λ' protocol: |   𝕄(Φ.φ):-    𝑛·0·1 := 5-    𝑛·0·2 := 𝑛·0·1.plus( x ↦ 6 )+    𝑛·0·1 := 5.0+    𝑛·0·2 := 𝑛·0·1.plus( x ↦ 6.0 )     𝔼(L_number_plus):-      𝔻(Φ.a🌵0):-        𝕄(Φ.a🌵0):-          𝑛·1·1 := 5-          𝑛·1·2 := Φ.bytes( φ ↦ 40-14-00-00-00-00-00-00:Δ )+      𝔻(ξ.ρ):+        𝕄(ξ.ρ):+          𝑛·1·1 := 5.0+          morphed 𝑛·1·2 := 𝑛·1·1+        boxed 𝑛·1·3 := 𝑛·1·2.φ+        𝕄(𝑛·1·3):+          𝑛·1·4 := Φ.bytes( φ ↦ 40-14-00-00-00-00-00-00:Δ )+          morphed 𝑛·1·5 := 𝑛·1·4+        boxed 𝑛·1·6 := 𝑛·1·5.φ         𝛿·1·1 := 40-14-00-00-00-00-00-00  # 5.0       𝛿1·1 := 𝛿·1·1  # 𝔻(ξ.ρ)-      𝔻(Φ.a🌵1):-        𝕄(Φ.a🌵1):-          𝑛·1·3 := 6-          𝑛·1·4 := Φ.bytes( φ ↦ 40-18-00-00-00-00-00-00:Δ )+      𝔻(ξ.x):+        𝕄(ξ.x):+          𝑛·1·7 := 6.0+          morphed 𝑛·1·8 := 𝑛·1·7+        boxed 𝑛·1·9 := 𝑛·1·8.φ+        𝕄(𝑛·1·9):+          𝑛·1·10 := Φ.bytes( φ ↦ 40-18-00-00-00-00-00-00:Δ )+          morphed 𝑛·1·11 := 𝑛·1·10+        boxed 𝑛·1·12 := 𝑛·1·11.φ         𝛿·1·2 := 40-18-00-00-00-00-00-00  # 6.0       𝛿2·1 := 𝛿·1·2  # 𝔻(ξ.x)-      𝑛·1·5 := Φ.number( φ ↦ 𝜎1:λ )  # 𝑛-      𝕄(𝑛·1·5):-        𝑛·1·6 := Φ.number( φ ↦ 𝜎1:λ )-      𝑛·1·7 := 𝑛·1·6  # 𝕄(𝑛·1·5)+      𝑛·1·13 := Φ.number( φ ↦ 𝜎1:λ )  # 𝑛+      𝕄(𝑛·1·13):+        𝑛·1·14 := Φ.number( φ ↦ 𝜎1:λ )+        morphed 𝑛·1·15 := 𝑛·1·14+      𝑛·1·16 := 𝑛·1·15  # 𝕄(𝑛·1·13)+    morphed 𝑛·0·3 := 𝜎1:λ
test-resources/evaluate-packs/answers-a-whole-family-of-names-with-one-entry.yaml view
@@ -20,38 +20,59 @@ result: '𝜎2:λ' protocol: |   𝕄(Φ.φ):-    𝑛·0·1 := 5-    𝑛·0·2 := 𝑛·0·1.plus( x ↦ 6 )+    𝑛·0·1 := 5.0+    𝑛·0·2 := 𝑛·0·1.plus( x ↦ 6.0 )     𝔼(L_number_plus):-      𝔻(Φ.a🌵0):-        𝕄(Φ.a🌵0):-          𝑛·1·1 := 5-          𝑛·1·2 := Φ.bytes( φ ↦ 40-14-00-00-00-00-00-00:Δ )+      𝔻(ξ.ρ):+        𝕄(ξ.ρ):+          𝑛·1·1 := 5.0+          morphed 𝑛·1·2 := 𝑛·1·1+        boxed 𝑛·1·3 := 𝑛·1·2.φ+        𝕄(𝑛·1·3):+          𝑛·1·4 := Φ.bytes( φ ↦ 40-14-00-00-00-00-00-00:Δ )+          morphed 𝑛·1·5 := 𝑛·1·4+        boxed 𝑛·1·6 := 𝑛·1·5.φ         𝛿·1·1 := 40-14-00-00-00-00-00-00  # 5.0       𝛿1·1 := 𝛿·1·1  # 𝔻(ξ.ρ)-      𝔻(Φ.a🌵1):-        𝕄(Φ.a🌵1):-          𝑛·1·3 := 6-          𝑛·1·4 := Φ.bytes( φ ↦ 40-18-00-00-00-00-00-00:Δ )+      𝔻(ξ.x):+        𝕄(ξ.x):+          𝑛·1·7 := 6.0+          morphed 𝑛·1·8 := 𝑛·1·7+        boxed 𝑛·1·9 := 𝑛·1·8.φ+        𝕄(𝑛·1·9):+          𝑛·1·10 := Φ.bytes( φ ↦ 40-18-00-00-00-00-00-00:Δ )+          morphed 𝑛·1·11 := 𝑛·1·10+        boxed 𝑛·1·12 := 𝑛·1·11.φ         𝛿·1·2 := 40-18-00-00-00-00-00-00  # 6.0       𝛿2·1 := 𝛿·1·2  # 𝔻(ξ.x)-      𝑛·1·5 := Φ.number( φ ↦ 𝜎1:λ )  # 𝑛-      𝕄(𝑛·1·5):-        𝑛·1·6 := Φ.number( φ ↦ 𝜎1:λ )-      𝑛·1·7 := 𝑛·1·6  # 𝕄(𝑛·1·5)-    𝑛·0·3 := 𝑛·1·6.times( x ↦ 7 )+      𝑛·1·13 := Φ.number( φ ↦ 𝜎1:λ )  # 𝑛+      𝕄(𝑛·1·13):+        𝑛·1·14 := Φ.number( φ ↦ 𝜎1:λ )+        morphed 𝑛·1·15 := 𝑛·1·14+      𝑛·1·16 := 𝑛·1·15  # 𝕄(𝑛·1·13)+    𝑛·0·3 := 𝑛·1·14.times( x ↦ 7.0 )     𝔼(L_number_times):-      𝔻(Φ.a🌵2):-        𝕄(Φ.a🌵2):+      𝔻(ξ.ρ):+        𝕄(ξ.ρ):           𝑛·2·1 := Φ.number( φ ↦ 𝜎1:λ )-      𝛿1·2 := 𝔻(𝜎1:λ)  # 𝔻(ξ.ρ)-      𝔻(Φ.a🌵3):-        𝕄(Φ.a🌵3):-          𝑛·2·2 := 7-          𝑛·2·3 := Φ.bytes( φ ↦ 40-1C-00-00-00-00-00-00:Δ )-        𝛿·2·1 := 40-1C-00-00-00-00-00-00  # 7.0-      𝛿2·2 := 𝛿·2·1  # 𝔻(ξ.x)-      𝑛·2·4 := Φ.number( φ ↦ 𝜎2:λ )  # 𝑛-      𝕄(𝑛·2·4):-        𝑛·2·5 := Φ.number( φ ↦ 𝜎2:λ )-      𝑛·2·6 := 𝑛·2·5  # 𝕄(𝑛·2·4)+          morphed 𝑛·2·2 := 𝑛·1·16+        boxed 𝑛·2·3 := 𝑛·2·2.φ+        𝛿·2·1 := 𝔻(𝜎1:λ)+      𝛿1·2 := 𝛿·2·1  # 𝔻(ξ.ρ)+      𝔻(ξ.x):+        𝕄(ξ.x):+          𝑛·2·4 := 7.0+          morphed 𝑛·2·5 := 𝑛·2·4+        boxed 𝑛·2·6 := 𝑛·2·5.φ+        𝕄(𝑛·2·6):+          𝑛·2·7 := Φ.bytes( φ ↦ 40-1C-00-00-00-00-00-00:Δ )+          morphed 𝑛·2·8 := 𝑛·2·7+        boxed 𝑛·2·9 := 𝑛·2·8.φ+        𝛿·2·2 := 40-1C-00-00-00-00-00-00  # 7.0+      𝛿2·2 := 𝛿·2·2  # 𝔻(ξ.x)+      𝑛·2·10 := Φ.number( φ ↦ 𝜎2:λ )  # 𝑛+      𝕄(𝑛·2·10):+        𝑛·2·11 := Φ.number( φ ↦ 𝜎2:λ )+        morphed 𝑛·2·12 := 𝑛·2·11+      𝑛·2·13 := 𝑛·2·12  # 𝕄(𝑛·2·10)+    morphed 𝑛·0·4 := 𝜎2:λ
test-resources/evaluate-packs/answers-its-own-sides-on-a-condition-another-join-read.yaml view
@@ -39,66 +39,95 @@ result: '⟦ φ ↦ ⟦ Δ ⤍ 40-51-80-00-00-00-00-00 ⟧ ⟧' protocol: |   𝕄(Φ.φ):-    𝑛·0·1 := Φ.pick( x ↦ 5.gt( 0 ).if( 7, 7 ) )-    𝑛·0·2 := 𝑛·0·1( y ↦ 5.gt( 0 ).if( 70, 70 ) )+    𝑛·0·1 := Φ.pick( x ↦ 5.0.gt( 0.0 ).if( 7.0, 7.0 ) )+    𝑛·0·2 := 𝑛·0·1( y ↦ 5.0.gt( 0.0 ).if( 70.0, 70.0 ) )     𝔼(L_pick):-      𝔻(Φ.a🌵0):-        𝕄(Φ.a🌵0):-          𝑛·1·1 := 5-          𝑛·1·2 := 𝑛·1·1.gt( x ↦ 0 )+      𝔻(ξ.x):+        𝕄(ξ.x):+          𝑛·1·1 := 5.0+          𝑛·1·2 := 𝑛·1·1.gt( x ↦ 0.0 )           𝔼(L_number_gt):-            𝔻(Φ.a🌵1):-              𝕄(Φ.a🌵1):-                𝑛·2·1 := 5-                𝑛·2·2 := Φ.bytes( φ ↦ 40-14-00-00-00-00-00-00:Δ )+            𝔻(ξ.ρ):+              𝕄(ξ.ρ):+                𝑛·2·1 := 5.0+                morphed 𝑛·2·2 := 𝑛·2·1+              boxed 𝑛·2·3 := 𝑛·2·2.φ+              𝕄(𝑛·2·3):+                𝑛·2·4 := Φ.bytes( φ ↦ 40-14-00-00-00-00-00-00:Δ )+                morphed 𝑛·2·5 := 𝑛·2·4+              boxed 𝑛·2·6 := 𝑛·2·5.φ               𝛿·2·1 := 40-14-00-00-00-00-00-00  # 5.0             𝛿1·2 := 𝛿·2·1  # 𝔻(ξ.ρ)-            𝔻(Φ.a🌵2):-              𝕄(Φ.a🌵2):-                𝑛·2·3 := 0-                𝑛·2·4 := Φ.bytes( φ ↦ 00-00-00-00-00-00-00-00:Δ )+            𝔻(ξ.x):+              𝕄(ξ.x):+                𝑛·2·7 := 0.0+                morphed 𝑛·2·8 := 𝑛·2·7+              boxed 𝑛·2·9 := 𝑛·2·8.φ+              𝕄(𝑛·2·9):+                𝑛·2·10 := Φ.bytes( φ ↦ 00-00-00-00-00-00-00-00:Δ )+                morphed 𝑛·2·11 := 𝑛·2·10+              boxed 𝑛·2·12 := 𝑛·2·11.φ               𝛿·2·2 := 00-00-00-00-00-00-00-00  # 0.0             𝛿2·2 := 𝛿·2·2  # 𝔻(ξ.x)-            𝑛·2·5 := Φ.bool( φ ↦ 𝜎1:λ )  # 𝑛-            𝕄(𝑛·2·5):-              𝑛·2·6 := Φ.bool( φ ↦ 𝜎1:λ )-            𝑛·2·7 := 𝑛·2·6  # 𝕄(𝑛·2·5)-          𝑛·1·3 := 𝑛·2·6.if( then ↦ 7 )-          𝑛·1·4 := 𝑛·1·3( else ↦ 7 )+            𝑛·2·13 := Φ.bool( φ ↦ 𝜎1:λ )  # 𝑛+            𝕄(𝑛·2·13):+              𝑛·2·14 := Φ.bool( φ ↦ 𝜎1:λ )+              morphed 𝑛·2·15 := 𝑛·2·14+            𝑛·2·16 := 𝑛·2·15  # 𝕄(𝑛·2·13)+          𝑛·1·3 := 𝑛·2·14.if( then ↦ 7.0 )+          𝑛·1·4 := 𝑛·1·3( else ↦ 7.0 )+          morphed 𝑛·1·5 := 𝑛·1·4         𝔼(L_fork):-          𝔻(Φ.a🌵3):-            𝕄(Φ.a🌵3):+          𝔻(ξ.ρ):+            𝕄(ξ.ρ):               𝑛·3·1 := Φ.bool( φ ↦ 𝜎1:λ )-          𝛿1·3 := 𝔻(𝜎1:λ)  # 𝔻(ξ.ρ)-          𝕄(Φ.a🌵4):-            𝑛·3·2 := 7-          𝕄(Φ.a🌵5):-            𝑛·3·3 := 7-          𝑛·3·4 := 𝑛·3·3  # 𝕄(ξ.then), 𝕄(ξ.else)-        𝕄(Φ.a🌵0):-          𝑛·1·5 := Φ.bytes( φ ↦ 40-1C-00-00-00-00-00-00:Δ )+              morphed 𝑛·3·2 := 𝑛·2·16+            boxed 𝑛·3·3 := 𝑛·3·2.φ+            𝛿·3·1 := 𝔻(𝜎1:λ)+          𝛿1·3 := 𝛿·3·1  # 𝔻(ξ.ρ)+          𝕄(ξ.then):+            𝑛·3·4 := 7.0+            morphed 𝑛·3·5 := 𝑛·3·4+          𝕄(ξ.else):+            𝑛·3·6 := 7.0+            morphed 𝑛·3·7 := 𝑛·3·6+          𝑛·3·8 := 𝑛·3·7  # 𝕄(ξ.then), 𝕄(ξ.else)+        boxed 𝑛·1·6 := 𝑛·3·8.φ+        𝕄(𝑛·1·6):+          𝑛·1·7 := Φ.bytes( φ ↦ 40-1C-00-00-00-00-00-00:Δ )+          morphed 𝑛·1·8 := 𝑛·1·7+        boxed 𝑛·1·9 := 𝑛·1·8.φ         𝛿·1·1 := 40-1C-00-00-00-00-00-00  # 7.0       𝛿1·1 := 𝛿·1·1  # 𝔻(ξ.x)-      𝕄(Φ.a🌵6):-        𝑛·1·6 := Φ.number( φ ↦ 𝑛·2·2 )-        𝑛·1·7 := 𝑛·1·6.gt( x ↦ 𝑛·2·3 )+      𝕄(ξ.y):+        𝑛·1·10 := Φ.number( φ ↦ 𝑛·2·4 )+        𝑛·1·11 := 𝑛·1·10.gt( x ↦ 𝑛·2·7 )         𝔼(L_number_gt):           𝑛·4·1 := 𝑛·3·1  # 𝑛-          𝑛·4·2 := 𝑛·2·7  # 𝕄(𝑛·4·1)-        𝑛·1·8 := 𝑛·3·1.if( then ↦ 70 )-        𝑛·1·9 := 𝑛·1·8( else ↦ 70 )-      𝑛1·1 := 𝑛·1·9  # 𝕄(ξ.y)+          𝑛·4·2 := 𝑛·2·16  # 𝕄(𝑛·4·1)+        𝑛·1·12 := 𝑛·3·1.if( then ↦ 70.0 )+        𝑛·1·13 := 𝑛·1·12( else ↦ 70.0 )+        morphed 𝑛·1·14 := 𝑛·1·13+      𝑛1·1 := 𝑛·1·13  # 𝕄(ξ.y)       𝔼(L_fork):  # 𝔼(𝑛1)-        𝔻(Φ.a🌵7):-          𝕄(Φ.a🌵7):+        𝔻(ξ.ρ):+          𝕄(ξ.ρ):             𝑛·5·1 := Φ.bool( φ ↦ 𝜎1:λ )-        𝛿1·5 := 𝔻(𝜎1:λ)  # 𝔻(ξ.ρ)-        𝕄(Φ.a🌵8):-          𝑛·5·2 := 70-        𝕄(Φ.a🌵9):-          𝑛·5·3 := 70-        𝑛·5·4 := 𝑛·5·3  # 𝕄(ξ.then), 𝕄(ξ.else)-      𝑛2·1 := 𝑛·5·3  # 𝔼(𝑛1)-      𝑛·1·10 := 𝑛·5·3  # 𝑛-      𝑛·1·11 := 𝑛·5·3  # 𝕄(𝑛·1·10)+            morphed 𝑛·5·2 := 𝑛·4·2+          boxed 𝑛·5·3 := 𝑛·5·2.φ+          𝛿·5·1 := 𝔻(𝜎1:λ)+        𝛿1·5 := 𝛿·5·1  # 𝔻(ξ.ρ)+        𝕄(ξ.then):+          𝑛·5·4 := 70.0+          morphed 𝑛·5·5 := 𝑛·5·4+        𝕄(ξ.else):+          𝑛·5·6 := 70.0+          morphed 𝑛·5·7 := 𝑛·5·6+        𝑛·5·8 := 𝑛·5·7  # 𝕄(ξ.then), 𝕄(ξ.else)+      𝑛2·1 := 𝑛·5·8  # 𝔼(𝑛1)+      𝑛·1·15 := 𝑛·5·8  # 𝑛+      𝕄(𝑛·1·15):+        morphed 𝑛·1·16 := 𝑛·5·8+      𝑛·1·17 := 𝑛·1·16  # 𝕄(𝑛·1·15)     𝑛·0·3 := Φ.bytes( φ ↦ 40-51-80-00-00-00-00-00:Δ )+    morphed 𝑛·0·4 := 𝑛·0·3
test-resources/evaluate-packs/answers-the-same-under-acyclic.yaml view
@@ -21,22 +21,34 @@ result: '𝜎1:λ' protocol: |   𝕄(Φ.φ):-    𝑛·0·1 := 5-    𝑛·0·2 := 𝑛·0·1.plus( x ↦ 6 )+    𝑛·0·1 := 5.0+    𝑛·0·2 := 𝑛·0·1.plus( x ↦ 6.0 )     𝔼(L_number_plus):-      𝔻(Φ.a🌵0):-        𝕄(Φ.a🌵0):-          𝑛·1·1 := 5-          𝑛·1·2 := Φ.bytes( φ ↦ 40-14-00-00-00-00-00-00:Δ )+      𝔻(ξ.ρ):+        𝕄(ξ.ρ):+          𝑛·1·1 := 5.0+          morphed 𝑛·1·2 := 𝑛·1·1+        boxed 𝑛·1·3 := 𝑛·1·2.φ+        𝕄(𝑛·1·3):+          𝑛·1·4 := Φ.bytes( φ ↦ 40-14-00-00-00-00-00-00:Δ )+          morphed 𝑛·1·5 := 𝑛·1·4+        boxed 𝑛·1·6 := 𝑛·1·5.φ         𝛿·1·1 := 40-14-00-00-00-00-00-00  # 5.0       𝛿1·1 := 𝛿·1·1  # 𝔻(ξ.ρ)-      𝔻(Φ.a🌵1):-        𝕄(Φ.a🌵1):-          𝑛·1·3 := 6-          𝑛·1·4 := Φ.bytes( φ ↦ 40-18-00-00-00-00-00-00:Δ )+      𝔻(ξ.x):+        𝕄(ξ.x):+          𝑛·1·7 := 6.0+          morphed 𝑛·1·8 := 𝑛·1·7+        boxed 𝑛·1·9 := 𝑛·1·8.φ+        𝕄(𝑛·1·9):+          𝑛·1·10 := Φ.bytes( φ ↦ 40-18-00-00-00-00-00-00:Δ )+          morphed 𝑛·1·11 := 𝑛·1·10+        boxed 𝑛·1·12 := 𝑛·1·11.φ         𝛿·1·2 := 40-18-00-00-00-00-00-00  # 6.0       𝛿2·1 := 𝛿·1·2  # 𝔻(ξ.x)-      𝑛·1·5 := Φ.number( φ ↦ 𝜎1:λ )  # 𝑛-      𝕄(𝑛·1·5):-        𝑛·1·6 := Φ.number( φ ↦ 𝜎1:λ )-      𝑛·1·7 := 𝑛·1·6  # 𝕄(𝑛·1·5)+      𝑛·1·13 := Φ.number( φ ↦ 𝜎1:λ )  # 𝑛+      𝕄(𝑛·1·13):+        𝑛·1·14 := Φ.number( φ ↦ 𝜎1:λ )+        morphed 𝑛·1·15 := 𝑛·1·14+      𝑛·1·16 := 𝑛·1·15  # 𝕄(𝑛·1·13)+    morphed 𝑛·0·3 := 𝜎1:λ
test-resources/evaluate-packs/binds-a-copy-over-no-bare-symbol-as-it-is.yaml view
@@ -19,9 +19,13 @@ protocol: |   𝕄(Φ.y):     𝔼(L_hold):-      𝕄(Φ.a🌵0):+      𝕄(ξ.b):         𝑛·1·1 := Φ.step( tup ↦ Φ.items.tail )-      𝑛1·1 := 𝑛·1·1  # 𝕄(ξ.b)-      𝑛2·1 := 𝑛·1·1  # 𝑛1-      𝑛·1·2 := 𝑛·1·1:φ  # 𝑛-      𝑛·1·3 := 𝑛·1·1:φ  # 𝕄(𝑛·1·2)+        morphed 𝑛·1·2 := 𝑛·1·1+      𝑛1·1 := 𝑛·1·2  # 𝕄(ξ.b)+      𝑛2·1 := 𝑛·1·2  # 𝑛1+      𝑛·1·3 := 𝑛·1·2:φ  # 𝑛+      𝕄(𝑛·1·3):+        morphed 𝑛·1·4 := 𝑛·1·2:φ+      𝑛·1·5 := 𝑛·1·4  # 𝕄(𝑛·1·3)+    morphed 𝑛·0·1 := 𝑛·1·2
test-resources/evaluate-packs/blames-the-lambda-the-operand-was-parked-on.yaml view
@@ -16,7 +16,7 @@ protocol: |   𝕄(Φ.x):     𝔼(L_entry):-      𝔻(Φ.a🌵0):-        𝕄(Φ.a🌵0):+      𝔻(ξ.v):+        𝕄(ξ.v):           unanswered L_none  # 𝕄(L_none:λ)       stuck L_none
test-resources/evaluate-packs/cuts-a-recursion-running-through-evaluate-lines.yaml view
@@ -24,5 +24,7 @@ protocol: |   𝕄(Φ.app):     𝔼(L_loop):+      𝕄(ξ.next):+        morphed 𝑛·1·1 := ⟦ next ↦ Φ.loop, λ ⤍ L_loop ⟧       𝑛1·1 := ⟦ next ↦ Φ.loop, λ ⤍ L_loop ⟧  # 𝕄(ξ.next)       looped ⟦ next ↦ Φ.loop, λ ⤍ L_loop ⟧  # 𝔼(𝑛1), proven
test-resources/evaluate-packs/cuts-a-recursion-that-differs-by-symbols-only.yaml view
@@ -25,9 +25,12 @@   𝕄(Φ.y):     𝑛·0·1 := Φ.down( x ↦ 𝜎1:λ )     𝔼(L_down):-      𝛿1·1 := 𝔻(𝜎1:λ)  # 𝔻(ξ.x)+      𝔻(ξ.x):+        𝛿·1·1 := 𝔻(𝜎1:λ)+      𝛿1·1 := 𝛿·1·1  # 𝔻(ξ.x)       𝑛·1·1 := Φ.down( x ↦ 𝜎2:λ )  # 𝑛       𝕄(𝑛·1·1):         𝑛·1·2 := Φ.down( x ↦ 𝜎2:λ )-      𝑛·1·3 := 𝑛·1·2  # 𝕄(𝑛·1·1)+        morphed 𝑛·1·3 := 𝑛·1·2+      𝑛·1·4 := 𝑛·1·2  # 𝕄(𝑛·1·1)     looped 𝑛·0·1  # proven
+ test-resources/evaluate-packs/dataizes-what-a-morph-operand-of-the-same-entry-brought-down.yaml view
@@ -0,0 +1,40 @@+# SPDX-FileCopyrightText: Copyright (c) 2025 Objectionary.com+# SPDX-License-Identifier: MIT+---+# yamllint disable rule:line-length+# The entry morphs the body of 'v' and dataizes what the morph came to, the+# way the 'L_entry' of the lowering of EO wants both the object a body morphs+# into and the data that object dataizes to (#2139). An operand of 'dataize'+# naming the meta 'morph' bound runs as a line once that meta is bound, so+# one entry does what took the 'L_entry' and the 'L_root' of EO two. The+# protocol writes the line of '𝛿2' right after the line of '𝑛1' it read,+# commented with the dataization of that meta, while '𝛿1', reading '$', still+# comes down before every operand of 'morph'.+symbolic: |+  - λ: L_entry+    morph:+      𝑛1: $.v.φ+    dataize:+      𝛿1: $.n+      𝛿2: 𝑛1+    𝑛: ⟦ φ ↦ ⟦ λ ⤍ 𝜎 ⟧ ⟧+location: 'Q.y'+input: '⟦ y ↦ ⟦ n ↦ ⟦ λ ⤍ 𝜎1 ⟧, v ↦ ⟦ φ ↦ Φ.t ⟧, λ ⤍ L_entry ⟧.φ, t ↦ ⟦ Δ ⤍ 2A- ⟧ ⟧'+result: '𝜎2:λ'+protocol: |+  𝕄(Φ.y):+    𝔼(L_entry):+      𝔻(ξ.n):+        𝛿·1·1 := 𝔻(𝜎1:λ)+      𝛿1·1 := 𝛿·1·1  # 𝔻(ξ.n)+      𝕄(ξ.v.φ):+        morphed 𝑛·1·1 := 2A-:Δ+      𝑛1·1 := 2A-:Δ  # 𝕄(ξ.v.φ)+      𝔻(𝑛1):+        𝛿·1·2 := 2A-+      𝛿2·1 := 𝛿·1·2  # 𝔻(𝑛1)+      𝑛·1·2 := 𝜎2:λ:φ  # 𝑛+      𝕄(𝑛·1·2):+        morphed 𝑛·1·3 := 𝜎2:λ:φ+      𝑛·1·4 := 𝑛·1·3  # 𝕄(𝑛·1·2)+    morphed 𝑛·0·1 := 𝜎2:λ
test-resources/evaluate-packs/defers-a-copy-a-join-passed-through-stay-and-symbolize.yaml view
@@ -41,24 +41,32 @@ protocol: |   𝕄(Φ.y):     𝔼(L_use):-      𝔻(Φ.a🌵0):+      𝔻(ξ.x):         𝔼(L_fork):-          𝛿1·2 := 𝔻(𝜎2:λ)  # 𝔻(ξ.c)+          𝔻(ξ.c):+            𝛿·2·1 := 𝔻(𝜎2:λ)+          𝛿1·2 := 𝛿·2·1  # 𝔻(ξ.c)+          𝕄(ξ.a):+            morphed 𝑛·2·1 := 01-:Δ           𝑛1·2 := 01-:Δ  # 𝕄(ξ.a)-          𝕄(Φ.a🌵3):-            𝑛·2·1 := Φ.step( tup ↦ Φ.items.tail )-          𝑛2·2 := 𝑛·2·1  # 𝕄(ξ.b)+          𝕄(ξ.b):+            𝑛·2·2 := Φ.step( tup ↦ Φ.items.tail )+            morphed 𝑛·2·3 := 𝑛·2·2+          𝑛2·2 := 𝑛·2·3  # 𝕄(ξ.b)           𝑛3·2 := 01-:Δ  # 𝑛1-          𝑛4·2 := 𝑛·2·1  # 𝑛2+          𝑛4·2 := 𝑛·2·3  # 𝑛2           symbolized 𝜎3 := 01-           𝑛5·2 := 𝜎3:λ  # symbolize(𝑛3)           𝑛7·2 := 𝑛5·2  # 𝑛5-          𝑛6·2 := 𝑛·2·1  # 𝑛4+          𝑛6·2 := 𝑛·2·3  # 𝑛4           deferred 𝜎4 := Φ.step( tup ↦ 𝜎1:λ )  # 𝕄(𝑛6.φ)           joined 𝜎5 := 𝛿1·2 ? 𝜎3 : 𝜎4-          𝑛·2·2 := 𝜎5:λ  # 𝑛7, 𝑛8-      𝛿1·1 := 𝔻(𝜎5:λ)  # 𝔻(ξ.x)+          𝑛·2·4 := 𝜎5:λ  # 𝑛7, 𝑛8+        𝛿·1·1 := 𝔻(𝜎5:λ)+      𝛿1·1 := 𝛿·1·1  # 𝔻(ξ.x)       𝑛·1·1 := Φ.num( φ ↦ 𝜎6:λ )  # 𝑛       𝕄(𝑛·1·1):         𝑛·1·2 := Φ.num( φ ↦ 𝜎6:λ )-      𝑛·1·3 := 𝑛·1·2  # 𝕄(𝑛·1·1)+        morphed 𝑛·1·3 := 𝑛·1·2+      𝑛·1·4 := 𝑛·1·3  # 𝕄(𝑛·1·1)+    morphed 𝑛·0·1 := 𝜎6:λ
test-resources/evaluate-packs/defers-a-copy-whose-phi-reads-nothing-of-its-own.yaml view
@@ -20,9 +20,13 @@ protocol: |   𝕄(Φ.y):     𝔼(L_hold):-      𝕄(Φ.a🌵0):+      𝕄(ξ.b):         𝑛·1·1 := Φ.step( tup ↦ Φ.items.tail )-      𝑛1·1 := 𝑛·1·1  # 𝕄(ξ.b)+        morphed 𝑛·1·2 := 𝑛·1·1+      𝑛1·1 := 𝑛·1·2  # 𝕄(ξ.b)       deferred 𝜎2 := Φ.step( tup ↦ 𝜎1:λ )  # 𝕄(𝑛1.φ)-      𝑛·1·2 := 𝜎2:λ:φ  # 𝑛-      𝑛·1·3 := 𝜎2:λ:φ  # 𝕄(𝑛·1·2)+      𝑛·1·3 := 𝜎2:λ:φ  # 𝑛+      𝕄(𝑛·1·3):+        morphed 𝑛·1·4 := 𝜎2:λ:φ+      𝑛·1·5 := 𝑛·1·4  # 𝕄(𝑛·1·3)+    morphed 𝑛·0·1 := 𝜎2:λ
test-resources/evaluate-packs/diverges-on-a-side-holding-a-datum-though-it-may-fabricate.yaml view
@@ -21,5 +21,11 @@ protocol: |   𝕄(Φ.y):     𝔼(L_fork):-      𝛿1·1 := 𝔻(𝜎1:λ)  # 𝔻(ξ.c)-      diverged 01-:Δ vs Φ.zu:φ  # they differ at .Δ, and the left side has Δ+      𝔻(ξ.c):+        𝛿·1·1 := 𝔻(𝜎1:λ)+      𝛿1·1 := 𝛿·1·1  # 𝔻(ξ.c)+      𝕄(ξ.a):+        morphed 𝑛·1·1 := 01-:Δ+      𝕄(ξ.b):+        morphed 𝑛·1·2 := Φ.zu:φ+      diverged 01-:Δ vs 𝑛·1·2  # they differ at .Δ, and the left side has Δ
test-resources/evaluate-packs/diverges-on-a-side-holding-a-symbol-though-it-may-fabricate.yaml view
@@ -23,5 +23,11 @@ protocol: |   𝕄(Φ.y):     𝔼(L_fork):-      𝛿1·1 := 𝔻(𝜎1:λ)  # 𝔻(ξ.c)-      diverged 𝜎2:λ vs ⟦ φ ↦ Φ.zu, ke ↦ Φ.ty ⟧  # they differ at .λ, and the left side has λ+      𝔻(ξ.c):+        𝛿·1·1 := 𝔻(𝜎1:λ)+      𝛿1·1 := 𝛿·1·1  # 𝔻(ξ.c)+      𝕄(ξ.a):+        morphed 𝑛·1·1 := 𝜎2:λ+      𝕄(ξ.b):+        morphed 𝑛·1·2 := ⟦ φ ↦ Φ.zu, ke ↦ Φ.ty ⟧+      diverged 𝜎2:λ vs 𝑛·1·2  # they differ at .λ, and the left side has λ
test-resources/evaluate-packs/diverges-on-two-sides-of-metas-that-differ.yaml view
@@ -23,7 +23,13 @@ protocol: |   𝕄(Φ.y):     𝔼(L_fork):-      𝛿1·1 := 𝔻(𝜎1:λ)  # 𝔻(ξ.c)-      𝑛1·1 := 01-:Δ:φ  # 𝕄(ξ.a)-      𝑛2·1 := 02-:Δ:φ  # 𝕄(ξ.b)+      𝔻(ξ.c):+        𝛿·1·1 := 𝔻(𝜎1:λ)+      𝛿1·1 := 𝛿·1·1  # 𝔻(ξ.c)+      𝕄(ξ.a):+        morphed 𝑛·1·1 := 01-:Δ:φ+      𝑛1·1 := 𝑛·1·1  # 𝕄(ξ.a)+      𝕄(ξ.b):+        morphed 𝑛·1·2 := 02-:Δ:φ+      𝑛2·1 := 𝑛·1·2  # 𝕄(ξ.b)       diverged 𝑛1·1 vs 𝑛2·1  # they differ at .φ.Δ, and the join names no otherwise
test-resources/evaluate-packs/diverges-on-two-sides-that-do-not-join.yaml view
@@ -39,36 +39,52 @@ fails: "No entry of --symbolic answers the λ function 'L_fork'" protocol: |   𝕄(Φ.φ):-    𝑛·0·1 := 5-    𝑛·0·2 := 𝑛·0·1.gt( x ↦ 0 )+    𝑛·0·1 := 5.0+    𝑛·0·2 := 𝑛·0·1.gt( x ↦ 0.0 )     𝔼(L_number_gt):-      𝔻(Φ.a🌵0):-        𝕄(Φ.a🌵0):-          𝑛·1·1 := 5-          𝑛·1·2 := Φ.bytes( φ ↦ 40-14-00-00-00-00-00-00:Δ )+      𝔻(ξ.ρ):+        𝕄(ξ.ρ):+          𝑛·1·1 := 5.0+          morphed 𝑛·1·2 := 𝑛·1·1+        boxed 𝑛·1·3 := 𝑛·1·2.φ+        𝕄(𝑛·1·3):+          𝑛·1·4 := Φ.bytes( φ ↦ 40-14-00-00-00-00-00-00:Δ )+          morphed 𝑛·1·5 := 𝑛·1·4+        boxed 𝑛·1·6 := 𝑛·1·5.φ         𝛿·1·1 := 40-14-00-00-00-00-00-00  # 5.0       𝛿1·1 := 𝛿·1·1  # 𝔻(ξ.ρ)-      𝔻(Φ.a🌵1):-        𝕄(Φ.a🌵1):-          𝑛·1·3 := 0-          𝑛·1·4 := Φ.bytes( φ ↦ 00-00-00-00-00-00-00-00:Δ )+      𝔻(ξ.x):+        𝕄(ξ.x):+          𝑛·1·7 := 0.0+          morphed 𝑛·1·8 := 𝑛·1·7+        boxed 𝑛·1·9 := 𝑛·1·8.φ+        𝕄(𝑛·1·9):+          𝑛·1·10 := Φ.bytes( φ ↦ 00-00-00-00-00-00-00-00:Δ )+          morphed 𝑛·1·11 := 𝑛·1·10+        boxed 𝑛·1·12 := 𝑛·1·11.φ         𝛿·1·2 := 00-00-00-00-00-00-00-00  # 0.0       𝛿2·1 := 𝛿·1·2  # 𝔻(ξ.x)-      𝑛·1·5 := Φ.bool( φ ↦ 𝜎1:λ )  # 𝑛-      𝕄(𝑛·1·5):-        𝑛·1·6 := Φ.bool( φ ↦ 𝜎1:λ )-      𝑛·1·7 := 𝑛·1·6  # 𝕄(𝑛·1·5)-    𝑛·0·3 := 𝑛·1·6.if( then ↦ 1.plus( 2 ) )-    𝑛·0·4 := 𝑛·0·3( else ↦ 3.plus( 4 ) )+      𝑛·1·13 := Φ.bool( φ ↦ 𝜎1:λ )  # 𝑛+      𝕄(𝑛·1·13):+        𝑛·1·14 := Φ.bool( φ ↦ 𝜎1:λ )+        morphed 𝑛·1·15 := 𝑛·1·14+      𝑛·1·16 := 𝑛·1·15  # 𝕄(𝑛·1·13)+    𝑛·0·3 := 𝑛·1·14.if( then ↦ 1.0.plus( 2.0 ) )+    𝑛·0·4 := 𝑛·0·3( else ↦ 3.0.plus( 4.0 ) )     𝔼(L_fork):-      𝔻(Φ.a🌵2):-        𝕄(Φ.a🌵2):+      𝔻(ξ.ρ):+        𝕄(ξ.ρ):           𝑛·2·1 := Φ.bool( φ ↦ 𝜎1:λ )-      𝛿1·2 := 𝔻(𝜎1:λ)  # 𝔻(ξ.ρ)-      𝕄(Φ.a🌵3):-        𝑛·2·2 := 1-        𝑛·2·3 := 𝑛·2·2.plus( x ↦ 2 )-      𝕄(Φ.a🌵4):-        𝑛·2·4 := 3-        𝑛·2·5 := 𝑛·2·4.plus( x ↦ 4 )-      diverged 𝑛·2·3 vs 𝑛·2·5  # they differ at .ρ, and the join names no otherwise+          morphed 𝑛·2·2 := 𝑛·1·16+        boxed 𝑛·2·3 := 𝑛·2·2.φ+        𝛿·2·1 := 𝔻(𝜎1:λ)+      𝛿1·2 := 𝛿·2·1  # 𝔻(ξ.ρ)+      𝕄(ξ.then):+        𝑛·2·4 := 1.0+        𝑛·2·5 := 𝑛·2·4.plus( x ↦ 2.0 )+        morphed 𝑛·2·6 := 𝑛·2·5+      𝕄(ξ.else):+        𝑛·2·7 := 3.0+        𝑛·2·8 := 𝑛·2·7.plus( x ↦ 4.0 )+        morphed 𝑛·2·9 := 𝑛·2·8+      diverged 𝑛·2·5 vs 𝑛·2·8  # they differ at .ρ, and the join names no otherwise
test-resources/evaluate-packs/fabricates-a-formation-holding-the-bytes-its-condition-came-down-to.yaml view
@@ -22,7 +22,12 @@ protocol: |   𝕄(Φ.y):     𝔼(L_fork):-      𝔻(Φ.a🌵0):+      𝔻(ξ.c):         𝛿·1·1 := 3A-       𝛿1·1 := 𝛿·1·1  # 𝔻(ξ.c)-      fabricated 𝑛·1·1 := Φ.wu:φ and ⟦ φ ↦ Φ.ol, pe ↦ Φ.ki ⟧  # ⟦ left🌵 ↦ Φ.wu:φ, right🌵 ↦ ⟦ φ ↦ Φ.ol, pe ↦ Φ.ki ⟧, φ ↦ ⟦ λ ⤍ L_join, on🌵 ↦ 3A-:Δ, left🌵 ↦ ρ.left🌵.φ, right🌵 ↦ ρ.right🌵.φ ⟧, pe ↦ ⟦ λ ⤍ L_join, on🌵 ↦ 3A-:Δ, left🌵 ↦ ρ.left🌵.pe, right🌵 ↦ ρ.right🌵.pe ⟧ ⟧+      𝕄(ξ.a):+        morphed 𝑛·1·1 := Φ.wu:φ+      𝕄(ξ.b):+        morphed 𝑛·1·2 := ⟦ φ ↦ Φ.ol, pe ↦ Φ.ki ⟧+      fabricated 𝑛·1·3 := 𝑛·1·1 and 𝑛·1·2  # ⟦ left🌵 ↦ 𝑛·1·1, right🌵 ↦ 𝑛·1·2, φ ↦ ⟦ λ ⤍ L_join, on🌵 ↦ 3A-:Δ, left🌵 ↦ ρ.left🌵.φ, right🌵 ↦ ρ.right🌵.φ ⟧, pe ↦ ⟦ λ ⤍ L_join, on🌵 ↦ 3A-:Δ, left🌵 ↦ ρ.left🌵.pe, right🌵 ↦ ρ.right🌵.pe ⟧ ⟧+    morphed 𝑛·0·1 := ⟦ λ ⤍ L_join, on🌵 ↦ 3A-:Δ, left🌵 ↦ ρ.left🌵.pe, right🌵 ↦ ρ.right🌵.pe ⟧
test-resources/evaluate-packs/fabricates-a-formation-holding-the-symbol-a-path-of-its-condition-reaches.yaml view
@@ -21,4 +21,9 @@ protocol: |   𝕄(Φ.y):     𝔼(L_join):-      fabricated 𝑛·1·1 := Φ.jo:φ and ⟦ φ ↦ Φ.ma, tu ↦ Φ.ip ⟧  # ⟦ left🌵 ↦ Φ.jo:φ, right🌵 ↦ ⟦ φ ↦ Φ.ma, tu ↦ Φ.ip ⟧, φ ↦ ⟦ λ ⤍ L_join, on🌵 ↦ 𝜎5:λ, left🌵 ↦ ρ.left🌵.φ, right🌵 ↦ ρ.right🌵.φ ⟧, tu ↦ ⟦ λ ⤍ L_join, on🌵 ↦ 𝜎5:λ, left🌵 ↦ ρ.left🌵.tu, right🌵 ↦ ρ.right🌵.tu ⟧ ⟧+      𝕄(ξ.left🌵):+        morphed 𝑛·1·1 := Φ.jo:φ+      𝕄(ξ.right🌵):+        morphed 𝑛·1·2 := ⟦ φ ↦ Φ.ma, tu ↦ Φ.ip ⟧+      fabricated 𝑛·1·3 := 𝑛·1·1 and 𝑛·1·2  # ⟦ left🌵 ↦ 𝑛·1·1, right🌵 ↦ 𝑛·1·2, φ ↦ ⟦ λ ⤍ L_join, on🌵 ↦ 𝜎5:λ, left🌵 ↦ ρ.left🌵.φ, right🌵 ↦ ρ.right🌵.φ ⟧, tu ↦ ⟦ λ ⤍ L_join, on🌵 ↦ 𝜎5:λ, left🌵 ↦ ρ.left🌵.tu, right🌵 ↦ ρ.right🌵.tu ⟧ ⟧+    morphed 𝑛·0·1 := ⟦ λ ⤍ L_join, on🌵 ↦ 𝜎5:λ, left🌵 ↦ ρ.left🌵.tu, right🌵 ↦ ρ.right🌵.tu ⟧
test-resources/evaluate-packs/fabricates-a-formation-of-two-sides-of-different-shapes.yaml view
@@ -28,5 +28,12 @@ protocol: |   𝕄(Φ.y):     𝔼(L_fork):-      𝛿1·1 := 𝔻(𝜎1:λ)  # 𝔻(ξ.c)-      fabricated 𝑛·1·1 := ⟦ φ ↦ Φ.hx, ke ↦ Φ.ty ⟧ and Φ.zu:φ  # ⟦ left🌵 ↦ ⟦ φ ↦ Φ.hx, ke ↦ Φ.ty ⟧, right🌵 ↦ Φ.zu:φ, φ ↦ ⟦ λ ⤍ L_join, on🌵 ↦ 𝜎1:λ, left🌵 ↦ ρ.left🌵.φ, right🌵 ↦ ρ.right🌵.φ ⟧, ke ↦ ⟦ λ ⤍ L_join, on🌵 ↦ 𝜎1:λ, left🌵 ↦ ρ.left🌵.ke, right🌵 ↦ ρ.right🌵.ke ⟧ ⟧+      𝔻(ξ.c):+        𝛿·1·1 := 𝔻(𝜎1:λ)+      𝛿1·1 := 𝛿·1·1  # 𝔻(ξ.c)+      𝕄(ξ.a):+        morphed 𝑛·1·1 := ⟦ φ ↦ Φ.hx, ke ↦ Φ.ty ⟧+      𝕄(ξ.b):+        morphed 𝑛·1·2 := Φ.zu:φ+      fabricated 𝑛·1·3 := 𝑛·1·1 and 𝑛·1·2  # ⟦ left🌵 ↦ 𝑛·1·1, right🌵 ↦ 𝑛·1·2, φ ↦ ⟦ λ ⤍ L_join, on🌵 ↦ 𝜎1:λ, left🌵 ↦ ρ.left🌵.φ, right🌵 ↦ ρ.right🌵.φ ⟧, ke ↦ ⟦ λ ⤍ L_join, on🌵 ↦ 𝜎1:λ, left🌵 ↦ ρ.left🌵.ke, right🌵 ↦ ρ.right🌵.ke ⟧ ⟧+    morphed 𝑛·0·1 := ⟦ λ ⤍ L_join, on🌵 ↦ 𝜎1:λ, left🌵 ↦ ρ.left🌵.ke, right🌵 ↦ ρ.right🌵.ke ⟧
test-resources/evaluate-packs/fails-on-a-side-whose-morph-got-stuck.yaml view
@@ -22,7 +22,9 @@ protocol: |   𝕄(Φ.y):     𝔼(L_fork):-      𝛿1·1 := 𝔻(𝜎1:λ)  # 𝔻(ξ.c)-      𝕄(Φ.a🌵1):+      𝔻(ξ.c):+        𝛿·1·1 := 𝔻(𝜎1:λ)+      𝛿1·1 := 𝛿·1·1  # 𝔻(ξ.c)+      𝕄(ξ.a):         unanswered L_none  # 𝕄(L_none:λ)       stuck L_none
test-resources/evaluate-packs/fails-on-an-operand-that-reaches-the-terminator.yaml view
@@ -36,16 +36,21 @@ fails: 'terminator ⊥' protocol: |   𝕄(Φ.l🌵.e1):-    𝑛·0·1 := ⟦ n ↦ ∅, v ↦ ∅, λ ⤍ L_entry ⟧( n ↦ 1 )+    𝑛·0·1 := ⟦ n ↦ ∅, v ↦ ∅, λ ⤍ L_entry ⟧( n ↦ 1.0 )     𝑛·0·2 := 𝑛·0·1( v ↦ Φ.block( buffer ↦ Φ.bytes( φ ↦ 𝜎1:λ ) ).read )     𝔼(L_entry):-      𝔻(Φ.a🌵0):-        𝕄(Φ.a🌵0):-          𝑛·1·1 := 1-          𝑛·1·2 := Φ.bytes( φ ↦ 3F-F0-00-00-00-00-00-00:Δ )+      𝔻(ξ.n):+        𝕄(ξ.n):+          𝑛·1·1 := 1.0+          morphed 𝑛·1·2 := 𝑛·1·1+        boxed 𝑛·1·3 := 𝑛·1·2.φ+        𝕄(𝑛·1·3):+          𝑛·1·4 := Φ.bytes( φ ↦ 3F-F0-00-00-00-00-00-00:Δ )+          morphed 𝑛·1·5 := 𝑛·1·4+        boxed 𝑛·1·6 := 𝑛·1·5.φ         𝛿·1·1 := 3F-F0-00-00-00-00-00-00  # 1.0       𝛿1·1 := 𝛿·1·1  # 𝔻(ξ.n)-      𝕄(Φ.a🌵1):-        𝑛·1·3 := Φ.block( buffer ↦ Φ.bytes( φ ↦ 𝜎1:λ ) )-        𝑛·1·4 := Φ.dataized( target ↦ ⊥ )+      𝕄(ξ.v.φ):+        𝑛·1·7 := Φ.block( buffer ↦ Φ.bytes( φ ↦ 𝜎1:λ ) )+        𝑛·1·8 := Φ.dataized( target ↦ ⊥ )         𝔼(L_dataized):
test-resources/evaluate-packs/fires-a-formation-once-however-often-it-is-met.yaml view
@@ -41,51 +41,76 @@ protocol: |   𝕄(Φ.p):     𝔼(L_pair):-      𝕄(Φ.a🌵0):-        𝑛·1·1 := 7-        𝑛·1·2 := 𝑛·1·1.plus( x ↦ 5.plus( 6 ) )+      𝕄(ξ.a):+        𝑛·1·1 := 7.0+        𝑛·1·2 := 𝑛·1·1.plus( x ↦ 5.0.plus( 6.0 ) )+        morphed 𝑛·1·3 := 𝑛·1·2       𝑛1·1 := 𝑛·1·2  # 𝕄(ξ.a)-      𝕄(Φ.a🌵1):-        𝑛·1·3 := 7-        𝑛·1·4 := 𝑛·1·3.plus( x ↦ 5.plus( 6 ) )-      𝑛2·1 := 𝑛·1·4  # 𝕄(ξ.b)+      𝕄(ξ.b):+        𝑛·1·4 := 7.0+        𝑛·1·5 := 𝑛·1·4.plus( x ↦ 5.0.plus( 6.0 ) )+        morphed 𝑛·1·6 := 𝑛·1·5+      𝑛2·1 := 𝑛·1·5  # 𝕄(ξ.b)       𝔼(L_number_plus):  # 𝔼(𝑛1)-        𝔻(Φ.a🌵2):-          𝕄(Φ.a🌵2):-            𝑛·2·1 := 7-            𝑛·2·2 := Φ.bytes( φ ↦ 40-1C-00-00-00-00-00-00:Δ )+        𝔻(ξ.ρ):+          𝕄(ξ.ρ):+            𝑛·2·1 := 7.0+            morphed 𝑛·2·2 := 𝑛·2·1+          boxed 𝑛·2·3 := 𝑛·2·2.φ+          𝕄(𝑛·2·3):+            𝑛·2·4 := Φ.bytes( φ ↦ 40-1C-00-00-00-00-00-00:Δ )+            morphed 𝑛·2·5 := 𝑛·2·4+          boxed 𝑛·2·6 := 𝑛·2·5.φ           𝛿·2·1 := 40-1C-00-00-00-00-00-00  # 7.0         𝛿1·2 := 𝛿·2·1  # 𝔻(ξ.ρ)-        𝔻(Φ.a🌵3):-          𝕄(Φ.a🌵3):-            𝑛·2·3 := 5-            𝑛·2·4 := 𝑛·2·3.plus( x ↦ 6 )+        𝔻(ξ.x):+          𝕄(ξ.x):+            𝑛·2·7 := 5.0+            𝑛·2·8 := 𝑛·2·7.plus( x ↦ 6.0 )+            morphed 𝑛·2·9 := 𝑛·2·8           𝔼(L_number_plus):-            𝔻(Φ.a🌵4):-              𝕄(Φ.a🌵4):-                𝑛·3·1 := 5-                𝑛·3·2 := Φ.bytes( φ ↦ 40-14-00-00-00-00-00-00:Δ )+            𝔻(ξ.ρ):+              𝕄(ξ.ρ):+                𝑛·3·1 := 5.0+                morphed 𝑛·3·2 := 𝑛·3·1+              boxed 𝑛·3·3 := 𝑛·3·2.φ+              𝕄(𝑛·3·3):+                𝑛·3·4 := Φ.bytes( φ ↦ 40-14-00-00-00-00-00-00:Δ )+                morphed 𝑛·3·5 := 𝑛·3·4+              boxed 𝑛·3·6 := 𝑛·3·5.φ               𝛿·3·1 := 40-14-00-00-00-00-00-00  # 5.0             𝛿1·3 := 𝛿·3·1  # 𝔻(ξ.ρ)-            𝔻(Φ.a🌵5):-              𝕄(Φ.a🌵5):-                𝑛·3·3 := 6-                𝑛·3·4 := Φ.bytes( φ ↦ 40-18-00-00-00-00-00-00:Δ )+            𝔻(ξ.x):+              𝕄(ξ.x):+                𝑛·3·7 := 6.0+                morphed 𝑛·3·8 := 𝑛·3·7+              boxed 𝑛·3·9 := 𝑛·3·8.φ+              𝕄(𝑛·3·9):+                𝑛·3·10 := Φ.bytes( φ ↦ 40-18-00-00-00-00-00-00:Δ )+                morphed 𝑛·3·11 := 𝑛·3·10+              boxed 𝑛·3·12 := 𝑛·3·11.φ               𝛿·3·2 := 40-18-00-00-00-00-00-00  # 6.0             𝛿2·3 := 𝛿·3·2  # 𝔻(ξ.x)-            𝑛·3·5 := Φ.number( φ ↦ 𝜎1:λ )  # 𝑛-            𝕄(𝑛·3·5):-              𝑛·3·6 := Φ.number( φ ↦ 𝜎1:λ )-            𝑛·3·7 := 𝑛·3·6  # 𝕄(𝑛·3·5)-        𝛿2·2 := 𝔻(𝜎1:λ)  # 𝔻(ξ.x)-        𝑛·2·5 := Φ.number( φ ↦ 𝜎2:λ )  # 𝑛-        𝕄(𝑛·2·5):-          𝑛·2·6 := Φ.number( φ ↦ 𝜎2:λ )-        𝑛·2·7 := 𝑛·2·6  # 𝕄(𝑛·2·5)-      𝑛3·1 := 𝑛·2·7  # 𝔼(𝑛1)+            𝑛·3·13 := Φ.number( φ ↦ 𝜎1:λ )  # 𝑛+            𝕄(𝑛·3·13):+              𝑛·3·14 := Φ.number( φ ↦ 𝜎1:λ )+              morphed 𝑛·3·15 := 𝑛·3·14+            𝑛·3·16 := 𝑛·3·15  # 𝕄(𝑛·3·13)+          boxed 𝑛·2·10 := 𝑛·3·16.φ+          𝛿·2·2 := 𝔻(𝜎1:λ)+        𝛿2·2 := 𝛿·2·2  # 𝔻(ξ.x)+        𝑛·2·11 := Φ.number( φ ↦ 𝜎2:λ )  # 𝑛+        𝕄(𝑛·2·11):+          𝑛·2·12 := Φ.number( φ ↦ 𝜎2:λ )+          morphed 𝑛·2·13 := 𝑛·2·12+        𝑛·2·14 := 𝑛·2·13  # 𝕄(𝑛·2·11)+      𝑛3·1 := 𝑛·2·14  # 𝔼(𝑛1)       𝔼(L_number_plus):  # 𝔼(𝑛2)-        𝑛·4·1 := 𝑛·2·6  # 𝑛+        𝑛·4·1 := 𝑛·2·12  # 𝑛         𝑛·4·2 := 𝑛3·1  # 𝕄(𝑛·4·1)       𝑛4·1 := 𝑛·4·2  # 𝔼(𝑛2)-      𝑛·1·5 := ⟦ a ↦ 𝑛·2·6, b ↦ 𝑛·2·6 ⟧:φ  # 𝑛-      𝑛·1·6 := ⟦ a ↦ 𝑛·2·6, b ↦ 𝑛·2·6 ⟧:φ  # 𝕄(𝑛·1·5)+      𝑛·1·7 := ⟦ a ↦ 𝑛·4·2, b ↦ 𝑛·4·2 ⟧:φ  # 𝑛+      𝕄(𝑛·1·7):+        morphed 𝑛·1·8 := ⟦ a ↦ 𝑛·4·2, b ↦ 𝑛·4·2 ⟧:φ+      𝑛·1·9 := 𝑛·1·8  # 𝕄(𝑛·1·7)+    morphed 𝑛·0·1 := ⟦ a ↦ 𝑛·4·2, b ↦ 𝑛·4·2 ⟧
test-resources/evaluate-packs/fires-the-answer-of-an-evaluate-line-on-the-next-line.yaml view
@@ -23,14 +23,23 @@ protocol: |   𝕄(Φ.y):     𝔼(L_twice):+      𝕄(ξ.a):+        morphed 𝑛·1·1 := L_first:λ       𝑛1·1 := L_first:λ  # 𝕄(ξ.a)       𝔼(L_first):  # 𝔼(𝑛1)         𝑛·2·1 := L_second:λ  # 𝑛-        𝑛·2·2 := L_second:λ  # 𝕄(𝑛·2·1)+        𝕄(𝑛·2·1):+          morphed 𝑛·2·2 := L_second:λ+        𝑛·2·3 := L_second:λ  # 𝕄(𝑛·2·1)       𝑛2·1 := L_second:λ  # 𝔼(𝑛1)       𝔼(L_second):  # 𝔼(𝑛2)         𝑛·3·1 := 𝜎1:λ:φ  # 𝑛-        𝑛·3·2 := 𝜎1:λ:φ  # 𝕄(𝑛·3·1)-      𝑛3·1 := 𝑛·3·2  # 𝔼(𝑛2)-      𝑛·1·1 := 𝑛3·1  # 𝑛-      𝑛·1·2 := 𝑛3·1  # 𝕄(𝑛·1·1)+        𝕄(𝑛·3·1):+          morphed 𝑛·3·2 := 𝜎1:λ:φ+        𝑛·3·3 := 𝑛·3·2  # 𝕄(𝑛·3·1)+      𝑛3·1 := 𝑛·3·3  # 𝔼(𝑛2)+      𝑛·1·2 := 𝑛3·1  # 𝑛+      𝕄(𝑛·1·2):+        morphed 𝑛·1·3 := 𝑛3·1+      𝑛·1·4 := 𝑛3·1  # 𝕄(𝑛·1·2)+    morphed 𝑛·0·1 := 𝜎1:λ
test-resources/evaluate-packs/fires-the-arguments-of-a-copy-a-defer-line-defers.yaml view
@@ -34,30 +34,40 @@ protocol: |   𝕄(Φ.y):     𝔼(L_inc):-      𝔻(Φ.a🌵0):+      𝔻(ξ.x):         𝔼(L_fork):-          𝛿1·2 := 𝔻(𝜎2:λ)  # 𝔻(ξ.c)-          𝑛1·2 := 𝜎3:λ  # 𝕄(ξ.a)-          𝕄(Φ.a🌵3):-            𝑛·2·1 := Φ.box( x ↦ Φ.items.tail )-            𝑛·2·2 := 𝑛·2·1( w ↦ Φ.inc( 2A-:Δ ) )-          𝑛2·2 := 𝑛·2·2  # 𝕄(ξ.b)+          𝔻(ξ.c):+            𝛿·2·1 := 𝔻(𝜎2:λ)+          𝛿1·2 := 𝛿·2·1  # 𝔻(ξ.c)+          𝕄(ξ.a):+            morphed 𝑛·2·1 := 𝜎3:λ+          𝑛1·2 := 𝑛·2·1  # 𝕄(ξ.a)+          𝕄(ξ.b):+            𝑛·2·2 := Φ.box( x ↦ Φ.items.tail )+            𝑛·2·3 := 𝑛·2·2( w ↦ Φ.inc( 2A-:Δ ) )+            morphed 𝑛·2·4 := 𝑛·2·3+          𝑛2·2 := 𝑛·2·4  # 𝕄(ξ.b)           𝑛3·2 := 𝑛1·2  # 𝑛1           𝕄(𝑛2.w):-            𝑛·2·3 := Φ.inc( x ↦ 2A-:Δ )+            𝑛·2·5 := Φ.inc( x ↦ 2A-:Δ )+            morphed 𝑛·2·6 := 𝑛·2·5           𝔼(L_inc):  # 𝔼(𝑛2.w)-            𝔻(Φ.a🌵4):+            𝔻(ξ.x):               𝛿·3·1 := 2A-             𝛿1·3 := 𝛿·3·1  # 𝔻(ξ.x)             𝑛·3·1 := Φ.num( φ ↦ 𝜎4:λ )  # 𝑛             𝕄(𝑛·3·1):               𝑛·3·2 := Φ.num( φ ↦ 𝜎4:λ )-            𝑛·3·3 := 𝑛·3·2  # 𝕄(𝑛·3·1)-          deferred 𝜎5 := Φ.box( x ↦ 𝜎1:λ, w ↦ 𝑛·3·2 )  # 𝕄(𝑛2.φ)+              morphed 𝑛·3·3 := 𝑛·3·2+            𝑛·3·4 := 𝑛·3·3  # 𝕄(𝑛·3·1)+          deferred 𝜎5 := Φ.box( x ↦ 𝜎1:λ, w ↦ 𝑛·3·4 )  # 𝕄(𝑛2.φ)           joined 𝜎6 := 𝛿1·2 ? 𝜎3 : 𝜎5-          𝑛·2·4 := 𝜎6:λ  # 𝑛3, 𝑛4-      𝛿1·1 := 𝔻(𝜎6:λ)  # 𝔻(ξ.x)+          𝑛·2·7 := 𝜎6:λ  # 𝑛3, 𝑛4+        𝛿·1·1 := 𝔻(𝜎6:λ)+      𝛿1·1 := 𝛿·1·1  # 𝔻(ξ.x)       𝑛·1·1 := Φ.num( φ ↦ 𝜎7:λ )  # 𝑛       𝕄(𝑛·1·1):         𝑛·1·2 := Φ.num( φ ↦ 𝜎7:λ )-      𝑛·1·3 := 𝑛·1·2  # 𝕄(𝑛·1·1)+        morphed 𝑛·1·3 := 𝑛·1·2+      𝑛·1·4 := 𝑛·1·3  # 𝕄(𝑛·1·1)+    morphed 𝑛·0·1 := 𝜎7:λ
test-resources/evaluate-packs/fires-what-a-rewrite-line-unwrapped.yaml view
@@ -29,11 +29,18 @@ protocol: |   𝕄(Φ.y):     𝔼(L_pick):-      𝑛1·1 := L_inner:λ:φ  # 𝕄(ξ.x)+      𝕄(ξ.x):+        morphed 𝑛·1·1 := L_inner:λ:φ+      𝑛1·1 := 𝑛·1·1  # 𝕄(ξ.x)       𝑛2·1 := L_inner:λ  # rewrite(𝑛1)       𝔼(L_inner):  # 𝔼(𝑛2)         𝑛·2·1 := 𝜎1:λ:φ  # 𝑛-        𝑛·2·2 := 𝜎1:λ:φ  # 𝕄(𝑛·2·1)-      𝑛3·1 := 𝑛·2·2  # 𝔼(𝑛2)-      𝑛·1·1 := 𝑛3·1  # 𝑛-      𝑛·1·2 := 𝑛3·1  # 𝕄(𝑛·1·1)+        𝕄(𝑛·2·1):+          morphed 𝑛·2·2 := 𝜎1:λ:φ+        𝑛·2·3 := 𝑛·2·2  # 𝕄(𝑛·2·1)+      𝑛3·1 := 𝑛·2·3  # 𝔼(𝑛2)+      𝑛·1·2 := 𝑛3·1  # 𝑛+      𝕄(𝑛·1·2):+        morphed 𝑛·1·3 := 𝑛3·1+      𝑛·1·4 := 𝑛3·1  # 𝕄(𝑛·1·2)+    morphed 𝑛·0·1 := 𝜎1:λ
test-resources/evaluate-packs/hands-a-named-argument-to-both-sides-of-an-attribute-of-a-fabricated-formation.yaml view
@@ -29,10 +29,21 @@ protocol: |   𝕄(Φ.y):     𝔼(L_fork):-      𝛿1·1 := 𝔻(𝜎1:λ)  # 𝔻(ξ.c)-      fabricated 𝑛·1·1 := ⟦ φ ↦ Φ.hx, ke(x) ↦ x:k ⟧ and ⟦ ke(x) ↦ x:k ⟧  # ⟦ left🌵 ↦ ⟦ φ ↦ Φ.hx, ke(x) ↦ x:k ⟧, right🌵 ↦ ⟦ ke(x) ↦ x:k ⟧, φ ↦ ⟦ λ ⤍ L_join, on🌵 ↦ 𝜎1:λ, left🌵 ↦ ρ.left🌵.φ, right🌵 ↦ ρ.right🌵.φ ⟧, ke(x) ↦ ⟦ λ ⤍ L_join, on🌵 ↦ 𝜎1:λ, left🌵 ↦ ρ.left🌵.ke( x ↦ x ), right🌵 ↦ ρ.right🌵.ke( x ↦ x ) ⟧ ⟧+      𝔻(ξ.c):+        𝛿·1·1 := 𝔻(𝜎1:λ)+      𝛿1·1 := 𝛿·1·1  # 𝔻(ξ.c)+      𝕄(ξ.a):+        morphed 𝑛·1·1 := ⟦ φ ↦ Φ.hx, ke(x) ↦ x:k ⟧+      𝕄(ξ.b):+        morphed 𝑛·1·2 := ⟦ ke(x) ↦ x:k ⟧+      fabricated 𝑛·1·3 := 𝑛·1·1 and 𝑛·1·2  # ⟦ left🌵 ↦ 𝑛·1·1, right🌵 ↦ 𝑛·1·2, φ ↦ ⟦ λ ⤍ L_join, on🌵 ↦ 𝜎1:λ, left🌵 ↦ ρ.left🌵.φ, right🌵 ↦ ρ.right🌵.φ ⟧, ke(x) ↦ ⟦ λ ⤍ L_join, on🌵 ↦ 𝜎1:λ, left🌵 ↦ ρ.left🌵.ke( x ↦ x ), right🌵 ↦ ρ.right🌵.ke( x ↦ x ) ⟧ ⟧     𝑛·0·1 := ⟦ λ ⤍ L_join, x ↦ ∅, on🌵 ↦ 𝜎1:λ, left🌵 ↦ ρ.left🌵.ke( x ↦ x ), right🌵 ↦ ρ.right🌵.ke( x ↦ x ) ⟧( x ↦ 𝜎2:λ )     𝔼(L_join):-      𝑛·2·1 := ⟦ x ↦ ∅, k ↦ x ⟧( x ↦ 𝜎2:λ )  # 𝕄(Φ.a🌵3)-      𝑛·2·2 := ⟦ x ↦ ∅, k ↦ x ⟧( x ↦ 𝜎2:λ )  # 𝕄(Φ.a🌵4)-      𝑛·2·3 := 𝑛·2·2  # 𝕄(ξ.left🌵), 𝕄(ξ.right🌵)+      𝑛·2·1 := ⟦ x ↦ ∅, k ↦ x ⟧( x ↦ 𝜎2:λ )  # 𝕄(ξ.left🌵)+      𝕄(ξ.left🌵):+        morphed 𝑛·2·2 := 𝑛·2·1+      𝑛·2·3 := ⟦ x ↦ ∅, k ↦ x ⟧( x ↦ 𝜎2:λ )  # 𝕄(ξ.right🌵)+      𝕄(ξ.right🌵):+        morphed 𝑛·2·4 := 𝑛·2·3+      𝑛·2·5 := 𝑛·2·4  # 𝕄(ξ.left🌵), 𝕄(ξ.right🌵)+    morphed 𝑛·0·2 := 𝜎2:λ
test-resources/evaluate-packs/hands-a-positional-argument-to-both-sides-of-an-attribute-of-a-fabricated-formation.yaml view
@@ -29,10 +29,21 @@ protocol: |   𝕄(Φ.y):     𝔼(L_fork):-      𝛿1·1 := 𝔻(𝜎1:λ)  # 𝔻(ξ.c)-      fabricated 𝑛·1·1 := ⟦ φ ↦ Φ.hx, ke(x) ↦ x:k ⟧ and ⟦ ke(x) ↦ x:k ⟧  # ⟦ left🌵 ↦ ⟦ φ ↦ Φ.hx, ke(x) ↦ x:k ⟧, right🌵 ↦ ⟦ ke(x) ↦ x:k ⟧, φ ↦ ⟦ λ ⤍ L_join, on🌵 ↦ 𝜎1:λ, left🌵 ↦ ρ.left🌵.φ, right🌵 ↦ ρ.right🌵.φ ⟧, ke(x) ↦ ⟦ λ ⤍ L_join, on🌵 ↦ 𝜎1:λ, left🌵 ↦ ρ.left🌵.ke( x ↦ x ), right🌵 ↦ ρ.right🌵.ke( x ↦ x ) ⟧ ⟧+      𝔻(ξ.c):+        𝛿·1·1 := 𝔻(𝜎1:λ)+      𝛿1·1 := 𝛿·1·1  # 𝔻(ξ.c)+      𝕄(ξ.a):+        morphed 𝑛·1·1 := ⟦ φ ↦ Φ.hx, ke(x) ↦ x:k ⟧+      𝕄(ξ.b):+        morphed 𝑛·1·2 := ⟦ ke(x) ↦ x:k ⟧+      fabricated 𝑛·1·3 := 𝑛·1·1 and 𝑛·1·2  # ⟦ left🌵 ↦ 𝑛·1·1, right🌵 ↦ 𝑛·1·2, φ ↦ ⟦ λ ⤍ L_join, on🌵 ↦ 𝜎1:λ, left🌵 ↦ ρ.left🌵.φ, right🌵 ↦ ρ.right🌵.φ ⟧, ke(x) ↦ ⟦ λ ⤍ L_join, on🌵 ↦ 𝜎1:λ, left🌵 ↦ ρ.left🌵.ke( x ↦ x ), right🌵 ↦ ρ.right🌵.ke( x ↦ x ) ⟧ ⟧     𝑛·0·1 := ⟦ λ ⤍ L_join, x ↦ ∅, on🌵 ↦ 𝜎1:λ, left🌵 ↦ ρ.left🌵.ke( x ↦ x ), right🌵 ↦ ρ.right🌵.ke( x ↦ x ) ⟧( x ↦ 𝜎2:λ )     𝔼(L_join):-      𝑛·2·1 := ⟦ x ↦ ∅, k ↦ x ⟧( x ↦ 𝜎2:λ )  # 𝕄(Φ.a🌵3)-      𝑛·2·2 := ⟦ x ↦ ∅, k ↦ x ⟧( x ↦ 𝜎2:λ )  # 𝕄(Φ.a🌵4)-      𝑛·2·3 := 𝑛·2·2  # 𝕄(ξ.left🌵), 𝕄(ξ.right🌵)+      𝑛·2·1 := ⟦ x ↦ ∅, k ↦ x ⟧( x ↦ 𝜎2:λ )  # 𝕄(ξ.left🌵)+      𝕄(ξ.left🌵):+        morphed 𝑛·2·2 := 𝑛·2·1+      𝑛·2·3 := ⟦ x ↦ ∅, k ↦ x ⟧( x ↦ 𝜎2:λ )  # 𝕄(ξ.right🌵)+      𝕄(ξ.right🌵):+        morphed 𝑛·2·4 := 𝑛·2·3+      𝑛·2·5 := 𝑛·2·4  # 𝕄(ξ.left🌵), 𝕄(ξ.right🌵)+    morphed 𝑛·0·2 := 𝜎2:λ
test-resources/evaluate-packs/hands-a-side-of-a-fabricated-formation-only-the-voids-its-attribute-leaves.yaml view
@@ -31,13 +31,28 @@ protocol: |   𝕄(Φ.y):     𝔼(L_fork):-      𝛿1·1 := 𝔻(𝜎1:λ)  # 𝔻(ξ.c)-      fabricated 𝑛·1·1 := ⟦ φ ↦ Φ.hx, ke(x) ↦ x:k ⟧ and ⟦ ke(x, w) ↦ x:k ⟧  # ⟦ left🌵 ↦ ⟦ φ ↦ Φ.hx, ke(x) ↦ x:k ⟧, right🌵 ↦ ⟦ ke(x, w) ↦ x:k ⟧, φ ↦ ⟦ λ ⤍ L_join, on🌵 ↦ 𝜎1:λ, left🌵 ↦ ρ.left🌵.φ, right🌵 ↦ ρ.right🌵.φ ⟧, ke(x, w) ↦ ⟦ λ ⤍ L_join, on🌵 ↦ 𝜎1:λ, left🌵 ↦ ρ.left🌵.ke( x ↦ x ), right🌵 ↦ ρ.right🌵.ke( x ↦ x, w ↦ w ) ⟧ ⟧+      𝔻(ξ.c):+        𝛿·1·1 := 𝔻(𝜎1:λ)+      𝛿1·1 := 𝛿·1·1  # 𝔻(ξ.c)+      𝕄(ξ.a):+        morphed 𝑛·1·1 := ⟦ φ ↦ Φ.hx, ke(x) ↦ x:k ⟧+      𝕄(ξ.b):+        morphed 𝑛·1·2 := ⟦ ke(x, w) ↦ x:k ⟧+      fabricated 𝑛·1·3 := 𝑛·1·1 and 𝑛·1·2  # ⟦ left🌵 ↦ 𝑛·1·1, right🌵 ↦ 𝑛·1·2, φ ↦ ⟦ λ ⤍ L_join, on🌵 ↦ 𝜎1:λ, left🌵 ↦ ρ.left🌵.φ, right🌵 ↦ ρ.right🌵.φ ⟧, ke(x, w) ↦ ⟦ λ ⤍ L_join, on🌵 ↦ 𝜎1:λ, left🌵 ↦ ρ.left🌵.ke( x ↦ x ), right🌵 ↦ ρ.right🌵.ke( x ↦ x, w ↦ w ) ⟧ ⟧     𝑛·0·1 := ⟦ λ ⤍ L_join, x ↦ ∅, w ↦ ∅, on🌵 ↦ 𝜎1:λ, left🌵 ↦ ρ.left🌵.ke( x ↦ x ), right🌵 ↦ ρ.right🌵.ke( x ↦ x, w ↦ w ) ⟧( x ↦ 𝜎2:λ:φ )     𝔼(L_join):-      𝑛·2·1 := ⟦ x ↦ ∅, k ↦ x ⟧( x ↦ 𝜎2:λ:φ )  # 𝕄(Φ.a🌵3)-      𝑛·2·2 := ⟦ x ↦ ∅, w ↦ ∅, k ↦ x ⟧( x ↦ 𝜎2:λ:φ )  # 𝕄(Φ.a🌵4)-      𝑛·2·3 := 𝑛·2·2( w ↦ ⊥ )  # 𝕄(Φ.a🌵4)-      fabricated 𝑛·2·4 := 𝑛·2·1 and 𝑛·2·3  # ⟦ left🌵 ↦ 𝑛·2·1, right🌵 ↦ 𝑛·2·3, x ↦ ⟦ λ ⤍ L_join, on🌵 ↦ 𝜎1:λ, left🌵 ↦ ρ.left🌵.x, right🌵 ↦ ρ.right🌵.x ⟧, k ↦ ⟦ λ ⤍ L_join, on🌵 ↦ 𝜎1:λ, left🌵 ↦ ρ.left🌵.k, right🌵 ↦ ρ.right🌵.k ⟧, w ↦ ⟦ λ ⤍ L_join, on🌵 ↦ 𝜎1:λ, left🌵 ↦ ρ.left🌵.w, right🌵 ↦ ρ.right🌵.w ⟧ ⟧+      𝑛·2·1 := ⟦ x ↦ ∅, k ↦ x ⟧( x ↦ 𝜎2:λ:φ )  # 𝕄(ξ.left🌵)+      𝕄(ξ.left🌵):+        morphed 𝑛·2·2 := 𝑛·2·1+      𝑛·2·3 := ⟦ x ↦ ∅, w ↦ ∅, k ↦ x ⟧( x ↦ 𝜎2:λ:φ )  # 𝕄(ξ.right🌵)+      𝑛·2·4 := 𝑛·2·3( w ↦ ⊥ )  # 𝕄(ξ.right🌵)+      𝕄(ξ.right🌵):+        morphed 𝑛·2·5 := 𝑛·2·4+      fabricated 𝑛·2·6 := 𝑛·2·2 and 𝑛·2·5  # ⟦ left🌵 ↦ 𝑛·2·2, right🌵 ↦ 𝑛·2·5, x ↦ ⟦ λ ⤍ L_join, on🌵 ↦ 𝜎1:λ, left🌵 ↦ ρ.left🌵.x, right🌵 ↦ ρ.right🌵.x ⟧, k ↦ ⟦ λ ⤍ L_join, on🌵 ↦ 𝜎1:λ, left🌵 ↦ ρ.left🌵.k, right🌵 ↦ ρ.right🌵.k ⟧, w ↦ ⟦ λ ⤍ L_join, on🌵 ↦ 𝜎1:λ, left🌵 ↦ ρ.left🌵.w, right🌵 ↦ ρ.right🌵.w ⟧ ⟧     𝔼(L_join):-      𝑛·3·1 := 𝜎2:λ:φ  # 𝕄(ξ.left🌵), 𝕄(ξ.right🌵)+      𝕄(ξ.left🌵):+        morphed 𝑛·3·1 := 𝜎2:λ:φ+      𝕄(ξ.right🌵):+        morphed 𝑛·3·2 := 𝑛·3·1+      𝑛·3·3 := 𝑛·3·1  # 𝕄(ξ.left🌵), 𝕄(ξ.right🌵)+    morphed 𝑛·0·2 := 𝜎2:λ
+ test-resources/evaluate-packs/heads-the-block-of-a-dataize-operand-with-the-operand.yaml view
@@ -0,0 +1,53 @@+# SPDX-FileCopyrightText: Copyright (c) 2025 Objectionary.com+# SPDX-License-Identifier: MIT+---+# yamllint disable rule:line-length+# An operand of 'dataize' is brought down where it stands in the universe,+# under a name phino makes up for it, but the block the 𝔻 opens is headed by+# the operand as the entry spells it, the way the line binding its meta is+# commented, so a reader learns what the block brings down from its first+# line rather than from the line after it closes (#2145).+symbolic: |+  - λ: L_number_plus+    dataize:+      𝛿1: $.ρ+      𝛿2: $.x+    𝑛: Φ.number( φ ↦ ⟦ λ ⤍ 𝜎 ⟧ )+location: 'Q.@'+input: >+  ⟦ bytes(φ) ↦ ⟦⟧,+    number(φ) ↦ ⟦ plus(ρ, x) ↦ ⟦ λ ⤍ L_number_plus ⟧ ⟧,+    φ ↦ 5.plus( 6 ).φ ⟧+protocol: |+  𝕄(Φ.φ):+    𝑛·0·1 := 5.0+    𝑛·0·2 := 𝑛·0·1.plus( x ↦ 6.0 )+    𝔼(L_number_plus):+      𝔻(ξ.ρ):+        𝕄(ξ.ρ):+          𝑛·1·1 := 5.0+          morphed 𝑛·1·2 := 𝑛·1·1+        boxed 𝑛·1·3 := 𝑛·1·2.φ+        𝕄(𝑛·1·3):+          𝑛·1·4 := Φ.bytes( φ ↦ 40-14-00-00-00-00-00-00:Δ )+          morphed 𝑛·1·5 := 𝑛·1·4+        boxed 𝑛·1·6 := 𝑛·1·5.φ+        𝛿·1·1 := 40-14-00-00-00-00-00-00  # 5.0+      𝛿1·1 := 𝛿·1·1  # 𝔻(ξ.ρ)+      𝔻(ξ.x):+        𝕄(ξ.x):+          𝑛·1·7 := 6.0+          morphed 𝑛·1·8 := 𝑛·1·7+        boxed 𝑛·1·9 := 𝑛·1·8.φ+        𝕄(𝑛·1·9):+          𝑛·1·10 := Φ.bytes( φ ↦ 40-18-00-00-00-00-00-00:Δ )+          morphed 𝑛·1·11 := 𝑛·1·10+        boxed 𝑛·1·12 := 𝑛·1·11.φ+        𝛿·1·2 := 40-18-00-00-00-00-00-00  # 6.0+      𝛿2·1 := 𝛿·1·2  # 𝔻(ξ.x)+      𝑛·1·13 := Φ.number( φ ↦ 𝜎1:λ )  # 𝑛+      𝕄(𝑛·1·13):+        𝑛·1·14 := Φ.number( φ ↦ 𝜎1:λ )+        morphed 𝑛·1·15 := 𝑛·1·14+      𝑛·1·16 := 𝑛·1·15  # 𝕄(𝑛·1·13)+    morphed 𝑛·0·3 := 𝜎1:λ
+ test-resources/evaluate-packs/heads-the-block-of-a-morph-operand-with-the-operand.yaml view
@@ -0,0 +1,28 @@+# SPDX-FileCopyrightText: Copyright (c) 2025 Objectionary.com+# SPDX-License-Identifier: MIT+---+# yamllint disable rule:line-length+# An operand of 'morph' is reduced where it stands in the universe, under a+# name phino makes up for it, but the block the 𝕄 opens is headed by the+# operand as the entry spells it, the way the line binding its meta is+# commented, and not by that made-up name (#2145).+symbolic: |+  - λ: L_kept+    morph:+      𝑛1: $.x+    𝑛: ⟦ φ ↦ ⟦ λ ⤍ 𝜎 ⟧ ⟧+location: 'Q.y'+input: '⟦ bytes(φ) ↦ ⟦⟧, number(φ) ↦ ⟦ neg ↦ ⟦⟧ ⟧, y ↦ ⟦ x ↦ 5, λ ⤍ L_kept ⟧.φ ⟧'+result: '𝜎1:λ'+protocol: |+  𝕄(Φ.y):+    𝔼(L_kept):+      𝕄(ξ.x):+        𝑛·1·1 := 5.0+        morphed 𝑛·1·2 := 𝑛·1·1+      𝑛1·1 := 𝑛·1·2  # 𝕄(ξ.x)+      𝑛·1·3 := 𝜎1:λ:φ  # 𝑛+      𝕄(𝑛·1·3):+        morphed 𝑛·1·4 := 𝜎1:λ:φ+      𝑛·1·5 := 𝑛·1·4  # 𝕄(𝑛·1·3)+    morphed 𝑛·0·1 := 𝜎1:λ
test-resources/evaluate-packs/joins-a-bool-literal-a-rewrite-line-brought-into-the-shape-of-the-other-side.yaml view
@@ -43,10 +43,17 @@ protocol: |   𝕄(Φ.y):     𝔼(L_fork):-      𝛿1·1 := 𝔻(𝜎1:λ)  # 𝔻(ξ.c)-      𝑛1·1 := ⟦ φ ↦ 𝜎2:λ, kq ↦ Φ.true ⟧  # 𝕄(ξ.a)-      𝑛2·1 := ⟦ φ ↦ 𝜎3:λ, kq ↦ Φ.bool( if ↦ 01-:Δ ) ⟧  # 𝕄(ξ.b)+      𝔻(ξ.c):+        𝛿·1·1 := 𝔻(𝜎1:λ)+      𝛿1·1 := 𝛿·1·1  # 𝔻(ξ.c)+      𝕄(ξ.a):+        morphed 𝑛·1·1 := ⟦ φ ↦ 𝜎2:λ, kq ↦ Φ.true ⟧+      𝑛1·1 := 𝑛·1·1  # 𝕄(ξ.a)+      𝕄(ξ.b):+        morphed 𝑛·1·2 := ⟦ φ ↦ 𝜎3:λ, kq ↦ Φ.bool( if ↦ 01-:Δ ) ⟧+      𝑛2·1 := 𝑛·1·2  # 𝕄(ξ.b)       𝑛3·1 := ⟦ φ ↦ 𝜎2:λ, kq ↦ Φ.bool( if ↦ 01-:Δ ) ⟧  # rewrite(𝑛1)       𝑛4·1 := 𝑛2·1  # 𝑛2       joined 𝜎4 := 𝛿1·1 ? 𝜎2 : 𝜎3-      𝑛·1·1 := ⟦ φ ↦ 𝜎4:λ, kq ↦ Φ.bool( if ↦ 01-:Δ ) ⟧  # 𝑛3, 𝑛4+      𝑛·1·3 := ⟦ φ ↦ 𝜎4:λ, kq ↦ Φ.bool( if ↦ 01-:Δ ) ⟧  # 𝑛3, 𝑛4+    morphed 𝑛·0·1 := 𝜎4:λ
test-resources/evaluate-packs/joins-a-side-a-morph-line-brought-down-from-what-a-rewrite-line-unwrapped.yaml view
@@ -37,10 +37,19 @@ protocol: |   𝕄(Φ.y):     𝔼(L_fork):-      𝛿1·1 := 𝔻(𝜎1:λ)  # 𝔻(ξ.c)-      𝑛1·1 := 𝜎2:λ:φ  # 𝕄(ξ.a)-      𝑛2·1 := Φ.t:φ  # 𝕄(ξ.b)+      𝔻(ξ.c):+        𝛿·1·1 := 𝔻(𝜎1:λ)+      𝛿1·1 := 𝛿·1·1  # 𝔻(ξ.c)+      𝕄(ξ.a):+        morphed 𝑛·1·1 := 𝜎2:λ:φ+      𝑛1·1 := 𝑛·1·1  # 𝕄(ξ.a)+      𝕄(ξ.b):+        morphed 𝑛·1·2 := Φ.t:φ+      𝑛2·1 := 𝑛·1·2  # 𝕄(ξ.b)       𝑛3·1 := Φ.t  # rewrite(𝑛2)-      𝑛4·1 := 𝜎3:λ:φ  # 𝕄(𝑛3)+      𝕄(𝑛3):+        morphed 𝑛·1·3 := 𝜎3:λ:φ+      𝑛4·1 := 𝑛·1·3  # 𝕄(𝑛3)       joined 𝜎4 := 𝛿1·1 ? 𝜎2 : 𝜎3-      𝑛·1·1 := 𝜎4:λ:φ  # 𝑛1, 𝑛4+      𝑛·1·4 := 𝜎4:λ:φ  # 𝑛1, 𝑛4+    morphed 𝑛·0·1 := 𝜎4:λ
test-resources/evaluate-packs/joins-a-side-an-evaluate-line-fired-with-a-bool-it-left-as-it-is.yaml view
@@ -30,17 +30,24 @@ protocol: |   𝕄(Φ.y):     𝔼(L_fork):-      𝛿1·1 := 𝔻(𝜎1:λ)  # 𝔻(ξ.c)+      𝔻(ξ.c):+        𝛿·1·1 := 𝔻(𝜎1:λ)+      𝛿1·1 := 𝛿·1·1  # 𝔻(ξ.c)+      𝕄(ξ.a):+        morphed 𝑛·1·1 := L_eq:λ       𝑛1·1 := L_eq:λ  # 𝕄(ξ.a)-      𝕄(Φ.a🌵2):-        𝑛·1·1 := Φ.bool( φ ↦ 𝜎2:λ )-      𝑛2·1 := 𝑛·1·1  # 𝕄(ξ.b)+      𝕄(ξ.b):+        𝑛·1·2 := Φ.bool( φ ↦ 𝜎2:λ )+        morphed 𝑛·1·3 := 𝑛·1·2+      𝑛2·1 := 𝑛·1·3  # 𝕄(ξ.b)       𝔼(L_eq):  # 𝔼(𝑛1)         𝑛·2·1 := Φ.bool( φ ↦ 𝜎3:λ )  # 𝑛         𝕄(𝑛·2·1):           𝑛·2·2 := Φ.bool( φ ↦ 𝜎3:λ )-        𝑛·2·3 := 𝑛·2·2  # 𝕄(𝑛·2·1)-      𝑛3·1 := 𝑛·2·3  # 𝔼(𝑛1)+          morphed 𝑛·2·3 := 𝑛·2·2+        𝑛·2·4 := 𝑛·2·3  # 𝕄(𝑛·2·1)+      𝑛3·1 := 𝑛·2·4  # 𝔼(𝑛1)       𝑛4·1 := 𝑛2·1  # 𝔼(𝑛2)       joined 𝜎4 := 𝛿1·1 ? 𝜎3 : 𝜎2-      𝑛·1·2 := 𝜎4:λ:φ  # 𝑛3, 𝑛4+      𝑛·1·4 := 𝜎4:λ:φ  # 𝑛3, 𝑛4+    morphed 𝑛·0·1 := 𝜎4:λ
test-resources/evaluate-packs/joins-a-side-holding-a-datum-a-symbolize-turned-into-a-symbol.yaml view
@@ -31,12 +31,19 @@ protocol: |   𝕄(Φ.y):     𝔼(L_fork):-      𝛿1·1 := 𝔻(𝜎3:λ)  # 𝔻(ξ.c)-      𝑛1·1 := 1:φ  # 𝕄(ξ.a)-      𝑛2·1 := Φ.number( φ ↦ Φ.bytes( φ ↦ 𝜎2:λ ) ):φ  # 𝕄(ξ.b)+      𝔻(ξ.c):+        𝛿·1·1 := 𝔻(𝜎3:λ)+      𝛿1·1 := 𝛿·1·1  # 𝔻(ξ.c)+      𝕄(ξ.a):+        morphed 𝑛·1·1 := 1.0:φ+      𝑛1·1 := 𝑛·1·1  # 𝕄(ξ.a)+      𝕄(ξ.b):+        morphed 𝑛·1·2 := Φ.number( φ ↦ Φ.bytes( φ ↦ 𝜎2:λ ) ):φ+      𝑛2·1 := 𝑛·1·2  # 𝕄(ξ.b)       symbolized 𝜎4 := 3F-F0-00-00-00-00-00-00  # 1.0       𝑛3·1 := Φ.number( φ ↦ Φ.bytes( φ ↦ 𝜎4:λ ) ):φ  # symbolize(𝑛1)       𝑛4·1 := 𝑛2·1  # 𝑛2       joined 𝜎5 := 𝛿1·1 ? 𝜎4 : 𝜎2-      𝑛·1·1 := Φ.number( φ ↦ Φ.bytes( φ ↦ 𝜎5:λ ) ):φ  # 𝑛3, 𝑛4+      𝑛·1·3 := Φ.number( φ ↦ Φ.bytes( φ ↦ 𝜎5:λ ) ):φ  # 𝑛3, 𝑛4     𝑛·0·1 := Φ.number( φ ↦ Φ.bytes( φ ↦ 𝜎5:λ ) )+    morphed 𝑛·0·2 := 𝑛·0·1
test-resources/evaluate-packs/joins-a-side-reading-the-rho-it-binds-in-a-fabricated-formation.yaml view
@@ -30,9 +30,20 @@ protocol: |   𝕄(Φ.y):     𝔼(L_fork):-      𝛿1·1 := 𝔻(𝜎1:λ)  # 𝔻(ξ.c)-      fabricated 𝑛·1·1 := ⟦ tu ↦ ∅, φ ↦ ρ.s ⟧ and 𝜎3:λ:k:φ  # ⟦ tu ↦ ∅, left🌵 ↦ ⟦ tu ↦ ∅, φ ↦ ρ.s ⟧( tu ↦ tu ), right🌵 ↦ 𝜎3:λ:k:φ, φ ↦ ⟦ λ ⤍ L_join, on🌵 ↦ 𝜎1:λ, left🌵 ↦ ρ.left🌵.φ, right🌵 ↦ ρ.right🌵.φ ⟧ ⟧+      𝔻(ξ.c):+        𝛿·1·1 := 𝔻(𝜎1:λ)+      𝛿1·1 := 𝛿·1·1  # 𝔻(ξ.c)+      𝕄(ξ.a):+        morphed 𝑛·1·1 := ⟦ tu ↦ ∅, φ ↦ ρ.s ⟧+      𝕄(ξ.b):+        morphed 𝑛·1·2 := 𝜎3:λ:k:φ+      fabricated 𝑛·1·3 := 𝑛·1·1 and 𝑛·1·2  # ⟦ tu ↦ ∅, left🌵 ↦ 𝑛·1·1( tu ↦ tu ), right🌵 ↦ 𝑛·1·2, φ ↦ ⟦ λ ⤍ L_join, on🌵 ↦ 𝜎1:λ, left🌵 ↦ ρ.left🌵.φ, right🌵 ↦ ρ.right🌵.φ ⟧ ⟧     𝔼(L_join):-      𝑛·2·1 := Φ.t( tu ↦ ⊥ )  # 𝕄(Φ.a🌵3)+      𝑛·2·1 := Φ.t( tu ↦ ⊥ )  # 𝕄(ξ.left🌵)+      𝕄(ξ.left🌵):+        morphed 𝑛·2·2 := 𝜎2:λ:k+      𝕄(ξ.right🌵):+        morphed 𝑛·2·3 := 𝜎3:λ:k       joined 𝜎4 := ξ.on🌵 ? 𝜎2 : 𝜎3-      𝑛·2·2 := 𝜎4:λ:k  # 𝕄(ξ.left🌵), 𝕄(ξ.right🌵)+      𝑛·2·4 := 𝜎4:λ:k  # 𝕄(ξ.left🌵), 𝕄(ξ.right🌵)+    morphed 𝑛·0·1 := 𝜎4:λ
test-resources/evaluate-packs/joins-a-side-whose-void-the-fabricated-formation-holds.yaml view
@@ -31,9 +31,20 @@ protocol: |   𝕄(Φ.y):     𝔼(L_fork):-      𝛿1·1 := 𝔻(𝜎1:λ)  # 𝔻(ξ.c)-      fabricated 𝑛·1·1 := ⟦ φ ↦ 𝜎2:λ:k, tu ↦ ∅ ⟧ and 𝜎3:λ:k:φ  # ⟦ tu ↦ ∅, left🌵 ↦ ⟦ φ ↦ 𝜎2:λ:k, tu ↦ ∅ ⟧( tu ↦ tu ), right🌵 ↦ 𝜎3:λ:k:φ, φ ↦ ⟦ λ ⤍ L_join, on🌵 ↦ 𝜎1:λ, left🌵 ↦ ρ.left🌵.φ, right🌵 ↦ ρ.right🌵.φ ⟧ ⟧+      𝔻(ξ.c):+        𝛿·1·1 := 𝔻(𝜎1:λ)+      𝛿1·1 := 𝛿·1·1  # 𝔻(ξ.c)+      𝕄(ξ.a):+        morphed 𝑛·1·1 := ⟦ φ ↦ 𝜎2:λ:k, tu ↦ ∅ ⟧+      𝕄(ξ.b):+        morphed 𝑛·1·2 := 𝜎3:λ:k:φ+      fabricated 𝑛·1·3 := 𝑛·1·1 and 𝑛·1·2  # ⟦ tu ↦ ∅, left🌵 ↦ 𝑛·1·1( tu ↦ tu ), right🌵 ↦ 𝑛·1·2, φ ↦ ⟦ λ ⤍ L_join, on🌵 ↦ 𝜎1:λ, left🌵 ↦ ρ.left🌵.φ, right🌵 ↦ ρ.right🌵.φ ⟧ ⟧     𝔼(L_join):-      𝑛·2·1 := ⟦ φ ↦ 𝜎2:λ:k, tu ↦ ∅ ⟧( tu ↦ ⊥ )  # 𝕄(Φ.a🌵3)+      𝑛·2·1 := 𝑛·1·1( tu ↦ ⊥ )  # 𝕄(ξ.left🌵)+      𝕄(ξ.left🌵):+        morphed 𝑛·2·2 := 𝜎2:λ:k+      𝕄(ξ.right🌵):+        morphed 𝑛·2·3 := 𝜎3:λ:k       joined 𝜎4 := ξ.on🌵 ? 𝜎2 : 𝜎3-      𝑛·2·2 := 𝜎4:λ:k  # 𝕄(ξ.left🌵), 𝕄(ξ.right🌵)+      𝑛·2·4 := 𝜎4:λ:k  # 𝕄(ξ.left🌵), 𝕄(ξ.right🌵)+    morphed 𝑛·0·1 := 𝜎4:λ
test-resources/evaluate-packs/joins-again-the-attribute-of-a-fabricated-formation.yaml view
@@ -30,12 +30,21 @@ protocol: |   𝕄(Φ.y):     𝔼(L_fork):-      𝛿1·1 := 𝔻(𝜎1:λ)  # 𝔻(ξ.c)-      fabricated 𝑛·1·1 := ⟦ φ ↦ Φ.hx( 𝜎2:λ ), ob ↦ Φ.hx ⟧ and Φ.hx( 𝜎3:λ ):φ  # ⟦ left🌵 ↦ ⟦ φ ↦ Φ.hx( 𝜎2:λ ), ob ↦ Φ.hx ⟧, right🌵 ↦ Φ.hx( 𝜎3:λ ):φ, φ ↦ ⟦ λ ⤍ L_join, on🌵 ↦ 𝜎1:λ, left🌵 ↦ ρ.left🌵.φ, right🌵 ↦ ρ.right🌵.φ ⟧, ob ↦ ⟦ λ ⤍ L_join, on🌵 ↦ 𝜎1:λ, left🌵 ↦ ρ.left🌵.ob, right🌵 ↦ ρ.right🌵.ob ⟧ ⟧+      𝔻(ξ.c):+        𝛿·1·1 := 𝔻(𝜎1:λ)+      𝛿1·1 := 𝛿·1·1  # 𝔻(ξ.c)+      𝕄(ξ.a):+        morphed 𝑛·1·1 := ⟦ φ ↦ Φ.hx( 𝜎2:λ ), ob ↦ Φ.hx ⟧+      𝕄(ξ.b):+        morphed 𝑛·1·2 := Φ.hx( 𝜎3:λ ):φ+      fabricated 𝑛·1·3 := 𝑛·1·1 and 𝑛·1·2  # ⟦ left🌵 ↦ 𝑛·1·1, right🌵 ↦ 𝑛·1·2, φ ↦ ⟦ λ ⤍ L_join, on🌵 ↦ 𝜎1:λ, left🌵 ↦ ρ.left🌵.φ, right🌵 ↦ ρ.right🌵.φ ⟧, ob ↦ ⟦ λ ⤍ L_join, on🌵 ↦ 𝜎1:λ, left🌵 ↦ ρ.left🌵.ob, right🌵 ↦ ρ.right🌵.ob ⟧ ⟧     𝔼(L_join):-      𝕄(Φ.a🌵3):+      𝕄(ξ.left🌵):         𝑛·2·1 := Φ.hx( ke ↦ 𝜎2:λ )-      𝕄(Φ.a🌵4):-        𝑛·2·2 := Φ.hx( ke ↦ 𝜎3:λ )+        morphed 𝑛·2·2 := 𝑛·2·1+      𝕄(ξ.right🌵):+        𝑛·2·3 := Φ.hx( ke ↦ 𝜎3:λ )+        morphed 𝑛·2·4 := 𝑛·2·3       joined 𝜎4 := ξ.on🌵 ? 𝜎2 : 𝜎3-      𝑛·2·3 := ⟦ ke ↦ 𝜎4:λ, φ ↦ ke ⟧  # 𝕄(ξ.left🌵), 𝕄(ξ.right🌵)+      𝑛·2·5 := ⟦ ke ↦ 𝜎4:λ, φ ↦ ke ⟧  # 𝕄(ξ.left🌵), 𝕄(ξ.right🌵)+    morphed 𝑛·0·1 := 𝜎4:λ
test-resources/evaluate-packs/joins-an-attribute-named-left-of-a-copy-in-a-fabricated-formation.yaml view
@@ -31,8 +31,19 @@ protocol: |   𝕄(Φ.y):     𝔼(L_fork):-      𝛿1·1 := 𝔻(𝜎1:λ)  # 𝔻(ξ.c)-      fabricated 𝑛·1·1 := ⟦ φ ↦ Φ.hx, ke ↦ 𝜎2:λ:left ⟧ and 𝜎3:λ:left:ke  # ⟦ left🌵 ↦ ⟦ φ ↦ Φ.hx, ke ↦ 𝜎2:λ:left ⟧, right🌵 ↦ 𝜎3:λ:left:ke, φ ↦ ⟦ λ ⤍ L_join, on🌵 ↦ 𝜎1:λ, left🌵 ↦ ρ.left🌵.φ, right🌵 ↦ ρ.right🌵.φ ⟧, ke ↦ ⟦ λ ⤍ L_join, on🌵 ↦ 𝜎1:λ, left🌵 ↦ ρ.left🌵.ke, right🌵 ↦ ρ.right🌵.ke ⟧ ⟧+      𝔻(ξ.c):+        𝛿·1·1 := 𝔻(𝜎1:λ)+      𝛿1·1 := 𝛿·1·1  # 𝔻(ξ.c)+      𝕄(ξ.a):+        morphed 𝑛·1·1 := ⟦ φ ↦ Φ.hx, ke ↦ 𝜎2:λ:left ⟧+      𝕄(ξ.b):+        morphed 𝑛·1·2 := 𝜎3:λ:left:ke+      fabricated 𝑛·1·3 := 𝑛·1·1 and 𝑛·1·2  # ⟦ left🌵 ↦ 𝑛·1·1, right🌵 ↦ 𝑛·1·2, φ ↦ ⟦ λ ⤍ L_join, on🌵 ↦ 𝜎1:λ, left🌵 ↦ ρ.left🌵.φ, right🌵 ↦ ρ.right🌵.φ ⟧, ke ↦ ⟦ λ ⤍ L_join, on🌵 ↦ 𝜎1:λ, left🌵 ↦ ρ.left🌵.ke, right🌵 ↦ ρ.right🌵.ke ⟧ ⟧     𝔼(L_join):+      𝕄(ξ.left🌵):+        morphed 𝑛·2·1 := 𝜎2:λ:left+      𝕄(ξ.right🌵):+        morphed 𝑛·2·2 := 𝜎3:λ:left       joined 𝜎4 := ξ.on🌵 ? 𝜎2 : 𝜎3-      𝑛·2·1 := 𝜎4:λ:left  # 𝕄(ξ.left🌵), 𝕄(ξ.right🌵)+      𝑛·2·3 := 𝜎4:λ:left  # 𝕄(ξ.left🌵), 𝕄(ξ.right🌵)+    morphed 𝑛·0·1 := 𝜎4:λ
+ test-resources/evaluate-packs/joins-two-numbers-into-one-symbol-keeping-the-constant-a-method-holds.yaml view
@@ -0,0 +1,50 @@+# SPDX-FileCopyrightText: Copyright (c) 2025 Objectionary.com+# SPDX-License-Identifier: MIT+---+# yamllint disable rule:line-length+# Both sides are copies of 'number', so both carry its method 'neg', and 'neg'+# holds the library constant '-1'. The left side is the literal '42', the+# right one a number some firing computed. A 'symbolize' line turning every+# datum of a term into a symbol turned the '-1' of each side into a symbol of+# its own, and the join joined the two into one more symbol, though both sides+# hold the same '-1' there (#2131). The line leaves a method of a formation+# holding no λ alone, so the '42' alone becomes a symbol, 𝜎4, the '-1' stays a+# datum on both sides, and the join makes one symbol, 𝜎5, of 𝜎4 and 𝜎2.+symbolic: |+  - λ: L_fork+    dataize:+      𝛿1: $.c+    morph:+      𝑛1: $.a+      𝑛2: $.b+    symbolize:+      𝑛3: 𝑛1+      𝑛4: 𝑛2+    join:+      on: 𝛿1+      left: 𝑛3+      right: 𝑛4+location: 'Q.y'+input: '⟦ number ↦ ⟦ φ ↦ ∅, neg ↦ ⟦ φ ↦ ξ.ρ.times( x ↦ Φ.number( φ ↦ Φ.bytes( φ ↦ ⟦ Δ ⤍ BF-F0-00-00-00-00-00-00 ⟧ ) ) ) ⟧ ⟧, bytes ↦ ⟦ φ ↦ ∅ ⟧, y ↦ ⟦ c ↦ ⟦ λ ⤍ 𝜎3 ⟧, a ↦ Φ.number( φ ↦ Φ.bytes( φ ↦ ⟦ Δ ⤍ 40-45-00-00-00-00-00-00 ⟧ ) ), b ↦ Φ.number( φ ↦ Φ.bytes( φ ↦ ⟦ λ ⤍ 𝜎2 ⟧ ) ), λ ⤍ L_fork ⟧.φ ⟧'+result: '𝜎5:λ:φ'+protocol: |+  𝕄(Φ.y):+    𝔼(L_fork):+      𝔻(ξ.c):+        𝛿·1·1 := 𝔻(𝜎3:λ)+      𝛿1·1 := 𝛿·1·1  # 𝔻(ξ.c)+      𝕄(ξ.a):+        𝑛·1·1 := 42.0+        morphed 𝑛·1·2 := 𝑛·1·1+      𝑛1·1 := 𝑛·1·2  # 𝕄(ξ.a)+      𝕄(ξ.b):+        𝑛·1·3 := Φ.number( φ ↦ Φ.bytes( φ ↦ 𝜎2:λ ) )+        morphed 𝑛·1·4 := 𝑛·1·3+      𝑛2·1 := 𝑛·1·4  # 𝕄(ξ.b)+      symbolized 𝜎4 := 40-45-00-00-00-00-00-00  # 42.0+      𝑛3·1 := ⟦ φ ↦ Φ.bytes( φ ↦ 𝜎4:λ ), neg ↦ ρ.times( x ↦ -1.0 ):φ ⟧  # symbolize(𝑛1)+      𝑛4·1 := 𝑛2·1  # 𝑛2+      joined 𝜎5 := 𝛿1·1 ? 𝜎4 : 𝜎2+      𝑛·1·5 := ⟦ φ ↦ Φ.bytes( φ ↦ 𝜎5:λ ), neg ↦ ρ.times( x ↦ -1.0 ):φ ⟧  # 𝑛3, 𝑛4+    𝑛·0·1 := Φ.bytes( φ ↦ 𝜎5:λ )+    morphed 𝑛·0·2 := 𝑛·0·1
test-resources/evaluate-packs/joins-two-sides-a-stay-brought-to-one-shape.yaml view
@@ -34,11 +34,18 @@ protocol: |   𝕄(Φ.y):     𝔼(L_fork):-      𝛿1·1 := 𝔻(𝜎3:λ)  # 𝔻(ξ.c)-      𝑛1·1 := Φ.number( φ ↦ Φ.bytes( φ ↦ 𝜎1:λ, ρ ↦ ⟦ ρ ↦ Φ.bytes( φ ↦ 𝜎1:λ ), x ↦ ∅ ⟧ ) ):φ  # 𝕄(ξ.a)-      𝑛2·1 := Φ.number( φ ↦ Φ.bytes( φ ↦ 𝜎2:λ ) ):φ  # 𝕄(ξ.b)+      𝔻(ξ.c):+        𝛿·1·1 := 𝔻(𝜎3:λ)+      𝛿1·1 := 𝛿·1·1  # 𝔻(ξ.c)+      𝕄(ξ.a):+        morphed 𝑛·1·1 := Φ.number( φ ↦ Φ.bytes( φ ↦ 𝜎1:λ, ρ ↦ ⟦ ρ ↦ Φ.bytes( φ ↦ 𝜎1:λ ), x ↦ ∅ ⟧ ) ):φ+      𝑛1·1 := 𝑛·1·1  # 𝕄(ξ.a)+      𝕄(ξ.b):+        morphed 𝑛·1·2 := Φ.number( φ ↦ Φ.bytes( φ ↦ 𝜎2:λ ) ):φ+      𝑛2·1 := 𝑛·1·2  # 𝕄(ξ.b)       𝑛3·1 := Φ.number( φ ↦ Φ.bytes( φ ↦ 𝜎1:λ ) ):φ  # stay(𝑛1)       𝑛4·1 := 𝑛2·1  # 𝑛2       joined 𝜎4 := 𝛿1·1 ? 𝜎1 : 𝜎2-      𝑛·1·1 := Φ.number( φ ↦ Φ.bytes( φ ↦ 𝜎4:λ ) ):φ  # 𝑛3, 𝑛4+      𝑛·1·3 := Φ.number( φ ↦ Φ.bytes( φ ↦ 𝜎4:λ ) ):φ  # 𝑛3, 𝑛4     𝑛·0·1 := Φ.number( φ ↦ Φ.bytes( φ ↦ 𝜎4:λ ) )+    morphed 𝑛·0·2 := 𝑛·0·1
test-resources/evaluate-packs/joins-two-sides-differing-in-a-datum-a-symbolize-found-off-the-phi-chain.yaml view
@@ -2,14 +2,14 @@ # SPDX-License-Identifier: MIT --- # yamllint disable rule:line-length-# Both sides hold a bool in the method 'kq', the way the 'rewrite' rules bring-# 'Φ.false' into the shape of a computed bool when EO lowers 'number.floor'.-# The datum of the left one sits in the formation bound to 'if', which is no-# φ of anything, so a 'symbolize' walking the φ chain alone left it a datum-# and the sides never joined (#2008). The line turns every datum of the term-# into a fresh symbol, wherever it stands, so '𝑛3' holds 𝜎3 where '𝑛1' held-# 'FF-', the two sides differ in their symbols alone, and the join makes one-# term of them, joining 𝜎3 with 𝜎2 into 𝜎4.+# Both sides hold a bool in φ, the way the 'rewrite' rules bring 'Φ.false'+# into the shape of a computed bool when EO lowers 'number.floor'. The datum+# of the left one sits in the formation bound to 'if', which is no φ of+# anything, so a 'symbolize' walking the φ chain alone left it a datum and the+# sides never joined (#2008). The line walks every argument of an application+# too, so '𝑛3' holds 𝜎3 where '𝑛1' held 'FF-', the two sides differ in their+# symbols alone, and the join makes one term of them, joining 𝜎3 with 𝜎2 into+# 𝜎4. symbolic: |   - λ: L_fork     dataize:@@ -26,16 +26,24 @@       right: 𝑛4 location: 'Q.y' hide-rho: false-input: '⟦ y ↦ ⟦ c ↦ ⟦ λ ⤍ 𝜎1 ⟧, a ↦ ⟦ φ ↦ ⟦⟧, kq ↦ Φ.bool( if ↦ ⟦ φ ↦ ⟦ Δ ⤍ FF- ⟧ ⟧ ) ⟧, b ↦ ⟦ φ ↦ ⟦⟧, kq ↦ Φ.bool( if ↦ ⟦ φ ↦ ⟦ λ ⤍ 𝜎2 ⟧ ⟧ ) ⟧, λ ⤍ L_fork ⟧.φ ⟧'-result: '⟦⟧'+input: '⟦ bool(if) ↦ ⟦ φ ↦ if ⟧, y ↦ ⟦ c ↦ ⟦ λ ⤍ 𝜎1 ⟧, a ↦ ⟦ φ ↦ Φ.bool( if ↦ ⟦ φ ↦ ⟦ Δ ⤍ FF- ⟧ ⟧ ) ⟧, b ↦ ⟦ φ ↦ Φ.bool( if ↦ ⟦ φ ↦ ⟦ λ ⤍ 𝜎2 ⟧ ⟧ ) ⟧, λ ⤍ L_fork ⟧.φ ⟧'+result: '⟦ if ↦ 𝜎4:λ:φ, φ ↦ if ⟧' protocol: |   𝕄(Φ.y):     𝔼(L_fork):-      𝛿1·1 := 𝔻(𝜎1:λ)  # 𝔻(ξ.c)-      𝑛1·1 := ⟦ φ ↦ ⟦⟧, kq ↦ Φ.bool( if ↦ FF-:Δ:φ ) ⟧  # 𝕄(ξ.a)-      𝑛2·1 := ⟦ φ ↦ ⟦⟧, kq ↦ Φ.bool( if ↦ 𝜎2:λ:φ ) ⟧  # 𝕄(ξ.b)+      𝔻(ξ.c):+        𝛿·1·1 := 𝔻(𝜎1:λ)+      𝛿1·1 := 𝛿·1·1  # 𝔻(ξ.c)+      𝕄(ξ.a):+        morphed 𝑛·1·1 := Φ.bool( if ↦ FF-:Δ:φ ):φ+      𝑛1·1 := 𝑛·1·1  # 𝕄(ξ.a)+      𝕄(ξ.b):+        morphed 𝑛·1·2 := Φ.bool( if ↦ 𝜎2:λ:φ ):φ+      𝑛2·1 := 𝑛·1·2  # 𝕄(ξ.b)       symbolized 𝜎3 := FF--      𝑛3·1 := ⟦ φ ↦ ⟦⟧, kq ↦ Φ.bool( if ↦ 𝜎3:λ:φ ) ⟧  # symbolize(𝑛1)-      𝑛4·1 := ⟦ φ ↦ ⟦⟧, kq ↦ Φ.bool( if ↦ 𝜎2:λ:φ ) ⟧  # 𝑛2+      𝑛3·1 := Φ.bool( if ↦ 𝜎3:λ:φ ):φ  # symbolize(𝑛1)+      𝑛4·1 := 𝑛·1·2  # 𝑛2       joined 𝜎4 := 𝛿1·1 ? 𝜎3 : 𝜎2-      𝑛·1·1 := ⟦ φ ↦ ⟦⟧, kq ↦ Φ.bool( if ↦ 𝜎4:λ:φ ) ⟧  # 𝑛3, 𝑛4+      𝑛·1·3 := Φ.bool( if ↦ 𝜎4:λ:φ ):φ  # 𝑛3, 𝑛4+    𝑛·0·1 := Φ.bool( if ↦ 𝜎4:λ:φ )+    morphed 𝑛·0·2 := 𝑛·0·1
test-resources/evaluate-packs/joins-two-sides-that-differ-only-in-symbols.yaml view
@@ -37,88 +37,136 @@ result: '𝜎5:λ' protocol: |   𝕄(Φ.φ):-    𝑛·0·1 := 5-    𝑛·0·2 := 𝑛·0·1.gt( x ↦ 0 )+    𝑛·0·1 := 5.0+    𝑛·0·2 := 𝑛·0·1.gt( x ↦ 0.0 )     𝔼(L_number_gt):-      𝔻(Φ.a🌵0):-        𝕄(Φ.a🌵0):-          𝑛·1·1 := 5-          𝑛·1·2 := Φ.bytes( φ ↦ 40-14-00-00-00-00-00-00:Δ )+      𝔻(ξ.ρ):+        𝕄(ξ.ρ):+          𝑛·1·1 := 5.0+          morphed 𝑛·1·2 := 𝑛·1·1+        boxed 𝑛·1·3 := 𝑛·1·2.φ+        𝕄(𝑛·1·3):+          𝑛·1·4 := Φ.bytes( φ ↦ 40-14-00-00-00-00-00-00:Δ )+          morphed 𝑛·1·5 := 𝑛·1·4+        boxed 𝑛·1·6 := 𝑛·1·5.φ         𝛿·1·1 := 40-14-00-00-00-00-00-00  # 5.0       𝛿1·1 := 𝛿·1·1  # 𝔻(ξ.ρ)-      𝔻(Φ.a🌵1):-        𝕄(Φ.a🌵1):-          𝑛·1·3 := 0-          𝑛·1·4 := Φ.bytes( φ ↦ 00-00-00-00-00-00-00-00:Δ )+      𝔻(ξ.x):+        𝕄(ξ.x):+          𝑛·1·7 := 0.0+          morphed 𝑛·1·8 := 𝑛·1·7+        boxed 𝑛·1·9 := 𝑛·1·8.φ+        𝕄(𝑛·1·9):+          𝑛·1·10 := Φ.bytes( φ ↦ 00-00-00-00-00-00-00-00:Δ )+          morphed 𝑛·1·11 := 𝑛·1·10+        boxed 𝑛·1·12 := 𝑛·1·11.φ         𝛿·1·2 := 00-00-00-00-00-00-00-00  # 0.0       𝛿2·1 := 𝛿·1·2  # 𝔻(ξ.x)-      𝑛·1·5 := Φ.bool( φ ↦ 𝜎1:λ )  # 𝑛-      𝕄(𝑛·1·5):-        𝑛·1·6 := Φ.bool( φ ↦ 𝜎1:λ )-      𝑛·1·7 := 𝑛·1·6  # 𝕄(𝑛·1·5)-    𝑛·0·3 := 𝑛·1·6.if( then ↦ 1.plus( 2 ).plus( 𝑛·1·3 ) )-    𝑛·0·4 := 𝑛·0·3( else ↦ 3.plus( 4 ).plus( 𝑛·1·3 ) )+      𝑛·1·13 := Φ.bool( φ ↦ 𝜎1:λ )  # 𝑛+      𝕄(𝑛·1·13):+        𝑛·1·14 := Φ.bool( φ ↦ 𝜎1:λ )+        morphed 𝑛·1·15 := 𝑛·1·14+      𝑛·1·16 := 𝑛·1·15  # 𝕄(𝑛·1·13)+    𝑛·0·3 := 𝑛·1·14.if( then ↦ 1.0.plus( 2.0 ).plus( 𝑛·1·7 ) )+    𝑛·0·4 := 𝑛·0·3( else ↦ 3.0.plus( 4.0 ).plus( 𝑛·1·7 ) )     𝔼(L_fork):-      𝔻(Φ.a🌵2):-        𝕄(Φ.a🌵2):+      𝔻(ξ.ρ):+        𝕄(ξ.ρ):           𝑛·2·1 := Φ.bool( φ ↦ 𝜎1:λ )-      𝛿1·2 := 𝔻(𝜎1:λ)  # 𝔻(ξ.ρ)-      𝕄(Φ.a🌵3):-        𝑛·2·2 := 1-        𝑛·2·3 := 𝑛·2·2.plus( x ↦ 2 )+          morphed 𝑛·2·2 := 𝑛·1·16+        boxed 𝑛·2·3 := 𝑛·2·2.φ+        𝛿·2·1 := 𝔻(𝜎1:λ)+      𝛿1·2 := 𝛿·2·1  # 𝔻(ξ.ρ)+      𝕄(ξ.then):+        𝑛·2·4 := 1.0+        𝑛·2·5 := 𝑛·2·4.plus( x ↦ 2.0 )         𝔼(L_number_plus):-          𝔻(Φ.a🌵4):-            𝕄(Φ.a🌵4):-              𝑛·3·1 := 1-              𝑛·3·2 := Φ.bytes( φ ↦ 3F-F0-00-00-00-00-00-00:Δ )+          𝔻(ξ.ρ):+            𝕄(ξ.ρ):+              𝑛·3·1 := 1.0+              morphed 𝑛·3·2 := 𝑛·3·1+            boxed 𝑛·3·3 := 𝑛·3·2.φ+            𝕄(𝑛·3·3):+              𝑛·3·4 := Φ.bytes( φ ↦ 3F-F0-00-00-00-00-00-00:Δ )+              morphed 𝑛·3·5 := 𝑛·3·4+            boxed 𝑛·3·6 := 𝑛·3·5.φ             𝛿·3·1 := 3F-F0-00-00-00-00-00-00  # 1.0           𝛿1·3 := 𝛿·3·1  # 𝔻(ξ.ρ)-          𝔻(Φ.a🌵5):-            𝕄(Φ.a🌵5):-              𝑛·3·3 := 2-              𝑛·3·4 := Φ.bytes( φ ↦ 40-00-00-00-00-00-00-00:Δ )+          𝔻(ξ.x):+            𝕄(ξ.x):+              𝑛·3·7 := 2.0+              morphed 𝑛·3·8 := 𝑛·3·7+            boxed 𝑛·3·9 := 𝑛·3·8.φ+            𝕄(𝑛·3·9):+              𝑛·3·10 := Φ.bytes( φ ↦ 40-00-00-00-00-00-00-00:Δ )+              morphed 𝑛·3·11 := 𝑛·3·10+            boxed 𝑛·3·12 := 𝑛·3·11.φ             𝛿·3·2 := 40-00-00-00-00-00-00-00  # 2.0           𝛿2·3 := 𝛿·3·2  # 𝔻(ξ.x)-          𝑛·3·5 := Φ.number( φ ↦ 𝜎2:λ )  # 𝑛-          𝕄(𝑛·3·5):-            𝑛·3·6 := Φ.number( φ ↦ 𝜎2:λ )-          𝑛·3·7 := 𝑛·3·6  # 𝕄(𝑛·3·5)-        𝑛·2·4 := 𝑛·3·6.plus( x ↦ 𝑛·1·3 )-      𝕄(Φ.a🌵6):-        𝑛·2·5 := 3-        𝑛·2·6 := 𝑛·2·5.plus( x ↦ 4 )+          𝑛·3·13 := Φ.number( φ ↦ 𝜎2:λ )  # 𝑛+          𝕄(𝑛·3·13):+            𝑛·3·14 := Φ.number( φ ↦ 𝜎2:λ )+            morphed 𝑛·3·15 := 𝑛·3·14+          𝑛·3·16 := 𝑛·3·15  # 𝕄(𝑛·3·13)+        𝑛·2·6 := 𝑛·3·14.plus( x ↦ 𝑛·1·7 )+        morphed 𝑛·2·7 := 𝑛·2·6+      𝕄(ξ.else):+        𝑛·2·8 := 3.0+        𝑛·2·9 := 𝑛·2·8.plus( x ↦ 4.0 )         𝔼(L_number_plus):-          𝔻(Φ.a🌵7):-            𝕄(Φ.a🌵7):-              𝑛·4·1 := 3-              𝑛·4·2 := Φ.bytes( φ ↦ 40-08-00-00-00-00-00-00:Δ )+          𝔻(ξ.ρ):+            𝕄(ξ.ρ):+              𝑛·4·1 := 3.0+              morphed 𝑛·4·2 := 𝑛·4·1+            boxed 𝑛·4·3 := 𝑛·4·2.φ+            𝕄(𝑛·4·3):+              𝑛·4·4 := Φ.bytes( φ ↦ 40-08-00-00-00-00-00-00:Δ )+              morphed 𝑛·4·5 := 𝑛·4·4+            boxed 𝑛·4·6 := 𝑛·4·5.φ             𝛿·4·1 := 40-08-00-00-00-00-00-00  # 3.0           𝛿1·4 := 𝛿·4·1  # 𝔻(ξ.ρ)-          𝔻(Φ.a🌵8):-            𝕄(Φ.a🌵8):-              𝑛·4·3 := 4-              𝑛·4·4 := Φ.bytes( φ ↦ 40-10-00-00-00-00-00-00:Δ )+          𝔻(ξ.x):+            𝕄(ξ.x):+              𝑛·4·7 := 4.0+              morphed 𝑛·4·8 := 𝑛·4·7+            boxed 𝑛·4·9 := 𝑛·4·8.φ+            𝕄(𝑛·4·9):+              𝑛·4·10 := Φ.bytes( φ ↦ 40-10-00-00-00-00-00-00:Δ )+              morphed 𝑛·4·11 := 𝑛·4·10+            boxed 𝑛·4·12 := 𝑛·4·11.φ             𝛿·4·2 := 40-10-00-00-00-00-00-00  # 4.0           𝛿2·4 := 𝛿·4·2  # 𝔻(ξ.x)-          𝑛·4·5 := Φ.number( φ ↦ 𝜎3:λ )  # 𝑛-          𝕄(𝑛·4·5):-            𝑛·4·6 := Φ.number( φ ↦ 𝜎3:λ )-          𝑛·4·7 := 𝑛·4·6  # 𝕄(𝑛·4·5)-        𝑛·2·7 := 𝑛·4·6.plus( x ↦ 𝑛·1·3 )+          𝑛·4·13 := Φ.number( φ ↦ 𝜎3:λ )  # 𝑛+          𝕄(𝑛·4·13):+            𝑛·4·14 := Φ.number( φ ↦ 𝜎3:λ )+            morphed 𝑛·4·15 := 𝑛·4·14+          𝑛·4·16 := 𝑛·4·15  # 𝕄(𝑛·4·13)+        𝑛·2·10 := 𝑛·4·14.plus( x ↦ 𝑛·1·7 )+        morphed 𝑛·2·11 := 𝑛·2·10       joined 𝜎4 := 𝛿1·2 ? 𝜎2 : 𝜎3-      𝑛·2·8 := ⟦ x ↦ 𝑛·1·3, λ ⤍ L_number_plus ⟧  # 𝕄(ξ.then), 𝕄(ξ.else)+      𝑛·2·12 := ⟦ x ↦ 𝑛·1·7, λ ⤍ L_number_plus ⟧  # 𝕄(ξ.then), 𝕄(ξ.else)     𝔼(L_number_plus):-      𝔻(Φ.a🌵9):-        𝕄(Φ.a🌵9):+      𝔻(ξ.ρ):+        𝕄(ξ.ρ):           𝑛·5·1 := Φ.number( φ ↦ 𝜎4:λ )-      𝛿1·5 := 𝔻(𝜎4:λ)  # 𝔻(ξ.ρ)-      𝔻(Φ.a🌵10):-        𝕄(Φ.a🌵10):-          𝑛·5·2 := Φ.number( φ ↦ 𝑛·1·4 )-          𝑛·5·3 := Φ.bytes( φ ↦ 00-00-00-00-00-00-00-00:Δ )-        𝛿·5·1 := 00-00-00-00-00-00-00-00  # 0.0-      𝛿2·5 := 𝛿·5·1  # 𝔻(ξ.x)-      𝑛·5·4 := Φ.number( φ ↦ 𝜎5:λ )  # 𝑛-      𝕄(𝑛·5·4):-        𝑛·5·5 := Φ.number( φ ↦ 𝜎5:λ )-      𝑛·5·6 := 𝑛·5·5  # 𝕄(𝑛·5·4)+          morphed 𝑛·5·2 := 𝑛·5·1+        boxed 𝑛·5·3 := 𝑛·5·2.φ+        𝛿·5·1 := 𝔻(𝜎4:λ)+      𝛿1·5 := 𝛿·5·1  # 𝔻(ξ.ρ)+      𝔻(ξ.x):+        𝕄(ξ.x):+          𝑛·5·4 := Φ.number( φ ↦ 𝑛·1·10 )+          morphed 𝑛·5·5 := 𝑛·5·4+        boxed 𝑛·5·6 := 𝑛·5·5.φ+        𝕄(𝑛·5·6):+          𝑛·5·7 := Φ.bytes( φ ↦ 00-00-00-00-00-00-00-00:Δ )+          morphed 𝑛·5·8 := 𝑛·5·7+        boxed 𝑛·5·9 := 𝑛·5·8.φ+        𝛿·5·2 := 00-00-00-00-00-00-00-00  # 0.0+      𝛿2·5 := 𝛿·5·2  # 𝔻(ξ.x)+      𝑛·5·10 := Φ.number( φ ↦ 𝜎5:λ )  # 𝑛+      𝕄(𝑛·5·10):+        𝑛·5·11 := Φ.number( φ ↦ 𝜎5:λ )+        morphed 𝑛·5·12 := 𝑛·5·11+      𝑛·5·13 := 𝑛·5·12  # 𝕄(𝑛·5·10)+    morphed 𝑛·0·5 := 𝜎5:λ
test-resources/evaluate-packs/keeps-the-answer-a-firing-already-gave.yaml view
@@ -15,5 +15,7 @@   𝕄(Φ.x):     𝔼(L_answers):       𝑛·1·1 := L_none:λ  # 𝑛-      𝑛·1·2 := L_none:λ  # 𝕄(𝑛·1·1)+      𝕄(𝑛·1·1):+        morphed 𝑛·1·2 := L_none:λ+      𝑛·1·3 := L_none:λ  # 𝕄(𝑛·1·1)     unanswered L_none  # 𝕄(L_none:λ)
test-resources/evaluate-packs/keeps-the-rho-bindings-a-pack-asks-for.yaml view
@@ -18,6 +18,11 @@ protocol: |   𝕄(Φ.y):     𝔼(L_keep):-      𝑛1·1 := ⟦ λ ⤍ 𝜎1, ρ ↦ ⟦ x(ρ) ↦ 𝜎1:λ ⟧ ⟧  # 𝕄(ξ.x)-      𝑛·1·1 := ⟦ λ ⤍ 𝜎1, ρ ↦ ⟦ x(ρ) ↦ 𝜎1:λ ⟧ ⟧:z  # 𝑛-      𝑛·1·2 := ⟦ λ ⤍ 𝜎1, ρ ↦ ⟦ x(ρ) ↦ 𝜎1:λ ⟧ ⟧:z  # 𝕄(𝑛·1·1)+      𝕄(ξ.x):+        morphed 𝑛·1·1 := ⟦ λ ⤍ 𝜎1, ρ ↦ ⟦ x(ρ) ↦ 𝜎1:λ ⟧ ⟧+      𝑛1·1 := 𝑛·1·1  # 𝕄(ξ.x)+      𝑛·1·2 := ⟦ λ ⤍ 𝜎1, ρ ↦ ⟦ x(ρ) ↦ 𝜎1:λ ⟧ ⟧:z  # 𝑛+      𝕄(𝑛·1·2):+        morphed 𝑛·1·3 := ⟦ λ ⤍ 𝜎1, ρ ↦ ⟦ x(ρ) ↦ 𝜎1:λ ⟧ ⟧:z+      𝑛·1·4 := 𝑛·1·3  # 𝕄(𝑛·1·2)+    morphed 𝑛·0·1 := 𝑛1·1
test-resources/evaluate-packs/leaves-a-bare-symbol-an-evaluate-line-meets-as-it-is.yaml view
@@ -19,7 +19,12 @@ protocol: |   𝕄(Φ.y):     𝔼(L_keep):-      𝑛1·1 := 𝜎4:λ  # 𝕄(ξ.x)+      𝕄(ξ.x):+        morphed 𝑛·1·1 := 𝜎4:λ+      𝑛1·1 := 𝑛·1·1  # 𝕄(ξ.x)       𝑛2·1 := 𝑛1·1  # 𝔼(𝑛1)-      𝑛·1·1 := 𝜎4:λ:φ  # 𝑛-      𝑛·1·2 := 𝜎4:λ:φ  # 𝕄(𝑛·1·1)+      𝑛·1·2 := 𝜎4:λ:φ  # 𝑛+      𝕄(𝑛·1·2):+        morphed 𝑛·1·3 := 𝜎4:λ:φ+      𝑛·1·4 := 𝑛·1·3  # 𝕄(𝑛·1·2)+    morphed 𝑛·0·1 := 𝑛2·1
test-resources/evaluate-packs/mints-a-fresh-symbol-for-every-firing.yaml view
@@ -20,38 +20,59 @@ result: '𝜎2:λ' protocol: |   𝕄(Φ.φ):-    𝑛·0·1 := 5-    𝑛·0·2 := 𝑛·0·1.plus( x ↦ 6 )+    𝑛·0·1 := 5.0+    𝑛·0·2 := 𝑛·0·1.plus( x ↦ 6.0 )     𝔼(L_number_plus):-      𝔻(Φ.a🌵0):-        𝕄(Φ.a🌵0):-          𝑛·1·1 := 5-          𝑛·1·2 := Φ.bytes( φ ↦ 40-14-00-00-00-00-00-00:Δ )+      𝔻(ξ.ρ):+        𝕄(ξ.ρ):+          𝑛·1·1 := 5.0+          morphed 𝑛·1·2 := 𝑛·1·1+        boxed 𝑛·1·3 := 𝑛·1·2.φ+        𝕄(𝑛·1·3):+          𝑛·1·4 := Φ.bytes( φ ↦ 40-14-00-00-00-00-00-00:Δ )+          morphed 𝑛·1·5 := 𝑛·1·4+        boxed 𝑛·1·6 := 𝑛·1·5.φ         𝛿·1·1 := 40-14-00-00-00-00-00-00  # 5.0       𝛿1·1 := 𝛿·1·1  # 𝔻(ξ.ρ)-      𝔻(Φ.a🌵1):-        𝕄(Φ.a🌵1):-          𝑛·1·3 := 6-          𝑛·1·4 := Φ.bytes( φ ↦ 40-18-00-00-00-00-00-00:Δ )+      𝔻(ξ.x):+        𝕄(ξ.x):+          𝑛·1·7 := 6.0+          morphed 𝑛·1·8 := 𝑛·1·7+        boxed 𝑛·1·9 := 𝑛·1·8.φ+        𝕄(𝑛·1·9):+          𝑛·1·10 := Φ.bytes( φ ↦ 40-18-00-00-00-00-00-00:Δ )+          morphed 𝑛·1·11 := 𝑛·1·10+        boxed 𝑛·1·12 := 𝑛·1·11.φ         𝛿·1·2 := 40-18-00-00-00-00-00-00  # 6.0       𝛿2·1 := 𝛿·1·2  # 𝔻(ξ.x)-      𝑛·1·5 := Φ.number( φ ↦ 𝜎1:λ )  # 𝑛-      𝕄(𝑛·1·5):-        𝑛·1·6 := Φ.number( φ ↦ 𝜎1:λ )-      𝑛·1·7 := 𝑛·1·6  # 𝕄(𝑛·1·5)-    𝑛·0·3 := 𝑛·1·6.plus( x ↦ 7 )+      𝑛·1·13 := Φ.number( φ ↦ 𝜎1:λ )  # 𝑛+      𝕄(𝑛·1·13):+        𝑛·1·14 := Φ.number( φ ↦ 𝜎1:λ )+        morphed 𝑛·1·15 := 𝑛·1·14+      𝑛·1·16 := 𝑛·1·15  # 𝕄(𝑛·1·13)+    𝑛·0·3 := 𝑛·1·14.plus( x ↦ 7.0 )     𝔼(L_number_plus):-      𝔻(Φ.a🌵2):-        𝕄(Φ.a🌵2):+      𝔻(ξ.ρ):+        𝕄(ξ.ρ):           𝑛·2·1 := Φ.number( φ ↦ 𝜎1:λ )-      𝛿1·2 := 𝔻(𝜎1:λ)  # 𝔻(ξ.ρ)-      𝔻(Φ.a🌵3):-        𝕄(Φ.a🌵3):-          𝑛·2·2 := 7-          𝑛·2·3 := Φ.bytes( φ ↦ 40-1C-00-00-00-00-00-00:Δ )-        𝛿·2·1 := 40-1C-00-00-00-00-00-00  # 7.0-      𝛿2·2 := 𝛿·2·1  # 𝔻(ξ.x)-      𝑛·2·4 := Φ.number( φ ↦ 𝜎2:λ )  # 𝑛-      𝕄(𝑛·2·4):-        𝑛·2·5 := Φ.number( φ ↦ 𝜎2:λ )-      𝑛·2·6 := 𝑛·2·5  # 𝕄(𝑛·2·4)+          morphed 𝑛·2·2 := 𝑛·1·16+        boxed 𝑛·2·3 := 𝑛·2·2.φ+        𝛿·2·1 := 𝔻(𝜎1:λ)+      𝛿1·2 := 𝛿·2·1  # 𝔻(ξ.ρ)+      𝔻(ξ.x):+        𝕄(ξ.x):+          𝑛·2·4 := 7.0+          morphed 𝑛·2·5 := 𝑛·2·4+        boxed 𝑛·2·6 := 𝑛·2·5.φ+        𝕄(𝑛·2·6):+          𝑛·2·7 := Φ.bytes( φ ↦ 40-1C-00-00-00-00-00-00:Δ )+          morphed 𝑛·2·8 := 𝑛·2·7+        boxed 𝑛·2·9 := 𝑛·2·8.φ+        𝛿·2·2 := 40-1C-00-00-00-00-00-00  # 7.0+      𝛿2·2 := 𝛿·2·2  # 𝔻(ξ.x)+      𝑛·2·10 := Φ.number( φ ↦ 𝜎2:λ )  # 𝑛+      𝕄(𝑛·2·10):+        𝑛·2·11 := Φ.number( φ ↦ 𝜎2:λ )+        morphed 𝑛·2·12 := 𝑛·2·11+      𝑛·2·13 := 𝑛·2·12  # 𝕄(𝑛·2·10)+    morphed 𝑛·0·4 := 𝜎2:λ
test-resources/evaluate-packs/morphs-a-program-that-already-carries-a-symbol.yaml view
@@ -21,19 +21,29 @@ protocol: |   𝕄(Φ.φ):     𝑛·0·1 := Φ.number( φ ↦ 𝜎1:λ )-    𝑛·0·2 := 𝑛·0·1.plus( x ↦ 7 )+    𝑛·0·2 := 𝑛·0·1.plus( x ↦ 7.0 )     𝔼(L_number_plus):-      𝔻(Φ.a🌵0):-        𝕄(Φ.a🌵0):+      𝔻(ξ.ρ):+        𝕄(ξ.ρ):           𝑛·1·1 := Φ.number( φ ↦ 𝜎1:λ )-      𝛿1·1 := 𝔻(𝜎1:λ)  # 𝔻(ξ.ρ)-      𝔻(Φ.a🌵1):-        𝕄(Φ.a🌵1):-          𝑛·1·2 := 7-          𝑛·1·3 := Φ.bytes( φ ↦ 40-1C-00-00-00-00-00-00:Δ )-        𝛿·1·1 := 40-1C-00-00-00-00-00-00  # 7.0-      𝛿2·1 := 𝛿·1·1  # 𝔻(ξ.x)-      𝑛·1·4 := Φ.number( φ ↦ 𝜎2:λ )  # 𝑛-      𝕄(𝑛·1·4):-        𝑛·1·5 := Φ.number( φ ↦ 𝜎2:λ )-      𝑛·1·6 := 𝑛·1·5  # 𝕄(𝑛·1·4)+          morphed 𝑛·1·2 := 𝑛·1·1+        boxed 𝑛·1·3 := 𝑛·1·2.φ+        𝛿·1·1 := 𝔻(𝜎1:λ)+      𝛿1·1 := 𝛿·1·1  # 𝔻(ξ.ρ)+      𝔻(ξ.x):+        𝕄(ξ.x):+          𝑛·1·4 := 7.0+          morphed 𝑛·1·5 := 𝑛·1·4+        boxed 𝑛·1·6 := 𝑛·1·5.φ+        𝕄(𝑛·1·6):+          𝑛·1·7 := Φ.bytes( φ ↦ 40-1C-00-00-00-00-00-00:Δ )+          morphed 𝑛·1·8 := 𝑛·1·7+        boxed 𝑛·1·9 := 𝑛·1·8.φ+        𝛿·1·2 := 40-1C-00-00-00-00-00-00  # 7.0+      𝛿2·1 := 𝛿·1·2  # 𝔻(ξ.x)+      𝑛·1·10 := Φ.number( φ ↦ 𝜎2:λ )  # 𝑛+      𝕄(𝑛·1·10):+        𝑛·1·11 := Φ.number( φ ↦ 𝜎2:λ )+        morphed 𝑛·1·12 := 𝑛·1·11+      𝑛·1·13 := 𝑛·1·12  # 𝕄(𝑛·1·10)+    morphed 𝑛·0·3 := 𝜎2:λ
test-resources/evaluate-packs/morphs-two-branches-spelling-one-term-through-one-firing.yaml view
@@ -40,77 +40,116 @@ result: '⟦ x ↦ 9, λ ⤍ L_number_plus ⟧' protocol: |   𝕄(Φ.φ):-    𝑛·0·1 := 5-    𝑛·0·2 := 𝑛·0·1.gt( x ↦ 0 )+    𝑛·0·1 := 5.0+    𝑛·0·2 := 𝑛·0·1.gt( x ↦ 0.0 )     𝔼(L_number_gt):-      𝔻(Φ.a🌵0):-        𝕄(Φ.a🌵0):-          𝑛·1·1 := 5-          𝑛·1·2 := Φ.bytes( φ ↦ 40-14-00-00-00-00-00-00:Δ )+      𝔻(ξ.ρ):+        𝕄(ξ.ρ):+          𝑛·1·1 := 5.0+          morphed 𝑛·1·2 := 𝑛·1·1+        boxed 𝑛·1·3 := 𝑛·1·2.φ+        𝕄(𝑛·1·3):+          𝑛·1·4 := Φ.bytes( φ ↦ 40-14-00-00-00-00-00-00:Δ )+          morphed 𝑛·1·5 := 𝑛·1·4+        boxed 𝑛·1·6 := 𝑛·1·5.φ         𝛿·1·1 := 40-14-00-00-00-00-00-00  # 5.0       𝛿1·1 := 𝛿·1·1  # 𝔻(ξ.ρ)-      𝔻(Φ.a🌵1):-        𝕄(Φ.a🌵1):-          𝑛·1·3 := 0-          𝑛·1·4 := Φ.bytes( φ ↦ 00-00-00-00-00-00-00-00:Δ )+      𝔻(ξ.x):+        𝕄(ξ.x):+          𝑛·1·7 := 0.0+          morphed 𝑛·1·8 := 𝑛·1·7+        boxed 𝑛·1·9 := 𝑛·1·8.φ+        𝕄(𝑛·1·9):+          𝑛·1·10 := Φ.bytes( φ ↦ 00-00-00-00-00-00-00-00:Δ )+          morphed 𝑛·1·11 := 𝑛·1·10+        boxed 𝑛·1·12 := 𝑛·1·11.φ         𝛿·1·2 := 00-00-00-00-00-00-00-00  # 0.0       𝛿2·1 := 𝛿·1·2  # 𝔻(ξ.x)-      𝑛·1·5 := Φ.bool( φ ↦ 𝜎1:λ )  # 𝑛-      𝕄(𝑛·1·5):-        𝑛·1·6 := Φ.bool( φ ↦ 𝜎1:λ )-      𝑛·1·7 := 𝑛·1·6  # 𝕄(𝑛·1·5)-    𝑛·0·3 := 𝑛·1·6.if( then ↦ 1.plus( 2 ).plus( 3 ) )-    𝑛·0·4 := 𝑛·0·3( else ↦ 1.plus( 2 ).plus( 3 ) )+      𝑛·1·13 := Φ.bool( φ ↦ 𝜎1:λ )  # 𝑛+      𝕄(𝑛·1·13):+        𝑛·1·14 := Φ.bool( φ ↦ 𝜎1:λ )+        morphed 𝑛·1·15 := 𝑛·1·14+      𝑛·1·16 := 𝑛·1·15  # 𝕄(𝑛·1·13)+    𝑛·0·3 := 𝑛·1·14.if( then ↦ 1.0.plus( 2.0 ).plus( 3.0 ) )+    𝑛·0·4 := 𝑛·0·3( else ↦ 1.0.plus( 2.0 ).plus( 3.0 ) )     𝔼(L_fork):-      𝔻(Φ.a🌵2):-        𝕄(Φ.a🌵2):+      𝔻(ξ.ρ):+        𝕄(ξ.ρ):           𝑛·2·1 := Φ.bool( φ ↦ 𝜎1:λ )-      𝛿1·2 := 𝔻(𝜎1:λ)  # 𝔻(ξ.ρ)-      𝕄(Φ.a🌵3):-        𝑛·2·2 := 1-        𝑛·2·3 := 𝑛·2·2.plus( x ↦ 2 )+          morphed 𝑛·2·2 := 𝑛·1·16+        boxed 𝑛·2·3 := 𝑛·2·2.φ+        𝛿·2·1 := 𝔻(𝜎1:λ)+      𝛿1·2 := 𝛿·2·1  # 𝔻(ξ.ρ)+      𝕄(ξ.then):+        𝑛·2·4 := 1.0+        𝑛·2·5 := 𝑛·2·4.plus( x ↦ 2.0 )         𝔼(L_number_plus):-          𝔻(Φ.a🌵4):-            𝕄(Φ.a🌵4):-              𝑛·3·1 := 1-              𝑛·3·2 := Φ.bytes( φ ↦ 3F-F0-00-00-00-00-00-00:Δ )+          𝔻(ξ.ρ):+            𝕄(ξ.ρ):+              𝑛·3·1 := 1.0+              morphed 𝑛·3·2 := 𝑛·3·1+            boxed 𝑛·3·3 := 𝑛·3·2.φ+            𝕄(𝑛·3·3):+              𝑛·3·4 := Φ.bytes( φ ↦ 3F-F0-00-00-00-00-00-00:Δ )+              morphed 𝑛·3·5 := 𝑛·3·4+            boxed 𝑛·3·6 := 𝑛·3·5.φ             𝛿·3·1 := 3F-F0-00-00-00-00-00-00  # 1.0           𝛿1·3 := 𝛿·3·1  # 𝔻(ξ.ρ)-          𝔻(Φ.a🌵5):-            𝕄(Φ.a🌵5):-              𝑛·3·3 := 2-              𝑛·3·4 := Φ.bytes( φ ↦ 40-00-00-00-00-00-00-00:Δ )+          𝔻(ξ.x):+            𝕄(ξ.x):+              𝑛·3·7 := 2.0+              morphed 𝑛·3·8 := 𝑛·3·7+            boxed 𝑛·3·9 := 𝑛·3·8.φ+            𝕄(𝑛·3·9):+              𝑛·3·10 := Φ.bytes( φ ↦ 40-00-00-00-00-00-00-00:Δ )+              morphed 𝑛·3·11 := 𝑛·3·10+            boxed 𝑛·3·12 := 𝑛·3·11.φ             𝛿·3·2 := 40-00-00-00-00-00-00-00  # 2.0           𝛿2·3 := 𝛿·3·2  # 𝔻(ξ.x)-          𝑛·3·5 := Φ.number( φ ↦ 𝜎2:λ )  # 𝑛-          𝕄(𝑛·3·5):-            𝑛·3·6 := Φ.number( φ ↦ 𝜎2:λ )-          𝑛·3·7 := 𝑛·3·6  # 𝕄(𝑛·3·5)-        𝑛·2·4 := 𝑛·3·6.plus( x ↦ 3 )-      𝑛1·2 := 𝑛·2·4  # 𝕄(ξ.then)-      𝕄(Φ.a🌵6):-        𝑛·2·5 := Φ.number( φ ↦ 𝑛·3·2 )-        𝑛·2·6 := 𝑛·2·5.plus( x ↦ 𝑛·3·3 )+          𝑛·3·13 := Φ.number( φ ↦ 𝜎2:λ )  # 𝑛+          𝕄(𝑛·3·13):+            𝑛·3·14 := Φ.number( φ ↦ 𝜎2:λ )+            morphed 𝑛·3·15 := 𝑛·3·14+          𝑛·3·16 := 𝑛·3·15  # 𝕄(𝑛·3·13)+        𝑛·2·6 := 𝑛·3·14.plus( x ↦ 3.0 )+        morphed 𝑛·2·7 := 𝑛·2·6+      𝑛1·2 := 𝑛·2·6  # 𝕄(ξ.then)+      𝕄(ξ.else):+        𝑛·2·8 := Φ.number( φ ↦ 𝑛·3·4 )+        𝑛·2·9 := 𝑛·2·8.plus( x ↦ 𝑛·3·7 )         𝔼(L_number_plus):-          𝑛·4·1 := 𝑛·3·6  # 𝑛-          𝑛·4·2 := 𝑛·3·7  # 𝕄(𝑛·4·1)-        𝑛·2·7 := 𝑛·3·6.plus( x ↦ 3 )-      𝑛2·2 := 𝑛·2·7  # 𝕄(ξ.else)-      𝑛·2·8 := 𝑛·2·7  # 𝑛-      𝑛·2·9 := 𝑛·2·7  # 𝕄(𝑛·2·8)+          𝑛·4·1 := 𝑛·3·14  # 𝑛+          𝑛·4·2 := 𝑛·3·16  # 𝕄(𝑛·4·1)+        𝑛·2·10 := 𝑛·3·14.plus( x ↦ 3.0 )+        morphed 𝑛·2·11 := 𝑛·2·10+      𝑛2·2 := 𝑛·2·10  # 𝕄(ξ.else)+      𝑛·2·12 := 𝑛·2·10  # 𝑛+      𝕄(𝑛·2·12):+        morphed 𝑛·2·13 := 𝑛·2·10+      𝑛·2·14 := 𝑛·2·10  # 𝕄(𝑛·2·12)     𝔼(L_number_plus):-      𝔻(Φ.a🌵7):-        𝕄(Φ.a🌵7):+      𝔻(ξ.ρ):+        𝕄(ξ.ρ):           𝑛·5·1 := Φ.number( φ ↦ 𝜎2:λ )-      𝛿1·5 := 𝔻(𝜎2:λ)  # 𝔻(ξ.ρ)-      𝔻(Φ.a🌵8):-        𝕄(Φ.a🌵8):-          𝑛·5·2 := 3-          𝑛·5·3 := Φ.bytes( φ ↦ 40-08-00-00-00-00-00-00:Δ )-        𝛿·5·1 := 40-08-00-00-00-00-00-00  # 3.0-      𝛿2·5 := 𝛿·5·1  # 𝔻(ξ.x)-      𝑛·5·4 := Φ.number( φ ↦ 𝜎3:λ )  # 𝑛-      𝕄(𝑛·5·4):-        𝑛·5·5 := Φ.number( φ ↦ 𝜎3:λ )-      𝑛·5·6 := 𝑛·5·5  # 𝕄(𝑛·5·4)-    𝑛·0·5 := 𝑛·5·5.plus( x ↦ 9 )+          morphed 𝑛·5·2 := 𝑛·4·2+        boxed 𝑛·5·3 := 𝑛·5·2.φ+        𝛿·5·1 := 𝔻(𝜎2:λ)+      𝛿1·5 := 𝛿·5·1  # 𝔻(ξ.ρ)+      𝔻(ξ.x):+        𝕄(ξ.x):+          𝑛·5·4 := 3.0+          morphed 𝑛·5·5 := 𝑛·5·4+        boxed 𝑛·5·6 := 𝑛·5·5.φ+        𝕄(𝑛·5·6):+          𝑛·5·7 := Φ.bytes( φ ↦ 40-08-00-00-00-00-00-00:Δ )+          morphed 𝑛·5·8 := 𝑛·5·7+        boxed 𝑛·5·9 := 𝑛·5·8.φ+        𝛿·5·2 := 40-08-00-00-00-00-00-00  # 3.0+      𝛿2·5 := 𝛿·5·2  # 𝔻(ξ.x)+      𝑛·5·10 := Φ.number( φ ↦ 𝜎3:λ )  # 𝑛+      𝕄(𝑛·5·10):+        𝑛·5·11 := Φ.number( φ ↦ 𝜎3:λ )+        morphed 𝑛·5·12 := 𝑛·5·11+      𝑛·5·13 := 𝑛·5·12  # 𝕄(𝑛·5·10)+    𝑛·0·5 := 𝑛·5·11.plus( x ↦ 9.0 )+    morphed 𝑛·0·6 := 𝑛·0·5
test-resources/evaluate-packs/nests-the-firing-an-operand-brought-down.yaml view
@@ -21,39 +21,60 @@     φ ↦ 5.plus( 6.plus( 7 ) ).plus( 8 ) ⟧ protocol: |   𝕄(Φ.φ):-    𝑛·0·1 := 5-    𝑛·0·2 := 𝑛·0·1.plus( x ↦ 6.plus( 7 ) )+    𝑛·0·1 := 5.0+    𝑛·0·2 := 𝑛·0·1.plus( x ↦ 6.0.plus( 7.0 ) )     𝔼(L_number_plus):-      𝔻(Φ.a🌵0):-        𝕄(Φ.a🌵0):-          𝑛·1·1 := 5-          𝑛·1·2 := Φ.bytes( φ ↦ 40-14-00-00-00-00-00-00:Δ )+      𝔻(ξ.ρ):+        𝕄(ξ.ρ):+          𝑛·1·1 := 5.0+          morphed 𝑛·1·2 := 𝑛·1·1+        boxed 𝑛·1·3 := 𝑛·1·2.φ+        𝕄(𝑛·1·3):+          𝑛·1·4 := Φ.bytes( φ ↦ 40-14-00-00-00-00-00-00:Δ )+          morphed 𝑛·1·5 := 𝑛·1·4+        boxed 𝑛·1·6 := 𝑛·1·5.φ         𝛿·1·1 := 40-14-00-00-00-00-00-00  # 5.0       𝛿1·1 := 𝛿·1·1  # 𝔻(ξ.ρ)-      𝔻(Φ.a🌵1):-        𝕄(Φ.a🌵1):-          𝑛·1·3 := 6-          𝑛·1·4 := 𝑛·1·3.plus( x ↦ 7 )+      𝔻(ξ.x):+        𝕄(ξ.x):+          𝑛·1·7 := 6.0+          𝑛·1·8 := 𝑛·1·7.plus( x ↦ 7.0 )+          morphed 𝑛·1·9 := 𝑛·1·8         𝔼(L_number_plus):-          𝔻(Φ.a🌵2):-            𝕄(Φ.a🌵2):-              𝑛·2·1 := 6-              𝑛·2·2 := Φ.bytes( φ ↦ 40-18-00-00-00-00-00-00:Δ )+          𝔻(ξ.ρ):+            𝕄(ξ.ρ):+              𝑛·2·1 := 6.0+              morphed 𝑛·2·2 := 𝑛·2·1+            boxed 𝑛·2·3 := 𝑛·2·2.φ+            𝕄(𝑛·2·3):+              𝑛·2·4 := Φ.bytes( φ ↦ 40-18-00-00-00-00-00-00:Δ )+              morphed 𝑛·2·5 := 𝑛·2·4+            boxed 𝑛·2·6 := 𝑛·2·5.φ             𝛿·2·1 := 40-18-00-00-00-00-00-00  # 6.0           𝛿1·2 := 𝛿·2·1  # 𝔻(ξ.ρ)-          𝔻(Φ.a🌵3):-            𝕄(Φ.a🌵3):-              𝑛·2·3 := 7-              𝑛·2·4 := Φ.bytes( φ ↦ 40-1C-00-00-00-00-00-00:Δ )+          𝔻(ξ.x):+            𝕄(ξ.x):+              𝑛·2·7 := 7.0+              morphed 𝑛·2·8 := 𝑛·2·7+            boxed 𝑛·2·9 := 𝑛·2·8.φ+            𝕄(𝑛·2·9):+              𝑛·2·10 := Φ.bytes( φ ↦ 40-1C-00-00-00-00-00-00:Δ )+              morphed 𝑛·2·11 := 𝑛·2·10+            boxed 𝑛·2·12 := 𝑛·2·11.φ             𝛿·2·2 := 40-1C-00-00-00-00-00-00  # 7.0           𝛿2·2 := 𝛿·2·2  # 𝔻(ξ.x)-          𝑛·2·5 := Φ.number( φ ↦ 𝜎1:λ )  # 𝑛-          𝕄(𝑛·2·5):-            𝑛·2·6 := Φ.number( φ ↦ 𝜎1:λ )-          𝑛·2·7 := 𝑛·2·6  # 𝕄(𝑛·2·5)-      𝛿2·1 := 𝔻(𝜎1:λ)  # 𝔻(ξ.x)-      𝑛·1·5 := Φ.number( φ ↦ 𝜎2:λ )  # 𝑛-      𝕄(𝑛·1·5):-        𝑛·1·6 := Φ.number( φ ↦ 𝜎2:λ )-      𝑛·1·7 := 𝑛·1·6  # 𝕄(𝑛·1·5)-    𝑛·0·3 := 𝑛·1·6.plus( x ↦ 8 )+          𝑛·2·13 := Φ.number( φ ↦ 𝜎1:λ )  # 𝑛+          𝕄(𝑛·2·13):+            𝑛·2·14 := Φ.number( φ ↦ 𝜎1:λ )+            morphed 𝑛·2·15 := 𝑛·2·14+          𝑛·2·16 := 𝑛·2·15  # 𝕄(𝑛·2·13)+        boxed 𝑛·1·10 := 𝑛·2·16.φ+        𝛿·1·2 := 𝔻(𝜎1:λ)+      𝛿2·1 := 𝛿·1·2  # 𝔻(ξ.x)+      𝑛·1·11 := Φ.number( φ ↦ 𝜎2:λ )  # 𝑛+      𝕄(𝑛·1·11):+        𝑛·1·12 := Φ.number( φ ↦ 𝜎2:λ )+        morphed 𝑛·1·13 := 𝑛·1·12+      𝑛·1·14 := 𝑛·1·13  # 𝕄(𝑛·1·11)+    𝑛·0·3 := 𝑛·1·12.plus( x ↦ 8.0 )+    morphed 𝑛·0·4 := 𝑛·0·3
test-resources/evaluate-packs/parks-a-firing-whose-deferred-argument-got-stuck.yaml view
@@ -30,13 +30,14 @@ protocol: |   𝕄(Φ.p):     𝔼(L_pair):-      𝕄(Φ.a🌵0):+      𝕄(ξ.c):         𝑛·1·1 := Φ.box( s ↦ 𝜎1:λ )         𝑛·1·2 := 𝑛·1·1( x ↦ ⟦ arg ↦ L_none:λ, λ ⤍ L_outer ⟧.φ )-      𝑛1·1 := 𝑛·1·2  # 𝕄(ξ.c)+        morphed 𝑛·1·3 := 𝑛·1·2+      𝑛1·1 := 𝑛·1·3  # 𝕄(ξ.c)       𝕄(𝑛1.x):         𝔼(L_outer):-          𝔻(Φ.a🌵1):+          𝔻(ξ.arg):             unanswered L_none  # 𝔻(L_none:λ)           stuck L_none       stuck L_none
test-resources/evaluate-packs/parks-a-firing-whose-join-side-got-stuck.yaml view
@@ -21,7 +21,9 @@ protocol: |   𝕄(Φ.y):     𝔼(L_fork):-      𝛿1·1 := 𝔻(𝜎1:λ)  # 𝔻(ξ.c)-      𝕄(Φ.a🌵1):+      𝔻(ξ.c):+        𝛿·1·1 := 𝔻(𝜎1:λ)+      𝛿1·1 := 𝛿·1·1  # 𝔻(ξ.c)+      𝕄(ξ.a):         unanswered L_none  # 𝕄(L_none:λ)       stuck L_none
test-resources/evaluate-packs/parks-a-firing-whose-morph-operand-diverged.yaml view
@@ -26,11 +26,19 @@ protocol: |   𝕄(Φ.y):     𝔼(L_fork):-      𝛿1·1 := 𝔻(𝜎1:λ)  # 𝔻(ξ.c)-      𝕄(Φ.a🌵1):+      𝔻(ξ.c):+        𝛿·1·1 := 𝔻(𝜎1:λ)+      𝛿1·1 := 𝛿·1·1  # 𝔻(ξ.c)+      𝕄(ξ.a):         𝔼(L_fork):-          𝛿1·2 := 𝔻(𝜎2:λ)  # 𝔻(ξ.c)+          𝔻(ξ.c):+            𝛿·2·1 := 𝔻(𝜎2:λ)+          𝛿1·2 := 𝛿·2·1  # 𝔻(ξ.c)+          𝕄(ξ.a):+            morphed 𝑛·2·1 := 01-:Δ           𝑛1·2 := 01-:Δ  # 𝕄(ξ.a)+          𝕄(ξ.b):+            morphed 𝑛·2·2 := 02-:Δ           𝑛2·2 := 02-:Δ  # 𝕄(ξ.b)           diverged 𝑛1·2 vs 𝑛2·2  # they differ at .Δ, and the join names no otherwise       stuck L_fork
test-resources/evaluate-packs/parks-a-firing-whose-morph-operand-got-stuck.yaml view
@@ -25,7 +25,9 @@ protocol: |   𝕄(Φ.y):     𝔼(L_fork):-      𝛿1·1 := 𝔻(𝜎1:λ)  # 𝔻(ξ.c)-      𝕄(Φ.a🌵1):+      𝔻(ξ.c):+        𝛿·1·1 := 𝔻(𝜎1:λ)+      𝛿1·1 := 𝛿·1·1  # 𝔻(ξ.c)+      𝕄(ξ.a):         unanswered L_none  # 𝕄(L_none:λ)       stuck L_none
test-resources/evaluate-packs/parks-a-firing-whose-operand-dataizes-in-circles.yaml view
@@ -24,8 +24,12 @@ protocol: |   𝕄(Φ.x):     𝔼(L_outer):-      𝔻(Φ.a🌵0):-        𝕄(Φ.a🌵0):+      𝔻(ξ.arg):+        𝕄(ξ.arg):           𝑛·1·1 := Φ.cyc( x ↦ ⟦⟧ )-          𝑛·1·2 := Φ.cyc( x ↦ ⟦⟧ )-        looped 𝑛·1·2  # proven+          morphed 𝑛·1·2 := 𝑛·1·1+        boxed 𝑛·1·3 := 𝑛·1·2.φ+        𝕄(𝑛·1·3):+          𝑛·1·4 := Φ.cyc( x ↦ ⟦⟧ )+          morphed 𝑛·1·5 := 𝑛·1·4+        looped 𝑛·1·5  # 𝔻(𝑛·1·3), proven
test-resources/evaluate-packs/parks-a-firing-whose-operand-loops.yaml view
@@ -23,9 +23,11 @@ protocol: |   𝕄(Φ.x):     𝔼(L_outer):-      𝔻(Φ.a🌵0):-        𝕄(Φ.a🌵0):+      𝔻(ξ.arg):+        𝕄(ξ.arg):           𝔼(L_loop):             𝑛·2·1 := L_loop:λ  # 𝑛-            𝑛·2·2 := L_loop:λ  # 𝕄(𝑛·2·1)+            𝕄(𝑛·2·1):+              morphed 𝑛·2·2 := L_loop:λ+            𝑛·2·3 := L_loop:λ  # 𝕄(𝑛·2·1)           looped L_loop:λ  # proven
test-resources/evaluate-packs/parks-a-firing-whose-operand-never-came-down.yaml view
@@ -18,7 +18,7 @@ protocol: |   𝕄(Φ.y):     𝔼(L_pair):-      𝔻(Φ.a🌵0):-        𝕄(Φ.a🌵0):+      𝔻(ξ.x):+        𝕄(ξ.x):           unanswered L_none  # 𝕄(L_none:λ)       stuck L_none
test-resources/evaluate-packs/parks-a-firing-whose-operand-reaches-the-terminator.yaml view
@@ -37,18 +37,23 @@ result: '⟦ n ↦ 1, v ↦ Φ.block( buffer ↦ Φ.bytes( φ ↦ 𝜎1:λ ) ).read, λ ⤍ L_entry ⟧.φ' protocol: |   𝕄(Φ.l🌵.e1):-    𝑛·0·1 := ⟦ n ↦ ∅, v ↦ ∅, λ ⤍ L_entry ⟧( n ↦ 1 )+    𝑛·0·1 := ⟦ n ↦ ∅, v ↦ ∅, λ ⤍ L_entry ⟧( n ↦ 1.0 )     𝑛·0·2 := 𝑛·0·1( v ↦ Φ.block( buffer ↦ Φ.bytes( φ ↦ 𝜎1:λ ) ).read )     𝔼(L_entry):-      𝔻(Φ.a🌵0):-        𝕄(Φ.a🌵0):-          𝑛·1·1 := 1-          𝑛·1·2 := Φ.bytes( φ ↦ 3F-F0-00-00-00-00-00-00:Δ )+      𝔻(ξ.n):+        𝕄(ξ.n):+          𝑛·1·1 := 1.0+          morphed 𝑛·1·2 := 𝑛·1·1+        boxed 𝑛·1·3 := 𝑛·1·2.φ+        𝕄(𝑛·1·3):+          𝑛·1·4 := Φ.bytes( φ ↦ 3F-F0-00-00-00-00-00-00:Δ )+          morphed 𝑛·1·5 := 𝑛·1·4+        boxed 𝑛·1·6 := 𝑛·1·5.φ         𝛿·1·1 := 3F-F0-00-00-00-00-00-00  # 1.0       𝛿1·1 := 𝛿·1·1  # 𝔻(ξ.n)-      𝕄(Φ.a🌵1):-        𝑛·1·3 := Φ.block( buffer ↦ Φ.bytes( φ ↦ 𝜎1:λ ) )-        𝑛·1·4 := Φ.dataized( target ↦ ⊥ )+      𝕄(ξ.v.φ):+        𝑛·1·7 := Φ.block( buffer ↦ Φ.bytes( φ ↦ 𝜎1:λ ) )+        𝑛·1·8 := Φ.dataized( target ↦ ⊥ )         𝔼(L_dataized):           unanswered ⊥  # 𝔻(⊥)           stuck ⊥
test-resources/evaluate-packs/parks-an-entry-that-answers-with-a-firing-of-itself.yaml view
@@ -17,5 +17,7 @@   𝕄(Φ.x):     𝔼(L_loop):       𝑛·1·1 := L_loop:λ  # 𝑛-      𝑛·1·2 := L_loop:λ  # 𝕄(𝑛·1·1)+      𝕄(𝑛·1·1):+        morphed 𝑛·1·2 := L_loop:λ+      𝑛·1·3 := L_loop:λ  # 𝕄(𝑛·1·1)     looped L_loop:λ  # proven
test-resources/evaluate-packs/parks-an-evaluate-line-firing-a-lambda-no-entry-answers.yaml view
@@ -19,6 +19,8 @@ protocol: |   𝕄(Φ.y):     𝔼(L_fork):+      𝕄(ξ.a):+        morphed 𝑛·1·1 := L_none:λ       𝑛1·1 := L_none:λ  # 𝕄(ξ.a)       unanswered L_none  # 𝔼(L_none:λ)       stuck L_none
test-resources/evaluate-packs/parks-an-evaluate-line-over-a-term-that-is-no-formation.yaml view
@@ -28,6 +28,8 @@ protocol: |   𝕄(Φ.y):     𝔼(L_fork):-      𝑛1·1 := Φ.k:φ  # 𝕄(ξ.a)+      𝕄(ξ.a):+        morphed 𝑛·1·1 := Φ.k:φ+      𝑛1·1 := 𝑛·1·1  # 𝕄(ξ.a)       𝑛2·1 := Φ.k  # rewrite(𝑛1)       stuck L_fork
test-resources/evaluate-packs/parks-the-same-dataization-loop-under-partial.yaml view
@@ -25,9 +25,13 @@ protocol: |   𝕄(Φ.x):     𝔼(L_outer):-      𝔻(Φ.a🌵0):-        𝕄(Φ.a🌵0):+      𝔻(ξ.arg):+        𝕄(ξ.arg):           𝑛·1·1 := Φ.cyc( x ↦ ⟦⟧ )-          𝑛·1·2 := Φ.cyc( x ↦ ⟦⟧ )-        looped 𝑛·1·2  # proven+          morphed 𝑛·1·2 := 𝑛·1·1+        boxed 𝑛·1·3 := 𝑛·1·2.φ+        𝕄(𝑛·1·3):+          𝑛·1·4 := Φ.cyc( x ↦ ⟦⟧ )+          morphed 𝑛·1·5 := 𝑛·1·4+        looped 𝑛·1·5  # 𝔻(𝑛·1·3), proven       stuck L_outer
test-resources/evaluate-packs/parks-two-sides-that-do-not-join.yaml view
@@ -22,5 +22,11 @@ protocol: |   𝕄(Φ.y):     𝔼(L_fork):-      𝛿1·1 := 𝔻(𝜎1:λ)  # 𝔻(ξ.c)-      diverged 01-:Δ:φ vs 02-:Δ:φ  # they differ at .φ.Δ, and the join names no otherwise+      𝔻(ξ.c):+        𝛿·1·1 := 𝔻(𝜎1:λ)+      𝛿1·1 := 𝛿·1·1  # 𝔻(ξ.c)+      𝕄(ξ.a):+        morphed 𝑛·1·1 := 01-:Δ:φ+      𝕄(ξ.b):+        morphed 𝑛·1·2 := 02-:Δ:φ+      diverged 𝑛·1·1 vs 𝑛·1·2  # they differ at .φ.Δ, and the join names no otherwise
test-resources/evaluate-packs/passes-a-side-that-is-no-formation-through-an-evaluate-line-to-the-join.yaml view
@@ -39,9 +39,15 @@ protocol: |   𝕄(Φ.y):     𝔼(L_fork):-      𝛿1·1 := 𝔻(𝜎1:λ)  # 𝔻(ξ.c)-      𝑛1·1 := Φ.k:φ  # 𝕄(ξ.a)-      𝑛2·1 := Φ.j:φ  # 𝕄(ξ.b)+      𝔻(ξ.c):+        𝛿·1·1 := 𝔻(𝜎1:λ)+      𝛿1·1 := 𝛿·1·1  # 𝔻(ξ.c)+      𝕄(ξ.a):+        morphed 𝑛·1·1 := Φ.k:φ+      𝑛1·1 := 𝑛·1·1  # 𝕄(ξ.a)+      𝕄(ξ.b):+        morphed 𝑛·1·2 := Φ.j:φ+      𝑛2·1 := 𝑛·1·2  # 𝕄(ξ.b)       𝑛3·1 := Φ.k  # rewrite(𝑛1)       𝑛5·1 := Φ.k  # 𝔼(𝑛3)       𝑛4·1 := Φ.j  # rewrite(𝑛2)
test-resources/evaluate-packs/reduces-a-carried-symbol-through-morphing.yaml view
@@ -15,6 +15,11 @@ protocol: |   𝕄(Φ.y):     𝔼(L_keep):-      𝑛1·1 := 𝜎4:λ  # 𝕄(ξ.x)-      𝑛·1·1 := 𝜎5:λ:z  # 𝑛-      𝑛·1·2 := 𝜎5:λ:z  # 𝕄(𝑛·1·1)+      𝕄(ξ.x):+        morphed 𝑛·1·1 := 𝜎4:λ+      𝑛1·1 := 𝑛·1·1  # 𝕄(ξ.x)+      𝑛·1·2 := 𝜎5:λ:z  # 𝑛+      𝕄(𝑛·1·2):+        morphed 𝑛·1·3 := 𝜎5:λ:z+      𝑛·1·4 := 𝑛·1·3  # 𝕄(𝑛·1·2)+    morphed 𝑛·0·1 := 𝜎5:λ
test-resources/evaluate-packs/spends-a-step-on-every-firing-of-an-evaluate-line.yaml view
@@ -23,7 +23,9 @@ protocol: |   𝕄(Φ.app):     𝔼(L_loop):+      𝕄(ξ.next):+        morphed 𝑛·1·1 := ⟦ next ↦ Φ.loop, λ ⤍ L_loop ⟧       𝑛1·1 := ⟦ next ↦ Φ.loop, λ ⤍ L_loop ⟧  # 𝕄(ξ.next)       𝔼(L_loop):  # 𝔼(𝑛1)-        𝕄(Φ.a🌵1):+        𝕄(ξ.next):           starved 6
test-resources/evaluate-packs/spends-the-step-budget-on-an-entry-answering-itself.yaml view
@@ -17,19 +17,29 @@   𝕄(Φ.x):     𝔼(L_loop):       𝑛·1·1 := L_loop:λ  # 𝑛-      𝑛·1·2 := L_loop:λ  # 𝕄(𝑛·1·1)+      𝕄(𝑛·1·1):+        morphed 𝑛·1·2 := L_loop:λ+      𝑛·1·3 := L_loop:λ  # 𝕄(𝑛·1·1)     𝔼(L_loop):       𝑛·2·1 := L_loop:λ  # 𝑛-      𝑛·2·2 := L_loop:λ  # 𝕄(𝑛·2·1)+      𝕄(𝑛·2·1):+        morphed 𝑛·2·2 := L_loop:λ+      𝑛·2·3 := L_loop:λ  # 𝕄(𝑛·2·1)     𝔼(L_loop):       𝑛·3·1 := L_loop:λ  # 𝑛-      𝑛·3·2 := L_loop:λ  # 𝕄(𝑛·3·1)+      𝕄(𝑛·3·1):+        morphed 𝑛·3·2 := L_loop:λ+      𝑛·3·3 := L_loop:λ  # 𝕄(𝑛·3·1)     𝔼(L_loop):       𝑛·4·1 := L_loop:λ  # 𝑛-      𝑛·4·2 := L_loop:λ  # 𝕄(𝑛·4·1)+      𝕄(𝑛·4·1):+        morphed 𝑛·4·2 := L_loop:λ+      𝑛·4·3 := L_loop:λ  # 𝕄(𝑛·4·1)     𝔼(L_loop):       𝑛·5·1 := L_loop:λ  # 𝑛-      𝑛·5·2 := L_loop:λ  # 𝕄(𝑛·5·1)+      𝕄(𝑛·5·1):+        morphed 𝑛·5·2 := L_loop:λ+      𝑛·5·3 := L_loop:λ  # 𝕄(𝑛·5·1)     𝔼(L_loop):       𝑛·6·1 := L_loop:λ  # 𝑛       𝕄(𝑛·6·1):
test-resources/evaluate-packs/starts-minting-after-the-symbols-the-program-carries.yaml view
@@ -20,22 +20,34 @@ result: '𝜎4:λ' protocol: |   𝕄(Φ.φ):-    𝑛·0·1 := 5-    𝑛·0·2 := 𝑛·0·1.plus( x ↦ 6 )+    𝑛·0·1 := 5.0+    𝑛·0·2 := 𝑛·0·1.plus( x ↦ 6.0 )     𝔼(L_number_plus):-      𝔻(Φ.a🌵0):-        𝕄(Φ.a🌵0):-          𝑛·1·1 := 5-          𝑛·1·2 := Φ.bytes( φ ↦ 40-14-00-00-00-00-00-00:Δ )+      𝔻(ξ.ρ):+        𝕄(ξ.ρ):+          𝑛·1·1 := 5.0+          morphed 𝑛·1·2 := 𝑛·1·1+        boxed 𝑛·1·3 := 𝑛·1·2.φ+        𝕄(𝑛·1·3):+          𝑛·1·4 := Φ.bytes( φ ↦ 40-14-00-00-00-00-00-00:Δ )+          morphed 𝑛·1·5 := 𝑛·1·4+        boxed 𝑛·1·6 := 𝑛·1·5.φ         𝛿·1·1 := 40-14-00-00-00-00-00-00  # 5.0       𝛿1·1 := 𝛿·1·1  # 𝔻(ξ.ρ)-      𝔻(Φ.a🌵1):-        𝕄(Φ.a🌵1):-          𝑛·1·3 := 6-          𝑛·1·4 := Φ.bytes( φ ↦ 40-18-00-00-00-00-00-00:Δ )+      𝔻(ξ.x):+        𝕄(ξ.x):+          𝑛·1·7 := 6.0+          morphed 𝑛·1·8 := 𝑛·1·7+        boxed 𝑛·1·9 := 𝑛·1·8.φ+        𝕄(𝑛·1·9):+          𝑛·1·10 := Φ.bytes( φ ↦ 40-18-00-00-00-00-00-00:Δ )+          morphed 𝑛·1·11 := 𝑛·1·10+        boxed 𝑛·1·12 := 𝑛·1·11.φ         𝛿·1·2 := 40-18-00-00-00-00-00-00  # 6.0       𝛿2·1 := 𝛿·1·2  # 𝔻(ξ.x)-      𝑛·1·5 := Φ.number( φ ↦ 𝜎4:λ )  # 𝑛-      𝕄(𝑛·1·5):-        𝑛·1·6 := Φ.number( φ ↦ 𝜎4:λ )-      𝑛·1·7 := 𝑛·1·6  # 𝕄(𝑛·1·5)+      𝑛·1·13 := Φ.number( φ ↦ 𝜎4:λ )  # 𝑛+      𝕄(𝑛·1·13):+        𝑛·1·14 := Φ.number( φ ↦ 𝜎4:λ )+        morphed 𝑛·1·15 := 𝑛·1·14+      𝑛·1·16 := 𝑛·1·15  # 𝕄(𝑛·1·13)+    morphed 𝑛·0·3 := 𝜎4:λ
test-resources/evaluate-packs/takes-the-answer-of-a-firing-with-the-same-operands.yaml view
@@ -38,25 +38,36 @@ protocol: |   𝕄(Φ.p):     𝔼(L_pair):-      𝕄(Φ.a🌵0):+      𝕄(ξ.a):         𝑛·1·1 := Φ.dataized( target ↦ Φ.x )+        morphed 𝑛·1·2 := 𝑛·1·1       𝑛1·1 := 𝑛·1·1  # 𝕄(ξ.a)-      𝕄(Φ.a🌵1):-        𝑛·1·2 := Φ.dataized( target ↦ Φ.y )-      𝑛2·1 := 𝑛·1·2  # 𝕄(ξ.b)+      𝕄(ξ.b):+        𝑛·1·3 := Φ.dataized( target ↦ Φ.y )+        morphed 𝑛·1·4 := 𝑛·1·3+      𝑛2·1 := 𝑛·1·3  # 𝕄(ξ.b)       𝔼(L_dataized):  # 𝔼(𝑛1)-        𝔻(Φ.a🌵2):+        𝔻(ξ.target):+          𝕄(ξ.target):+            morphed 𝑛·2·1 := 01-:Δ           𝛿·2·1 := 01-         𝛿1·2 := 𝛿·2·1  # 𝔻(ξ.target)-        𝑛·2·1 := 𝜎1:λ  # 𝑛-        𝑛·2·2 := 𝜎1:λ  # 𝕄(𝑛·2·1)-      𝑛3·1 := 𝑛·2·2  # 𝔼(𝑛1)+        𝑛·2·2 := 𝜎1:λ  # 𝑛+        𝕄(𝑛·2·2):+          morphed 𝑛·2·3 := 𝜎1:λ+        𝑛·2·4 := 𝑛·2·3  # 𝕄(𝑛·2·2)+      𝑛3·1 := 𝑛·2·4  # 𝔼(𝑛1)       𝔼(L_dataized):  # 𝔼(𝑛2)-        𝔻(Φ.a🌵3):+        𝔻(ξ.target):+          𝕄(ξ.target):+            morphed 𝑛·3·1 := 01-:Δ           𝛿·3·1 := 01-         𝛿1·3 := 𝛿·3·1  # 𝔻(ξ.target)-        𝑛·3·1 := 𝑛3·1  # 𝑛-        𝑛·3·2 := 𝑛3·1  # 𝕄(𝑛·3·1)-      𝑛4·1 := 𝑛·3·2  # 𝔼(𝑛2)-      𝑛·1·3 := ⟦ a ↦ 𝜎1:λ, b ↦ 𝜎1:λ ⟧:φ  # 𝑛-      𝑛·1·4 := ⟦ a ↦ 𝜎1:λ, b ↦ 𝜎1:λ ⟧:φ  # 𝕄(𝑛·1·3)+        𝑛·3·2 := 𝑛3·1  # 𝑛+        𝑛·3·3 := 𝑛3·1  # 𝕄(𝑛·3·2)+      𝑛4·1 := 𝑛·3·3  # 𝔼(𝑛2)+      𝑛·1·5 := ⟦ a ↦ 𝜎1:λ, b ↦ 𝜎1:λ ⟧:φ  # 𝑛+      𝕄(𝑛·1·5):+        morphed 𝑛·1·6 := ⟦ a ↦ 𝜎1:λ, b ↦ 𝜎1:λ ⟧:φ+      𝑛·1·7 := 𝑛·1·6  # 𝕄(𝑛·1·5)+    morphed 𝑛·0·1 := ⟦ a ↦ 𝜎1:λ, b ↦ 𝜎1:λ ⟧
test-resources/evaluate-packs/takes-the-one-term-both-sides-of-a-join-come-to.yaml view
@@ -34,53 +34,81 @@ result: '𝜎2:λ' protocol: |   𝕄(Φ.φ):-    𝑛·0·1 := 5-    𝑛·0·2 := 𝑛·0·1.gt( x ↦ 0 )+    𝑛·0·1 := 5.0+    𝑛·0·2 := 𝑛·0·1.gt( x ↦ 0.0 )     𝔼(L_number_gt):-      𝔻(Φ.a🌵0):-        𝕄(Φ.a🌵0):-          𝑛·1·1 := 5-          𝑛·1·2 := Φ.bytes( φ ↦ 40-14-00-00-00-00-00-00:Δ )+      𝔻(ξ.ρ):+        𝕄(ξ.ρ):+          𝑛·1·1 := 5.0+          morphed 𝑛·1·2 := 𝑛·1·1+        boxed 𝑛·1·3 := 𝑛·1·2.φ+        𝕄(𝑛·1·3):+          𝑛·1·4 := Φ.bytes( φ ↦ 40-14-00-00-00-00-00-00:Δ )+          morphed 𝑛·1·5 := 𝑛·1·4+        boxed 𝑛·1·6 := 𝑛·1·5.φ         𝛿·1·1 := 40-14-00-00-00-00-00-00  # 5.0       𝛿1·1 := 𝛿·1·1  # 𝔻(ξ.ρ)-      𝔻(Φ.a🌵1):-        𝕄(Φ.a🌵1):-          𝑛·1·3 := 0-          𝑛·1·4 := Φ.bytes( φ ↦ 00-00-00-00-00-00-00-00:Δ )+      𝔻(ξ.x):+        𝕄(ξ.x):+          𝑛·1·7 := 0.0+          morphed 𝑛·1·8 := 𝑛·1·7+        boxed 𝑛·1·9 := 𝑛·1·8.φ+        𝕄(𝑛·1·9):+          𝑛·1·10 := Φ.bytes( φ ↦ 00-00-00-00-00-00-00-00:Δ )+          morphed 𝑛·1·11 := 𝑛·1·10+        boxed 𝑛·1·12 := 𝑛·1·11.φ         𝛿·1·2 := 00-00-00-00-00-00-00-00  # 0.0       𝛿2·1 := 𝛿·1·2  # 𝔻(ξ.x)-      𝑛·1·5 := Φ.bool( φ ↦ 𝜎1:λ )  # 𝑛-      𝕄(𝑛·1·5):-        𝑛·1·6 := Φ.bool( φ ↦ 𝜎1:λ )-      𝑛·1·7 := 𝑛·1·6  # 𝕄(𝑛·1·5)-    𝑛·0·3 := 𝑛·1·6.if( then ↦ 1.plus( 2 ) )-    𝑛·0·4 := 𝑛·0·3( else ↦ 1.plus( 2 ) )+      𝑛·1·13 := Φ.bool( φ ↦ 𝜎1:λ )  # 𝑛+      𝕄(𝑛·1·13):+        𝑛·1·14 := Φ.bool( φ ↦ 𝜎1:λ )+        morphed 𝑛·1·15 := 𝑛·1·14+      𝑛·1·16 := 𝑛·1·15  # 𝕄(𝑛·1·13)+    𝑛·0·3 := 𝑛·1·14.if( then ↦ 1.0.plus( 2.0 ) )+    𝑛·0·4 := 𝑛·0·3( else ↦ 1.0.plus( 2.0 ) )     𝔼(L_fork):-      𝔻(Φ.a🌵2):-        𝕄(Φ.a🌵2):+      𝔻(ξ.ρ):+        𝕄(ξ.ρ):           𝑛·2·1 := Φ.bool( φ ↦ 𝜎1:λ )-      𝛿1·2 := 𝔻(𝜎1:λ)  # 𝔻(ξ.ρ)-      𝕄(Φ.a🌵3):-        𝑛·2·2 := 1-        𝑛·2·3 := 𝑛·2·2.plus( x ↦ 2 )-      𝕄(Φ.a🌵4):-        𝑛·2·4 := 1-        𝑛·2·5 := 𝑛·2·4.plus( x ↦ 2 )-      𝑛·2·6 := 𝑛·2·5  # 𝕄(ξ.then), 𝕄(ξ.else)+          morphed 𝑛·2·2 := 𝑛·1·16+        boxed 𝑛·2·3 := 𝑛·2·2.φ+        𝛿·2·1 := 𝔻(𝜎1:λ)+      𝛿1·2 := 𝛿·2·1  # 𝔻(ξ.ρ)+      𝕄(ξ.then):+        𝑛·2·4 := 1.0+        𝑛·2·5 := 𝑛·2·4.plus( x ↦ 2.0 )+        morphed 𝑛·2·6 := 𝑛·2·5+      𝕄(ξ.else):+        𝑛·2·7 := 1.0+        𝑛·2·8 := 𝑛·2·7.plus( x ↦ 2.0 )+        morphed 𝑛·2·9 := 𝑛·2·8+      𝑛·2·10 := 𝑛·2·8  # 𝕄(ξ.then), 𝕄(ξ.else)     𝔼(L_number_plus):-      𝔻(Φ.a🌵5):-        𝕄(Φ.a🌵5):-          𝑛·3·1 := 1-          𝑛·3·2 := Φ.bytes( φ ↦ 3F-F0-00-00-00-00-00-00:Δ )+      𝔻(ξ.ρ):+        𝕄(ξ.ρ):+          𝑛·3·1 := 1.0+          morphed 𝑛·3·2 := 𝑛·3·1+        boxed 𝑛·3·3 := 𝑛·3·2.φ+        𝕄(𝑛·3·3):+          𝑛·3·4 := Φ.bytes( φ ↦ 3F-F0-00-00-00-00-00-00:Δ )+          morphed 𝑛·3·5 := 𝑛·3·4+        boxed 𝑛·3·6 := 𝑛·3·5.φ         𝛿·3·1 := 3F-F0-00-00-00-00-00-00  # 1.0       𝛿1·3 := 𝛿·3·1  # 𝔻(ξ.ρ)-      𝔻(Φ.a🌵6):-        𝕄(Φ.a🌵6):-          𝑛·3·3 := 2-          𝑛·3·4 := Φ.bytes( φ ↦ 40-00-00-00-00-00-00-00:Δ )+      𝔻(ξ.x):+        𝕄(ξ.x):+          𝑛·3·7 := 2.0+          morphed 𝑛·3·8 := 𝑛·3·7+        boxed 𝑛·3·9 := 𝑛·3·8.φ+        𝕄(𝑛·3·9):+          𝑛·3·10 := Φ.bytes( φ ↦ 40-00-00-00-00-00-00-00:Δ )+          morphed 𝑛·3·11 := 𝑛·3·10+        boxed 𝑛·3·12 := 𝑛·3·11.φ         𝛿·3·2 := 40-00-00-00-00-00-00-00  # 2.0       𝛿2·3 := 𝛿·3·2  # 𝔻(ξ.x)-      𝑛·3·5 := Φ.number( φ ↦ 𝜎2:λ )  # 𝑛-      𝕄(𝑛·3·5):-        𝑛·3·6 := Φ.number( φ ↦ 𝜎2:λ )-      𝑛·3·7 := 𝑛·3·6  # 𝕄(𝑛·3·5)+      𝑛·3·13 := Φ.number( φ ↦ 𝜎2:λ )  # 𝑛+      𝕄(𝑛·3·13):+        𝑛·3·14 := Φ.number( φ ↦ 𝜎2:λ )+        morphed 𝑛·3·15 := 𝑛·3·14+      𝑛·3·16 := 𝑛·3·15  # 𝕄(𝑛·3·13)+    morphed 𝑛·0·5 := 𝜎2:λ
test-resources/evaluate-packs/tells-two-carried-symbols-apart-in-one-firing.yaml view
@@ -23,15 +23,23 @@     𝑛·0·1 := Φ.number( φ ↦ 𝜎2:λ )     𝑛·0·2 := 𝑛·0·1.plus( x ↦ Φ.number( φ ↦ 𝜎5:λ ) )     𝔼(L_number_plus):-      𝔻(Φ.a🌵0):-        𝕄(Φ.a🌵0):+      𝔻(ξ.ρ):+        𝕄(ξ.ρ):           𝑛·1·1 := Φ.number( φ ↦ 𝜎2:λ )-      𝛿1·1 := 𝔻(𝜎2:λ)  # 𝔻(ξ.ρ)-      𝔻(Φ.a🌵1):-        𝕄(Φ.a🌵1):-          𝑛·1·2 := Φ.number( φ ↦ 𝜎5:λ )-      𝛿2·1 := 𝔻(𝜎5:λ)  # 𝔻(ξ.x)-      𝑛·1·3 := Φ.number( φ ↦ 𝜎6:λ )  # 𝑛-      𝕄(𝑛·1·3):-        𝑛·1·4 := Φ.number( φ ↦ 𝜎6:λ )-      𝑛·1·5 := 𝑛·1·4  # 𝕄(𝑛·1·3)+          morphed 𝑛·1·2 := 𝑛·1·1+        boxed 𝑛·1·3 := 𝑛·1·2.φ+        𝛿·1·1 := 𝔻(𝜎2:λ)+      𝛿1·1 := 𝛿·1·1  # 𝔻(ξ.ρ)+      𝔻(ξ.x):+        𝕄(ξ.x):+          𝑛·1·4 := Φ.number( φ ↦ 𝜎5:λ )+          morphed 𝑛·1·5 := 𝑛·1·4+        boxed 𝑛·1·6 := 𝑛·1·5.φ+        𝛿·1·2 := 𝔻(𝜎5:λ)+      𝛿2·1 := 𝛿·1·2  # 𝔻(ξ.x)+      𝑛·1·7 := Φ.number( φ ↦ 𝜎6:λ )  # 𝑛+      𝕄(𝑛·1·7):+        𝑛·1·8 := Φ.number( φ ↦ 𝜎6:λ )+        morphed 𝑛·1·9 := 𝑛·1·8+      𝑛·1·10 := 𝑛·1·9  # 𝕄(𝑛·1·7)+    morphed 𝑛·0·3 := 𝜎6:λ
test-resources/evaluate-packs/tells-two-symbols-of-one-datum-apart-between-firings.yaml view
@@ -35,29 +35,42 @@     𝑛·0·1 := Φ.pair( x ↦ Φ.dataized( Φ.bytes( φ ↦ 𝜎1:λ ) ).φ )     𝑛·0·2 := 𝑛·0·1( y ↦ Φ.dataized( Φ.bytes( φ ↦ 𝜎2:λ ) ).φ )     𝔼(L_pair):-      𝕄(Φ.a🌵0):+      𝕄(ξ.x):         𝑛·1·1 := Φ.dataized( target ↦ Φ.bytes( φ ↦ 𝜎1:λ ) )         𝔼(L_dataized):-          𝔻(Φ.a🌵1):-            𝕄(Φ.a🌵1):+          𝔻(ξ.target):+            𝕄(ξ.target):               𝑛·2·1 := Φ.bytes( φ ↦ 𝜎1:λ )-          𝛿1·2 := 𝔻(𝜎1:λ)  # 𝔻(ξ.target)-          𝑛·2·2 := Φ.bytes( φ ↦ 𝜎3:λ )  # 𝑛-          𝕄(𝑛·2·2):-            𝑛·2·3 := Φ.bytes( φ ↦ 𝜎3:λ )-          𝑛·2·4 := 𝑛·2·3  # 𝕄(𝑛·2·2)-      𝑛1·1 := 𝜎3:λ  # 𝕄(ξ.x)-      𝕄(Φ.a🌵2):-        𝑛·1·2 := Φ.dataized( target ↦ Φ.bytes( φ ↦ 𝜎2:λ ) )+              morphed 𝑛·2·2 := 𝑛·2·1+            boxed 𝑛·2·3 := 𝑛·2·2.φ+            𝛿·2·1 := 𝔻(𝜎1:λ)+          𝛿1·2 := 𝛿·2·1  # 𝔻(ξ.target)+          𝑛·2·4 := Φ.bytes( φ ↦ 𝜎3:λ )  # 𝑛+          𝕄(𝑛·2·4):+            𝑛·2·5 := Φ.bytes( φ ↦ 𝜎3:λ )+            morphed 𝑛·2·6 := 𝑛·2·5+          𝑛·2·7 := 𝑛·2·6  # 𝕄(𝑛·2·4)+        morphed 𝑛·1·2 := 𝜎3:λ+      𝑛1·1 := 𝑛·1·2  # 𝕄(ξ.x)+      𝕄(ξ.y):+        𝑛·1·3 := Φ.dataized( target ↦ Φ.bytes( φ ↦ 𝜎2:λ ) )         𝔼(L_dataized):-          𝔻(Φ.a🌵3):-            𝕄(Φ.a🌵3):+          𝔻(ξ.target):+            𝕄(ξ.target):               𝑛·3·1 := Φ.bytes( φ ↦ 𝜎2:λ )-          𝛿1·3 := 𝔻(𝜎2:λ)  # 𝔻(ξ.target)-          𝑛·3·2 := Φ.bytes( φ ↦ 𝜎4:λ )  # 𝑛-          𝕄(𝑛·3·2):-            𝑛·3·3 := Φ.bytes( φ ↦ 𝜎4:λ )-          𝑛·3·4 := 𝑛·3·3  # 𝕄(𝑛·3·2)-      𝑛2·1 := 𝜎4:λ  # 𝕄(ξ.y)-      𝑛·1·3 := ⟦ x ↦ 𝜎3:λ, y ↦ 𝜎4:λ ⟧:φ  # 𝑛-      𝑛·1·4 := ⟦ x ↦ 𝜎3:λ, y ↦ 𝜎4:λ ⟧:φ  # 𝕄(𝑛·1·3)+              morphed 𝑛·3·2 := 𝑛·3·1+            boxed 𝑛·3·3 := 𝑛·3·2.φ+            𝛿·3·1 := 𝔻(𝜎2:λ)+          𝛿1·3 := 𝛿·3·1  # 𝔻(ξ.target)+          𝑛·3·4 := Φ.bytes( φ ↦ 𝜎4:λ )  # 𝑛+          𝕄(𝑛·3·4):+            𝑛·3·5 := Φ.bytes( φ ↦ 𝜎4:λ )+            morphed 𝑛·3·6 := 𝑛·3·5+          𝑛·3·7 := 𝑛·3·6  # 𝕄(𝑛·3·4)+        morphed 𝑛·1·4 := 𝜎4:λ+      𝑛2·1 := 𝑛·1·4  # 𝕄(ξ.y)+      𝑛·1·5 := ⟦ x ↦ 𝜎3:λ, y ↦ 𝜎4:λ ⟧:φ  # 𝑛+      𝕄(𝑛·1·5):+        morphed 𝑛·1·6 := ⟦ x ↦ 𝜎3:λ, y ↦ 𝜎4:λ ⟧:φ+      𝑛·1·7 := 𝑛·1·6  # 𝕄(𝑛·1·5)+    morphed 𝑛·0·3 := ⟦ x ↦ 𝜎3:λ, y ↦ 𝜎4:λ ⟧
test-resources/evaluate-packs/tells-why-a-join-fabricated-nothing-of-a-side-that-is-no-formation.yaml view
@@ -32,7 +32,11 @@ protocol: |   𝕄(Φ.y):     𝔼(L_fork):-      𝛿1·1 := 𝔻(𝜎1:λ)  # 𝔻(ξ.c)-      𝑛1·1 := Φ.k:φ  # 𝕄(ξ.a)+      𝔻(ξ.c):+        𝛿·1·1 := 𝔻(𝜎1:λ)+      𝛿1·1 := 𝛿·1·1  # 𝔻(ξ.c)+      𝕄(ξ.a):+        morphed 𝑛·1·1 := Φ.k:φ+      𝑛1·1 := 𝑛·1·1  # 𝕄(ξ.a)       𝑛2·1 := Φ.k  # rewrite(𝑛1)       diverged 𝑛1·1 vs 𝑛2·1  # the right side is no formation
test-resources/evaluate-packs/writes-no-joined-line-for-a-join-the-memo-answered.yaml view
@@ -40,113 +40,168 @@ result: '𝜎6:λ' protocol: |   𝕄(Φ.φ):-    𝑛·0·1 := 5-    𝑛·0·2 := 𝑛·0·1.gt( x ↦ 0 )+    𝑛·0·1 := 5.0+    𝑛·0·2 := 𝑛·0·1.gt( x ↦ 0.0 )     𝔼(L_number_gt):-      𝔻(Φ.a🌵0):-        𝕄(Φ.a🌵0):-          𝑛·1·1 := 5-          𝑛·1·2 := Φ.bytes( φ ↦ 40-14-00-00-00-00-00-00:Δ )+      𝔻(ξ.ρ):+        𝕄(ξ.ρ):+          𝑛·1·1 := 5.0+          morphed 𝑛·1·2 := 𝑛·1·1+        boxed 𝑛·1·3 := 𝑛·1·2.φ+        𝕄(𝑛·1·3):+          𝑛·1·4 := Φ.bytes( φ ↦ 40-14-00-00-00-00-00-00:Δ )+          morphed 𝑛·1·5 := 𝑛·1·4+        boxed 𝑛·1·6 := 𝑛·1·5.φ         𝛿·1·1 := 40-14-00-00-00-00-00-00  # 5.0       𝛿1·1 := 𝛿·1·1  # 𝔻(ξ.ρ)-      𝔻(Φ.a🌵1):-        𝕄(Φ.a🌵1):-          𝑛·1·3 := 0-          𝑛·1·4 := Φ.bytes( φ ↦ 00-00-00-00-00-00-00-00:Δ )+      𝔻(ξ.x):+        𝕄(ξ.x):+          𝑛·1·7 := 0.0+          morphed 𝑛·1·8 := 𝑛·1·7+        boxed 𝑛·1·9 := 𝑛·1·8.φ+        𝕄(𝑛·1·9):+          𝑛·1·10 := Φ.bytes( φ ↦ 00-00-00-00-00-00-00-00:Δ )+          morphed 𝑛·1·11 := 𝑛·1·10+        boxed 𝑛·1·12 := 𝑛·1·11.φ         𝛿·1·2 := 00-00-00-00-00-00-00-00  # 0.0       𝛿2·1 := 𝛿·1·2  # 𝔻(ξ.x)-      𝑛·1·5 := Φ.bool( φ ↦ 𝜎1:λ )  # 𝑛-      𝕄(𝑛·1·5):-        𝑛·1·6 := Φ.bool( φ ↦ 𝜎1:λ )-      𝑛·1·7 := 𝑛·1·6  # 𝕄(𝑛·1·5)-    𝑛·0·3 := 𝑛·1·6.if( then ↦ 1.plus( 2 ).plus( 𝑛·1·3 ) )-    𝑛·0·4 := 𝑛·0·3( else ↦ 3.plus( 4 ).plus( 𝑛·1·3 ) )+      𝑛·1·13 := Φ.bool( φ ↦ 𝜎1:λ )  # 𝑛+      𝕄(𝑛·1·13):+        𝑛·1·14 := Φ.bool( φ ↦ 𝜎1:λ )+        morphed 𝑛·1·15 := 𝑛·1·14+      𝑛·1·16 := 𝑛·1·15  # 𝕄(𝑛·1·13)+    𝑛·0·3 := 𝑛·1·14.if( then ↦ 1.0.plus( 2.0 ).plus( 𝑛·1·7 ) )+    𝑛·0·4 := 𝑛·0·3( else ↦ 3.0.plus( 4.0 ).plus( 𝑛·1·7 ) )     𝔼(L_fork):-      𝔻(Φ.a🌵2):-        𝕄(Φ.a🌵2):+      𝔻(ξ.ρ):+        𝕄(ξ.ρ):           𝑛·2·1 := Φ.bool( φ ↦ 𝜎1:λ )-      𝛿1·2 := 𝔻(𝜎1:λ)  # 𝔻(ξ.ρ)-      𝕄(Φ.a🌵3):-        𝑛·2·2 := 1-        𝑛·2·3 := 𝑛·2·2.plus( x ↦ 2 )+          morphed 𝑛·2·2 := 𝑛·1·16+        boxed 𝑛·2·3 := 𝑛·2·2.φ+        𝛿·2·1 := 𝔻(𝜎1:λ)+      𝛿1·2 := 𝛿·2·1  # 𝔻(ξ.ρ)+      𝕄(ξ.then):+        𝑛·2·4 := 1.0+        𝑛·2·5 := 𝑛·2·4.plus( x ↦ 2.0 )         𝔼(L_number_plus):-          𝔻(Φ.a🌵4):-            𝕄(Φ.a🌵4):-              𝑛·3·1 := 1-              𝑛·3·2 := Φ.bytes( φ ↦ 3F-F0-00-00-00-00-00-00:Δ )+          𝔻(ξ.ρ):+            𝕄(ξ.ρ):+              𝑛·3·1 := 1.0+              morphed 𝑛·3·2 := 𝑛·3·1+            boxed 𝑛·3·3 := 𝑛·3·2.φ+            𝕄(𝑛·3·3):+              𝑛·3·4 := Φ.bytes( φ ↦ 3F-F0-00-00-00-00-00-00:Δ )+              morphed 𝑛·3·5 := 𝑛·3·4+            boxed 𝑛·3·6 := 𝑛·3·5.φ             𝛿·3·1 := 3F-F0-00-00-00-00-00-00  # 1.0           𝛿1·3 := 𝛿·3·1  # 𝔻(ξ.ρ)-          𝔻(Φ.a🌵5):-            𝕄(Φ.a🌵5):-              𝑛·3·3 := 2-              𝑛·3·4 := Φ.bytes( φ ↦ 40-00-00-00-00-00-00-00:Δ )+          𝔻(ξ.x):+            𝕄(ξ.x):+              𝑛·3·7 := 2.0+              morphed 𝑛·3·8 := 𝑛·3·7+            boxed 𝑛·3·9 := 𝑛·3·8.φ+            𝕄(𝑛·3·9):+              𝑛·3·10 := Φ.bytes( φ ↦ 40-00-00-00-00-00-00-00:Δ )+              morphed 𝑛·3·11 := 𝑛·3·10+            boxed 𝑛·3·12 := 𝑛·3·11.φ             𝛿·3·2 := 40-00-00-00-00-00-00-00  # 2.0           𝛿2·3 := 𝛿·3·2  # 𝔻(ξ.x)-          𝑛·3·5 := Φ.number( φ ↦ 𝜎2:λ )  # 𝑛-          𝕄(𝑛·3·5):-            𝑛·3·6 := Φ.number( φ ↦ 𝜎2:λ )-          𝑛·3·7 := 𝑛·3·6  # 𝕄(𝑛·3·5)-        𝑛·2·4 := 𝑛·3·6.plus( x ↦ 𝑛·1·3 )-      𝕄(Φ.a🌵6):-        𝑛·2·5 := 3-        𝑛·2·6 := 𝑛·2·5.plus( x ↦ 4 )+          𝑛·3·13 := Φ.number( φ ↦ 𝜎2:λ )  # 𝑛+          𝕄(𝑛·3·13):+            𝑛·3·14 := Φ.number( φ ↦ 𝜎2:λ )+            morphed 𝑛·3·15 := 𝑛·3·14+          𝑛·3·16 := 𝑛·3·15  # 𝕄(𝑛·3·13)+        𝑛·2·6 := 𝑛·3·14.plus( x ↦ 𝑛·1·7 )+        morphed 𝑛·2·7 := 𝑛·2·6+      𝕄(ξ.else):+        𝑛·2·8 := 3.0+        𝑛·2·9 := 𝑛·2·8.plus( x ↦ 4.0 )         𝔼(L_number_plus):-          𝔻(Φ.a🌵7):-            𝕄(Φ.a🌵7):-              𝑛·4·1 := 3-              𝑛·4·2 := Φ.bytes( φ ↦ 40-08-00-00-00-00-00-00:Δ )+          𝔻(ξ.ρ):+            𝕄(ξ.ρ):+              𝑛·4·1 := 3.0+              morphed 𝑛·4·2 := 𝑛·4·1+            boxed 𝑛·4·3 := 𝑛·4·2.φ+            𝕄(𝑛·4·3):+              𝑛·4·4 := Φ.bytes( φ ↦ 40-08-00-00-00-00-00-00:Δ )+              morphed 𝑛·4·5 := 𝑛·4·4+            boxed 𝑛·4·6 := 𝑛·4·5.φ             𝛿·4·1 := 40-08-00-00-00-00-00-00  # 3.0           𝛿1·4 := 𝛿·4·1  # 𝔻(ξ.ρ)-          𝔻(Φ.a🌵8):-            𝕄(Φ.a🌵8):-              𝑛·4·3 := 4-              𝑛·4·4 := Φ.bytes( φ ↦ 40-10-00-00-00-00-00-00:Δ )+          𝔻(ξ.x):+            𝕄(ξ.x):+              𝑛·4·7 := 4.0+              morphed 𝑛·4·8 := 𝑛·4·7+            boxed 𝑛·4·9 := 𝑛·4·8.φ+            𝕄(𝑛·4·9):+              𝑛·4·10 := Φ.bytes( φ ↦ 40-10-00-00-00-00-00-00:Δ )+              morphed 𝑛·4·11 := 𝑛·4·10+            boxed 𝑛·4·12 := 𝑛·4·11.φ             𝛿·4·2 := 40-10-00-00-00-00-00-00  # 4.0           𝛿2·4 := 𝛿·4·2  # 𝔻(ξ.x)-          𝑛·4·5 := Φ.number( φ ↦ 𝜎3:λ )  # 𝑛-          𝕄(𝑛·4·5):-            𝑛·4·6 := Φ.number( φ ↦ 𝜎3:λ )-          𝑛·4·7 := 𝑛·4·6  # 𝕄(𝑛·4·5)-        𝑛·2·7 := 𝑛·4·6.plus( x ↦ 𝑛·1·3 )+          𝑛·4·13 := Φ.number( φ ↦ 𝜎3:λ )  # 𝑛+          𝕄(𝑛·4·13):+            𝑛·4·14 := Φ.number( φ ↦ 𝜎3:λ )+            morphed 𝑛·4·15 := 𝑛·4·14+          𝑛·4·16 := 𝑛·4·15  # 𝕄(𝑛·4·13)+        𝑛·2·10 := 𝑛·4·14.plus( x ↦ 𝑛·1·7 )+        morphed 𝑛·2·11 := 𝑛·2·10       joined 𝜎4 := 𝛿1·2 ? 𝜎2 : 𝜎3-      𝑛·2·8 := ⟦ x ↦ 𝑛·1·3, λ ⤍ L_number_plus ⟧  # 𝕄(ξ.then), 𝕄(ξ.else)+      𝑛·2·12 := ⟦ x ↦ 𝑛·1·7, λ ⤍ L_number_plus ⟧  # 𝕄(ξ.then), 𝕄(ξ.else)     𝔼(L_number_plus):-      𝔻(Φ.a🌵9):-        𝕄(Φ.a🌵9):+      𝔻(ξ.ρ):+        𝕄(ξ.ρ):           𝑛·5·1 := Φ.number( φ ↦ 𝜎4:λ )-      𝛿1·5 := 𝔻(𝜎4:λ)  # 𝔻(ξ.ρ)-      𝔻(Φ.a🌵10):-        𝕄(Φ.a🌵10):-          𝑛·5·2 := Φ.number( φ ↦ 𝑛·1·4 )-          𝑛·5·3 := Φ.bytes( φ ↦ 00-00-00-00-00-00-00-00:Δ )-        𝛿·5·1 := 00-00-00-00-00-00-00-00  # 0.0-      𝛿2·5 := 𝛿·5·1  # 𝔻(ξ.x)-      𝑛·5·4 := Φ.number( φ ↦ 𝜎5:λ )  # 𝑛-      𝕄(𝑛·5·4):-        𝑛·5·5 := Φ.number( φ ↦ 𝜎5:λ )-      𝑛·5·6 := 𝑛·5·5  # 𝕄(𝑛·5·4)-    𝑛·0·5 := 𝑛·5·5.plus( x ↦ Φ.f )+          morphed 𝑛·5·2 := 𝑛·5·1+        boxed 𝑛·5·3 := 𝑛·5·2.φ+        𝛿·5·1 := 𝔻(𝜎4:λ)+      𝛿1·5 := 𝛿·5·1  # 𝔻(ξ.ρ)+      𝔻(ξ.x):+        𝕄(ξ.x):+          𝑛·5·4 := Φ.number( φ ↦ 𝑛·1·10 )+          morphed 𝑛·5·5 := 𝑛·5·4+        boxed 𝑛·5·6 := 𝑛·5·5.φ+        𝕄(𝑛·5·6):+          𝑛·5·7 := Φ.bytes( φ ↦ 00-00-00-00-00-00-00-00:Δ )+          morphed 𝑛·5·8 := 𝑛·5·7+        boxed 𝑛·5·9 := 𝑛·5·8.φ+        𝛿·5·2 := 00-00-00-00-00-00-00-00  # 0.0+      𝛿2·5 := 𝛿·5·2  # 𝔻(ξ.x)+      𝑛·5·10 := Φ.number( φ ↦ 𝜎5:λ )  # 𝑛+      𝕄(𝑛·5·10):+        𝑛·5·11 := Φ.number( φ ↦ 𝜎5:λ )+        morphed 𝑛·5·12 := 𝑛·5·11+      𝑛·5·13 := 𝑛·5·12  # 𝕄(𝑛·5·10)+    𝑛·0·5 := 𝑛·5·11.plus( x ↦ Φ.f )     𝔼(L_number_plus):-      𝔻(Φ.a🌵11):-        𝕄(Φ.a🌵11):+      𝔻(ξ.ρ):+        𝕄(ξ.ρ):           𝑛·6·1 := Φ.number( φ ↦ 𝜎5:λ )-      𝛿1·6 := 𝔻(𝜎5:λ)  # 𝔻(ξ.ρ)-      𝔻(Φ.a🌵12):-        𝕄(Φ.a🌵12):-          𝑛·6·2 := Φ.number( φ ↦ 𝑛·1·2 )-          𝑛·6·3 := 𝑛·6·2.gt( x ↦ 𝑛·5·2 )+          morphed 𝑛·6·2 := 𝑛·5·13+        boxed 𝑛·6·3 := 𝑛·6·2.φ+        𝛿·6·1 := 𝔻(𝜎5:λ)+      𝛿1·6 := 𝛿·6·1  # 𝔻(ξ.ρ)+      𝔻(ξ.x):+        𝕄(ξ.x):+          𝑛·6·4 := Φ.number( φ ↦ 𝑛·1·4 )+          𝑛·6·5 := 𝑛·6·4.gt( x ↦ 𝑛·5·4 )           𝔼(L_number_gt):             𝑛·7·1 := 𝑛·2·1  # 𝑛-            𝑛·7·2 := 𝑛·1·7  # 𝕄(𝑛·7·1)-          𝑛·6·4 := 𝑛·2·1.if( then ↦ 𝑛·3·1.plus( 𝑛·3·3 ).plus( 𝑛·5·2 ) )-          𝑛·6·5 := 𝑛·6·4( else ↦ 𝑛·4·1.plus( 𝑛·4·3 ).plus( 𝑛·5·2 ) )+            𝑛·7·2 := 𝑛·1·16  # 𝕄(𝑛·7·1)+          𝑛·6·6 := 𝑛·2·1.if( then ↦ 𝑛·3·1.plus( 𝑛·3·7 ).plus( 𝑛·5·4 ) )+          𝑛·6·7 := 𝑛·6·6( else ↦ 𝑛·4·1.plus( 𝑛·4·7 ).plus( 𝑛·5·4 ) )+          morphed 𝑛·6·8 := 𝑛·6·7         𝔼(L_fork):-          𝑛·8·1 := ⟦ x ↦ 𝑛·5·2, λ ⤍ L_number_plus ⟧  # 𝕄(ξ.then), 𝕄(ξ.else)+          𝑛·8·1 := ⟦ x ↦ 𝑛·5·4, λ ⤍ L_number_plus ⟧  # 𝕄(ξ.then), 𝕄(ξ.else)         𝔼(L_number_plus):           𝑛·9·1 := 𝑛·6·1  # 𝑛-          𝑛·9·2 := 𝑛·5·6  # 𝕄(𝑛·9·1)-      𝛿2·6 := 𝔻(𝜎5:λ)  # 𝔻(ξ.x)-      𝑛·6·6 := Φ.number( φ ↦ 𝜎6:λ )  # 𝑛-      𝕄(𝑛·6·6):-        𝑛·6·7 := Φ.number( φ ↦ 𝜎6:λ )-      𝑛·6·8 := 𝑛·6·7  # 𝕄(𝑛·6·6)+          𝑛·9·2 := 𝑛·5·13  # 𝕄(𝑛·9·1)+        boxed 𝑛·6·9 := 𝑛·9·2.φ+        𝛿·6·2 := 𝔻(𝜎5:λ)+      𝛿2·6 := 𝛿·6·2  # 𝔻(ξ.x)+      𝑛·6·10 := Φ.number( φ ↦ 𝜎6:λ )  # 𝑛+      𝕄(𝑛·6·10):+        𝑛·6·11 := Φ.number( φ ↦ 𝜎6:λ )+        morphed 𝑛·6·12 := 𝑛·6·11+      𝑛·6·13 := 𝑛·6·12  # 𝕄(𝑛·6·10)+    morphed 𝑛·0·6 := 𝜎6:λ
test-resources/latex-packs/one-binding-sugar.yaml view
@@ -6,5 +6,5 @@   [[ x -> [[ y -> 42 ]] ]] result: |-   \begin{phiquation}-  |42| : |y| : |x|{.}+  |42.0| : |y| : |x|{.}   \end{phiquation}
test-resources/latex-packs/primitives.yaml view
@@ -6,5 +6,5 @@   [[ n -> 42, s -> "hello" ]] result: |-   \begin{phiquation}-  [[ |n| -> |42|, |s| -> "hello" ]]{.}+  [[ |n| -> |42.0|, |s| -> "hello" ]]{.}   \end{phiquation}
+ test-resources/rewriter-packs/custom/fast-replacing-keeps-bindings-around-an-empty-middle.yaml view
@@ -0,0 +1,10 @@+# SPDX-FileCopyrightText: Copyright (c) 2025 Objectionary.com+# SPDX-License-Identifier: MIT+---+input: '[[ a -> [[ ]], b -> [[ ]] ]]'+output: '⟦ y ↦ ξ, a ↦ ⟦⟧, b ↦ ⟦⟧ ⟧'+rules:+  custom:+    - name: insert-y+      pattern: '[[ !B1, !B2 ]]'+      result: '[[ !B1, y -> $, !B2 ]]'
+ test-resources/rewriter-packs/custom/fast-replacing-keeps-bindings-with-a-condition-and-empty-middle.yaml view
@@ -0,0 +1,12 @@+# SPDX-FileCopyrightText: Copyright (c) 2025 Objectionary.com+# SPDX-License-Identifier: MIT+---+input: '[[ a -> [[ ]], b -> [[ ]] ]]'+output: '⟦ y ↦ ξ, a ↦ ⟦⟧, b ↦ ⟦⟧ ⟧'+rules:+  custom:+    - name: insert-y+      pattern: '[[ !B1, !B2 ]]'+      result: '[[ !B1, y -> $, !B2 ]]'+      when:+        eq: [1, 1]
test-resources/rewriter-packs/custom/sed.yaml view
@@ -33,7 +33,7 @@           args: ['!e10', '"s/([0-9]+)/$1!/g"']         - meta: '!e21'           function: sed-          args: ['!e11', '"s/(hi) \\((jeff)\\)/$2 $1"']+          args: ['!e11', '"s/(hi) \\((jeff)\\)/$2 $1/"']         - meta: '!e5'           function: sed           args: ['!e1', '"s/\\s/(/g"']
test/BytesSpec.hs view
@@ -50,15 +50,15 @@    describe "numToBts/btsToNum round trip" $     forM_-      [ ("normal integer", 42, (== Left 42))-      , ("normal fraction", 3.5, (== Right 3.5))-      , ("zero", 0.0, (== Left 0))-      , ("negative integer", -2, (== Left (-2)))-      , ("negative fraction", -0.25, (== Right (-0.25)))-      , ("NaN", 0 / 0, either (const False) isNaN)-      , ("positive infinity", 1 / 0, either (const False) (\num -> isInfinite num && num > 0))-      , ("negative infinity", -(1 / 0), either (const False) (\num -> isInfinite num && num < 0))-      , ("negative zero", -0.0, either (const False) isNegativeZero)+      [ ("normal integer", 42, (== 42))+      , ("normal fraction", 3.5, (== 3.5))+      , ("zero", 0.0, (== 0))+      , ("negative integer", -2, (== -2))+      , ("negative fraction", -0.25, (== -0.25))+      , ("NaN", 0 / 0, isNaN)+      , ("positive infinity", 1 / 0, \num -> isInfinite num && num > 0)+      , ("negative infinity", -(1 / 0), \num -> isInfinite num && num < 0)+      , ("negative zero", -0.0, isNegativeZero)       ]       (\(desc, num, predicate) -> it desc (btsToNum (numToBts num) `shouldSatisfy` predicate)) @@ -92,9 +92,9 @@    describe "btsToNum on the named non-finite patterns" $     forM_-      [ ("NaN", NfNan, either (const False) isNaN)-      , ("positive infinity", NfPinf, either (const False) (\num -> isInfinite num && num > 0))-      , ("negative infinity", NfNinf, either (const False) (\num -> isInfinite num && num < 0))+      [ ("NaN", NfNan, isNaN)+      , ("positive infinity", NfPinf, \num -> isInfinite num && num > 0)+      , ("negative infinity", NfNinf, \num -> isInfinite num && num < 0)       ]       (\(desc, value, predicate) -> it desc (btsToNum (nonFiniteBts value) `shouldSatisfy` predicate)) 
test/CLISpec.hs view
@@ -202,3237 +202,3592 @@       )    it "fails when both --pin and --pin-file are given" $-    withStdin "[[ ]]" $-      testCLIFailed-        ["--pin=" ++ showVersion version, "--pin-file=pinned-version.txt", "rewrite"]-        ["[ERROR]"]--  describe "--hide-rho" $-    forM_-      [-        ( "drops every rho binding from the default salty output"-        , "[[ foo -> [[ x -> [[ ]], ^ -> $.y ]], y -> [[ ]] ]]"-        , ["rewrite", "--flat", "--hide-rho"]-        , ["⟦ foo ↦ ⟦ x ↦ ⟦⟧ ⟧, y ↦ ⟦⟧ ⟧"]-        )-      ,-        ( "also drops the rho that --sweet leaves behind"-        , "[[ foo -> [[ x -> [[ ]], ^ -> $.y ]], y -> [[ ]] ]]"-        , ["rewrite", "--flat", "--sweet", "--hide-rho"]-        , ["⟦ foo ↦ ⟦⟧:x, y ↦ ⟦⟧ ⟧"]-        )-      ,-        ( "keeps sweet numeric literals intact"-        , "[[ a -> 42 ]]"-        , ["rewrite", "--flat", "--sweet", "--hide-rho"]-        , ["42:a"]-        )-      ,-        ( "keeps the one-binding sugar after inline voids"-        , "[[ x(y) -> [[ a -> 42, ^ -> ? ]] ]]"-        , ["rewrite", "--flat", "--sweet", "--hide-rho"]-        , ["⟦ x(y) ↦ 42:a ⟧"]-        )-      ,-        ( "prints a lone void as the term without the rho would be printed"-        , "⟦ a ↦ ⟦ x ↦ ⟦ ρ ↦ ξ, y ↦ ∅ ⟧ ⟧ ⟧"-        , ["rewrite", "--sweet", "--hide-rho"]-        , ["⟦ x(y) ↦ ⟦⟧ ⟧:a"]-        )-      ,-        ( "indents the body as the term without the rho would be indented"-        , "⟦ a ↦ ⟦ x ↦ ⟦ ρ ↦ ∅, b ↦ ⟦ c ↦ ∅, ρ ↦ ∅ ⟧ ⟧ ⟧ ⟧"-        , ["rewrite", "--sweet", "--hide-rho", "--margin=3"]-        , ["⟦\n  b(c) ↦ ⟦⟧\n⟧:x:a"]-        )-      ,-        ( "prints positional arguments as such once the rho is gone"-        , "⟦ a ↦ ξ.b(ρ ↦ ξ, α0 ↦ ξ.c, α1 ↦ ξ.d) ⟧"-        , ["rewrite", "--flat", "--sweet", "--hide-rho"]-        , ["b( c, d ):a"]-        )-      ,-        ( "drops the rho from the LaTeX output too"-        , "[[ n -> [[ ^ -> ?, k -> $ ]] ]]"-        , ["rewrite", "--flat", "--hide-rho", "--output=latex"]-        , ["[[ |n| -> [[ |k| -> \\phiTerminal{\\xi} ]] ]]"]-        )-      ]-      (\(desc, input, args, expected) -> it desc (withStdin input (testCLISucceeded args expected)))--  it "keeps a data literal sugared when it is applied to more arguments" $-    withStdin "⟦ i ↦ 42(z ↦ ξ.f), s ↦ \"Hello\"(z ↦ ξ.f) ⟧" $-      testCLISucceeded ["rewrite", "--sweet", "--flat"] ["⟦ i ↦ 42( z ↦ f ), s ↦ \"Hello\"( z ↦ f ) ⟧"]--  it "prints the one-binding sugar after inline voids with --sweet" $-    withStdin "[[ x(y) -> [[ a -> 42 ]] ]]" $-      testCLISucceeded ["rewrite", "--sweet"] ["⟦ x(y) ↦ 42:a ⟧"]--  it "prints debug info with --log-level=DEBUG" $-    withStdin "[[]]" $-      testCLISucceeded ["rewrite", "--log-level=DEBUG"] ["[DEBUG]:"]--  describe "--log-level accepts every named level" $-    forM_-      ["INFO", "info", "ERROR", "ERR", "error", "NONE", "none"]-      ( \flagValue ->-          it ("--log-level=" ++ flagValue) $-            withStdin "[[]]" $-              testCLISucceeded ["rewrite", "--log-level=" ++ flagValue] ["⟧"]-      )--  describe "--log-level prints nothing below its level" $-    forM_-      [("NONE", ["[DEBUG]", "[INFO]"]), ("ERROR", ["[DEBUG]", "[INFO]"]), ("INFO", ["[DEBUG]"])]-      ( \(level, hidden) ->-          it ("--log-level=" ++ level) $-            withStdin "[[]]" $ do-              (out, _) <- withStdout (try (runCLI ["rewrite", "--log-level=" ++ level]) :: IO (Either ExitCode ()))-              forM_ hidden (out `shouldNotContain`)-      )--  it "fails on an unrecognized --log-level value" $-    withStdin "[[]]" $-      testCLIFailed ["rewrite", "--log-level=verbose"] ["unknown log-level: verbose"]--  describe "rewriting" $ do-    let xmir =-          unlines-            [ "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"-            , "<object>"-            , "  <o name=\"bar\">"-            , "    <o base=\"∅\" name=\"x\"/>"-            , "  </o>"-            , "</object>"-            ]-    describe "fails" $ do-      forM_-        [ ("with --input=latex", "", ["rewrite", "--input=latex"], ["The value 'latex' can't be used for '--input' option"])-        , ("with negative --log-lines", "", ["rewrite", "--log-lines=-2"], ["--log-lines must be >= -1"])-        , ("with negative --max-depth", "", ["rewrite", "--max-depth=-1"], ["--max-depth must be positive"])-        , ("with zero --max-cycles", "", ["rewrite", "--max-cycles=0"], ["--max-cycles must be positive"])-        , ("with zero --meet-length", "", ["rewrite", "--output=latex", "--meet-length=0"], ["--meet-length must be positive"])-        , ("with a --max-cycles value above maxBound Int", "", ["rewrite", "--max-cycles=18446744073709551617"], ["value must be between", "9223372036854775807"])-        ,-          ( "with --normalize and --must=1"-          , "[[ x -> [[ y -> 5 ]].y ]].x"-          , ["rewrite", "--max-cycles=2", "--max-depth=1", "--normalize", "--must=1"]-          , ["it's expected rewriting cycles to be in range [1], but rewriting has already reached 2"]-          )-        , ("when --in-place is used without input file", "[[ ]]", ["rewrite", "--in-place"], ["--in-place requires an input file"])-        ]-        (\(desc, input, args, expected) -> it desc (withStdin input (testCLIFailed args expected)))--      it "when --in-place is used with --target" $-        withTempFile "inplaceXXXXXX.phi" $ \(path, h) -> do-          hPutStr h "[[ ]]"-          hClose h-          testCLIFailed-            ["rewrite", "--in-place", "--target=output.phi", path]-            ["--in-place and --target cannot be used together"]--      it "fails when --in-place is used with a non-phi output format" $-        withTempFile "inplaceXXXXXX.phi" $ \(path, h) -> do-          hPutStr h "[[ ]]"-          hClose h-          testCLIFailed-            ["rewrite", "--in-place", "--output=latex", path]-            ["The option --in-place requires the output format to match the input format"]--      it "requires an XMIR input to be written back as XMIR" $-        withTempFileContent "inplaceXXXXXX.xmir" xmir $ \path ->-          testCLIFailed-            ["rewrite", "--input=xmir", "--in-place", path]-            ["The option --in-place requires the output format to match the input format"]--      it "does not leak a HasCallStack backtrace into errors" $ do-        (out, _) <- withStdout (try (runCLI ["rewrite", "--in-place"]) :: IO (Either ExitCode ()))-        out `shouldNotContain` "HasCallStack backtrace"-        out `shouldNotContain` "ExitFailure 1"-        out `shouldContain` "[ERROR]:"--      it "prints optparse errors once, without a backtrace" $ do-        (out, _) <- withStdout (try (runCLI ["rewrite", "--badopt"]) :: IO (Either ExitCode ()))-        out `shouldNotContain` "HasCallStack backtrace"-        out `shouldNotContain` "ExitFailure 1"-        out `shouldContain` "[ERROR]:"--      forM_-        [ ("when --update is used without --target", "[[ ]]", ["rewrite", "--update"], ["--update requires --target"])-        ,-          ( "when --update is used without an input file"-          , "[[ ]]"-          , ["rewrite", "--update", "--target=output.phi"]-          , ["--update requires an input file"]-          )-        ,-          ( "when --sequence is used with --in-place"-          , "[[ ]]"-          , ["rewrite", "--sequence", "--in-place", "input.phi"]-          , ["--in-place and --sequence cannot be used together"]-          )-        ,-          ( "when --focus is used with --in-place"-          , "[[ ]]"-          , ["rewrite", "--focus=Q.y", "--in-place", "input.phi"]-          , ["--in-place and --focus cannot be used together"]-          )-        ,-          ( "when --show is used with --in-place"-          , "[[ ]]"-          , ["rewrite", "--show=Q.y", "--in-place", "input.phi"]-          , ["--in-place and --show cannot be used together"]-          )-        ,-          ( "when --update is used with --in-place"-          , "[[ ]]"-          , ["rewrite", "--update", "--in-place", "input.phi"]-          , ["--update and --in-place cannot be used together"]-          )-        ,-          ( "when --hide is used with --in-place"-          , "[[ ]]"-          , ["rewrite", "--hide=Q.y", "--in-place", "input.phi"]-          , ["--in-place and --hide cannot be used together"]-          )-        ,-          ( "when --hide-rho is used with --in-place"-          , "[[ ]]"-          , ["rewrite", "--hide-rho", "--in-place", "input.phi"]-          , ["--in-place and --hide-rho cannot be used together"]-          )-        ,-          ( "with --depth-sensitive"-          , "[[ x -> \"x\"]]"-          , ["rewrite", "--depth-sensitive", "--max-depth=1", "--max-cycles=1", rule "infinite.yaml"]-          , ["[ERROR]: With option --depth-sensitive it's expected rewriting iterations amount does not reach the limit: --max-depth=1"]-          )-        ,-          ( "with looping rules"-          , "[[ x -> \"0\" ]]"-          , ["rewrite", rule "first.yaml", rule "second.yaml", "--max-depth=1", "--max-cycles=3"]-          , ["it seems rewriting is looping"]-          )-        ]-        (\(desc, input, args, expected) -> it desc (withStdin input (testCLIFailed args expected)))--      it "with wrong attribute and valid error message" $-        testCLIFailed-          ["rewrite", resource "with-$this-attribute.phi"]-          [ "[ERROR]: Couldn't parse given phi expression, cause:"-          , "unexpected"-          ]--      forM_-        [-          ( "with --output != latex and --nonumber"-          , ["rewrite", "--nonumber", "--output=xmir"]-          , ["The --nonumber option can stay together with --output=latex only"]-          )-        , ("with --omit-listing and --output != xmir", ["rewrite", "--omit-listing", "--output=phi"], ["--omit-listing"])-        , ("with --omit-comments and --output != xmir", ["rewrite", "--omit-comments", "--output=phi"], ["--omit-comments"])-        ,-          ( "with --expression and --output != latex"-          , ["rewrite", "--expression=foo", "--output=phi"]-          , ["--expression option can stay together with --output=latex only"]-          )-        ,-          ( "with --label and --output != latex"-          , ["rewrite", "--label=foo", "--output=phi"]-          , ["--label option can stay together with --output=latex only"]-          )-        ,-          ( "with an unsafe --label and --output=latex"-          , ["rewrite", "--label=eq:bad%tag", "--output=latex"]-          , ["The --label option must contain only letters, numbers, colons, periods, underscores, and hyphens"]-          )-        ,-          ( "with --compress and --output != latex"-          , ["rewrite", "--compress", "--output=phi"]-          , ["--compress option can stay together with --output=latex only"]-          )-        ,-          ( "with --meet-prefix and --output != latex"-          , ["rewrite", "--meet-prefix=foo", "--output=phi"]-          , ["--meet-prefix option can stay together with --output=latex only"]-          )-        ,-          ( "with wrong --hide option"-          , ["rewrite", "--hide=Q.x(Q.y)"]-          , ["[ERROR]: Invalid set of arguments: Only dispatch expression", "but given: Φ.x( Φ.y )"]-          )-        , ("with many --show options", ["rewrite", "--show=Q.x.y", "--show=hello"], ["The option --show can be used only once"])-        ,-          ( "with wrong --show option"-          , ["rewrite", "--show=Q.x(Q.y)"]-          , ["[ERROR]:", "Only dispatch expression started with Φ (or Q) can be used in --show"]-          )-        , ("with --show overlapping --hide", ["rewrite", "--show=Q.x", "--hide=Q.x"], ["[ERROR]:", "The --show locator 'Φ.x' is also listed in --hide"])-        , ("with --hide of an ancestor of --show", ["rewrite", "--show=Q.y.z", "--hide=Q.y"], ["[ERROR]:", "The --show locator 'Φ.y.z' lies inside the --hide locator 'Φ.y'"])-        , ("with --meet-popularity < 0", ["rewrite", "--meet-popularity=-1"], ["[ERROR]:", "--meet-popularity must be positive"])-        , ("with --meet-popularity > 100", ["rewrite", "--meet-popularity=102"], ["[ERROR]:", "--meet-popularity must be <= 100"])-        ,-          ( "with --meet-popularity and output != latex"-          , ["rewrite", "--meet-popularity=51", "--output=phi"]-          , ["[ERROR]:", "--meet-popularity option can stay together with --output=latex only"]-          )-        ,-          ( "with --meet-length and output != latex"-          , ["rewrite", "--meet-length=4", "--output=phi"]-          , ["[ERROR]:", "--meet-length option can stay together with --output=latex only"]-          )-        , ("with non-dispatch --focus", ["rewrite", "--focus=Q.x(Q.y)"], ["[ERROR]"])-        , ("with --focus!=Q and --output=XMIR", ["rewrite", "--focus=Q.x", "--output=xmir"], ["[ERROR]"])-        , ("with --margin < 0", ["rewrite", "--margin=-1"], ["[ERROR]"])-        , ("with --breakpoint which does not exist across the rules", ["rewrite", "--breakpoint=hello", "--normalize"], ["[ERROR]"])-        ]-        (\(desc, args, expected) -> it desc (withStdin "" (testCLIFailed args expected)))--    it "prints help" $-      testCLISucceeded-        ["rewrite", "--help"]-        ["Rewrite the 𝜑-expression", "--seed SEED"]--    it "accepts --seed flag" $-      withStdin "[[ x -> 5 ]]" $-        testCLISucceeded-          ["rewrite", "--seed=42", "--sweet"]-          ["5:x"]--    it "defaults --seed to 0 in help" $-      testCLISucceeded-        ["rewrite", "--help"]-        ["default: 0"]--    it "describes --must as counting rewriting cycles, not rules applied" $-      testCLISucceeded-        ["rewrite", "--help"]-        ["Stops execution if number of rewriting cycles is not"]--    it "reproduces the same shuffle order for the same --seed" $ do-      let args =-            [ "rewrite"-            , "--shuffle"-            , "--seed=42"-            , "--sweet"-            , "--sequence"-            , "--max-depth=1"-            , "--max-cycles=1"-            , rule "swap-a.yaml"-            , rule "swap-b.yaml"-            ]-      (firstRun, _) <- withStdin "[[ x -> 5 ]]" $ withStdout (runCLI args)-      (secondRun, _) <- withStdin "[[ x -> 5 ]]" $ withStdout (runCLI args)-      firstRun `shouldBe` secondRun--    it "fails with a non-integer --seed" $-      withStdin "[[ ]]" $-        testCLIFailed-          ["rewrite", "--seed=abc"]-          ["[ERROR]"]--    it "saves steps to dir with --steps-dir" $-      withTempDirectory "phino-steps" $ \dir ->-        withStdin "[[ x -> \"hello\"]]" $ do-          testCLISucceeded-            ["rewrite", rule "infinite.yaml", "--max-cycles=2", "--max-depth=2", "--steps-dir=" ++ dir, "--sweet"]-            ["hello_hi_hi"]-          doesDirectoryExist dir `shouldReturn` True-          files <- listDirectory dir-          length files `shouldBe` 4-          doesFileExist (dir ++ "/00001.phi") `shouldReturn` True-          doesFileExist (dir ++ "/00003.phi") `shouldReturn` True--    it "clears step files left from an earlier, longer run with --steps-dir" $-      withTempDirectory "phino-steps-stale" $ \dir -> do-        withStdin "[[ x -> \"hello\"]]" $-          testCLISucceeded-            ["rewrite", rule "infinite.yaml", "--max-cycles=2", "--max-depth=2", "--steps-dir=" ++ dir, "--sweet"]-            ["hello_hi_hi"]-        files <- listDirectory dir-        length files `shouldBe` 4-        withStdin "[[ x -> \"hello\"]]" $-          testCLISucceeded-            ["rewrite", rule "infinite.yaml", "--max-cycles=1", "--max-depth=2", "--steps-dir=" ++ dir, "--sweet"]-            ["hello_hi"]-        files2 <- listDirectory dir-        sort files2 `shouldBe` ["00001.phi", "00002.phi"]--    it "gives the saved steps the --canonize and --hide of the printed ones" $-      withTempDirectory "phino-steps-filtered" $ \dir ->-        withStdin "[[ m -> [[ x -> [[ L> Plus ]], y -> $.x ]].y, k -> [[ L> Minus ]] ]]" $ do-          testCLISucceeded-            ["rewrite", "--normalize", "--hide=Q.k", "--canonize", "--steps-dir=" ++ dir, "--flat"]-            ["Fn1"]-          files <- listDirectory dir-          null files `shouldBe` False-          saved <- mapM (\file -> readFile (dir ++ "/" ++ file)) files-          concat saved `shouldNotContain` "Minus"-          concat saved `shouldNotContain` "Plus"--    it "gives the saved steps the --focus of the printed ones" $-      withTempDirectory "phino-steps-focused" $ \dir ->-        withStdin "⟦ x ↦ ⟦ k ↦ ⟦ q ↦ ⟦⟧ ⟧ ⟧.k, y ↦ ⟦ z ↦ ⟦⟧ ⟧ ⟧" $ do-          testCLISucceeded-            ["rewrite", "--normalize", "--sweet", "--flat", "--focus=Q.y", "--sequence", "--steps-dir=" ++ dir]-            ["⟦⟧:z"]-          files <- listDirectory dir-          null files `shouldBe` False-          saved <- mapM (\file -> readFile (dir ++ "/" ++ file)) (sort files)-          map (filter (/= '\n')) saved `shouldSatisfy` all (== "⟦⟧:z")--    it "saves dataize steps to dir with --steps-dir" $-      withTempDirectory "phino-steps-dataize" $ \dir ->-        withStdin "[[ bytes ↦ ⟦ φ ↦ ∅ ⟧, number(φ) -> [[ plus(^, x) -> [[ L> L_number_plus ]] ]], @ -> 5.plus(6).plus(7) ]]" $ do-          testCLISucceeded-            ["dataize", symbolic, "--steps-dir=" ++ dir, "--sweet"]-            ["40-45"]-          doesDirectoryExist dir `shouldReturn` True-          files <- listDirectory dir-          let steps = sort files-          steps `shouldBe` map (\n -> printf "%05d.phi" (n :: Int)) [1 .. length steps]-          length steps `shouldSatisfy` (> 18)--    it "saves steps with a .tex extension when --output=latex is used with --steps-dir" $-      withTempDirectory "phino-steps-latex" $ \dir ->-        withStdin "[[ x -> \"hello\"]]" $ do-          testCLISucceeded-            ["rewrite", rule "infinite.yaml", "--max-cycles=2", "--max-depth=2", "--steps-dir=" ++ dir, "--output=latex", "--sweet"]-            ["\\begin{phiquation}"]-          doesDirectoryExist dir `shouldReturn` True-          files <- listDirectory dir-          length files `shouldBe` 4-          doesFileExist (dir ++ "/00001.tex") `shouldReturn` True-          doesFileExist (dir ++ "/00003.tex") `shouldReturn` True--    it "desugares without any rules flag from file" $-      testCLISucceeded-        ["rewrite", resource "desugar.phi"]-        ["⟦ foo ↦ ξ.x ⟧"]--    it "desugares with without any rules flag from stdin" $-      withStdin "[[foo ↦ x]]" $-        testCLISucceeded ["rewrite"] ["⟦ foo ↦ ξ.x ⟧"]--    it "keeps the bytes of a string intact while desugaring it" $-      withStdin "⟦ φ ↦ Φ.string(as-bytes ↦ Φ.bytes(data ↦ ⟦ Δ ⤍ 65-0A-65, ρ ↦ ∅ ⟧)), ρ ↦ ∅ ⟧" $-        testCLISucceeded ["rewrite", "--flat"] ["Δ ⤍ 65-0A-65"]--    it "rewrites with single rule" $-      withStdin "T(x -> Q.y)" $-        testCLISucceeded ["rewrite", "--rule=resources/normalize/dc.yaml"] ["⊥"]--    it "fails when a rewriting rule uses a dataization-only function" $-      withStdin "⟦⟧" $-        testCLIFailed-          ["rewrite", rule "evaluate-in-rewrite.yaml"]-          ["Function 'evaluate' in rule 'uses-evaluate' is available only for dataization and morphing, not for rewriting"]--    it "names the join function in the error message" $-      withStdin "⟦⟧" $-        testCLIFailed-          ["rewrite", rule "join-broken.yaml"]-          ["Function join() can work with bindings only"]--    it "reports a broken rule file in plain text with its path" $-      withStdin "⟦⟧" $-        testCLIFailed-          ["rewrite", rule "malformed-pattern.yaml"]-          [ "Could not load rule from 'test-resources/cli/rules/malformed-pattern.yaml'"-          , "unexpected '⟧'"-          ]--    it "normalizes with --normalize flag" $-      testCLISucceeded-        ["rewrite", "--normalize", resource "normalize.phi", "--margin=25"]-        [ unlines-            [ "⟦"-            , "  x ↦ ⟦"-            , "    ρ ↦ ⟦ y ↦ ⟦ ρ ↦ ∅ ⟧ ⟧"-            , "  ⟧"-            , "⟧"-            ]-        ]--    it "normalizes and applies --rule at the same time" $-      withStdin "⟦ k ↦ ⟦ m ↦ ⟦ Δ ⤍ 01- ⟧ ⟧.m, j ↦ ⟦ λ ⤍ Marker ⟧ ⟧" $-        testCLISucceeded-          ["rewrite", "--normalize", rule "marker.yaml", "--sweet"]-          ["⟦ k ↦ 01-:Δ, j ↦ FF-:Δ ⟧"]--    it "normalizes from stdin" $-      withStdin "⟦ a ↦ ⟦ b ↦ ∅ ⟧ (b ↦ [[ ]]) ⟧" $-        testCLISucceeded-          ["rewrite", "--normalize", "--margin=20"]-          ["⟦ a ↦ ⟦ b ↦ ⟦⟧ ⟧ ⟧"]--    it "rewrites with --sweet flag" $-      withStdin "[[ x -> 5]]" $-        testCLISucceeded-          ["rewrite", "--sweet"]-          ["5:x"]--    it "rewrites as XMIR" $-      withStdin "[[ x -> Q.y ]]" $-        testCLISucceeded-          ["rewrite", "--output=xmir"]-          ["<?xml version=\"1.0\" encoding=\"UTF-8\"?>", "<object", "  <o base=\"Φ.y\" name=\"x\"/>"]--    it "accepts --focus=Φ together with --output=xmir, since Φ and Q are the same locator" $-      withStdin "[[ x -> Q.y ]]" $-        testCLISucceeded-          ["rewrite", "--focus=Φ", "--output=xmir"]-          ["<?xml version=\"1.0\" encoding=\"UTF-8\"?>", "<object", "  <o base=\"Φ.y\" name=\"x\"/>"]--    it "rewrites a formation of several bindings at the root as XMIR" $-      withStdin "⟦ x ↦ 1, ρ ↦ 2 ⟧" $-        testCLISucceeded-          ["rewrite", "--output=xmir"]-          ["<o base=\"Φ.number\" name=\"x\">", "<o base=\"Φ.number\" name=\"ρ\">"]--    it "emits a real revision and ms in XMIR" $ do-      (output, _) <- withStdin "[[ x -> Q.y ]]" $ withStdout (runCLI ["rewrite", "--output=xmir"])-      let attrValue :: String -> String -> String-          attrValue name text =-            let needle = name ++ "=\""-                breakOn :: String -> Maybe String-                breakOn haystack-                  | needle `isPrefixOf` haystack = Just (drop (length needle) haystack)-                  | null haystack = Nothing-                  | otherwise = breakOn (drop 1 haystack)-             in case breakOn text of-                  Just afterNeedle -> takeWhile (/= '"') afterNeedle-                  Nothing -> ""-          revision = attrValue "revision" output-          ms = attrValue "ms" output-      revision `shouldSatisfy` (\sha -> length sha == 7 && all (`elem` "0123456789abcdef") sha)-      revision `shouldNotBe` "1234567"-      ms `shouldSatisfy` (all isDigit)--    it "rewrites as LaTeX" $-      withStdin "[[ x_o -> Q.z(y -> 5), q$ -> T, w -> $, ^ -> Q, @ -> 1, y -> \"H$@^M\", L> Fu_nc ]]" $-        testCLISucceeded-          ["rewrite", "--output=latex", "--sweet"]-          [ unlines-              [ "\\begin{phiquation}"-              , "[["-              , "  |x\\char95{}o| -> Q . |z| ( |y| -> |5| ),"-              , "  |q\\char36{}| -> T,"-              , "  |w| -> \\phiTerminal{\\xi},"-              , "  \\phiTerminal{\\rho} -> Q,"-              , "  @ -> |1|,"-              , "  |y| -> \"H\\char36{}\\char64{}\\char94{}M\","-              , "  L> |Fu\\char95{}nc|"-              , "]]{.}"-              , "\\end{phiquation}"-              ]-          ]--    it "rewrites as LaTeX without numeration" $-      withStdin "[[ x -> 5 ]]" $-        testCLISucceeded-          ["rewrite", "--output=latex", "--sweet", "--nonumber", "--flat"]-          [ unlines-              [ "\\begin{phiquation*}"-              , "|5| : |x|{.}"-              , "\\end{phiquation*}"-              ]-          ]--    it "rewrites an alpha-index argument as \\alpha subscript in LaTeX" $-      withStdin "Q.foo(~1 -> Q.y)" $-        testCLISucceeded-          ["rewrite", "--output=latex", "--flat", "--nonumber"]-          [ unlines-              [ "\\begin{phiquation*}"-              , "Q . |foo| ( \\phiTerminal{\\alpha_{1}} -> Q . |y| ){.}"-              , "\\end{phiquation*}"-              ]-          ]--    it "rewrite as LaTeX with expression name" $-      withStdin "[[ x -> 5 ]]" $-        testCLISucceeded-          ["rewrite", "--output=latex", "--sweet", "--flat", "--expression=foo"]-          [ unlines-              [ "\\begin{phiquation}"-              , "\\phiExpression{foo} |5| : |x|{.}"-              , "\\end{phiquation}"-              ]-          ]--    it "escapes special characters in the LaTeX expression name" $-      withStdin "[[ x -> 5 ]]" $-        testCLISucceeded-          ["rewrite", "--output=latex", "--sweet", "--flat", "--expression=bad%tag"]-          [ unlines-              [ "\\begin{phiquation}"-              , "\\phiExpression{bad\\char37{}tag} |5| : |x|{.}"-              , "\\end{phiquation}"-              ]-          ]--    it "rewrite as LaTeX with label name" $-      withStdin "[[ x -> 5 ]]" $-        testCLISucceeded-          ["rewrite", "--output=latex", "--sweet", "--flat", "--label=foo"]-          [ unlines-              [ "\\begin{phiquation}\n\\label{foo}"-              , "|5| : |x|{.}"-              , "\\end{phiquation}"-              ]-          ]--    it "rewrites with XMIR as input" $-      withStdin "<object><o name=\"app\"><o name=\"x\" base=\"Φ.number\"/></o></object>" $-        testCLISucceeded-          ["rewrite", "--input=xmir", "--sweet"]-          ["Φ.number:x:app"]--    it "rewrites and prints with XMIR as input and output" $-      withStdin-        ( intercalate-            ""-            [ "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"-            , "<object><o name=\"app\"><o name=\"x\" base=\"Φ.number\"/></o></object>"-            ]-        )-        ( testCLISucceeded-            ["rewrite", "--input=xmir", "--output=xmir", "--sweet", "--flat"]-            [ "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"-            , "<listing>Φ.number:x:app</listing>"-            ]-        )--    it "rewrites as XMIR with omit-listing flag" $-      withStdin "[[ x -> Q.y ]]" $-        testCLISucceeded-          ["rewrite", "--output=xmir", "--omit-listing"]-          ["<?xml version=\"1.0\" encoding=\"UTF-8\"?>", "<object", "<listing>1 line(s)</listing>", "  <o base=\"Φ.y\" name=\"x\"/>"]--    it "does not fail on exactly 1 rewriting" $-      withStdin "⟦ t ↦ ⟦ x ↦ \"foo\" ⟧ ⟧" $-        testCLISucceeded-          ["rewrite", rule "simple.yaml", "--must=1", "--sweet"]-          ["\"bar\":x"]--    it "prints many expressions with --sequence" $-      withStdin "[[ x -> \"foo\" ]]" $-        testCLISucceeded-          [ "rewrite"-          , rule "first.yaml"-          , rule "second.yaml"-          , "--max-depth=1"-          , "--max-cycles=2"-          , "--sequence"-          , "--sweet"-          , "--flat"-          ]-          [ unlines-              [ "\"foo\":x"-              , "Φ.x( y ↦ \"foo\" )"-              , "\"foo\":x"-              ]-          ]--    it "prefixes every step with a header when --headers is on" $-      withStdin "[[ x -> \"foo\" ]]" $-        testCLISucceeded-          [ "rewrite"-          , rule "first.yaml"-          , rule "second.yaml"-          , "--max-depth=1"-          , "--max-cycles=2"-          , "--sequence"-          , "--headers"-          , "--sweet"-          , "--flat"-          ]-          [ intercalate-              "\n"-              [ ""-              , "=== Step #1"-              , "\"foo\":x"-              , ""-              , "=== Step #2, Rule 'first', 23t -> 26t"-              , "Φ.x( y ↦ \"foo\" )"-              , ""-              , "=== Step #3, Rule 'second', 26t -> 23t"-              , "\"foo\":x"-              ]-          ]--    it "ignores --headers without --sequence" $-      withStdin "[[ x -> \"foo\" ]]" $-        testCLISucceeded-          ["rewrite", rule "simple.yaml", "--headers", "--sweet", "--flat"]-          ["\"bar\":x"]--    it "emits step headers as LaTeX comments with --headers" $-      withStdin "[[ x -> \"foo\" ]]" $-        testCLISucceeded-          [ "rewrite"-          , rule "first.yaml"-          , rule "second.yaml"-          , "--max-depth=1"-          , "--max-cycles=2"-          , "--sequence"-          , "--headers"-          , "--sweet"-          , "--flat"-          , "--output=latex"-          ]-          [ unlines-              [ "\\begin{phiquation}"-              , "% === Step #1"-              , "\"foo\" : |x| \\phiNormalize[\\nameref{r:first}]"-              , "% === Step #2, Rule 'first', 23t -> 26t"-              , "  \\phiNormalize Q . |x| ( |y| -> \"foo\" ) \\phiNormalize[\\nameref{r:second}]"-              , "% === Step #3, Rule 'second', 26t -> 23t"-              , "  \\phiNormalize \"foo\" : |x|{.}"-              , "\\end{phiquation}"-              ]-          ]--    it "prints only one latex preamble with --sequence" $-      withStdin "[[ x -> \"foo\" ]]" $-        testCLISucceeded-          [ "rewrite"-          , rule "first.yaml"-          , rule "second.yaml"-          , "--max-depth=1"-          , "--max-cycles=2"-          , "--sequence"-          , "--sweet"-          , "--flat"-          , "--output=latex"-          ]-          [ unlines-              [ "\\begin{phiquation}"-              , "\"foo\" : |x| \\phiNormalize[\\nameref{r:first}]"-              , "  \\phiNormalize Q . |x| ( |y| -> \"foo\" ) \\phiNormalize[\\nameref{r:second}]"-              , "  \\phiNormalize \"foo\" : |x|{.}"-              , "\\end{phiquation}"-              ]-          ]--    it "prints meet prefix with --meet-prefix=foo in LaTeX" $-      withStdin "[[ x -> ?, y -> $.x ]](x -> [[ D> 42- ]]).y" $-        testCLISucceeded-          ["rewrite", "--normalize", "--sweet", "--sequence", "--output=latex", "--flat", "--compress", "--meet-prefix=foo"]-          [ unlines-              [ "\\begin{phiquation}"-              , "[[ |x| -> ?, |y| -> |x| ]] ( |x| -> |42-| : D ) . |y| \\phiNormalize[\\nameref{r:copy}]"-              , "  \\phiNormalize \\phinoMeet{foo:1}{ [[ |x| -> |42-| : D, |y| -> |x| ]] } . |y| \\phiNormalize[\\nameref{r:dot}]"-              , "  \\phiNormalize |42-| : D : |x| . |x| ( \\phiTerminal{\\rho} -> \\phinoAgain{foo:1} ) \\phiNormalize[\\nameref{r:dot}]"-              , "  \\phiNormalize |42-| : D ( \\phiTerminal{\\rho} -> |42-| : D : |x|, \\phiTerminal{\\rho} -> \\phinoAgain{foo:1} ) \\phiNormalize[\\nameref{r:skip}]"-              , "  \\phiNormalize |42-| : D ( \\phiTerminal{\\rho} -> \\phinoAgain{foo:1} ) \\phiNormalize[\\nameref{r:skip}]"-              , "  \\phiNormalize |42-| : D{.}"-              , "\\end{phiquation}"-              ]-          ]--    it "prints with compressed expressions in LaTeX" $-      withStdin "[[ x -> ?, y -> $.x ]](x -> [[ D> 42- ]]).y" $-        testCLISucceeded-          ["rewrite", "--normalize", "--sweet", "--sequence", "--output=latex", "--flat", "--compress"]-          [ unlines-              [ "\\begin{phiquation}"-              , "[[ |x| -> ?, |y| -> |x| ]] ( |x| -> |42-| : D ) . |y| \\phiNormalize[\\nameref{r:copy}]"-              , "  \\phiNormalize \\phinoMeet{1}{ [[ |x| -> |42-| : D, |y| -> |x| ]] } . |y| \\phiNormalize[\\nameref{r:dot}]"-              , "  \\phiNormalize |42-| : D : |x| . |x| ( \\phiTerminal{\\rho} -> \\phinoAgain{1} ) \\phiNormalize[\\nameref{r:dot}]"-              , "  \\phiNormalize |42-| : D ( \\phiTerminal{\\rho} -> |42-| : D : |x|, \\phiTerminal{\\rho} -> \\phinoAgain{1} ) \\phiNormalize[\\nameref{r:skip}]"-              , "  \\phiNormalize |42-| : D ( \\phiTerminal{\\rho} -> \\phinoAgain{1} ) \\phiNormalize[\\nameref{r:skip}]"-              , "  \\phiNormalize |42-| : D{.}"-              , "\\end{phiquation}"-              ]-          ]--    it "should not print \\phinoMeet{} twice" $-      withStdin "[[ ex -> [[ x -> [[ y -> ?, k -> [[ t -> 42]]  ]]( y -> [[ t -> 42 ]]) ]].i ]]" $-        testCLISucceeded-          ["rewrite", "--normalize", "--sequence", "--flat", "--compress", "--output=latex", "--sweet"]-          [ unlines-              [ "\\begin{phiquation}"-              , "[[ |y| -> ?, |k| -> \\phinoMeet{1}{ |42| : |t| } ]] ( |y| -> \\phinoAgain{1} ) : |x| . |i| : |ex| \\phiNormalize[\\nameref{r:copy}]"-              , "  \\phiNormalize [[ |y| -> \\phinoAgain{1}, |k| -> \\phinoAgain{1} ]] : |x| . |i| : |ex| \\phiNormalize[\\nameref{r:stop}]"-              , "  \\phiNormalize T : |ex|{.}"-              , "\\end{phiquation}"-              ]-          ]--    it "should not meet expression with high --meet-popularity" $-      withStdin "[[ ex -> [[ x -> [[ y -> ?, k -> [[ t -> 42]]  ]]( y -> [[ t -> 42 ]]) ]].i ]]" $-        testCLISucceeded-          ["rewrite", "--normalize", "--sequence", "--flat", "--compress", "--output=latex", "--sweet", "--meet-popularity=70"]-          [ unlines-              [ "\\begin{phiquation}"-              , "[[ |y| -> ?, |k| -> |42| : |t| ]] ( |y| -> |42| : |t| ) : |x| . |i| : |ex| \\phiNormalize[\\nameref{r:copy}]"-              , "  \\phiNormalize [[ |y| -> |42| : |t|, |k| -> |42| : |t| ]] : |x| . |i| : |ex| \\phiNormalize[\\nameref{r:stop}]"-              , "  \\phiNormalize T : |ex|{.}"-              , "\\end{phiquation}"-              ]-          ]--    it "meets with --meet-length=32" $-      withStdin "[[ ex -> [[ x -> [[ y -> ?, k -> [[ t -> 42]]  ]]( y -> [[ t -> 42 ]]) ]].i ]]" $-        testCLISucceeded-          ["rewrite", "--normalize", "--sequence", "--flat", "--compress", "--output=latex", "--sweet", "--meet-length=32"]-          [ unlines-              [ "\\begin{phiquation}"-              , "[[ |y| -> ?, |k| -> |42| : |t| ]] ( |y| -> |42| : |t| ) : |x| . |i| : |ex| \\phiNormalize[\\nameref{r:copy}]"-              , "  \\phiNormalize [[ |y| -> |42| : |t|, |k| -> |42| : |t| ]] : |x| . |i| : |ex| \\phiNormalize[\\nameref{r:stop}]"-              , "  \\phiNormalize T : |ex|{.}"-              , "\\end{phiquation}"-              ]-          ]--    it "focuses expression in latex with sequence" $-      withStdin "[[ ex -> [[ x -> [[ y -> ?, k -> [[ t -> 42]]  ]]( y -> [[ t -> 42 ]]) ]].i ]]" $-        testCLISucceeded-          ["rewrite", "--normalize", "--sequence", "--flat", "--output=latex", "--sweet", "--focus=Q.ex"]-          [ unlines-              [ "\\begin{phiquation}"-              , "[[ |y| -> ?, |k| -> |42| : |t| ]] ( |y| -> |42| : |t| ) : |x| . |i| \\phiNormalize[\\nameref{r:copy}]"-              , "  \\phiNormalize [[ |y| -> |42| : |t|, |k| -> |42| : |t| ]] : |x| . |i| \\phiNormalize[\\nameref{r:stop}]"-              , "  \\phiNormalize T{.}"-              , "\\end{phiquation}"-              ]-          ]--    it "focuses expression in latex without sequence" $-      withStdin "[[ ex -> [[ x -> [[ y -> ?, k -> [[ t -> 42]]  ]]( y -> [[ t -> 42 ]]) ]].i ]]" $-        testCLISucceeded-          ["rewrite", "--normalize", "--flat", "--output=latex", "--sweet", "--focus=Q.ex"]-          [ unlines-              [ "\\begin{phiquation}"-              , "T{.}"-              , "\\end{phiquation}"-              ]-          ]--    it "shows exceeding of limits in latex" $-      withStdin "[[ x -> $.y, y -> $.x ]].x" $-        testCLISucceeded-          ["rewrite", "--normalize", "--flat", "--sequence", "--output=latex", "--sweet", "--max-depth=1", "--max-cycles=1"]-          [ unlines-              [ "\\begin{phiquation}"-              , "[[ |x| -> |y|, |y| -> |x| ]] . |x| \\phiNormalize[\\nameref{r:dot}]"-              , "  \\phiNormalize |x| : |y| . |y| ( \\phiTerminal{\\rho} -> [[ |x| -> |y|, |y| -> |x| ]] ) \\phiNormalize"-              , "  \\phiNormalize \\dots"-              , "\\end{phiquation}"-              ]-          ]--    it "focuses expression in phi without sequence" $-      withStdin "[[ ex -> [[ x -> [[ y -> ?, k -> [[ t -> 42]]  ]]( y -> [[ t -> 42 ]]) ]].i ]]" $-        testCLISucceeded-          ["rewrite", "--normalize", "--flat", "--output=phi", "--sweet", "--focus=Q.ex"]-          ["⊥"]--    it "focuses expression in phi with sequence" $-      withStdin "[[ ex -> [[ x -> [[ y -> ?, k -> [[ t -> 42]]  ]]( y -> [[ t -> 42 ]]) ]].i ]]" $-        testCLISucceeded-          ["rewrite", "--normalize", "--sequence", "--flat", "--output=phi", "--sweet", "--focus=Q.ex"]-          [ unlines-              [ "⟦ y ↦ ∅, k ↦ 42:t ⟧( y ↦ 42:t ):x.i"-              , "⟦ y ↦ 42:t, k ↦ 42:t ⟧:x.i"-              , "⊥"-              ]-          ]--    it "prints input as listing in XMIR" $-      withStdin "[[ app -> [[]] ]]" $-        testCLISucceeded-          ["rewrite", "--output=xmir", "--omit-comments", "--sweet", "--flat"]-          ["  <listing>[[ app -&gt; [[]] ]]</listing>"]--    it "print expression in listing in XMIRs with --sequence" $-      withStdin "[[ x -> \"foo\" ]]" $-        testCLISucceeded-          ["rewrite", "--output=xmir", "--omit-comments", "--sweet", "--flat", "--sequence", rule "simple.yaml"]-          ["  <listing>\"foo\":x</listing>", "  <listing>\"bar\":x</listing>"]--    describe "must range tests" $ do-      describe "fails" $ do-        it "when cycles exceed range ..1" $-          withStdin "[[ x -> [[ y -> 5 ]].y ]].x" $-            testCLIFailed-              ["rewrite", "--max-depth=1", "--max-cycles=2", "--normalize", "--must=..1"]-              ["it's expected rewriting cycles to be in range [..1], but rewriting has already reached 2"]--        it "when cycles below range 2.." $-          withStdin "⟦ t ↦ ⟦ x ↦ \"foo\" ⟧ ⟧" $-            testCLIFailed-              ["rewrite", rule "simple.yaml", "--must=2.."]-              ["it's expected rewriting cycles to be in range [2..], but rewriting stopped after 1"]--        it "with invalid range 5..3" $-          withStdin "[[ ]]" $-            testCLIFailed-              ["rewrite", "--must=5..3"]-              ["cannot parse value `5..3'"]--        it "with negative in range -1..5" $-          withStdin "[[ ]]" $-            testCLIFailed-              ["rewrite", "--must=-1..5"]-              ["cannot parse value `-1..5'"]--        it "with malformed range syntax" $-          withStdin "[[ ]]" $-            testCLIFailed-              ["rewrite", "--must=3...5"]-              ["cannot parse value `3...5'"]--      it "accepts range ..5 (0 to 5 cycles)" $-        withStdin "[[ ]]" $-          testCLISucceeded ["rewrite", "--must=..5", "--sweet"] ["⟦⟧"]--      it "accepts range 0..0 (exactly 0 cycles)" $-        withStdin "[[ ]]" $-          testCLISucceeded ["rewrite", "--must=0..0", "--sweet"] ["⟦⟧"]--      it "accepts range 1..1 (exactly 1 cycle)" $-        withStdin "⟦ t ↦ ⟦ x ↦ \"foo\" ⟧ ⟧" $-          testCLISucceeded-            ["rewrite", rule "simple.yaml", "--must=1..1", "--sweet"]-            ["\"bar\":x"]--      it "accepts range 1..3 when 1 cycle happens" $-        withStdin "⟦ t ↦ ⟦ x ↦ \"foo\" ⟧ ⟧" $-          testCLISucceeded-            ["rewrite", rule "simple.yaml", "--must=1..3", "--sweet"]-            ["\"bar\":x"]--      it "accepts range 0.. (0 or more)" $-        withStdin "[[ ]]" $-          testCLISucceeded ["rewrite", "--must=0..", "--sweet"] ["⟦⟧"]--    it "prints to target file" $-      withStdin "[[ ]]" $-        withTempFile "targetXXXXXX.tmp" $ \(path, h) -> do-          hClose h-          testCLISucceeded ["rewrite", "--sweet", printf "--target=%s" path] []-          content <- readFile path-          content `shouldBe` "⟦⟧\n"--    it "modifies file in-place" $-      withTempFile "inplaceXXXXXX.phi" $ \(path, h) -> do-        hPutStr h "[[ x -> \"foo\" ]]"-        hClose h-        testCLISucceeded ["rewrite", rule "simple.yaml", "--in-place", "--sweet", path] []-        content <- readFile path-        content `shouldBe` "\"bar\":x\n"--    it "writes the same bytes to --target as it prints to the console" $-      withTempFile "targetXXXXXX.tmp" $ \(path, h) -> do-        hClose h-        (console, _) <- withStdout (withStdin "[[ ]]" (runCLI ["rewrite", "--sweet"]))-        withStdin "[[ ]]" (testCLISucceeded ["rewrite", "--sweet", printf "--target=%s" path] [])-        content <- readFile path-        content `shouldBe` console--    it "keeps the trailing newline of the source file when nothing is rewritten by --in-place" $-      withTempFile "inplaceXXXXXX.phi" $ \(path, h) -> do-        hPutStr h "⟦⟧\n"-        hClose h-        testCLISucceeded ["rewrite", "--in-place", "--sweet", path] []-        content <- readFile path-        content `shouldBe` "⟦⟧\n"--    it "modifies file in-place with --focus=Φ, since Φ and Q are the same locator" $-      withTempFile "inplaceXXXXXX.phi" $ \(path, h) -> do-        hPutStr h "[[ x -> \"foo\" ]]"-        hClose h-        testCLISucceeded ["rewrite", rule "simple.yaml", "--focus=Φ", "--in-place", "--sweet", path] []-        content <- readFile path-        content `shouldBe` "\"bar\":x\n"--    it "keeps the XMIR format when rewriting an XMIR file in-place" $-      withTempFileContent "inplaceXXXXXX.xmir" xmir $ \path -> do-        testCLISucceeded ["rewrite", "--input=xmir", "--output=xmir", "--in-place", path] []-        content <- readFile path-        _ <- evaluate (length content)-        content `shouldContain` "<object"--    it "skips rewriting with --update when target is newer than source" $-      withTempFileContent "src-XXXXXX.phi" "[[ x -> \"foo\" ]]" $ \src ->-        withTempFileContent "tgt-XXXXXX.phi" "ORIGINAL" $ \tgt -> do-          now <- getCurrentTime-          setModificationTime src (addUTCTime (-60) now)-          setModificationTime tgt now-          testCLISucceeded-            ["rewrite", rule "simple.yaml", "--update", "--sweet", "--target=" ++ tgt, src]-            []-          content <- readFile tgt-          content `shouldBe` "ORIGINAL"--    it "logs the skip reason at debug level when --update finds a newer target" $-      withTempFileContent "src-XXXXXX.phi" "[[ x -> \"foo\" ]]" $ \src ->-        withTempFileContent "tgt-XXXXXX.phi" "ORIGINAL" $ \tgt -> do-          now <- getCurrentTime-          setModificationTime src (addUTCTime (-60) now)-          setModificationTime tgt now-          testCLISucceeded-            ["rewrite", rule "simple.yaml", "--update", "--sweet", "--log-level=DEBUG", "--target=" ++ tgt, src]-            ["is newer than source", "skipping rewriting (--update)"]--    it "logs progress at debug level when printing to --target" $-      withStdin "[[ ]]" $-        withTempFile "targetXXXXXX.tmp" $ \(path, h) -> do-          hClose h-          testCLISucceeded-            ["rewrite", "--sweet", "--log-level=DEBUG", printf "--target=%s" path]-            ["The option '--target' is specified, printing to", "The command result was saved in"]--    it "logs progress at debug level when modifying a file in-place" $-      withTempFile "inplaceXXXXXX.phi" $ \(path, h) -> do-        hPutStr h "[[ x -> \"foo\" ]]"-        hClose h-        testCLISucceeded-          ["rewrite", rule "simple.yaml", "--in-place", "--sweet", "--log-level=DEBUG", path]-          ["The option '--in-place' is specified, writing back to", "was modified in-place"]--    it "rewrites with --update when source is newer than target" $-      withTempFileContent "src-XXXXXX.phi" "[[ x -> \"foo\" ]]" $ \src ->-        withTempFileContent "tgt-XXXXXX.phi" "ORIGINAL" $ \tgt -> do-          now <- getCurrentTime-          setModificationTime tgt (addUTCTime (-60) now)-          setModificationTime src now-          testCLISucceeded-            ["rewrite", rule "simple.yaml", "--update", "--sweet", "--target=" ++ tgt, src]-            []-          content <- readFile tgt-          content `shouldBe` "\"bar\":x\n"--    it "rewrites with cycles" $-      withStdin "[[ x -> \"x\" ]]" $-        testCLISucceeded-          ["rewrite", "--sweet", rule "infinite.yaml", "--max-depth=1", "--max-cycles=2"]-          ["\"x_hi_hi\":x"]--    it "hides default package" $-      withStdin "[[ org -> [[ eolang -> [[ number -> [[]] ]]]], x -> 42 ]]" $-        testCLISucceeded-          ["rewrite", "--sweet", "--flat", "--hide=Q.org"]-          ["42:x"]--    it "hides several FQNs" $-      withStdin "[[ org -> [[ eolang -> Q.x, yegor256 -> Q.y ]], x -> 42 ]]" $-        testCLISucceeded-          ["rewrite", "--sweet", "--flat", "--hide=Q.org.eolang", "--hide=Q.org.yegor256"]-          ["⟦ org ↦ ⟦⟧, x ↦ 42 ⟧"]--    it "shows and hides" $-      withStdin "[[ org -> [[ eolang -> Q.x, yegor256 -> Q.y ]], x -> 42 ]]" $-        testCLISucceeded-          ["rewrite", "--sweet", "--flat", "--show=Q.org", "--hide=Q.org.eolang"]-          ["Φ.y:yegor256:org"]--    it "hides an object outside the --show locator" $-      withStdin "[[ org -> [[ eolang -> Q.x ]], x -> 42 ]]" $-        testCLISucceeded-          ["rewrite", "--sweet", "--flat", "--show=Q.x", "--hide=Q.org.eolang"]-          ["42:x"]--    it "hides an object inside an earlier --hide locator" $-      withStdin "[[ org -> [[ eolang -> [[ number -> [[]] ]] ]], x -> 42 ]]" $-        testCLISucceeded-          ["rewrite", "--sweet", "--flat", "--hide=Q.org.eolang", "--hide=Q.org.eolang.number"]-          ["⟦ org ↦ ⟦⟧, x ↦ 42 ⟧"]--    it "fails on a --show locator that matches nothing" $-      withStdin "[[ a -> [[ b -> Q, c -> Q ]], d -> Q ]]" $-        testCLIFailed-          ["rewrite", "--flat", "--show=Q.zzz"]-          ["[ERROR]:", "Can't find object by locator: 'Φ.zzz'"]--    it "shows the whole program with --show=Q" $-      withStdin "[[ a -> [[ b -> Q, c -> Q ]], d -> Q ]]" $-        testCLISucceeded-          ["rewrite", "--flat", "--show=Q"]-          ["⟦ a ↦ ⟦ b ↦ Φ, c ↦ Φ ⟧, d ↦ Φ ⟧"]--    it "prints in line with --flat" $-      withStdin "[[ x -> 5, y -> \"hey\", z -> [[ w -> [[ ]] ]] ]]" $-        testCLISucceeded-          ["rewrite", "--sweet", "--flat"]-          ["⟦ x ↦ 5, y ↦ \"hey\", z ↦ ⟦⟧:w ⟧"]--    it "removes unnecessary rho bindings in primitive applications" $-      withStdin-        ( unlines-            [ "[["-            , "  z -> [[ x -> [[ t -> 42 ]].t ]].x,"-            , "  org -> [[ eolang -> [[ bytes -> [[ data -> ? ]], number -> [[ as-bytes -> ? ]] ]] ]]"-            , "]]"-            ]-        )-        ( testCLISucceeded-            ["rewrite", "--sweet", "--normalize", "--flat"]-            ["⟦ z ↦ 42, org ↦ ⟦ bytes(data) ↦ ⟦⟧, number(as-bytes) ↦ ⟦⟧ ⟧:eolang ⟧"]-        )--    it "reduces log message" $-      withStdin "[[ x -> [[ y -> ? ]](y -> 5) ]]" $-        testCLISucceeded-          ["rewrite", "--log-level=debug", "--log-lines=1", "--normalize"]-          [ intercalate-              "\n"-              [ "[DEBUG]: Applied 'copy' (32 nodes -> 27 nodes)"-              , "---| log is limited by --log-lines=1 option |---"-              ]-          ]--    it "reports a condition that raised while being evaluated" $-      withStdin "[[ x -> [[ y -> ∅ ]] ]]" $-        testCLISucceeded-          ["rewrite", rule "raising-condition.yaml", "--log-level=debug", "--flat"]-          [ "raised and was treated as not met: user error (Only data objects and bytes are supported"-          , "⟦ x ↦ ⟦ y ↦ ∅ ⟧ ⟧"-          ]--    it "canonizes expression" $-      withStdin "[[ x -> [[ y -> [[ L> Func ]].q, z -> Q.x(a -> [[ w -> [[ L> Atom ]], L> Hello ]]) ]], L> Package ]]" $-        testCLISucceeded-          ["rewrite", "--canonize", "--sweet", "--flat"]-          ["⟦ x ↦ ⟦ y ↦ Fn1:λ.q, z ↦ Φ.x( a ↦ ⟦ w ↦ Fn2:λ, λ ⤍ Fn3 ⟧ ) ⟧, λ ⤍ Package ⟧"]--    it "rewrites by locator" $-      withStdin "[[ ex -> [[ x -> [[ y -> 5 ]].y ]], abc -> [[ x -> ? ]](x -> 5) ]]" $-        testCLISucceeded-          ["rewrite", "--sweet", "--flat", "--locator=Q.ex", "--normalize"]-          ["⟦ ex ↦ 5:x, abc ↦ ∅:x( x ↦ 5 ) ⟧"]--    it "returns original expression on --breakpoint" $-      withStdin "[[ x -> ?, y -> $.x ]](x -> [[ D> 42- ]]).y" $-        testCLISucceeded-          ["rewrite", "--sweet", "--flat", "--normalize", "--breakpoint=stop", "--log-level=debug"]-          [ "Applied 'copy' (22 nodes -> 17 nodes)"-          , "Rule 'stop' is a breakpoint, dropping down all the previous rewritings..."-          , "⟦ x ↦ ∅, y ↦ x ⟧( x ↦ 42-:Δ ).y"-          ]--    it "finishes under --depth-sensitive when the only match left would not change the term" $-      withTempFileContent "phino-fixpoint.yaml" "name: fix\npattern: '[[ x -> !e1, !B1 ]]'\nresult: '[[ x -> Q, !B1 ]]'\n" $ \fix ->-        withStdin "[[ x -> $ ]]" $-          testCLISucceeded ["rewrite", "--rule=" ++ fix, "--max-depth=1", "--depth-sensitive", "--flat"] ["⟦ x ↦ Φ ⟧"]--    it "keeps the rewritten expression when the --breakpoint rule fired" $-      withStdin "⟦ a ↦ ⟦ b ↦ Φ ⟧.b ⟧" $-        testCLISucceeded-          ["rewrite", "--flat", "--normalize", "--breakpoint=dot"]-          ["⟦ a ↦ Φ( ρ ↦ ⟦ b ↦ Φ ⟧ ) ⟧"]--  describe "morph --focus under --locator" $ do-    it "finds the same object for the steps and for the answer" $-      withStdin "⟦ t ↦ ⟦ a ↦ ⟦ Δ ⤍ 01- ⟧ ⟧, a ↦ ⟦ Δ ⤍ 02- ⟧ ⟧" $ do-        (out, _) <- withStdout (runCLI ["morph", "--locator=Q.t", "--focus=Q.a", "--flat", "--sequence"])-        filter (not . null) (lines out) `shouldSatisfy` all (== "⟦ Δ ⤍ 02- ⟧")-    it "prints the answer when --focus names the object at --locator" $-      withStdin "⟦ t ↦ ⟦ a ↦ ⟦ Δ ⤍ 01- ⟧ ⟧ ⟧" $-        testCLISucceeded ["morph", "--locator=Q.t", "--focus=Q.t", "--flat", "--sequence"] ["⟦ a ↦ ⟦ Δ ⤍ 01- ⟧ ⟧"]-    it "fails on a --focus it cannot find before it prints any step" $-      withStdin "⟦ t ↦ ⟦ a ↦ ⟦ Δ ⤍ 01- ⟧ ⟧ ⟧" $ do-        (out, _) <- withStdout (try (runCLI ["morph", "--locator=Q.t", "--focus=Q.nope", "--flat", "--sequence"]) :: IO (Either ExitCode ()))-        out `shouldNotContain` "⟦ t ↦"--  it "fails on a --hide locator that matches nothing, as --show does" $-    withStdin "[[ x -> Q.y ]]" $-      testCLIFailed ["rewrite", "--hide=Q.nope"] ["Can't find object by locator: 'Φ.nope'"]--  it "names the term dataization cannot take, and says a normal form is expected" $-    withStdin "⟦ x ↦ ⟦ b ↦ ⟦ Δ ⤍ 01- ⟧ ⟧.b ⟧" $-      testCLIFailed ["dataize", "--locator=Q.x"] ["expects a normal form: 01-:Δ:b.b"]--  describe "--partial parks a universe that --max-cycles stops" $ do-    let heavy = "⟦ simple ↦ ⟦ Δ ⤍ 01- ⟧, heavy ↦ ⟦ a ↦ ⟦ b ↦ ⟦ c ↦ ⟦ Δ ⤍ 02- ⟧ ⟧ ⟧ ⟧.a.b.c ⟧"-    it "in dataize" $-      withStdin heavy $-        testCLISucceeded ["dataize", "--locator=Q.simple", "--max-cycles=1", "--partial", "--flat"] ["⟦ Δ ⤍ 01- ⟧"]-    it "in morph" $-      withStdin heavy $-        testCLISucceeded ["morph", "--locator=Q.simple", "--max-cycles=1", "--partial", "--flat"] ["⟦ Δ ⤍ 01- ⟧"]--  describe "dataize" $ do-    it "prints help" $-      testCLISucceeded ["dataize", "--help"] ["Dataize the 𝜑-expression"]--    it "names every block of a --symbolic entry in its help" $-      testCLISucceeded ["dataize", "--help"] ["\"dataize\"", "\"morph\"", "\"evaluate\"", "\"defer\"", "\"stay\"", "\"rewrite\"", "\"symbolize\"", "\"join\""]--    it "dataizes simple expression" $-      withStdin "[[ D> 01- ]]" $-        testCLISucceeded ["dataize"] ["01-"]--    it "accepts --seed flag" $-      withStdin "[[ D> 01- ]]" $-        testCLISucceeded ["dataize", "--seed=7"] ["01-"]--    it "fails to dataize an empty object, which dataizes the terminator ⊥" $-      withStdin "[[ ]]" $-        testCLIFailed ["dataize"] ["terminator ⊥"]--    it "fails with negative --max-steps" $-      withStdin "[[ D> 01- ]]" $-        testCLIFailed ["dataize", "--max-steps=-1"] ["--max-steps must be positive"]--    it "fails on --max-steps instead of dataizing forever" $-      loopingLambdas $ \endless ->-        withStdin "⟦ @ ↦ ⟦ λ ⤍ L_loop ⟧ ⟧" $-          testCLIFailed-            ["dataize", "--symbolic=" ++ endless, "--max-steps=40"]-            ["[ERROR]: Morphing did not finish before reaching the limit of steps: --max-steps=40"]--    it "parks --max-steps on a residual with --partial" $-      loopingLambdas $ \endless ->-        withStdin "⟦ @ ↦ ⟦ λ ⤍ L_loop ⟧ ⟧" $-          testCLISucceeded-            ["dataize", "--symbolic=" ++ endless, "--max-steps=40", "--partial", "--flat", "--hide-rho"]-            ["⟦ λ ⤍ L_loop ⟧"]--    it "fails on --max-firings before --max-steps is spent" $-      loopingLambdas $ \endless ->-        withStdin "⟦ @ ↦ ⟦ λ ⤍ L_loop ⟧ ⟧" $-          testCLIFailed-            ["dataize", "--symbolic=" ++ endless, "--max-steps=400", "--max-firings=5"]-            ["[ERROR]: Evaluation did not finish before reaching the limit of firings: --max-firings=5"]--    describe "--acyclic=proven" $ do-      let circling = "⟦ cyc ↦ ⟦ x ↦ ∅, φ ↦ Φ.cyc( ξ.x ) ⟧, t ↦ Φ.cyc( ⟦⟧ ) ⟧"-      it "spends the whole budget and fails on the limit without the flag" $-        withStdin circling $-          testCLIFailed-            ["dataize", "--locator=Q.t", "--max-steps=40"]-            ["[ERROR]: Morphing did not finish before reaching the limit of steps: --max-steps=40"]--      it "names the term it came back to with the flag" $-        withStdin circling $-          testCLIFailed-            ["dataize", "--locator=Q.t", "--acyclic=proven", "--max-steps=4000"]-            ["[ERROR]: Reduction entered a formation it is already inside:"]--      it "prints the residue and exits successfully with --partial" $-        withStdin circling $-          testCLISucceeded-            ["dataize", "--locator=Q.t", "--acyclic=proven", "--partial", "--max-steps=4000", "--flat", "--hide-rho"]-            ["Φ.cyc( α0 ↦ ⟦⟧ )"]--      it "writes the cut to the protocol where the formation would have opened" $-        withTempFile "protocolXXXXXX.txt" $ \(path, stream) -> do-          hClose stream-          withStdin circling $-            testCLISucceeded-              ["dataize", "--locator=Q.t", "--acyclic=proven", "--partial", "--protocol=" ++ path, "--sweet", "--hide-rho", "--flat", "--quiet"]-              []-          records <- readProtocol path-          lines records-            `shouldBe` [ "𝔻(Φ.t):"-                       , "  𝕄(Φ.t):"-                       , "    𝑛·0·1 := Φ.cyc( x ↦ ⟦⟧ )"-                       , "    𝑛·0·2 := Φ.cyc( x ↦ ⟦⟧ )"-                       , "  looped 𝑛·0·2  # proven"-                       ]--      it "writes the cut to the XML protocol with the formation in an element of its own" $-        withTempFile "protocolXXXXXX.xml" $ \(path, stream) -> do-          hClose stream-          withStdin circling $-            testCLISucceeded-              ["dataize", "--locator=Q.t", "--acyclic=proven", "--partial", "--protocol=" ++ path, "--sweet", "--hide-rho", "--flat", "--quiet"]-              []-          records <- readProtocol path-          lines records-            `shouldBe` [ "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"-                       , "<dataize at=\"Φ.t\">"-                       , "  <morph at=\"Φ.t\">"-                       , "    <formed meta=\"𝑛·0·1\"/>"-                       , "    <applied meta=\"𝑛·0·2\" by=\"morph\" at=\"Φ.t\" of=\"Φ.cyc\"><meta attr=\"x\">𝑛·0·1</meta></applied>"-                       , "    <morphed meta=\"𝑛·0·3\">𝑛·0·2</morphed>"-                       , "    <applied meta=\"𝑛·0·4\" by=\"morph\" at=\"Φ.t\" of=\"Φ.cyc\"><meta attr=\"x\">𝑛·0·1</meta></applied>"-                       , "    <morphed meta=\"𝑛·0·5\">𝑛·0·4</morphed>"-                       , "  </morph>"-                       , "  <looped by=\"dataize\" match=\"proven\" at=\"Φ.t\" of=\"𝑛·0·4\"/>"-                       , "</dataize>"-                       ]--      it "writes a plausible cut to the protocol as plausible" $-        withTempFile "protocolXXXXXX.txt" $ \(path, stream) -> do-          hClose stream-          withStdin circling $-            testCLISucceeded-              ["dataize", "--locator=Q.t", "--acyclic=plausible", "--partial", "--protocol=" ++ path, "--sweet", "--hide-rho", "--flat", "--quiet"]-              []-          records <- readProtocol path-          lines records `shouldContain` ["  looped 𝑛·0·2  # plausible"]--      it "refuses the flag without a mode" $-        withStdin "⟦ t ↦ ⟦ Δ ⤍ 01-02 ⟧ ⟧" $-          testCLIFailed ["dataize", "--locator=Q.t", "--acyclic"] ["The option `--acyclic` expects an argument"]--      it "refuses a mode it does not know" $-        withStdin "⟦ t ↦ ⟦ Δ ⤍ 01-02 ⟧ ⟧" $-          testCLIFailed ["dataize", "--locator=Q.t", "--acyclic=sure"] ["The value 'sure' can't be used for '--acyclic' option"]--      it "answers a terminating program the same way with the flag" $-        withStdin "⟦ t ↦ ⟦ Δ ⤍ 01-02 ⟧ ⟧" $-          testCLISucceeded ["dataize", "--locator=Q.t", "--acyclic=proven"] ["01-02"]--    describe "--max-loops" $ do-      let once = "⟦ f ↦ ⟦ x ↦ ∅, φ ↦ ξ.x.again ⟧, start ↦ ⟦ again ↦ Φ.f( x ↦ Φ.stop( v ↦ Φ.start ) ) ⟧, stop ↦ ⟦ v ↦ ∅, again ↦ ⟦ Δ ⤍ 01- ⟧ ⟧, t ↦ Φ.f( x ↦ Φ.start ) ⟧"-      it "cuts a recursion that stops after one round without the flag" $-        withStdin once $-          testCLIFailed-            ["dataize", "--locator=Q.t", "--acyclic=plausible"]-            ["[ERROR]: Reduction entered a formation it is already inside:"]--      it "dataizes a recursion that stops after one round with --max-loops=1" $-        withStdin once $-          testCLISucceeded ["dataize", "--locator=Q.t", "--acyclic=plausible", "--max-loops=1"] ["01-"]--      it "cuts a recursion that never stops with --max-loops=1" $-        withStdin "⟦ cyc ↦ ⟦ x ↦ ∅, φ ↦ Φ.cyc( ξ.x ) ⟧, t ↦ Φ.cyc( ⟦⟧ ) ⟧" $-          testCLIFailed-            ["dataize", "--locator=Q.t", "--acyclic=proven", "--max-loops=1", "--max-steps=4000"]-            ["[ERROR]: Reduction entered a formation it is already inside:"]--      it "refuses a negative count" $-        withStdin once $-          testCLIFailed ["dataize", "--locator=Q.t", "--acyclic=plausible", "--max-loops=-1"] ["--max-loops must be >= 0"]--      it "refuses the flag without --acyclic" $-        withStdin once $-          testCLIFailed ["dataize", "--locator=Q.t", "--max-loops=1"] ["The option --max-loops requires --acyclic, since only --acyclic cuts a loop"]--    it "dataizes with --sequence" $-      withStdin "[[ @ -> [[ x -> [[ D> 01-, y -> ? ]](y -> [[ ]]) ]].x ]]" $-        testCLISucceeded-          ["dataize", "--sequence", "--output=latex", "--flat", "--sweet"]-          [ intercalate-              "\n"-              [ "\\begin{phiquation}"-              , "[[ D> |01-|, |y| -> ? ]] ( |y| -> [[]] ) : |x| . |x| : @ \\phiDataize[\\nameref{r:box}]"-              , "  \\phiDataize [[ D> |01-|, |y| -> ? ]] ( |y| -> [[]] ) : |x| . |x| : @ . @ \\phiNormalize[\\nameref{r:copy}]"-              , "  \\phiNormalize [[ D> |01-|, |y| -> [[]] ]] : |x| . |x| : @ . @ \\phiNormalize[\\nameref{r:dot}]"-              , "  \\phiNormalize [[ D> |01-|, |y| -> [[]] ]] ( \\phiTerminal{\\rho} -> [[ D> |01-|, |y| -> [[]] ]] : |x| ) : @ . @ \\phiNormalize[\\nameref{r:skip}]"-              , "  \\phiNormalize [[ D> |01-|, |y| -> [[]] ]] : @ . @ \\phiNormalize[\\nameref{r:dot}]"-              , "  \\phiNormalize [[ D> |01-|, |y| -> [[]] ]] ( \\phiTerminal{\\rho} -> Q ) \\phiNormalize[\\nameref{r:skip}]"-              , "  \\phiNormalize [[ D> |01-|, |y| -> [[]] ]] \\phiDataize[\\nameref{r:delta}]"-              , "  \\phiDataize |01-|{.}"-              , "\\end{phiquation}"-              , "01-"-              ]-          ]--    it "keeps the delta step in --sequence under --quiet" $-      withStdin "[[ D> 01- ]]" $-        testCLISucceeded-          ["dataize", "--sequence", "--quiet", "--output=latex", "--flat", "--sweet"]-          [ intercalate-              "\n"-              [ "|01-| : D \\phiDataize[\\nameref{r:delta}]"-              , "  \\phiDataize |01-|{.}"-              , "\\end{phiquation}"-              ]-          ]--    it "ends the phi --sequence at the bare data" $-      withStdin "[[ D> 01- ]]" $-        testCLISucceeded-          ["dataize", "--sequence", "--quiet", "--flat", "--sweet"]-          ["01-:Δ\n01-"]--    it "focuses a compressed sequence whose meet replaces a step root" $-      withStdin "[[ @ -> [[ @ -> $.c.plus( 32.0 ), c -> 25.0 ]], bytes ↦ ⟦ φ ↦ ∅ ⟧, number(φ) -> [[ plus -> [[ ^ -> ?, x -> ?, L> L_number_plus ]] ]] ]]" $-        testCLISucceeded-          ["dataize", symbolic, "--output=latex", "--sweet", "--nonumber", "--compress", "--canonize", "--meet-prefix=dataization", "--sequence", "--flat", "--quiet", "--hide=Q.bytes", "--hide=Q.number", "--locator=Q.@", "--focus=Q.@", "--meet-length=5", "--meet-popularity=1"]-          ["\\phinoMeet{dataization:1}{ [[ @ -> |c| . |plus| ( |32| ), |c| -> |25| ]] } \\phiDataize[\\nameref{r:box}]"]--    it "compresses a canonized whole-expression sequence into a meet" $-      withStdin "[[ @ -> [[ @ -> $.c.plus( 32.0 ), c -> 25.0 ]], bytes ↦ ⟦ φ ↦ ∅ ⟧, number(φ) -> [[ plus -> [[ ^ -> ?, x -> ?, L> L_number_plus ]] ]] ]]" $-        testCLISucceeded-          ["dataize", symbolic, "--output=latex", "--sweet", "--nonumber", "--compress", "--canonize", "--meet-prefix=dataization", "--sequence", "--flat", "--quiet", "--meet-length=5", "--meet-popularity=1"]-          ["\\phinoMeet{dataization:1}"]--    it "canonizes the residue it prints with --partial" $-      withStdin "[[ @ -> [[ L> Foo ]] ]]" $-        testCLISucceeded ["dataize", "--partial", "--canonize", "--flat", "--sweet"] ["Fn1:λ"]--    it "dataizes with --locator" $-      withStdin "[[ ex -> [[ @ -> Q.x ]], x -> [[ D> 42- ]] ]]" $-        testCLISucceeded ["dataize", "--locator=Q.ex"] ["42-"]--    it "does not print bytes with --quiet" $-      withStdin "[[ D> 01- ]]" $-        testCLISucceeded ["dataize", "--quiet"] []--    describe "--abridged" $ do-      let wide = "⟦ t ↦ ⟦ φ ↦ ⟦ Δ ⤍ 01-02 ⟧, anfang ↦ ξ.schluss, mitte ↦ ξ.anfang, schluss ↦ ξ.mitte, rand ↦ ξ.schluss ⟧ ⟧"-          stuck = "⟦ t ↦ ⟦ λ ⤍ L_weit, anfang ↦ ξ.schluss, mitte ↦ ξ.anfang, schluss ↦ ξ.mitte, rand ↦ ξ.schluss ⟧ ⟧"-      it "folds a long formation in the text protocol" $-        withTempFile "protocolXXXXXX.txt" $ \(path, stream) -> do-          hClose stream-          withStdin stuck $-            testCLISucceeded ["dataize", "--locator=Q.t", "--partial", "--protocol=" ++ path, "--abridged", "--sweet", "--hide-rho", "--quiet"] []-          records <- readProtocol path-          lines records `shouldContain` ["  unanswered L_weit  # 𝔻(⟦ λ ⤍ L_weit, +4 ⟧)"]-      it "names a long formation in the XML protocol by its path rather than folding it" $-        withTempFile "protocolXXXXXX.xml" $ \(path, stream) -> do-          hClose stream-          withStdin stuck $-            testCLISucceeded ["dataize", "--locator=Q.t", "--partial", "--protocol=" ++ path, "--abridged", "--sweet", "--hide-rho", "--quiet"] []-          records <- readProtocol path-          lines records `shouldContain` ["  <unanswered λ=\"L_weit\" by=\"dataize\">Φ.t</unanswered>"]-      forM_-        [ ("textXXXXXX.txt", "      𝛿·1·1 := 01-02-..(8b)..-0B-0C")-        , ("XMLXXXXXX.xml", "      <dataized meta=\"𝛿·1·1\">01-02-..(8b)..-0B-0C</dataized>")-        ]-        ( \(template, line) ->-            it ("cuts a long datum a firing came down to under --abridged-data, as " ++ line) $-              withTempFile template $ \(path, stream) -> do-                hClose stream-                withLambdasOf (T.pack "- λ: L_outer\n  dataize:\n    𝛿1: ξ.arg\n  𝑛: ⟦ λ ⤍ 𝜎 ⟧\n") $ \outer ->-                  withStdin "⟦ x ↦ ⟦ arg ↦ ⟦ Δ ⤍ 01-02-03-04-05-06-07-08-09-0A-0B-0C ⟧, λ ⤍ L_outer ⟧ ⟧" $-                    testCLISucceeded ["dataize", "--symbolic=" ++ outer, "--locator=Q.x", "--partial", "--protocol=" ++ path, "--abridged", "--abridged-data", "--sweet", "--hide-rho", "--quiet"] []-                records <- readProtocol path-                lines records `shouldContain` [line]-        )-      forM_-        [ ("textXXXXXX.txt", "      𝛿·1·1 := 30-31-32-33-34-35-36-37-38-39-41-42-43-44-45-46")-        , ("XMLXXXXXX.xml", "      <dataized meta=\"𝛿·1·1\">30-31-32-33-34-35-36-37-38-39-41-42-43-44-45-46</dataized>")-        ]-        ( \(template, line) ->-            it ("keeps a long datum a firing came down to whole without --abridged-data, as " ++ line) $-              withTempFile template $ \(path, stream) -> do-                hClose stream-                withLambdasOf (T.pack "- λ: L_hex\n  dataize:\n    𝛿1: ξ.arg\n  𝑛: ⟦ λ ⤍ 𝜎 ⟧\n") $ \hex ->-                  withStdin "⟦ x ↦ ⟦ arg ↦ ⟦ Δ ⤍ 30-31-32-33-34-35-36-37-38-39-41-42-43-44-45-46 ⟧, λ ⤍ L_hex ⟧ ⟧" $-                    testCLISucceeded ["dataize", "--symbolic=" ++ hex, "--locator=Q.x", "--partial", "--protocol=" ++ path, "--abridged", "--sweet", "--hide-rho", "--quiet"] []-                records <- readProtocol path-                lines records `shouldContain` [line]-        )-      it "folds a long formation under the width given as the value" $-        withTempFile "protocolXXXXXX.txt" $ \(path, stream) -> do-          hClose stream-          withStdin stuck $-            testCLISucceeded ["dataize", "--locator=Q.t", "--partial", "--protocol=" ++ path, "--abridged=64", "--sweet", "--hide-rho", "--quiet"] []-          records <- readProtocol path-          lines records `shouldContain` ["  unanswered L_weit  # 𝔻(⟦ λ ⤍ L_weit, +4 ⟧)"]-      it "keeps a formation whole under a width it fits in" $-        withTempFile "protocolXXXXXX.txt" $ \(path, stream) -> do-          hClose stream-          withStdin stuck $-            testCLISucceeded ["dataize", "--locator=Q.t", "--partial", "--protocol=" ++ path, "--abridged=200", "--sweet", "--hide-rho", "--quiet"] []-          records <- readProtocol path-          lines records `shouldContain` ["  unanswered L_weit  # 𝔻(⟦ λ ⤍ L_weit, anfang ↦ schluss, mitte ↦ anfang, schluss ↦ mitte, rand ↦ schluss ⟧)"]-      it "refuses a width that is not a number" $-        withStdin wide $-          testCLIFailed ["dataize", "--locator=Q.t", "--protocol=breit.txt", "--abridged=breit"] ["cannot parse value `breit'"]-      it "leaves the printed result whole" $-        withTempFile "protocolXXXXXX.txt" $ \(path, stream) -> do-          hClose stream-          withStdin wide $-            testCLISucceeded ["morph", "--locator=Q.t", "--protocol=" ++ path, "--abridged", "--sweet", "--hide-rho", "--flat"] ["anfang ↦ schluss, mitte ↦ anfang"]-      it "refuses the flag without a protocol" $-        withStdin wide $-          testCLIFailed ["dataize", "--locator=Q.t", "--abridged"] ["The option --abridged requires --protocol"]-      it "refuses to cut the data in dataize without --abridged" $-        withStdin wide $-          testCLIFailed ["dataize", "--locator=Q.t", "--protocol=daten.txt", "--abridged-data"] ["The option --abridged-data requires --abridged"]-      it "refuses to cut the data in morph without --abridged" $-        withStdin wide $-          testCLIFailed ["morph", "--locator=Q.t", "--protocol=daten.xml", "--abridged-data"] ["The option --abridged-data requires --abridged"]--    describe "--protocol" $ do-      let sum' = "[[ bytes ↦ ⟦ φ ↦ ∅ ⟧, number(φ) -> [[ plus(^, x) -> [[ L> L_number_plus ]] ]], @ -> 5.plus(6) ]]"-          chained = "[[ bytes ↦ ⟦ φ ↦ ∅ ⟧, number(φ) -> [[ plus(^, x) -> [[ L> L_number_plus ]] ]], @ -> 5.plus(6).plus(7) ]]"-          nested = "[[ bytes ↦ ⟦ φ ↦ ∅ ⟧, number(φ) -> [[ plus(^, x) -> [[ L> L_number_plus ]] ]], @ -> 5.plus(6.plus(7)) ]]"-          mixed = "[[ bytes ↦ ⟦ φ ↦ ∅ ⟧, number(φ) -> [[ plus(^, x) -> [[ L> L_number_plus ]], times(^, x) -> [[ L> L_number_times ]] ]], @ -> 5.plus(6).times(7) ]]"-      it "opens the protocol with the run it is the protocol of" $-        withTempFile "protocolXXXXXX.txt" $ \(path, stream) -> do-          hClose stream-          withStdin "[[ D> 01- ]]" $-            testCLISucceeded ["dataize", "--protocol=" ++ path, "--quiet"] []-          records <- readProtocol path-          records `shouldBe` "𝔻(Φ):\n  𝛿·0·1 := 01-\n"--      it "closes the protocol with its msec on the third line from the end" $-        withTempFile "protocolXXXXXX.txt" $ \(path, stream) -> do-          hClose stream-          withStdin sum' $-            testCLISucceeded ["dataize", symbolic, "--protocol=" ++ path, "--quiet"] []-          records <- readUtf8 path-          (lines records !! (length (lines records) - 3)) `shouldSatisfy` isPrefixOf "msec "--      it "closes the protocol with the firings it counted" $-        withTempFile "protocolXXXXXX.txt" $ \(path, stream) -> do-          hClose stream-          withStdin sum' $-            testCLISucceeded ["dataize", symbolic, "--protocol=" ++ path, "--quiet"] []-          records <- readUtf8 path-          lines records `shouldContain` ["firings 1"]--      it "closes the protocol with its fps on the last line" $-        withTempFile "protocolXXXXXX.txt" $ \(path, stream) -> do-          hClose stream-          withStdin sum' $-            testCLISucceeded ["dataize", symbolic, "--protocol=" ++ path, "--quiet"] []-          records <- readUtf8 path-          last (lines records) `shouldSatisfy` isPrefixOf "fps "--      it "closes the protocol with zero firings when the run fires nothing" $-        withTempFile "protocolXXXXXX.txt" $ \(path, stream) -> do-          hClose stream-          withStdin "[[ D> 01- ]]" $-            testCLISucceeded ["dataize", "--protocol=" ++ path, "--quiet"] []-          records <- readUtf8 path-          lines records `shouldContain` ["firings 0"]--      it "writes one line per operand and one per answer of a firing" $-        withTempFile "protocolXXXXXX.txt" $ \(path, stream) -> do-          hClose stream-          withStdin sum' $-            testCLISucceeded ["dataize", symbolic, "--protocol=" ++ path, "--quiet", "--sweet", "--hide-rho"] []-          records <- readProtocol path-          lines records-            `shouldBe` [ "𝔻(Φ):"-                       , "  𝕄(Φ):"-                       , "    𝑛·0·1 := 5"-                       , "    𝑛·0·2 := 𝑛·0·1.plus( x ↦ 6 )"-                       , "  𝔼(L_number_plus):"-                       , "    𝔻(Φ.a🌵0):"-                       , "      𝕄(Φ.a🌵0):"-                       , "        𝑛·1·1 := 5"-                       , "        𝑛·1·2 := Φ.bytes( φ ↦ 40-14-00-00-00-00-00-00:Δ )"-                       , "      𝛿·1·1 := 40-14-00-00-00-00-00-00  # 5.0"-                       , "    𝛿1·1 := 𝛿·1·1  # 𝔻(ξ.ρ)"-                       , "    𝔻(Φ.a🌵1):"-                       , "      𝕄(Φ.a🌵1):"-                       , "        𝑛·1·3 := 6"-                       , "        𝑛·1·4 := Φ.bytes( φ ↦ 40-18-00-00-00-00-00-00:Δ )"-                       , "      𝛿·1·2 := 40-18-00-00-00-00-00-00  # 6.0"-                       , "    𝛿2·1 := 𝛿·1·2  # 𝔻(ξ.x)"-                       , "    𝑛·1·5 := Φ.number( φ ↦ 𝜎1:λ )  # 𝑛"-                       , "    𝕄(𝑛·1·5):"-                       , "      𝑛·1·6 := Φ.number( φ ↦ 𝜎1:λ )"-                       , "    𝑛·1·7 := 𝑛·1·6  # 𝕄(𝑛·1·5)"-                       ]--      it "numbers the firings of one entry apart and names the symbol between them" $-        withTempFile "protocolXXXXXX.txt" $ \(path, stream) -> do-          hClose stream-          withStdin chained $-            testCLISucceeded ["dataize", symbolic, "--protocol=" ++ path, "--quiet", "--sweet", "--hide-rho"] []-          records <- readProtocol path-          lines records-            `shouldBe` [ "𝔻(Φ):"-                       , "  𝕄(Φ):"-                       , "    𝑛·0·1 := 5"-                       , "    𝑛·0·2 := 𝑛·0·1.plus( x ↦ 6 )"-                       , "    𝔼(L_number_plus):"-                       , "      𝔻(Φ.a🌵0):"-                       , "        𝕄(Φ.a🌵0):"-                       , "          𝑛·1·1 := 5"-                       , "          𝑛·1·2 := Φ.bytes( φ ↦ 40-14-00-00-00-00-00-00:Δ )"-                       , "        𝛿·1·1 := 40-14-00-00-00-00-00-00  # 5.0"-                       , "      𝛿1·1 := 𝛿·1·1  # 𝔻(ξ.ρ)"-                       , "      𝔻(Φ.a🌵1):"-                       , "        𝕄(Φ.a🌵1):"-                       , "          𝑛·1·3 := 6"-                       , "          𝑛·1·4 := Φ.bytes( φ ↦ 40-18-00-00-00-00-00-00:Δ )"-                       , "        𝛿·1·2 := 40-18-00-00-00-00-00-00  # 6.0"-                       , "      𝛿2·1 := 𝛿·1·2  # 𝔻(ξ.x)"-                       , "      𝑛·1·5 := Φ.number( φ ↦ 𝜎1:λ )  # 𝑛"-                       , "      𝕄(𝑛·1·5):"-                       , "        𝑛·1·6 := Φ.number( φ ↦ 𝜎1:λ )"-                       , "      𝑛·1·7 := 𝑛·1·6  # 𝕄(𝑛·1·5)"-                       , "    𝑛·0·3 := 𝑛·1·6.plus( x ↦ 7 )"-                       , "  𝔼(L_number_plus):"-                       , "    𝔻(Φ.a🌵2):"-                       , "      𝕄(Φ.a🌵2):"-                       , "        𝑛·2·1 := Φ.number( φ ↦ 𝜎1:λ )"-                       , "    𝛿1·2 := 𝔻(𝜎1:λ)  # 𝔻(ξ.ρ)"-                       , "    𝔻(Φ.a🌵3):"-                       , "      𝕄(Φ.a🌵3):"-                       , "        𝑛·2·2 := 7"-                       , "        𝑛·2·3 := Φ.bytes( φ ↦ 40-1C-00-00-00-00-00-00:Δ )"-                       , "      𝛿·2·1 := 40-1C-00-00-00-00-00-00  # 7.0"-                       , "    𝛿2·2 := 𝛿·2·1  # 𝔻(ξ.x)"-                       , "    𝑛·2·4 := Φ.number( φ ↦ 𝜎2:λ )  # 𝑛"-                       , "    𝕄(𝑛·2·4):"-                       , "      𝑛·2·5 := Φ.number( φ ↦ 𝜎2:λ )"-                       , "    𝑛·2·6 := 𝑛·2·5  # 𝕄(𝑛·2·4)"-                       ]--      it "numbers the firings of different entries apart" $-        withTempFile "protocolXXXXXX.txt" $ \(path, stream) -> do-          hClose stream-          withStdin mixed $-            testCLISucceeded ["dataize", symbolic, "--protocol=" ++ path, "--quiet", "--sweet", "--hide-rho"] []-          records <- readProtocol path-          lines records-            `shouldBe` [ "𝔻(Φ):"-                       , "  𝕄(Φ):"-                       , "    𝑛·0·1 := 5"-                       , "    𝑛·0·2 := 𝑛·0·1.plus( x ↦ 6 )"-                       , "    𝔼(L_number_plus):"-                       , "      𝔻(Φ.a🌵0):"-                       , "        𝕄(Φ.a🌵0):"-                       , "          𝑛·1·1 := 5"-                       , "          𝑛·1·2 := Φ.bytes( φ ↦ 40-14-00-00-00-00-00-00:Δ )"-                       , "        𝛿·1·1 := 40-14-00-00-00-00-00-00  # 5.0"-                       , "      𝛿1·1 := 𝛿·1·1  # 𝔻(ξ.ρ)"-                       , "      𝔻(Φ.a🌵1):"-                       , "        𝕄(Φ.a🌵1):"-                       , "          𝑛·1·3 := 6"-                       , "          𝑛·1·4 := Φ.bytes( φ ↦ 40-18-00-00-00-00-00-00:Δ )"-                       , "        𝛿·1·2 := 40-18-00-00-00-00-00-00  # 6.0"-                       , "      𝛿2·1 := 𝛿·1·2  # 𝔻(ξ.x)"-                       , "      𝑛·1·5 := Φ.number( φ ↦ 𝜎1:λ )  # 𝑛"-                       , "      𝕄(𝑛·1·5):"-                       , "        𝑛·1·6 := Φ.number( φ ↦ 𝜎1:λ )"-                       , "      𝑛·1·7 := 𝑛·1·6  # 𝕄(𝑛·1·5)"-                       , "    𝑛·0·3 := 𝑛·1·6.times( x ↦ 7 )"-                       , "  𝔼(L_number_times):"-                       , "    𝔻(Φ.a🌵2):"-                       , "      𝕄(Φ.a🌵2):"-                       , "        𝑛·2·1 := Φ.number( φ ↦ 𝜎1:λ )"-                       , "    𝛿1·2 := 𝔻(𝜎1:λ)  # 𝔻(ξ.ρ)"-                       , "    𝔻(Φ.a🌵3):"-                       , "      𝕄(Φ.a🌵3):"-                       , "        𝑛·2·2 := 7"-                       , "        𝑛·2·3 := Φ.bytes( φ ↦ 40-1C-00-00-00-00-00-00:Δ )"-                       , "      𝛿·2·1 := 40-1C-00-00-00-00-00-00  # 7.0"-                       , "    𝛿2·2 := 𝛿·2·1  # 𝔻(ξ.x)"-                       , "    𝑛·2·4 := Φ.number( φ ↦ 𝜎2:λ )  # 𝑛"-                       , "    𝕄(𝑛·2·4):"-                       , "      𝑛·2·5 := Φ.number( φ ↦ 𝜎2:λ )"-                       , "    𝑛·2·6 := 𝑛·2·5  # 𝕄(𝑛·2·4)"-                       ]--      it "nests the firing an operand of another firing brought down" $-        withTempFile "protocolXXXXXX.txt" $ \(path, stream) -> do-          hClose stream-          withStdin nested $-            testCLISucceeded ["dataize", symbolic, "--protocol=" ++ path, "--quiet", "--sweet", "--hide-rho"] []-          records <- readProtocol path-          lines records-            `shouldBe` [ "𝔻(Φ):"-                       , "  𝕄(Φ):"-                       , "    𝑛·0·1 := 5"-                       , "    𝑛·0·2 := 𝑛·0·1.plus( x ↦ 6.plus( 7 ) )"-                       , "  𝔼(L_number_plus):"-                       , "    𝔻(Φ.a🌵0):"-                       , "      𝕄(Φ.a🌵0):"-                       , "        𝑛·1·1 := 5"-                       , "        𝑛·1·2 := Φ.bytes( φ ↦ 40-14-00-00-00-00-00-00:Δ )"-                       , "      𝛿·1·1 := 40-14-00-00-00-00-00-00  # 5.0"-                       , "    𝛿1·1 := 𝛿·1·1  # 𝔻(ξ.ρ)"-                       , "    𝔻(Φ.a🌵1):"-                       , "      𝕄(Φ.a🌵1):"-                       , "        𝑛·1·3 := 6"-                       , "        𝑛·1·4 := 𝑛·1·3.plus( x ↦ 7 )"-                       , "      𝔼(L_number_plus):"-                       , "        𝔻(Φ.a🌵2):"-                       , "          𝕄(Φ.a🌵2):"-                       , "            𝑛·2·1 := 6"-                       , "            𝑛·2·2 := Φ.bytes( φ ↦ 40-18-00-00-00-00-00-00:Δ )"-                       , "          𝛿·2·1 := 40-18-00-00-00-00-00-00  # 6.0"-                       , "        𝛿1·2 := 𝛿·2·1  # 𝔻(ξ.ρ)"-                       , "        𝔻(Φ.a🌵3):"-                       , "          𝕄(Φ.a🌵3):"-                       , "            𝑛·2·3 := 7"-                       , "            𝑛·2·4 := Φ.bytes( φ ↦ 40-1C-00-00-00-00-00-00:Δ )"-                       , "          𝛿·2·2 := 40-1C-00-00-00-00-00-00  # 7.0"-                       , "        𝛿2·2 := 𝛿·2·2  # 𝔻(ξ.x)"-                       , "        𝑛·2·5 := Φ.number( φ ↦ 𝜎1:λ )  # 𝑛"-                       , "        𝕄(𝑛·2·5):"-                       , "          𝑛·2·6 := Φ.number( φ ↦ 𝜎1:λ )"-                       , "        𝑛·2·7 := 𝑛·2·6  # 𝕄(𝑛·2·5)"-                       , "    𝛿2·1 := 𝔻(𝜎1:λ)  # 𝔻(ξ.x)"-                       , "    𝑛·1·5 := Φ.number( φ ↦ 𝜎2:λ )  # 𝑛"-                       , "    𝕄(𝑛·1·5):"-                       , "      𝑛·1·6 := Φ.number( φ ↦ 𝜎2:λ )"-                       , "    𝑛·1·7 := 𝑛·1·6  # 𝕄(𝑛·1·5)"-                       ]--      it "writes a stuck firing to the XML protocol" $-        withTempFile "protocolXXXXXX.xml" $ \(path, stream) -> do-          hClose stream-          withLambdasOf (T.pack "- λ: L_outer\n  dataize:\n    𝛿1: ξ.arg\n  𝑛: ⟦ λ ⤍ 𝜎 ⟧\n") $ \outer ->-            withStdin "⟦ x ↦ ⟦ arg ↦ ⟦ λ ⤍ L_absent ⟧, λ ⤍ L_outer ⟧.φ ⟧" $-              testCLISucceeded ["morph", "--symbolic=" ++ outer, "--locator=Q.x", "--partial", "--protocol=" ++ path, "--quiet", "--sweet", "--hide-rho"] []-          records <- readProtocol path-          lines records `shouldContain` ["    <stuck λ=\"L_absent\"/>"]--      it "writes a starved step budget to the XML protocol" $-        withTempFile "protocolXXXXXX.xml" $ \(path, stream) -> do-          hClose stream-          withLambdasOf (T.pack "- λ: L_outer\n  dataize:\n    𝛿1: ξ.arg\n  𝑛: ⟦ λ ⤍ 𝜎 ⟧\n") $ \outer ->-            withStdin "⟦ x ↦ ⟦ arg ↦ ⟦ φ ↦ ⟦ φ ↦ ⟦ Δ ⤍ 07- ⟧ ⟧ ⟧, λ ⤍ L_outer ⟧ ⟧" $-              testCLISucceeded ["dataize", "--symbolic=" ++ outer, "--locator=Q.x", "--partial", "--max-steps=3", "--protocol=" ++ path, "--quiet", "--sweet", "--hide-rho", "--flat"] []-          records <- readProtocol path-          lines records `shouldContain` ["      <starved limit=\"3\" by=\"dataize\" at=\"Φ.a🌵0\"/>"]--      forM_-        [("XMLXXXXXX.xml", "<spent limit=\"5\" by="), ("textXXXXXX.txt", "  spent 5")]-        ( \(template, record) ->-            it ("writes a spent firing budget to the protocol as " ++ record) $-              withTempFile template $ \(path, stream) -> do-                hClose stream-                loopingLambdas $ \endless ->-                  withStdin "⟦ @ ↦ ⟦ λ ⤍ L_loop ⟧ ⟧" $-                    testCLIFailed ["dataize", "--symbolic=" ++ endless, "--max-steps=400", "--max-firings=5", "--protocol=" ++ path] ["--max-firings=5"]-                records <- readProtocol path-                any (record `isInfixOf`) (lines records) `shouldBe` True-        )--      it "keeps the lines of a run that fails" $-        withTempFile "protocolXXXXXX.txt" $ \(path, stream) -> do-          hClose stream-          withStdin "[[ bytes ↦ ⟦ φ ↦ ∅ ⟧, number(φ) -> [[ plus(^, x) -> [[ L> L_number_plus ]], nope -> [[ ^ -> ?, L> L_number_nope ]] ]], @ -> 5.plus(6).nope ]]" $-            testCLIFailed-              ["dataize", symbolic, "--protocol=" ++ path, "--quiet", "--sweet", "--hide-rho"]-              ["No entry of --symbolic answers the λ function 'L_number_nope'"]-          records <- readProtocol path-          lines records-            `shouldBe` [ "𝔻(Φ):"-                       , "  𝕄(Φ):"-                       , "    𝑛·0·1 := 5"-                       , "    𝑛·0·2 := 𝑛·0·1.plus( x ↦ 6 )"-                       , "    𝔼(L_number_plus):"-                       , "      𝔻(Φ.a🌵0):"-                       , "        𝕄(Φ.a🌵0):"-                       , "          𝑛·1·1 := 5"-                       , "          𝑛·1·2 := Φ.bytes( φ ↦ 40-14-00-00-00-00-00-00:Δ )"-                       , "        𝛿·1·1 := 40-14-00-00-00-00-00-00  # 5.0"-                       , "      𝛿1·1 := 𝛿·1·1  # 𝔻(ξ.ρ)"-                       , "      𝔻(Φ.a🌵1):"-                       , "        𝕄(Φ.a🌵1):"-                       , "          𝑛·1·3 := 6"-                       , "          𝑛·1·4 := Φ.bytes( φ ↦ 40-18-00-00-00-00-00-00:Δ )"-                       , "        𝛿·1·2 := 40-18-00-00-00-00-00-00  # 6.0"-                       , "      𝛿2·1 := 𝛿·1·2  # 𝔻(ξ.x)"-                       , "      𝑛·1·5 := Φ.number( φ ↦ 𝜎1:λ )  # 𝑛"-                       , "      𝕄(𝑛·1·5):"-                       , "        𝑛·1·6 := Φ.number( φ ↦ 𝜎1:λ )"-                       , "      𝑛·1·7 := 𝑛·1·6  # 𝕄(𝑛·1·5)"-                       , "  unanswered L_number_nope  # 𝔻(L_number_nope:λ)"-                       ]--      it "truncates the lines left over from the previous run" $-        withTempFileContent "protocolXXXXXX.txt" "𝔼(L_number_gt)\n" $ \path -> do-          withStdin "[[ D> 01- ]]" $-            testCLISucceeded ["dataize", "--protocol=" ++ path, "--quiet"] []-          records <- readProtocol path-          records `shouldBe` "𝔻(Φ):\n  𝛿·0·1 := 01-\n"--      it "writes the lines in 𝜑 even with --output=xmir" $-        withTempFile "protocolXXXXXX.txt" $ \(path, stream) -> do-          hClose stream-          withStdin sum' $-            testCLISucceeded ["dataize", symbolic, "--protocol=" ++ path, "--output=xmir", "--quiet", "--sweet", "--hide-rho"] []-          records <- readProtocol path-          records `shouldEndWith` "    𝕄(𝑛·1·5):\n      𝑛·1·6 := Φ.number( φ ↦ 𝜎1:λ )\n    𝑛·1·7 := 𝑛·1·6  # 𝕄(𝑛·1·5)\n"--      describe "as XML" $ do-        it "writes the document when the file is named .xml" $-          withTempFile "protocolXXXXXX.xml" $ \(path, stream) -> do-            hClose stream-            withStdin sum' $-              testCLISucceeded ["dataize", symbolic, "--protocol=" ++ path, "--quiet", "--sweet", "--hide-rho"] []-            records <- readProtocol path-            lines records-              `shouldBe` [ "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"-                         , "<dataize at=\"Φ\">"-                         , "  <morph at=\"Φ\">"-                         , "    <applied meta=\"𝑛·0·1\" of=\"Φ.bytes\"><datum attr=\"φ\">40-14-00-00-00-00-00-00</datum></applied>"-                         , "    <applied meta=\"𝑛·0·2\" by=\"morph\" at=\"Φ\" of=\"Φ.number\"><meta attr=\"φ\">𝑛·0·1</meta></applied>"-                         , "    <applied meta=\"𝑛·0·3\" of=\"Φ.bytes\"><datum attr=\"φ\">40-18-00-00-00-00-00-00</datum></applied>"-                         , "    <applied meta=\"𝑛·0·4\" of=\"Φ.number\"><meta attr=\"φ\">𝑛·0·3</meta></applied>"-                         , "    <applied meta=\"𝑛·0·5\" by=\"morph\" at=\"Φ\" of=\"𝑛·0·2.plus\"><meta attr=\"x\">𝑛·0·4</meta></applied>"-                         , "    <morphed meta=\"𝑛·0·6\">𝑛·0·5</morphed>"-                         , "  </morph>"-                         , "  <evaluate λ=\"L_number_plus\" by=\"dataize\" at=\"Φ\">"-                         , "    <dataize at=\"Φ.a🌵0\">"-                         , "      <morph at=\"Φ.a🌵0\">"-                         , "        <applied meta=\"𝑛·1·1\" by=\"morph\" at=\"Φ.a🌵0\" of=\"Φ.number\"><meta attr=\"φ\">𝑛·0·1</meta></applied>"-                         , "        <morphed meta=\"𝑛·1·2\">𝑛·1·1</morphed>"-                         , "        <applied meta=\"𝑛·1·3\" by=\"morph\" at=\"Φ.a🌵0\" of=\"Φ.bytes\"><datum attr=\"φ\">40-14-00-00-00-00-00-00</datum></applied>"-                         , "        <morphed meta=\"𝑛·1·4\">𝑛·1·3</morphed>"-                         , "      </morph>"-                         , "      <dataized meta=\"𝛿·1·1\" number=\"5.0\">40-14-00-00-00-00-00-00</dataized>"-                         , "    </dataize>"-                         , "    <bound meta=\"𝛿1·1\">𝛿·1·1</bound>"-                         , "    <dataize at=\"Φ.a🌵1\">"-                         , "      <morph at=\"Φ.a🌵1\">"-                         , "        <applied meta=\"𝑛·1·5\" by=\"morph\" at=\"Φ.a🌵1\" of=\"Φ.number\"><meta attr=\"φ\">𝑛·0·3</meta></applied>"-                         , "        <morphed meta=\"𝑛·1·6\">𝑛·1·5</morphed>"-                         , "        <applied meta=\"𝑛·1·7\" by=\"morph\" at=\"Φ.a🌵1\" of=\"Φ.bytes\"><datum attr=\"φ\">40-18-00-00-00-00-00-00</datum></applied>"-                         , "        <morphed meta=\"𝑛·1·8\">𝑛·1·7</morphed>"-                         , "      </morph>"-                         , "      <dataized meta=\"𝛿·1·2\" number=\"6.0\">40-18-00-00-00-00-00-00</dataized>"-                         , "    </dataize>"-                         , "    <bound meta=\"𝛿2·1\">𝛿·1·2</bound>"-                         , "    <minted symbol=\"𝜎1\">40-14-00-00-00-00-00-00 40-18-00-00-00-00-00-00</minted>"-                         , "    <applied meta=\"𝑛·1·9\" of=\"Φ.number\"><symbol attr=\"φ\">𝜎1</symbol></applied>"-                         , "    <built meta=\"𝑛·1·10\">𝑛·1·9</built>"-                         , "    <morph at=\"𝑛·1·10\">"-                         , "      <applied meta=\"𝑛·1·11\" by=\"morph\" at=\"𝑛·1·10\" of=\"Φ.number\"><symbol attr=\"φ\">𝜎1</symbol></applied>"-                         , "      <morphed meta=\"𝑛·1·12\">𝑛·1·11</morphed>"-                         , "    </morph>"-                         , "    <answered meta=\"𝑛·1·13\">𝑛·1·11</answered>"-                         , "  </evaluate>"-                         , "  <manufactured meta=\"𝛿·0·1\">𝜎1</manufactured>"-                         , "</dataize>"-                         ]--        it "nests the judgment right inside the '<protocol>' root" $-          withTempFile "protocolXXXXXX.xml" $ \(path, stream) -> do-            hClose stream-            withStdin "[[ D> 01- ]]" $-              testCLISucceeded ["dataize", "--protocol=" ++ path, "--quiet"] []-            records <- readUtf8 path-            take 4 (lines records)-              `shouldBe` [ "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"-                         , "<protocol>"-                         , "  <dataize at=\"Φ\">"-                         , "    <dataized meta=\"𝛿·0·1\">01-</dataized>"-                         ]--        it "closes the '<protocol>' root with its msec" $-          withTempFile "protocolXXXXXX.xml" $ \(path, stream) -> do-            hClose stream-            withStdin "[[ D> 01- ]]" $-              testCLISucceeded ["dataize", "--protocol=" ++ path, "--quiet"] []-            records <- readUtf8 path-            (lines records !! 5) `shouldSatisfy` isPrefixOf "  <msec>"--        it "closes the '<protocol>' root with its firings" $-          withTempFile "protocolXXXXXX.xml" $ \(path, stream) -> do-            hClose stream-            withStdin "[[ D> 01- ]]" $-              testCLISucceeded ["dataize", "--protocol=" ++ path, "--quiet"] []-            records <- readUtf8 path-            lines records `shouldContain` ["  <firings>0</firings>"]--        it "closes the '<protocol>' root with its fps, then '</protocol>' itself" $-          withTempFile "protocolXXXXXX.xml" $ \(path, stream) -> do-            hClose stream-            withStdin "[[ D> 01- ]]" $-              testCLISucceeded ["dataize", "--protocol=" ++ path, "--quiet"] []-            records <- readUtf8 path-            drop 7 (lines records) `shouldBe` ["  <fps>0</fps>", "</protocol>"]--        it "nests what a φ body dataizes inside the element of the box rule that entered it" $-          withTempFile "protocolXXXXXX.xml" $ \(path, stream) -> do-            hClose stream-            withStdin sum' $-              testCLISucceeded ["dataize", symbolic, "--protocol=" ++ path, "--quiet", "--sweet", "--hide-rho"] []-            records <- readProtocol path-            filter (\line -> not (any (`isInfixOf` line) ["<applied ", "<morph ", "<morphed ", "</morph>"])) (lines records)-              `shouldContain` [ "    <dataize at=\"Φ.a🌵0\">"-                              , "      <dataized meta=\"𝛿·1·1\" number=\"5.0\">40-14-00-00-00-00-00-00</dataized>"-                              , "    </dataize>"-                              ]--        it "closes the document even when nothing fires" $-          withTempFile "protocolXXXXXX.xml" $ \(path, stream) -> do-            hClose stream-            withStdin "[[ D> 01- ]]" $-              testCLISucceeded ["dataize", "--protocol=" ++ path, "--quiet"] []-            records <- readProtocol path-            lines records-              `shouldBe` [ "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"-                         , "<dataize at=\"Φ\">"-                         , "  <dataized meta=\"𝛿·0·1\">01-</dataized>"-                         , "</dataize>"-                         ]--        it "tells a manufactured datum from data by the name of the element" $-          withTempFile "protocolXXXXXX.xml" $ \(path, stream) -> do-            hClose stream-            withStdin chained $-              testCLISucceeded ["dataize", symbolic, "--protocol=" ++ path, "--quiet", "--sweet", "--hide-rho"] []-            records <- readProtocol path-            lines records-              `shouldBe` [ "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"-                         , "<dataize at=\"Φ\">"-                         , "  <morph at=\"Φ\">"-                         , "    <applied meta=\"𝑛·0·1\" of=\"Φ.bytes\"><datum attr=\"φ\">40-14-00-00-00-00-00-00</datum></applied>"-                         , "    <applied meta=\"𝑛·0·2\" by=\"morph\" at=\"Φ\" of=\"Φ.number\"><meta attr=\"φ\">𝑛·0·1</meta></applied>"-                         , "    <applied meta=\"𝑛·0·3\" of=\"Φ.bytes\"><datum attr=\"φ\">40-18-00-00-00-00-00-00</datum></applied>"-                         , "    <applied meta=\"𝑛·0·4\" of=\"Φ.number\"><meta attr=\"φ\">𝑛·0·3</meta></applied>"-                         , "    <applied meta=\"𝑛·0·5\" by=\"morph\" at=\"Φ\" of=\"𝑛·0·2.plus\"><meta attr=\"x\">𝑛·0·4</meta></applied>"-                         , "    <evaluate λ=\"L_number_plus\" by=\"morph\" at=\"Φ\">"-                         , "      <dataize at=\"Φ.a🌵0\">"-                         , "        <morph at=\"Φ.a🌵0\">"-                         , "          <applied meta=\"𝑛·1·1\" by=\"morph\" at=\"Φ.a🌵0\" of=\"Φ.number\"><meta attr=\"φ\">𝑛·0·1</meta></applied>"-                         , "          <morphed meta=\"𝑛·1·2\">𝑛·1·1</morphed>"-                         , "          <applied meta=\"𝑛·1·3\" by=\"morph\" at=\"Φ.a🌵0\" of=\"Φ.bytes\"><datum attr=\"φ\">40-14-00-00-00-00-00-00</datum></applied>"-                         , "          <morphed meta=\"𝑛·1·4\">𝑛·1·3</morphed>"-                         , "        </morph>"-                         , "        <dataized meta=\"𝛿·1·1\" number=\"5.0\">40-14-00-00-00-00-00-00</dataized>"-                         , "      </dataize>"-                         , "      <bound meta=\"𝛿1·1\">𝛿·1·1</bound>"-                         , "      <dataize at=\"Φ.a🌵1\">"-                         , "        <morph at=\"Φ.a🌵1\">"-                         , "          <applied meta=\"𝑛·1·5\" by=\"morph\" at=\"Φ.a🌵1\" of=\"Φ.number\"><meta attr=\"φ\">𝑛·0·3</meta></applied>"-                         , "          <morphed meta=\"𝑛·1·6\">𝑛·1·5</morphed>"-                         , "          <applied meta=\"𝑛·1·7\" by=\"morph\" at=\"Φ.a🌵1\" of=\"Φ.bytes\"><datum attr=\"φ\">40-18-00-00-00-00-00-00</datum></applied>"-                         , "          <morphed meta=\"𝑛·1·8\">𝑛·1·7</morphed>"-                         , "        </morph>"-                         , "        <dataized meta=\"𝛿·1·2\" number=\"6.0\">40-18-00-00-00-00-00-00</dataized>"-                         , "      </dataize>"-                         , "      <bound meta=\"𝛿2·1\">𝛿·1·2</bound>"-                         , "      <minted symbol=\"𝜎1\">40-14-00-00-00-00-00-00 40-18-00-00-00-00-00-00</minted>"-                         , "      <applied meta=\"𝑛·1·9\" of=\"Φ.number\"><symbol attr=\"φ\">𝜎1</symbol></applied>"-                         , "      <built meta=\"𝑛·1·10\">𝑛·1·9</built>"-                         , "      <morph at=\"𝑛·1·10\">"-                         , "        <applied meta=\"𝑛·1·11\" by=\"morph\" at=\"𝑛·1·10\" of=\"Φ.number\"><symbol attr=\"φ\">𝜎1</symbol></applied>"-                         , "        <morphed meta=\"𝑛·1·12\">𝑛·1·11</morphed>"-                         , "      </morph>"-                         , "      <answered meta=\"𝑛·1·13\">𝑛·1·11</answered>"-                         , "    </evaluate>"-                         , "    <applied meta=\"𝑛·0·6\" of=\"Φ.bytes\"><datum attr=\"φ\">40-1C-00-00-00-00-00-00</datum></applied>"-                         , "    <applied meta=\"𝑛·0·7\" of=\"Φ.number\"><meta attr=\"φ\">𝑛·0·6</meta></applied>"-                         , "    <applied meta=\"𝑛·0·8\" by=\"morph\" at=\"Φ\" of=\"𝑛·1·11.plus\"><meta attr=\"x\">𝑛·0·7</meta></applied>"-                         , "    <morphed meta=\"𝑛·0·9\">𝑛·0·8</morphed>"-                         , "  </morph>"-                         , "  <evaluate λ=\"L_number_plus\" by=\"dataize\" at=\"Φ\">"-                         , "    <dataize at=\"Φ.a🌵2\">"-                         , "      <morph at=\"Φ.a🌵2\">"-                         , "        <applied meta=\"𝑛·2·1\" by=\"morph\" at=\"Φ.a🌵2\" of=\"Φ.number\"><symbol attr=\"φ\">𝜎1</symbol></applied>"-                         , "        <morphed meta=\"𝑛·2·2\">𝑛·2·1</morphed>"-                         , "      </morph>"-                         , "      <manufactured meta=\"𝛿·2·1\">𝜎1</manufactured>"-                         , "    </dataize>"-                         , "    <bound meta=\"𝛿1·2\">𝛿·2·1</bound>"-                         , "    <dataize at=\"Φ.a🌵3\">"-                         , "      <morph at=\"Φ.a🌵3\">"-                         , "        <applied meta=\"𝑛·2·3\" by=\"morph\" at=\"Φ.a🌵3\" of=\"Φ.number\"><meta attr=\"φ\">𝑛·0·6</meta></applied>"-                         , "        <morphed meta=\"𝑛·2·4\">𝑛·2·3</morphed>"-                         , "        <applied meta=\"𝑛·2·5\" by=\"morph\" at=\"Φ.a🌵3\" of=\"Φ.bytes\"><datum attr=\"φ\">40-1C-00-00-00-00-00-00</datum></applied>"-                         , "        <morphed meta=\"𝑛·2·6\">𝑛·2·5</morphed>"-                         , "      </morph>"-                         , "      <dataized meta=\"𝛿·2·2\" number=\"7.0\">40-1C-00-00-00-00-00-00</dataized>"-                         , "    </dataize>"-                         , "    <bound meta=\"𝛿2·2\">𝛿·2·2</bound>"-                         , "    <minted symbol=\"𝜎2\">𝜎1 40-1C-00-00-00-00-00-00</minted>"-                         , "    <applied meta=\"𝑛·2·7\" of=\"Φ.number\"><symbol attr=\"φ\">𝜎2</symbol></applied>"-                         , "    <built meta=\"𝑛·2·8\">𝑛·2·7</built>"-                         , "    <morph at=\"𝑛·2·8\">"-                         , "      <applied meta=\"𝑛·2·9\" by=\"morph\" at=\"𝑛·2·8\" of=\"Φ.number\"><symbol attr=\"φ\">𝜎2</symbol></applied>"-                         , "      <morphed meta=\"𝑛·2·10\">𝑛·2·9</morphed>"-                         , "    </morph>"-                         , "    <answered meta=\"𝑛·2·11\">𝑛·2·9</answered>"-                         , "  </evaluate>"-                         , "  <manufactured meta=\"𝛿·0·1\">𝜎2</manufactured>"-                         , "</dataize>"-                         ]--        it "writes a firing whose two sides do not join as diverged, naming both sides" $-          withTempFile "protocolXXXXXX.xml" $ \(path, stream) -> do-            hClose stream-            withLambdasOf (T.pack "- λ: L_fork\n  dataize:\n    𝛿1: $.c\n  join:\n    on: 𝛿1\n    left: $.a\n    right: $.b\n") $ \joins ->-              withStdin "⟦ y ↦ ⟦ c ↦ ⟦ λ ⤍ 𝜎1 ⟧, a ↦ ⟦ φ ↦ ⟦ Δ ⤍ 01- ⟧ ⟧, b ↦ ⟦ φ ↦ ⟦ Δ ⤍ 02- ⟧ ⟧, λ ⤍ L_fork ⟧.φ ⟧" $-                testCLIFailed ["morph", "--symbolic=" ++ joins, "--locator=Q.y", "--protocol=" ++ path, "--quiet", "--sweet", "--hide-rho"] ["No entry of --symbolic answers the λ function 'L_fork'"]-            records <- readProtocol path-            lines records-              `shouldBe` [ "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"-                         , "<morph at=\"Φ.y\">"-                         , "  <evaluate λ=\"L_fork\" by=\"morph\" at=\"Φ.y\">"-                         , "    <dataize at=\"Φ.a🌵0\">"-                         , "      <manufactured meta=\"𝛿·1·1\">𝜎1</manufactured>"-                         , "    </dataize>"-                         , "    <bound meta=\"𝛿1·1\">𝛿·1·1</bound>"-                         , "    <morph at=\"Φ.a🌵1\">"-                         , "      <formed meta=\"𝑛·1·1\"><datum attr=\"φ\">01-</datum></formed>"-                         , "      <morphed meta=\"𝑛·1·2\">𝑛·1·1</morphed>"-                         , "    </morph>"-                         , "    <morph at=\"Φ.a🌵2\">"-                         , "      <formed meta=\"𝑛·1·3\"><datum attr=\"φ\">02-</datum></formed>"-                         , "      <morphed meta=\"𝑛·1·4\">𝑛·1·3</morphed>"-                         , "    </morph>"-                         , "    <diverged left=\"𝑛·1·1\" right=\"𝑛·1·3\" at=\".φ.Δ\" reason=\"the join names no otherwise\"/>"-                         , "  </evaluate>"-                         , "</morph>"-                         ]--        it "writes the symbol a join minted for two sides that differ only in their symbols as an element of its own" $-          withTempFile "protocolXXXXXX.xml" $ \(path, stream) -> do-            hClose stream-            withLambdasOf (T.pack "- λ: L_fork\n  dataize:\n    𝛿1: $.c\n  join:\n    on: 𝛿1\n    left: $.a\n    right: $.b\n") $ \joins ->-              withStdin "⟦ y ↦ ⟦ c ↦ ⟦ λ ⤍ 𝜎1 ⟧, a ↦ ⟦ φ ↦ ⟦ λ ⤍ 𝜎2 ⟧ ⟧, b ↦ ⟦ φ ↦ ⟦ λ ⤍ 𝜎3 ⟧ ⟧, λ ⤍ L_fork ⟧.φ ⟧" $-                testCLISucceeded ["morph", "--symbolic=" ++ joins, "--locator=Q.y", "--protocol=" ++ path, "--quiet", "--sweet", "--hide-rho"] []-            records <- readProtocol path-            lines records-              `shouldBe` [ "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"-                         , "<morph at=\"Φ.y\">"-                         , "  <evaluate λ=\"L_fork\" by=\"morph\" at=\"Φ.y\">"-                         , "    <dataize at=\"Φ.a🌵0\">"-                         , "      <manufactured meta=\"𝛿·1·1\">𝜎1</manufactured>"-                         , "    </dataize>"-                         , "    <bound meta=\"𝛿1·1\">𝛿·1·1</bound>"-                         , "    <morph at=\"Φ.a🌵1\">"-                         , "      <formed meta=\"𝑛·1·1\"><symbol attr=\"φ\">𝜎2</symbol></formed>"-                         , "      <morphed meta=\"𝑛·1·2\">𝑛·1·1</morphed>"-                         , "    </morph>"-                         , "    <morph at=\"Φ.a🌵2\">"-                         , "      <formed meta=\"𝑛·1·3\"><symbol attr=\"φ\">𝜎3</symbol></formed>"-                         , "      <morphed meta=\"𝑛·1·4\">𝑛·1·3</morphed>"-                         , "    </morph>"-                         , "    <joined symbol=\"𝜎4\" on=\"𝛿1·1\" left=\"𝜎2\" right=\"𝜎3\"/>"-                         , "    <formed meta=\"𝑛·1·5\"><symbol attr=\"φ\">𝜎4</symbol></formed>"-                         , "    <answered meta=\"𝑛·1·6\">𝑛·1·5</answered>"-                         , "  </evaluate>"-                         , "  <morphed meta=\"𝑛·0·1\">𝜎4</morphed>"-                         , "</morph>"-                         ]--        it "writes one 'minted' element per symbol the answer asked for" $-          withTempFile "protocolXXXXXX.xml" $ \(path, stream) -> do-            hClose stream-            withLambdasOf (T.pack "- λ: L_pair\n  morph:\n    𝑛1: $.x\n  𝑛: ⟦ left ↦ ⟦ λ ⤍ 𝜎 ⟧, right ↦ ⟦ λ ⤍ 𝜎 ⟧ ⟧\n") $ \pairs ->-              withStdin "⟦ y ↦ ⟦ x ↦ ⟦ Δ ⤍ 01- ⟧, λ ⤍ L_pair ⟧.left ⟧" $-                testCLISucceeded ["morph", "--symbolic=" ++ pairs, "--locator=Q.y", "--protocol=" ++ path, "--quiet", "--sweet", "--hide-rho"] []-            records <- readProtocol path-            lines records-              `shouldBe` [ "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"-                         , "<morph at=\"Φ.y\">"-                         , "  <evaluate λ=\"L_pair\" by=\"morph\" at=\"Φ.y\">"-                         , "    <morph at=\"Φ.a🌵0\">"-                         , "      <morphed meta=\"𝑛·1·1\">01-</morphed>"-                         , "    </morph>"-                         , "    <bound meta=\"𝑛1·1\">01-</bound>"-                         , "    <minted symbol=\"𝜎1\"/>"-                         , "    <minted symbol=\"𝜎2\"/>"-                         , "    <formed meta=\"𝑛·1·2\"><symbol attr=\"left\">𝜎1</symbol><symbol attr=\"right\">𝜎2</symbol></formed>"-                         , "    <built meta=\"𝑛·1·3\">𝑛·1·2</built>"-                         , "    <morph at=\"𝑛·1·3\">"-                         , "      <morphed meta=\"𝑛·1·4\">𝑛·1·2</morphed>"-                         , "    </morph>"-                         , "    <answered meta=\"𝑛·1·5\">𝑛·1·2</answered>"-                         , "  </evaluate>"-                         , "  <morphed meta=\"𝑛·0·1\">𝜎1</morphed>"-                         , "</morph>"-                         ]--        it "writes no 'minted' element for a firing minting nothing" $-          withTempFile "protocolXXXXXX.xml" $ \(path, stream) -> do-            hClose stream-            withLambdasOf (T.pack "- λ: L_keep\n  morph:\n    𝑛1: $.x\n  𝑛: ⟦ z ↦ 𝑛1 ⟧\n") $ \keeps ->-              withStdin "⟦ y ↦ ⟦ x ↦ ⟦ Δ ⤍ 01- ⟧, λ ⤍ L_keep ⟧.z ⟧" $-                testCLISucceeded ["morph", "--symbolic=" ++ keeps, "--locator=Q.y", "--protocol=" ++ path, "--quiet", "--sweet", "--hide-rho"] []-            records <- readProtocol path-            lines records-              `shouldBe` [ "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"-                         , "<morph at=\"Φ.y\">"-                         , "  <evaluate λ=\"L_keep\" by=\"morph\" at=\"Φ.y\">"-                         , "    <morph at=\"Φ.a🌵0\">"-                         , "      <morphed meta=\"𝑛·1·1\">01-</morphed>"-                         , "    </morph>"-                         , "    <bound meta=\"𝑛1·1\">01-</bound>"-                         , "    <formed meta=\"𝑛·1·2\"><datum attr=\"z\">01-</datum></formed>"-                         , "    <built meta=\"𝑛·1·3\">𝑛·1·2</built>"-                         , "    <morph at=\"𝑛·1·3\">"-                         , "      <morphed meta=\"𝑛·1·4\">𝑛·1·2</morphed>"-                         , "    </morph>"-                         , "    <answered meta=\"𝑛·1·5\">𝑛·1·2</answered>"-                         , "  </evaluate>"-                         , "  <morphed meta=\"𝑛·0·1\">01-</morphed>"-                         , "</morph>"-                         ]--        it "nests a firing an operand took inside the firing that asked" $-          withTempFile "protocolXXXXXX.xml" $ \(path, stream) -> do-            hClose stream-            withStdin nested $-              testCLISucceeded ["dataize", symbolic, "--protocol=" ++ path, "--quiet", "--sweet", "--hide-rho"] []-            records <- readProtocol path-            lines records-              `shouldBe` [ "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"-                         , "<dataize at=\"Φ\">"-                         , "  <morph at=\"Φ\">"-                         , "    <applied meta=\"𝑛·0·1\" of=\"Φ.bytes\"><datum attr=\"φ\">40-14-00-00-00-00-00-00</datum></applied>"-                         , "    <applied meta=\"𝑛·0·2\" by=\"morph\" at=\"Φ\" of=\"Φ.number\"><meta attr=\"φ\">𝑛·0·1</meta></applied>"-                         , "    <applied meta=\"𝑛·0·3\" of=\"Φ.bytes\"><datum attr=\"φ\">40-18-00-00-00-00-00-00</datum></applied>"-                         , "    <applied meta=\"𝑛·0·4\" of=\"Φ.number\"><meta attr=\"φ\">𝑛·0·3</meta></applied>"-                         , "    <applied meta=\"𝑛·0·5\" of=\"Φ.bytes\"><datum attr=\"φ\">40-1C-00-00-00-00-00-00</datum></applied>"-                         , "    <applied meta=\"𝑛·0·6\" of=\"Φ.number\"><meta attr=\"φ\">𝑛·0·5</meta></applied>"-                         , "    <applied meta=\"𝑛·0·7\" of=\"𝑛·0·4.plus\"><meta attr=\"α0\">𝑛·0·6</meta></applied>"-                         , "    <applied meta=\"𝑛·0·8\" by=\"morph\" at=\"Φ\" of=\"𝑛·0·2.plus\"><meta attr=\"x\">𝑛·0·7</meta></applied>"-                         , "    <morphed meta=\"𝑛·0·9\">𝑛·0·8</morphed>"-                         , "  </morph>"-                         , "  <evaluate λ=\"L_number_plus\" by=\"dataize\" at=\"Φ\">"-                         , "    <dataize at=\"Φ.a🌵0\">"-                         , "      <morph at=\"Φ.a🌵0\">"-                         , "        <applied meta=\"𝑛·1·1\" by=\"morph\" at=\"Φ.a🌵0\" of=\"Φ.number\"><meta attr=\"φ\">𝑛·0·1</meta></applied>"-                         , "        <morphed meta=\"𝑛·1·2\">𝑛·1·1</morphed>"-                         , "        <applied meta=\"𝑛·1·3\" by=\"morph\" at=\"Φ.a🌵0\" of=\"Φ.bytes\"><datum attr=\"φ\">40-14-00-00-00-00-00-00</datum></applied>"-                         , "        <morphed meta=\"𝑛·1·4\">𝑛·1·3</morphed>"-                         , "      </morph>"-                         , "      <dataized meta=\"𝛿·1·1\" number=\"5.0\">40-14-00-00-00-00-00-00</dataized>"-                         , "    </dataize>"-                         , "    <bound meta=\"𝛿1·1\">𝛿·1·1</bound>"-                         , "    <dataize at=\"Φ.a🌵1\">"-                         , "      <morph at=\"Φ.a🌵1\">"-                         , "        <applied meta=\"𝑛·1·5\" by=\"morph\" at=\"Φ.a🌵1\" of=\"Φ.number\"><meta attr=\"φ\">𝑛·0·3</meta></applied>"-                         , "        <applied meta=\"𝑛·1·6\" by=\"morph\" at=\"Φ.a🌵1\" of=\"𝑛·1·5.plus\"><meta attr=\"x\">𝑛·0·6</meta></applied>"-                         , "        <morphed meta=\"𝑛·1·7\">𝑛·1·6</morphed>"-                         , "      </morph>"-                         , "      <evaluate λ=\"L_number_plus\" by=\"dataize\" at=\"Φ.a🌵1\">"-                         , "        <dataize at=\"Φ.a🌵2\">"-                         , "          <morph at=\"Φ.a🌵2\">"-                         , "            <applied meta=\"𝑛·2·1\" by=\"morph\" at=\"Φ.a🌵2\" of=\"Φ.number\"><meta attr=\"φ\">𝑛·0·3</meta></applied>"-                         , "            <morphed meta=\"𝑛·2·2\">𝑛·2·1</morphed>"-                         , "            <applied meta=\"𝑛·2·3\" by=\"morph\" at=\"Φ.a🌵2\" of=\"Φ.bytes\"><datum attr=\"φ\">40-18-00-00-00-00-00-00</datum></applied>"-                         , "            <morphed meta=\"𝑛·2·4\">𝑛·2·3</morphed>"-                         , "          </morph>"-                         , "          <dataized meta=\"𝛿·2·1\" number=\"6.0\">40-18-00-00-00-00-00-00</dataized>"-                         , "        </dataize>"-                         , "        <bound meta=\"𝛿1·2\">𝛿·2·1</bound>"-                         , "        <dataize at=\"Φ.a🌵3\">"-                         , "          <morph at=\"Φ.a🌵3\">"-                         , "            <applied meta=\"𝑛·2·5\" by=\"morph\" at=\"Φ.a🌵3\" of=\"Φ.number\"><meta attr=\"φ\">𝑛·0·5</meta></applied>"-                         , "            <morphed meta=\"𝑛·2·6\">𝑛·2·5</morphed>"-                         , "            <applied meta=\"𝑛·2·7\" by=\"morph\" at=\"Φ.a🌵3\" of=\"Φ.bytes\"><datum attr=\"φ\">40-1C-00-00-00-00-00-00</datum></applied>"-                         , "            <morphed meta=\"𝑛·2·8\">𝑛·2·7</morphed>"-                         , "          </morph>"-                         , "          <dataized meta=\"𝛿·2·2\" number=\"7.0\">40-1C-00-00-00-00-00-00</dataized>"-                         , "        </dataize>"-                         , "        <bound meta=\"𝛿2·2\">𝛿·2·2</bound>"-                         , "        <minted symbol=\"𝜎1\">40-18-00-00-00-00-00-00 40-1C-00-00-00-00-00-00</minted>"-                         , "        <applied meta=\"𝑛·2·9\" of=\"Φ.number\"><symbol attr=\"φ\">𝜎1</symbol></applied>"-                         , "        <built meta=\"𝑛·2·10\">𝑛·2·9</built>"-                         , "        <morph at=\"𝑛·2·10\">"-                         , "          <applied meta=\"𝑛·2·11\" by=\"morph\" at=\"𝑛·2·10\" of=\"Φ.number\"><symbol attr=\"φ\">𝜎1</symbol></applied>"-                         , "          <morphed meta=\"𝑛·2·12\">𝑛·2·11</morphed>"-                         , "        </morph>"-                         , "        <answered meta=\"𝑛·2·13\">𝑛·2·11</answered>"-                         , "      </evaluate>"-                         , "      <manufactured meta=\"𝛿·1·2\">𝜎1</manufactured>"-                         , "    </dataize>"-                         , "    <bound meta=\"𝛿2·1\">𝛿·1·2</bound>"-                         , "    <minted symbol=\"𝜎2\">40-14-00-00-00-00-00-00 𝜎1</minted>"-                         , "    <applied meta=\"𝑛·1·8\" of=\"Φ.number\"><symbol attr=\"φ\">𝜎2</symbol></applied>"-                         , "    <built meta=\"𝑛·1·9\">𝑛·1·8</built>"-                         , "    <morph at=\"𝑛·1·9\">"-                         , "      <applied meta=\"𝑛·1·10\" by=\"morph\" at=\"𝑛·1·9\" of=\"Φ.number\"><symbol attr=\"φ\">𝜎2</symbol></applied>"-                         , "      <morphed meta=\"𝑛·1·11\">𝑛·1·10</morphed>"-                         , "    </morph>"-                         , "    <answered meta=\"𝑛·1·12\">𝑛·1·10</answered>"-                         , "  </evaluate>"-                         , "  <manufactured meta=\"𝛿·0·1\">𝜎2</manufactured>"-                         , "</dataize>"-                         ]--        it "records a λ function no entry answers as a childless element" $-          withTempFile "protocolXXXXXX.xml" $ \(path, stream) -> do-            hClose stream-            withStdin "[[ bytes ↦ ⟦ φ ↦ ∅ ⟧, number(φ) -> [[ times(^, x) -> [[ L> L_number_times ]], nope -> [[ ^ -> ?, L> L_number_nope ]] ]], @ -> 2.times(3).nope ]]" $-              testCLISucceeded ["dataize", symbolic, "--partial", "--protocol=" ++ path, "--quiet", "--sweet", "--hide-rho"] []-            records <- readProtocol path-            lines records-              `shouldBe` [ "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"-                         , "<dataize at=\"Φ\">"-                         , "  <morph at=\"Φ\">"-                         , "    <applied meta=\"𝑛·0·1\" of=\"Φ.bytes\"><datum attr=\"φ\">40-00-00-00-00-00-00-00</datum></applied>"-                         , "    <applied meta=\"𝑛·0·2\" by=\"morph\" at=\"Φ\" of=\"Φ.number\"><meta attr=\"φ\">𝑛·0·1</meta></applied>"-                         , "    <applied meta=\"𝑛·0·3\" of=\"Φ.bytes\"><datum attr=\"φ\">40-08-00-00-00-00-00-00</datum></applied>"-                         , "    <applied meta=\"𝑛·0·4\" of=\"Φ.number\"><meta attr=\"φ\">𝑛·0·3</meta></applied>"-                         , "    <applied meta=\"𝑛·0·5\" by=\"morph\" at=\"Φ\" of=\"𝑛·0·2.times\"><meta attr=\"x\">𝑛·0·4</meta></applied>"-                         , "    <evaluate λ=\"L_number_times\" by=\"morph\" at=\"Φ\">"-                         , "      <dataize at=\"Φ.a🌵0\">"-                         , "        <morph at=\"Φ.a🌵0\">"-                         , "          <applied meta=\"𝑛·1·1\" by=\"morph\" at=\"Φ.a🌵0\" of=\"Φ.number\"><meta attr=\"φ\">𝑛·0·1</meta></applied>"-                         , "          <morphed meta=\"𝑛·1·2\">𝑛·1·1</morphed>"-                         , "          <applied meta=\"𝑛·1·3\" by=\"morph\" at=\"Φ.a🌵0\" of=\"Φ.bytes\"><datum attr=\"φ\">40-00-00-00-00-00-00-00</datum></applied>"-                         , "          <morphed meta=\"𝑛·1·4\">𝑛·1·3</morphed>"-                         , "        </morph>"-                         , "        <dataized meta=\"𝛿·1·1\" number=\"2.0\">40-00-00-00-00-00-00-00</dataized>"-                         , "      </dataize>"-                         , "      <bound meta=\"𝛿1·1\">𝛿·1·1</bound>"-                         , "      <dataize at=\"Φ.a🌵1\">"-                         , "        <morph at=\"Φ.a🌵1\">"-                         , "          <applied meta=\"𝑛·1·5\" by=\"morph\" at=\"Φ.a🌵1\" of=\"Φ.number\"><meta attr=\"φ\">𝑛·0·3</meta></applied>"-                         , "          <morphed meta=\"𝑛·1·6\">𝑛·1·5</morphed>"-                         , "          <applied meta=\"𝑛·1·7\" by=\"morph\" at=\"Φ.a🌵1\" of=\"Φ.bytes\"><datum attr=\"φ\">40-08-00-00-00-00-00-00</datum></applied>"-                         , "          <morphed meta=\"𝑛·1·8\">𝑛·1·7</morphed>"-                         , "        </morph>"-                         , "        <dataized meta=\"𝛿·1·2\" number=\"3.0\">40-08-00-00-00-00-00-00</dataized>"-                         , "      </dataize>"-                         , "      <bound meta=\"𝛿2·1\">𝛿·1·2</bound>"-                         , "      <minted symbol=\"𝜎1\">40-00-00-00-00-00-00-00 40-08-00-00-00-00-00-00</minted>"-                         , "      <applied meta=\"𝑛·1·9\" of=\"Φ.number\"><symbol attr=\"φ\">𝜎1</symbol></applied>"-                         , "      <built meta=\"𝑛·1·10\">𝑛·1·9</built>"-                         , "      <morph at=\"𝑛·1·10\">"-                         , "        <applied meta=\"𝑛·1·11\" by=\"morph\" at=\"𝑛·1·10\" of=\"Φ.number\"><symbol attr=\"φ\">𝜎1</symbol></applied>"-                         , "        <morphed meta=\"𝑛·1·12\">𝑛·1·11</morphed>"-                         , "      </morph>"-                         , "      <answered meta=\"𝑛·1·13\">𝑛·1·11</answered>"-                         , "    </evaluate>"-                         , "    <morphed meta=\"𝑛·0·6\">𝑛·1·11.nope</morphed>"-                         , "  </morph>"-                         , "  <unanswered λ=\"L_number_nope\" by=\"dataize\">𝑛·1·11.nope</unanswered>"-                         , "</dataize>"-                         ]--        it "names the root after the judgment a morphing ran" $-          withTempFile "protocolXXXXXX.xml" $ \(path, stream) -> do-            hClose stream-            withStdin "[[ x -> [[ L> L_number_nope ]].foo ]]" $-              testCLISucceeded ["morph", "--locator=Q.x", "--partial", "--protocol=" ++ path, "--quiet"] []-            records <- readProtocol path-            lines records-              `shouldBe` [ "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"-                         , "<morph at=\"Φ.x\">"-                         , "  <formed meta=\"𝑛·0·1\" λ=\"L_number_nope\"/>"-                         , "  <unanswered λ=\"L_number_nope\" by=\"morph\">𝑛·0·1</unanswered>"-                         , "</morph>"-                         ]--        it "closes the document even when the run fails" $-          withTempFile "protocolXXXXXX.xml" $ \(path, stream) -> do-            hClose stream-            withStdin "[[ bytes ↦ ⟦ φ ↦ ∅ ⟧, number(φ) -> [[ times(^, x) -> [[ L> L_number_times ]], nope -> [[ ^ -> ?, L> L_number_nope ]] ]], @ -> 2.times(3).nope ]]" $-              testCLIFailed ["dataize", symbolic, "--protocol=" ++ path] ["No entry of --symbolic answers"]-            records <- readProtocol path-            lines records-              `shouldBe` [ "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"-                         , "<dataize at=\"Φ\">"-                         , "  <morph at=\"Φ\">"-                         , "    <applied meta=\"𝑛·0·1\" of=\"Φ.bytes\"><datum attr=\"φ\">40-00-00-00-00-00-00-00</datum></applied>"-                         , "    <applied meta=\"𝑛·0·2\" by=\"morph\" at=\"Φ\" of=\"Φ.number\"><meta attr=\"φ\">𝑛·0·1</meta></applied>"-                         , "    <applied meta=\"𝑛·0·3\" of=\"Φ.bytes\"><datum attr=\"φ\">40-08-00-00-00-00-00-00</datum></applied>"-                         , "    <applied meta=\"𝑛·0·4\" of=\"Φ.number\"><meta attr=\"φ\">𝑛·0·3</meta></applied>"-                         , "    <applied meta=\"𝑛·0·5\" by=\"morph\" at=\"Φ\" of=\"𝑛·0·2.times\"><meta attr=\"x\">𝑛·0·4</meta></applied>"-                         , "    <evaluate λ=\"L_number_times\" by=\"morph\" at=\"Φ\">"-                         , "      <dataize at=\"Φ.a🌵0\">"-                         , "        <morph at=\"Φ.a🌵0\">"-                         , "          <applied meta=\"𝑛·1·1\" by=\"morph\" at=\"Φ.a🌵0\" of=\"Φ.number\"><meta attr=\"φ\">𝑛·0·1</meta></applied>"-                         , "          <morphed meta=\"𝑛·1·2\">𝑛·1·1</morphed>"-                         , "          <applied meta=\"𝑛·1·3\" by=\"morph\" at=\"Φ.a🌵0\" of=\"Φ.bytes\"><datum attr=\"φ\">40-00-00-00-00-00-00-00</datum></applied>"-                         , "          <morphed meta=\"𝑛·1·4\">𝑛·1·3</morphed>"-                         , "        </morph>"-                         , "        <dataized meta=\"𝛿·1·1\" number=\"2.0\">40-00-00-00-00-00-00-00</dataized>"-                         , "      </dataize>"-                         , "      <bound meta=\"𝛿1·1\">𝛿·1·1</bound>"-                         , "      <dataize at=\"Φ.a🌵1\">"-                         , "        <morph at=\"Φ.a🌵1\">"-                         , "          <applied meta=\"𝑛·1·5\" by=\"morph\" at=\"Φ.a🌵1\" of=\"Φ.number\"><meta attr=\"φ\">𝑛·0·3</meta></applied>"-                         , "          <morphed meta=\"𝑛·1·6\">𝑛·1·5</morphed>"-                         , "          <applied meta=\"𝑛·1·7\" by=\"morph\" at=\"Φ.a🌵1\" of=\"Φ.bytes\"><datum attr=\"φ\">40-08-00-00-00-00-00-00</datum></applied>"-                         , "          <morphed meta=\"𝑛·1·8\">𝑛·1·7</morphed>"-                         , "        </morph>"-                         , "        <dataized meta=\"𝛿·1·2\" number=\"3.0\">40-08-00-00-00-00-00-00</dataized>"-                         , "      </dataize>"-                         , "      <bound meta=\"𝛿2·1\">𝛿·1·2</bound>"-                         , "      <minted symbol=\"𝜎1\">40-00-00-00-00-00-00-00 40-08-00-00-00-00-00-00</minted>"-                         , "      <applied meta=\"𝑛·1·9\" of=\"Φ.number\"><symbol attr=\"φ\">𝜎1</symbol></applied>"-                         , "      <built meta=\"𝑛·1·10\">𝑛·1·9</built>"-                         , "      <morph at=\"𝑛·1·10\">"-                         , "        <applied meta=\"𝑛·1·11\" by=\"morph\" at=\"𝑛·1·10\" of=\"Φ.number\"><symbol attr=\"φ\">𝜎1</symbol></applied>"-                         , "        <morphed meta=\"𝑛·1·12\">𝑛·1·11</morphed>"-                         , "      </morph>"-                         , "      <answered meta=\"𝑛·1·13\">𝑛·1·11</answered>"-                         , "    </evaluate>"-                         , "    <morphed meta=\"𝑛·0·6\">𝑛·1·11.nope</morphed>"-                         , "  </morph>"-                         , "  <unanswered λ=\"L_number_nope\" by=\"dataize\">𝑛·1·11.nope</unanswered>"-                         , "</dataize>"-                         ]--        it "writes the terminator as the term a meta was bound to" $-          withTempFile "protocolXXXXXX.xml" $ \(path, stream) -> do-            hClose stream-            withLambdasOf (T.pack "- λ: L_pick\n  morph:\n    𝑛1: ξ.absent\n  𝑛: ⟦ λ ⤍ 𝜎 ⟧\n") $ \picks ->-              withStdin "[[ x -> [[ here -> [[ ]], L> L_pick ]].foo ]]" $-                testCLIFailed ["morph", "--symbolic=" ++ picks, "--locator=Q.x", "--protocol=" ++ path, "--quiet", "--hide-rho"] ["No entry of --symbolic answers the λ function '𝜎1'"]-            records <- readProtocol path-            lines records-              `shouldBe` [ "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"-                         , "<morph at=\"Φ.x\">"-                         , "  <evaluate λ=\"L_pick\" by=\"morph\" at=\"Φ.x\">"-                         , "    <morph at=\"Φ.a🌵0\">"-                         , "      <morphed meta=\"𝑛·1·1\">⊥</morphed>"-                         , "    </morph>"-                         , "    <bound meta=\"𝑛1·1\">⊥</bound>"-                         , "    <minted symbol=\"𝜎1\"/>"-                         , "    <built meta=\"𝑛·1·2\">𝜎1</built>"-                         , "    <morph at=\"𝑛·1·2\">"-                         , "      <morphed meta=\"𝑛·1·3\">𝜎1</morphed>"-                         , "    </morph>"-                         , "    <answered meta=\"𝑛·1·4\">𝜎1</answered>"-                         , "  </evaluate>"-                         , "  <unanswered λ=\"𝜎1\" by=\"morph\">𝜎1</unanswered>"-                         , "</morph>"-                         ]--        it "keeps writing text when the file is named anything else" $-          withTempFile "protocolXXXXXX.xmir" $ \(path, stream) -> do-            hClose stream-            withStdin sum' $-              testCLISucceeded ["dataize", symbolic, "--protocol=" ++ path, "--quiet", "--sweet", "--hide-rho"] []-            records <- readProtocol path-            take 1 (lines records) `shouldBe` ["𝔻(Φ):"]--    describe "--partial" $ do-      let stuck = "[[ bytes ↦ ⟦ φ ↦ ∅ ⟧, number(φ) -> [[ times(^, x) -> [[ L> L_number_times ]], nope -> [[ ^ -> ?, L> L_number_nope ]] ]], @ -> 2.times(3).nope ]]"-          dispatched = "[[ foo -> [[ bar -> [[ ^ -> ?, L> L_number_nope ]] ]], @ -> Q.foo.bar ]]"-          wrapped = "[[ app -> [[ foo -> [[ bar -> [[ ^ -> ?, L> L_number_nope ]] ]], @ -> Q.app.foo.bar ]] ]]"-      it "fails on a λ function that cannot fire without the flag" $-        withStdin stuck $-          testCLIFailed-            ["dataize", symbolic, "--sweet", "--hide-rho"]-            ["No entry of --symbolic answers the λ function 'L_number_nope'"]--      it "prints the residue with the stuck application intact and exits successfully" $-        withStdin stuck $-          testCLISucceeded-            ["dataize", symbolic, "--partial", "--sweet", "--hide-rho"]-            ["L_number_nope:λ"]--      it "keeps what was evaluated before the stuck site in the residue" $-        withStdin stuck $-          testCLISucceeded-            ["dataize", symbolic, "--partial", "--sweet"]-            ["φ ↦ 𝜎1:λ"]--      it "records every firing before the stuck site in --protocol" $-        withTempFile "protocolXXXXXX.txt" $ \(path, stream) -> do-          hClose stream-          withStdin stuck $-            testCLISucceeded ["dataize", symbolic, "--partial", "--protocol=" ++ path, "--quiet", "--sweet", "--hide-rho"] []-          records <- readProtocol path-          lines records-            `shouldBe` [ "𝔻(Φ):"-                       , "  𝕄(Φ):"-                       , "    𝑛·0·1 := 2"-                       , "    𝑛·0·2 := 𝑛·0·1.times( x ↦ 3 )"-                       , "    𝔼(L_number_times):"-                       , "      𝔻(Φ.a🌵0):"-                       , "        𝕄(Φ.a🌵0):"-                       , "          𝑛·1·1 := 2"-                       , "          𝑛·1·2 := Φ.bytes( φ ↦ 40-00-00-00-00-00-00-00:Δ )"-                       , "        𝛿·1·1 := 40-00-00-00-00-00-00-00  # 2.0"-                       , "      𝛿1·1 := 𝛿·1·1  # 𝔻(ξ.ρ)"-                       , "      𝔻(Φ.a🌵1):"-                       , "        𝕄(Φ.a🌵1):"-                       , "          𝑛·1·3 := 3"-                       , "          𝑛·1·4 := Φ.bytes( φ ↦ 40-08-00-00-00-00-00-00:Δ )"-                       , "        𝛿·1·2 := 40-08-00-00-00-00-00-00  # 3.0"-                       , "      𝛿2·1 := 𝛿·1·2  # 𝔻(ξ.x)"-                       , "      𝑛·1·5 := Φ.number( φ ↦ 𝜎1:λ )  # 𝑛"-                       , "      𝕄(𝑛·1·5):"-                       , "        𝑛·1·6 := Φ.number( φ ↦ 𝜎1:λ )"-                       , "      𝑛·1·7 := 𝑛·1·6  # 𝕄(𝑛·1·5)"-                       , "  unanswered L_number_nope  # 𝔻(L_number_nope:λ)"-                       ]--      it "still prints bytes when nothing gets stuck" $-        withStdin "[[ bytes ↦ ⟦ φ ↦ ∅ ⟧, number(φ) -> [[ plus(^, x) -> [[ L> L_number_plus ]] ]], @ -> 5.plus(6) ]]" $-          testCLISucceeded ["dataize", symbolic, "--partial"] ["40-45-00-00-00-00-00-00"]--      it "prints the residual at --locator as XMIR, with ρ hidden" $-        withStdin wrapped $-          testCLISucceeded-            ["dataize", symbolic, "--partial", "--locator=Q.app", "--output=xmir", "--hide-rho"]-            ["<o name=\"λ\">L_number_nope</o>"]--      it "prints the residual at --locator, not the whole program" $-        withStdin wrapped $ do-          (out, _) <- withStdout (runCLI ["dataize", symbolic, "--partial", "--locator=Q.app", "--hide-rho", "--flat"])-          lines out `shouldBe` ["⟦ λ ⤍ L_number_nope ⟧"]--      it "prints a residual of several top bindings as XMIR" $-        withStdin dispatched $-          testCLISucceeded-            ["dataize", symbolic, "--partial", "--output=xmir"]-            ["<o base=\"Φ.foo\" name=\"ρ\"/>", "<o name=\"λ\">L_number_nope</o>"]--      it "prints the chain of steps ending in the residue with --sequence" $-        withStdin stuck $-          testCLISucceeded-            ["dataize", symbolic, "--partial", "--sequence", "--sweet", "--hide-rho", "--flat"]-            ["2.times( 3 ).nope", "L_number_nope:λ"]--      it "still stops on the terminator ⊥, since a wrong operand is not a stuck λ function" $-        withStdin "[[ ]]" $-          testCLIFailed ["dataize", "--partial"] ["terminator ⊥"]--    describe "--symbolic" $ do-      let sum' = "[[ bytes ↦ ⟦ φ ↦ ∅ ⟧, number(φ) -> [[ plus(^, x) -> [[ L> L_number_plus ]] ]], @ -> 5.plus(6) ]]"-      it "fires the λ function an entry of the file answers" $-        withStdin sum' $-          testCLISucceeded ["dataize", symbolic] ["40-45-00-00-00-00-00-00"]--      it "reports the progress of the run with --log-level=INFO" $-        withStdin sum' $-          testCLISucceeded ["dataize", symbolic, "--log-level=INFO", "--quiet"] ["[INFO]: Started "]--      it "gets stuck on every λ function when it is not given" $-        withStdin sum' $-          testCLIFailed ["dataize"] ["No entry of --symbolic answers the λ function 'L_number_plus'"]--      it "fails when the file is not there" $-        withStdin sum' $-          testCLIFailed ["dataize", "--symbolic=no-such-file.yaml"] ["no-such-file.yaml"]--      it "fails on a file that carries no entries at all, before dataizing anything" $-        withTempFileContent "symbolicXXXXXX.yaml" "nope: true\n" $ \path ->-          withStdin sum' $-            testCLIFailed ["dataize", "--symbolic=" ++ path] ["cannot be read"]--    describe "a join on a condition nobody decided" $ do-      let joining = T.pack "- λ: L_fork\n  dataize:\n    𝛿1: $.c\n  join:\n    on: 𝛿1\n    left: $.a\n    right: $.b\n"-          dataized :: String -> IO String-          dataized program =-            withLambdasOf joining $ \joins ->-              withStdin program $-                fst <$> withStdout (runCLI ["dataize", "--symbolic=" ++ joins, "--locator=Q.y"])-      it "gets stuck on two sides that do not join, instead of splitting the run" $-        withLambdasOf joining $ \joins ->-          withStdin "⟦ y ↦ ⟦ c ↦ ⟦ λ ⤍ 𝜎1 ⟧, a ↦ ⟦ Δ ⤍ 01- ⟧, b ↦ ⟦ Δ ⤍ 02- ⟧, λ ⤍ L_fork ⟧ ⟧" $-            testCLIFailed ["dataize", "--symbolic=" ++ joins, "--locator=Q.y"] ["No entry of --symbolic answers the λ function 'L_fork'"]--      it "gets stuck on a side whose morph got stuck" $-        withLambdasOf joining $ \joins ->-          withStdin "⟦ y ↦ ⟦ c ↦ ⟦ λ ⤍ 𝜎1 ⟧, a ↦ ⟦ Δ ⤍ 05- ⟧, b ↦ ⟦ λ ⤍ L_none ⟧.x, λ ⤍ L_fork ⟧ ⟧" $-            testCLIFailed ["dataize", "--symbolic=" ++ joins, "--locator=Q.y"] ["No entry of --symbolic answers the λ function 'L_none'"]--      it "dataizes two sides that come to the same term" $ do-        printed <- dataized "⟦ y ↦ ⟦ c ↦ ⟦ λ ⤍ 𝜎1 ⟧, a ↦ ⟦ Δ ⤍ 07- ⟧, b ↦ ⟦ Δ ⤍ 07- ⟧, λ ⤍ L_fork ⟧ ⟧"-        lines printed `shouldBe` ["07-"]--      it "dataizes two sides that differ only in their symbols" $ do-        printed <- dataized "⟦ y ↦ ⟦ c ↦ ⟦ λ ⤍ 𝜎1 ⟧, a ↦ ⟦ φ ↦ ⟦ λ ⤍ 𝜎2 ⟧ ⟧, b ↦ ⟦ φ ↦ ⟦ λ ⤍ 𝜎3 ⟧ ⟧, λ ⤍ L_fork ⟧ ⟧"-        length (lines printed) `shouldBe` 1--    describe "--inside" $ do-      let universe = "[[ bytes ↦ ⟦ φ ↦ ∅ ⟧, number(φ) -> [[ plus(^, x) -> [[ L> L_number_plus ]] ]], @ -> [[ D> 01- ]] ]]"-      it "dataizes an expression the input does not contain" $-        withStdin universe $-          testCLISucceeded ["dataize", symbolic, "--inside=5.plus( 6 )"] ["40-45-00-00-00-00-00-00"]--      it "normalizes what it is handed before dataizing it" $-        withStdin universe $-          testCLISucceeded ["dataize", "--inside=[[ x -> [[ D> 2A- ]] ]].x"] ["2A-"]--      it "morphs inside the universe just as it dataizes inside it" $-        withStdin universe $-          testCLISucceeded ["morph", symbolic, "--inside=5.plus( 6 )", "--sweet", "--hide-rho", "--flat"] ["⟦ x ↦ 6, λ ⤍ L_number_plus ⟧"]--      it "opens the text protocol before the objects it made while aiming" $-        withTempFile "protocolXXXXXX.txt" $ \(path, stream) -> do-          hClose stream-          withStdin universe $-            testCLISucceeded ["morph", symbolic, "--inside=5.plus( 6 )", "--protocol=" ++ path, "--sweet", "--hide-rho", "--quiet"] []-          records <- readProtocol path-          takeWhile (/= '(') (concat (take 1 (lines records))) `shouldBe` "𝕄"--      it "opens the XML protocol before the objects it made while aiming" $-        withTempFile "protocolXXXXXX.xml" $ \(path, stream) -> do-          hClose stream-          withStdin universe $-            testCLISucceeded ["dataize", symbolic, "--inside=5.plus( 6 )", "--protocol=" ++ path, "--sweet", "--hide-rho", "--quiet"] []-          records <- readProtocol path-          take 1 (lines records) `shouldBe` ["<?xml version=\"1.0\" encoding=\"UTF-8\"?>"]--      it "cannot be used together with --locator" $-        withStdin universe $-          testCLIFailed ["dataize", "--inside=Q.@", "--locator=Q.@"] ["--inside and --locator cannot be used together"]--      it "fails when the input expression is not a formation" $-        withStdin "Q.x" $-          testCLIFailed ["dataize", "--inside=Q.x"] ["--inside requires the input expression to be a formation"]--    describe "fails" $ do-      it "with --output != latex and --nonumber" $-        withStdin "" $-          testCLIFailed-            ["dataize", "--nonumber", "--output=xmir"]-            ["The --nonumber option can stay together with --output=latex only"]--      it "with --omit-listing and --output != xmir" $-        withStdin "" $-          testCLIFailed-            ["dataize", "--omit-listing", "--output=phi"]-            ["--omit-listing"]--      it "with --omit-comments and --output != xmir" $-        withStdin "" $-          testCLIFailed-            ["dataize", "--omit-comments", "--output=phi"]-            ["--omit-comments"]--      it "with --expression and --output != latex" $-        withStdin "" $-          testCLIFailed-            ["dataize", "--expression=foo", "--output=phi"]-            ["--expression option can stay together with --output=latex only"]--      it "with --label and --output != latex" $-        withStdin "" $-          testCLIFailed-            ["dataize", "--label=foo", "--output=phi"]-            ["--label option can stay together with --output=latex only"]--      it "with wrong --hide option" $-        withStdin "" $-          testCLIFailed-            ["dataize", "--hide=Q.x(Q.y)"]-            ["[ERROR]: Invalid set of arguments: Only dispatch expression", "but given: Φ.x( Φ.y )"]--      it "with wrong --show option" $-        withStdin "" $-          testCLIFailed-            ["dataize", "--show=Q.x(Q.y)"]-            ["[ERROR]:", "Only dispatch expression started with Φ (or Q) can be used in --show"]--      it "with wrong --locator option" $-        withStdin "" $-          testCLIFailed-            ["dataize", "--locator=Q.x(Q.y)"]-            ["[ERROR]:", "Only dispatch expression started with Φ (or Q) can be used in --locator"]--      it "with wrong --focus option" $-        withStdin "" $-          testCLIFailed-            ["dataize", "--focus=Q.x(Q.y)"]-            ["[ERROR]:", "Only dispatch expression started with Φ (or Q) can be used in --focus"]--      it "with wrong --locator option written through ξ, keeping ξ in the error" $-        withStdin "" $-          testCLIFailed-            ["dataize", "--locator=ξ.x"]-            ["[ERROR]:", "but given: ξ.x"]--    it "accepts --depth-sensitive" $-      withStdin "[[ D> 01- ]]" $-        testCLISucceeded ["dataize", "--depth-sensitive"] ["01-"]--  describe "morph" $ do-    let chained = "[[ bytes ↦ ⟦ φ ↦ ∅ ⟧, number(φ) -> [[ plus(^, x) -> [[ L> L_number_plus ]] ]], @ -> 5.plus(6).plus(7) ]]"-    it "prints help" $-      testCLISucceeded ["morph", "--help"] ["Morph the 𝜑-expression"]--    it "hands the top formation back untouched under the default locator" $-      withStdin "[[ D> 01- ]]" $-        testCLISucceeded ["morph", "--flat", "--hide-rho"] ["⟦ Δ ⤍ 01- ⟧"]--    it "stops at the bare saturated λ-formation" $-      withStdin chained $-        testCLISucceeded-          ["morph", symbolic, "--locator=Q.@", "--sweet", "--hide-rho", "--flat"]-          ["⟦ x ↦ 7, λ ⤍ L_number_plus ⟧"]--    it "leaves to dataize the firing that takes the same term to bytes" $-      withStdin chained $-        testCLISucceeded ["dataize", symbolic] ["40-45-00-00-00-00-00-00"]--    it "morphs the subterm --locator aims at" $-      withStdin "[[ ex -> Q.x, x -> [[ D> 42- ]] ]]" $-        testCLISucceeded ["morph", "--locator=Q.ex", "--flat", "--hide-rho"] ["⟦ Δ ⤍ 42- ⟧"]--    it "canonizes the answer it prints" $-      withStdin "[[ x -> [[ L> Foo ]], y -> [[ L> Bar ]] ]]" $-        testCLISucceeded ["morph", "--canonize", "--flat", "--sweet"] ["⟦ x ↦ Fn1:λ, y ↦ Fn2:λ ⟧"]--    it "hides a binding of the answer it prints" $-      withStdin "[[ x -> [[ L> Foo ]], y -> [[ L> Bar ]] ]]" $-        testCLISucceeded ["morph", "--hide=Q.x", "--flat", "--sweet"] ["Bar:λ:y"]--    it "shows only one binding of the answer it prints" $-      withStdin "[[ x -> [[ L> Foo ]], y -> [[ L> Bar ]] ]]" $-        testCLISucceeded ["morph", "--show=Q.x", "--flat", "--sweet"] ["Foo:λ:x"]--    it "prints ⊥ instead of failing the run" $-      withStdin "[[ x -> $ ]]" $-        testCLISucceeded ["morph", "--locator=Q.x"] ["⊥"]--    it "fails to dataize what it morphs to ⊥" $-      withStdin "[[ x -> $ ]]" $-        testCLIFailed ["dataize", "--locator=Q.x"] ["terminator ⊥"]--    it "prints the chain of morphing steps with --sequence" $-      withStdin chained $-        testCLISucceeded-          ["morph", symbolic, "--locator=Q.@", "--sequence", "--headers", "--sweet", "--hide-rho", "--flat"]-          [ "Rule 'maa'"-          , "Rule 'alpha'"-          , "Rule 'copy'"-          , "Rule 'mf'"-          , "⟦ x ↦ 7, λ ⤍ L_number_plus ⟧"-          ]--    it "writes every step of a LaTeX --sequence with the arrow of its judgment" $-      withStdin "[[ q -> [[ ]], k -> Q.q ]]" $-        testCLISucceeded-          ["morph", "--locator=Q.k", "--sequence", "--output=latex", "--flat", "--sweet", "--quiet"]-          [ intercalate-              "\n"-              [ "\\begin{phiquation}"-              , "[[ |q| -> [[]], |k| -> Q . |q| ]] \\phiMorph[\\nameref{r:md}]"-              , "  \\phiMorph [[ |q| -> [[]], |k| -> [[ |q| -> [[]], |k| -> Q . |q| ]] . |q| ]] \\phiNormalize[\\nameref{r:dot}]"-              , "  \\phiNormalize [[ |q| -> [[]], |k| -> [[]] ( \\phiTerminal{\\rho} -> Q ) ]] \\phiNormalize[\\nameref{r:skip}]"-              , "  \\phiNormalize [[ |q| -> [[]], |k| -> [[]] ]] \\phiMorph[\\nameref{r:mf}]"-              , "  \\phiMorph [[ |q| -> [[]], |k| -> [[]] ]]{.}"-              , "\\end{phiquation}"-              ]-          ]--    it "does not print the result with --quiet" $-      withStdin "[[ D> 01- ]]" $-        testCLISucceeded ["morph", "--quiet"] []--    it "records the λ functions it fires with --protocol" $-      withTempFile "protocolXXXXXX.txt" $ \(path, stream) -> do-        hClose stream-        withStdin chained $-          testCLISucceeded ["morph", symbolic, "--locator=Q.@", "--protocol=" ++ path, "--quiet", "--sweet", "--hide-rho"] []-        records <- readProtocol path-        lines records-          `shouldBe` [ "𝕄(Φ.φ):"-                     , "  𝑛·0·1 := 5"-                     , "  𝑛·0·2 := 𝑛·0·1.plus( x ↦ 6 )"-                     , "  𝔼(L_number_plus):"-                     , "    𝔻(Φ.a🌵0):"-                     , "      𝕄(Φ.a🌵0):"-                     , "        𝑛·1·1 := 5"-                     , "        𝑛·1·2 := Φ.bytes( φ ↦ 40-14-00-00-00-00-00-00:Δ )"-                     , "      𝛿·1·1 := 40-14-00-00-00-00-00-00  # 5.0"-                     , "    𝛿1·1 := 𝛿·1·1  # 𝔻(ξ.ρ)"-                     , "    𝔻(Φ.a🌵1):"-                     , "      𝕄(Φ.a🌵1):"-                     , "        𝑛·1·3 := 6"-                     , "        𝑛·1·4 := Φ.bytes( φ ↦ 40-18-00-00-00-00-00-00:Δ )"-                     , "      𝛿·1·2 := 40-18-00-00-00-00-00-00  # 6.0"-                     , "    𝛿2·1 := 𝛿·1·2  # 𝔻(ξ.x)"-                     , "    𝑛·1·5 := Φ.number( φ ↦ 𝜎1:λ )  # 𝑛"-                     , "    𝕄(𝑛·1·5):"-                     , "      𝑛·1·6 := Φ.number( φ ↦ 𝜎1:λ )"-                     , "    𝑛·1·7 := 𝑛·1·6  # 𝕄(𝑛·1·5)"-                     , "  𝑛·0·3 := 𝑛·1·6.plus( x ↦ 7 )"-                     ]--    it "saves morphing steps to dir with --steps-dir" $-      withTempDirectory "phino-steps-morph" $ \dir ->-        withStdin chained $ do-          testCLISucceeded-            ["morph", symbolic, "--locator=Q.@", "--steps-dir=" ++ dir, "--sweet", "--hide-rho", "--flat"]-            ["⟦ x ↦ 7, λ ⤍ L_number_plus ⟧"]-          steps <- sort <$> listDirectory dir-          steps `shouldBe` map (\n -> printf "%05d.phi" (n :: Int)) [1 .. length steps]-          length steps `shouldSatisfy` (> 0)--    it "accepts --seed, --shuffle and --depth-sensitive" $-      withStdin "[[ D> 01- ]]" $-        testCLISucceeded ["morph", "--seed=7", "--shuffle", "--depth-sensitive", "--flat", "--hide-rho"] ["⟦ Δ ⤍ 01- ⟧"]--    it "returns the λ-formation dataize cannot finish on" $-      withStdin "⟦ @ ↦ ⟦ λ ⤍ L_number_div, ρ ↦ ⟦ Δ ⤍ 40-45-00-00-00-00-00-00 ⟧, x ↦ ⟦ Δ ⤍ 40-00-00-00-00-00-00-00 ⟧ ⟧ ⟧" $-        testCLISucceeded-          ["morph", "--locator=Q.@", "--max-steps=40", "--flat", "--hide-rho"]-          ["⟦ λ ⤍ L_number_div"]--    it "fails once the --max-steps budget is spent" $-      withStdin chained $-        testCLIFailed-          ["morph", "--locator=Q.@", "--max-steps=3"]-          ["[ERROR]: Morphing did not finish before reaching the limit of steps: --max-steps=3"]--    it "names morphing, not dataization, when the budget is spent before dataization starts" $-      withStdin "⟦ x ↦ Φ.x.y ⟧" $-        testCLIFailed-          ["morph", "--locator=Q.x", "--max-steps=5"]-          ["[ERROR]: Morphing did not finish before reaching the limit of steps: --max-steps=5"]--    describe "--max-firings" $ do-      let splitting = withLambdasOf (T.pack "- λ: L_split\n  morph:\n    𝑛1: Φ.s.foo\n    𝑛2: Φ.s.foo\n  𝑛: ⟦ l ↦ 𝑛1, r ↦ 𝑛2 ⟧\n")-          split = "⟦ s ↦ ⟦ λ ⤍ L_split ⟧, x ↦ Φ.s.foo ⟧"-      it "fails with non-positive --max-firings" $-        withStdin split $-          testCLIFailed ["morph", "--max-firings=0"] ["--max-firings must be positive"]--      it "fails once the --max-firings budget is spent on a widening recursion" $-        splitting $ \table ->-          withStdin split $-            testCLIFailed-              ["morph", "--symbolic=" ++ table, "--locator=Q.x", "--max-firings=64"]-              ["[ERROR]: Evaluation did not finish before reaching the limit of firings: --max-firings=64"]--      it "ends the widening recursion with --partial" $-        splitting $ \table ->-          withStdin split $-            testCLISucceeded-              ["morph", "--symbolic=" ++ table, "--locator=Q.x", "--max-firings=64", "--partial", "--flat", "--hide-rho", "--sweet"]-              ["L_split:λ.foo"]--      it "fires no more λ functions than --max-firings allows" $-        splitting $ \table ->-          withTempFile "protocolXXXXXX.txt" $ \(path, stream) -> do-            hClose stream-            withStdin split $-              testCLISucceeded-                ["morph", "--symbolic=" ++ table, "--locator=Q.x", "--max-firings=64", "--partial", "--protocol=" ++ path, "--quiet"]-                []-            records <- readProtocol path-            length (filter (isInfixOf "𝔼(L_split)") (lines records)) `shouldBe` 64--    describe "--max-seconds" $ do-      let ladder = withLambdasOf (T.pack "- λ: L_split\n  morph:\n    𝑛1: ξ.n.foo\n    𝑛2: ξ.n.foo\n  𝑛: ⟦ l ↦ 𝑛1, r ↦ 𝑛2 ⟧\n")-          rungs = "⟦ " ++ intercalate ", " [printf "l%d ↦ ⟦ λ ⤍ L_split, n ↦ Φ.l%d ⟧" rung (rung + 1) | rung <- [0 .. 23 :: Int]] ++ ", l24 ↦ ⟦⟧, x ↦ Φ.l0.foo ⟧"-          bounded :: Expectation -> Expectation-          bounded check = timeout 60000000 check >>= (`shouldBe` Just ())-      it "fails with non-positive --max-seconds" $-        withStdin rungs $-          testCLIFailed ["morph", "--max-seconds=0"] ["--max-seconds must be positive"]--      it "fails with a --max-seconds value above maxBound Int, naming the limit" $-        withStdin rungs $-          testCLIFailed-            ["morph", "--max-seconds=9223372036854775808"]-            ["value must be between", "9223372036854775807"]--      it "fails once the --max-seconds budget is spent" $-        ladder $ \table ->-          bounded $-            withStdin rungs $-              testCLIFailed-                ["morph", "--symbolic=" ++ table, "--locator=Q.x", "--max-seconds=1"]-                ["[ERROR]: Evaluation did not finish before reaching the limit of seconds: --max-seconds=1"]--      it "fails dataize once the --max-seconds budget is spent" $-        ladder $ \table ->-          bounded $-            withStdin rungs $-              testCLIFailed-                ["dataize", "--symbolic=" ++ table, "--locator=Q.x", "--max-seconds=1"]-                ["[ERROR]: Evaluation did not finish before reaching the limit of seconds: --max-seconds=1"]--      it "fails once the --max-seconds budget is spent with --partial" $-        ladder $ \table ->-          bounded $-            withStdin rungs $-              testCLIFailed-                ["morph", "--symbolic=" ++ table, "--max-seconds=1", "--locator=Q.x", "--partial"]-                ["[ERROR]: Evaluation did not finish before reaching the limit of seconds: --max-seconds=1"]--      forM_ [["--locator=Q.x"], ["--locator=Q.x", "--partial"]] $ \opts ->-        it ("writes the timeout as the last line of the protocol with " ++ unwords opts) $-          ladder $ \table ->-            withTempFile "protocolXXXXXX.txt" $ \(path, stream) -> do-              hClose stream-              bounded $-                withStdin rungs $-                  testCLIFailed-                    (["morph", "--symbolic=" ++ table, "--max-seconds=1", "--protocol=" ++ path, "--quiet"] ++ opts)-                    ["--max-seconds=1"]-              records <- readProtocol path-              dropWhile (== ' ') (last (lines records)) `shouldBe` "timeout 1"--      it "writes the timeout once to the XML protocol" $-        ladder $ \table ->-          withTempFile "protocolXXXXXX.xml" $ \(path, stream) -> do-            hClose stream-            bounded $-              withStdin rungs $-                testCLIFailed-                  ["morph", "--symbolic=" ++ table, "--locator=Q.x", "--partial", "--max-seconds=1", "--protocol=" ++ path, "--quiet"]-                  ["--max-seconds=1"]-            records <- readProtocol path-            length (filter (isInfixOf "<timeout limit=\"1\" by=\"morph\" at=\"") (lines records)) `shouldBe` 1--      it "closes the XML protocol of a run out of time" $-        ladder $ \table ->-          withTempFile "protocolXXXXXX.xml" $ \(path, stream) -> do-            hClose stream-            bounded $-              withStdin rungs $-                testCLIFailed-                  ["morph", "--symbolic=" ++ table, "--locator=Q.x", "--max-seconds=1", "--protocol=" ++ path, "--quiet"]-                  ["--max-seconds=1"]-            document <- X.readFile X.def path-            X.nameLocalName (X.elementName (X.documentRoot document)) `shouldBe` T.pack "protocol"--    it "parks the spent budget as a residual with --partial" $-      withStdin "⟦ φ ↦ 5.gt(Φ.nan) ⟧" $-        testCLISucceeded-          ["morph", "--locator=Q.@", "--max-steps=10", "--partial", "--flat", "--hide-rho", "--sweet"]-          ["5.gt( Φ.nan )"]--    describe "--partial" $ do-      let stuck = "[[ @ -> [[ L> Sym_arg_0 ]].foo ]]"-      it "fails on a λ function that cannot fire without the flag" $-        withStdin stuck $-          testCLIFailed ["morph", "--locator=Q.@"] ["No entry of --symbolic answers the λ function 'Sym_arg_0'"]--      it "prints the residue with the stuck application intact and exits successfully" $-        withStdin stuck $-          testCLISucceeded-            ["morph", "--locator=Q.@", "--partial", "--flat", "--hide-rho"]-            ["⟦ λ ⤍ Sym_arg_0 ⟧.foo"]--    describe "a copy a 'defer' line of a join defers" $ do-      let joining = T.pack "- λ: L_fork\n  dataize:\n    𝛿1: $.c\n  morph:\n    𝑛1: $.a\n    𝑛2: $.b\n  defer:\n    𝑛3: 𝑛1\n    𝑛4: 𝑛2\n  join:\n    on: 𝛿1\n    left: 𝑛3\n    right: 𝑛4\n"-          counting = T.pack "- λ: L_inc\n  dataize:\n    𝛿1: $.x\n  𝑛: Φ.num( φ ↦ ⟦ λ ⤍ 𝜎 ⟧ )\n"-          recordedWith :: T.Text -> [String] -> String -> IO [String]-          recordedWith entries options program =-            withTempFile "protocolXXXXXX.xml" $ \(path, stream) -> do-              hClose stream-              withLambdasOf entries $ \joins ->-                withStdin program $-                  testCLIFailed (["dataize", "--symbolic=" ++ joins, "--locator=Q.y", "--protocol=" ++ path, "--quiet", "--sweet", "--hide-rho"] ++ options) ["No entry of --symbolic answers the λ function 'L_fork'"]-              lines <$> readProtocol path-          deferred :: [String] -> String -> IO [String]-          deferred = recordedWith joining-      it "writes the copy it deferred to the XML protocol as a call of the object it was made of" $ do-        records <- deferred [] "⟦ step(tup) ↦ ⟦ φ ↦ tup.next ⟧, items ↦ ⟦ tail ↦ ⟦ λ ⤍ 𝜎1 ⟧ ⟧, y ↦ ⟦ c ↦ ⟦ λ ⤍ 𝜎2 ⟧, a ↦ ⟦ Δ ⤍ 01- ⟧, b ↦ Φ.step( tup ↦ Φ.items.tail ), λ ⤍ L_fork ⟧ ⟧"-        records-          `shouldBe` [ "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"-                     , "<dataize at=\"Φ.y\">"-                     , "  <evaluate λ=\"L_fork\" by=\"dataize\" at=\"Φ.y\">"-                     , "    <dataize at=\"Φ.a🌵0\">"-                     , "      <manufactured meta=\"𝛿·1·1\">𝜎2</manufactured>"-                     , "    </dataize>"-                     , "    <bound meta=\"𝛿1·1\">𝛿·1·1</bound>"-                     , "    <morph at=\"Φ.a🌵1\">"-                     , "      <morphed meta=\"𝑛·1·1\">01-</morphed>"-                     , "    </morph>"-                     , "    <bound meta=\"𝑛1·1\">01-</bound>"-                     , "    <morph at=\"Φ.a🌵2\">"-                     , "      <applied meta=\"𝑛·1·2\" by=\"morph\" at=\"Φ.a🌵2\" of=\"Φ.step\"><ref attr=\"tup\">Φ.items.tail</ref></applied>"-                     , "      <morphed meta=\"𝑛·1·3\">𝑛·1·2</morphed>"-                     , "    </morph>"-                     , "    <bound meta=\"𝑛2·1\">𝑛·1·2</bound>"-                     , "    <bound meta=\"𝑛3·1\">01-</bound>"-                     , "    <deferred symbol=\"𝜎3\" by=\"morph\" at=\"𝑛2.φ\" of=\"Φ.step\"><with><symbol attr=\"tup\">𝜎1</symbol></with></deferred>"-                     , "    <diverged left=\"𝑛3·1\" right=\"𝜎3\" at=\".Δ\" reason=\"the join names no otherwise\"/>"-                     , "  </evaluate>"-                     , "</dataize>"-                     ]--      it "names the object a deferred copy was made of through the formation its ρ holds" $ do-        records <- deferred [] "⟦ joined(items) ↦ ⟦ φ ↦ step( tup ↦ items ), step(ρ, tup) ↦ ⟦ φ ↦ tup.next ⟧ ⟧, items ↦ ⟦ tail ↦ ⟦ λ ⤍ 𝜎1 ⟧ ⟧, y ↦ ⟦ c ↦ ⟦ λ ⤍ 𝜎2 ⟧, a ↦ ⟦ Δ ⤍ 01- ⟧, b ↦ Φ.joined( items ↦ Φ.items.tail ).φ, λ ⤍ L_fork ⟧ ⟧"-        records `shouldContain` ["    <deferred symbol=\"𝜎3\" by=\"morph\" at=\"𝑛2.φ\" of=\"Φ.joined.step\"><with><symbol attr=\"tup\">𝜎1</symbol></with></deferred>"]--      it "names an argument of a deferred copy that is no bare symbol by the element that formed it" $ do-        records <- deferred [] "⟦ box(x, w) ↦ ⟦ φ ↦ x.next ⟧, items ↦ ⟦ tail ↦ ⟦ λ ⤍ 𝜎1 ⟧ ⟧, y ↦ ⟦ c ↦ ⟦ λ ⤍ 𝜎2 ⟧, a ↦ ⟦ Δ ⤍ 01- ⟧, b ↦ Φ.box( x ↦ Φ.items.tail, w ↦ ⟦ z ↦ Φ ⟧ ), λ ⤍ L_fork ⟧ ⟧"-        records `shouldContain` ["    <deferred symbol=\"𝜎3\" by=\"morph\" at=\"𝑛2.φ\" of=\"Φ.box\"><with><symbol attr=\"x\">𝜎1</symbol><meta attr=\"w\">𝑛·1·3</meta></with></deferred>"]--      it "names a deferred copy of a formation the world does not declare by the element that formed it" $ do-        records <- deferred [] "⟦ wrap(v) ↦ ⟦⟧, items ↦ ⟦ tail ↦ ⟦ λ ⤍ 𝜎1 ⟧ ⟧, y ↦ ⟦ c ↦ ⟦ λ ⤍ 𝜎2 ⟧, a ↦ ⟦ Δ ⤍ 01- ⟧, b ↦ Φ.wrap( v ↦ ⟦ inner(x) ↦ ⟦ φ ↦ x.next ⟧ ⟧ ).v.inner( x ↦ Φ.items.tail ), λ ⤍ L_fork ⟧ ⟧"-        records `shouldContain` ["    <formed meta=\"𝑛·1·7\"><symbol attr=\"x\">𝜎1</symbol><ref attr=\"φ\">x.next</ref></formed>", "    <deferred symbol=\"𝜎3\" by=\"morph\" at=\"𝑛2.φ\" of=\"𝑛·1·7\"/>"]--      it "names an argument of a deferred copy by the object its morph answered" $ do-        records <- recordedWith (joining <> counting) [] "⟦ box(x, w) ↦ ⟦ φ ↦ x.next ⟧, num(φ) ↦ ⟦⟧, inc(x) ↦ ⟦ λ ⤍ L_inc ⟧, items ↦ ⟦ tail ↦ ⟦ λ ⤍ 𝜎1 ⟧ ⟧, y ↦ ⟦ c ↦ ⟦ λ ⤍ 𝜎2 ⟧, a ↦ ⟦ Δ ⤍ 01- ⟧, b ↦ Φ.box( x ↦ Φ.items.tail, w ↦ Φ.inc( ⟦ Δ ⤍ 2A- ⟧ ) ), λ ⤍ L_fork ⟧ ⟧"-        filter (isInfixOf "<deferred") records `shouldBe` ["    <deferred symbol=\"𝜎4\" by=\"morph\" at=\"𝑛2.φ\" of=\"Φ.box\"><with><symbol attr=\"x\">𝜎1</symbol><meta attr=\"w\">𝑛·2·3</meta></with></deferred>"]--    describe "--acyclic=plausible" $ do-      let twins = "[[ bytes ↦ ⟦ φ ↦ ∅ ⟧, number(φ) -> [[ plus(^, x) -> [[ L> L_number_plus ]] ]], pair(a, b) -> [[ L> L_pair ]], t -> Φ.pair( 7.plus( 5.plus( 6 ) ), 7.plus( 5.plus( 6 ) ) ).φ ]]"-          pairs = T.pack "- λ: L_number_plus\n  dataize:\n    𝛿1: $.ρ\n    𝛿2: $.x\n  𝑛: Φ.number( φ ↦ ⟦ λ ⤍ 𝜎 ⟧ )\n- λ: L_pair\n  morph:\n    𝑛1: $.a\n    𝑛2: $.b\n  evaluate:\n    𝑛3: 𝑛1\n    𝑛4: 𝑛2\n  𝑛: ⟦ φ ↦ ⟦ a ↦ 𝑛3, b ↦ 𝑛4 ⟧ ⟧\n"-          recorded :: String -> IO [String]-          recorded mode =-            withTempFile "protocolXXXXXX.txt" $ \(path, stream) -> do-              hClose stream-              withLambdasOf pairs $ \file ->-                withStdin twins $-                  testCLISucceeded-                    ["morph", "--symbolic=" ++ file, "--locator=Q.t", "--acyclic=" ++ mode, "--protocol=" ++ path, "--quiet"]-                    []-              lines <$> readProtocol path-      it "charges a formation once per argument spelling it under proven" $-        withLambdasOf pairs $ \file ->-          withStdin twins $-            testCLIFailed-              ["morph", "--symbolic=" ++ file, "--locator=Q.t", "--acyclic=proven", "--max-firings=4"]-              ["[ERROR]: Evaluation did not finish before reaching the limit of firings: --max-firings=4"]--      it "charges a formation once for the run under plausible" $-        withLambdasOf pairs $ \file ->-          withStdin twins $-            testCLISucceeded-              ["morph", "--symbolic=" ++ file, "--locator=Q.t", "--acyclic=plausible", "--max-firings=3", "--flat", "--hide-rho", "--sweet"]-              ["b ↦ ⟦ φ ↦ 𝜎2:λ, plus(x) ↦ L_number_plus:λ ⟧"]--      it "reduces the operands of a formation once per argument spelling it under proven" $-        recorded "proven" >>= (`shouldSatisfy` ((== 4) . length . filter (isInfixOf "𝛿1·")))--      it "reduces the operands of a formation once for the run under plausible" $-        recorded "plausible" >>= (`shouldSatisfy` ((== 2) . length . filter (isInfixOf "𝛿1·")))--      it "writes a recalled firing at its own site under plausible" $-        recorded "plausible" >>= (`shouldSatisfy` ((== 3) . length . filter (isInfixOf "𝔼(L_number_plus)")))--      it "answers a recalled firing with the line of the first under plausible" $-        recorded "plausible" >>= (`shouldContain` ["      𝑛·4·1 := 𝑛·2·6  # 𝑛"])--      it "dataizes to the same datum under plausible" $-        withStdin chained $-          testCLISucceeded-            ["dataize", symbolic, "--acyclic=plausible", "--locator=Q.@"]-            ["40-45-00-00-00-00-00-00"]--    describe "--acyclic=proven" $ do-      let looping = "⟦ x ↦ ⟦ λ ⤍ L_loop ⟧.foo ⟧"-      it "spends the whole budget and fails on the limit without the flag" $-        loopingLambdas $ \endless ->-          withStdin looping $-            testCLIFailed-              ["morph", "--symbolic=" ++ endless, "--locator=Q.x", "--max-steps=40"]-              ["[ERROR]: Morphing did not finish before reaching the limit of steps: --max-steps=40"]--      it "prints the residue and exits successfully with the flag" $-        loopingLambdas $ \endless ->-          withStdin looping $-            testCLISucceeded-              ["morph", "--symbolic=" ++ endless, "--locator=Q.x", "--acyclic=proven", "--max-steps=4000", "--flat", "--hide-rho"]-              ["⟦ λ ⤍ L_loop ⟧.foo"]--      it "answers a terminating program the same way with the flag" $-        withStdin chained $-          testCLISucceeded-            ["morph", symbolic, "--acyclic=proven", "--locator=Q.@", "--sweet", "--hide-rho", "--flat"]-            ["⟦ x ↦ 7, λ ⤍ L_number_plus ⟧"]--    describe "--max-loops" $ do-      it "fires a looping λ function once more with --max-loops=1" $-        loopingLambdas $ \endless ->-          withTempFile "protocolXXXXXX.txt" $ \(path, stream) -> do-            hClose stream-            withStdin "⟦ x ↦ ⟦ λ ⤍ L_loop ⟧.foo ⟧" $-              testCLISucceeded-                ["morph", "--symbolic=" ++ endless, "--locator=Q.x", "--acyclic=proven", "--max-loops=1", "--protocol=" ++ path, "--quiet"]-                []-            records <- readProtocol path-            length (filter (isInfixOf "𝔼(L_loop)") (lines records)) `shouldBe` 2--      it "refuses the flag without --acyclic" $-        withStdin "⟦ x ↦ ⟦ λ ⤍ L_loop ⟧.foo ⟧" $-          testCLIFailed ["morph", "--locator=Q.x", "--max-loops=3"] ["The option --max-loops requires --acyclic, since only --acyclic cuts a loop"]--    it "prints a top formation of several bindings as XMIR" $-      withStdin "[[ x -> [[ D> 01- ]], y -> [[ D> 02- ]] ]]" $-        testCLISucceeded-          ["morph", "--output=xmir"]-          ["<o name=\"x\">01-</o>", "<o name=\"y\">02-</o>"]--    describe "fails" $ do-      it "with --output != latex and --nonumber" $-        withStdin "" $-          testCLIFailed-            ["morph", "--nonumber", "--output=xmir"]-            ["The --nonumber option can stay together with --output=latex only"]--      it "with --show used more than once" $-        withStdin "" $-          testCLIFailed-            ["morph", "--show=Q.a", "--show=Q.b"]-            ["The option --show can be used only once"]--      it "with wrong --locator option" $-        withStdin "" $-          testCLIFailed-            ["morph", "--locator=Q.x(Q.y)"]-            ["[ERROR]:", "Only dispatch expression started with Φ (or Q) can be used in --locator"]--  describe "explain" $ do-    forM_-      ["--morph", "--dataize", "--contextualize"]-      ( \judgment ->-          it ("refuses --normalize together with " ++ judgment) $-            testCLIFailed ["explain", judgment, "--normalize"] ["The --normalize option cannot be used together with"]-      )--    it "prints help" $-      testCLISucceeded-        ["explain", "--help"]-        ["Explain built-in morphing rules", "Explain built-in dataization rules", "Explain built-in contextualization rules"]--    it "explains single rule" $ do-      latex <- explainPack "test-resources/explain-packs/normalize/copy.yaml"-      testCLISucceeded ["explain", "--rule=resources/normalize/copy.yaml"] [latex <> "\n"]--    it "explains single rule with a label" $-      testCLISucceeded-        ["explain", rule "labeled.yaml"]-        [ unlines-            [ "\\phinoNormalizationRule[\\lambda]{copy}"-            , "  { [[ B_1, \\tau -> ?, B_2 ]] ( \\tau -> k ) }"-            , "  { [[ B_1, \\tau -> k, B_2 ]] }"-            , "  { }"-            , "  { }"-            ]-        ]--    it "explains multiple rules" $-      testCLISucceeded-        ["explain", "--rule=resources/normalize/copy.yaml", "--rule=resources/normalize/alpha.yaml"]-        ["\\phinoNormalizationRule{copy}", "\\phinoNormalizationRule{alpha}"]--    it "reproduces the same shuffle order for the same --seed" $ do-      let args =-            [ "explain"-            , "--shuffle"-            , "--seed=42"-            , rule "swap-a.yaml"-            , rule "swap-b.yaml"-            ]-      (firstRun, _) <- withStdout (runCLI args)-      (secondRun, _) <- withStdout (runCLI args)-      firstRun `shouldBe` secondRun--    it "accepts --seed flag" $-      testCLISucceeded-        ["explain", "--seed=7", "--normalize"]-        ["\\phinoNormalizationRule{alpha}"]--    forM_-      [ ("normalization", "--normalize", "normalize")-      , ("morphing", "--morph", "morphing")-      , ("dataization", "--dataize", "dataization")-      , ("contextualization", "--contextualize", "contextualization")-      ]-      ( \(judgment, option, dir) -> it ("explains " <> judgment <> " rules") $ do-          packs <- allPathsIn ("test-resources/explain-packs" </> dir)-          latex <- mapM explainPack (sort packs)-          testCLISucceeded ["explain", option] [unlines latex]-      )--    it "fails with no rules specified" $-      testCLIFailed-        ["explain"]-        ["Either --rule, --normalize, --morph, --dataize or --contextualize must be specified"]--    it "fails when more than one rule set is specified" $-      testCLIFailed-        ["explain", "--morph", "--dataize"]-        ["Only one of --morph, --dataize or --contextualize can be specified"]--    it "allows --normalize together with --rule" $-      testCLISucceeded-        ["explain", "--normalize", "--rule=resources/normalize/copy.yaml"]-        ["\\phinoNormalizationRule{copy}"]--    it "allows --shuffle together with --morph" $-      testCLISucceeded-        ["explain", "--morph", "--shuffle"]-        ["\\begin{phinoMorphingInference}"]--    it "writes to target file" $-      bracket-        ( do-            tmp <- getTemporaryDirectory-            stamp <- getPOSIXTime-            let dir = tmp </> ("phino-test-" ++ show (floor stamp :: Integer))-            createDirectoryIfMissing True dir-            pure (dir </> "explain.tex", dir)-        )-        (\(_, dir) -> removeDirectoryRecursive dir)-        ( \(path, _) -> do-            testCLISucceeded ["explain", "--normalize", printf "--target=%s" path] []-            content <- readFile path-            _ <- evaluate (length content)-            content `shouldContain` "\\phinoNormalizationRule{alpha}"-        )--  describe "merge" $ do-    it "prints help" $-      testCLISucceeded ["merge", "--help"] ["Paths to input files"]--    it "merges single expression" $-      testCLISucceeded-        ["merge", resource "desugar.phi", "--sweet", "--flat"]-        ["x:foo"]--    it "merges EO expressions" $-      testCLISucceeded-        ["merge", "--sweet", resource "number.phi", resource "bytes.phi", resource "string.phi", "--margin=25"]-        [ unlines-            [ "⟦"-            , "  eolang ↦ ⟦"-            , "    number(φ) ↦ ⟦⟧,"-            , "    bytes(data) ↦ ⟦⟧,"-            , "    string(φ) ↦ ⟦⟧,"-            , "    λ ⤍ Package"-            , "  ⟧,"-            , "  λ ⤍ Package"-            , "⟧:org"-            ]-        ]--    it "fails on merging non formations" $-      testCLIFailed-        ["merge", resource "dispatch.phi", resource "number.phi"]-        ["Invalid expression format, only expressions with top level formations are supported for 'merge' command"]--    it "fails on merging conflicted bindings" $-      testCLIFailed-        ["merge", resource "foo.phi", resource "desugar.phi"]-        ["Can't merge two bindings, conflict found"]--    it "fails on merging empty list of expressions" $-      testCLIFailed-        ["merge"]-        ["At least one input file must be specified for 'merge' command"]--    it "merges and prints as XMIR, with the listing rendered from the merged expression" $-      testCLISucceeded-        ["merge", resource "desugar.phi", "--output=xmir"]-        ["<?xml version=\"1.0\" encoding=\"UTF-8\"?>", "<listing>⟦ foo ↦ ξ.x ⟧</listing>", "<o base=\"ξ.x\" name=\"foo\"/>"]--    it "names an atom of XMIR after its locator and keeps its type" $ do-      let xmir = "<object><o name=\"number\"><o name=\"plus\"><o base=\"∅\" name=\"b\"/><o atom=\"Φ.number\" name=\"λ\"/></o></o></object>"-      withTempFileContent "phino-atom.xmir" xmir $ \file -> do-        testCLISucceeded-          ["merge", "--input=xmir", "--sweet", "--flat", file]-          ["L_number_plus:λ"]-        testCLISucceeded-          ["merge", "--input=xmir", "--output=xmir", file]-          ["<o atom=\"Φ.number\" name=\"λ\">L_number_plus</o>"]--    it "keeps the type of an atom of XMIR under --canonize" $ do-      let xmir = "<object><o name=\"number\"><o name=\"plus\"><o base=\"∅\" name=\"b\"/><o atom=\"Φ.number\" name=\"λ\"/></o></o></object>"-      withTempFileContent "phino-canonized-atom.xmir" xmir $ \file ->-        testCLISucceeded-          ["rewrite", "--input=xmir", "--output=xmir", "--canonize", file]-          ["<o atom=\"Φ.number\" name=\"λ\">Fn1</o>"]--    it "reproduces the same output for the same --seed" $ do-      let args =-            [ "merge"-            , "--seed=42"-            , "--sweet"-            , resource "number.phi"-            , resource "bytes.phi"-            ]-      (firstRun, _) <- withStdout (runCLI args)-      (secondRun, _) <- withStdout (runCLI args)-      firstRun `shouldBe` secondRun--  describe "compile" $ do-    it "writes the module to the target" $-      withTempDirectory "phino-compile" $ \dir -> do-        createDirectoryIfMissing True dir-        withCurrentDirectory dir (runCLI ["compile", "--target=gen/Compiled.hs"])-        doesFileExist (dir </> "gen" </> "Compiled.hs") `shouldReturn` True-    it "writes the rules of --rule into the module" $-      withTempDirectory "phino-compile" $ \dir -> do-        createDirectoryIfMissing True dir-        simple <- makeAbsolute "test-resources/cli/rules/simple.yaml"-        withCurrentDirectory dir (runCLI ["compile", "--rule=" ++ simple, "--target=Compiled.hs"])-        readFile' (dir </> "Compiled.hs") >>= (`shouldSatisfy` ("R.direct \"foo\"" `isInfixOf`))-    it "turns the flag on in a new cabal.project.local" $-      withTempDirectory "phino-compile" $ \dir -> do-        createDirectoryIfMissing True dir-        writeFile (dir </> "phino.cabal") ""-        withCurrentDirectory dir (runCLI ["compile", "--target=Compiled.hs"])-        readFile (dir </> "cabal.project.local") `shouldReturn` "package phino\n  flags: +compiled\n"-    it "prints the lines an existing cabal.project.local lacks" $-      withTempDirectory "phino-compile" $ \dir -> do-        createDirectoryIfMissing True dir-        writeFile (dir </> "phino.cabal") ""-        writeFile (dir </> "cabal.project.local") "tests: True\n"-        withCurrentDirectory dir (testCLISucceeded ["compile", "--target=Compiled.hs"] ["package phino\n  flags: +compiled"])-    it "leaves an existing cabal.project.local as it is" $-      withTempDirectory "phino-compile" $ \dir -> do-        createDirectoryIfMissing True dir-        writeFile (dir </> "phino.cabal") ""-        writeFile (dir </> "cabal.project.local") "tests: True\n"-        withStdout (withCurrentDirectory dir (runCLI ["compile", "--target=Compiled.hs"]))-        readFile (dir </> "cabal.project.local") `shouldReturn` "tests: True\n"-    it "writes cabal.project.local next to the module's project, not the current directory" $-      withTempDirectory "phino-compile" $ \dir -> do-        createDirectoryIfMissing True (dir </> "project" </> "gen")-        writeFile (dir </> "project" </> "phino.cabal") ""-        createDirectoryIfMissing True (dir </> "elsewhere")-        withCurrentDirectory-          (dir </> "elsewhere")-          (runCLI ["compile", "--target=" ++ (dir </> "project" </> "gen" </> "Compiled.hs")])-        doesFileExist (dir </> "elsewhere" </> "cabal.project.local") `shouldReturn` False-        readFile (dir </> "project" </> "cabal.project.local") `shouldReturn` "package phino\n  flags: +compiled\n"-    it "only prints the lines when it cannot find the project of the module" $-      withTempDirectory "phino-compile" $ \dir -> do-        createDirectoryIfMissing True dir-        withCurrentDirectory dir (testCLISucceeded ["compile", "--target=gen/Compiled.hs"] ["package phino\n  flags: +compiled"])-        doesFileExist (dir </> "cabal.project.local") `shouldReturn` False-    it "refuses a rule it cannot compile" $-      withTempDirectory "phino-compile" $ \dir -> do-        createDirectoryIfMissing True dir-        writeFile (dir </> "having.yaml") "name: hv\npattern: '[[ x -> !e1, !B1 ]]'\nresult: '[[ !B1 ]]'\nhaving:\n  eq: ['!e1', 'Q']\n"-        withCurrentDirectory dir (testCLIFailed ["compile", "--rule=having.yaml", "--target=Compiled.hs"] ["The rule 'hv' cannot be compiled, since it has a 'having' condition"])--  describe "match" $ do-    it "prints help" $-      testCLISucceeded-        ["match", "--help"]-        ["Pattern expression to match against", "Predicate for matched substitutions"]--    it "describes --sweet and --flat as applying to the result only" $-      testCLISucceeded-        ["match", "--help"]-        ["Print result 𝜑-expression using syntax sugar", "Print result 𝜑-expression in one line"]--    it "rejects --sequence, which --help must not mention" $-      withStdin "[[ ]]" $-        testCLIFailed ["match", "--sequence", "--pattern=[[ !B ]]"] ["Invalid option"]--    it "takes from stdin" $-      withStdin "[[]]" $-        testCLISucceeded ["match", "--log-level=debug"] ["[DEBUG]"]--    it "takes from file" $-      testCLISucceeded ["match", resource "foo.phi", "--log-level=debug"] ["[DEBUG]"]--    it "does not print substitutions without pattern" $-      withStdin "[[]]" $-        testCLISucceeded ["match", "--log-level=debug"] ["[DEBUG]: The --pattern is not provided, no substitutions are built"]--    it "refuses --when without --pattern" $-      withStdin "[[]]" $-        testCLIFailed ["match", "--when=bogus"] ["The option --when requires --pattern"]--    it "reproduces the same output for the same --seed" $ do-      dir <- getTemporaryDirectory-      let file = dir ++ "/phino-match-seed-test.phi"-      writeFile file "[[ x -> Q.x, y -> Q.y, z -> Q.z ]]"-      let args =-            [ "match"-            , "--seed=42"-            , "--sweet"-            , "--flat"-            , "--pattern=Q.!t"-            , file-            ]-      (firstRun, _) <- withStdout (runCLI args)-      (secondRun, _) <- withStdout (runCLI args)-      firstRun `shouldBe` secondRun-      removeFile file--    it "numbers two anonymous captures of one kind, so they stay apart" $-      withStdin "[[ a -> [[ ]], b -> Q ]]" $-        testCLISucceeded ["match", "--pattern=[[ !t -> !e, !t -> !e ]]"] ["e#1 >> ⟦⟧\ne#2 >> Φ\nt#1 >> a\nt#2 >> b"]--    it "prints many substitutions" $-      withStdin "[[ x -> Q.x, y -> Q.y ]]" $-        testCLISucceeded ["match", "--pattern=Q.!t"] ["t >> x\n------\nt >> y"]--    it "does not match a length against a literal that wraps around Int" $-      withStdin "[[ a -> $, b -> Q ]]" $-        testCLIFailed ["match", "--pattern=[[ !B1 ]]", "--when=eq(length(!B1),18446744073709551618)"] ["no substitutions are built"]--    it "builds substitutions with conditions" $-      withStdin "[[ x -> Q.y ]].x" $-        testCLISucceeded-          ["match", "--pattern=[[ !t1 -> Q.y, !B1 ]].!t1", "--when=eq(length(!B1),0)"]-          ["B1 >> ⟦⟧\nt1 >> x"]--    it "builds with condition from file" $-      testCLISucceeded-        ["match", "--pattern=[[ !B1 ]]", "--when=eq(length(!B1),1)", resource "foo.phi"]-        ["B1 >> ⟦ foo ↦ Φ.org.eolang.x ⟧"]+    withTempFileContent "pin-versionXXXXXX.txt" (showVersion version ++ "\n") $ \file ->+      withStdin "[[ ]]" $+        testCLIFailed+          ["--pin=" ++ showVersion version, "--pin-file=" ++ file, "rewrite"]+          ["[ERROR]: Invalid option `--pin-file=" ++ file ++ "'"]++  describe "--hide-rho" $+    forM_+      [+        ( "drops every rho binding from the default salty output"+        , "[[ foo -> [[ x -> [[ ]], ^ -> $.y ]], y -> [[ ]] ]]"+        , ["rewrite", "--flat", "--hide-rho"]+        , ["⟦ foo ↦ ⟦ x ↦ ⟦⟧ ⟧, y ↦ ⟦⟧ ⟧"]+        )+      ,+        ( "also drops the rho that --sweet leaves behind"+        , "[[ foo -> [[ x -> [[ ]], ^ -> $.y ]], y -> [[ ]] ]]"+        , ["rewrite", "--flat", "--sweet", "--hide-rho"]+        , ["⟦ foo ↦ ⟦⟧:x, y ↦ ⟦⟧ ⟧"]+        )+      ,+        ( "keeps sweet numeric literals intact"+        , "[[ a -> 42 ]]"+        , ["rewrite", "--flat", "--sweet", "--hide-rho"]+        , ["42.0:a"]+        )+      ,+        ( "keeps the one-binding sugar after inline voids"+        , "[[ x(y) -> [[ a -> 42, ^ -> ? ]] ]]"+        , ["rewrite", "--flat", "--sweet", "--hide-rho"]+        , ["⟦ x(y) ↦ 42.0:a ⟧"]+        )+      ,+        ( "prints a lone void as the term without the rho would be printed"+        , "⟦ a ↦ ⟦ x ↦ ⟦ ρ ↦ ξ, y ↦ ∅ ⟧ ⟧ ⟧"+        , ["rewrite", "--sweet", "--hide-rho"]+        , ["⟦ x(y) ↦ ⟦⟧ ⟧:a"]+        )+      ,+        ( "indents the body as the term without the rho would be indented"+        , "⟦ a ↦ ⟦ x ↦ ⟦ ρ ↦ ∅, b ↦ ⟦ c ↦ ∅, ρ ↦ ∅ ⟧ ⟧ ⟧ ⟧"+        , ["rewrite", "--sweet", "--hide-rho", "--margin=3"]+        , ["⟦\n  b(c) ↦ ⟦⟧\n⟧:x:a"]+        )+      ,+        ( "prints positional arguments as such once the rho is gone"+        , "⟦ a ↦ ξ.b(ρ ↦ ξ, α0 ↦ ξ.c, α1 ↦ ξ.d) ⟧"+        , ["rewrite", "--flat", "--sweet", "--hide-rho"]+        , ["b( c, d ):a"]+        )+      ,+        ( "drops the rho from the LaTeX output too"+        , "[[ n -> [[ ^ -> ?, k -> $ ]] ]]"+        , ["rewrite", "--flat", "--hide-rho", "--output=latex"]+        , ["[[ |n| -> [[ |k| -> \\phiTerminal{\\xi} ]] ]]"]+        )+      ,+        ( "drops the rho argument of a number literal from the LaTeX output"+        , "42(ρ ↦ ξ)"+        , ["rewrite", "--flat", "--hide-rho", "--output=latex"]+        , ["Q . |number| ( @ -> Q . |bytes| ( @ -> [[ D> |40-45-00-00-00-00-00-00| ]] ) ){.}"]+        )+      ,+        ( "drops only the rho argument of a string literal from the LaTeX output"+        , "\"s\"(ρ ↦ ⊥, b ↦ Φ)"+        , ["rewrite", "--flat", "--hide-rho", "--output=latex"]+        , ["Q . |string| ( @ -> Q . |bytes| ( @ -> [[ D> |73-| ]] ) ) ( |b| -> Q ){.}"]+        )+      ,+        ( "drops the rho argument of a non-finite literal from the LaTeX output"+        , "Φ.nan(ρ ↦ ξ)"+        , ["rewrite", "--flat", "--hide-rho", "--output=latex"]+        , ["Q . |number| ( @ -> Q . |bytes| ( @ -> [[ D> |7F-F8-00-00-00-00-00-00| ]] ) ){.}"]+        )+      ]+      (\(desc, input, args, expected) -> it desc (withStdin input (testCLISucceeded args expected)))++  it "keeps a data literal sugared when it is applied to more arguments" $+    withStdin "⟦ i ↦ 42(z ↦ ξ.f), s ↦ \"Hello\"(z ↦ ξ.f) ⟧" $+      testCLISucceeded ["rewrite", "--sweet", "--flat"] ["⟦ i ↦ 42.0( z ↦ f ), s ↦ \"Hello\"( z ↦ f ) ⟧"]++  it "prints the one-binding sugar after inline voids with --sweet" $+    withStdin "[[ x(y) -> [[ a -> 42 ]] ]]" $+      testCLISucceeded ["rewrite", "--sweet"] ["⟦ x(y) ↦ 42.0:a ⟧"]++  it "prints debug info with --log-level=DEBUG" $+    withStdin "[[]]" $+      testCLISucceeded ["rewrite", "--log-level=DEBUG"] ["[DEBUG]:"]++  describe "--log-level accepts every named level" $+    forM_+      ["INFO", "info", "ERROR", "ERR", "error", "NONE", "none"]+      ( \flagValue ->+          it ("--log-level=" ++ flagValue) $+            withStdin "[[]]" $+              testCLISucceeded ["rewrite", "--log-level=" ++ flagValue] ["⟧"]+      )++  describe "--log-level prints nothing below its level" $+    forM_+      [("NONE", ["[DEBUG]", "[INFO]"]), ("ERROR", ["[DEBUG]", "[INFO]"]), ("INFO", ["[DEBUG]"])]+      ( \(level, hidden) ->+          it ("--log-level=" ++ level) $+            withStdin "[[]]" $ do+              (out, _) <- withStdout (try (runCLI ["rewrite", "--log-level=" ++ level]) :: IO (Either ExitCode ()))+              forM_ hidden (out `shouldNotContain`)+      )++  it "fails on an unrecognized --log-level value" $+    withStdin "[[]]" $+      testCLIFailed ["rewrite", "--log-level=verbose"] ["unknown log-level: verbose"]++  describe "rewriting" $ do+    let xmir =+          unlines+            [ "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"+            , "<object>"+            , "  <o name=\"bar\">"+            , "    <o base=\"∅\" name=\"x\"/>"+            , "  </o>"+            , "</object>"+            ]+    describe "fails" $ do+      forM_+        [ ("with --input=latex", "", ["rewrite", "--input=latex"], ["The value 'latex' can't be used for '--input' option"])+        , ("with negative --log-lines", "", ["rewrite", "--log-lines=-2"], ["--log-lines must be >= -1"])+        , ("with negative --max-depth", "", ["rewrite", "--max-depth=-1"], ["--max-depth must be positive"])+        , ("with zero --max-cycles", "", ["rewrite", "--max-cycles=0"], ["--max-cycles must be positive"])+        , ("with zero --meet-length", "", ["rewrite", "--output=latex", "--meet-length=0"], ["--meet-length must be positive"])+        , ("with a --max-cycles value above maxBound Int", "", ["rewrite", "--max-cycles=18446744073709551617"], ["value must be between", "9223372036854775807"])+        ,+          ( "with --normalize and --must=1"+          , "[[ x -> [[ y -> 5 ]].y ]].x"+          , ["rewrite", "--max-cycles=2", "--max-depth=1", "--normalize", "--must=1"]+          , ["it's expected rewriting cycles to be in range [1], but rewriting has already reached 2"]+          )+        , ("when --in-place is used without input file", "[[ ]]", ["rewrite", "--in-place"], ["--in-place requires an input file"])+        ]+        (\(desc, input, args, expected) -> it desc (withStdin input (testCLIFailed args expected)))++      it "when --in-place is used with --target" $+        withTempFile "inplaceXXXXXX.phi" $ \(path, h) -> do+          hPutStr h "[[ ]]"+          hClose h+          testCLIFailed+            ["rewrite", "--in-place", "--target=output.phi", path]+            ["--in-place and --target cannot be used together"]++      it "fails when --in-place is used with a non-phi output format" $+        withTempFile "inplaceXXXXXX.phi" $ \(path, h) -> do+          hPutStr h "[[ ]]"+          hClose h+          testCLIFailed+            ["rewrite", "--in-place", "--output=latex", path]+            ["The option --in-place requires the output format to match the input format"]++      it "requires an XMIR input to be written back as XMIR" $+        withTempFileContent "inplaceXXXXXX.xmir" xmir $ \path ->+          testCLIFailed+            ["rewrite", "--input=xmir", "--in-place", path]+            ["The option --in-place requires the output format to match the input format"]++      it "does not leak a HasCallStack backtrace into errors" $ do+        (out, _) <- withStdout (try (runCLI ["rewrite", "--in-place"]) :: IO (Either ExitCode ()))+        out `shouldNotContain` "HasCallStack backtrace"+        out `shouldNotContain` "ExitFailure 1"+        out `shouldContain` "[ERROR]:"++      it "prints optparse errors once, without a backtrace" $ do+        (out, _) <- withStdout (try (runCLI ["rewrite", "--badopt"]) :: IO (Either ExitCode ()))+        out `shouldNotContain` "HasCallStack backtrace"+        out `shouldNotContain` "ExitFailure 1"+        out `shouldContain` "[ERROR]:"++      forM_+        [ ("when --update is used without --target", "[[ ]]", ["rewrite", "--update"], ["--update requires --target"])+        ,+          ( "when --update is used without an input file"+          , "[[ ]]"+          , ["rewrite", "--update", "--target=output.phi"]+          , ["--update requires an input file"]+          )+        ,+          ( "when --sequence is used with --in-place"+          , "[[ ]]"+          , ["rewrite", "--sequence", "--in-place", "input.phi"]+          , ["--in-place and --sequence cannot be used together"]+          )+        ,+          ( "when --focus is used with --in-place"+          , "[[ ]]"+          , ["rewrite", "--focus=Q.y", "--in-place", "input.phi"]+          , ["--in-place and --focus cannot be used together"]+          )+        ,+          ( "when --show is used with --in-place"+          , "[[ ]]"+          , ["rewrite", "--show=Q.y", "--in-place", "input.phi"]+          , ["--in-place and --show cannot be used together"]+          )+        ,+          ( "when --update is used with --in-place"+          , "[[ ]]"+          , ["rewrite", "--update", "--in-place", "input.phi"]+          , ["--update and --in-place cannot be used together"]+          )+        ,+          ( "when --hide is used with --in-place"+          , "[[ ]]"+          , ["rewrite", "--hide=Q.y", "--in-place", "input.phi"]+          , ["--in-place and --hide cannot be used together"]+          )+        ,+          ( "when --hide-rho is used with --in-place"+          , "[[ ]]"+          , ["rewrite", "--hide-rho", "--in-place", "input.phi"]+          , ["--in-place and --hide-rho cannot be used together"]+          )+        ,+          ( "with --depth-sensitive"+          , "[[ x -> \"x\"]]"+          , ["rewrite", "--depth-sensitive", "--max-depth=1", "--max-cycles=1", rule "infinite.yaml"]+          , ["[ERROR]: With option --depth-sensitive it's expected rewriting iterations amount does not reach the limit: --max-depth=1"]+          )+        ,+          ( "with looping rules"+          , "[[ x -> \"0\" ]]"+          , ["rewrite", rule "first.yaml", rule "second.yaml", "--max-depth=1", "--max-cycles=3"]+          , ["it seems rewriting is looping"]+          )+        ]+        (\(desc, input, args, expected) -> it desc (withStdin input (testCLIFailed args expected)))++      it "with wrong attribute and valid error message" $+        testCLIFailed+          ["rewrite", resource "with-$this-attribute.phi"]+          [ "[ERROR]: Couldn't parse given phi expression, cause:"+          , "unexpected"+          ]++      forM_+        [+          ( "with --output != latex and --nonumber"+          , ["rewrite", "--nonumber", "--output=xmir"]+          , ["The --nonumber option can stay together with --output=latex only"]+          )+        , ("with --omit-listing and --output != xmir", ["rewrite", "--omit-listing", "--output=phi"], ["--omit-listing"])+        , ("with --omit-comments and --output != xmir", ["rewrite", "--omit-comments", "--output=phi"], ["--omit-comments"])+        ,+          ( "with --expression and --output != latex"+          , ["rewrite", "--expression=foo", "--output=phi"]+          , ["--expression option can stay together with --output=latex only"]+          )+        ,+          ( "with --label and --output != latex"+          , ["rewrite", "--label=foo", "--output=phi"]+          , ["--label option can stay together with --output=latex only"]+          )+        ,+          ( "with an unsafe --label and --output=latex"+          , ["rewrite", "--label=eq:bad%tag", "--output=latex"]+          , ["The --label option must contain only letters, numbers, colons, periods, underscores, and hyphens"]+          )+        ,+          ( "with --compress and --output != latex"+          , ["rewrite", "--compress", "--output=phi"]+          , ["--compress option can stay together with --output=latex only"]+          )+        ,+          ( "with --meet-prefix and --output != latex"+          , ["rewrite", "--meet-prefix=foo", "--output=phi"]+          , ["--meet-prefix option can stay together with --output=latex only"]+          )+        ,+          ( "with wrong --hide option"+          , ["rewrite", "--hide=Q.x(Q.y)"]+          , ["[ERROR]: Invalid set of arguments: Only dispatch expression", "but given: Φ.x( Φ.y )"]+          )+        , ("with many --show options", ["rewrite", "--show=Q.x.y", "--show=hello"], ["The option --show can be used only once"])+        ,+          ( "with wrong --show option"+          , ["rewrite", "--show=Q.x(Q.y)"]+          , ["[ERROR]:", "Only dispatch expression started with Φ (or Q) can be used in --show"]+          )+        , ("with --show overlapping --hide", ["rewrite", "--show=Q.x", "--hide=Q.x"], ["[ERROR]:", "The --show locator 'Φ.x' is also listed in --hide"])+        , ("with --hide of an ancestor of --show", ["rewrite", "--show=Q.y.z", "--hide=Q.y"], ["[ERROR]:", "The --show locator 'Φ.y.z' lies inside the --hide locator 'Φ.y'"])+        , ("with --meet-popularity < 0", ["rewrite", "--meet-popularity=-1"], ["[ERROR]:", "--meet-popularity must be positive"])+        , ("with --meet-popularity > 100", ["rewrite", "--meet-popularity=102"], ["[ERROR]:", "--meet-popularity must be <= 100"])+        ,+          ( "with --meet-popularity and output != latex"+          , ["rewrite", "--meet-popularity=51", "--output=phi"]+          , ["[ERROR]:", "--meet-popularity option can stay together with --output=latex only"]+          )+        ,+          ( "with --meet-length and output != latex"+          , ["rewrite", "--meet-length=4", "--output=phi"]+          , ["[ERROR]:", "--meet-length option can stay together with --output=latex only"]+          )+        ,+          ( "with non-dispatch --focus"+          , ["rewrite", "--focus=Q.x(Q.y)"]+          , ["Invalid set of arguments: Only dispatch expression started with Φ (or Q) can be used in --focus, but given: Φ.x( Φ.y )"]+          )+        , ("with --focus!=Q and --output=XMIR", ["rewrite", "--focus=Q.x", "--output=xmir"], ["Only --focus=Q is allowed to be used with --output=xmir"])+        , ("with --margin < 0", ["rewrite", "--margin=-1"], ["--margin must be positive"])+        ,+          ( "with --breakpoint which does not exist across the rules"+          , ["rewrite", "--breakpoint=hello", "--normalize"]+          , ["The rule 'hello' provided in '--breakpoint' option is absent across given rewriting rules: alpha"]+          )+        ]+        (\(desc, args, expected) -> it desc (withStdin "[[ x -> 5 ]]" (testCLIFailed args expected)))++    it "prints help" $+      testCLISucceeded+        ["rewrite", "--help"]+        ["Rewrite the 𝜑-expression", "--seed SEED"]++    it "accepts --seed flag" $+      withStdin "[[ x -> 5 ]]" $+        testCLISucceeded+          ["rewrite", "--seed=42", "--sweet"]+          ["5.0:x"]++    it "defaults --seed to 0 in help" $+      testCLISucceeded+        ["rewrite", "--help"]+        ["default: 0"]++    it "describes --must as counting rewriting cycles, not rules applied" $+      testCLISucceeded+        ["rewrite", "--help"]+        ["Stops execution if number of rewriting cycles is not"]++    it "reproduces the same shuffle order for the same --seed" $ do+      let args =+            [ "rewrite"+            , "--shuffle"+            , "--seed=42"+            , "--sweet"+            , "--sequence"+            , "--max-depth=1"+            , "--max-cycles=1"+            , rule "swap-a.yaml"+            , rule "swap-b.yaml"+            ]+      (firstRun, _) <- withStdin "[[ x -> 5 ]]" $ withStdout (runCLI args)+      (secondRun, _) <- withStdin "[[ x -> 5 ]]" $ withStdout (runCLI args)+      firstRun `shouldBe` secondRun++    it "shuffles the rules away from their file order" $ do+      let base =+            [ "--sweet"+            , "--sequence"+            , "--max-depth=1"+            , "--max-cycles=1"+            , rule "swap-a.yaml"+            , rule "swap-b.yaml"+            ]+      (plain, _) <- withStdin "[[ x -> 5 ]]" $ withStdout (runCLI ("rewrite" : base))+      (mixed, _) <- withStdin "[[ x -> 5 ]]" $ withStdout (runCLI ("rewrite" : "--shuffle" : "--seed=1" : base))+      mixed `shouldNotBe` plain++    it "fails with a non-integer --seed" $+      withStdin "[[ ]]" $+        testCLIFailed+          ["rewrite", "--seed=abc"]+          ["[ERROR]"]++    it "refuses a --seed outside the Int range" $+      withStdin "[[ ]]" $+        testCLIFailed+          ["rewrite", "--seed=18446744073709551616"]+          ["value must be between"]++    it "refuses a non-positive --abridged width" $+      withStdin "[[ t -> 01- ]]" $+        testCLIFailed+          ["dataize", "--locator=Q.t", "--protocol=p.txt", "--abridged=0"]+          ["--abridged must be positive"]++    it "saves steps to dir with --steps-dir" $+      withTempDirectory "phino-steps" $ \dir ->+        withStdin "[[ x -> \"hello\"]]" $ do+          testCLISucceeded+            ["rewrite", rule "infinite.yaml", "--max-cycles=2", "--max-depth=2", "--steps-dir=" ++ dir, "--sweet"]+            ["hello_hi_hi"]+          doesDirectoryExist dir `shouldReturn` True+          files <- listDirectory dir+          length files `shouldBe` 4+          doesFileExist (dir ++ "/00001.phi") `shouldReturn` True+          doesFileExist (dir ++ "/00003.phi") `shouldReturn` True++    it "clears step files left from an earlier, longer run with --steps-dir" $+      withTempDirectory "phino-steps-stale" $ \dir -> do+        withStdin "[[ x -> \"hello\"]]" $+          testCLISucceeded+            ["rewrite", rule "infinite.yaml", "--max-cycles=2", "--max-depth=2", "--steps-dir=" ++ dir, "--sweet"]+            ["hello_hi_hi"]+        files <- listDirectory dir+        length files `shouldBe` 4+        withStdin "[[ x -> \"hello\"]]" $+          testCLISucceeded+            ["rewrite", rule "infinite.yaml", "--max-cycles=1", "--max-depth=2", "--steps-dir=" ++ dir, "--sweet"]+            ["hello_hi"]+        files2 <- listDirectory dir+        sort files2 `shouldBe` ["00001.phi", "00002.phi"]++    it "gives the saved steps the --canonize and --hide of the printed ones" $+      withTempDirectory "phino-steps-filtered" $ \dir ->+        withStdin "[[ m -> [[ x -> [[ L> Plus ]], y -> $.x ]].y, k -> [[ L> Minus ]] ]]" $ do+          testCLISucceeded+            ["rewrite", "--normalize", "--hide=Q.k", "--canonize", "--steps-dir=" ++ dir, "--flat"]+            ["Fn1"]+          files <- listDirectory dir+          null files `shouldBe` False+          saved <- mapM (\file -> readFile (dir ++ "/" ++ file)) files+          concat saved `shouldNotContain` "Minus"+          concat saved `shouldNotContain` "Plus"++    it "gives the saved steps the --focus of the printed ones" $+      withTempDirectory "phino-steps-focused" $ \dir ->+        withStdin "⟦ x ↦ ⟦ k ↦ ⟦ q ↦ ⟦⟧ ⟧ ⟧.k, y ↦ ⟦ z ↦ ⟦⟧ ⟧ ⟧" $ do+          testCLISucceeded+            ["rewrite", "--normalize", "--sweet", "--flat", "--focus=Q.y", "--sequence", "--steps-dir=" ++ dir]+            ["⟦⟧:z"]+          files <- listDirectory dir+          null files `shouldBe` False+          saved <- mapM (\file -> readFile (dir ++ "/" ++ file)) (sort files)+          map (filter (/= '\n')) saved `shouldSatisfy` all (== "⟦⟧:z")++    it "saves dataize steps to dir with --steps-dir" $+      withTempDirectory "phino-steps-dataize" $ \dir ->+        withStdin "[[ bytes ↦ ⟦ φ ↦ ∅ ⟧, number(φ) -> [[ plus(^, x) -> [[ L> L_number_plus ]] ]], @ -> 5.plus(6).plus(7) ]]" $ do+          testCLISucceeded+            ["dataize", symbolic, "--steps-dir=" ++ dir, "--sweet"]+            ["40-45"]+          doesDirectoryExist dir `shouldReturn` True+          files <- listDirectory dir+          let steps = sort files+          steps `shouldBe` map (\n -> printf "%05d.phi" (n :: Int)) [1 .. length steps]+          length steps `shouldSatisfy` (> 18)++    it "saves steps with a .tex extension when --output=latex is used with --steps-dir" $+      withTempDirectory "phino-steps-latex" $ \dir ->+        withStdin "[[ x -> \"hello\"]]" $ do+          testCLISucceeded+            ["rewrite", rule "infinite.yaml", "--max-cycles=2", "--max-depth=2", "--steps-dir=" ++ dir, "--output=latex", "--sweet"]+            ["\\begin{phiquation}"]+          doesDirectoryExist dir `shouldReturn` True+          files <- listDirectory dir+          length files `shouldBe` 4+          doesFileExist (dir ++ "/00001.tex") `shouldReturn` True+          doesFileExist (dir ++ "/00003.tex") `shouldReturn` True++    it "desugares without any rules flag from file" $+      testCLISucceeded+        ["rewrite", resource "desugar.phi"]+        ["⟦ foo ↦ ξ.x ⟧"]++    it "desugares with without any rules flag from stdin" $+      withStdin "[[foo ↦ x]]" $+        testCLISucceeded ["rewrite"] ["⟦ foo ↦ ξ.x ⟧"]++    it "keeps the bytes of a string intact while desugaring it" $+      withStdin "⟦ φ ↦ Φ.string(as-bytes ↦ Φ.bytes(data ↦ ⟦ Δ ⤍ 65-0A-65, ρ ↦ ∅ ⟧)), ρ ↦ ∅ ⟧" $+        testCLISucceeded ["rewrite", "--flat"] ["Δ ⤍ 65-0A-65"]++    it "rewrites with single rule" $+      withStdin "T(x -> Q.y)" $+        testCLISucceeded ["rewrite", "--rule=resources/normalize/dc.yaml"] ["⊥"]++    it "fails when a rewriting rule uses a dataization-only function" $+      withStdin "⟦⟧" $+        testCLIFailed+          ["rewrite", rule "evaluate-in-rewrite.yaml"]+          ["Function 'evaluate' in rule 'uses-evaluate' is available only for dataization and morphing, not for rewriting"]++    it "names the join function in the error message" $+      withStdin "⟦⟧" $+        testCLIFailed+          ["rewrite", rule "join-broken.yaml"]+          ["Function join() can work with bindings only"]++    it "reports a broken rule file in plain text with its path" $+      withStdin "⟦⟧" $+        testCLIFailed+          ["rewrite", rule "malformed-pattern.yaml"]+          [ "Could not load rule from 'test-resources/cli/rules/malformed-pattern.yaml'"+          , "unexpected '⟧'"+          ]++    it "counts the position of a YAML error from one" $+      withTempFileContent "badruleXXXXXX.yaml" "name: a\npattern: x: y\n" $ \path ->+        withStdin "⟦⟧" $+          testCLIFailed+            ["rewrite", "--rule=" ++ path]+            ["YAML parse exception at line 2, column 11"]++    it "normalizes with --normalize flag" $+      testCLISucceeded+        ["rewrite", "--normalize", resource "normalize.phi", "--margin=25"]+        [ unlines+            [ "⟦"+            , "  x ↦ ⟦"+            , "    ρ ↦ ⟦ y ↦ ⟦ ρ ↦ ∅ ⟧ ⟧"+            , "  ⟧"+            , "⟧"+            ]+        ]++    it "normalizes and applies --rule at the same time" $+      withStdin "⟦ k ↦ ⟦ m ↦ ⟦ Δ ⤍ 01- ⟧ ⟧.m, j ↦ ⟦ λ ⤍ Marker ⟧ ⟧" $+        testCLISucceeded+          ["rewrite", "--normalize", rule "marker.yaml", "--sweet"]+          ["⟦ k ↦ 01-:Δ, j ↦ FF-:Δ ⟧"]++    it "normalizes from stdin" $+      withStdin "⟦ a ↦ ⟦ b ↦ ∅ ⟧ (b ↦ [[ ]]) ⟧" $+        testCLISucceeded+          ["rewrite", "--normalize", "--margin=20"]+          ["⟦ a ↦ ⟦ b ↦ ⟦⟧ ⟧ ⟧"]++    it "rewrites with --sweet flag" $+      withStdin "[[ x -> 5]]" $+        testCLISucceeded+          ["rewrite", "--sweet"]+          ["5.0:x"]++    it "rewrites as XMIR" $+      withStdin "[[ x -> Q.y ]]" $+        testCLISucceeded+          ["rewrite", "--output=xmir"]+          ["<?xml version=\"1.0\" encoding=\"UTF-8\"?>", "<object", "  <o base=\"Φ.y\" name=\"x\"/>"]++    it "accepts --focus=Φ together with --output=xmir, since Φ and Q are the same locator" $+      withStdin "[[ x -> Q.y ]]" $+        testCLISucceeded+          ["rewrite", "--focus=Φ", "--output=xmir"]+          ["<?xml version=\"1.0\" encoding=\"UTF-8\"?>", "<object", "  <o base=\"Φ.y\" name=\"x\"/>"]++    it "rewrites a formation of several bindings at the root as XMIR" $+      withStdin "⟦ x ↦ 1, ρ ↦ 2 ⟧" $+        testCLISucceeded+          ["rewrite", "--output=xmir"]+          ["<o base=\"Φ.number\" name=\"x\">", "<o base=\"Φ.number\" name=\"ρ\">"]++    it "emits a real revision and ms in XMIR" $ do+      (output, _) <- withStdin "[[ x -> Q.y ]]" $ withStdout (runCLI ["rewrite", "--output=xmir"])+      let attrValue :: String -> String -> String+          attrValue name text =+            let needle = name ++ "=\""+                breakOn :: String -> Maybe String+                breakOn haystack+                  | needle `isPrefixOf` haystack = Just (drop (length needle) haystack)+                  | null haystack = Nothing+                  | otherwise = breakOn (drop 1 haystack)+             in case breakOn text of+                  Just afterNeedle -> takeWhile (/= '"') afterNeedle+                  Nothing -> ""+          revision = attrValue "revision" output+          ms = attrValue "ms" output+      revision `shouldSatisfy` (\sha -> length sha == 7 && all (`elem` "0123456789abcdef") sha)+      revision `shouldNotBe` "1234567"+      ms `shouldSatisfy` (all isDigit)++    it "rewrites as LaTeX" $+      withStdin "[[ x_o -> Q.z(y -> 5), q$ -> T, w -> $, ^ -> Q, @ -> 1, y -> \"H$@^M\", L> Fu_nc ]]" $+        testCLISucceeded+          ["rewrite", "--output=latex", "--sweet"]+          [ unlines+              [ "\\begin{phiquation}"+              , "[["+              , "  |x\\char95{}o| -> Q . |z| ( |y| -> |5.0| ),"+              , "  |q\\char36{}| -> T,"+              , "  |w| -> \\phiTerminal{\\xi},"+              , "  \\phiTerminal{\\rho} -> Q,"+              , "  @ -> |1.0|,"+              , "  |y| -> \"H\\char36{}\\char64{}\\char94{}M\","+              , "  L> |Fu\\char95{}nc|"+              , "]]{.}"+              , "\\end{phiquation}"+              ]+          ]++    it "rewrites as LaTeX without numeration" $+      withStdin "[[ x -> 5 ]]" $+        testCLISucceeded+          ["rewrite", "--output=latex", "--sweet", "--nonumber", "--flat"]+          [ unlines+              [ "\\begin{phiquation*}"+              , "|5.0| : |x|{.}"+              , "\\end{phiquation*}"+              ]+          ]++    it "rewrites an alpha-index argument as \\alpha subscript in LaTeX" $+      withStdin "Q.foo(~1 -> Q.y)" $+        testCLISucceeded+          ["rewrite", "--output=latex", "--flat", "--nonumber"]+          [ unlines+              [ "\\begin{phiquation*}"+              , "Q . |foo| ( \\phiTerminal{\\alpha_{1}} -> Q . |y| ){.}"+              , "\\end{phiquation*}"+              ]+          ]++    it "rewrite as LaTeX with expression name" $+      withStdin "[[ x -> 5 ]]" $+        testCLISucceeded+          ["rewrite", "--output=latex", "--sweet", "--flat", "--expression=foo"]+          [ unlines+              [ "\\begin{phiquation}"+              , "\\phiExpression{foo} |5.0| : |x|{.}"+              , "\\end{phiquation}"+              ]+          ]++    it "escapes special characters in the LaTeX expression name" $+      withStdin "[[ x -> 5 ]]" $+        testCLISucceeded+          ["rewrite", "--output=latex", "--sweet", "--flat", "--expression=bad%tag"]+          [ unlines+              [ "\\begin{phiquation}"+              , "\\phiExpression{bad\\char37{}tag} |5.0| : |x|{.}"+              , "\\end{phiquation}"+              ]+          ]++    it "rewrite as LaTeX with label name" $+      withStdin "[[ x -> 5 ]]" $+        testCLISucceeded+          ["rewrite", "--output=latex", "--sweet", "--flat", "--label=foo"]+          [ unlines+              [ "\\begin{phiquation}\n\\label{foo}"+              , "|5.0| : |x|{.}"+              , "\\end{phiquation}"+              ]+          ]++    it "rewrites with XMIR as input" $+      withStdin "<object><o name=\"app\"><o name=\"x\" base=\"Φ.number\"/></o></object>" $+        testCLISucceeded+          ["rewrite", "--input=xmir", "--sweet"]+          ["Φ.number:x:app"]++    it "rewrites and prints with XMIR as input and output" $+      withStdin+        ( intercalate+            ""+            [ "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"+            , "<object><o name=\"app\"><o name=\"x\" base=\"Φ.number\"/></o></object>"+            ]+        )+        ( testCLISucceeded+            ["rewrite", "--input=xmir", "--output=xmir", "--sweet", "--flat"]+            [ "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"+            , "<listing>Φ.number:x:app</listing>"+            ]+        )++    it "rewrites as XMIR with omit-listing flag" $+      withStdin "[[ x -> Q.y ]]" $+        testCLISucceeded+          ["rewrite", "--output=xmir", "--omit-listing"]+          ["<?xml version=\"1.0\" encoding=\"UTF-8\"?>", "<object", "<listing>1 line(s)</listing>", "  <o base=\"Φ.y\" name=\"x\"/>"]++    it "does not fail on exactly 1 rewriting" $+      withStdin "⟦ t ↦ ⟦ x ↦ \"foo\" ⟧ ⟧" $+        testCLISucceeded+          ["rewrite", rule "simple.yaml", "--must=1", "--sweet"]+          ["\"bar\":x"]++    it "prints many expressions with --sequence" $+      withStdin "[[ x -> \"foo\" ]]" $+        testCLISucceeded+          [ "rewrite"+          , rule "first.yaml"+          , rule "second.yaml"+          , "--max-depth=1"+          , "--max-cycles=2"+          , "--sequence"+          , "--sweet"+          , "--flat"+          ]+          [ unlines+              [ "\"foo\":x"+              , "Φ.x( y ↦ \"foo\" )"+              , "\"foo\":x"+              ]+          ]++    it "prefixes every step with a header when --headers is on" $+      withStdin "[[ x -> \"foo\" ]]" $+        testCLISucceeded+          [ "rewrite"+          , rule "first.yaml"+          , rule "second.yaml"+          , "--max-depth=1"+          , "--max-cycles=2"+          , "--sequence"+          , "--headers"+          , "--sweet"+          , "--flat"+          ]+          [ intercalate+              "\n"+              [ ""+              , "=== Step #1"+              , "\"foo\":x"+              , ""+              , "=== Step #2, Rule 'first', 23t -> 26t"+              , "Φ.x( y ↦ \"foo\" )"+              , ""+              , "=== Step #3, Rule 'second', 26t -> 23t"+              , "\"foo\":x"+              ]+          ]++    it "ignores --headers without --sequence" $+      withStdin "[[ x -> \"foo\" ]]" $+        testCLISucceeded+          ["rewrite", rule "simple.yaml", "--headers", "--sweet", "--flat"]+          ["\"bar\":x"]++    it "emits step headers as LaTeX comments with --headers" $+      withStdin "[[ x -> \"foo\" ]]" $+        testCLISucceeded+          [ "rewrite"+          , rule "first.yaml"+          , rule "second.yaml"+          , "--max-depth=1"+          , "--max-cycles=2"+          , "--sequence"+          , "--headers"+          , "--sweet"+          , "--flat"+          , "--output=latex"+          ]+          [ unlines+              [ "\\begin{phiquation}"+              , "% === Step #1"+              , "\"foo\" : |x| \\phiNormalize[\\nameref{r:first}]"+              , "% === Step #2, Rule 'first', 23t -> 26t"+              , "  \\phiNormalize Q . |x| ( |y| -> \"foo\" ) \\phiNormalize[\\nameref{r:second}]"+              , "% === Step #3, Rule 'second', 26t -> 23t"+              , "  \\phiNormalize \"foo\" : |x|{.}"+              , "\\end{phiquation}"+              ]+          ]++    it "prints only one latex preamble with --sequence" $+      withStdin "[[ x -> \"foo\" ]]" $+        testCLISucceeded+          [ "rewrite"+          , rule "first.yaml"+          , rule "second.yaml"+          , "--max-depth=1"+          , "--max-cycles=2"+          , "--sequence"+          , "--sweet"+          , "--flat"+          , "--output=latex"+          ]+          [ unlines+              [ "\\begin{phiquation}"+              , "\"foo\" : |x| \\phiNormalize[\\nameref{r:first}]"+              , "  \\phiNormalize Q . |x| ( |y| -> \"foo\" ) \\phiNormalize[\\nameref{r:second}]"+              , "  \\phiNormalize \"foo\" : |x|{.}"+              , "\\end{phiquation}"+              ]+          ]++    it "prints meet prefix with --meet-prefix=foo in LaTeX" $+      withStdin "[[ x -> ?, y -> $.x ]](x -> [[ D> 42- ]]).y" $+        testCLISucceeded+          ["rewrite", "--normalize", "--sweet", "--sequence", "--output=latex", "--flat", "--compress", "--meet-prefix=foo"]+          [ unlines+              [ "\\begin{phiquation}"+              , "[[ |x| -> ?, |y| -> |x| ]] ( |x| -> |42-| : D ) . |y| \\phiNormalize[\\nameref{r:copy}]"+              , "  \\phiNormalize \\phinoMeet{foo:1}{ [[ |x| -> |42-| : D, |y| -> |x| ]] } . |y| \\phiNormalize[\\nameref{r:dot}]"+              , "  \\phiNormalize |42-| : D : |x| . |x| ( \\phiTerminal{\\rho} -> \\phinoAgain{foo:1} ) \\phiNormalize[\\nameref{r:dot}]"+              , "  \\phiNormalize |42-| : D ( \\phiTerminal{\\rho} -> |42-| : D : |x|, \\phiTerminal{\\rho} -> \\phinoAgain{foo:1} ) \\phiNormalize[\\nameref{r:skip}]"+              , "  \\phiNormalize |42-| : D ( \\phiTerminal{\\rho} -> \\phinoAgain{foo:1} ) \\phiNormalize[\\nameref{r:skip}]"+              , "  \\phiNormalize |42-| : D{.}"+              , "\\end{phiquation}"+              ]+          ]++    it "prints with compressed expressions in LaTeX" $+      withStdin "[[ x -> ?, y -> $.x ]](x -> [[ D> 42- ]]).y" $+        testCLISucceeded+          ["rewrite", "--normalize", "--sweet", "--sequence", "--output=latex", "--flat", "--compress"]+          [ unlines+              [ "\\begin{phiquation}"+              , "[[ |x| -> ?, |y| -> |x| ]] ( |x| -> |42-| : D ) . |y| \\phiNormalize[\\nameref{r:copy}]"+              , "  \\phiNormalize \\phinoMeet{1}{ [[ |x| -> |42-| : D, |y| -> |x| ]] } . |y| \\phiNormalize[\\nameref{r:dot}]"+              , "  \\phiNormalize |42-| : D : |x| . |x| ( \\phiTerminal{\\rho} -> \\phinoAgain{1} ) \\phiNormalize[\\nameref{r:dot}]"+              , "  \\phiNormalize |42-| : D ( \\phiTerminal{\\rho} -> |42-| : D : |x|, \\phiTerminal{\\rho} -> \\phinoAgain{1} ) \\phiNormalize[\\nameref{r:skip}]"+              , "  \\phiNormalize |42-| : D ( \\phiTerminal{\\rho} -> \\phinoAgain{1} ) \\phiNormalize[\\nameref{r:skip}]"+              , "  \\phiNormalize |42-| : D{.}"+              , "\\end{phiquation}"+              ]+          ]++    it "should not print \\phinoMeet{} twice" $+      withStdin "[[ ex -> [[ x -> [[ y -> ?, k -> [[ t -> 42]]  ]]( y -> [[ t -> 42 ]]) ]].i ]]" $+        testCLISucceeded+          ["rewrite", "--normalize", "--sequence", "--flat", "--compress", "--output=latex", "--sweet"]+          [ unlines+              [ "\\begin{phiquation}"+              , "[[ |y| -> ?, |k| -> \\phinoMeet{1}{ |42.0| : |t| } ]] ( |y| -> \\phinoAgain{1} ) : |x| . |i| : |ex| \\phiNormalize[\\nameref{r:copy}]"+              , "  \\phiNormalize [[ |y| -> \\phinoAgain{1}, |k| -> \\phinoAgain{1} ]] : |x| . |i| : |ex| \\phiNormalize[\\nameref{r:stop}]"+              , "  \\phiNormalize T : |ex|{.}"+              , "\\end{phiquation}"+              ]+          ]++    it "should not meet expression with high --meet-popularity" $+      withStdin "[[ ex -> [[ x -> [[ y -> ?, k -> [[ t -> 42]]  ]]( y -> [[ t -> 42 ]]) ]].i ]]" $+        testCLISucceeded+          ["rewrite", "--normalize", "--sequence", "--flat", "--compress", "--output=latex", "--sweet", "--meet-popularity=70"]+          [ unlines+              [ "\\begin{phiquation}"+              , "[[ |y| -> ?, |k| -> |42.0| : |t| ]] ( |y| -> |42.0| : |t| ) : |x| . |i| : |ex| \\phiNormalize[\\nameref{r:copy}]"+              , "  \\phiNormalize [[ |y| -> |42.0| : |t|, |k| -> |42.0| : |t| ]] : |x| . |i| : |ex| \\phiNormalize[\\nameref{r:stop}]"+              , "  \\phiNormalize T : |ex|{.}"+              , "\\end{phiquation}"+              ]+          ]++    it "meets with --meet-length=32" $+      withStdin "[[ ex -> [[ x -> [[ y -> ?, k -> [[ t -> 42]]  ]]( y -> [[ t -> 42 ]]) ]].i ]]" $+        testCLISucceeded+          ["rewrite", "--normalize", "--sequence", "--flat", "--compress", "--output=latex", "--sweet", "--meet-length=32"]+          [ unlines+              [ "\\begin{phiquation}"+              , "[[ |y| -> ?, |k| -> |42.0| : |t| ]] ( |y| -> |42.0| : |t| ) : |x| . |i| : |ex| \\phiNormalize[\\nameref{r:copy}]"+              , "  \\phiNormalize [[ |y| -> |42.0| : |t|, |k| -> |42.0| : |t| ]] : |x| . |i| : |ex| \\phiNormalize[\\nameref{r:stop}]"+              , "  \\phiNormalize T : |ex|{.}"+              , "\\end{phiquation}"+              ]+          ]++    it "focuses expression in latex with sequence" $+      withStdin "[[ ex -> [[ x -> [[ y -> ?, k -> [[ t -> 42]]  ]]( y -> [[ t -> 42 ]]) ]].i ]]" $+        testCLISucceeded+          ["rewrite", "--normalize", "--sequence", "--flat", "--output=latex", "--sweet", "--focus=Q.ex"]+          [ unlines+              [ "\\begin{phiquation}"+              , "[[ |y| -> ?, |k| -> |42.0| : |t| ]] ( |y| -> |42.0| : |t| ) : |x| . |i| \\phiNormalize[\\nameref{r:copy}]"+              , "  \\phiNormalize [[ |y| -> |42.0| : |t|, |k| -> |42.0| : |t| ]] : |x| . |i| \\phiNormalize[\\nameref{r:stop}]"+              , "  \\phiNormalize T{.}"+              , "\\end{phiquation}"+              ]+          ]++    it "focuses expression in latex without sequence" $+      withStdin "[[ ex -> [[ x -> [[ y -> ?, k -> [[ t -> 42]]  ]]( y -> [[ t -> 42 ]]) ]].i ]]" $+        testCLISucceeded+          ["rewrite", "--normalize", "--flat", "--output=latex", "--sweet", "--focus=Q.ex"]+          [ unlines+              [ "\\begin{phiquation}"+              , "T{.}"+              , "\\end{phiquation}"+              ]+          ]++    it "shows exceeding of limits in latex" $+      withStdin "[[ x -> $.y, y -> $.x ]].x" $+        testCLISucceeded+          ["rewrite", "--normalize", "--flat", "--sequence", "--output=latex", "--sweet", "--max-depth=1", "--max-cycles=1"]+          [ unlines+              [ "\\begin{phiquation}"+              , "[[ |x| -> |y|, |y| -> |x| ]] . |x| \\phiNormalize[\\nameref{r:dot}]"+              , "  \\phiNormalize |x| : |y| . |y| ( \\phiTerminal{\\rho} -> [[ |x| -> |y|, |y| -> |x| ]] ) \\phiNormalize"+              , "  \\phiNormalize \\dots"+              , "\\end{phiquation}"+              ]+          ]++    it "ends the last step with a period when --max-cycles lands on the normal form" $+      withStdin "⟦ a ↦ ⟦ x ↦ ∅ ⟧( x ↦ ⟦⟧ ).x ⟧" $+        testCLISucceeded+          ["rewrite", "--normalize", "--max-cycles=1", "--sequence", "--output=latex"]+          ["  \\phiNormalize [[ |a| -> [[]] ]]{.}"]++    it "focuses expression in phi without sequence" $+      withStdin "[[ ex -> [[ x -> [[ y -> ?, k -> [[ t -> 42]]  ]]( y -> [[ t -> 42 ]]) ]].i ]]" $+        testCLISucceeded+          ["rewrite", "--normalize", "--flat", "--output=phi", "--sweet", "--focus=Q.ex"]+          ["⊥"]++    it "focuses expression in phi with sequence" $+      withStdin "[[ ex -> [[ x -> [[ y -> ?, k -> [[ t -> 42]]  ]]( y -> [[ t -> 42 ]]) ]].i ]]" $+        testCLISucceeded+          ["rewrite", "--normalize", "--sequence", "--flat", "--output=phi", "--sweet", "--focus=Q.ex"]+          [ unlines+              [ "⟦ y ↦ ∅, k ↦ 42.0:t ⟧( y ↦ 42.0:t ):x.i"+              , "⟦ y ↦ 42.0:t, k ↦ 42.0:t ⟧:x.i"+              , "⊥"+              ]+          ]++    it "prints input as listing in XMIR" $+      withStdin "[[ app -> [[]] ]]" $+        testCLISucceeded+          ["rewrite", "--output=xmir", "--omit-comments", "--sweet", "--flat"]+          ["  <listing>[[ app -&gt; [[]] ]]</listing>"]++    it "print expression in listing in XMIRs with --sequence" $+      withStdin "[[ x -> \"foo\" ]]" $+        testCLISucceeded+          ["rewrite", "--output=xmir", "--omit-comments", "--sweet", "--flat", "--sequence", rule "simple.yaml"]+          ["  <listing>\"foo\":x</listing>", "  <listing>\"bar\":x</listing>"]++    describe "must range tests" $ do+      describe "fails" $ do+        it "when cycles exceed range ..1" $+          withStdin "[[ x -> [[ y -> 5 ]].y ]].x" $+            testCLIFailed+              ["rewrite", "--max-depth=1", "--max-cycles=2", "--normalize", "--must=..1"]+              ["it's expected rewriting cycles to be in range [..1], but rewriting has already reached 2"]++        it "when cycles below range 2.." $+          withStdin "⟦ t ↦ ⟦ x ↦ \"foo\" ⟧ ⟧" $+            testCLIFailed+              ["rewrite", rule "simple.yaml", "--must=2.."]+              ["it's expected rewriting cycles to be in range [2..], but rewriting stopped after 1"]++        it "with invalid range 5..3" $+          withStdin "[[ ]]" $+            testCLIFailed+              ["rewrite", "--must=5..3"]+              ["cannot parse value `5..3'"]++        it "with negative in range -1..5" $+          withStdin "[[ ]]" $+            testCLIFailed+              ["rewrite", "--must=-1..5"]+              ["cannot parse value `-1..5'"]++        it "with malformed range syntax" $+          withStdin "[[ ]]" $+            testCLIFailed+              ["rewrite", "--must=3...5"]+              ["cannot parse value `3...5'"]++      it "accepts range ..5 (0 to 5 cycles)" $+        withStdin "[[ ]]" $+          testCLISucceeded ["rewrite", "--must=..5", "--sweet"] ["⟦⟧"]++      it "accepts range 0..0 (exactly 0 cycles)" $+        withStdin "[[ ]]" $+          testCLISucceeded ["rewrite", "--must=0..0", "--sweet"] ["⟦⟧"]++      it "accepts range 1..1 (exactly 1 cycle)" $+        withStdin "⟦ t ↦ ⟦ x ↦ \"foo\" ⟧ ⟧" $+          testCLISucceeded+            ["rewrite", rule "simple.yaml", "--must=1..1", "--sweet"]+            ["\"bar\":x"]++      it "accepts range 1..3 when 1 cycle happens" $+        withStdin "⟦ t ↦ ⟦ x ↦ \"foo\" ⟧ ⟧" $+          testCLISucceeded+            ["rewrite", rule "simple.yaml", "--must=1..3", "--sweet"]+            ["\"bar\":x"]++      it "accepts range 0.. (0 or more)" $+        withStdin "[[ ]]" $+          testCLISucceeded ["rewrite", "--must=0..", "--sweet"] ["⟦⟧"]++    it "prints to target file" $+      withStdin "[[ ]]" $+        withTempFile "targetXXXXXX.tmp" $ \(path, h) -> do+          hClose h+          testCLISucceeded ["rewrite", "--sweet", printf "--target=%s" path] []+          content <- readFile path+          content `shouldBe` "⟦⟧\n"++    it "modifies file in-place" $+      withTempFile "inplaceXXXXXX.phi" $ \(path, h) -> do+        hPutStr h "[[ x -> \"foo\" ]]"+        hClose h+        testCLISucceeded ["rewrite", rule "simple.yaml", "--in-place", "--sweet", path] []+        content <- readFile path+        content `shouldBe` "\"bar\":x\n"++    it "writes the same bytes to --target as it prints to the console" $+      withTempFile "targetXXXXXX.tmp" $ \(path, h) -> do+        hClose h+        (console, _) <- withStdout (withStdin "[[ ]]" (runCLI ["rewrite", "--sweet"]))+        withStdin "[[ ]]" (testCLISucceeded ["rewrite", "--sweet", printf "--target=%s" path] [])+        content <- readFile path+        content `shouldBe` console++    it "keeps the trailing newline of the source file when nothing is rewritten by --in-place" $+      withTempFile "inplaceXXXXXX.phi" $ \(path, h) -> do+        hPutStr h "⟦⟧\n"+        hClose h+        testCLISucceeded ["rewrite", "--in-place", "--sweet", path] []+        content <- readFile path+        content `shouldBe` "⟦⟧\n"++    it "modifies file in-place with --focus=Φ, since Φ and Q are the same locator" $+      withTempFile "inplaceXXXXXX.phi" $ \(path, h) -> do+        hPutStr h "[[ x -> \"foo\" ]]"+        hClose h+        testCLISucceeded ["rewrite", rule "simple.yaml", "--focus=Φ", "--in-place", "--sweet", path] []+        content <- readFile path+        content `shouldBe` "\"bar\":x\n"++    it "keeps the XMIR format when rewriting an XMIR file in-place" $+      withTempFileContent "inplaceXXXXXX.xmir" xmir $ \path -> do+        testCLISucceeded ["rewrite", "--input=xmir", "--output=xmir", "--in-place", path] []+        content <- readFile path+        _ <- evaluate (length content)+        content `shouldContain` "<object"++    it "skips rewriting with --update when target is newer than source" $+      withTempFileContent "src-XXXXXX.phi" "[[ x -> \"foo\" ]]" $ \src ->+        withTempFileContent "tgt-XXXXXX.phi" "ORIGINAL" $ \tgt -> do+          now <- getCurrentTime+          setModificationTime src (addUTCTime (-60) now)+          setModificationTime tgt now+          testCLISucceeded+            ["rewrite", rule "simple.yaml", "--update", "--sweet", "--target=" ++ tgt, src]+            []+          content <- readFile tgt+          content `shouldBe` "ORIGINAL"++    it "logs the skip reason at debug level when --update finds a newer target" $+      withTempFileContent "src-XXXXXX.phi" "[[ x -> \"foo\" ]]" $ \src ->+        withTempFileContent "tgt-XXXXXX.phi" "ORIGINAL" $ \tgt -> do+          now <- getCurrentTime+          setModificationTime src (addUTCTime (-60) now)+          setModificationTime tgt now+          testCLISucceeded+            ["rewrite", rule "simple.yaml", "--update", "--sweet", "--log-level=DEBUG", "--target=" ++ tgt, src]+            ["is newer than source", "skipping rewriting (--update)"]++    it "logs progress at debug level when printing to --target" $+      withStdin "[[ ]]" $+        withTempFile "targetXXXXXX.tmp" $ \(path, h) -> do+          hClose h+          testCLISucceeded+            ["rewrite", "--sweet", "--log-level=DEBUG", printf "--target=%s" path]+            ["The option '--target' is specified, printing to", "The command result was saved in"]++    it "logs progress at debug level when modifying a file in-place" $+      withTempFile "inplaceXXXXXX.phi" $ \(path, h) -> do+        hPutStr h "[[ x -> \"foo\" ]]"+        hClose h+        testCLISucceeded+          ["rewrite", rule "simple.yaml", "--in-place", "--sweet", "--log-level=DEBUG", path]+          ["The option '--in-place' is specified, writing back to", "was modified in-place"]++    it "rewrites with --update when source is newer than target" $+      withTempFileContent "src-XXXXXX.phi" "[[ x -> \"foo\" ]]" $ \src ->+        withTempFileContent "tgt-XXXXXX.phi" "ORIGINAL" $ \tgt -> do+          now <- getCurrentTime+          setModificationTime tgt (addUTCTime (-60) now)+          setModificationTime src now+          testCLISucceeded+            ["rewrite", rule "simple.yaml", "--update", "--sweet", "--target=" ++ tgt, src]+            []+          content <- readFile tgt+          content `shouldBe` "\"bar\":x\n"++    it "rewrites with cycles" $+      withStdin "[[ x -> \"x\" ]]" $+        testCLISucceeded+          ["rewrite", "--sweet", rule "infinite.yaml", "--max-depth=1", "--max-cycles=2"]+          ["\"x_hi_hi\":x"]++    it "hides default package" $+      withStdin "[[ org -> [[ eolang -> [[ number -> [[]] ]]]], x -> 42 ]]" $+        testCLISucceeded+          ["rewrite", "--sweet", "--flat", "--hide=Q.org"]+          ["42.0:x"]++    it "hides several FQNs" $+      withStdin "[[ org -> [[ eolang -> Q.x, yegor256 -> Q.y ]], x -> 42 ]]" $+        testCLISucceeded+          ["rewrite", "--sweet", "--flat", "--hide=Q.org.eolang", "--hide=Q.org.yegor256"]+          ["⟦ org ↦ ⟦⟧, x ↦ 42.0 ⟧"]++    it "shows and hides" $+      withStdin "[[ org -> [[ eolang -> Q.x, yegor256 -> Q.y ]], x -> 42 ]]" $+        testCLISucceeded+          ["rewrite", "--sweet", "--flat", "--show=Q.org", "--hide=Q.org.eolang"]+          ["Φ.y:yegor256:org"]++    it "hides an object outside the --show locator" $+      withStdin "[[ org -> [[ eolang -> Q.x ]], x -> 42 ]]" $+        testCLISucceeded+          ["rewrite", "--sweet", "--flat", "--show=Q.x", "--hide=Q.org.eolang"]+          ["42.0:x"]++    it "hides an object inside an earlier --hide locator" $+      withStdin "[[ org -> [[ eolang -> [[ number -> [[]] ]] ]], x -> 42 ]]" $+        testCLISucceeded+          ["rewrite", "--sweet", "--flat", "--hide=Q.org.eolang", "--hide=Q.org.eolang.number"]+          ["⟦ org ↦ ⟦⟧, x ↦ 42.0 ⟧"]++    it "fails on a --show locator that matches nothing" $+      withStdin "[[ a -> [[ b -> Q, c -> Q ]], d -> Q ]]" $+        testCLIFailed+          ["rewrite", "--flat", "--show=Q.zzz"]+          ["[ERROR]:", "Can't find object by locator: 'Φ.zzz'"]++    it "shows the whole program with --show=Q" $+      withStdin "[[ a -> [[ b -> Q, c -> Q ]], d -> Q ]]" $+        testCLISucceeded+          ["rewrite", "--flat", "--show=Q"]+          ["⟦ a ↦ ⟦ b ↦ Φ, c ↦ Φ ⟧, d ↦ Φ ⟧"]++    it "prints in line with --flat" $+      withStdin "[[ x -> 5, y -> \"hey\", z -> [[ w -> [[ ]] ]] ]]" $+        testCLISucceeded+          ["rewrite", "--sweet", "--flat"]+          ["⟦ x ↦ 5.0, y ↦ \"hey\", z ↦ ⟦⟧:w ⟧"]++    it "removes unnecessary rho bindings in primitive applications" $+      withStdin+        ( unlines+            [ "[["+            , "  z -> [[ x -> [[ t -> 42 ]].t ]].x,"+            , "  org -> [[ eolang -> [[ bytes -> [[ data -> ? ]], number -> [[ as-bytes -> ? ]] ]] ]]"+            , "]]"+            ]+        )+        ( testCLISucceeded+            ["rewrite", "--sweet", "--normalize", "--flat"]+            ["⟦ z ↦ 42.0, org ↦ ⟦ bytes(data) ↦ ⟦⟧, number(as-bytes) ↦ ⟦⟧ ⟧:eolang ⟧"]+        )++    it "reduces log message" $+      withStdin "[[ x -> [[ y -> ? ]](y -> 5) ]]" $+        testCLISucceeded+          ["rewrite", "--log-level=debug", "--log-lines=1", "--normalize"]+          [ intercalate+              "\n"+              [ "[DEBUG]: Applied 'copy' (32 nodes -> 27 nodes)"+              , "---| log is limited by --log-lines=1 option |---"+              ]+          ]++    it "reports a condition that raised while being evaluated" $+      withStdin "[[ x -> [[ y -> ∅ ]] ]]" $+        testCLISucceeded+          ["rewrite", rule "raising-condition.yaml", "--log-level=debug", "--flat"]+          [ "raised and was treated as not met: user error (Only data objects and bytes are supported"+          , "⟦ x ↦ ⟦ y ↦ ∅ ⟧ ⟧"+          ]++    it "canonizes expression" $+      withStdin "[[ x -> [[ y -> [[ L> Func ]].q, z -> Q.x(a -> [[ w -> [[ L> Atom ]], L> Hello ]]) ]], L> Package ]]" $+        testCLISucceeded+          ["rewrite", "--canonize", "--sweet", "--flat"]+          ["⟦ x ↦ ⟦ y ↦ Fn1:λ.q, z ↦ Φ.x( a ↦ ⟦ w ↦ Fn2:λ, λ ⤍ Fn3 ⟧ ) ⟧, λ ⤍ Package ⟧"]++    it "rewrites by locator" $+      withStdin "[[ ex -> [[ x -> [[ y -> 5 ]].y ]], abc -> [[ x -> ? ]](x -> 5) ]]" $+        testCLISucceeded+          ["rewrite", "--sweet", "--flat", "--locator=Q.ex", "--normalize"]+          ["⟦ ex ↦ 5.0:x, abc ↦ ∅:x( x ↦ 5.0 ) ⟧"]++    it "returns original expression on --breakpoint" $+      withStdin "[[ x -> ?, y -> $.x ]](x -> [[ D> 42- ]]).y" $+        testCLISucceeded+          ["rewrite", "--sweet", "--flat", "--normalize", "--breakpoint=stop", "--log-level=debug"]+          [ "Applied 'copy' (22 nodes -> 17 nodes)"+          , "Rule 'stop' is a breakpoint, dropping down all the previous rewritings..."+          , "⟦ x ↦ ∅, y ↦ x ⟧( x ↦ 42-:Δ ).y"+          ]++    it "leaves no step files of the rewritings --breakpoint dropped" $+      withTempDirectory "phino-steps-breakpoint" $ \dir ->+        withStdin "[[ x -> ?, y -> $.x ]](x -> [[ D> 42- ]]).y" $ do+          testCLISucceeded+            ["rewrite", "--sweet", "--flat", "--normalize", "--breakpoint=stop", "--steps-dir=" ++ dir]+            ["⟦ x ↦ ∅, y ↦ x ⟧( x ↦ 42-:Δ ).y"]+          listDirectory dir `shouldReturn` []++    it "finishes under --depth-sensitive when the only match left would not change the term" $+      withTempFileContent "phino-fixpoint.yaml" "name: fix\npattern: '[[ x -> !e1, !B1 ]]'\nresult: '[[ x -> Q, !B1 ]]'\n" $ \fix ->+        withStdin "[[ x -> $ ]]" $+          testCLISucceeded ["rewrite", "--rule=" ++ fix, "--max-depth=1", "--depth-sensitive", "--flat"] ["⟦ x ↦ Φ ⟧"]++    it "keeps the rewritten expression when the --breakpoint rule fired" $+      withStdin "⟦ a ↦ ⟦ b ↦ Φ ⟧.b ⟧" $+        testCLISucceeded+          ["rewrite", "--flat", "--normalize", "--breakpoint=dot"]+          ["⟦ a ↦ Φ( ρ ↦ ⟦ b ↦ Φ ⟧ ) ⟧"]++  describe "morph --focus under --locator" $ do+    it "finds the same object for the steps and for the answer" $+      withStdin "⟦ t ↦ ⟦ a ↦ ⟦ Δ ⤍ 01- ⟧ ⟧, a ↦ ⟦ Δ ⤍ 02- ⟧ ⟧" $ do+        (out, _) <- withStdout (runCLI ["morph", "--locator=Q.t", "--focus=Q.a", "--flat", "--sequence"])+        filter (not . null) (lines out) `shouldSatisfy` all (== "⟦ Δ ⤍ 02- ⟧")+    it "prints the answer when --focus names the object at --locator" $+      withStdin "⟦ t ↦ ⟦ a ↦ ⟦ Δ ⤍ 01- ⟧ ⟧ ⟧" $+        testCLISucceeded ["morph", "--locator=Q.t", "--focus=Q.t", "--flat", "--sequence"] ["⟦ a ↦ ⟦ Δ ⤍ 01- ⟧ ⟧"]+    it "fails on a --focus it cannot find before it prints any step" $+      withStdin "⟦ t ↦ ⟦ a ↦ ⟦ Δ ⤍ 01- ⟧ ⟧ ⟧" $ do+        (out, _) <- withStdout (try (runCLI ["morph", "--locator=Q.t", "--focus=Q.nope", "--flat", "--sequence"]) :: IO (Either ExitCode ()))+        out `shouldNotContain` "⟦ t ↦"++  it "fails on a --hide locator that matches nothing, as --show does" $+    withStdin "[[ x -> Q.y ]]" $+      testCLIFailed ["rewrite", "--hide=Q.nope"] ["Can't find object by locator: 'Φ.nope'"]++  it "names the term dataization cannot take, and says a normal form is expected" $+    withStdin "⟦ x ↦ ⟦ b ↦ ⟦ Δ ⤍ 01- ⟧ ⟧.b ⟧" $+      testCLIFailed ["dataize", "--locator=Q.x"] ["expects a normal form: 01-:Δ:b.b"]++  describe "--partial parks a universe that --max-cycles stops" $ do+    let heavy = "⟦ simple ↦ ⟦ Δ ⤍ 01- ⟧, heavy ↦ ⟦ a ↦ ⟦ b ↦ ⟦ c ↦ ⟦ Δ ⤍ 02- ⟧ ⟧ ⟧ ⟧.a.b.c ⟧"+    it "in dataize" $+      withStdin heavy $+        testCLISucceeded ["dataize", "--locator=Q.simple", "--max-cycles=1", "--partial", "--flat"] ["⟦ Δ ⤍ 01- ⟧"]+    it "in morph" $+      withStdin heavy $+        testCLISucceeded ["morph", "--locator=Q.simple", "--max-cycles=1", "--partial", "--flat"] ["⟦ Δ ⤍ 01- ⟧"]++  describe "dataize" $ do+    it "prints help" $+      testCLISucceeded ["dataize", "--help"] ["Dataize the 𝜑-expression"]++    it "names every block of a --symbolic entry in its help" $+      testCLISucceeded ["dataize", "--help"] ["\"dataize\"", "\"morph\"", "\"evaluate\"", "\"defer\"", "\"stay\"", "\"rewrite\"", "\"symbolize\"", "\"join\""]++    it "dataizes simple expression" $+      withStdin "[[ D> 01- ]]" $+        testCLISucceeded ["dataize"] ["01-"]++    it "accepts --seed flag" $+      withStdin "[[ D> 01- ]]" $+        testCLISucceeded ["dataize", "--seed=7"] ["01-"]++    it "fails to dataize an empty object, which dataizes the terminator ⊥" $+      withStdin "[[ ]]" $+        testCLIFailed ["dataize"] ["terminator ⊥"]++    it "fails with negative --max-steps" $+      withStdin "[[ D> 01- ]]" $+        testCLIFailed ["dataize", "--max-steps=-1"] ["--max-steps must be positive"]++    it "fails on --max-steps instead of dataizing forever" $+      loopingLambdas $ \endless ->+        withStdin "⟦ @ ↦ ⟦ λ ⤍ L_loop ⟧ ⟧" $+          testCLIFailed+            ["dataize", "--symbolic=" ++ endless, "--max-steps=40"]+            ["[ERROR]: Morphing did not finish before reaching the limit of steps: --max-steps=40"]++    it "parks --max-steps on a residual with --partial" $+      loopingLambdas $ \endless ->+        withStdin "⟦ @ ↦ ⟦ λ ⤍ L_loop ⟧ ⟧" $+          testCLISucceeded+            ["dataize", "--symbolic=" ++ endless, "--max-steps=40", "--partial", "--flat", "--hide-rho"]+            ["⟦ λ ⤍ L_loop ⟧"]++    it "fails on --max-firings before --max-steps is spent" $+      loopingLambdas $ \endless ->+        withStdin "⟦ @ ↦ ⟦ λ ⤍ L_loop ⟧ ⟧" $+          testCLIFailed+            ["dataize", "--symbolic=" ++ endless, "--max-steps=400", "--max-firings=5"]+            ["[ERROR]: Evaluation did not finish before reaching the limit of firings: --max-firings=5"]++    describe "--acyclic=proven" $ do+      let circling = "⟦ cyc ↦ ⟦ x ↦ ∅, φ ↦ Φ.cyc( ξ.x ) ⟧, t ↦ Φ.cyc( ⟦⟧ ) ⟧"+      it "spends the whole budget and fails on the limit without the flag" $+        withStdin circling $+          testCLIFailed+            ["dataize", "--locator=Q.t", "--max-steps=40"]+            ["[ERROR]: Morphing did not finish before reaching the limit of steps: --max-steps=40"]++      it "names the term it came back to with the flag" $+        withStdin circling $+          testCLIFailed+            ["dataize", "--locator=Q.t", "--acyclic=proven", "--max-steps=4000"]+            ["[ERROR]: Reduction entered a formation it is already inside:"]++      it "prints the residue and exits successfully with --partial" $+        withStdin circling $+          testCLISucceeded+            ["dataize", "--locator=Q.t", "--acyclic=proven", "--partial", "--max-steps=4000", "--flat", "--hide-rho"]+            ["Φ.cyc( α0 ↦ ⟦⟧ )"]++      it "writes the cut to the protocol where the formation would have opened" $+        withTempFile "protocolXXXXXX.txt" $ \(path, stream) -> do+          hClose stream+          withStdin circling $+            testCLISucceeded+              ["dataize", "--locator=Q.t", "--acyclic=proven", "--partial", "--protocol=" ++ path, "--sweet", "--hide-rho", "--flat", "--quiet"]+              []+          records <- readProtocol path+          lines records+            `shouldBe` [ "𝔻(Φ.t):"+                       , "  𝕄(Φ.t):"+                       , "    𝑛·0·1 := Φ.cyc( x ↦ ⟦⟧ )"+                       , "    morphed 𝑛·0·2 := 𝑛·0·1"+                       , "  boxed 𝑛·0·3 := 𝑛·0·2.φ"+                       , "  𝕄(𝑛·0·3):"+                       , "    𝑛·0·4 := Φ.cyc( x ↦ ⟦⟧ )"+                       , "    morphed 𝑛·0·5 := 𝑛·0·4"+                       , "  looped 𝑛·0·5  # 𝔻(𝑛·0·3), proven"+                       ]++      it "writes the cut to the XML protocol with the formation in an element of its own" $+        withTempFile "protocolXXXXXX.xml" $ \(path, stream) -> do+          hClose stream+          withStdin circling $+            testCLISucceeded+              ["dataize", "--locator=Q.t", "--acyclic=proven", "--partial", "--protocol=" ++ path, "--sweet", "--hide-rho", "--flat", "--quiet"]+              []+          records <- readProtocol path+          lines records+            `shouldBe` [ "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"+                       , "<dataize at=\"Φ.t\">"+                       , "  <morph at=\"Φ.t\">"+                       , "    <formed meta=\"𝑒·0·1\"/>"+                       , "    <applied meta=\"𝑛·0·1\" by=\"morph\" at=\"Φ.t\" of=\"Φ.cyc\"><meta attr=\"x\">𝑒·0·1</meta></applied>"+                       , "    <morphed meta=\"𝑛·0·2\">𝑛·0·1</morphed>"+                       , "  </morph>"+                       , "  <boxed meta=\"𝑛·0·3\">𝑛·0·2.φ</boxed>"+                       , "  <morph at=\"𝑛·0·3\">"+                       , "    <applied meta=\"𝑛·0·4\" by=\"morph\" at=\"𝑛·0·3\" of=\"Φ.cyc\"><meta attr=\"x\">𝑒·0·1</meta></applied>"+                       , "    <morphed meta=\"𝑛·0·5\">𝑛·0·4</morphed>"+                       , "  </morph>"+                       , "  <looped by=\"dataize\" match=\"proven\" at=\"𝑛·0·3\" of=\"𝑛·0·5\"/>"+                       , "</dataize>"+                       ]++      it "writes a plausible cut to the protocol as plausible" $+        withTempFile "protocolXXXXXX.txt" $ \(path, stream) -> do+          hClose stream+          withStdin circling $+            testCLISucceeded+              ["dataize", "--locator=Q.t", "--acyclic=plausible", "--partial", "--protocol=" ++ path, "--sweet", "--hide-rho", "--flat", "--quiet"]+              []+          records <- readProtocol path+          lines records `shouldContain` ["  looped 𝑛·0·5  # 𝔻(𝑛·0·3), plausible"]++      it "refuses the flag without a mode" $+        withStdin "⟦ t ↦ ⟦ Δ ⤍ 01-02 ⟧ ⟧" $+          testCLIFailed ["dataize", "--locator=Q.t", "--acyclic"] ["The option `--acyclic` expects an argument"]++      it "refuses a mode it does not know" $+        withStdin "⟦ t ↦ ⟦ Δ ⤍ 01-02 ⟧ ⟧" $+          testCLIFailed ["dataize", "--locator=Q.t", "--acyclic=sure"] ["The value 'sure' can't be used for '--acyclic' option"]++      it "answers a terminating program the same way with the flag" $+        withStdin "⟦ t ↦ ⟦ Δ ⤍ 01-02 ⟧ ⟧" $+          testCLISucceeded ["dataize", "--locator=Q.t", "--acyclic=proven"] ["01-02"]++    describe "--max-loops" $ do+      let once = "⟦ f ↦ ⟦ x ↦ ∅, φ ↦ ξ.x.again ⟧, start ↦ ⟦ again ↦ Φ.f( x ↦ Φ.stop( v ↦ Φ.start ) ) ⟧, stop ↦ ⟦ v ↦ ∅, again ↦ ⟦ Δ ⤍ 01- ⟧ ⟧, t ↦ Φ.f( x ↦ Φ.start ) ⟧"+      it "cuts a recursion that stops after one round without the flag" $+        withStdin once $+          testCLIFailed+            ["dataize", "--locator=Q.t", "--acyclic=plausible"]+            ["[ERROR]: Reduction entered a formation it is already inside:"]++      it "dataizes a recursion that stops after one round with --max-loops=1" $+        withStdin once $+          testCLISucceeded ["dataize", "--locator=Q.t", "--acyclic=plausible", "--max-loops=1"] ["01-"]++      it "cuts a recursion that never stops with --max-loops=1" $+        withStdin "⟦ cyc ↦ ⟦ x ↦ ∅, φ ↦ Φ.cyc( ξ.x ) ⟧, t ↦ Φ.cyc( ⟦⟧ ) ⟧" $+          testCLIFailed+            ["dataize", "--locator=Q.t", "--acyclic=proven", "--max-loops=1", "--max-steps=4000"]+            ["[ERROR]: Reduction entered a formation it is already inside:"]++      it "refuses a negative count" $+        withStdin once $+          testCLIFailed ["dataize", "--locator=Q.t", "--acyclic=plausible", "--max-loops=-1"] ["--max-loops must be >= 0"]++      it "refuses the flag without --acyclic" $+        withStdin once $+          testCLIFailed ["dataize", "--locator=Q.t", "--max-loops=1"] ["The option --max-loops requires --acyclic, since only --acyclic cuts a loop"]++    it "dataizes with --sequence" $+      withStdin "[[ @ -> [[ x -> [[ D> 01-, y -> ? ]](y -> [[ ]]) ]].x ]]" $+        testCLISucceeded+          ["dataize", "--sequence", "--output=latex", "--flat", "--sweet"]+          [ intercalate+              "\n"+              [ "\\begin{phiquation}"+              , "[[ D> |01-|, |y| -> ? ]] ( |y| -> [[]] ) : |x| . |x| : @ \\phiDataize[\\nameref{r:box}]"+              , "  \\phiDataize [[ D> |01-|, |y| -> ? ]] ( |y| -> [[]] ) : |x| . |x| : @ . @ \\phiNormalize[\\nameref{r:copy}]"+              , "  \\phiNormalize [[ D> |01-|, |y| -> [[]] ]] : |x| . |x| : @ . @ \\phiNormalize[\\nameref{r:dot}]"+              , "  \\phiNormalize [[ D> |01-|, |y| -> [[]] ]] ( \\phiTerminal{\\rho} -> [[ D> |01-|, |y| -> [[]] ]] : |x| ) : @ . @ \\phiNormalize[\\nameref{r:skip}]"+              , "  \\phiNormalize [[ D> |01-|, |y| -> [[]] ]] : @ . @ \\phiNormalize[\\nameref{r:dot}]"+              , "  \\phiNormalize [[ D> |01-|, |y| -> [[]] ]] ( \\phiTerminal{\\rho} -> Q ) \\phiNormalize[\\nameref{r:skip}]"+              , "  \\phiNormalize [[ D> |01-|, |y| -> [[]] ]] \\phiDataize[\\nameref{r:delta}]"+              , "  \\phiDataize |01-|{.}"+              , "\\end{phiquation}"+              , "01-"+              ]+          ]++    it "keeps the delta step in --sequence under --quiet" $+      withStdin "[[ D> 01- ]]" $+        testCLISucceeded+          ["dataize", "--sequence", "--quiet", "--output=latex", "--flat", "--sweet"]+          [ intercalate+              "\n"+              [ "|01-| : D \\phiDataize[\\nameref{r:delta}]"+              , "  \\phiDataize |01-|{.}"+              , "\\end{phiquation}"+              ]+          ]++    it "ends the phi --sequence at the bare data" $+      withStdin "[[ D> 01- ]]" $+        testCLISucceeded+          ["dataize", "--sequence", "--quiet", "--flat", "--sweet"]+          ["01-:Δ\n01-"]++    it "focuses a compressed sequence whose meet replaces a step root" $+      withStdin "[[ @ -> [[ @ -> $.c.plus( 32.0 ), c -> 25.0 ]], bytes ↦ ⟦ φ ↦ ∅ ⟧, number(φ) -> [[ plus -> [[ ^ -> ?, x -> ?, L> L_number_plus ]] ]] ]]" $+        testCLISucceeded+          ["dataize", symbolic, "--output=latex", "--sweet", "--nonumber", "--compress", "--canonize", "--meet-prefix=dataization", "--sequence", "--flat", "--quiet", "--hide=Q.bytes", "--hide=Q.number", "--locator=Q.@", "--focus=Q.@", "--meet-length=5", "--meet-popularity=1"]+          ["\\phinoMeet{dataization:1}{ [[ @ -> |c| . |plus| ( |32.0| ), |c| -> |25.0| ]] } \\phiDataize[\\nameref{r:box}]"]++    it "compresses a canonized whole-expression sequence into a meet" $+      withStdin "[[ @ -> [[ @ -> $.c.plus( 32.0 ), c -> 25.0 ]], bytes ↦ ⟦ φ ↦ ∅ ⟧, number(φ) -> [[ plus -> [[ ^ -> ?, x -> ?, L> L_number_plus ]] ]] ]]" $+        testCLISucceeded+          ["dataize", symbolic, "--output=latex", "--sweet", "--nonumber", "--compress", "--canonize", "--meet-prefix=dataization", "--sequence", "--flat", "--quiet", "--meet-length=5", "--meet-popularity=1"]+          ["\\phinoMeet{dataization:1}"]++    it "canonizes the residue it prints with --partial" $+      withStdin "[[ @ -> [[ L> Foo ]] ]]" $+        testCLISucceeded ["dataize", "--partial", "--canonize", "--flat", "--sweet"] ["Fn1:λ"]++    it "dataizes with --locator" $+      withStdin "[[ ex -> [[ @ -> Q.x ]], x -> [[ D> 42- ]] ]]" $+        testCLISucceeded ["dataize", "--locator=Q.ex"] ["42-"]++    it "does not print bytes with --quiet" $+      withStdin "[[ D> 01- ]]" $+        testCLISucceeded ["dataize", "--quiet"] []++    describe "--abridged" $ do+      let wide = "⟦ t ↦ ⟦ φ ↦ ⟦ Δ ⤍ 01-02 ⟧, anfang ↦ ξ.schluss, mitte ↦ ξ.anfang, schluss ↦ ξ.mitte, rand ↦ ξ.schluss ⟧ ⟧"+          stuck = "⟦ t ↦ ⟦ λ ⤍ L_weit, anfang ↦ ξ.schluss, mitte ↦ ξ.anfang, schluss ↦ ξ.mitte, rand ↦ ξ.schluss ⟧ ⟧"+      it "folds a long formation in the text protocol" $+        withTempFile "protocolXXXXXX.txt" $ \(path, stream) -> do+          hClose stream+          withStdin stuck $+            testCLISucceeded ["dataize", "--locator=Q.t", "--partial", "--protocol=" ++ path, "--abridged", "--sweet", "--hide-rho", "--quiet"] []+          records <- readProtocol path+          lines records `shouldContain` ["  unanswered L_weit  # 𝔻(⟦ λ ⤍ L_weit, +4 ⟧)"]+      it "names a long formation in the XML protocol by its path rather than folding it" $+        withTempFile "protocolXXXXXX.xml" $ \(path, stream) -> do+          hClose stream+          withStdin stuck $+            testCLISucceeded ["dataize", "--locator=Q.t", "--partial", "--protocol=" ++ path, "--abridged", "--sweet", "--hide-rho", "--quiet"] []+          records <- readProtocol path+          lines records `shouldContain` ["  <unanswered λ=\"L_weit\" by=\"dataize\">Φ.t</unanswered>"]+      forM_+        [ ("textXXXXXX.txt", "      𝛿·1·1 := 01-02-..(8b)..-0B-0C")+        , ("XMLXXXXXX.xml", "      <dataized meta=\"𝛿·1·1\">01-02-..(8b)..-0B-0C</dataized>")+        ]+        ( \(template, line) ->+            it ("cuts a long datum a firing came down to under --abridged-data, as " ++ line) $+              withTempFile template $ \(path, stream) -> do+                hClose stream+                withLambdasOf (T.pack "- λ: L_outer\n  dataize:\n    𝛿1: ξ.arg\n  𝑛: ⟦ λ ⤍ 𝜎 ⟧\n") $ \outer ->+                  withStdin "⟦ x ↦ ⟦ arg ↦ ⟦ Δ ⤍ 01-02-03-04-05-06-07-08-09-0A-0B-0C ⟧, λ ⤍ L_outer ⟧ ⟧" $+                    testCLISucceeded ["dataize", "--symbolic=" ++ outer, "--locator=Q.x", "--partial", "--protocol=" ++ path, "--abridged", "--abridged-data", "--sweet", "--hide-rho", "--quiet"] []+                records <- readProtocol path+                lines records `shouldContain` [line]+        )+      forM_+        [ ("textXXXXXX.txt", "      𝛿·1·1 := 30-31-32-33-34-35-36-37-38-39-41-42-43-44-45-46")+        , ("XMLXXXXXX.xml", "      <dataized meta=\"𝛿·1·1\">30-31-32-33-34-35-36-37-38-39-41-42-43-44-45-46</dataized>")+        ]+        ( \(template, line) ->+            it ("keeps a long datum a firing came down to whole without --abridged-data, as " ++ line) $+              withTempFile template $ \(path, stream) -> do+                hClose stream+                withLambdasOf (T.pack "- λ: L_hex\n  dataize:\n    𝛿1: ξ.arg\n  𝑛: ⟦ λ ⤍ 𝜎 ⟧\n") $ \hex ->+                  withStdin "⟦ x ↦ ⟦ arg ↦ ⟦ Δ ⤍ 30-31-32-33-34-35-36-37-38-39-41-42-43-44-45-46 ⟧, λ ⤍ L_hex ⟧ ⟧" $+                    testCLISucceeded ["dataize", "--symbolic=" ++ hex, "--locator=Q.x", "--partial", "--protocol=" ++ path, "--abridged", "--sweet", "--hide-rho", "--quiet"] []+                records <- readProtocol path+                lines records `shouldContain` [line]+        )+      it "folds a long formation under the width given as the value" $+        withTempFile "protocolXXXXXX.txt" $ \(path, stream) -> do+          hClose stream+          withStdin stuck $+            testCLISucceeded ["dataize", "--locator=Q.t", "--partial", "--protocol=" ++ path, "--abridged=64", "--sweet", "--hide-rho", "--quiet"] []+          records <- readProtocol path+          lines records `shouldContain` ["  unanswered L_weit  # 𝔻(⟦ λ ⤍ L_weit, +4 ⟧)"]+      it "keeps a formation whole under a width it fits in" $+        withTempFile "protocolXXXXXX.txt" $ \(path, stream) -> do+          hClose stream+          withStdin stuck $+            testCLISucceeded ["dataize", "--locator=Q.t", "--partial", "--protocol=" ++ path, "--abridged=200", "--sweet", "--hide-rho", "--quiet"] []+          records <- readProtocol path+          lines records `shouldContain` ["  unanswered L_weit  # 𝔻(⟦ λ ⤍ L_weit, anfang ↦ schluss, mitte ↦ anfang, schluss ↦ mitte, rand ↦ schluss ⟧)"]+      it "refuses a width that is not a number" $+        withStdin wide $+          testCLIFailed ["dataize", "--locator=Q.t", "--protocol=breit.txt", "--abridged=breit"] ["cannot parse value `breit'"]+      it "leaves the printed result whole" $+        withTempFile "protocolXXXXXX.txt" $ \(path, stream) -> do+          hClose stream+          withStdin wide $+            testCLISucceeded ["morph", "--locator=Q.t", "--protocol=" ++ path, "--abridged", "--sweet", "--hide-rho", "--flat"] ["anfang ↦ schluss, mitte ↦ anfang"]+      it "refuses the flag without a protocol" $+        withStdin wide $+          testCLIFailed ["dataize", "--locator=Q.t", "--abridged"] ["The option --abridged requires --protocol"]+      it "refuses to cut the data in dataize without --abridged" $+        withStdin wide $+          testCLIFailed ["dataize", "--locator=Q.t", "--protocol=daten.txt", "--abridged-data"] ["The option --abridged-data requires --abridged"]+      it "refuses to cut the data in morph without --abridged" $+        withStdin wide $+          testCLIFailed ["morph", "--locator=Q.t", "--protocol=daten.xml", "--abridged-data"] ["The option --abridged-data requires --abridged"]++    describe "--protocol" $ do+      let sum' = "[[ bytes ↦ ⟦ φ ↦ ∅ ⟧, number(φ) -> [[ plus(^, x) -> [[ L> L_number_plus ]] ]], @ -> 5.plus(6) ]]"+          chained = "[[ bytes ↦ ⟦ φ ↦ ∅ ⟧, number(φ) -> [[ plus(^, x) -> [[ L> L_number_plus ]] ]], @ -> 5.plus(6).plus(7) ]]"+          nested = "[[ bytes ↦ ⟦ φ ↦ ∅ ⟧, number(φ) -> [[ plus(^, x) -> [[ L> L_number_plus ]] ]], @ -> 5.plus(6.plus(7)) ]]"+          mixed = "[[ bytes ↦ ⟦ φ ↦ ∅ ⟧, number(φ) -> [[ plus(^, x) -> [[ L> L_number_plus ]], times(^, x) -> [[ L> L_number_times ]] ]], @ -> 5.plus(6).times(7) ]]"+      it "opens the protocol with the run it is the protocol of" $+        withTempFile "protocolXXXXXX.txt" $ \(path, stream) -> do+          hClose stream+          withStdin "[[ D> 01- ]]" $+            testCLISucceeded ["dataize", "--protocol=" ++ path, "--quiet"] []+          records <- readProtocol path+          records `shouldBe` "𝔻(Φ):\n  𝛿·0·1 := 01-\n"++      it "closes the protocol with its msec on the third line from the end" $+        withTempFile "protocolXXXXXX.txt" $ \(path, stream) -> do+          hClose stream+          withStdin sum' $+            testCLISucceeded ["dataize", symbolic, "--protocol=" ++ path, "--quiet"] []+          records <- readUtf8 path+          (lines records !! (length (lines records) - 3)) `shouldSatisfy` isPrefixOf "msec "++      it "closes the protocol with the firings it counted" $+        withTempFile "protocolXXXXXX.txt" $ \(path, stream) -> do+          hClose stream+          withStdin sum' $+            testCLISucceeded ["dataize", symbolic, "--protocol=" ++ path, "--quiet"] []+          records <- readUtf8 path+          lines records `shouldContain` ["firings 1"]++      it "closes the protocol with its fps on the last line" $+        withTempFile "protocolXXXXXX.txt" $ \(path, stream) -> do+          hClose stream+          withStdin sum' $+            testCLISucceeded ["dataize", symbolic, "--protocol=" ++ path, "--quiet"] []+          records <- readUtf8 path+          last (lines records) `shouldSatisfy` isPrefixOf "fps "++      it "closes the protocol with zero firings when the run fires nothing" $+        withTempFile "protocolXXXXXX.txt" $ \(path, stream) -> do+          hClose stream+          withStdin "[[ D> 01- ]]" $+            testCLISucceeded ["dataize", "--protocol=" ++ path, "--quiet"] []+          records <- readUtf8 path+          lines records `shouldContain` ["firings 0"]++      it "writes one line per operand and one per answer of a firing" $+        withTempFile "protocolXXXXXX.txt" $ \(path, stream) -> do+          hClose stream+          withStdin sum' $+            testCLISucceeded ["dataize", symbolic, "--protocol=" ++ path, "--quiet", "--sweet", "--hide-rho"] []+          records <- readProtocol path+          lines records+            `shouldBe` [ "𝔻(Φ):"+                       , "  boxed 𝑛·0·1 := Φ.φ"+                       , "  𝕄(𝑛·0·1):"+                       , "    𝑛·0·2 := 5.0"+                       , "    𝑛·0·3 := 𝑛·0·2.plus( x ↦ 6.0 )"+                       , "    morphed 𝑛·0·4 := 𝑛·0·3"+                       , "  𝔼(L_number_plus):  # 𝔻(𝑛·0·1)"+                       , "    𝔻(ξ.ρ):"+                       , "      𝕄(ξ.ρ):"+                       , "        𝑛·1·1 := 5.0"+                       , "        morphed 𝑛·1·2 := 𝑛·1·1"+                       , "      boxed 𝑛·1·3 := 𝑛·1·2.φ"+                       , "      𝕄(𝑛·1·3):"+                       , "        𝑛·1·4 := Φ.bytes( φ ↦ 40-14-00-00-00-00-00-00:Δ )"+                       , "        morphed 𝑛·1·5 := 𝑛·1·4"+                       , "      boxed 𝑛·1·6 := 𝑛·1·5.φ"+                       , "      𝛿·1·1 := 40-14-00-00-00-00-00-00  # 5.0"+                       , "    𝛿1·1 := 𝛿·1·1  # 𝔻(ξ.ρ)"+                       , "    𝔻(ξ.x):"+                       , "      𝕄(ξ.x):"+                       , "        𝑛·1·7 := 6.0"+                       , "        morphed 𝑛·1·8 := 𝑛·1·7"+                       , "      boxed 𝑛·1·9 := 𝑛·1·8.φ"+                       , "      𝕄(𝑛·1·9):"+                       , "        𝑛·1·10 := Φ.bytes( φ ↦ 40-18-00-00-00-00-00-00:Δ )"+                       , "        morphed 𝑛·1·11 := 𝑛·1·10"+                       , "      boxed 𝑛·1·12 := 𝑛·1·11.φ"+                       , "      𝛿·1·2 := 40-18-00-00-00-00-00-00  # 6.0"+                       , "    𝛿2·1 := 𝛿·1·2  # 𝔻(ξ.x)"+                       , "    𝑛·1·13 := Φ.number( φ ↦ 𝜎1:λ )  # 𝑛"+                       , "    𝕄(𝑛·1·13):"+                       , "      𝑛·1·14 := Φ.number( φ ↦ 𝜎1:λ )"+                       , "      morphed 𝑛·1·15 := 𝑛·1·14"+                       , "    𝑛·1·16 := 𝑛·1·15  # 𝕄(𝑛·1·13)"+                       , "  boxed 𝑛·0·5 := 𝑛·1·16.φ"+                       , "  𝛿·0·1 := 𝔻(𝜎1:λ)"+                       ]++      it "writes what every morphing a dataization ran came to" $+        withTempFile "protocolXXXXXX.txt" $ \(path, stream) -> do+          hClose stream+          withLambdasOf (T.pack "- λ: L_make\n  𝑛: ⟦ φ ↦ Φ.bytes( φ ↦ ⟦ λ ⤍ 𝜎 ⟧ ) ⟧\n") $ \makes ->+            withStdin "⟦ bytes ↦ ⟦ φ ↦ ∅ ⟧, make ↦ ⟦ λ ⤍ L_make ⟧, foo ↦ ⟦ φ ↦ Φ.make ⟧, e ↦ Φ.foo ⟧" $+              testCLISucceeded ["dataize", "--symbolic=" ++ makes, "--locator=Q.e", "--partial", "--protocol=" ++ path, "--quiet", "--sweet", "--hide-rho"] []+          records <- readProtocol path+          lines records+            `shouldBe` [ "𝔻(Φ.e):"+                       , "  𝕄(Φ.e):"+                       , "    morphed 𝑛·0·1 := Φ.make:φ"+                       , "  boxed 𝑛·0·2 := 𝑛·0·1.φ"+                       , "  𝕄(𝑛·0·2):"+                       , "    morphed 𝑛·0·3 := L_make:λ"+                       , "  𝔼(L_make):  # 𝔻(𝑛·0·2)"+                       , "    𝑛·1·1 := Φ.bytes( φ ↦ 𝜎1:λ ):φ  # 𝑛"+                       , "    𝕄(𝑛·1·1):"+                       , "      morphed 𝑛·1·2 := Φ.bytes( φ ↦ 𝜎1:λ ):φ"+                       , "    𝑛·1·3 := 𝑛·1·2  # 𝕄(𝑛·1·1)"+                       , "  boxed 𝑛·0·4 := 𝑛·1·3.φ"+                       , "  𝕄(𝑛·0·4):"+                       , "    𝑛·0·5 := Φ.bytes( φ ↦ 𝜎1:λ )"+                       , "    morphed 𝑛·0·6 := 𝑛·0·5"+                       , "  boxed 𝑛·0·7 := 𝑛·0·6.φ"+                       , "  𝛿·0·1 := 𝔻(𝜎1:λ)"+                       ]++      it "numbers the firings of one entry apart and names the symbol between them" $+        withTempFile "protocolXXXXXX.txt" $ \(path, stream) -> do+          hClose stream+          withStdin chained $+            testCLISucceeded ["dataize", symbolic, "--protocol=" ++ path, "--quiet", "--sweet", "--hide-rho"] []+          records <- readProtocol path+          lines records+            `shouldBe` [ "𝔻(Φ):"+                       , "  boxed 𝑛·0·1 := Φ.φ"+                       , "  𝕄(𝑛·0·1):"+                       , "    𝑛·0·2 := 5.0"+                       , "    𝑛·0·3 := 𝑛·0·2.plus( x ↦ 6.0 )"+                       , "    𝔼(L_number_plus):"+                       , "      𝔻(ξ.ρ):"+                       , "        𝕄(ξ.ρ):"+                       , "          𝑛·1·1 := 5.0"+                       , "          morphed 𝑛·1·2 := 𝑛·1·1"+                       , "        boxed 𝑛·1·3 := 𝑛·1·2.φ"+                       , "        𝕄(𝑛·1·3):"+                       , "          𝑛·1·4 := Φ.bytes( φ ↦ 40-14-00-00-00-00-00-00:Δ )"+                       , "          morphed 𝑛·1·5 := 𝑛·1·4"+                       , "        boxed 𝑛·1·6 := 𝑛·1·5.φ"+                       , "        𝛿·1·1 := 40-14-00-00-00-00-00-00  # 5.0"+                       , "      𝛿1·1 := 𝛿·1·1  # 𝔻(ξ.ρ)"+                       , "      𝔻(ξ.x):"+                       , "        𝕄(ξ.x):"+                       , "          𝑛·1·7 := 6.0"+                       , "          morphed 𝑛·1·8 := 𝑛·1·7"+                       , "        boxed 𝑛·1·9 := 𝑛·1·8.φ"+                       , "        𝕄(𝑛·1·9):"+                       , "          𝑛·1·10 := Φ.bytes( φ ↦ 40-18-00-00-00-00-00-00:Δ )"+                       , "          morphed 𝑛·1·11 := 𝑛·1·10"+                       , "        boxed 𝑛·1·12 := 𝑛·1·11.φ"+                       , "        𝛿·1·2 := 40-18-00-00-00-00-00-00  # 6.0"+                       , "      𝛿2·1 := 𝛿·1·2  # 𝔻(ξ.x)"+                       , "      𝑛·1·13 := Φ.number( φ ↦ 𝜎1:λ )  # 𝑛"+                       , "      𝕄(𝑛·1·13):"+                       , "        𝑛·1·14 := Φ.number( φ ↦ 𝜎1:λ )"+                       , "        morphed 𝑛·1·15 := 𝑛·1·14"+                       , "      𝑛·1·16 := 𝑛·1·15  # 𝕄(𝑛·1·13)"+                       , "    𝑛·0·4 := 𝑛·1·14.plus( x ↦ 7.0 )"+                       , "    morphed 𝑛·0·5 := 𝑛·0·4"+                       , "  𝔼(L_number_plus):  # 𝔻(𝑛·0·1)"+                       , "    𝔻(ξ.ρ):"+                       , "      𝕄(ξ.ρ):"+                       , "        𝑛·2·1 := Φ.number( φ ↦ 𝜎1:λ )"+                       , "        morphed 𝑛·2·2 := 𝑛·1·16"+                       , "      boxed 𝑛·2·3 := 𝑛·2·2.φ"+                       , "      𝛿·2·1 := 𝔻(𝜎1:λ)"+                       , "    𝛿1·2 := 𝛿·2·1  # 𝔻(ξ.ρ)"+                       , "    𝔻(ξ.x):"+                       , "      𝕄(ξ.x):"+                       , "        𝑛·2·4 := 7.0"+                       , "        morphed 𝑛·2·5 := 𝑛·2·4"+                       , "      boxed 𝑛·2·6 := 𝑛·2·5.φ"+                       , "      𝕄(𝑛·2·6):"+                       , "        𝑛·2·7 := Φ.bytes( φ ↦ 40-1C-00-00-00-00-00-00:Δ )"+                       , "        morphed 𝑛·2·8 := 𝑛·2·7"+                       , "      boxed 𝑛·2·9 := 𝑛·2·8.φ"+                       , "      𝛿·2·2 := 40-1C-00-00-00-00-00-00  # 7.0"+                       , "    𝛿2·2 := 𝛿·2·2  # 𝔻(ξ.x)"+                       , "    𝑛·2·10 := Φ.number( φ ↦ 𝜎2:λ )  # 𝑛"+                       , "    𝕄(𝑛·2·10):"+                       , "      𝑛·2·11 := Φ.number( φ ↦ 𝜎2:λ )"+                       , "      morphed 𝑛·2·12 := 𝑛·2·11"+                       , "    𝑛·2·13 := 𝑛·2·12  # 𝕄(𝑛·2·10)"+                       , "  boxed 𝑛·0·6 := 𝑛·2·13.φ"+                       , "  𝛿·0·1 := 𝔻(𝜎2:λ)"+                       ]++      it "numbers the firings of different entries apart" $+        withTempFile "protocolXXXXXX.txt" $ \(path, stream) -> do+          hClose stream+          withStdin mixed $+            testCLISucceeded ["dataize", symbolic, "--protocol=" ++ path, "--quiet", "--sweet", "--hide-rho"] []+          records <- readProtocol path+          lines records+            `shouldBe` [ "𝔻(Φ):"+                       , "  boxed 𝑛·0·1 := Φ.φ"+                       , "  𝕄(𝑛·0·1):"+                       , "    𝑛·0·2 := 5.0"+                       , "    𝑛·0·3 := 𝑛·0·2.plus( x ↦ 6.0 )"+                       , "    𝔼(L_number_plus):"+                       , "      𝔻(ξ.ρ):"+                       , "        𝕄(ξ.ρ):"+                       , "          𝑛·1·1 := 5.0"+                       , "          morphed 𝑛·1·2 := 𝑛·1·1"+                       , "        boxed 𝑛·1·3 := 𝑛·1·2.φ"+                       , "        𝕄(𝑛·1·3):"+                       , "          𝑛·1·4 := Φ.bytes( φ ↦ 40-14-00-00-00-00-00-00:Δ )"+                       , "          morphed 𝑛·1·5 := 𝑛·1·4"+                       , "        boxed 𝑛·1·6 := 𝑛·1·5.φ"+                       , "        𝛿·1·1 := 40-14-00-00-00-00-00-00  # 5.0"+                       , "      𝛿1·1 := 𝛿·1·1  # 𝔻(ξ.ρ)"+                       , "      𝔻(ξ.x):"+                       , "        𝕄(ξ.x):"+                       , "          𝑛·1·7 := 6.0"+                       , "          morphed 𝑛·1·8 := 𝑛·1·7"+                       , "        boxed 𝑛·1·9 := 𝑛·1·8.φ"+                       , "        𝕄(𝑛·1·9):"+                       , "          𝑛·1·10 := Φ.bytes( φ ↦ 40-18-00-00-00-00-00-00:Δ )"+                       , "          morphed 𝑛·1·11 := 𝑛·1·10"+                       , "        boxed 𝑛·1·12 := 𝑛·1·11.φ"+                       , "        𝛿·1·2 := 40-18-00-00-00-00-00-00  # 6.0"+                       , "      𝛿2·1 := 𝛿·1·2  # 𝔻(ξ.x)"+                       , "      𝑛·1·13 := Φ.number( φ ↦ 𝜎1:λ )  # 𝑛"+                       , "      𝕄(𝑛·1·13):"+                       , "        𝑛·1·14 := Φ.number( φ ↦ 𝜎1:λ )"+                       , "        morphed 𝑛·1·15 := 𝑛·1·14"+                       , "      𝑛·1·16 := 𝑛·1·15  # 𝕄(𝑛·1·13)"+                       , "    𝑛·0·4 := 𝑛·1·14.times( x ↦ 7.0 )"+                       , "    morphed 𝑛·0·5 := 𝑛·0·4"+                       , "  𝔼(L_number_times):  # 𝔻(𝑛·0·1)"+                       , "    𝔻(ξ.ρ):"+                       , "      𝕄(ξ.ρ):"+                       , "        𝑛·2·1 := Φ.number( φ ↦ 𝜎1:λ )"+                       , "        morphed 𝑛·2·2 := 𝑛·1·16"+                       , "      boxed 𝑛·2·3 := 𝑛·2·2.φ"+                       , "      𝛿·2·1 := 𝔻(𝜎1:λ)"+                       , "    𝛿1·2 := 𝛿·2·1  # 𝔻(ξ.ρ)"+                       , "    𝔻(ξ.x):"+                       , "      𝕄(ξ.x):"+                       , "        𝑛·2·4 := 7.0"+                       , "        morphed 𝑛·2·5 := 𝑛·2·4"+                       , "      boxed 𝑛·2·6 := 𝑛·2·5.φ"+                       , "      𝕄(𝑛·2·6):"+                       , "        𝑛·2·7 := Φ.bytes( φ ↦ 40-1C-00-00-00-00-00-00:Δ )"+                       , "        morphed 𝑛·2·8 := 𝑛·2·7"+                       , "      boxed 𝑛·2·9 := 𝑛·2·8.φ"+                       , "      𝛿·2·2 := 40-1C-00-00-00-00-00-00  # 7.0"+                       , "    𝛿2·2 := 𝛿·2·2  # 𝔻(ξ.x)"+                       , "    𝑛·2·10 := Φ.number( φ ↦ 𝜎2:λ )  # 𝑛"+                       , "    𝕄(𝑛·2·10):"+                       , "      𝑛·2·11 := Φ.number( φ ↦ 𝜎2:λ )"+                       , "      morphed 𝑛·2·12 := 𝑛·2·11"+                       , "    𝑛·2·13 := 𝑛·2·12  # 𝕄(𝑛·2·10)"+                       , "  boxed 𝑛·0·6 := 𝑛·2·13.φ"+                       , "  𝛿·0·1 := 𝔻(𝜎2:λ)"+                       ]++      it "nests the firing an operand of another firing brought down" $+        withTempFile "protocolXXXXXX.txt" $ \(path, stream) -> do+          hClose stream+          withStdin nested $+            testCLISucceeded ["dataize", symbolic, "--protocol=" ++ path, "--quiet", "--sweet", "--hide-rho"] []+          records <- readProtocol path+          lines records+            `shouldBe` [ "𝔻(Φ):"+                       , "  boxed 𝑛·0·1 := Φ.φ"+                       , "  𝕄(𝑛·0·1):"+                       , "    𝑛·0·2 := 5.0"+                       , "    𝑛·0·3 := 𝑛·0·2.plus( x ↦ 6.0.plus( 7.0 ) )"+                       , "    morphed 𝑛·0·4 := 𝑛·0·3"+                       , "  𝔼(L_number_plus):  # 𝔻(𝑛·0·1)"+                       , "    𝔻(ξ.ρ):"+                       , "      𝕄(ξ.ρ):"+                       , "        𝑛·1·1 := 5.0"+                       , "        morphed 𝑛·1·2 := 𝑛·1·1"+                       , "      boxed 𝑛·1·3 := 𝑛·1·2.φ"+                       , "      𝕄(𝑛·1·3):"+                       , "        𝑛·1·4 := Φ.bytes( φ ↦ 40-14-00-00-00-00-00-00:Δ )"+                       , "        morphed 𝑛·1·5 := 𝑛·1·4"+                       , "      boxed 𝑛·1·6 := 𝑛·1·5.φ"+                       , "      𝛿·1·1 := 40-14-00-00-00-00-00-00  # 5.0"+                       , "    𝛿1·1 := 𝛿·1·1  # 𝔻(ξ.ρ)"+                       , "    𝔻(ξ.x):"+                       , "      𝕄(ξ.x):"+                       , "        𝑛·1·7 := 6.0"+                       , "        𝑛·1·8 := 𝑛·1·7.plus( x ↦ 7.0 )"+                       , "        morphed 𝑛·1·9 := 𝑛·1·8"+                       , "      𝔼(L_number_plus):"+                       , "        𝔻(ξ.ρ):"+                       , "          𝕄(ξ.ρ):"+                       , "            𝑛·2·1 := 6.0"+                       , "            morphed 𝑛·2·2 := 𝑛·2·1"+                       , "          boxed 𝑛·2·3 := 𝑛·2·2.φ"+                       , "          𝕄(𝑛·2·3):"+                       , "            𝑛·2·4 := Φ.bytes( φ ↦ 40-18-00-00-00-00-00-00:Δ )"+                       , "            morphed 𝑛·2·5 := 𝑛·2·4"+                       , "          boxed 𝑛·2·6 := 𝑛·2·5.φ"+                       , "          𝛿·2·1 := 40-18-00-00-00-00-00-00  # 6.0"+                       , "        𝛿1·2 := 𝛿·2·1  # 𝔻(ξ.ρ)"+                       , "        𝔻(ξ.x):"+                       , "          𝕄(ξ.x):"+                       , "            𝑛·2·7 := 7.0"+                       , "            morphed 𝑛·2·8 := 𝑛·2·7"+                       , "          boxed 𝑛·2·9 := 𝑛·2·8.φ"+                       , "          𝕄(𝑛·2·9):"+                       , "            𝑛·2·10 := Φ.bytes( φ ↦ 40-1C-00-00-00-00-00-00:Δ )"+                       , "            morphed 𝑛·2·11 := 𝑛·2·10"+                       , "          boxed 𝑛·2·12 := 𝑛·2·11.φ"+                       , "          𝛿·2·2 := 40-1C-00-00-00-00-00-00  # 7.0"+                       , "        𝛿2·2 := 𝛿·2·2  # 𝔻(ξ.x)"+                       , "        𝑛·2·13 := Φ.number( φ ↦ 𝜎1:λ )  # 𝑛"+                       , "        𝕄(𝑛·2·13):"+                       , "          𝑛·2·14 := Φ.number( φ ↦ 𝜎1:λ )"+                       , "          morphed 𝑛·2·15 := 𝑛·2·14"+                       , "        𝑛·2·16 := 𝑛·2·15  # 𝕄(𝑛·2·13)"+                       , "      boxed 𝑛·1·10 := 𝑛·2·16.φ"+                       , "      𝛿·1·2 := 𝔻(𝜎1:λ)"+                       , "    𝛿2·1 := 𝛿·1·2  # 𝔻(ξ.x)"+                       , "    𝑛·1·11 := Φ.number( φ ↦ 𝜎2:λ )  # 𝑛"+                       , "    𝕄(𝑛·1·11):"+                       , "      𝑛·1·12 := Φ.number( φ ↦ 𝜎2:λ )"+                       , "      morphed 𝑛·1·13 := 𝑛·1·12"+                       , "    𝑛·1·14 := 𝑛·1·13  # 𝕄(𝑛·1·11)"+                       , "  boxed 𝑛·0·5 := 𝑛·1·14.φ"+                       , "  𝛿·0·1 := 𝔻(𝜎2:λ)"+                       ]++      it "writes a stuck firing to the XML protocol" $+        withTempFile "protocolXXXXXX.xml" $ \(path, stream) -> do+          hClose stream+          withLambdasOf (T.pack "- λ: L_outer\n  dataize:\n    𝛿1: ξ.arg\n  𝑛: ⟦ λ ⤍ 𝜎 ⟧\n") $ \outer ->+            withStdin "⟦ x ↦ ⟦ arg ↦ ⟦ λ ⤍ L_absent ⟧, λ ⤍ L_outer ⟧.φ ⟧" $+              testCLISucceeded ["morph", "--symbolic=" ++ outer, "--locator=Q.x", "--partial", "--protocol=" ++ path, "--quiet", "--sweet", "--hide-rho"] []+          records <- readProtocol path+          lines records `shouldContain` ["    <stuck λ=\"L_absent\"/>"]++      it "writes a starved step budget to the XML protocol" $+        withTempFile "protocolXXXXXX.xml" $ \(path, stream) -> do+          hClose stream+          withLambdasOf (T.pack "- λ: L_outer\n  dataize:\n    𝛿1: ξ.arg\n  𝑛: ⟦ λ ⤍ 𝜎 ⟧\n") $ \outer ->+            withStdin "⟦ x ↦ ⟦ arg ↦ ⟦ φ ↦ ⟦ φ ↦ ⟦ Δ ⤍ 07- ⟧ ⟧ ⟧, λ ⤍ L_outer ⟧ ⟧" $+              testCLISucceeded ["dataize", "--symbolic=" ++ outer, "--locator=Q.x", "--partial", "--max-steps=3", "--protocol=" ++ path, "--quiet", "--sweet", "--hide-rho", "--flat"] []+          records <- readProtocol path+          lines records `shouldContain` ["      <starved limit=\"3\" by=\"dataize\" at=\"𝑛·1·2\"/>"]++      forM_+        [("XMLXXXXXX.xml", "<spent limit=\"5\" by="), ("textXXXXXX.txt", "  spent 5")]+        ( \(template, record) ->+            it ("writes a spent firing budget to the protocol as " ++ record) $+              withTempFile template $ \(path, stream) -> do+                hClose stream+                loopingLambdas $ \endless ->+                  withStdin "⟦ @ ↦ ⟦ λ ⤍ L_loop ⟧ ⟧" $+                    testCLIFailed ["dataize", "--symbolic=" ++ endless, "--max-steps=400", "--max-firings=5", "--protocol=" ++ path] ["--max-firings=5"]+                records <- readProtocol path+                any (record `isInfixOf`) (lines records) `shouldBe` True+        )++      it "keeps the lines of a run that fails" $+        withTempFile "protocolXXXXXX.txt" $ \(path, stream) -> do+          hClose stream+          withStdin "[[ bytes ↦ ⟦ φ ↦ ∅ ⟧, number(φ) -> [[ plus(^, x) -> [[ L> L_number_plus ]], nope -> [[ ^ -> ?, L> L_number_nope ]] ]], @ -> 5.plus(6).nope ]]" $+            testCLIFailed+              ["dataize", symbolic, "--protocol=" ++ path, "--quiet", "--sweet", "--hide-rho"]+              ["No entry of --symbolic answers the λ function 'L_number_nope'"]+          records <- readProtocol path+          lines records+            `shouldBe` [ "𝔻(Φ):"+                       , "  boxed 𝑛·0·1 := Φ.φ"+                       , "  𝕄(𝑛·0·1):"+                       , "    𝑛·0·2 := 5.0"+                       , "    𝑛·0·3 := 𝑛·0·2.plus( x ↦ 6.0 )"+                       , "    𝔼(L_number_plus):"+                       , "      𝔻(ξ.ρ):"+                       , "        𝕄(ξ.ρ):"+                       , "          𝑛·1·1 := 5.0"+                       , "          morphed 𝑛·1·2 := 𝑛·1·1"+                       , "        boxed 𝑛·1·3 := 𝑛·1·2.φ"+                       , "        𝕄(𝑛·1·3):"+                       , "          𝑛·1·4 := Φ.bytes( φ ↦ 40-14-00-00-00-00-00-00:Δ )"+                       , "          morphed 𝑛·1·5 := 𝑛·1·4"+                       , "        boxed 𝑛·1·6 := 𝑛·1·5.φ"+                       , "        𝛿·1·1 := 40-14-00-00-00-00-00-00  # 5.0"+                       , "      𝛿1·1 := 𝛿·1·1  # 𝔻(ξ.ρ)"+                       , "      𝔻(ξ.x):"+                       , "        𝕄(ξ.x):"+                       , "          𝑛·1·7 := 6.0"+                       , "          morphed 𝑛·1·8 := 𝑛·1·7"+                       , "        boxed 𝑛·1·9 := 𝑛·1·8.φ"+                       , "        𝕄(𝑛·1·9):"+                       , "          𝑛·1·10 := Φ.bytes( φ ↦ 40-18-00-00-00-00-00-00:Δ )"+                       , "          morphed 𝑛·1·11 := 𝑛·1·10"+                       , "        boxed 𝑛·1·12 := 𝑛·1·11.φ"+                       , "        𝛿·1·2 := 40-18-00-00-00-00-00-00  # 6.0"+                       , "      𝛿2·1 := 𝛿·1·2  # 𝔻(ξ.x)"+                       , "      𝑛·1·13 := Φ.number( φ ↦ 𝜎1:λ )  # 𝑛"+                       , "      𝕄(𝑛·1·13):"+                       , "        𝑛·1·14 := Φ.number( φ ↦ 𝜎1:λ )"+                       , "        morphed 𝑛·1·15 := 𝑛·1·14"+                       , "      𝑛·1·16 := 𝑛·1·15  # 𝕄(𝑛·1·13)"+                       , "    morphed 𝑛·0·4 := L_number_nope:λ"+                       , "  unanswered L_number_nope  # 𝔻(L_number_nope:λ)"+                       ]++      it "truncates the lines left over from the previous run" $+        withTempFileContent "protocolXXXXXX.txt" "𝔼(L_number_gt)\n" $ \path -> do+          withStdin "[[ D> 01- ]]" $+            testCLISucceeded ["dataize", "--protocol=" ++ path, "--quiet"] []+          records <- readProtocol path+          records `shouldBe` "𝔻(Φ):\n  𝛿·0·1 := 01-\n"++      it "writes the lines in 𝜑 even with --output=xmir" $+        withTempFile "protocolXXXXXX.txt" $ \(path, stream) -> do+          hClose stream+          withStdin sum' $+            testCLISucceeded ["dataize", symbolic, "--protocol=" ++ path, "--output=xmir", "--quiet", "--sweet", "--hide-rho"] []+          records <- readProtocol path+          records `shouldEndWith` "    𝕄(𝑛·1·13):\n      𝑛·1·14 := Φ.number( φ ↦ 𝜎1:λ )\n      morphed 𝑛·1·15 := 𝑛·1·14\n    𝑛·1·16 := 𝑛·1·15  # 𝕄(𝑛·1·13)\n  boxed 𝑛·0·5 := 𝑛·1·16.φ\n  𝛿·0·1 := 𝔻(𝜎1:λ)\n"++      it "numbers every step in the text as the XML of the same run numbers it" $+        withTempFile "protocolXXXXXX.txt" $ \(text, one) ->+          withTempFile "protocolXXXXXX.xml" $ \(xml, two) -> do+            hClose one+            hClose two+            withStdin nested $+              testCLISucceeded ["dataize", symbolic, "--protocol=" ++ text, "--quiet", "--sweet", "--hide-rho"] []+            withStdin nested $+              testCLISucceeded ["dataize", symbolic, "--protocol=" ++ xml, "--quiet", "--sweet", "--hide-rho"] []+            lined <- readProtocol text+            marked <- readProtocol xml+            [meta | meta : ":=" : _ <- map (dropWhile (`elem` ["boxed", "morphed", "fabricated"]) . words) (lines lined), "𝑛·" `isPrefixOf` meta]+              `shouldBe` [takeWhile (/= '"') (drop 6 token) | token <- words marked, "meta=\"𝑛·" `isPrefixOf` token]++      describe "as XML" $ do+        it "writes the document when the file is named .xml" $+          withTempFile "protocolXXXXXX.xml" $ \(path, stream) -> do+            hClose stream+            withStdin sum' $+              testCLISucceeded ["dataize", symbolic, "--protocol=" ++ path, "--quiet", "--sweet", "--hide-rho"] []+            records <- readProtocol path+            lines records+              `shouldBe` [ "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"+                         , "<dataize at=\"Φ\">"+                         , "  <boxed meta=\"𝑛·0·1\">Φ.φ</boxed>"+                         , "  <morph at=\"𝑛·0·1\">"+                         , "    <applied meta=\"𝑒·0·1\" of=\"Φ.bytes\"><datum attr=\"φ\">40-14-00-00-00-00-00-00</datum></applied>"+                         , "    <applied meta=\"𝑛·0·2\" by=\"morph\" at=\"𝑛·0·1\" of=\"Φ.number\"><meta attr=\"φ\">𝑒·0·1</meta></applied>"+                         , "    <applied meta=\"𝑒·0·2\" of=\"Φ.bytes\"><datum attr=\"φ\">40-18-00-00-00-00-00-00</datum></applied>"+                         , "    <applied meta=\"𝑒·0·3\" of=\"Φ.number\"><meta attr=\"φ\">𝑒·0·2</meta></applied>"+                         , "    <applied meta=\"𝑛·0·3\" by=\"morph\" at=\"𝑛·0·1\" of=\"𝑛·0·2.plus\"><meta attr=\"x\">𝑒·0·3</meta></applied>"+                         , "    <morphed meta=\"𝑛·0·4\">𝑛·0·3</morphed>"+                         , "  </morph>"+                         , "  <evaluate λ=\"L_number_plus\" by=\"dataize\" at=\"𝑛·0·1\">"+                         , "    <dataize at=\"ξ.ρ\">"+                         , "      <morph at=\"ξ.ρ\">"+                         , "        <applied meta=\"𝑛·1·1\" by=\"morph\" at=\"ξ.ρ\" of=\"Φ.number\"><meta attr=\"φ\">𝑒·0·1</meta></applied>"+                         , "        <morphed meta=\"𝑛·1·2\">𝑛·1·1</morphed>"+                         , "      </morph>"+                         , "      <boxed meta=\"𝑛·1·3\">𝑛·1·2.φ</boxed>"+                         , "      <morph at=\"𝑛·1·3\">"+                         , "        <applied meta=\"𝑛·1·4\" by=\"morph\" at=\"𝑛·1·3\" of=\"Φ.bytes\"><datum attr=\"φ\">40-14-00-00-00-00-00-00</datum></applied>"+                         , "        <morphed meta=\"𝑛·1·5\">𝑛·1·4</morphed>"+                         , "      </morph>"+                         , "      <boxed meta=\"𝑛·1·6\">𝑛·1·5.φ</boxed>"+                         , "      <dataized meta=\"𝛿·1·1\" number=\"5.0\">40-14-00-00-00-00-00-00</dataized>"+                         , "    </dataize>"+                         , "    <bound meta=\"𝛿1·1\">𝛿·1·1</bound>"+                         , "    <dataize at=\"ξ.x\">"+                         , "      <morph at=\"ξ.x\">"+                         , "        <applied meta=\"𝑛·1·7\" by=\"morph\" at=\"ξ.x\" of=\"Φ.number\"><meta attr=\"φ\">𝑒·0·2</meta></applied>"+                         , "        <morphed meta=\"𝑛·1·8\">𝑛·1·7</morphed>"+                         , "      </morph>"+                         , "      <boxed meta=\"𝑛·1·9\">𝑛·1·8.φ</boxed>"+                         , "      <morph at=\"𝑛·1·9\">"+                         , "        <applied meta=\"𝑛·1·10\" by=\"morph\" at=\"𝑛·1·9\" of=\"Φ.bytes\"><datum attr=\"φ\">40-18-00-00-00-00-00-00</datum></applied>"+                         , "        <morphed meta=\"𝑛·1·11\">𝑛·1·10</morphed>"+                         , "      </morph>"+                         , "      <boxed meta=\"𝑛·1·12\">𝑛·1·11.φ</boxed>"+                         , "      <dataized meta=\"𝛿·1·2\" number=\"6.0\">40-18-00-00-00-00-00-00</dataized>"+                         , "    </dataize>"+                         , "    <bound meta=\"𝛿2·1\">𝛿·1·2</bound>"+                         , "    <minted symbol=\"𝜎1\">40-14-00-00-00-00-00-00 40-18-00-00-00-00-00-00</minted>"+                         , "    <applied meta=\"𝑒·1·1\" of=\"Φ.number\"><symbol attr=\"φ\">𝜎1</symbol></applied>"+                         , "    <built meta=\"𝑛·1·13\">𝑒·1·1</built>"+                         , "    <morph at=\"𝑛·1·13\">"+                         , "      <applied meta=\"𝑛·1·14\" by=\"morph\" at=\"𝑛·1·13\" of=\"Φ.number\"><symbol attr=\"φ\">𝜎1</symbol></applied>"+                         , "      <morphed meta=\"𝑛·1·15\">𝑛·1·14</morphed>"+                         , "    </morph>"+                         , "    <answered meta=\"𝑛·1·16\">𝑛·1·15</answered>"+                         , "  </evaluate>"+                         , "  <boxed meta=\"𝑛·0·5\">𝑛·1·16.φ</boxed>"+                         , "  <manufactured meta=\"𝛿·0·1\">𝜎1</manufactured>"+                         , "</dataize>"+                         ]++        it "nests the judgment right inside the '<protocol>' root" $+          withTempFile "protocolXXXXXX.xml" $ \(path, stream) -> do+            hClose stream+            withStdin "[[ D> 01- ]]" $+              testCLISucceeded ["dataize", "--protocol=" ++ path, "--quiet"] []+            records <- readUtf8 path+            take 4 (lines records)+              `shouldBe` [ "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"+                         , "<protocol>"+                         , "  <dataize at=\"Φ\">"+                         , "    <dataized meta=\"𝛿·0·1\">01-</dataized>"+                         ]++        it "closes the '<protocol>' root with its msec" $+          withTempFile "protocolXXXXXX.xml" $ \(path, stream) -> do+            hClose stream+            withStdin "[[ D> 01- ]]" $+              testCLISucceeded ["dataize", "--protocol=" ++ path, "--quiet"] []+            records <- readUtf8 path+            (lines records !! 5) `shouldSatisfy` isPrefixOf "  <msec>"++        it "closes the '<protocol>' root with its firings" $+          withTempFile "protocolXXXXXX.xml" $ \(path, stream) -> do+            hClose stream+            withStdin "[[ D> 01- ]]" $+              testCLISucceeded ["dataize", "--protocol=" ++ path, "--quiet"] []+            records <- readUtf8 path+            lines records `shouldContain` ["  <firings>0</firings>"]++        it "closes the '<protocol>' root with its fps, then '</protocol>' itself" $+          withTempFile "protocolXXXXXX.xml" $ \(path, stream) -> do+            hClose stream+            withStdin "[[ D> 01- ]]" $+              testCLISucceeded ["dataize", "--protocol=" ++ path, "--quiet"] []+            records <- readUtf8 path+            drop 7 (lines records) `shouldBe` ["  <fps>0</fps>", "</protocol>"]++        it "nests what a φ body dataizes inside the element of the box rule that entered it" $+          withTempFile "protocolXXXXXX.xml" $ \(path, stream) -> do+            hClose stream+            withStdin sum' $+              testCLISucceeded ["dataize", symbolic, "--protocol=" ++ path, "--quiet", "--sweet", "--hide-rho"] []+            records <- readProtocol path+            filter (\line -> not (any (`isInfixOf` line) ["<applied ", "<morph ", "<morphed ", "</morph>"])) (lines records)+              `shouldContain` [ "    <dataize at=\"ξ.ρ\">"+                              , "      <boxed meta=\"𝑛·1·3\">𝑛·1·2.φ</boxed>"+                              , "      <boxed meta=\"𝑛·1·6\">𝑛·1·5.φ</boxed>"+                              , "      <dataized meta=\"𝛿·1·1\" number=\"5.0\">40-14-00-00-00-00-00-00</dataized>"+                              , "    </dataize>"+                              ]++        it "closes the document even when nothing fires" $+          withTempFile "protocolXXXXXX.xml" $ \(path, stream) -> do+            hClose stream+            withStdin "[[ D> 01- ]]" $+              testCLISucceeded ["dataize", "--protocol=" ++ path, "--quiet"] []+            records <- readProtocol path+            lines records+              `shouldBe` [ "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"+                         , "<dataize at=\"Φ\">"+                         , "  <dataized meta=\"𝛿·0·1\">01-</dataized>"+                         , "</dataize>"+                         ]++        it "tells a manufactured datum from data by the name of the element" $+          withTempFile "protocolXXXXXX.xml" $ \(path, stream) -> do+            hClose stream+            withStdin chained $+              testCLISucceeded ["dataize", symbolic, "--protocol=" ++ path, "--quiet", "--sweet", "--hide-rho"] []+            records <- readProtocol path+            lines records+              `shouldBe` [ "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"+                         , "<dataize at=\"Φ\">"+                         , "  <boxed meta=\"𝑛·0·1\">Φ.φ</boxed>"+                         , "  <morph at=\"𝑛·0·1\">"+                         , "    <applied meta=\"𝑒·0·1\" of=\"Φ.bytes\"><datum attr=\"φ\">40-14-00-00-00-00-00-00</datum></applied>"+                         , "    <applied meta=\"𝑛·0·2\" by=\"morph\" at=\"𝑛·0·1\" of=\"Φ.number\"><meta attr=\"φ\">𝑒·0·1</meta></applied>"+                         , "    <applied meta=\"𝑒·0·2\" of=\"Φ.bytes\"><datum attr=\"φ\">40-18-00-00-00-00-00-00</datum></applied>"+                         , "    <applied meta=\"𝑒·0·3\" of=\"Φ.number\"><meta attr=\"φ\">𝑒·0·2</meta></applied>"+                         , "    <applied meta=\"𝑛·0·3\" by=\"morph\" at=\"𝑛·0·1\" of=\"𝑛·0·2.plus\"><meta attr=\"x\">𝑒·0·3</meta></applied>"+                         , "    <evaluate λ=\"L_number_plus\" by=\"morph\" at=\"𝑛·0·1\">"+                         , "      <dataize at=\"ξ.ρ\">"+                         , "        <morph at=\"ξ.ρ\">"+                         , "          <applied meta=\"𝑛·1·1\" by=\"morph\" at=\"ξ.ρ\" of=\"Φ.number\"><meta attr=\"φ\">𝑒·0·1</meta></applied>"+                         , "          <morphed meta=\"𝑛·1·2\">𝑛·1·1</morphed>"+                         , "        </morph>"+                         , "        <boxed meta=\"𝑛·1·3\">𝑛·1·2.φ</boxed>"+                         , "        <morph at=\"𝑛·1·3\">"+                         , "          <applied meta=\"𝑛·1·4\" by=\"morph\" at=\"𝑛·1·3\" of=\"Φ.bytes\"><datum attr=\"φ\">40-14-00-00-00-00-00-00</datum></applied>"+                         , "          <morphed meta=\"𝑛·1·5\">𝑛·1·4</morphed>"+                         , "        </morph>"+                         , "        <boxed meta=\"𝑛·1·6\">𝑛·1·5.φ</boxed>"+                         , "        <dataized meta=\"𝛿·1·1\" number=\"5.0\">40-14-00-00-00-00-00-00</dataized>"+                         , "      </dataize>"+                         , "      <bound meta=\"𝛿1·1\">𝛿·1·1</bound>"+                         , "      <dataize at=\"ξ.x\">"+                         , "        <morph at=\"ξ.x\">"+                         , "          <applied meta=\"𝑛·1·7\" by=\"morph\" at=\"ξ.x\" of=\"Φ.number\"><meta attr=\"φ\">𝑒·0·2</meta></applied>"+                         , "          <morphed meta=\"𝑛·1·8\">𝑛·1·7</morphed>"+                         , "        </morph>"+                         , "        <boxed meta=\"𝑛·1·9\">𝑛·1·8.φ</boxed>"+                         , "        <morph at=\"𝑛·1·9\">"+                         , "          <applied meta=\"𝑛·1·10\" by=\"morph\" at=\"𝑛·1·9\" of=\"Φ.bytes\"><datum attr=\"φ\">40-18-00-00-00-00-00-00</datum></applied>"+                         , "          <morphed meta=\"𝑛·1·11\">𝑛·1·10</morphed>"+                         , "        </morph>"+                         , "        <boxed meta=\"𝑛·1·12\">𝑛·1·11.φ</boxed>"+                         , "        <dataized meta=\"𝛿·1·2\" number=\"6.0\">40-18-00-00-00-00-00-00</dataized>"+                         , "      </dataize>"+                         , "      <bound meta=\"𝛿2·1\">𝛿·1·2</bound>"+                         , "      <minted symbol=\"𝜎1\">40-14-00-00-00-00-00-00 40-18-00-00-00-00-00-00</minted>"+                         , "      <applied meta=\"𝑒·1·1\" of=\"Φ.number\"><symbol attr=\"φ\">𝜎1</symbol></applied>"+                         , "      <built meta=\"𝑛·1·13\">𝑒·1·1</built>"+                         , "      <morph at=\"𝑛·1·13\">"+                         , "        <applied meta=\"𝑛·1·14\" by=\"morph\" at=\"𝑛·1·13\" of=\"Φ.number\"><symbol attr=\"φ\">𝜎1</symbol></applied>"+                         , "        <morphed meta=\"𝑛·1·15\">𝑛·1·14</morphed>"+                         , "      </morph>"+                         , "      <answered meta=\"𝑛·1·16\">𝑛·1·15</answered>"+                         , "    </evaluate>"+                         , "    <applied meta=\"𝑒·0·4\" of=\"Φ.bytes\"><datum attr=\"φ\">40-1C-00-00-00-00-00-00</datum></applied>"+                         , "    <applied meta=\"𝑒·0·5\" of=\"Φ.number\"><meta attr=\"φ\">𝑒·0·4</meta></applied>"+                         , "    <applied meta=\"𝑛·0·4\" by=\"morph\" at=\"𝑛·0·1\" of=\"𝑛·1·14.plus\"><meta attr=\"x\">𝑒·0·5</meta></applied>"+                         , "    <morphed meta=\"𝑛·0·5\">𝑛·0·4</morphed>"+                         , "  </morph>"+                         , "  <evaluate λ=\"L_number_plus\" by=\"dataize\" at=\"𝑛·0·1\">"+                         , "    <dataize at=\"ξ.ρ\">"+                         , "      <morph at=\"ξ.ρ\">"+                         , "        <applied meta=\"𝑛·2·1\" by=\"morph\" at=\"ξ.ρ\" of=\"Φ.number\"><symbol attr=\"φ\">𝜎1</symbol></applied>"+                         , "        <morphed meta=\"𝑛·2·2\">𝑛·2·1</morphed>"+                         , "      </morph>"+                         , "      <boxed meta=\"𝑛·2·3\">𝑛·2·2.φ</boxed>"+                         , "      <manufactured meta=\"𝛿·2·1\">𝜎1</manufactured>"+                         , "    </dataize>"+                         , "    <bound meta=\"𝛿1·2\">𝛿·2·1</bound>"+                         , "    <dataize at=\"ξ.x\">"+                         , "      <morph at=\"ξ.x\">"+                         , "        <applied meta=\"𝑛·2·4\" by=\"morph\" at=\"ξ.x\" of=\"Φ.number\"><meta attr=\"φ\">𝑒·0·4</meta></applied>"+                         , "        <morphed meta=\"𝑛·2·5\">𝑛·2·4</morphed>"+                         , "      </morph>"+                         , "      <boxed meta=\"𝑛·2·6\">𝑛·2·5.φ</boxed>"+                         , "      <morph at=\"𝑛·2·6\">"+                         , "        <applied meta=\"𝑛·2·7\" by=\"morph\" at=\"𝑛·2·6\" of=\"Φ.bytes\"><datum attr=\"φ\">40-1C-00-00-00-00-00-00</datum></applied>"+                         , "        <morphed meta=\"𝑛·2·8\">𝑛·2·7</morphed>"+                         , "      </morph>"+                         , "      <boxed meta=\"𝑛·2·9\">𝑛·2·8.φ</boxed>"+                         , "      <dataized meta=\"𝛿·2·2\" number=\"7.0\">40-1C-00-00-00-00-00-00</dataized>"+                         , "    </dataize>"+                         , "    <bound meta=\"𝛿2·2\">𝛿·2·2</bound>"+                         , "    <minted symbol=\"𝜎2\">𝜎1 40-1C-00-00-00-00-00-00</minted>"+                         , "    <applied meta=\"𝑒·2·1\" of=\"Φ.number\"><symbol attr=\"φ\">𝜎2</symbol></applied>"+                         , "    <built meta=\"𝑛·2·10\">𝑒·2·1</built>"+                         , "    <morph at=\"𝑛·2·10\">"+                         , "      <applied meta=\"𝑛·2·11\" by=\"morph\" at=\"𝑛·2·10\" of=\"Φ.number\"><symbol attr=\"φ\">𝜎2</symbol></applied>"+                         , "      <morphed meta=\"𝑛·2·12\">𝑛·2·11</morphed>"+                         , "    </morph>"+                         , "    <answered meta=\"𝑛·2·13\">𝑛·2·12</answered>"+                         , "  </evaluate>"+                         , "  <boxed meta=\"𝑛·0·6\">𝑛·2·13.φ</boxed>"+                         , "  <manufactured meta=\"𝛿·0·1\">𝜎2</manufactured>"+                         , "</dataize>"+                         ]++        it "writes a firing whose two sides do not join as diverged, naming both sides" $+          withTempFile "protocolXXXXXX.xml" $ \(path, stream) -> do+            hClose stream+            withLambdasOf (T.pack "- λ: L_fork\n  dataize:\n    𝛿1: $.c\n  join:\n    on: 𝛿1\n    left: $.a\n    right: $.b\n") $ \joins ->+              withStdin "⟦ y ↦ ⟦ c ↦ ⟦ λ ⤍ 𝜎1 ⟧, a ↦ ⟦ φ ↦ ⟦ Δ ⤍ 01- ⟧ ⟧, b ↦ ⟦ φ ↦ ⟦ Δ ⤍ 02- ⟧ ⟧, λ ⤍ L_fork ⟧.φ ⟧" $+                testCLIFailed ["morph", "--symbolic=" ++ joins, "--locator=Q.y", "--protocol=" ++ path, "--quiet", "--sweet", "--hide-rho"] ["No entry of --symbolic answers the λ function 'L_fork'"]+            records <- readProtocol path+            lines records+              `shouldBe` [ "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"+                         , "<morph at=\"Φ.y\">"+                         , "  <evaluate λ=\"L_fork\" by=\"morph\" at=\"Φ.y\">"+                         , "    <dataize at=\"ξ.c\">"+                         , "      <manufactured meta=\"𝛿·1·1\">𝜎1</manufactured>"+                         , "    </dataize>"+                         , "    <bound meta=\"𝛿1·1\">𝛿·1·1</bound>"+                         , "    <morph at=\"ξ.a\">"+                         , "      <formed meta=\"𝑒·1·1\"><datum attr=\"φ\">01-</datum></formed>"+                         , "      <morphed meta=\"𝑛·1·1\">𝑒·1·1</morphed>"+                         , "    </morph>"+                         , "    <morph at=\"ξ.b\">"+                         , "      <formed meta=\"𝑒·1·2\"><datum attr=\"φ\">02-</datum></formed>"+                         , "      <morphed meta=\"𝑛·1·2\">𝑒·1·2</morphed>"+                         , "    </morph>"+                         , "    <diverged left=\"𝑛·1·1\" right=\"𝑛·1·2\" at=\".φ.Δ\" reason=\"the join names no otherwise\"/>"+                         , "  </evaluate>"+                         , "</morph>"+                         ]++        it "writes the symbol a join minted for two sides that differ only in their symbols as an element of its own" $+          withTempFile "protocolXXXXXX.xml" $ \(path, stream) -> do+            hClose stream+            withLambdasOf (T.pack "- λ: L_fork\n  dataize:\n    𝛿1: $.c\n  join:\n    on: 𝛿1\n    left: $.a\n    right: $.b\n") $ \joins ->+              withStdin "⟦ y ↦ ⟦ c ↦ ⟦ λ ⤍ 𝜎1 ⟧, a ↦ ⟦ φ ↦ ⟦ λ ⤍ 𝜎2 ⟧ ⟧, b ↦ ⟦ φ ↦ ⟦ λ ⤍ 𝜎3 ⟧ ⟧, λ ⤍ L_fork ⟧.φ ⟧" $+                testCLISucceeded ["morph", "--symbolic=" ++ joins, "--locator=Q.y", "--protocol=" ++ path, "--quiet", "--sweet", "--hide-rho"] []+            records <- readProtocol path+            lines records+              `shouldBe` [ "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"+                         , "<morph at=\"Φ.y\">"+                         , "  <evaluate λ=\"L_fork\" by=\"morph\" at=\"Φ.y\">"+                         , "    <dataize at=\"ξ.c\">"+                         , "      <manufactured meta=\"𝛿·1·1\">𝜎1</manufactured>"+                         , "    </dataize>"+                         , "    <bound meta=\"𝛿1·1\">𝛿·1·1</bound>"+                         , "    <morph at=\"ξ.a\">"+                         , "      <formed meta=\"𝑒·1·1\"><symbol attr=\"φ\">𝜎2</symbol></formed>"+                         , "      <morphed meta=\"𝑛·1·1\">𝑒·1·1</morphed>"+                         , "    </morph>"+                         , "    <morph at=\"ξ.b\">"+                         , "      <formed meta=\"𝑒·1·2\"><symbol attr=\"φ\">𝜎3</symbol></formed>"+                         , "      <morphed meta=\"𝑛·1·2\">𝑒·1·2</morphed>"+                         , "    </morph>"+                         , "    <joined symbol=\"𝜎4\" on=\"𝛿1·1\" left=\"𝜎2\" right=\"𝜎3\"/>"+                         , "    <formed meta=\"𝑒·1·3\"><symbol attr=\"φ\">𝜎4</symbol></formed>"+                         , "    <answered meta=\"𝑛·1·3\">𝑒·1·3</answered>"+                         , "  </evaluate>"+                         , "  <morphed meta=\"𝑛·0·1\">𝜎4</morphed>"+                         , "</morph>"+                         ]++        it "writes one 'minted' element per symbol the answer asked for" $+          withTempFile "protocolXXXXXX.xml" $ \(path, stream) -> do+            hClose stream+            withLambdasOf (T.pack "- λ: L_pair\n  morph:\n    𝑛1: $.x\n  𝑛: ⟦ left ↦ ⟦ λ ⤍ 𝜎 ⟧, right ↦ ⟦ λ ⤍ 𝜎 ⟧ ⟧\n") $ \pairs ->+              withStdin "⟦ y ↦ ⟦ x ↦ ⟦ Δ ⤍ 01- ⟧, λ ⤍ L_pair ⟧.left ⟧" $+                testCLISucceeded ["morph", "--symbolic=" ++ pairs, "--locator=Q.y", "--protocol=" ++ path, "--quiet", "--sweet", "--hide-rho"] []+            records <- readProtocol path+            lines records+              `shouldBe` [ "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"+                         , "<morph at=\"Φ.y\">"+                         , "  <evaluate λ=\"L_pair\" by=\"morph\" at=\"Φ.y\">"+                         , "    <morph at=\"ξ.x\">"+                         , "      <morphed meta=\"𝑛·1·1\">01-</morphed>"+                         , "    </morph>"+                         , "    <bound meta=\"𝑛1·1\">01-</bound>"+                         , "    <minted symbol=\"𝜎1\"/>"+                         , "    <minted symbol=\"𝜎2\"/>"+                         , "    <formed meta=\"𝑒·1·1\"><symbol attr=\"left\">𝜎1</symbol><symbol attr=\"right\">𝜎2</symbol></formed>"+                         , "    <built meta=\"𝑛·1·2\">𝑒·1·1</built>"+                         , "    <morph at=\"𝑛·1·2\">"+                         , "      <morphed meta=\"𝑛·1·3\">𝑒·1·1</morphed>"+                         , "    </morph>"+                         , "    <answered meta=\"𝑛·1·4\">𝑛·1·3</answered>"+                         , "  </evaluate>"+                         , "  <morphed meta=\"𝑛·0·1\">𝜎1</morphed>"+                         , "</morph>"+                         ]++        it "writes no 'minted' element for a firing minting nothing" $+          withTempFile "protocolXXXXXX.xml" $ \(path, stream) -> do+            hClose stream+            withLambdasOf (T.pack "- λ: L_keep\n  morph:\n    𝑛1: $.x\n  𝑛: ⟦ z ↦ 𝑛1 ⟧\n") $ \keeps ->+              withStdin "⟦ y ↦ ⟦ x ↦ ⟦ Δ ⤍ 01- ⟧, λ ⤍ L_keep ⟧.z ⟧" $+                testCLISucceeded ["morph", "--symbolic=" ++ keeps, "--locator=Q.y", "--protocol=" ++ path, "--quiet", "--sweet", "--hide-rho"] []+            records <- readProtocol path+            lines records+              `shouldBe` [ "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"+                         , "<morph at=\"Φ.y\">"+                         , "  <evaluate λ=\"L_keep\" by=\"morph\" at=\"Φ.y\">"+                         , "    <morph at=\"ξ.x\">"+                         , "      <morphed meta=\"𝑛·1·1\">01-</morphed>"+                         , "    </morph>"+                         , "    <bound meta=\"𝑛1·1\">01-</bound>"+                         , "    <formed meta=\"𝑒·1·1\"><datum attr=\"z\">01-</datum></formed>"+                         , "    <built meta=\"𝑛·1·2\">𝑒·1·1</built>"+                         , "    <morph at=\"𝑛·1·2\">"+                         , "      <morphed meta=\"𝑛·1·3\">𝑒·1·1</morphed>"+                         , "    </morph>"+                         , "    <answered meta=\"𝑛·1·4\">𝑛·1·3</answered>"+                         , "  </evaluate>"+                         , "  <morphed meta=\"𝑛·0·1\">01-</morphed>"+                         , "</morph>"+                         ]++        it "nests a firing an operand took inside the firing that asked" $+          withTempFile "protocolXXXXXX.xml" $ \(path, stream) -> do+            hClose stream+            withStdin nested $+              testCLISucceeded ["dataize", symbolic, "--protocol=" ++ path, "--quiet", "--sweet", "--hide-rho"] []+            records <- readProtocol path+            lines records+              `shouldBe` [ "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"+                         , "<dataize at=\"Φ\">"+                         , "  <boxed meta=\"𝑛·0·1\">Φ.φ</boxed>"+                         , "  <morph at=\"𝑛·0·1\">"+                         , "    <applied meta=\"𝑒·0·1\" of=\"Φ.bytes\"><datum attr=\"φ\">40-14-00-00-00-00-00-00</datum></applied>"+                         , "    <applied meta=\"𝑛·0·2\" by=\"morph\" at=\"𝑛·0·1\" of=\"Φ.number\"><meta attr=\"φ\">𝑒·0·1</meta></applied>"+                         , "    <applied meta=\"𝑒·0·2\" of=\"Φ.bytes\"><datum attr=\"φ\">40-18-00-00-00-00-00-00</datum></applied>"+                         , "    <applied meta=\"𝑒·0·3\" of=\"Φ.number\"><meta attr=\"φ\">𝑒·0·2</meta></applied>"+                         , "    <applied meta=\"𝑒·0·4\" of=\"Φ.bytes\"><datum attr=\"φ\">40-1C-00-00-00-00-00-00</datum></applied>"+                         , "    <applied meta=\"𝑒·0·5\" of=\"Φ.number\"><meta attr=\"φ\">𝑒·0·4</meta></applied>"+                         , "    <applied meta=\"𝑒·0·6\" of=\"𝑒·0·3.plus\"><meta attr=\"α0\">𝑒·0·5</meta></applied>"+                         , "    <applied meta=\"𝑛·0·3\" by=\"morph\" at=\"𝑛·0·1\" of=\"𝑛·0·2.plus\"><meta attr=\"x\">𝑒·0·6</meta></applied>"+                         , "    <morphed meta=\"𝑛·0·4\">𝑛·0·3</morphed>"+                         , "  </morph>"+                         , "  <evaluate λ=\"L_number_plus\" by=\"dataize\" at=\"𝑛·0·1\">"+                         , "    <dataize at=\"ξ.ρ\">"+                         , "      <morph at=\"ξ.ρ\">"+                         , "        <applied meta=\"𝑛·1·1\" by=\"morph\" at=\"ξ.ρ\" of=\"Φ.number\"><meta attr=\"φ\">𝑒·0·1</meta></applied>"+                         , "        <morphed meta=\"𝑛·1·2\">𝑛·1·1</morphed>"+                         , "      </morph>"+                         , "      <boxed meta=\"𝑛·1·3\">𝑛·1·2.φ</boxed>"+                         , "      <morph at=\"𝑛·1·3\">"+                         , "        <applied meta=\"𝑛·1·4\" by=\"morph\" at=\"𝑛·1·3\" of=\"Φ.bytes\"><datum attr=\"φ\">40-14-00-00-00-00-00-00</datum></applied>"+                         , "        <morphed meta=\"𝑛·1·5\">𝑛·1·4</morphed>"+                         , "      </morph>"+                         , "      <boxed meta=\"𝑛·1·6\">𝑛·1·5.φ</boxed>"+                         , "      <dataized meta=\"𝛿·1·1\" number=\"5.0\">40-14-00-00-00-00-00-00</dataized>"+                         , "    </dataize>"+                         , "    <bound meta=\"𝛿1·1\">𝛿·1·1</bound>"+                         , "    <dataize at=\"ξ.x\">"+                         , "      <morph at=\"ξ.x\">"+                         , "        <applied meta=\"𝑛·1·7\" by=\"morph\" at=\"ξ.x\" of=\"Φ.number\"><meta attr=\"φ\">𝑒·0·2</meta></applied>"+                         , "        <applied meta=\"𝑛·1·8\" by=\"morph\" at=\"ξ.x\" of=\"𝑛·1·7.plus\"><meta attr=\"x\">𝑒·0·5</meta></applied>"+                         , "        <morphed meta=\"𝑛·1·9\">𝑛·1·8</morphed>"+                         , "      </morph>"+                         , "      <evaluate λ=\"L_number_plus\" by=\"dataize\" at=\"ξ.x\">"+                         , "        <dataize at=\"ξ.ρ\">"+                         , "          <morph at=\"ξ.ρ\">"+                         , "            <applied meta=\"𝑛·2·1\" by=\"morph\" at=\"ξ.ρ\" of=\"Φ.number\"><meta attr=\"φ\">𝑒·0·2</meta></applied>"+                         , "            <morphed meta=\"𝑛·2·2\">𝑛·2·1</morphed>"+                         , "          </morph>"+                         , "          <boxed meta=\"𝑛·2·3\">𝑛·2·2.φ</boxed>"+                         , "          <morph at=\"𝑛·2·3\">"+                         , "            <applied meta=\"𝑛·2·4\" by=\"morph\" at=\"𝑛·2·3\" of=\"Φ.bytes\"><datum attr=\"φ\">40-18-00-00-00-00-00-00</datum></applied>"+                         , "            <morphed meta=\"𝑛·2·5\">𝑛·2·4</morphed>"+                         , "          </morph>"+                         , "          <boxed meta=\"𝑛·2·6\">𝑛·2·5.φ</boxed>"+                         , "          <dataized meta=\"𝛿·2·1\" number=\"6.0\">40-18-00-00-00-00-00-00</dataized>"+                         , "        </dataize>"+                         , "        <bound meta=\"𝛿1·2\">𝛿·2·1</bound>"+                         , "        <dataize at=\"ξ.x\">"+                         , "          <morph at=\"ξ.x\">"+                         , "            <applied meta=\"𝑛·2·7\" by=\"morph\" at=\"ξ.x\" of=\"Φ.number\"><meta attr=\"φ\">𝑒·0·4</meta></applied>"+                         , "            <morphed meta=\"𝑛·2·8\">𝑛·2·7</morphed>"+                         , "          </morph>"+                         , "          <boxed meta=\"𝑛·2·9\">𝑛·2·8.φ</boxed>"+                         , "          <morph at=\"𝑛·2·9\">"+                         , "            <applied meta=\"𝑛·2·10\" by=\"morph\" at=\"𝑛·2·9\" of=\"Φ.bytes\"><datum attr=\"φ\">40-1C-00-00-00-00-00-00</datum></applied>"+                         , "            <morphed meta=\"𝑛·2·11\">𝑛·2·10</morphed>"+                         , "          </morph>"+                         , "          <boxed meta=\"𝑛·2·12\">𝑛·2·11.φ</boxed>"+                         , "          <dataized meta=\"𝛿·2·2\" number=\"7.0\">40-1C-00-00-00-00-00-00</dataized>"+                         , "        </dataize>"+                         , "        <bound meta=\"𝛿2·2\">𝛿·2·2</bound>"+                         , "        <minted symbol=\"𝜎1\">40-18-00-00-00-00-00-00 40-1C-00-00-00-00-00-00</minted>"+                         , "        <applied meta=\"𝑒·2·1\" of=\"Φ.number\"><symbol attr=\"φ\">𝜎1</symbol></applied>"+                         , "        <built meta=\"𝑛·2·13\">𝑒·2·1</built>"+                         , "        <morph at=\"𝑛·2·13\">"+                         , "          <applied meta=\"𝑛·2·14\" by=\"morph\" at=\"𝑛·2·13\" of=\"Φ.number\"><symbol attr=\"φ\">𝜎1</symbol></applied>"+                         , "          <morphed meta=\"𝑛·2·15\">𝑛·2·14</morphed>"+                         , "        </morph>"+                         , "        <answered meta=\"𝑛·2·16\">𝑛·2·15</answered>"+                         , "      </evaluate>"+                         , "      <boxed meta=\"𝑛·1·10\">𝑛·2·16.φ</boxed>"+                         , "      <manufactured meta=\"𝛿·1·2\">𝜎1</manufactured>"+                         , "    </dataize>"+                         , "    <bound meta=\"𝛿2·1\">𝛿·1·2</bound>"+                         , "    <minted symbol=\"𝜎2\">40-14-00-00-00-00-00-00 𝜎1</minted>"+                         , "    <applied meta=\"𝑒·1·1\" of=\"Φ.number\"><symbol attr=\"φ\">𝜎2</symbol></applied>"+                         , "    <built meta=\"𝑛·1·11\">𝑒·1·1</built>"+                         , "    <morph at=\"𝑛·1·11\">"+                         , "      <applied meta=\"𝑛·1·12\" by=\"morph\" at=\"𝑛·1·11\" of=\"Φ.number\"><symbol attr=\"φ\">𝜎2</symbol></applied>"+                         , "      <morphed meta=\"𝑛·1·13\">𝑛·1·12</morphed>"+                         , "    </morph>"+                         , "    <answered meta=\"𝑛·1·14\">𝑛·1·13</answered>"+                         , "  </evaluate>"+                         , "  <boxed meta=\"𝑛·0·5\">𝑛·1·14.φ</boxed>"+                         , "  <manufactured meta=\"𝛿·0·1\">𝜎2</manufactured>"+                         , "</dataize>"+                         ]++        it "records a λ function no entry answers as a childless element" $+          withTempFile "protocolXXXXXX.xml" $ \(path, stream) -> do+            hClose stream+            withStdin "[[ bytes ↦ ⟦ φ ↦ ∅ ⟧, number(φ) -> [[ times(^, x) -> [[ L> L_number_times ]], nope -> [[ ^ -> ?, L> L_number_nope ]] ]], @ -> 2.times(3).nope ]]" $+              testCLISucceeded ["dataize", symbolic, "--partial", "--protocol=" ++ path, "--quiet", "--sweet", "--hide-rho"] []+            records <- readProtocol path+            lines records+              `shouldBe` [ "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"+                         , "<dataize at=\"Φ\">"+                         , "  <boxed meta=\"𝑛·0·1\">Φ.φ</boxed>"+                         , "  <morph at=\"𝑛·0·1\">"+                         , "    <applied meta=\"𝑒·0·1\" of=\"Φ.bytes\"><datum attr=\"φ\">40-00-00-00-00-00-00-00</datum></applied>"+                         , "    <applied meta=\"𝑛·0·2\" by=\"morph\" at=\"𝑛·0·1\" of=\"Φ.number\"><meta attr=\"φ\">𝑒·0·1</meta></applied>"+                         , "    <applied meta=\"𝑒·0·2\" of=\"Φ.bytes\"><datum attr=\"φ\">40-08-00-00-00-00-00-00</datum></applied>"+                         , "    <applied meta=\"𝑒·0·3\" of=\"Φ.number\"><meta attr=\"φ\">𝑒·0·2</meta></applied>"+                         , "    <applied meta=\"𝑛·0·3\" by=\"morph\" at=\"𝑛·0·1\" of=\"𝑛·0·2.times\"><meta attr=\"x\">𝑒·0·3</meta></applied>"+                         , "    <evaluate λ=\"L_number_times\" by=\"morph\" at=\"𝑛·0·1\">"+                         , "      <dataize at=\"ξ.ρ\">"+                         , "        <morph at=\"ξ.ρ\">"+                         , "          <applied meta=\"𝑛·1·1\" by=\"morph\" at=\"ξ.ρ\" of=\"Φ.number\"><meta attr=\"φ\">𝑒·0·1</meta></applied>"+                         , "          <morphed meta=\"𝑛·1·2\">𝑛·1·1</morphed>"+                         , "        </morph>"+                         , "        <boxed meta=\"𝑛·1·3\">𝑛·1·2.φ</boxed>"+                         , "        <morph at=\"𝑛·1·3\">"+                         , "          <applied meta=\"𝑛·1·4\" by=\"morph\" at=\"𝑛·1·3\" of=\"Φ.bytes\"><datum attr=\"φ\">40-00-00-00-00-00-00-00</datum></applied>"+                         , "          <morphed meta=\"𝑛·1·5\">𝑛·1·4</morphed>"+                         , "        </morph>"+                         , "        <boxed meta=\"𝑛·1·6\">𝑛·1·5.φ</boxed>"+                         , "        <dataized meta=\"𝛿·1·1\" number=\"2.0\">40-00-00-00-00-00-00-00</dataized>"+                         , "      </dataize>"+                         , "      <bound meta=\"𝛿1·1\">𝛿·1·1</bound>"+                         , "      <dataize at=\"ξ.x\">"+                         , "        <morph at=\"ξ.x\">"+                         , "          <applied meta=\"𝑛·1·7\" by=\"morph\" at=\"ξ.x\" of=\"Φ.number\"><meta attr=\"φ\">𝑒·0·2</meta></applied>"+                         , "          <morphed meta=\"𝑛·1·8\">𝑛·1·7</morphed>"+                         , "        </morph>"+                         , "        <boxed meta=\"𝑛·1·9\">𝑛·1·8.φ</boxed>"+                         , "        <morph at=\"𝑛·1·9\">"+                         , "          <applied meta=\"𝑛·1·10\" by=\"morph\" at=\"𝑛·1·9\" of=\"Φ.bytes\"><datum attr=\"φ\">40-08-00-00-00-00-00-00</datum></applied>"+                         , "          <morphed meta=\"𝑛·1·11\">𝑛·1·10</morphed>"+                         , "        </morph>"+                         , "        <boxed meta=\"𝑛·1·12\">𝑛·1·11.φ</boxed>"+                         , "        <dataized meta=\"𝛿·1·2\" number=\"3.0\">40-08-00-00-00-00-00-00</dataized>"+                         , "      </dataize>"+                         , "      <bound meta=\"𝛿2·1\">𝛿·1·2</bound>"+                         , "      <minted symbol=\"𝜎1\">40-00-00-00-00-00-00-00 40-08-00-00-00-00-00-00</minted>"+                         , "      <applied meta=\"𝑒·1·1\" of=\"Φ.number\"><symbol attr=\"φ\">𝜎1</symbol></applied>"+                         , "      <built meta=\"𝑛·1·13\">𝑒·1·1</built>"+                         , "      <morph at=\"𝑛·1·13\">"+                         , "        <applied meta=\"𝑛·1·14\" by=\"morph\" at=\"𝑛·1·13\" of=\"Φ.number\"><symbol attr=\"φ\">𝜎1</symbol></applied>"+                         , "        <morphed meta=\"𝑛·1·15\">𝑛·1·14</morphed>"+                         , "      </morph>"+                         , "      <answered meta=\"𝑛·1·16\">𝑛·1·15</answered>"+                         , "    </evaluate>"+                         , "    <morphed meta=\"𝑛·0·4\">𝑛·1·14.nope</morphed>"+                         , "  </morph>"+                         , "  <unanswered λ=\"L_number_nope\" by=\"dataize\">𝑛·1·14.nope</unanswered>"+                         , "</dataize>"+                         ]++        it "names the root after the judgment a morphing ran" $+          withTempFile "protocolXXXXXX.xml" $ \(path, stream) -> do+            hClose stream+            withStdin "[[ x -> [[ L> L_number_nope ]].foo ]]" $+              testCLISucceeded ["morph", "--locator=Q.x", "--partial", "--protocol=" ++ path, "--quiet"] []+            records <- readProtocol path+            lines records+              `shouldBe` [ "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"+                         , "<morph at=\"Φ.x\">"+                         , "  <formed meta=\"𝑒·0·1\" λ=\"L_number_nope\"/>"+                         , "  <unanswered λ=\"L_number_nope\" by=\"morph\">𝑒·0·1</unanswered>"+                         , "</morph>"+                         ]++        it "closes the document even when the run fails" $+          withTempFile "protocolXXXXXX.xml" $ \(path, stream) -> do+            hClose stream+            withStdin "[[ bytes ↦ ⟦ φ ↦ ∅ ⟧, number(φ) -> [[ times(^, x) -> [[ L> L_number_times ]], nope -> [[ ^ -> ?, L> L_number_nope ]] ]], @ -> 2.times(3).nope ]]" $+              testCLIFailed ["dataize", symbolic, "--protocol=" ++ path] ["No entry of --symbolic answers"]+            records <- readProtocol path+            lines records+              `shouldBe` [ "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"+                         , "<dataize at=\"Φ\">"+                         , "  <boxed meta=\"𝑛·0·1\">Φ.φ</boxed>"+                         , "  <morph at=\"𝑛·0·1\">"+                         , "    <applied meta=\"𝑒·0·1\" of=\"Φ.bytes\"><datum attr=\"φ\">40-00-00-00-00-00-00-00</datum></applied>"+                         , "    <applied meta=\"𝑛·0·2\" by=\"morph\" at=\"𝑛·0·1\" of=\"Φ.number\"><meta attr=\"φ\">𝑒·0·1</meta></applied>"+                         , "    <applied meta=\"𝑒·0·2\" of=\"Φ.bytes\"><datum attr=\"φ\">40-08-00-00-00-00-00-00</datum></applied>"+                         , "    <applied meta=\"𝑒·0·3\" of=\"Φ.number\"><meta attr=\"φ\">𝑒·0·2</meta></applied>"+                         , "    <applied meta=\"𝑛·0·3\" by=\"morph\" at=\"𝑛·0·1\" of=\"𝑛·0·2.times\"><meta attr=\"x\">𝑒·0·3</meta></applied>"+                         , "    <evaluate λ=\"L_number_times\" by=\"morph\" at=\"𝑛·0·1\">"+                         , "      <dataize at=\"ξ.ρ\">"+                         , "        <morph at=\"ξ.ρ\">"+                         , "          <applied meta=\"𝑛·1·1\" by=\"morph\" at=\"ξ.ρ\" of=\"Φ.number\"><meta attr=\"φ\">𝑒·0·1</meta></applied>"+                         , "          <morphed meta=\"𝑛·1·2\">𝑛·1·1</morphed>"+                         , "        </morph>"+                         , "        <boxed meta=\"𝑛·1·3\">𝑛·1·2.φ</boxed>"+                         , "        <morph at=\"𝑛·1·3\">"+                         , "          <applied meta=\"𝑛·1·4\" by=\"morph\" at=\"𝑛·1·3\" of=\"Φ.bytes\"><datum attr=\"φ\">40-00-00-00-00-00-00-00</datum></applied>"+                         , "          <morphed meta=\"𝑛·1·5\">𝑛·1·4</morphed>"+                         , "        </morph>"+                         , "        <boxed meta=\"𝑛·1·6\">𝑛·1·5.φ</boxed>"+                         , "        <dataized meta=\"𝛿·1·1\" number=\"2.0\">40-00-00-00-00-00-00-00</dataized>"+                         , "      </dataize>"+                         , "      <bound meta=\"𝛿1·1\">𝛿·1·1</bound>"+                         , "      <dataize at=\"ξ.x\">"+                         , "        <morph at=\"ξ.x\">"+                         , "          <applied meta=\"𝑛·1·7\" by=\"morph\" at=\"ξ.x\" of=\"Φ.number\"><meta attr=\"φ\">𝑒·0·2</meta></applied>"+                         , "          <morphed meta=\"𝑛·1·8\">𝑛·1·7</morphed>"+                         , "        </morph>"+                         , "        <boxed meta=\"𝑛·1·9\">𝑛·1·8.φ</boxed>"+                         , "        <morph at=\"𝑛·1·9\">"+                         , "          <applied meta=\"𝑛·1·10\" by=\"morph\" at=\"𝑛·1·9\" of=\"Φ.bytes\"><datum attr=\"φ\">40-08-00-00-00-00-00-00</datum></applied>"+                         , "          <morphed meta=\"𝑛·1·11\">𝑛·1·10</morphed>"+                         , "        </morph>"+                         , "        <boxed meta=\"𝑛·1·12\">𝑛·1·11.φ</boxed>"+                         , "        <dataized meta=\"𝛿·1·2\" number=\"3.0\">40-08-00-00-00-00-00-00</dataized>"+                         , "      </dataize>"+                         , "      <bound meta=\"𝛿2·1\">𝛿·1·2</bound>"+                         , "      <minted symbol=\"𝜎1\">40-00-00-00-00-00-00-00 40-08-00-00-00-00-00-00</minted>"+                         , "      <applied meta=\"𝑒·1·1\" of=\"Φ.number\"><symbol attr=\"φ\">𝜎1</symbol></applied>"+                         , "      <built meta=\"𝑛·1·13\">𝑒·1·1</built>"+                         , "      <morph at=\"𝑛·1·13\">"+                         , "        <applied meta=\"𝑛·1·14\" by=\"morph\" at=\"𝑛·1·13\" of=\"Φ.number\"><symbol attr=\"φ\">𝜎1</symbol></applied>"+                         , "        <morphed meta=\"𝑛·1·15\">𝑛·1·14</morphed>"+                         , "      </morph>"+                         , "      <answered meta=\"𝑛·1·16\">𝑛·1·15</answered>"+                         , "    </evaluate>"+                         , "    <morphed meta=\"𝑛·0·4\">𝑛·1·14.nope</morphed>"+                         , "  </morph>"+                         , "  <unanswered λ=\"L_number_nope\" by=\"dataize\">𝑛·1·14.nope</unanswered>"+                         , "</dataize>"+                         ]++        it "writes the terminator as the term a meta was bound to" $+          withTempFile "protocolXXXXXX.xml" $ \(path, stream) -> do+            hClose stream+            withLambdasOf (T.pack "- λ: L_pick\n  morph:\n    𝑛1: ξ.absent\n  𝑛: ⟦ λ ⤍ 𝜎 ⟧\n") $ \picks ->+              withStdin "[[ x -> [[ here -> [[ ]], L> L_pick ]].foo ]]" $+                testCLIFailed ["morph", "--symbolic=" ++ picks, "--locator=Q.x", "--protocol=" ++ path, "--quiet", "--hide-rho"] ["No entry of --symbolic answers the λ function '𝜎1'"]+            records <- readProtocol path+            lines records+              `shouldBe` [ "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"+                         , "<morph at=\"Φ.x\">"+                         , "  <evaluate λ=\"L_pick\" by=\"morph\" at=\"Φ.x\">"+                         , "    <morph at=\"ξ.absent\">"+                         , "      <morphed meta=\"𝑛·1·1\">⊥</morphed>"+                         , "    </morph>"+                         , "    <bound meta=\"𝑛1·1\">⊥</bound>"+                         , "    <minted symbol=\"𝜎1\"/>"+                         , "    <built meta=\"𝑛·1·2\">𝜎1</built>"+                         , "    <morph at=\"𝑛·1·2\">"+                         , "      <morphed meta=\"𝑛·1·3\">𝜎1</morphed>"+                         , "    </morph>"+                         , "    <answered meta=\"𝑛·1·4\">𝜎1</answered>"+                         , "  </evaluate>"+                         , "  <unanswered λ=\"𝜎1\" by=\"morph\">𝜎1</unanswered>"+                         , "</morph>"+                         ]++        it "keeps writing text when the file is named anything else" $+          withTempFile "protocolXXXXXX.xmir" $ \(path, stream) -> do+            hClose stream+            withStdin sum' $+              testCLISucceeded ["dataize", symbolic, "--protocol=" ++ path, "--quiet", "--sweet", "--hide-rho"] []+            records <- readProtocol path+            take 1 (lines records) `shouldBe` ["𝔻(Φ):"]++    describe "--partial" $ do+      let stuck = "[[ bytes ↦ ⟦ φ ↦ ∅ ⟧, number(φ) -> [[ times(^, x) -> [[ L> L_number_times ]], nope -> [[ ^ -> ?, L> L_number_nope ]] ]], @ -> 2.times(3).nope ]]"+          dispatched = "[[ foo -> [[ bar -> [[ ^ -> ?, L> L_number_nope ]] ]], @ -> Q.foo.bar ]]"+          wrapped = "[[ app -> [[ foo -> [[ bar -> [[ ^ -> ?, L> L_number_nope ]] ]], @ -> Q.app.foo.bar ]] ]]"+      it "fails on a λ function that cannot fire without the flag" $+        withStdin stuck $+          testCLIFailed+            ["dataize", symbolic, "--sweet", "--hide-rho"]+            ["No entry of --symbolic answers the λ function 'L_number_nope'"]++      it "prints the residue with the stuck application intact and exits successfully" $+        withStdin stuck $+          testCLISucceeded+            ["dataize", symbolic, "--partial", "--sweet", "--hide-rho"]+            ["L_number_nope:λ"]++      it "keeps what was evaluated before the stuck site in the residue" $+        withStdin stuck $+          testCLISucceeded+            ["dataize", symbolic, "--partial", "--sweet"]+            ["φ ↦ 𝜎1:λ"]++      it "records every firing before the stuck site in --protocol" $+        withTempFile "protocolXXXXXX.txt" $ \(path, stream) -> do+          hClose stream+          withStdin stuck $+            testCLISucceeded ["dataize", symbolic, "--partial", "--protocol=" ++ path, "--quiet", "--sweet", "--hide-rho"] []+          records <- readProtocol path+          lines records+            `shouldBe` [ "𝔻(Φ):"+                       , "  boxed 𝑛·0·1 := Φ.φ"+                       , "  𝕄(𝑛·0·1):"+                       , "    𝑛·0·2 := 2.0"+                       , "    𝑛·0·3 := 𝑛·0·2.times( x ↦ 3.0 )"+                       , "    𝔼(L_number_times):"+                       , "      𝔻(ξ.ρ):"+                       , "        𝕄(ξ.ρ):"+                       , "          𝑛·1·1 := 2.0"+                       , "          morphed 𝑛·1·2 := 𝑛·1·1"+                       , "        boxed 𝑛·1·3 := 𝑛·1·2.φ"+                       , "        𝕄(𝑛·1·3):"+                       , "          𝑛·1·4 := Φ.bytes( φ ↦ 40-00-00-00-00-00-00-00:Δ )"+                       , "          morphed 𝑛·1·5 := 𝑛·1·4"+                       , "        boxed 𝑛·1·6 := 𝑛·1·5.φ"+                       , "        𝛿·1·1 := 40-00-00-00-00-00-00-00  # 2.0"+                       , "      𝛿1·1 := 𝛿·1·1  # 𝔻(ξ.ρ)"+                       , "      𝔻(ξ.x):"+                       , "        𝕄(ξ.x):"+                       , "          𝑛·1·7 := 3.0"+                       , "          morphed 𝑛·1·8 := 𝑛·1·7"+                       , "        boxed 𝑛·1·9 := 𝑛·1·8.φ"+                       , "        𝕄(𝑛·1·9):"+                       , "          𝑛·1·10 := Φ.bytes( φ ↦ 40-08-00-00-00-00-00-00:Δ )"+                       , "          morphed 𝑛·1·11 := 𝑛·1·10"+                       , "        boxed 𝑛·1·12 := 𝑛·1·11.φ"+                       , "        𝛿·1·2 := 40-08-00-00-00-00-00-00  # 3.0"+                       , "      𝛿2·1 := 𝛿·1·2  # 𝔻(ξ.x)"+                       , "      𝑛·1·13 := Φ.number( φ ↦ 𝜎1:λ )  # 𝑛"+                       , "      𝕄(𝑛·1·13):"+                       , "        𝑛·1·14 := Φ.number( φ ↦ 𝜎1:λ )"+                       , "        morphed 𝑛·1·15 := 𝑛·1·14"+                       , "      𝑛·1·16 := 𝑛·1·15  # 𝕄(𝑛·1·13)"+                       , "    morphed 𝑛·0·4 := L_number_nope:λ"+                       , "  unanswered L_number_nope  # 𝔻(L_number_nope:λ)"+                       ]++      it "still prints bytes when nothing gets stuck" $+        withStdin "[[ bytes ↦ ⟦ φ ↦ ∅ ⟧, number(φ) -> [[ plus(^, x) -> [[ L> L_number_plus ]] ]], @ -> 5.plus(6) ]]" $+          testCLISucceeded ["dataize", symbolic, "--partial"] ["40-45-00-00-00-00-00-00"]++      it "prints the residual at --locator as XMIR, with ρ hidden" $+        withStdin wrapped $+          testCLISucceeded+            ["dataize", symbolic, "--partial", "--locator=Q.app", "--output=xmir", "--hide-rho"]+            ["<o name=\"λ\">L_number_nope</o>"]++      it "prints the residual at --locator, not the whole program" $+        withStdin wrapped $ do+          (out, _) <- withStdout (runCLI ["dataize", symbolic, "--partial", "--locator=Q.app", "--hide-rho", "--flat"])+          lines out `shouldBe` ["⟦ λ ⤍ L_number_nope ⟧"]++      it "prints a residual of several top bindings as XMIR" $+        withStdin dispatched $+          testCLISucceeded+            ["dataize", symbolic, "--partial", "--output=xmir"]+            ["<o base=\"Φ.foo\" name=\"ρ\"/>", "<o name=\"λ\">L_number_nope</o>"]++      it "prints the chain of steps ending in the residue with --sequence" $+        withStdin stuck $+          testCLISucceeded+            ["dataize", symbolic, "--partial", "--sequence", "--sweet", "--hide-rho", "--flat"]+            ["2.0.times( 3.0 ).nope", "L_number_nope:λ"]++      it "prints the terminator ⊥ the object reduces to as the residue" $+        withStdin "[[ ]]" $+          testCLISucceeded ["dataize", "--partial"] ["⊥"]++    describe "--symbolic" $ do+      let sum' = "[[ bytes ↦ ⟦ φ ↦ ∅ ⟧, number(φ) -> [[ plus(^, x) -> [[ L> L_number_plus ]] ]], @ -> 5.plus(6) ]]"+      it "fires the λ function an entry of the file answers" $+        withStdin sum' $+          testCLISucceeded ["dataize", symbolic] ["40-45-00-00-00-00-00-00"]++      it "reports the progress of the run with --log-level=INFO" $+        withStdin sum' $+          testCLISucceeded ["dataize", symbolic, "--log-level=INFO", "--quiet"] ["[INFO]: Started "]++      it "gets stuck on every λ function when it is not given" $+        withStdin sum' $+          testCLIFailed ["dataize"] ["No entry of --symbolic answers the λ function 'L_number_plus'"]++      it "fails when the file is not there" $+        withStdin sum' $+          testCLIFailed ["dataize", "--symbolic=no-such-file.yaml"] ["no-such-file.yaml"]++      it "fails on a file that carries no entries at all, before dataizing anything" $+        withTempFileContent "symbolicXXXXXX.yaml" "nope: true\n" $ \path ->+          withStdin sum' $+            testCLIFailed ["dataize", "--symbolic=" ++ path] ["cannot be read"]++    describe "a join on a condition nobody decided" $ do+      let joining = T.pack "- λ: L_fork\n  dataize:\n    𝛿1: $.c\n  join:\n    on: 𝛿1\n    left: $.a\n    right: $.b\n"+          dataized :: String -> IO String+          dataized program =+            withLambdasOf joining $ \joins ->+              withStdin program $+                fst <$> withStdout (runCLI ["dataize", "--symbolic=" ++ joins, "--locator=Q.y"])+      it "gets stuck on two sides that do not join, instead of splitting the run" $+        withLambdasOf joining $ \joins ->+          withStdin "⟦ y ↦ ⟦ c ↦ ⟦ λ ⤍ 𝜎1 ⟧, a ↦ ⟦ Δ ⤍ 01- ⟧, b ↦ ⟦ Δ ⤍ 02- ⟧, λ ⤍ L_fork ⟧ ⟧" $+            testCLIFailed ["dataize", "--symbolic=" ++ joins, "--locator=Q.y"] ["No entry of --symbolic answers the λ function 'L_fork'"]++      it "gets stuck on a side whose morph got stuck" $+        withLambdasOf joining $ \joins ->+          withStdin "⟦ y ↦ ⟦ c ↦ ⟦ λ ⤍ 𝜎1 ⟧, a ↦ ⟦ Δ ⤍ 05- ⟧, b ↦ ⟦ λ ⤍ L_none ⟧.x, λ ⤍ L_fork ⟧ ⟧" $+            testCLIFailed ["dataize", "--symbolic=" ++ joins, "--locator=Q.y"] ["No entry of --symbolic answers the λ function 'L_none'"]++      it "dataizes two sides that come to the same term" $ do+        printed <- dataized "⟦ y ↦ ⟦ c ↦ ⟦ λ ⤍ 𝜎1 ⟧, a ↦ ⟦ Δ ⤍ 07- ⟧, b ↦ ⟦ Δ ⤍ 07- ⟧, λ ⤍ L_fork ⟧ ⟧"+        lines printed `shouldBe` ["07-"]++      it "dataizes two sides that differ only in their symbols" $ do+        printed <- dataized "⟦ y ↦ ⟦ c ↦ ⟦ λ ⤍ 𝜎1 ⟧, a ↦ ⟦ φ ↦ ⟦ λ ⤍ 𝜎2 ⟧ ⟧, b ↦ ⟦ φ ↦ ⟦ λ ⤍ 𝜎3 ⟧ ⟧, λ ⤍ L_fork ⟧ ⟧"+        length (lines printed) `shouldBe` 1++    describe "--inside" $ do+      let universe = "[[ bytes ↦ ⟦ φ ↦ ∅ ⟧, number(φ) -> [[ plus(^, x) -> [[ L> L_number_plus ]] ]], @ -> [[ D> 01- ]] ]]"+      it "dataizes an expression the input does not contain" $+        withStdin universe $+          testCLISucceeded ["dataize", symbolic, "--inside=5.plus( 6 )"] ["40-45-00-00-00-00-00-00"]++      it "normalizes what it is handed before dataizing it" $+        withStdin universe $+          testCLISucceeded ["dataize", "--inside=[[ x -> [[ D> 2A- ]] ]].x"] ["2A-"]++      it "morphs inside the universe just as it dataizes inside it" $+        withStdin universe $+          testCLISucceeded ["morph", symbolic, "--inside=5.plus( 6 )", "--sweet", "--hide-rho", "--flat"] ["⟦ x ↦ 6.0, λ ⤍ L_number_plus ⟧"]++      it "opens the text protocol before the objects it made while aiming" $+        withTempFile "protocolXXXXXX.txt" $ \(path, stream) -> do+          hClose stream+          withStdin universe $+            testCLISucceeded ["morph", symbolic, "--inside=5.plus( 6 )", "--protocol=" ++ path, "--sweet", "--hide-rho", "--quiet"] []+          records <- readProtocol path+          takeWhile (/= '(') (concat (take 1 (lines records))) `shouldBe` "𝕄"++      it "opens the XML protocol before the objects it made while aiming" $+        withTempFile "protocolXXXXXX.xml" $ \(path, stream) -> do+          hClose stream+          withStdin universe $+            testCLISucceeded ["dataize", symbolic, "--inside=5.plus( 6 )", "--protocol=" ++ path, "--sweet", "--hide-rho", "--quiet"] []+          records <- readProtocol path+          take 1 (lines records) `shouldBe` ["<?xml version=\"1.0\" encoding=\"UTF-8\"?>"]++      it "sites what it made while aiming at the place it put the expression at" $+        withTempFile "protocolXXXXXX.txt" $ \(path, stream) -> do+          hClose stream+          withStdin universe $+            testCLISucceeded ["dataize", "--inside=[[ x -> ? ]]( x -> [[ D> 2A- ]] ).x", "--protocol=" ++ path, "--sweet", "--quiet"] []+          records <- readProtocol path+          take 2 (lines records) `shouldBe` ["𝔻(Φ.a🌵0):", "  𝑛·0·1 := ∅:x( x ↦ 2A-:Δ )"]++      it "cannot be used together with --locator" $+        withStdin universe $+          testCLIFailed ["dataize", "--inside=Q.@", "--locator=Q.@"] ["--inside and --locator cannot be used together"]++      it "accepts --locator=Φ with --inside, since Φ and Q both name the root" $+        withStdin universe $+          testCLISucceeded ["dataize", "--inside=[[ x -> [[ D> 2A- ]] ]].x", "--locator=Φ"] ["2A-"]++      it "accepts --locator=Φ with morph --inside, since Φ and Q both name the root" $+        withStdin universe $+          testCLISucceeded ["morph", symbolic, "--inside=5.plus( 6 )", "--locator=Φ", "--sweet", "--hide-rho", "--flat"] ["⟦ x ↦ 6.0, λ ⤍ L_number_plus ⟧"]++      it "fails when the input expression is not a formation" $+        withStdin "Q.x" $+          testCLIFailed ["dataize", "--inside=Q.x"] ["--inside requires the input expression to be a formation"]++    describe "fails" $ do+      it "with --output != latex and --nonumber" $+        withStdin "" $+          testCLIFailed+            ["dataize", "--nonumber", "--output=xmir"]+            ["The --nonumber option can stay together with --output=latex only"]++      it "with --omit-listing and --output != xmir" $+        withStdin "" $+          testCLIFailed+            ["dataize", "--omit-listing", "--output=phi"]+            ["--omit-listing"]++      it "with --omit-comments and --output != xmir" $+        withStdin "" $+          testCLIFailed+            ["dataize", "--omit-comments", "--output=phi"]+            ["--omit-comments"]++      it "with --expression and --output != latex" $+        withStdin "" $+          testCLIFailed+            ["dataize", "--expression=foo", "--output=phi"]+            ["--expression option can stay together with --output=latex only"]++      it "with --label and --output != latex" $+        withStdin "" $+          testCLIFailed+            ["dataize", "--label=foo", "--output=phi"]+            ["--label option can stay together with --output=latex only"]++      it "with wrong --hide option" $+        withStdin "" $+          testCLIFailed+            ["dataize", "--hide=Q.x(Q.y)"]+            ["[ERROR]: Invalid set of arguments: Only dispatch expression", "but given: Φ.x( Φ.y )"]++      it "with wrong --show option" $+        withStdin "" $+          testCLIFailed+            ["dataize", "--show=Q.x(Q.y)"]+            ["[ERROR]:", "Only dispatch expression started with Φ (or Q) can be used in --show"]++      it "with wrong --locator option" $+        withStdin "" $+          testCLIFailed+            ["dataize", "--locator=Q.x(Q.y)"]+            ["[ERROR]:", "Only dispatch expression started with Φ (or Q) can be used in --locator"]++      it "with wrong --focus option" $+        withStdin "" $+          testCLIFailed+            ["dataize", "--focus=Q.x(Q.y)"]+            ["[ERROR]:", "Only dispatch expression started with Φ (or Q) can be used in --focus"]++      it "with wrong --locator option written through ξ, keeping ξ in the error" $+        withStdin "" $+          testCLIFailed+            ["dataize", "--locator=ξ.x"]+            ["[ERROR]:", "but given: ξ.x"]++    it "accepts --depth-sensitive" $+      withStdin "[[ D> 01- ]]" $+        testCLISucceeded ["dataize", "--depth-sensitive"] ["01-"]++    it "finishes exactly on --max-cycles without reporting the limit" $+      withStdin "⟦ φ ↦ ⟦ Δ ⤍ 01- ⟧ ⟧" $+        testCLISucceeded ["dataize", "--max-cycles=1"] ["01-"]++    it "reports --max-cycles exceeded when a rule still applies at the limit" $+      withStdin "[[ @ -> [[ x -> [[ D> 00- ]] ]].x ]]" $+        testCLIFailed+          ["dataize", "--max-cycles=1"]+          ["[ERROR]: Normalization did not finish before reaching the limit of cycles: --max-cycles=1"]++  describe "morph" $ do+    let chained = "[[ bytes ↦ ⟦ φ ↦ ∅ ⟧, number(φ) -> [[ plus(^, x) -> [[ L> L_number_plus ]] ]], @ -> 5.plus(6).plus(7) ]]"+    it "prints help" $+      testCLISucceeded ["morph", "--help"] ["Morph the 𝜑-expression"]++    it "hands the top formation back untouched under the default locator" $+      withStdin "[[ D> 01- ]]" $+        testCLISucceeded ["morph", "--flat", "--hide-rho"] ["⟦ Δ ⤍ 01- ⟧"]++    it "stops at the bare saturated λ-formation" $+      withStdin chained $+        testCLISucceeded+          ["morph", symbolic, "--locator=Q.@", "--sweet", "--hide-rho", "--flat"]+          ["⟦ x ↦ 7.0, λ ⤍ L_number_plus ⟧"]++    it "leaves to dataize the firing that takes the same term to bytes" $+      withStdin chained $+        testCLISucceeded ["dataize", symbolic] ["40-45-00-00-00-00-00-00"]++    it "morphs the subterm --locator aims at" $+      withStdin "[[ ex -> Q.x, x -> [[ D> 42- ]] ]]" $+        testCLISucceeded ["morph", "--locator=Q.ex", "--flat", "--hide-rho"] ["⟦ Δ ⤍ 42- ⟧"]++    it "canonizes the answer it prints" $+      withStdin "[[ x -> [[ L> Foo ]], y -> [[ L> Bar ]] ]]" $+        testCLISucceeded ["morph", "--canonize", "--flat", "--sweet"] ["⟦ x ↦ Fn1:λ, y ↦ Fn2:λ ⟧"]++    it "hides a binding of the answer it prints" $+      withStdin "[[ x -> [[ L> Foo ]], y -> [[ L> Bar ]] ]]" $+        testCLISucceeded ["morph", "--hide=Q.x", "--flat", "--sweet"] ["Bar:λ:y"]++    it "shows only one binding of the answer it prints" $+      withStdin "[[ x -> [[ L> Foo ]], y -> [[ L> Bar ]] ]]" $+        testCLISucceeded ["morph", "--show=Q.x", "--flat", "--sweet"] ["Foo:λ:x"]++    it "prints ⊥ instead of failing the run" $+      withStdin "[[ x -> $ ]]" $+        testCLISucceeded ["morph", "--locator=Q.x"] ["⊥"]++    it "fails to dataize what it morphs to ⊥" $+      withStdin "[[ x -> $ ]]" $+        testCLIFailed ["dataize", "--locator=Q.x"] ["terminator ⊥"]++    it "prints the chain of morphing steps with --sequence" $+      withStdin chained $+        testCLISucceeded+          ["morph", symbolic, "--locator=Q.@", "--sequence", "--headers", "--sweet", "--hide-rho", "--flat"]+          [ "Rule 'maa'"+          , "Rule 'alpha'"+          , "Rule 'copy'"+          , "Rule 'mf'"+          , "⟦ x ↦ 7.0, λ ⤍ L_number_plus ⟧"+          ]++    it "writes every step of a LaTeX --sequence with the arrow of its judgment" $+      withStdin "[[ q -> [[ ]], k -> Q.q ]]" $+        testCLISucceeded+          ["morph", "--locator=Q.k", "--sequence", "--output=latex", "--flat", "--sweet", "--quiet"]+          [ intercalate+              "\n"+              [ "\\begin{phiquation}"+              , "[[ |q| -> [[]], |k| -> Q . |q| ]] \\phiMorph[\\nameref{r:md}]"+              , "  \\phiMorph [[ |q| -> [[]], |k| -> [[ |q| -> [[]], |k| -> Q . |q| ]] . |q| ]] \\phiNormalize[\\nameref{r:dot}]"+              , "  \\phiNormalize [[ |q| -> [[]], |k| -> [[]] ( \\phiTerminal{\\rho} -> Q ) ]] \\phiNormalize[\\nameref{r:skip}]"+              , "  \\phiNormalize [[ |q| -> [[]], |k| -> [[]] ]] \\phiMorph[\\nameref{r:mf}]"+              , "  \\phiMorph [[ |q| -> [[]], |k| -> [[]] ]]{.}"+              , "\\end{phiquation}"+              ]+          ]++    it "does not print the result with --quiet" $+      withStdin "[[ D> 01- ]]" $+        testCLISucceeded ["morph", "--quiet"] []++    it "records the λ functions it fires with --protocol" $+      withTempFile "protocolXXXXXX.txt" $ \(path, stream) -> do+        hClose stream+        withStdin chained $+          testCLISucceeded ["morph", symbolic, "--locator=Q.@", "--protocol=" ++ path, "--quiet", "--sweet", "--hide-rho"] []+        records <- readProtocol path+        lines records+          `shouldBe` [ "𝕄(Φ.φ):"+                     , "  𝑛·0·1 := 5.0"+                     , "  𝑛·0·2 := 𝑛·0·1.plus( x ↦ 6.0 )"+                     , "  𝔼(L_number_plus):"+                     , "    𝔻(ξ.ρ):"+                     , "      𝕄(ξ.ρ):"+                     , "        𝑛·1·1 := 5.0"+                     , "        morphed 𝑛·1·2 := 𝑛·1·1"+                     , "      boxed 𝑛·1·3 := 𝑛·1·2.φ"+                     , "      𝕄(𝑛·1·3):"+                     , "        𝑛·1·4 := Φ.bytes( φ ↦ 40-14-00-00-00-00-00-00:Δ )"+                     , "        morphed 𝑛·1·5 := 𝑛·1·4"+                     , "      boxed 𝑛·1·6 := 𝑛·1·5.φ"+                     , "      𝛿·1·1 := 40-14-00-00-00-00-00-00  # 5.0"+                     , "    𝛿1·1 := 𝛿·1·1  # 𝔻(ξ.ρ)"+                     , "    𝔻(ξ.x):"+                     , "      𝕄(ξ.x):"+                     , "        𝑛·1·7 := 6.0"+                     , "        morphed 𝑛·1·8 := 𝑛·1·7"+                     , "      boxed 𝑛·1·9 := 𝑛·1·8.φ"+                     , "      𝕄(𝑛·1·9):"+                     , "        𝑛·1·10 := Φ.bytes( φ ↦ 40-18-00-00-00-00-00-00:Δ )"+                     , "        morphed 𝑛·1·11 := 𝑛·1·10"+                     , "      boxed 𝑛·1·12 := 𝑛·1·11.φ"+                     , "      𝛿·1·2 := 40-18-00-00-00-00-00-00  # 6.0"+                     , "    𝛿2·1 := 𝛿·1·2  # 𝔻(ξ.x)"+                     , "    𝑛·1·13 := Φ.number( φ ↦ 𝜎1:λ )  # 𝑛"+                     , "    𝕄(𝑛·1·13):"+                     , "      𝑛·1·14 := Φ.number( φ ↦ 𝜎1:λ )"+                     , "      morphed 𝑛·1·15 := 𝑛·1·14"+                     , "    𝑛·1·16 := 𝑛·1·15  # 𝕄(𝑛·1·13)"+                     , "  𝑛·0·3 := 𝑛·1·14.plus( x ↦ 7.0 )"+                     , "  morphed 𝑛·0·4 := 𝑛·0·3"+                     ]++    it "saves morphing steps to dir with --steps-dir" $+      withTempDirectory "phino-steps-morph" $ \dir ->+        withStdin chained $ do+          testCLISucceeded+            ["morph", symbolic, "--locator=Q.@", "--steps-dir=" ++ dir, "--sweet", "--hide-rho", "--flat"]+            ["⟦ x ↦ 7.0, λ ⤍ L_number_plus ⟧"]+          steps <- sort <$> listDirectory dir+          steps `shouldBe` map (\n -> printf "%05d.phi" (n :: Int)) [1 .. length steps]+          length steps `shouldSatisfy` (> 0)++    it "accepts --seed, --shuffle and --depth-sensitive" $+      withStdin "[[ D> 01- ]]" $+        testCLISucceeded ["morph", "--seed=7", "--shuffle", "--depth-sensitive", "--flat", "--hide-rho"] ["⟦ Δ ⤍ 01- ⟧"]++    it "returns the λ-formation dataize cannot finish on" $+      withStdin "⟦ @ ↦ ⟦ λ ⤍ L_number_div, ρ ↦ ⟦ Δ ⤍ 40-45-00-00-00-00-00-00 ⟧, x ↦ ⟦ Δ ⤍ 40-00-00-00-00-00-00-00 ⟧ ⟧ ⟧" $+        testCLISucceeded+          ["morph", "--locator=Q.@", "--max-steps=40", "--flat", "--hide-rho"]+          ["⟦ λ ⤍ L_number_div"]++    it "fails once the --max-steps budget is spent" $+      withStdin chained $+        testCLIFailed+          ["morph", "--locator=Q.@", "--max-steps=3"]+          ["[ERROR]: Morphing did not finish before reaching the limit of steps: --max-steps=3"]++    it "names morphing, not dataization, when the budget is spent before dataization starts" $+      withStdin "⟦ x ↦ Φ.x.y ⟧" $+        testCLIFailed+          ["morph", "--locator=Q.x", "--max-steps=5"]+          ["[ERROR]: Morphing did not finish before reaching the limit of steps: --max-steps=5"]++    describe "--max-firings" $ do+      let splitting = withLambdasOf (T.pack "- λ: L_split\n  morph:\n    𝑛1: Φ.s.foo\n    𝑛2: Φ.s.foo\n  𝑛: ⟦ l ↦ 𝑛1, r ↦ 𝑛2 ⟧\n")+          split = "⟦ s ↦ ⟦ λ ⤍ L_split ⟧, x ↦ Φ.s.foo ⟧"+      it "fails with non-positive --max-firings" $+        withStdin split $+          testCLIFailed ["morph", "--max-firings=0"] ["--max-firings must be positive"]++      it "fails once the --max-firings budget is spent on a widening recursion" $+        splitting $ \table ->+          withStdin split $+            testCLIFailed+              ["morph", "--symbolic=" ++ table, "--locator=Q.x", "--max-firings=64"]+              ["[ERROR]: Evaluation did not finish before reaching the limit of firings: --max-firings=64"]++      it "ends the widening recursion with --partial" $+        splitting $ \table ->+          withStdin split $+            testCLISucceeded+              ["morph", "--symbolic=" ++ table, "--locator=Q.x", "--max-firings=64", "--partial", "--flat", "--hide-rho", "--sweet"]+              ["L_split:λ.foo"]++      it "fires no more λ functions than --max-firings allows" $+        splitting $ \table ->+          withTempFile "protocolXXXXXX.txt" $ \(path, stream) -> do+            hClose stream+            withStdin split $+              testCLISucceeded+                ["morph", "--symbolic=" ++ table, "--locator=Q.x", "--max-firings=64", "--partial", "--protocol=" ++ path, "--quiet"]+                []+            records <- readProtocol path+            length (filter (isInfixOf "𝔼(L_split)") (lines records)) `shouldBe` 64++    describe "--max-seconds" $ do+      let ladder = withLambdasOf (T.pack "- λ: L_split\n  morph:\n    𝑛1: ξ.n.foo\n    𝑛2: ξ.n.foo\n  𝑛: ⟦ l ↦ 𝑛1, r ↦ 𝑛2 ⟧\n")+          rungs = "⟦ " ++ intercalate ", " [printf "l%d ↦ ⟦ λ ⤍ L_split, n ↦ Φ.l%d ⟧" rung (rung + 1) | rung <- [0 .. 23 :: Int]] ++ ", l24 ↦ ⟦⟧, x ↦ Φ.l0.foo ⟧"+          bounded :: Expectation -> Expectation+          bounded check = timeout 60000000 check >>= (`shouldBe` Just ())+      it "fails with non-positive --max-seconds" $+        withStdin rungs $+          testCLIFailed ["morph", "--max-seconds=0"] ["--max-seconds must be positive"]++      it "fails with a --max-seconds value above maxBound Int, naming the limit" $+        withStdin rungs $+          testCLIFailed+            ["morph", "--max-seconds=9223372036854775808"]+            ["value must be between", "9223372036854775807"]++      it "fails once the --max-seconds budget is spent" $+        ladder $ \table ->+          bounded $+            withStdin rungs $+              testCLIFailed+                ["morph", "--symbolic=" ++ table, "--locator=Q.x", "--max-seconds=1"]+                ["[ERROR]: Evaluation did not finish before reaching the limit of seconds: --max-seconds=1"]++      it "fails dataize once the --max-seconds budget is spent" $+        ladder $ \table ->+          bounded $+            withStdin rungs $+              testCLIFailed+                ["dataize", "--symbolic=" ++ table, "--locator=Q.x", "--max-seconds=1"]+                ["[ERROR]: Evaluation did not finish before reaching the limit of seconds: --max-seconds=1"]++      it "fails once the --max-seconds budget is spent with --partial" $+        ladder $ \table ->+          bounded $+            withStdin rungs $+              testCLIFailed+                ["morph", "--symbolic=" ++ table, "--max-seconds=1", "--locator=Q.x", "--partial"]+                ["[ERROR]: Evaluation did not finish before reaching the limit of seconds: --max-seconds=1"]++      forM_ [["--locator=Q.x"], ["--locator=Q.x", "--partial"]] $ \opts ->+        it ("writes the timeout as the last line of the protocol with " ++ unwords opts) $+          ladder $ \table ->+            withTempFile "protocolXXXXXX.txt" $ \(path, stream) -> do+              hClose stream+              bounded $+                withStdin rungs $+                  testCLIFailed+                    (["morph", "--symbolic=" ++ table, "--max-seconds=1", "--protocol=" ++ path, "--quiet"] ++ opts)+                    ["--max-seconds=1"]+              records <- readProtocol path+              dropWhile (== ' ') (last (lines records)) `shouldBe` "timeout 1"++      it "writes the timeout once to the XML protocol" $+        ladder $ \table ->+          withTempFile "protocolXXXXXX.xml" $ \(path, stream) -> do+            hClose stream+            bounded $+              withStdin rungs $+                testCLIFailed+                  ["morph", "--symbolic=" ++ table, "--locator=Q.x", "--partial", "--max-seconds=1", "--protocol=" ++ path, "--quiet"]+                  ["--max-seconds=1"]+            records <- readProtocol path+            length (filter (isInfixOf "<timeout limit=\"1\" by=\"morph\" at=\"") (lines records)) `shouldBe` 1++      it "closes the XML protocol of a run out of time" $+        ladder $ \table ->+          withTempFile "protocolXXXXXX.xml" $ \(path, stream) -> do+            hClose stream+            bounded $+              withStdin rungs $+                testCLIFailed+                  ["morph", "--symbolic=" ++ table, "--locator=Q.x", "--max-seconds=1", "--protocol=" ++ path, "--quiet"]+                  ["--max-seconds=1"]+            document <- X.readFile X.def path+            X.nameLocalName (X.elementName (X.documentRoot document)) `shouldBe` T.pack "protocol"++    it "parks the spent budget as a residual with --partial" $+      withStdin "⟦ φ ↦ 5.gt(Φ.nan) ⟧" $+        testCLISucceeded+          ["morph", "--locator=Q.@", "--max-steps=10", "--partial", "--flat", "--hide-rho", "--sweet"]+          ["5.0.gt( Φ.nan )"]++    describe "--partial" $ do+      let stuck = "[[ @ -> [[ L> Sym_arg_0 ]].foo ]]"+      it "fails on a λ function that cannot fire without the flag" $+        withStdin stuck $+          testCLIFailed ["morph", "--locator=Q.@"] ["No entry of --symbolic answers the λ function 'Sym_arg_0'"]++      it "prints the residue with the stuck application intact and exits successfully" $+        withStdin stuck $+          testCLISucceeded+            ["morph", "--locator=Q.@", "--partial", "--flat", "--hide-rho"]+            ["⟦ λ ⤍ Sym_arg_0 ⟧.foo"]++    describe "a copy a 'defer' line of a join defers" $ do+      let joining = T.pack "- λ: L_fork\n  dataize:\n    𝛿1: $.c\n  morph:\n    𝑛1: $.a\n    𝑛2: $.b\n  defer:\n    𝑛3: 𝑛1\n    𝑛4: 𝑛2\n  join:\n    on: 𝛿1\n    left: 𝑛3\n    right: 𝑛4\n"+          counting = T.pack "- λ: L_inc\n  dataize:\n    𝛿1: $.x\n  𝑛: Φ.num( φ ↦ ⟦ λ ⤍ 𝜎 ⟧ )\n"+          recordedWith :: T.Text -> [String] -> String -> IO [String]+          recordedWith entries options program =+            withTempFile "protocolXXXXXX.xml" $ \(path, stream) -> do+              hClose stream+              withLambdasOf entries $ \joins ->+                withStdin program $+                  testCLIFailed (["dataize", "--symbolic=" ++ joins, "--locator=Q.y", "--protocol=" ++ path, "--quiet", "--sweet", "--hide-rho"] ++ options) ["No entry of --symbolic answers the λ function 'L_fork'"]+              lines <$> readProtocol path+          deferred :: [String] -> String -> IO [String]+          deferred = recordedWith joining+      it "writes the copy it deferred to the XML protocol as a call of the object it was made of" $ do+        records <- deferred [] "⟦ step(tup) ↦ ⟦ φ ↦ tup.next ⟧, items ↦ ⟦ tail ↦ ⟦ λ ⤍ 𝜎1 ⟧ ⟧, y ↦ ⟦ c ↦ ⟦ λ ⤍ 𝜎2 ⟧, a ↦ ⟦ Δ ⤍ 01- ⟧, b ↦ Φ.step( tup ↦ Φ.items.tail ), λ ⤍ L_fork ⟧ ⟧"+        records+          `shouldBe` [ "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"+                     , "<dataize at=\"Φ.y\">"+                     , "  <evaluate λ=\"L_fork\" by=\"dataize\" at=\"Φ.y\">"+                     , "    <dataize at=\"ξ.c\">"+                     , "      <manufactured meta=\"𝛿·1·1\">𝜎2</manufactured>"+                     , "    </dataize>"+                     , "    <bound meta=\"𝛿1·1\">𝛿·1·1</bound>"+                     , "    <morph at=\"ξ.a\">"+                     , "      <morphed meta=\"𝑛·1·1\">01-</morphed>"+                     , "    </morph>"+                     , "    <bound meta=\"𝑛1·1\">01-</bound>"+                     , "    <morph at=\"ξ.b\">"+                     , "      <applied meta=\"𝑛·1·2\" by=\"morph\" at=\"ξ.b\" of=\"Φ.step\"><ref attr=\"tup\">Φ.items.tail</ref></applied>"+                     , "      <morphed meta=\"𝑛·1·3\">𝑛·1·2</morphed>"+                     , "    </morph>"+                     , "    <bound meta=\"𝑛2·1\">𝑛·1·3</bound>"+                     , "    <bound meta=\"𝑛3·1\">01-</bound>"+                     , "    <deferred symbol=\"𝜎3\" by=\"morph\" at=\"𝑛2.φ\" of=\"Φ.step\"><with><symbol attr=\"tup\">𝜎1</symbol></with></deferred>"+                     , "    <diverged left=\"𝑛3·1\" right=\"𝜎3\" at=\".Δ\" reason=\"the join names no otherwise\"/>"+                     , "  </evaluate>"+                     , "</dataize>"+                     ]++      it "names the object a deferred copy was made of through the formation its ρ holds" $ do+        records <- deferred [] "⟦ joined(items) ↦ ⟦ φ ↦ step( tup ↦ items ), step(ρ, tup) ↦ ⟦ φ ↦ tup.next ⟧ ⟧, items ↦ ⟦ tail ↦ ⟦ λ ⤍ 𝜎1 ⟧ ⟧, y ↦ ⟦ c ↦ ⟦ λ ⤍ 𝜎2 ⟧, a ↦ ⟦ Δ ⤍ 01- ⟧, b ↦ Φ.joined( items ↦ Φ.items.tail ).φ, λ ⤍ L_fork ⟧ ⟧"+        records `shouldContain` ["    <deferred symbol=\"𝜎3\" by=\"morph\" at=\"𝑛2.φ\" of=\"Φ.joined.step\"><with><symbol attr=\"tup\">𝜎1</symbol></with></deferred>"]++      it "names an argument of a deferred copy that is no bare symbol by the meta its morph bound, though an element formed it" $ do+        records <- deferred [] "⟦ box(x, w) ↦ ⟦ φ ↦ x.next ⟧, items ↦ ⟦ tail ↦ ⟦ λ ⤍ 𝜎1 ⟧ ⟧, y ↦ ⟦ c ↦ ⟦ λ ⤍ 𝜎2 ⟧, a ↦ ⟦ Δ ⤍ 01- ⟧, b ↦ Φ.box( x ↦ Φ.items.tail, w ↦ ⟦ z ↦ Φ ⟧ ), λ ⤍ L_fork ⟧ ⟧"+        records `shouldContain` ["    <deferred symbol=\"𝜎3\" by=\"morph\" at=\"𝑛2.φ\" of=\"Φ.box\"><with><symbol attr=\"x\">𝜎1</symbol><meta attr=\"w\">𝑛·1·5</meta></with></deferred>"]++      it "names a deferred copy of a formation the world does not declare by the element that formed it" $ do+        records <- deferred [] "⟦ wrap(v) ↦ ⟦⟧, items ↦ ⟦ tail ↦ ⟦ λ ⤍ 𝜎1 ⟧ ⟧, y ↦ ⟦ c ↦ ⟦ λ ⤍ 𝜎2 ⟧, a ↦ ⟦ Δ ⤍ 01- ⟧, b ↦ Φ.wrap( v ↦ ⟦ inner(x) ↦ ⟦ φ ↦ x.next ⟧ ⟧ ).v.inner( x ↦ Φ.items.tail ), λ ⤍ L_fork ⟧ ⟧"+        records `shouldContain` ["    <formed meta=\"𝑒·1·3\"><symbol attr=\"x\">𝜎1</symbol><ref attr=\"φ\">x.next</ref></formed>", "    <deferred symbol=\"𝜎3\" by=\"morph\" at=\"𝑛2.φ\" of=\"𝑒·1·3\"/>"]++      it "names an argument of a deferred copy by the object its morph answered" $ do+        records <- recordedWith (joining <> counting) [] "⟦ box(x, w) ↦ ⟦ φ ↦ x.next ⟧, num(φ) ↦ ⟦⟧, inc(x) ↦ ⟦ λ ⤍ L_inc ⟧, items ↦ ⟦ tail ↦ ⟦ λ ⤍ 𝜎1 ⟧ ⟧, y ↦ ⟦ c ↦ ⟦ λ ⤍ 𝜎2 ⟧, a ↦ ⟦ Δ ⤍ 01- ⟧, b ↦ Φ.box( x ↦ Φ.items.tail, w ↦ Φ.inc( ⟦ Δ ⤍ 2A- ⟧ ) ), λ ⤍ L_fork ⟧ ⟧"+        filter (isInfixOf "<deferred") records `shouldBe` ["    <deferred symbol=\"𝜎4\" by=\"morph\" at=\"𝑛2.φ\" of=\"Φ.box\"><with><symbol attr=\"x\">𝜎1</symbol><meta attr=\"w\">𝑛·2·4</meta></with></deferred>"]++    describe "--acyclic=plausible" $ do+      let twins = "[[ bytes ↦ ⟦ φ ↦ ∅ ⟧, number(φ) -> [[ plus(^, x) -> [[ L> L_number_plus ]] ]], pair(a, b) -> [[ L> L_pair ]], t -> Φ.pair( 7.plus( 5.plus( 6 ) ), 7.plus( 5.plus( 6 ) ) ).φ ]]"+          pairs = T.pack "- λ: L_number_plus\n  dataize:\n    𝛿1: $.ρ\n    𝛿2: $.x\n  𝑛: Φ.number( φ ↦ ⟦ λ ⤍ 𝜎 ⟧ )\n- λ: L_pair\n  morph:\n    𝑛1: $.a\n    𝑛2: $.b\n  evaluate:\n    𝑛3: 𝑛1\n    𝑛4: 𝑛2\n  𝑛: ⟦ φ ↦ ⟦ a ↦ 𝑛3, b ↦ 𝑛4 ⟧ ⟧\n"+          recorded :: String -> IO [String]+          recorded mode =+            withTempFile "protocolXXXXXX.txt" $ \(path, stream) -> do+              hClose stream+              withLambdasOf pairs $ \file ->+                withStdin twins $+                  testCLISucceeded+                    ["morph", "--symbolic=" ++ file, "--locator=Q.t", "--acyclic=" ++ mode, "--protocol=" ++ path, "--quiet"]+                    []+              lines <$> readProtocol path+      it "charges a formation once per argument spelling it under proven" $+        withLambdasOf pairs $ \file ->+          withStdin twins $+            testCLIFailed+              ["morph", "--symbolic=" ++ file, "--locator=Q.t", "--acyclic=proven", "--max-firings=4"]+              ["[ERROR]: Evaluation did not finish before reaching the limit of firings: --max-firings=4"]++      it "charges a formation once for the run under plausible" $+        withLambdasOf pairs $ \file ->+          withStdin twins $+            testCLISucceeded+              ["morph", "--symbolic=" ++ file, "--locator=Q.t", "--acyclic=plausible", "--max-firings=3", "--flat", "--hide-rho", "--sweet"]+              ["b ↦ ⟦ φ ↦ 𝜎2:λ, plus(x) ↦ L_number_plus:λ ⟧"]++      it "reduces the operands of a formation once per argument spelling it under proven" $+        recorded "proven" >>= (`shouldSatisfy` ((== 4) . length . filter (isInfixOf "𝛿1·")))++      it "reduces the operands of a formation once for the run under plausible" $+        recorded "plausible" >>= (`shouldSatisfy` ((== 2) . length . filter (isInfixOf "𝛿1·")))++      it "writes a recalled firing at its own site under plausible" $+        recorded "plausible" >>= (`shouldSatisfy` ((== 3) . length . filter (isInfixOf "𝔼(L_number_plus)")))++      it "answers a recalled firing with the line of the first under plausible" $+        recorded "plausible" >>= (`shouldContain` ["      𝑛·4·1 := 𝑛·2·12  # 𝑛"])++      it "dataizes to the same datum under plausible" $+        withStdin chained $+          testCLISucceeded+            ["dataize", symbolic, "--acyclic=plausible", "--locator=Q.@"]+            ["40-45-00-00-00-00-00-00"]++    describe "--acyclic=proven" $ do+      let looping = "⟦ x ↦ ⟦ λ ⤍ L_loop ⟧.foo ⟧"+      it "spends the whole budget and fails on the limit without the flag" $+        loopingLambdas $ \endless ->+          withStdin looping $+            testCLIFailed+              ["morph", "--symbolic=" ++ endless, "--locator=Q.x", "--max-steps=40"]+              ["[ERROR]: Morphing did not finish before reaching the limit of steps: --max-steps=40"]++      it "prints the residue and exits successfully with the flag" $+        loopingLambdas $ \endless ->+          withStdin looping $+            testCLISucceeded+              ["morph", "--symbolic=" ++ endless, "--locator=Q.x", "--acyclic=proven", "--max-steps=4000", "--flat", "--hide-rho"]+              ["⟦ λ ⤍ L_loop ⟧.foo"]++      it "answers a terminating program the same way with the flag" $+        withStdin chained $+          testCLISucceeded+            ["morph", symbolic, "--acyclic=proven", "--locator=Q.@", "--sweet", "--hide-rho", "--flat"]+            ["⟦ x ↦ 7.0, λ ⤍ L_number_plus ⟧"]++    describe "--max-loops" $ do+      it "fires a looping λ function once more with --max-loops=1" $+        loopingLambdas $ \endless ->+          withTempFile "protocolXXXXXX.txt" $ \(path, stream) -> do+            hClose stream+            withStdin "⟦ x ↦ ⟦ λ ⤍ L_loop ⟧.foo ⟧" $+              testCLISucceeded+                ["morph", "--symbolic=" ++ endless, "--locator=Q.x", "--acyclic=proven", "--max-loops=1", "--protocol=" ++ path, "--quiet"]+                []+            records <- readProtocol path+            length (filter (isInfixOf "𝔼(L_loop)") (lines records)) `shouldBe` 2++      it "refuses the flag without --acyclic" $+        withStdin "⟦ x ↦ ⟦ λ ⤍ L_loop ⟧.foo ⟧" $+          testCLIFailed ["morph", "--locator=Q.x", "--max-loops=3"] ["The option --max-loops requires --acyclic, since only --acyclic cuts a loop"]++    it "prints a top formation of several bindings as XMIR" $+      withStdin "[[ x -> [[ D> 01- ]], y -> [[ D> 02- ]] ]]" $+        testCLISucceeded+          ["morph", "--output=xmir"]+          ["<o name=\"x\">01-</o>", "<o name=\"y\">02-</o>"]++    describe "fails" $ do+      it "with --output != latex and --nonumber" $+        withStdin "" $+          testCLIFailed+            ["morph", "--nonumber", "--output=xmir"]+            ["The --nonumber option can stay together with --output=latex only"]++      it "with --show used more than once" $+        withStdin "" $+          testCLIFailed+            ["morph", "--show=Q.a", "--show=Q.b"]+            ["The option --show can be used only once"]++      it "with wrong --locator option" $+        withStdin "" $+          testCLIFailed+            ["morph", "--locator=Q.x(Q.y)"]+            ["[ERROR]:", "Only dispatch expression started with Φ (or Q) can be used in --locator"]++  describe "explain" $ do+    forM_+      ["--morph", "--dataize", "--contextualize"]+      ( \judgment ->+          it ("refuses --normalize together with " ++ judgment) $+            testCLIFailed ["explain", judgment, "--normalize"] ["The --normalize option cannot be used together with"]+      )++    it "prints help" $+      testCLISucceeded+        ["explain", "--help"]+        ["Explain built-in morphing rules", "Explain built-in dataization rules", "Explain built-in contextualization rules"]++    it "explains single rule" $ do+      latex <- explainPack "test-resources/explain-packs/normalize/copy.yaml"+      testCLISucceeded ["explain", "--rule=resources/normalize/copy.yaml"] [latex <> "\n"]++    it "explains single rule with a label" $+      testCLISucceeded+        ["explain", rule "labeled.yaml"]+        [ unlines+            [ "\\phinoNormalizationRule[\\lambda]{copy}"+            , "  { [[ B_1, \\tau -> ?, B_2 ]] ( \\tau -> k ) }"+            , "  { [[ B_1, \\tau -> k, B_2 ]] }"+            , "  { }"+            , "  { }"+            ]+        ]++    it "explains multiple rules" $+      testCLISucceeded+        ["explain", "--rule=resources/normalize/copy.yaml", "--rule=resources/normalize/alpha.yaml"]+        ["\\phinoNormalizationRule{copy}", "\\phinoNormalizationRule{alpha}"]++    it "reproduces the same shuffle order for the same --seed" $ do+      let args =+            [ "explain"+            , "--shuffle"+            , "--seed=42"+            , rule "swap-a.yaml"+            , rule "swap-b.yaml"+            ]+      (firstRun, _) <- withStdout (runCLI args)+      (secondRun, _) <- withStdout (runCLI args)+      firstRun `shouldBe` secondRun++    it "shuffles the normalization rules away from their file order" $ do+      (plain, _) <- withStdout (runCLI ["explain", "--normalize"])+      (mixed, _) <- withStdout (runCLI ["explain", "--normalize", "--shuffle", "--seed=1"])+      mixed `shouldNotBe` plain++    it "accepts --seed flag" $+      testCLISucceeded+        ["explain", "--seed=7", "--normalize"]+        ["\\phinoNormalizationRule{alpha}"]++    forM_+      [ ("normalization", "--normalize", "normalize")+      , ("morphing", "--morph", "morphing")+      , ("dataization", "--dataize", "dataization")+      , ("contextualization", "--contextualize", "contextualization")+      ]+      ( \(judgment, option, dir) -> it ("explains " <> judgment <> " rules") $ do+          packs <- allPathsIn ("test-resources/explain-packs" </> dir)+          latex <- mapM explainPack (sort packs)+          testCLISucceeded ["explain", option] [unlines latex]+      )++    it "fails with no rules specified" $+      testCLIFailed+        ["explain"]+        ["Either --rule, --normalize, --morph, --dataize or --contextualize must be specified"]++    it "fails when more than one rule set is specified" $+      testCLIFailed+        ["explain", "--morph", "--dataize"]+        ["Only one of --morph, --dataize or --contextualize can be specified"]++    it "allows --normalize together with --rule" $+      testCLISucceeded+        ["explain", "--normalize", "--rule=resources/normalize/copy.yaml"]+        ["\\phinoNormalizationRule{copy}"]++    it "allows --shuffle together with --morph" $+      testCLISucceeded+        ["explain", "--morph", "--shuffle"]+        ["\\begin{phinoMorphingInference}"]++    it "writes to target file" $+      bracket+        ( do+            tmp <- getTemporaryDirectory+            stamp <- getPOSIXTime+            let dir = tmp </> ("phino-test-" ++ show (floor stamp :: Integer))+            createDirectoryIfMissing True dir+            pure (dir </> "explain.tex", dir)+        )+        (\(_, dir) -> removeDirectoryRecursive dir)+        ( \(path, _) -> do+            testCLISucceeded ["explain", "--normalize", printf "--target=%s" path] []+            content <- readFile path+            _ <- evaluate (length content)+            content `shouldContain` "\\phinoNormalizationRule{alpha}"+        )++  describe "merge" $ do+    it "prints help" $+      testCLISucceeded ["merge", "--help"] ["Paths to input files"]++    it "merges single expression" $+      testCLISucceeded+        ["merge", resource "desugar.phi", "--sweet", "--flat"]+        ["x:foo"]++    it "merges EO expressions" $+      testCLISucceeded+        ["merge", "--sweet", resource "number.phi", resource "bytes.phi", resource "string.phi", "--margin=25"]+        [ unlines+            [ "⟦"+            , "  eolang ↦ ⟦"+            , "    number(φ) ↦ ⟦⟧,"+            , "    bytes(data) ↦ ⟦⟧,"+            , "    string(φ) ↦ ⟦⟧,"+            , "    λ ⤍ Package"+            , "  ⟧,"+            , "  λ ⤍ Package"+            , "⟧:org"+            ]+        ]++    it "fails on merging non formations" $+      testCLIFailed+        ["merge", resource "dispatch.phi", resource "number.phi"]+        ["Invalid expression format, only expressions with top level formations are supported for 'merge' command"]++    it "fails on merging conflicted bindings" $+      testCLIFailed+        ["merge", resource "foo.phi", resource "desugar.phi"]+        ["Can't merge two bindings, conflict found"]++    it "fails on merging empty list of expressions" $+      testCLIFailed+        ["merge"]+        ["At least one input file must be specified for 'merge' command"]++    it "merges and prints as XMIR, with the listing rendered from the merged expression" $+      testCLISucceeded+        ["merge", resource "desugar.phi", "--output=xmir"]+        ["<?xml version=\"1.0\" encoding=\"UTF-8\"?>", "<listing>⟦ foo ↦ ξ.x ⟧</listing>", "<o base=\"ξ.x\" name=\"foo\"/>"]++    it "names an atom of XMIR after its locator and keeps its type" $ do+      let xmir = "<object><o name=\"number\"><o name=\"plus\"><o base=\"∅\" name=\"b\"/><o atom=\"Φ.number\" name=\"λ\"/></o></o></object>"+      withTempFileContent "phino-atom.xmir" xmir $ \file -> do+        testCLISucceeded+          ["merge", "--input=xmir", "--sweet", "--flat", file]+          ["L_number_plus:λ"]+        testCLISucceeded+          ["merge", "--input=xmir", "--output=xmir", file]+          ["<o atom=\"Φ.number\" name=\"λ\">L_number_plus</o>"]++    it "keeps the type of an atom of XMIR under --canonize" $ do+      let xmir = "<object><o name=\"number\"><o name=\"plus\"><o base=\"∅\" name=\"b\"/><o atom=\"Φ.number\" name=\"λ\"/></o></o></object>"+      withTempFileContent "phino-canonized-atom.xmir" xmir $ \file ->+        testCLISucceeded+          ["rewrite", "--input=xmir", "--output=xmir", "--canonize", file]+          ["<o atom=\"Φ.number\" name=\"λ\">Fn1</o>"]++    it "reproduces the same output for the same --seed" $ do+      let args =+            [ "merge"+            , "--seed=42"+            , "--sweet"+            , resource "number.phi"+            , resource "bytes.phi"+            ]+      (firstRun, _) <- withStdout (runCLI args)+      (secondRun, _) <- withStdout (runCLI args)+      firstRun `shouldBe` secondRun++  describe "compile" $ do+    it "writes the module to the target" $+      withTempDirectory "phino-compile" $ \dir -> do+        createDirectoryIfMissing True dir+        withCurrentDirectory dir (runCLI ["compile", "--target=gen/Compiled.hs"])+        doesFileExist (dir </> "gen" </> "Compiled.hs") `shouldReturn` True+    it "writes the rules of --rule into the module" $+      withTempDirectory "phino-compile" $ \dir -> do+        createDirectoryIfMissing True dir+        simple <- makeAbsolute "test-resources/cli/rules/simple.yaml"+        withCurrentDirectory dir (runCLI ["compile", "--rule=" ++ simple, "--target=Compiled.hs"])+        readFile' (dir </> "Compiled.hs") >>= (`shouldSatisfy` ("R.direct \"foo\"" `isInfixOf`))+    it "turns the flag on in a new cabal.project.local" $+      withTempDirectory "phino-compile" $ \dir -> do+        createDirectoryIfMissing True dir+        writeFile (dir </> "phino.cabal") ""+        withCurrentDirectory dir (runCLI ["compile", "--target=Compiled.hs"])+        readFile (dir </> "cabal.project.local") `shouldReturn` "package phino\n  flags: +compiled\n"+    it "prints the lines an existing cabal.project.local lacks" $+      withTempDirectory "phino-compile" $ \dir -> do+        createDirectoryIfMissing True dir+        writeFile (dir </> "phino.cabal") ""+        writeFile (dir </> "cabal.project.local") "tests: True\n"+        withCurrentDirectory dir (testCLISucceeded ["compile", "--target=Compiled.hs"] ["package phino\n  flags: +compiled"])+    it "leaves an existing cabal.project.local as it is" $+      withTempDirectory "phino-compile" $ \dir -> do+        createDirectoryIfMissing True dir+        writeFile (dir </> "phino.cabal") ""+        writeFile (dir </> "cabal.project.local") "tests: True\n"+        withStdout (withCurrentDirectory dir (runCLI ["compile", "--target=Compiled.hs"]))+        readFile (dir </> "cabal.project.local") `shouldReturn` "tests: True\n"+    it "writes cabal.project.local next to the module's project, not the current directory" $+      withTempDirectory "phino-compile" $ \dir -> do+        createDirectoryIfMissing True (dir </> "project" </> "gen")+        writeFile (dir </> "project" </> "phino.cabal") ""+        createDirectoryIfMissing True (dir </> "elsewhere")+        withCurrentDirectory+          (dir </> "elsewhere")+          (runCLI ["compile", "--target=" ++ (dir </> "project" </> "gen" </> "Compiled.hs")])+        doesFileExist (dir </> "elsewhere" </> "cabal.project.local") `shouldReturn` False+        readFile (dir </> "project" </> "cabal.project.local") `shouldReturn` "package phino\n  flags: +compiled\n"+    it "only prints the lines when it cannot find the project of the module" $+      withTempDirectory "phino-compile" $ \dir -> do+        createDirectoryIfMissing True dir+        withCurrentDirectory dir (testCLISucceeded ["compile", "--target=gen/Compiled.hs"] ["package phino\n  flags: +compiled"])+        doesFileExist (dir </> "cabal.project.local") `shouldReturn` False+    it "refuses a rule it cannot compile" $+      withTempDirectory "phino-compile" $ \dir -> do+        createDirectoryIfMissing True dir+        writeFile (dir </> "having.yaml") "name: hv\npattern: '[[ x -> !e1, !B1 ]]'\nresult: '[[ !B1 ]]'\nhaving:\n  eq: ['!e1', 'Q']\n"+        withCurrentDirectory dir (testCLIFailed ["compile", "--rule=having.yaml", "--target=Compiled.hs"] ["The rule 'hv' cannot be compiled, since it has a 'having' condition"])++  describe "match" $ do+    it "prints help" $+      testCLISucceeded+        ["match", "--help"]+        ["Pattern expression to match against", "Predicate for matched substitutions"]++    it "describes --sweet and --flat as applying to the result only" $+      testCLISucceeded+        ["match", "--help"]+        ["Print result 𝜑-expression using syntax sugar", "Print result 𝜑-expression in one line"]++    it "rejects --sequence, which --help must not mention" $+      withStdin "[[ ]]" $+        testCLIFailed ["match", "--sequence", "--pattern=[[ !B ]]"] ["Invalid option"]++    it "takes from stdin" $+      withStdin "[[]]" $+        testCLISucceeded ["match", "--log-level=debug"] ["[DEBUG]"]++    it "takes from file" $+      testCLISucceeded ["match", resource "foo.phi", "--log-level=debug"] ["[DEBUG]"]++    it "does not print substitutions without pattern" $+      withStdin "[[]]" $+        testCLISucceeded ["match", "--log-level=debug"] ["[DEBUG]: The --pattern is not provided, no substitutions are built"]++    it "refuses --when without --pattern" $+      withStdin "[[]]" $+        testCLIFailed ["match", "--when=bogus"] ["The option --when requires --pattern"]++    it "reproduces the same output for the same --seed" $ do+      dir <- getTemporaryDirectory+      let file = dir ++ "/phino-match-seed-test.phi"+      writeFile file "[[ x -> Q.x, y -> Q.y, z -> Q.z ]]"+      let args =+            [ "match"+            , "--seed=42"+            , "--sweet"+            , "--flat"+            , "--pattern=Q.!t"+            , file+            ]+      (firstRun, _) <- withStdout (runCLI args)+      (secondRun, _) <- withStdout (runCLI args)+      firstRun `shouldBe` secondRun+      removeFile file++    it "numbers two anonymous captures of one kind, so they stay apart" $+      withStdin "[[ a -> [[ ]], b -> Q ]]" $+        testCLISucceeded ["match", "--pattern=[[ !t -> !e, !t -> !e ]]"] ["e#1 >> ⟦⟧\ne#2 >> Φ\nt#1 >> a\nt#2 >> b"]++    it "prints many substitutions" $+      withStdin "[[ x -> Q.x, y -> Q.y ]]" $+        testCLISucceeded ["match", "--pattern=Q.!t"] ["t >> x\n------\nt >> y"]++    it "does not match a length against a literal that wraps around Int" $+      withStdin "[[ a -> $, b -> Q ]]" $+        testCLIFailed ["match", "--pattern=[[ !B1 ]]", "--when=eq(length(!B1),18446744073709551618)"] ["no substitutions are built"]++    it "builds substitutions with conditions" $+      withStdin "[[ x -> Q.y ]].x" $+        testCLISucceeded+          ["match", "--pattern=[[ !t1 -> Q.y, !B1 ]].!t1", "--when=eq(length(!B1),0)"]+          ["B1 >> ⟦⟧\nt1 >> x"]++    it "builds with condition from file" $+      testCLISucceeded+        ["match", "--pattern=[[ !B1 ]]", "--when=eq(length(!B1),1)", resource "foo.phi"]+        ["B1 >> ⟦ foo ↦ Φ.org.eolang.x ⟧"]++    it "keeps an 'or' met when a raising item comes first" $+      withStdin "[[ s -> [[ q -> ? ]] ]]" $+        testCLISucceeded+          ["match", "--flat", "--pattern=[[ s -> !e1 ]]", "--when=or(matches(\"x\", !e1), eq(1,1))"]+          ["e1 >> ⟦ q ↦ ∅ ⟧"]++    it "keeps an 'or' met when a raising item comes last" $+      withStdin "[[ s -> [[ q -> ? ]] ]]" $+        testCLISucceeded+          ["match", "--flat", "--pattern=[[ s -> !e1 ]]", "--when=or(eq(1,1), matches(\"x\", !e1))"]+          ["e1 >> ⟦ q ↦ ∅ ⟧"]++    it "logs a raising 'or' item as not met" $+      withStdin "[[ s -> [[ q -> ? ]] ]]" $+        testCLISucceeded+          ["match", "--log-level=debug", "--pattern=[[ s -> !e1 ]]", "--when=or(matches(\"x\", !e1), eq(1,1))"]+          ["raised and was treated as not met"]      it "rejects an anonymous meta in --when" $       withStdin "[[ x -> Q.y ]]" $
test/CSTSpec.hs view
@@ -639,7 +639,7 @@               , indent = 1               }           exString = EX_STRING{str = "hi", tab = TAB 0, rhos = []}-          exNumber = EX_NUMBER{num = Left 5, tab = TAB 0, rhos = []}+          exNumber = EX_NUMBER{num = 5, tab = TAB 0, rhos = []}           exNonFinite = EX_NONFINITE{global = Φ, nonfinite = NfNan, tab = TAB 0, rhos = []}           exMeta = EX_META{meta = metaVal}           exPhiMeet = EX_PHI_MEET{prefix = Just "p", idx = 1, expr = exGlobal}@@ -669,7 +669,7 @@       exString.str `shouldBe` "hi"       exString.tab `shouldBe` TAB 0       exString.rhos `shouldBe` []-      exNumber.num `shouldBe` Left 5+      exNumber.num `shouldBe` 5       exNumber.tab `shouldBe` TAB 0       exNumber.rhos `shouldBe` []       exNonFinite.global `shouldBe` Φ
test/DataizeSpec.hs view
@@ -14,7 +14,7 @@ import Control.Monad import Data.Aeson (FromJSON) import Data.IORef (newIORef)-import Data.List (find, isInfixOf, nub)+import Data.List (find, isInfixOf, isPrefixOf, nub) import Data.List.NonEmpty (NonEmpty (..)) import Data.Map.Strict qualified as Map import Data.Maybe (fromMaybe)@@ -219,39 +219,61 @@       protocol         `shouldBe` unlines           [ "  𝔻(Φ):"-          , "    𝕄(Φ):"-          , "      𝑛·0·1 := 2"-          , "      𝑛·0·2 := 𝑛·0·1.times( x ↦ 3 )"+          , "    boxed 𝑛·0·1 := Φ.φ"+          , "    𝕄(𝑛·0·1):"+          , "      𝑛·0·2 := 2.0"+          , "      𝑛·0·3 := 𝑛·0·2.times( x ↦ 3.0 )"           , "      𝔼(L_number_times):"-          , "        𝔻(Φ.a🌵0):"-          , "          𝕄(Φ.a🌵0):"-          , "            𝑛·1·1 := 2"-          , "            𝑛·1·2 := Φ.bytes( φ ↦ 40-00-00-00-00-00-00-00:Δ )"+          , "        𝔻(ξ.ρ):"+          , "          𝕄(ξ.ρ):"+          , "            𝑛·1·1 := 2.0"+          , "            morphed 𝑛·1·2 := 𝑛·1·1"+          , "          boxed 𝑛·1·3 := 𝑛·1·2.φ"+          , "          𝕄(𝑛·1·3):"+          , "            𝑛·1·4 := Φ.bytes( φ ↦ 40-00-00-00-00-00-00-00:Δ )"+          , "            morphed 𝑛·1·5 := 𝑛·1·4"+          , "          boxed 𝑛·1·6 := 𝑛·1·5.φ"           , "          𝛿·1·1 := 40-00-00-00-00-00-00-00  # 2.0"           , "        𝛿1·1 := 𝛿·1·1  # 𝔻(ξ.ρ)"-          , "        𝔻(Φ.a🌵1):"-          , "          𝕄(Φ.a🌵1):"-          , "            𝑛·1·3 := 3"-          , "            𝑛·1·4 := Φ.bytes( φ ↦ 40-08-00-00-00-00-00-00:Δ )"+          , "        𝔻(ξ.x):"+          , "          𝕄(ξ.x):"+          , "            𝑛·1·7 := 3.0"+          , "            morphed 𝑛·1·8 := 𝑛·1·7"+          , "          boxed 𝑛·1·9 := 𝑛·1·8.φ"+          , "          𝕄(𝑛·1·9):"+          , "            𝑛·1·10 := Φ.bytes( φ ↦ 40-08-00-00-00-00-00-00:Δ )"+          , "            morphed 𝑛·1·11 := 𝑛·1·10"+          , "          boxed 𝑛·1·12 := 𝑛·1·11.φ"           , "          𝛿·1·2 := 40-08-00-00-00-00-00-00  # 3.0"           , "        𝛿2·1 := 𝛿·1·2  # 𝔻(ξ.x)"-          , "        𝑛·1·5 := Φ.number( φ ↦ 𝜎1:λ )  # 𝑛"-          , "        𝕄(𝑛·1·5):"-          , "          𝑛·1·6 := Φ.number( φ ↦ 𝜎1:λ )"-          , "        𝑛·1·7 := 𝑛·1·6  # 𝕄(𝑛·1·5)"-          , "    unanswered L_number_nope  # 𝔻(⟦ ρ ↦ 𝑛·1·6, λ ⤍ L_number_nope ⟧)"+          , "        𝑛·1·13 := Φ.number( φ ↦ 𝜎1:λ )  # 𝑛"+          , "        𝕄(𝑛·1·13):"+          , "          𝑛·1·14 := Φ.number( φ ↦ 𝜎1:λ )"+          , "          morphed 𝑛·1·15 := 𝑛·1·14"+          , "        𝑛·1·16 := 𝑛·1·15  # 𝕄(𝑛·1·13)"+          , "      morphed 𝑛·0·4 := ⟦ ρ ↦ 𝑛·1·14, λ ⤍ L_number_nope ⟧"+          , "    unanswered L_number_nope  # 𝔻(⟦ ρ ↦ 𝑛·1·14, λ ⤍ L_number_nope ⟧)"           ]     it "leaves an unanswered λ function dataized directly as the whole residue" $ do       ((outcome, chain), protocol) <- partially known "[[ L> Sym_arg_0 ]]"       outcome `shouldBe` Residual placeholder-      protocol `shouldBe` "  𝔻(Φ):\n    unanswered Sym_arg_0  # 𝔻(Sym_arg_0:λ)\n"+      protocol `shouldBe` "  𝔻(Φ):\n    boxed 𝑛·0·1 := Φ.φ\n    unanswered Sym_arg_0  # 𝔻(Sym_arg_0:λ)\n"       map fst chain `shouldEndWith` [placeholder]     it "closes a dataization with the datum the delta rule found" $ do       (_, protocol) <- partially known "[[ @ -> [[ D> 01-02 ]] ]]"-      protocol `shouldBe` "  𝔻(Φ):\n    𝛿·0·1 := 01-02\n"+      protocol `shouldBe` "  𝔻(Φ):\n    boxed 𝑛·0·1 := Φ.φ\n    boxed 𝑛·0·2 := 01-02:Δ:φ.φ\n    𝛿·0·1 := 01-02\n"     it "continues the block of a dataization the box rule enters again at the same site" $ do       (_, protocol) <- partially known "[[ @ -> [[ @ -> [[ D> 0A- ]] ]] ]]"-      protocol `shouldBe` "  𝔻(Φ):\n    𝛿·0·1 := 0A-\n"+      protocol `shouldBe` "  𝔻(Φ):\n    boxed 𝑛·0·1 := Φ.φ\n    boxed 𝑛·0·2 := 0A-:Δ:φ:φ.φ\n    boxed 𝑛·0·3 := 0A-:Δ:φ.φ\n    𝛿·0·1 := 0A-\n"+    it "writes the two morphings of one dataization as two blocks" $ do+      (_, protocol) <- partially known "73.25"+      length (filter (isPrefixOf "    𝕄(") (lines protocol)) `shouldBe` 2+    it "writes every box step of a dataization into the protocol" $ do+      (_, protocol) <- partially known "73.25"+      filter (isInfixOf "boxed") (lines protocol) `shouldBe` ["    boxed 𝑛·0·1 := Φ.φ", "    boxed 𝑛·0·4 := 𝑛·0·3.φ", "    boxed 𝑛·0·7 := 𝑛·0·6.φ"]+    it "names the morphing that follows a box step by the meta the box step bound" $ do+      (_, protocol) <- partially known "73.25"+      lines protocol `shouldContain` ["    boxed 𝑛·0·1 := Φ.φ", "    𝕄(𝑛·0·1):"]     it "still reaches the manufactured datum when nothing is stuck" $ do       ((outcome, _), _) <- partially known "2.times(3)"       outcome `shouldBe` Dataized (BtMany ["40", "45", "00", "00", "00", "00", "00", "00"])@@ -261,6 +283,12 @@         Residual (ExFormation bds) -> bds `shouldContain` [BiLambda (Function "L_number_plus")]         other -> expectationFailure ("expected a residual formation, got " ++ show other)       protocol `shouldSatisfy` isInfixOf "unanswered ⊥  # 𝔻(⊥)"+    it "leaves the terminator ⊥ the object reduces to as the whole residue" $ do+      ((outcome, _), _) <- partially known "⊥"+      outcome `shouldBe` Residual ExTermination+    it "writes the terminator ⊥ the object reduces to into the protocol" $ do+      (_, protocol) <- partially known "⊥"+      protocol `shouldBe` "  𝔻(Φ):\n    boxed 𝑛·0·1 := Φ.φ\n  unanswered ⊥  # 𝔻(⊥)\n"    describe "ReduceContext's --max-depth/--max-cycles reach into the normalization it splices in" $ do     let boxed = "[[ @ -> [[ D> 00- ]] ]]"
test/DepsSpec.hs view
@@ -11,7 +11,7 @@ import Data.IORef (modifyIORef', newIORef, readIORef) import Data.List (isInfixOf, isPrefixOf) import Data.Time.Clock.POSIX (getPOSIXTime)-import Deps (Acyclic (Proven), Evaluation (EvAnswer, EvApplied, EvBound, EvBuilt, EvData, EvDeferred, EvDelta, EvDiverged, EvFabricated, EvFiring, EvJoined, EvLooped, EvManufactured, EvMorphed, EvReshaped, EvRun, EvStarted, EvStuck, EvStuckOn, EvSymbolized, EvTaken, EvTerm, EvWorld), Judgment (Dataization, Morphing), Nesting (..), Protocol (..), dontSaveEval, dontSaveStep, emptyNesting, emptyProgress, emptyProtocol, endEval, endEvalXml, perSecond, progressed, resited, saveStep)+import Deps (Acyclic (Proven), Evaluation (EvAnswer, EvApplied, EvBound, EvBoxed, EvBuilt, EvData, EvDeferred, EvDelta, EvDiverged, EvFabricated, EvFiring, EvJoined, EvLooped, EvManufactured, EvMorphed, EvReshaped, EvRun, EvStarted, EvStuck, EvStuckOn, EvSymbolized, EvTaken, EvTerm, EvWorld), Judgment (Dataization, Morphing), Nesting (..), Protocol (..), dontSaveEval, dontSaveStep, emptyNesting, emptyProgress, emptyProtocol, endEval, endEvalXml, perSecond, progressed, resited, saveStep) import Fixtures (readUtf8, recorded, recordedWhole, recordedXml) import Forks (Refusal (..), Side (..)) import GHC.Clock (getMonotonicTime)@@ -158,14 +158,33 @@     it "spells the datum of an operand by the name the delta rule just gave it" $ do       (_, written) <- recorded (\record -> mapM_ record [EvFiring 1 "L_ж" Dataization ExRoot, EvDelta 3 (BtOne "7F"), EvData 2 "𝛿1" (ExDispatch ExXi (AtLabel "щ")) (Right (BtOne "7F"))])       last (lines written) `shouldBe` "    𝛿1·1 := 𝛿·1·1  # 𝔻(ξ.щ)"-    it "writes no line for the symbol a dataization came down to" $ do-      (_, manufactured) <- recorded (\record -> mapM_ record [EvRun Dataization "Φ", EvFiring 1 "L_ж" Dataization ExRoot, EvManufactured 3 32, EvData 2 "𝛿1" (ExDispatch ExXi (AtLabel "щ")) (Left 32)])-      (_, plain) <- recorded (\record -> mapM_ record [EvRun Dataization "Φ", EvFiring 1 "L_ж" Dataization ExRoot, EvData 2 "𝛿1" (ExDispatch ExXi (AtLabel "щ")) (Left 32)])-      manufactured `shouldBe` plain-    it "writes no line for the term a morphing came to" $ do-      (_, morphed) <- recorded (\record -> mapM_ record [EvRun Morphing "Φ", EvStarted 1 Morphing (ExDispatch ExRoot (AtLabel "жук")), EvMorphed 2 (ExDispatch ExRoot (AtLabel "щука"))])-      (_, plain) <- recorded (\record -> mapM_ record [EvRun Morphing "Φ", EvStarted 1 Morphing (ExDispatch ExRoot (AtLabel "жук"))])-      morphed `shouldBe` plain+    it "writes the symbol a dataization came down to under a name of its own" $ do+      (_, written) <- recorded (\record -> mapM_ record [EvRun Dataization "Φ", EvManufactured 1 12])+      lines written `shouldBe` ["𝔻(Φ):", "  𝛿·0·1 := 𝔻(𝜎12:λ)"]+    it "counts the symbols a dataization came down to with the data the delta rule found" $ do+      (_, written) <- recorded (\record -> mapM_ record [EvRun Dataization "Φ", EvFiring 1 "L_ж" Dataization ExRoot, EvDelta 3 (BtOne "7F"), EvManufactured 3 32])+      last (lines written) `shouldBe` "      𝛿·1·2 := 𝔻(𝜎32:λ)"+    it "spells the symbol of an operand by the name the dataization just gave it" $ do+      (_, written) <- recorded (\record -> mapM_ record [EvRun Dataization "Φ", EvFiring 1 "L_ж" Dataization ExRoot, EvManufactured 3 32, EvData 2 "𝛿1" (ExDispatch ExXi (AtLabel "щ")) (Left 32)])+      last (lines written) `shouldBe` "    𝛿1·1 := 𝛿·1·1  # 𝔻(ξ.щ)"+    it "spells the symbol of an operand in full when the line before it named another symbol" $ do+      (_, written) <- recorded (\record -> mapM_ record [EvRun Dataization "Φ", EvFiring 1 "L_ж" Dataization ExRoot, EvManufactured 3 31, EvData 2 "𝛿1" (ExDispatch ExXi (AtLabel "щ")) (Left 32)])+      last (lines written) `shouldBe` "    𝛿1·1 := 𝔻(𝜎32:λ)  # 𝔻(ξ.щ)"+    it "writes the term a morphing came to as a line binding it to a fresh 𝑛 under the heading of the morphing" $ do+      (_, written) <- recorded (\record -> mapM_ record [EvRun Morphing "Φ", EvStarted 1 Morphing (ExDispatch ExRoot (AtLabel "жук")), EvMorphed 2 (ExDispatch ExRoot (AtLabel "щука"))])+      lines written `shouldBe` ["𝕄(Φ):", "  𝕄(Φ.жук):", "    morphed 𝑛·0·1 := Φ.щука"]+    it "spells the term a morphing came to by the name of the application that made it" $ do+      (_, written) <- recorded (\record -> mapM_ record [EvRun Morphing "Φ", EvStarted 1 Morphing (ExDispatch ExRoot (AtLabel "w")), EvApplied 2 Morphing (ExApplication (ExDispatch ExRoot (AtLabel "ёж")) (ArTau (AtLabel "q") (ExFormation []))) (ExFormation [BiTau (AtLabel "q") (ExFormation [])]) (ExDispatch ExRoot (AtLabel "w")), EvMorphed 2 (ExFormation [BiTau (AtLabel "q") (ExFormation [])])])+      last (lines written) `shouldBe` "    morphed 𝑛·0·2 := 𝑛·0·1"+    it "numbers a box step past the term the morphing before it came to, as the markup does" $ do+      (_, written) <- recorded (\record -> mapM_ record [EvRun Dataization "Φ.e", EvStarted 1 Morphing (ExDispatch ExRoot (AtLabel "e")), EvApplied 2 Morphing (ExApplication (ExDispatch ExRoot (AtLabel "байт")) (ArTau AtPhi (ExFormation [BiLambda (FnSymbol 1)]))) (ExFormation [BiTau AtPhi (ExFormation [BiLambda (FnSymbol 1)])]) (ExDispatch ExRoot (AtLabel "e")), EvMorphed 2 (ExFormation [BiTau AtPhi (ExFormation [BiLambda (FnSymbol 1)])]), EvBoxed 1 (ExDispatch (ExFormation [BiTau AtPhi (ExFormation [BiLambda (FnSymbol 1)])]) AtPhi)])+      last (lines written) `shouldBe` "  boxed 𝑛·0·3 := 𝑛·0·2.φ"+    it "names the term a morphing came to in the box after it" $ do+      (_, written) <- recorded (\record -> mapM_ record [EvRun Dataization "Φ", EvStarted 1 Morphing (ExDispatch ExRoot (AtLabel "жук")), EvMorphed 2 (ExFormation [BiTau AtPhi (ExDispatch ExRoot (AtLabel "щука"))]), EvBoxed 1 (ExDispatch (ExFormation [BiTau AtPhi (ExDispatch ExRoot (AtLabel "щука"))]) AtPhi)])+      last (lines written) `shouldBe` "  boxed 𝑛·0·2 := 𝑛·0·1.φ"+    it "names the term a morphing came to by its meta in the box after it, though an application named it before" $ do+      (_, written) <- recorded (\record -> mapM_ record [EvRun Dataization "Φ.ю", EvStarted 1 Morphing (ExDispatch ExRoot (AtLabel "ю")), EvApplied 2 Morphing (ExApplication (ExDispatch ExRoot (AtLabel "хорь")) (ArTau (AtLabel "ось") (ExFormation []))) (ExFormation [BiTau (AtLabel "ось") (ExFormation []), BiTau AtPhi (ExDispatch ExXi (AtLabel "ось"))]) (ExDispatch ExRoot (AtLabel "ю")), EvMorphed 2 (ExFormation [BiTau (AtLabel "ось") (ExFormation []), BiTau AtPhi (ExDispatch ExXi (AtLabel "ось"))]), EvBoxed 1 (ExDispatch (ExFormation [BiTau (AtLabel "ось") (ExFormation []), BiTau AtPhi (ExDispatch ExXi (AtLabel "ось"))]) AtPhi)])+      last (lines written) `shouldBe` "  boxed 𝑛·0·3 := 𝑛·0·2.φ"     it "spells the datum of an operand in full when the line before it found no datum" $ do       (_, written) <- recorded (\record -> mapM_ record [EvFiring 1 "L_ж" Dataization ExRoot, EvDelta 3 (BtOne "7F"), EvFiring 2 "L_з" Dataization ExRoot, EvData 2 "𝛿1" (ExDispatch ExXi (AtLabel "щ")) (Right (BtOne "7F"))])       last (lines written) `shouldBe` "    𝛿1·1 := 7F-  # 𝔻(ξ.щ)"@@ -193,6 +212,30 @@     it "writes the heading again for a block that follows a line beside it" $ do       (_, written) <- recorded (\record -> mapM_ record [EvRun Dataization "Φ", EvStarted 1 Morphing (ExDispatch ExRoot (AtLabel "w")), EvApplied 2 Morphing (ExApplication (ExDispatch ExRoot (AtLabel "ёж")) (ArTau (AtLabel "q") (ExFormation []))) (ExFormation [BiTau (AtLabel "q") (ExFormation [])]) (ExDispatch ExRoot (AtLabel "w")), EvDelta 1 (BtOne "0B"), EvStarted 1 Morphing (ExDispatch ExRoot (AtLabel "w")), EvApplied 2 Morphing (ExApplication (ExDispatch ExRoot (AtLabel "ёж")) (ArTau (AtLabel "q") (ExFormation []))) (ExFormation [BiTau (AtLabel "q") (ExFormation [])]) (ExDispatch ExRoot (AtLabel "w"))])       length (filter (== "  𝕄(Φ.w):") (lines written)) `shouldBe` 2+    it "writes the box step of a dataization as a line naming the φ it took" $ do+      (_, written) <- recorded (\record -> mapM_ record [EvRun Dataization "Φ", EvStarted 1 Morphing (ExDispatch ExRoot (AtLabel "гнёт")), EvApplied 2 Morphing (ExApplication (ExDispatch ExRoot (AtLabel "щур")) (ArTau (AtLabel "ял") (ExFormation []))) (ExFormation [BiTau (AtLabel "ял") (ExFormation []), BiTau AtPhi (ExDispatch ExXi (AtLabel "ял"))]) (ExDispatch ExRoot (AtLabel "гнёт")), EvBoxed 1 (ExDispatch (ExFormation [BiTau (AtLabel "ял") (ExFormation []), BiTau AtPhi (ExDispatch ExXi (AtLabel "ял"))]) AtPhi)])+      lines written `shouldBe` ["𝔻(Φ):", "  𝕄(Φ.гнёт):", "    𝑛·0·1 := Φ.щур( ял ↦ ⟦⟧ )", "  boxed 𝑛·0·2 := 𝑛·0·1.φ"]+    it "writes the heading again for a morphing that follows a box step" $ do+      (_, written) <- recorded (\record -> mapM_ record [EvRun Dataization "Φ", EvStarted 1 Morphing (ExDispatch ExRoot (AtLabel "гнёт")), EvApplied 2 Morphing (ExApplication (ExDispatch ExRoot (AtLabel "щур")) (ArTau (AtLabel "ял") (ExFormation []))) (ExFormation [BiTau (AtLabel "ял") (ExFormation []), BiTau AtPhi (ExDispatch ExXi (AtLabel "ял"))]) (ExDispatch ExRoot (AtLabel "гнёт")), EvBoxed 1 (ExDispatch (ExFormation [BiTau (AtLabel "ял") (ExFormation []), BiTau AtPhi (ExDispatch ExXi (AtLabel "ял"))]) AtPhi), EvStarted 1 Morphing (ExDispatch ExRoot (AtLabel "гнёт")), EvApplied 2 Morphing (ExApplication (ExDispatch ExRoot (AtLabel "щур")) (ArTau (AtLabel "ял") (ExBytes (BtOne "3C")))) (ExFormation [BiTau (AtLabel "ял") (ExBytes (BtOne "3C"))]) (ExDispatch ExRoot (AtLabel "гнёт"))])+      length (filter (== "  𝕄(Φ.гнёт):") (lines written)) `shouldBe` 2+    it "names the world it is told by Φ in the line of a box step" $ do+      (_, written) <- recorded (\record -> mapM_ record [EvRun Dataization "Φ", EvWorld (ExFormation [BiTau (AtLabel "вьюн") (ExFormation []), BiTau AtPhi (ExDispatch ExXi (AtLabel "вьюн"))]), EvBoxed 1 (ExDispatch (ExFormation [BiTau (AtLabel "вьюн") (ExFormation []), BiTau AtPhi (ExDispatch ExXi (AtLabel "вьюн"))]) AtPhi)])+      last (lines written) `shouldBe` "  boxed 𝑛·0·1 := Φ.φ"+    it "names a morphing that follows a box step by the meta the box step bound" $ do+      (_, written) <- recorded (\record -> mapM_ record [EvRun Dataization "Φ", EvWorld (ExFormation [BiTau (AtLabel "кулик") (ExFormation []), BiTau AtPhi (ExDispatch ExXi (AtLabel "кулик"))]), EvBoxed 1 (ExDispatch (ExFormation [BiTau (AtLabel "кулик") (ExFormation []), BiTau AtPhi (ExDispatch ExXi (AtLabel "кулик"))]) AtPhi), EvStarted 1 Morphing (ExDispatch (ExFormation [BiTau (AtLabel "кулик") (ExFormation []), BiTau AtPhi (ExDispatch ExXi (AtLabel "кулик"))]) AtPhi), EvApplied 2 Morphing (ExApplication (ExDispatch ExRoot (AtLabel "чиж")) (ArTau (AtLabel "уж") (ExBytes (BtOne "5E")))) (ExFormation [BiTau (AtLabel "уж") (ExBytes (BtOne "5E"))]) (ExDispatch (ExFormation [BiTau (AtLabel "кулик") (ExFormation []), BiTau AtPhi (ExDispatch ExXi (AtLabel "кулик"))]) AtPhi)])+      lines written `shouldContain` ["  boxed 𝑛·0·1 := Φ.φ", "  𝕄(𝑛·0·1):"]+    it "spells out again the φ a box step takes once more" $ do+      (_, written) <- recorded (\record -> mapM_ record [EvRun Dataization "Φ", EvWorld (ExFormation [BiTau (AtLabel "лунь") (ExFormation []), BiTau AtPhi (ExDispatch ExXi (AtLabel "лунь"))]), EvBoxed 1 (ExDispatch (ExFormation [BiTau (AtLabel "лунь") (ExFormation []), BiTau AtPhi (ExDispatch ExXi (AtLabel "лунь"))]) AtPhi), EvBoxed 1 (ExDispatch (ExFormation [BiTau (AtLabel "лунь") (ExFormation []), BiTau AtPhi (ExDispatch ExXi (AtLabel "лунь"))]) AtPhi)])+      last (lines written) `shouldBe` "  boxed 𝑛·0·2 := Φ.φ"+    it "names the answer of a firing by its line in the box step that takes its φ" $ do+      (_, written) <- recorded (\record -> mapM_ record [EvRun Dataization "Φ", EvFiring 1 "L_дрозд" Dataization ExRoot, EvBuilt 2 (ExFormation [BiTau AtPhi (ExApplication (ExDispatch ExRoot (AtLabel "сыч")) (ArTau AtPhi (ExFormation [BiLambda (FnSymbol 4)])))]), EvAnswer 2 (ExFormation [BiTau AtPhi (ExApplication (ExDispatch ExRoot (AtLabel "сыч")) (ArTau AtPhi (ExFormation [BiLambda (FnSymbol 4)])))]), EvBoxed 1 (ExDispatch (ExFormation [BiTau AtPhi (ExApplication (ExDispatch ExRoot (AtLabel "сыч")) (ArTau AtPhi (ExFormation [BiLambda (FnSymbol 4)])))]) AtPhi)])+      last (lines written) `shouldBe` "  boxed 𝑛·0·1 := 𝑛·1·2.φ"+    it "names the answer of a firing by its line in the box step after it, though a morphing named it before" $ do+      (_, written) <- recorded (\record -> mapM_ record [EvRun Dataization "Φ", EvFiring 1 "L_выдра" Dataization ExRoot, EvBuilt 2 (ExFormation [BiTau (AtLabel "кот") (ExFormation []), BiTau AtPhi (ExDispatch ExXi (AtLabel "кот"))]), EvMorphed 2 (ExFormation [BiTau (AtLabel "кот") (ExFormation []), BiTau AtPhi (ExDispatch ExXi (AtLabel "кот"))]), EvAnswer 2 (ExFormation [BiTau (AtLabel "кот") (ExFormation []), BiTau AtPhi (ExDispatch ExXi (AtLabel "кот"))]), EvBoxed 1 (ExDispatch (ExFormation [BiTau (AtLabel "кот") (ExFormation []), BiTau AtPhi (ExDispatch ExXi (AtLabel "кот"))]) AtPhi)])+      last (lines written) `shouldBe` "  boxed 𝑛·0·1 := 𝑛·1·3.φ"+    it "names the answer of a join by its line in the box step that takes its φ" $ do+      (_, written) <- recorded (\record -> mapM_ record [EvRun Dataization "Φ", EvFiring 1 "L_клёст" Dataization ExRoot, EvTaken 2 [ExDispatch ExXi (AtLabel "then")] (ExFormation [BiTau (AtLabel "жук") (ExFormation [BiLambda (FnSymbol 7)]), BiTau AtPhi (ExDispatch ExXi (AtLabel "жук"))]), EvBoxed 1 (ExDispatch (ExFormation [BiTau (AtLabel "жук") (ExFormation [BiLambda (FnSymbol 7)]), BiTau AtPhi (ExDispatch ExXi (AtLabel "жук"))]) AtPhi)])+      last (lines written) `shouldBe` "  boxed 𝑛·0·1 := 𝑛·1·1.φ"     it "keeps the mode of a cut made at the site of its block" $ do       (_, written) <- recorded (\record -> mapM_ record [EvRun Morphing "Φ", EvLooped 1 Morphing Proven (ExFormation []) ExRoot])       last (lines written) `shouldBe` "  looped ⟦⟧  # proven"@@ -286,43 +329,49 @@       lines written `shouldContain` ["  <applied meta=\"𝑛·0·1\" by=\"morph\" at=\"Φ\" of=\"Φ.ёрш\"><bottom attr=\"ц\"/></applied>"]     it "spells an argument dispatched from a named application as a reference, not a meta" $ do       (_, written) <- recordedXml (\record -> mapM_ record [EvRun Morphing "Φ", EvApplied 1 Morphing (ExApplication (ExDispatch ExRoot (AtLabel "жаба")) (ArTau (AtLabel "у") (ExDispatch (ExApplication (ExDispatch ExRoot (AtLabel "ил")) (ArTau (AtLabel "в") (ExFormation [BiLambda (FnSymbol 6)]))) (AtLabel "дно")))) (ExFormation []) ExRoot])-      lines written `shouldContain` ["  <applied meta=\"𝑛·0·2\" by=\"morph\" at=\"Φ\" of=\"Φ.жаба\"><ref attr=\"у\">𝑛·0·1.дно</ref></applied>"]-    it "names an argument that is an application by an element of its own" $ do+      lines written `shouldContain` ["  <applied meta=\"𝑛·0·1\" by=\"morph\" at=\"Φ\" of=\"Φ.жаба\"><ref attr=\"у\">𝑒·0·1.дно</ref></applied>"]+    it "names an argument that is an application by an element of its own with an expression meta" $ do       (_, written) <- recordedXml (\record -> mapM_ record [EvRun Morphing "Φ", EvApplied 1 Morphing (ExApplication (ExDispatch ExRoot (AtLabel "цапля")) (ArTau AtPhi (ExApplication (ExDispatch ExRoot (AtLabel "байты")) (ArTau AtPhi (ExFormation [BiDelta (BtOne "0C")]))))) (ExFormation [BiTau AtPhi (ExFormation [BiDelta (BtOne "0C")])]) ExRoot])-      lines written `shouldContain` ["  <applied meta=\"𝑛·0·1\" of=\"Φ.байты\"><datum attr=\"φ\">0C-</datum></applied>"]+      lines written `shouldContain` ["  <applied meta=\"𝑒·0·1\" of=\"Φ.байты\"><datum attr=\"φ\">0C-</datum></applied>"]+    it "gives an application the 𝑛 the text gives it, whatever applications its argument holds" $ do+      (_, written) <- recordedXml (\record -> mapM_ record [EvRun Morphing "Φ", EvApplied 1 Morphing (ExApplication (ExDispatch ExRoot (AtLabel "цапля")) (ArTau AtPhi (ExApplication (ExDispatch ExRoot (AtLabel "байты")) (ArTau AtPhi (ExFormation [BiDelta (BtOne "0C")]))))) (ExFormation [BiTau AtPhi (ExFormation [BiDelta (BtOne "0C")])]) ExRoot])+      lines written `shouldContain` ["  <applied meta=\"𝑛·0·1\" by=\"morph\" at=\"Φ\" of=\"Φ.цапля\"><meta attr=\"φ\">𝑒·0·1</meta></applied>"]+    it "gives an answer the 𝑛 the text gives it, whatever formation it holds" $ do+      (_, written) <- recordedXml (\record -> mapM_ record [EvRun Morphing "Φ", EvAnswer 1 (ExFormation [BiLambda (Function "L_вилка"), BiVoid (AtLabel "лево"), BiTau AtPhi (ExFormation [BiLambda (FnSymbol 5)])])])+      lines written `shouldContain` ["  <answered meta=\"𝑛·0·1\">𝑒·0·1</answered>"]     it "writes an application of two arguments nothing reduced as one element" $ do       (_, written) <- recordedXml (\record -> mapM_ record [EvRun Morphing "Φ", EvAnswer 1 (ExApplication (ExApplication (ExDispatch ExRoot (AtLabel "ёж")) (ArTau (AtLabel "а") (ExFormation [BiLambda (FnSymbol 1)]))) (ArTau (AtLabel "б") (ExFormation [BiLambda (FnSymbol 2)])))])-      lines written `shouldContain` ["  <applied meta=\"𝑛·0·1\" of=\"Φ.ёж\"><symbol attr=\"а\">𝜎1</symbol><symbol attr=\"б\">𝜎2</symbol></applied>"]+      lines written `shouldContain` ["  <applied meta=\"𝑒·0·1\" of=\"Φ.ёж\"><symbol attr=\"а\">𝜎1</symbol><symbol attr=\"б\">𝜎2</symbol></applied>"]     it "writes an answer as the name of the term it holds" $ do       (_, written) <- recordedXml (\record -> mapM_ record [EvRun Morphing "Φ", EvAnswer 1 (ExApplication (ExApplication (ExDispatch ExRoot (AtLabel "ёж")) (ArTau (AtLabel "а") (ExFormation [BiLambda (FnSymbol 1)]))) (ArTau (AtLabel "б") (ExFormation [BiLambda (FnSymbol 2)])))])-      lines written `shouldContain` ["  <answered meta=\"𝑛·0·2\">𝑛·0·1</answered>"]+      lines written `shouldContain` ["  <answered meta=\"𝑛·0·1\">𝑒·0·1</answered>"]     it "writes a term a firing built as the name of the term" $ do       (_, written) <- recordedXml (\record -> mapM_ record [EvRun Morphing "Φ", EvBuilt 1 (ExApplication (ExDispatch ExRoot (AtLabel "ёж")) (ArTau (AtLabel "а") (ExFormation [BiLambda (FnSymbol 1)])))])-      lines written `shouldContain` ["  <built meta=\"𝑛·0·2\">𝑛·0·1</built>"]+      lines written `shouldContain` ["  <built meta=\"𝑛·0·1\">𝑒·0·1</built>"]     it "spells an object an application made by its name in a later element" $ do       (_, written) <- recordedXml (\record -> mapM_ record [EvRun Morphing "Φ", EvFiring 1 "L_ы" Morphing ExRoot, EvBuilt 2 (ExDispatch ExRoot (AtLabel "ю")), EvApplied 2 Morphing (ExApplication (ExDispatch ExRoot (AtLabel "ёж")) (ArTau (AtLabel "q") (ExDispatch ExRoot (AtLabel "ю")))) (ExFormation [BiTau (AtLabel "q") (ExDispatch ExRoot (AtLabel "ю"))]) ExRoot, EvAnswer 2 (ExFormation [BiTau (AtLabel "q") (ExDispatch ExRoot (AtLabel "ю"))])])       lines written `shouldContain` ["    <answered meta=\"𝑛·1·3\">𝑛·1·2</answered>"]     it "writes a formation no object of the world made as an element holding its bindings" $ do       (_, written) <- recordedXml (\record -> mapM_ record [EvRun Morphing "Φ", EvAnswer 1 (ExFormation [BiLambda (Function "L_вилка"), BiVoid (AtLabel "лево"), BiTau AtPhi (ExFormation [BiLambda (FnSymbol 5)])])])-      lines written `shouldContain` ["  <formed meta=\"𝑛·0·1\" λ=\"L_вилка\"><void attr=\"лево\"/><symbol attr=\"φ\">𝜎5</symbol></formed>"]+      lines written `shouldContain` ["  <formed meta=\"𝑒·0·1\" λ=\"L_вилка\"><void attr=\"лево\"/><symbol attr=\"φ\">𝜎5</symbol></formed>"]     it "names a formation that copies an object of the world by the object and the attributes it binds" $ do       (_, written) <- recordedXml (\record -> mapM_ record [EvRun Morphing "Φ", EvWorld (ExFormation [BiTau (AtLabel "ёж") (ExFormation [BiVoid (AtLabel "q"), BiTau (AtLabel "w") ExRoot])]), EvAnswer 1 (ExFormation [BiTau (AtLabel "q") (ExFormation [BiLambda (FnSymbol 3)]), BiTau (AtLabel "w") ExRoot])])-      lines written `shouldContain` ["  <formed meta=\"𝑛·0·1\" of=\"Φ.ёж\"><symbol attr=\"q\">𝜎3</symbol></formed>"]+      lines written `shouldContain` ["  <formed meta=\"𝑒·0·1\" of=\"Φ.ёж\"><symbol attr=\"q\">𝜎3</symbol></formed>"]     it "names a formation that replaced an attribute of an object of the world by the object and that attribute" $ do       (_, written) <- recordedXml (\record -> mapM_ record [EvRun Morphing "Φ", EvWorld (ExFormation [BiTau (AtLabel "ёж") (ExFormation [BiVoid (AtLabel "q"), BiTau AtPhi (ExDispatch ExXi (AtLabel "q")), BiTau (AtLabel "w") ExRoot])]), EvAnswer 1 (ExFormation [BiTau (AtLabel "q") (ExFormation [BiLambda (FnSymbol 3)]), BiTau AtPhi (ExFormation [BiLambda (FnSymbol 5)]), BiTau (AtLabel "w") ExRoot])])-      lines written `shouldContain` ["  <formed meta=\"𝑛·0·1\" of=\"Φ.ёж\"><symbol attr=\"q\">𝜎3</symbol><symbol attr=\"φ\">𝜎5</symbol></formed>"]+      lines written `shouldContain` ["  <formed meta=\"𝑒·0·1\" of=\"Φ.ёж\"><symbol attr=\"q\">𝜎3</symbol><symbol attr=\"φ\">𝜎5</symbol></formed>"]     it "writes a formation that keeps no binding of an object of the world as a formation of its own" $ do       (_, written) <- recordedXml (\record -> mapM_ record [EvRun Morphing "Φ", EvWorld (ExFormation [BiTau (AtLabel "ёж") (ExFormation [BiVoid AtPhi])]), EvAnswer 1 (ExFormation [BiTau AtPhi (ExFormation [BiLambda (FnSymbol 8)])])])-      lines written `shouldContain` ["  <formed meta=\"𝑛·0·1\"><symbol attr=\"φ\">𝜎8</symbol></formed>"]+      lines written `shouldContain` ["  <formed meta=\"𝑒·0·1\"><symbol attr=\"φ\">𝜎8</symbol></formed>"]     it "spells a formation that is an object of the world by its path" $ do       (_, written) <- recordedXml (\record -> mapM_ record [EvRun Morphing "Φ", EvWorld (ExFormation [BiTau (AtLabel "ёж") (ExFormation [BiVoid (AtLabel "q"), BiTau (AtLabel "w") ExRoot])]), EvAnswer 1 (ExFormation [BiVoid (AtLabel "q"), BiTau (AtLabel "w") ExRoot])])       lines written `shouldContain` ["  <answered meta=\"𝑛·0·1\">Φ.ёж</answered>"]     it "names a formation that copies an object nested in the world by its path" $ do       (_, written) <- recordedXml (\record -> mapM_ record [EvRun Morphing "Φ", EvWorld (ExFormation [BiTau (AtLabel "лес") (ExFormation [BiTau (AtLabel "ёж") (ExFormation [BiVoid (AtLabel "q"), BiLambda (Function "L_ёж")])])]), EvAnswer 1 (ExFormation [BiTau (AtLabel "q") (ExFormation [BiLambda (FnSymbol 7)]), BiLambda (Function "L_ёж")])])-      lines written `shouldContain` ["  <formed meta=\"𝑛·0·1\" of=\"Φ.лес.ёж\"><symbol attr=\"q\">𝜎7</symbol></formed>"]+      lines written `shouldContain` ["  <formed meta=\"𝑒·0·1\" of=\"Φ.лес.ёж\"><symbol attr=\"q\">𝜎7</symbol></formed>"]     it "names a method of a copy by the copy it is dispatched from" $ do       (_, written) <- recordedXml (\record -> mapM_ record [EvRun Morphing "Φ", EvWorld (ExFormation [BiTau (AtLabel "ёж") (ExFormation [BiVoid (AtLabel "q"), BiTau (AtLabel "м") (ExFormation [BiVoid AtRho, BiVoid (AtLabel "z")])])]), EvAnswer 1 (ExFormation [BiTau AtRho (ExApplication (ExDispatch ExRoot (AtLabel "ёж")) (ArTau (AtLabel "q") (ExFormation [BiLambda (FnSymbol 3)]))), BiTau (AtLabel "z") (ExFormation [BiLambda (FnSymbol 4)])])])-      lines written `shouldContain` ["  <formed meta=\"𝑛·0·2\" of=\"𝑛·0·1.м\"><symbol attr=\"z\">𝜎4</symbol></formed>"]+      lines written `shouldContain` ["  <formed meta=\"𝑒·0·2\" of=\"𝑒·0·1.м\"><symbol attr=\"z\">𝜎4</symbol></formed>"]     it "writes nothing for the world it is told" $ do       (_, written) <- recordedXml (\record -> mapM_ record [EvRun Morphing "Φ", EvWorld (ExFormation [BiTau (AtLabel "ёж") (ExFormation [])])])       filter (isInfixOf "ёж") (lines written) `shouldBe` []@@ -371,19 +420,40 @@       filter (isInfixOf "morph") (lines written) `shouldBe` []     it "names the site of a block that is an application by an element written before the block" $ do       (_, written) <- recordedXml (\record -> mapM_ record [EvRun Dataization "Φ", EvStarted 1 Morphing (ExApplication (ExDispatch ExRoot (AtLabel "ж")) (ArTau (AtLabel "q") (ExFormation [BiLambda (FnSymbol 1)]))), EvDelta 2 (BtOne "0C")])-      lines written `shouldContain` ["  <applied meta=\"𝑛·0·1\" of=\"Φ.ж\"><symbol attr=\"q\">𝜎1</symbol></applied>", "  <morph at=\"𝑛·0·1\">"]+      lines written `shouldContain` ["  <applied meta=\"𝑒·0·1\" of=\"Φ.ж\"><symbol attr=\"q\">𝜎1</symbol></applied>", "  <morph at=\"𝑒·0·1\">"]     it "names nothing for the site of a block nothing stands in" $ do       (_, written) <- recordedXml (\record -> mapM_ record [EvRun Dataization "Φ", EvStarted 1 Morphing (ExApplication (ExDispatch ExRoot (AtLabel "ж")) (ArTau (AtLabel "q") (ExFormation [BiLambda (FnSymbol 1)]))), EvDelta 1 (BtOne "0C")])       filter (isInfixOf "applied") (lines written) `shouldBe` []     it "names the site of a firing that is an application by an element written before the firing" $ do       (_, written) <- recordedXml (\record -> mapM_ record [EvRun Morphing "Φ", EvFiring 1 "L_ж" Morphing (ExApplication (ExDispatch ExRoot (AtLabel "ж")) (ArTau (AtLabel "q") (ExFormation [BiLambda (FnSymbol 1)])))])-      lines written `shouldContain` ["  <applied meta=\"𝑛·0·1\" of=\"Φ.ж\"><symbol attr=\"q\">𝜎1</symbol></applied>", "  <evaluate λ=\"L_ж\" by=\"morph\" at=\"𝑛·0·1\">"]+      lines written `shouldContain` ["  <applied meta=\"𝑒·0·1\" of=\"Φ.ж\"><symbol attr=\"q\">𝜎1</symbol></applied>", "  <evaluate λ=\"L_ж\" by=\"morph\" at=\"𝑒·0·1\">"]     it "writes one element for two blocks of one judgment at one site in a row" $ do       (_, written) <- recordedXml (\record -> mapM_ record [EvRun Dataization "Φ", EvStarted 1 Morphing (ExDispatch ExRoot (AtLabel "w")), EvApplied 2 Morphing (ExApplication (ExDispatch ExRoot (AtLabel "ёж")) (ArTau (AtLabel "q") (ExFormation []))) (ExFormation [BiTau (AtLabel "q") (ExFormation [])]) (ExDispatch ExRoot (AtLabel "w")), EvStarted 1 Morphing (ExDispatch ExRoot (AtLabel "w")), EvApplied 2 Morphing (ExApplication (ExDispatch ExRoot (AtLabel "ёж")) (ArTau (AtLabel "q") (ExFormation []))) (ExFormation [BiTau (AtLabel "q") (ExFormation [])]) (ExDispatch ExRoot (AtLabel "w"))])       length (filter (isInfixOf "<morph") (lines written)) `shouldBe` 1     it "closes a block element before the element that follows beside it" $ do       (_, written) <- recordedXml (\record -> mapM_ record [EvRun Dataization "Φ", EvStarted 1 Morphing (ExDispatch ExRoot (AtLabel "w")), EvApplied 2 Morphing (ExApplication (ExDispatch ExRoot (AtLabel "ёж")) (ArTau (AtLabel "q") (ExFormation []))) (ExFormation [BiTau (AtLabel "q") (ExFormation [])]) (ExDispatch ExRoot (AtLabel "w")), EvDelta 1 (BtOne "0D")])       lines written `shouldContain` ["  </morph>", "  <dataized meta=\"𝛿·0·1\">0D-</dataized>"]+    it "writes the box step of a dataization as an element naming the φ it took" $ do+      (_, written) <- recordedXml (\record -> mapM_ record [EvRun Dataization "Φ", EvStarted 1 Morphing (ExDispatch ExRoot (AtLabel "гнёт")), EvApplied 2 Morphing (ExApplication (ExDispatch ExRoot (AtLabel "щур")) (ArTau (AtLabel "ял") (ExFormation [BiDelta (BtOne "3C")]))) (ExFormation [BiTau (AtLabel "ял") (ExFormation [BiDelta (BtOne "3C")]), BiTau AtPhi (ExDispatch ExXi (AtLabel "ял"))]) (ExDispatch ExRoot (AtLabel "гнёт")), EvBoxed 1 (ExDispatch (ExFormation [BiTau (AtLabel "ял") (ExFormation [BiDelta (BtOne "3C")]), BiTau AtPhi (ExDispatch ExXi (AtLabel "ял"))]) AtPhi)])+      lines written `shouldContain` ["  </morph>", "  <boxed meta=\"𝑛·0·2\">𝑛·0·1.φ</boxed>"]+    it "names the term a morphing came to by its meta in the element of the box after it, though an application named it before" $ do+      (_, written) <- recordedXml (\record -> mapM_ record [EvRun Dataization "Φ.ж", EvStarted 1 Morphing (ExDispatch ExRoot (AtLabel "ж")), EvApplied 2 Morphing (ExApplication (ExDispatch ExRoot (AtLabel "рысь")) (ArTau (AtLabel "пух") (ExFormation [BiDelta (BtOne "4E")]))) (ExFormation [BiTau (AtLabel "пух") (ExFormation [BiDelta (BtOne "4E")]), BiTau AtPhi (ExDispatch ExXi (AtLabel "пух"))]) (ExDispatch ExRoot (AtLabel "ж")), EvMorphed 2 (ExFormation [BiTau (AtLabel "пух") (ExFormation [BiDelta (BtOne "4E")]), BiTau AtPhi (ExDispatch ExXi (AtLabel "пух"))]), EvBoxed 1 (ExDispatch (ExFormation [BiTau (AtLabel "пух") (ExFormation [BiDelta (BtOne "4E")]), BiTau AtPhi (ExDispatch ExXi (AtLabel "пух"))]) AtPhi)])+      lines written `shouldContain` ["  <boxed meta=\"𝑛·0·3\">𝑛·0·2.φ</boxed>"]+    it "names the answer of a firing by its meta in the element of the box after it" $ do+      (_, written) <- recordedXml (\record -> mapM_ record [EvRun Dataization "Φ", EvFiring 1 "L_сова" Dataization ExRoot, EvAnswer 2 (ExFormation [BiTau (AtLabel "мох") (ExFormation []), BiTau AtPhi (ExDispatch ExXi (AtLabel "мох"))]), EvBoxed 1 (ExDispatch (ExFormation [BiTau (AtLabel "мох") (ExFormation []), BiTau AtPhi (ExDispatch ExXi (AtLabel "мох"))]) AtPhi)])+      lines written `shouldContain` ["  <boxed meta=\"𝑛·0·1\">𝑛·1·1.φ</boxed>"]+    it "writes a morph element again for a morphing that follows a box step" $ do+      (_, written) <- recordedXml (\record -> mapM_ record [EvRun Dataization "Φ", EvStarted 1 Morphing (ExDispatch ExRoot (AtLabel "гнёт")), EvApplied 2 Morphing (ExApplication (ExDispatch ExRoot (AtLabel "щур")) (ArTau (AtLabel "ял") (ExFormation [BiDelta (BtOne "3C")]))) (ExFormation [BiTau (AtLabel "ял") (ExFormation [BiDelta (BtOne "3C")]), BiTau AtPhi (ExDispatch ExXi (AtLabel "ял"))]) (ExDispatch ExRoot (AtLabel "гнёт")), EvBoxed 1 (ExDispatch (ExFormation [BiTau (AtLabel "ял") (ExFormation [BiDelta (BtOne "3C")]), BiTau AtPhi (ExDispatch ExXi (AtLabel "ял"))]) AtPhi), EvStarted 1 Morphing (ExDispatch ExRoot (AtLabel "гнёт")), EvApplied 2 Morphing (ExApplication (ExDispatch ExRoot (AtLabel "щур")) (ArTau (AtLabel "ял") (ExBytes (BtOne "3C")))) (ExFormation [BiTau (AtLabel "ял") (ExBytes (BtOne "3C"))]) (ExDispatch ExRoot (AtLabel "гнёт"))])+      length (filter (isInfixOf "<morph at=") (lines written)) `shouldBe` 2+    it "names the world it is told by Φ in the element of a box step" $ do+      (_, written) <- recordedXml (\record -> mapM_ record [EvRun Dataization "Φ", EvWorld (ExFormation [BiTau (AtLabel "вьюн") (ExFormation []), BiTau AtPhi (ExDispatch ExXi (AtLabel "вьюн"))]), EvBoxed 1 (ExDispatch (ExFormation [BiTau (AtLabel "вьюн") (ExFormation []), BiTau AtPhi (ExDispatch ExXi (AtLabel "вьюн"))]) AtPhi)])+      lines written `shouldContain` ["  <boxed meta=\"𝑛·0·1\">Φ.φ</boxed>"]+    it "names a morph element that follows a box step by the meta the box step bound" $ do+      (_, written) <- recordedXml (\record -> mapM_ record [EvRun Dataization "Φ", EvWorld (ExFormation [BiTau (AtLabel "кулик") (ExFormation []), BiTau AtPhi (ExDispatch ExXi (AtLabel "кулик"))]), EvBoxed 1 (ExDispatch (ExFormation [BiTau (AtLabel "кулик") (ExFormation []), BiTau AtPhi (ExDispatch ExXi (AtLabel "кулик"))]) AtPhi), EvStarted 1 Morphing (ExDispatch (ExFormation [BiTau (AtLabel "кулик") (ExFormation []), BiTau AtPhi (ExDispatch ExXi (AtLabel "кулик"))]) AtPhi), EvApplied 2 Morphing (ExApplication (ExDispatch ExRoot (AtLabel "чиж")) (ArTau (AtLabel "уж") (ExBytes (BtOne "5E")))) (ExFormation [BiTau (AtLabel "уж") (ExBytes (BtOne "5E"))]) (ExDispatch (ExFormation [BiTau (AtLabel "кулик") (ExFormation []), BiTau AtPhi (ExDispatch ExXi (AtLabel "кулик"))]) AtPhi)])+      lines written `shouldContain` ["  <boxed meta=\"𝑛·0·1\">Φ.φ</boxed>", "  <morph at=\"𝑛·0·1\">"]+    it "spells out again in an element the φ a box step takes once more" $ do+      (_, written) <- recordedXml (\record -> mapM_ record [EvRun Dataization "Φ", EvWorld (ExFormation [BiTau (AtLabel "лунь") (ExFormation []), BiTau AtPhi (ExDispatch ExXi (AtLabel "лунь"))]), EvBoxed 1 (ExDispatch (ExFormation [BiTau (AtLabel "лунь") (ExFormation []), BiTau AtPhi (ExDispatch ExXi (AtLabel "лунь"))]) AtPhi), EvBoxed 1 (ExDispatch (ExFormation [BiTau (AtLabel "лунь") (ExFormation []), BiTau AtPhi (ExDispatch ExXi (AtLabel "лунь"))]) AtPhi)])+      lines written `shouldContain` ["  <boxed meta=\"𝑛·0·2\">Φ.φ</boxed>"]     it "names the element over an answer by the element that built it" $ do       (_, written) <- recordedXml (\record -> mapM_ record [EvRun Dataization "Φ", EvFiring 1 "L_ф" Dataization ExRoot, EvBuilt 2 (ExDispatch ExRoot (AtLabel "ю")), EvStarted 2 Morphing (ExDispatch ExRoot (AtLabel "ю")), EvApplied 3 Morphing (ExApplication (ExDispatch ExRoot (AtLabel "ёж")) (ArTau (AtLabel "q") (ExFormation []))) (ExFormation [BiTau (AtLabel "q") (ExFormation [])]) (ExDispatch ExRoot (AtLabel "ю"))])       lines written `shouldContain` ["    <morph at=\"𝑛·1·1\">"]@@ -392,7 +462,7 @@       lines written `shouldContain` ["  <deferred symbol=\"𝜎9\" by=\"morph\" at=\"Φ\" of=\"Φ.ёж\"><with><symbol attr=\"щ\">𝜎4</symbol></with></deferred>"]     it "writes a deferred copy that names no call as an element naming the copy" $ do       (_, written) <- recordedXml (\record -> mapM_ record [EvRun Morphing "Φ", EvDeferred 1 9 Morphing (ExFormation [BiTau (AtLabel "щ") (ExFormation [BiLambda (FnSymbol 4)])]) Nothing ExRoot])-      lines written `shouldContain` ["  <deferred symbol=\"𝜎9\" by=\"morph\" at=\"Φ\" of=\"𝑛·0·1\"/>"]+      lines written `shouldContain` ["  <deferred symbol=\"𝜎9\" by=\"morph\" at=\"Φ\" of=\"𝑒·0·1\"/>"]     it "names an argument of a copy nothing worked out by an expression meta" $ do       (_, written) <- recordedXml (\record -> mapM_ record [EvRun Morphing "Φ", EvDeferred 1 9 Morphing (ExFormation []) (Just (ExApplication (ExDispatch ExRoot (AtLabel "ёж")) (ArTau (AtLabel "щ") (ExApplication (ExDispatch ExRoot (AtLabel "ю")) (ArTau (AtLabel "ж") (ExFormation [BiLambda (FnSymbol 2)])))))) ExRoot])       lines written `shouldSatisfy` any (isInfixOf "<meta attr=\"щ\">𝑒1</meta>")@@ -401,7 +471,7 @@       length (filter (isInfixOf ">𝑒1<") (lines written)) `shouldBe` 2     it "writes a cut that answered no copy as an element naming the formation it entered" $ do       (_, written) <- recordedXml (\record -> mapM_ record [EvRun Morphing "Φ", EvLooped 1 Morphing Proven (ExFormation [BiTau (AtLabel "ъ") ExRoot]) ExRoot])-      lines written `shouldContain` ["  <looped by=\"morph\" match=\"proven\" at=\"Φ\" of=\"𝑛·0·1\"/>"]+      lines written `shouldContain` ["  <looped by=\"morph\" match=\"proven\" at=\"Φ\" of=\"𝑒·0·1\"/>"]     it "names an argument of a deferred copy by the application that made it" $ do       (_, written) <- recordedXml (\record -> mapM_ record [EvRun Morphing "Φ", EvApplied 1 Morphing (ExApplication (ExDispatch ExRoot (AtLabel "кулик")) (ArTau (AtLabel "ш") ExRoot)) (ExFormation [BiTau (AtLabel "ш") ExRoot]) ExRoot, EvDeferred 1 31 Morphing (ExFormation [BiTau (AtLabel "щ") (ExFormation [BiTau (AtLabel "ш") ExRoot]), BiTau AtPhi (ExDispatch ExXi (AtLabel "щ"))]) (Just (ExApplication (ExDispatch ExRoot (AtLabel "ёж")) (ArTau (AtLabel "щ") (ExFormation [BiTau (AtLabel "ш") ExRoot])))) ExRoot])       lines written `shouldContain` ["  <deferred symbol=\"𝜎31\" by=\"morph\" at=\"Φ\" of=\"Φ.ёж\"><with><meta attr=\"щ\">𝑛·0·1</meta></with></deferred>"]@@ -428,7 +498,7 @@       lines written `shouldContain` ["    <diverged left=\"𝜎8\" right=\"𝑛6·1\" at=\".λ\" reason=\"the left side has λ\"/>"]     it "writes the formation a join fabricated as an element naming both sides in the markup" $ do       (_, written) <- recordedXml (\record -> mapM_ record [EvRun Morphing "Φ", EvFiring 1 "L_ф" Morphing ExRoot, EvFabricated 2 (ExMeta "n3", ExFormation [BiTau AtPhi (ExDispatch ExRoot (AtLabel "ёж"))]) (ExMeta "n4", ExFormation [BiTau (AtLabel "к") (ExDispatch ExRoot (AtLabel "щука"))]) (ExFormation [BiTau AtPhi (ExFormation [BiLambda (Function "L_ж"), BiTau (AtLabel "on") (ExFormation [BiDelta (BtOne "05")]), BiTau (AtLabel "left") (ExDispatch (ExFormation [BiTau AtPhi (ExDispatch ExRoot (AtLabel "ёж"))]) AtPhi), BiTau (AtLabel "right") (ExDispatch (ExFormation [BiTau (AtLabel "к") (ExDispatch ExRoot (AtLabel "щука"))]) AtPhi)]), BiTau (AtLabel "к") (ExFormation [BiLambda (Function "L_ж"), BiTau (AtLabel "on") (ExFormation [BiDelta (BtOne "05")]), BiTau (AtLabel "left") (ExDispatch (ExFormation [BiTau AtPhi (ExDispatch ExRoot (AtLabel "ёж"))]) (AtLabel "к")), BiTau (AtLabel "right") (ExDispatch (ExFormation [BiTau (AtLabel "к") (ExDispatch ExRoot (AtLabel "щука"))]) (AtLabel "к"))])])])-      lines written `shouldContain` ["    <fabricated meta=\"𝑛·1·6\" left=\"𝑛3·1\" right=\"𝑛4·1\">𝑛·1·5</fabricated>"]+      lines written `shouldContain` ["    <fabricated meta=\"𝑛·1·1\" left=\"𝑛3·1\" right=\"𝑛4·1\">𝑒·1·5</fabricated>"]     it "writes the symbol a symbolize line minted as an element holding the datum it stands for" $ do       (_, written) <- recordedXml (\record -> mapM_ record [EvRun Morphing "Φ", EvFiring 1 "L_ф" Morphing ExRoot, EvSymbolized 2 6 (BtOne "4A")])       lines written `shouldContain` ["    <symbolized symbol=\"𝜎6\">4A-</symbolized>"]
test/EmitSpec.hs view
@@ -45,6 +45,9 @@     it "tells two rules of the same name apart" $       fromRight "" (emitted [] [Y.Rule "ov" Nothing Nothing ExXi ExRoot Nothing Nothing Nothing, Y.Rule "ov" Nothing Nothing ExRoot ExXi Nothing Nothing Nothing] [] [] [] [])         `shouldSatisfy` (\source -> all (`isInfixOf` source) ["stepOv0 ::", "stepOv1 ::"])+    it "keeps the comment of a rule named by a folded scalar on one line" $+      fromRight "" (emitted [] [Y.Rule "folded rule name\n" Nothing Nothing ExXi ExRoot Nothing Nothing Nothing] [] [] [] [])+        `shouldSatisfy` ("-- The rule 'folded rule name'.\n" `isInfixOf`)     it "writes a function for every rule of morphing" $       fromRight "" (emitted [] [] [] Y.morphingRules [] [])         `shouldSatisfy` (\source -> all (\rule -> ("-- The morphing rule '" ++ rule.name ++ "'.") `isInfixOf` source) Y.morphingRules)
test/EncodingSpec.hs view
@@ -81,7 +81,7 @@      it "leaves every other constructor untouched" $ do       toASCII (EX_STRING "hi" (TAB 1) []) `shouldBe` EX_STRING "hi" (TAB 1) []-      toASCII (EX_NUMBER (Left 5) (TAB 1) []) `shouldBe` EX_NUMBER (Left 5) (TAB 1) []+      toASCII (EX_NUMBER 5 (TAB 1) []) `shouldBe` EX_NUMBER 5 (TAB 1) []       toASCII (EX_BYTES BT_EMPTY) `shouldBe` EX_BYTES BT_EMPTY    describe "toASCII on APP_BINDING" $
test/FilterSpec.hs view
@@ -116,6 +116,20 @@         map fst included `shouldBe` [expected, expected]         map snd included `shouldBe` [Just (Normalization, "rule-a"), Just (Evaluation, "rule-b")] +      forM_+        [ ("finds a nested object after a non-formation sibling", "[[ a -> Q, b -> [[ c -> Q ]] ]]", "Q.b.c", "[[ b -> [[ c -> Q ]] ]]")+        , ("finds a nested object after several non-formation siblings", "[[ a -> Q, d -> ?, b -> [[ c -> Q ]] ]]", "Q.b.c", "[[ b -> [[ c -> Q ]] ]]")+        , ("finds a deeper object after a non-formation sibling", "[[ a -> Q, b -> [[ x -> ?, c -> [[ d -> ? ]] ]] ]]", "Q.b.c.d", "[[ b -> [[ c -> [[ d -> ? ]] ]] ]]")+        , ("finds a nested object before a non-formation sibling", "[[ b -> [[ c -> Q ]], a -> Q ]]", "Q.b.c", "[[ b -> [[ c -> Q ]] ]]")+        ]+        ( \(desc, exprText, fqnText, expectedText) -> it desc $ do+            expr <- parseExpressionThrows exprText+            fqn <- parseExpressionThrows fqnText+            expected <- parseExpressionThrows expectedText+            [(expr', _)] <- F.include [(expr, Nothing)] [fqn]+            expr' `shouldBe` expected+        )+       it "keeps every matching fqn, not only the first one" $ do         expr <- parseExpressionThrows "[[ x -> ?, y -> ? ]]"         firstFqn <- parseExpressionThrows "Q.x"@@ -123,3 +137,18 @@         expected <- parseExpressionThrows "[[ x -> ?, y -> ? ]]"         [(expr', _)] <- F.include [(expr, Nothing)] [firstFqn, secondFqn]         expr' `shouldBe` expected++      forM_+        [ ("merges sibling children of one parent into a single binding", "[[ a -> [[ x -> Q, y -> Q ]], b -> Q ]]", ["Q.a.x", "Q.a.y"], "[[ a -> [[ x -> Q, y -> Q ]] ]]")+        , ("merges siblings at several depths", "[[ a -> [[ c -> [[ x -> ?, y -> ? ]], d -> ? ]], b -> ? ]]", ["Q.a.c.x", "Q.a.d", "Q.a.c.y"], "[[ a -> [[ c -> [[ x -> ?, y -> ? ]], d -> ? ]] ]]")+        , ("keeps a single nested child alone", "[[ a -> [[ x -> Q, y -> Q ]], b -> Q ]]", ["Q.a.x"], "[[ a -> [[ x -> Q ]] ]]")+        , ("keeps distinct top-level attributes", "[[ a -> [[ x -> Q ]], b -> Q ]]", ["Q.a", "Q.b"], "[[ a -> [[ x -> Q ]], b -> Q ]]")+        , ("does not repeat a binding selected twice", "[[ a -> [[ x -> Q ]], b -> Q ]]", ["Q.a.x", "Q.a.x"], "[[ a -> [[ x -> Q ]] ]]")+        ]+        ( \(desc, exprText, fqnTexts, expectedText) -> it desc $ do+            expr <- parseExpressionThrows exprText+            fqns <- traverse parseExpressionThrows fqnTexts+            expected <- parseExpressionThrows expectedText+            [(expr', _)] <- F.include [(expr, Nothing)] fqns+            expr' `shouldBe` expected+        )
test/FunctionsSpec.hs view
@@ -164,6 +164,12 @@         , \term -> expectExpression term (DataString (strToBts "5"))         )       ,+        ( "string keeps the fraction of a number"+        , "string"+        , [ArgExpression (DataNumber (numToBts (-0.375)))]+        , \term -> expectExpression term (DataString (strToBts "-0.375"))+        )+      ,         ( "string keeps a string expression as is"         , "string"         , [ArgExpression (DataString (strToBts "already"))]@@ -221,6 +227,12 @@         , "sed pattern must be in format s/pat/rep/[g]"         )       ,+        ( "sed fails when the replacement has no closing separator"+        , "sed"+        , [ArgExpression (DataString (strToBts "hello")), ArgExpression (DataString (strToBts "s/a/b"))]+        , "sed pattern must be in format s/pat/rep/[g]"+        )+      ,         ( "sed fails when the pattern is only a delimiter"         , "sed"         , [ArgExpression (DataString (strToBts "hello")), ArgExpression (DataString (strToBts "s/"))]@@ -267,6 +279,12 @@         , "sum"         , [ArgExpression (DataNumber (BtMany ["68", "65", "6C", "6C", "6F"]))]         , "Expected 8 bytes for a number, got 5"+        )+      ,+        ( "sum fails on a string even if it is 8 bytes long"+        , "sum"+        , [ArgExpression (DataString (strToBts "abcdefgh"))]+        , "Only numbers and bytes are supported by 'sum' function"         )       , ("sum fails on an empty argument list", "sum", [], "sum() requires at least 1 argument")       ,
test/LambdasSpec.hs view
@@ -38,6 +38,7 @@ order known func = map bound (lined known func)   where     bound :: Line -> T.Text+    bound (Dataized meta _) = _spelling meta     bound (Morphed meta _) = _spelling meta     bound (Evaluated meta _) = _spelling meta     bound (Deferred meta _) = _spelling meta@@ -45,6 +46,9 @@     bound (Symbolized meta _) = _spelling meta     bound (Rewritten meta _ _) = _spelling meta +dataizes :: Lambdas -> T.Text -> [(T.Text, T.Text)]+dataizes known func = [(_spelling meta, _spelling source) | Dataized meta source <- lined known func]+ morphs :: Lambdas -> T.Text -> [(T.Text, T.Text)] morphs known func = [(_spelling meta, _spelling source) | Morphed meta source <- lined known func] @@ -130,6 +134,22 @@       known <- lambdasOf "- λ: L_pair\n  dataize:\n    𝛿1: $.ρ\n  𝑛: ⟦ λ ⤍ 𝜎 ⟧\n"       map (_name . fst) (maybe [] _dataized (matched known "L_pair")) `shouldBe` ["d1"] +    it "keeps under 'dataize' only the operands reading '$'" $ do+      known <- lambdasOf "- λ: L_entry\n  morph:\n    𝑛1: $.v.φ\n  dataize:\n    𝛿1: $.n\n    𝛿2: 𝑛1\n  𝑛: ⟦ ⟧\n"+      map (_spelling . fst) (maybe [] _dataized (matched known "L_entry")) `shouldBe` ["𝛿1"]++    it "reads an operand of 'dataize' naming a meta as a line reading that meta" $ do+      known <- lambdasOf "- λ: L_entry\n  morph:\n    𝑛1: $.v.φ\n  dataize:\n    𝛿1: $.n\n    𝛿2: 𝑛1\n  𝑛: ⟦ ⟧\n"+      dataizes known "L_entry" `shouldBe` [("𝛿2", "𝑛1")]++    it "runs a 'dataize' line after the 'stay' line binding the meta it reads" $ do+      known <- lambdasOf "- λ: L_hold\n  morph:\n    𝑛1: $.a\n  dataize:\n    𝛿1: 𝑛2\n  stay:\n    𝑛2: 𝑛1\n  𝑛: 𝑛2\n"+      order known "L_hold" `shouldBe` ["𝑛2", "𝛿1"]++    it "runs a 'dataize' line before an 'evaluate' line no meta orders" $ do+      known <- lambdasOf "- λ: L_hold\n  morph:\n    𝑛1: $.a\n  dataize:\n    𝛿1: 𝑛1\n  evaluate:\n    𝑛2: 𝑛1\n  𝑛: 𝑛2\n"+      order known "L_hold" `shouldBe` ["𝛿1", "𝑛2"]+     it "reads the operands of 'morph' under expression metas" $ do       known <- lambdasOf "- λ: L_pick\n  morph:\n    𝑛1: $.then\n    𝑛2: $.else\n  𝑛: 𝑛1\n"       map (_spelling . fst) (maybe [] _morphed (matched known "L_pick")) `shouldBe` ["𝑛1", "𝑛2"]@@ -356,7 +376,11 @@       , ("two lines reading what each other made", "- λ: L_hold\n  morph:\n    𝑛1: $.a\n  evaluate:\n    𝑛2: 𝑛3\n  defer:\n    𝑛3: 𝑛2\n  𝑛: 𝑛2\n", "The line binding '𝑛2' of λ function 'L_hold' reads the meta it binds")       , ("a line reading what two lines reading each other made, blaming one of the two", "- λ: L_hold\n  morph:\n    𝑛1: $.a\n  evaluate:\n    𝑛5: 𝑛3\n  defer:\n    𝑛3: 𝑛4\n    𝑛4: 𝑛3\n  𝑛: 𝑛5\n", "The line binding '𝑛3' of λ function 'L_hold' reads the meta it binds")       , ("an answer naming a meta no block binds", "- λ: L_pair\n  dataize:\n    𝛿1: $.ρ\n  𝑛: 𝑛7\n", "reads the meta 'n7' that no block binds")-      , ("an operand of 'dataize' reading a meta", "- λ: L_pair\n  dataize:\n    𝛿1: 𝑛5\n  𝑛: ⟦ λ ⤍ 𝜎 ⟧\n", "of 'dataize' of λ function 'L_pair' reads the meta 'n5'")+      , ("an operand of 'dataize' reading a meta", "- λ: L_pair\n  dataize:\n    𝛿1: $.x.𝜏1\n  𝑛: ⟦ λ ⤍ 𝜎 ⟧\n", "of 'dataize' of λ function 'L_pair' reads the meta 't1'")+      , ("an operand of 'dataize' reading a meta inside a term", "- λ: L_hold\n  morph:\n    𝑛1: $.a\n  dataize:\n    𝛿1: 𝑛1.x\n  𝑛: 𝑛1\n", "The operand '𝛿1' of 'dataize' of λ function 'L_hold' reads the meta 'n1', while only a path from '$' or a meta alone can be reduced there")+      , ("an operand of 'dataize' naming a meta nothing binds", "- λ: L_hold\n  dataize:\n    𝛿1: 𝑛5\n  𝑛: ⟦ λ ⤍ 𝜎 ⟧\n", "The operand '𝑛5' of λ function 'L_hold' names no meta bound by 'morph' or by any line")+      , ("an operand of 'dataize' which is no term of the calculus", "- λ: L_hold\n  dataize:\n    𝛿1: ⟦ λ ⤍\n  𝑛: ⟦ λ ⤍ 𝜎 ⟧\n", "Error in $[0].dataize.𝛿1: expression:1:6")+      , ("an operand of 'dataize' naming a meta that came down to data", "- λ: L_hold\n  dataize:\n    𝛿1: $.a\n    𝛿2: 𝛿1\n  𝑛: ⟦ λ ⤍ 𝜎 ⟧\n", "Error in $[0].dataize.𝛿2")       , ("an operand of 'morph' reading a meta", "- λ: L_pair\n  morph:\n    𝑛1: $.x.𝜏1\n  𝑛: 𝑛1\n", "of 'morph' of λ function 'L_pair' reads the meta 't1'")       , ("an operand of 'morph' reading a meta inside a term", "- λ: L_hold\n  morph:\n    𝑛1: $.a\n    𝑛2: 𝑛1.x\n  𝑛: 𝑛2\n", "The operand '𝑛2' of 'morph' of λ function 'L_hold' reads the meta 'n1', while only a path from '$' or a meta alone can be reduced there")       , ("an operand of 'morph' naming a meta nothing binds", "- λ: L_hold\n  morph:\n    𝑛1: $.a\n    𝑛2: 𝑛5\n  𝑛: 𝑛2\n", "The operand '𝑛5' of λ function 'L_hold' names no meta bound by 'morph' or by any line")@@ -387,8 +411,9 @@       , ("a meta bound by 'morph' and again by 'stay'", "- λ: L_hold\n  morph:\n    𝑛1: $.a\n  stay:\n    𝑛1: 𝑛1\n  𝑛: 𝑛1\n", "The meta '𝑛1' of λ function 'L_hold' is bound by more than one line")       , ("a meta bound by 'morph' and again by 'defer'", "- λ: L_hold\n  morph:\n    𝑛1: $.a\n  defer:\n    𝑛1: 𝑛1\n  𝑛: 𝑛1\n", "The meta '𝑛1' of λ function 'L_hold' is bound by more than one line")       , ("an entry with both an '𝑛' key and a 'join' block", joint <> "  𝑛: ⟦ λ ⤍ 𝜎 ⟧\n", "The entry of λ function 'L_fork' has both an '𝑛' key and a 'join' block, while a join answers the term it joins")-      , ("a 'join' block joining on an expression meta", "- λ: L_fork\n  join:\n    on: 𝑛1\n    left: $.a\n    right: $.b\n", "The 'on' of the 'join' block of λ function 'L_fork' is '𝑛1', while it must name a bytes meta of 'dataize', such as '𝛿1', or be a path from '$', such as '$.on'")+      , ("a 'join' block joining on an expression meta", "- λ: L_fork\n  join:\n    on: 𝑛1\n    left: $.a\n    right: $.b\n", "The 'on' of the 'join' block of λ function 'L_fork' is '𝑛1', while it must name a bytes meta of 'dataize' reading a path from '$', such as '𝛿1', or be a path from '$', such as '$.on'")       , ("a 'join' block joining on a bytes meta 'dataize' never bound", "- λ: L_fork\n  dataize:\n    𝛿1: $.c\n  join:\n    on: 𝛿2\n    left: $.a\n    right: $.b\n", "The 'on' of the 'join' block of λ function 'L_fork' is '𝛿2', while it must name a bytes meta of 'dataize'")+      , ("a 'join' block joining on a bytes meta a 'dataize' line binds", "- λ: L_fork\n  dataize:\n    𝛿1: $.c\n    𝛿2: 𝑛1\n  morph:\n    𝑛1: $.d\n  join:\n    on: 𝛿2\n    left: $.a\n    right: $.b\n", "The 'on' of the 'join' block of λ function 'L_fork' is '𝛿2', while it must name a bytes meta of 'dataize' reading a path from '$'")       , ("a 'join' block joining on a path off 'Φ'", "- λ: L_fork\n  join:\n    on: Φ.c\n    left: $.a\n    right: $.b\n", "The 'on' of the 'join' block of λ function 'L_fork' is 'Φ.c'")       , ("a 'join' block joining on an application", "- λ: L_fork\n  join:\n    on: $.c(x ↦ $.d)\n    left: $.a\n    right: $.b\n", "The 'on' of the 'join' block of λ function 'L_fork' is '$.c(x ↦ $.d)'")       , ("a 'join' block with no 'on' key", "- λ: L_fork\n  dataize:\n    𝛿1: $.c\n  join:\n    left: $.a\n    right: $.b\n", "The 'join' block of λ function 'L_fork' has no 'on' key")@@ -531,15 +556,27 @@       masked <- parseExpressionThrows "⟦ φ ↦ ⟦ φ ↦ ⟦ λ ⤍ 𝜎5 ⟧ ⟧ ⟧"       (\(made, _, _) -> made) (symbolized term 4) `shouldBe` masked -    it "turns the datum a ρ of a formation holds into a fresh symbol" $ do+    it "keeps the datum the ρ of a formation holding no λ binds" $ do       term <- parseExpressionThrows "⟦ ρ ↦ ⟦ Δ ⤍ 02- ⟧, q ↦ ∅ ⟧"-      masked <- parseExpressionThrows "⟦ ρ ↦ ⟦ λ ⤍ 𝜎10 ⟧, q ↦ ∅ ⟧"-      (\(made, _, _) -> made) (symbolized term 9) `shouldBe` masked+      (\(made, _, _) -> made) (symbolized term 9) `shouldBe` term -    it "turns the datum a method of a formation holds into a fresh symbol" $ do+    it "keeps the datum a method of a formation holding no λ binds" $ do       term <- parseExpressionThrows "⟦ φ ↦ ⟦⟧, neg ↦ ⟦ Δ ⤍ BF-F0-00-00-00-00-00-00 ⟧ ⟧"-      masked <- parseExpressionThrows "⟦ φ ↦ ⟦⟧, neg ↦ ⟦ λ ⤍ 𝜎10 ⟧ ⟧"-      (\(made, _, _) -> made) (symbolized term 9) `shouldBe` masked+      (\(made, _, _) -> made) (symbolized term 9) `shouldBe` term++    it "mints no symbol for a constant the method of a number holds" $ do+      term <- parseExpressionThrows "⟦ φ ↦ Φ.bytes( φ ↦ ⟦ Δ ⤍ 40-45-00-00-00-00-00-00 ⟧ ), abs ↦ ⟦ φ ↦ ξ.ρ.lt( Φ.number( φ ↦ Φ.bytes( φ ↦ ⟦ Δ ⤍ 00-00-00-00-00-00-00-00 ⟧ ) ) ) ⟧ ⟧"+      (\(_, known, _) -> known) (symbolized term 603) `shouldBe` [(604, BtMany ["40", "45", "00", "00", "00", "00", "00", "00"])]++    it "turns the datum the ρ of an atom binds into a fresh symbol" $ do+      term <- parseExpressionThrows "⟦ λ ⤍ L_bytes_size, ρ ↦ ⟦ Δ ⤍ 2B-07 ⟧ ⟧"+      masked <- parseExpressionThrows "⟦ λ ⤍ L_bytes_size, ρ ↦ ⟦ λ ⤍ 𝜎32 ⟧ ⟧"+      (\(made, _, _) -> made) (symbolized term 31) `shouldBe` masked++    it "turns the datum an attribute of an atom binds into a fresh symbol" $ do+      term <- parseExpressionThrows "Φ.bool( if ↦ ⟦ λ ⤍ L_fork, left ↦ ∅, right ↦ ∅, on🌵 ↦ ⟦ Δ ⤍ FF- ⟧ ⟧ )"+      masked <- parseExpressionThrows "Φ.bool( if ↦ ⟦ λ ⤍ L_fork, left ↦ ∅, right ↦ ∅, on🌵 ↦ ⟦ λ ⤍ 𝜎58 ⟧ ⟧ )"+      (\(made, _, _) -> made) (symbolized term 57) `shouldBe` masked      it "turns the datum a named argument other than φ holds into a fresh symbol" $ do       term <- parseExpressionThrows "Φ.f( q ↦ ⟦ Δ ⤍ 1D- ⟧ )"
test/LiningSpec.hs view
@@ -60,7 +60,7 @@         , EX_ATTR (AT_LABEL "z")         , EX_TERMINATION DEAD         , EX_STRING "hi" (TAB 2) []-        , EX_NUMBER (Left 5) (TAB 2) []+        , EX_NUMBER 5 (TAB 2) []         , EX_NONFINITE Φ NfNan (TAB 2) []         , EX_META (META NO_EXCL E "x")         , EX_BYTES BT_EMPTY
test/MarginSpec.hs view
@@ -14,6 +14,7 @@ import Margin (withLines, withMargin) import Render (render) import Test.Hspec (Spec, describe, it, shouldBe, shouldNotBe)+import Text.Printf (printf)  bigLabel :: Attribute bigLabel = AtLabel "aVeryLongAttributeNameThatWontFitOnOneLine"@@ -273,6 +274,20 @@       rows marks `shouldBe` rows precomposed     it "keeps that string on one line within the margin" $       rows marks `shouldBe` 1+  describe "withMargin counts the comma after a binding" $+    it "wraps a binding that fits only without its trailing comma" $+      render (withMargin 30 (expressionToCST commaFormation))+        `shouldBe` "⟦\n  a ↦ Φ.f(\n    x ↦ Φ.xxxxxxxxxxx\n  ),\n  b ↦ ⊥\n⟧"++  describe "withMargin counts the attribute after a dispatch" $+    it "wraps a call that does not fit with its attribute" $+      render (withMargin 30 (expressionToCST dispatchedHead))+        `shouldBe` "Φ.fff(\n  aaaaa ↦ Φ.ccccc\n).dddddd"++  describe "withMargin counts the opening parenthesis of a split argument" $+    it "keeps every line of a chain of calls within the margin" $+      all ((<= 40) . T.length) (T.lines (render (withMargin 40 (expressionToCST chained))))+        `shouldBe` True   where     terms :: [(String, Expression)]     terms =@@ -288,6 +303,26 @@       ]     margins :: [Int]     margins = [0, 1, 40, 80, 1000]++    commaFormation :: Expression+    commaFormation =+      ExFormation+        [ BiTau (AtLabel "a") (ExApplication (ExDispatch ExRoot (AtLabel "f")) (ArTau (AtLabel "x") (ExDispatch ExRoot (AtLabel "xxxxxxxxxxx"))))+        , BiTau (AtLabel "b") ExTermination+        ]++    dispatchedHead :: Expression+    dispatchedHead =+      ExDispatch+        (ExApplication (ExDispatch ExRoot (AtLabel "fff")) (ArTau (AtLabel "aaaaa") (ExDispatch ExRoot (AtLabel "ccccc"))))+        (AtLabel "dddddd")++    chained :: Expression+    chained =+      foldl+        (\acc i -> ExApplication acc (ArTau (AtLabel (T.pack (printf "a%d" i))) (ExDispatch ExRoot (AtLabel (T.pack (printf "v%d" i))))))+        (ExDispatch ExRoot (AtLabel "f"))+        [0 .. 6 :: Int]      rows :: Expression -> Int     rows expression = length (T.lines (render (withMargin 30 (expressionToCST expression))))
test/MorphSpec.hs view
@@ -213,6 +213,12 @@     it "normalizes what it is handed before 𝔻 sees it" $ do       value <- reduced "[[ x -> [[ D> 01- ]] ]].x"       value `shouldBe` Dataized (BtOne "01")+    it "keeps the site the caller names what it reduces by" $ do+      univ <- parseExpressionThrows universe+      target <- parseExpressionThrows "Q.y"+      ctx <- defaultReduceContext ExRoot+      (_, aiming) <- insideUniverse target univ ctx{_site = ExDispatch ExXi (AtLabel "ψ")}+      aiming._site `shouldBe` ExDispatch ExXi (AtLabel "ψ")     it "refuses a universe which is not a formation" $ do       target <- parseExpressionThrows "Q.y"       (insideUniverse target ExRoot =<< defaultReduceContext ExRoot)
test/MustSpec.hs view
@@ -39,6 +39,10 @@       , ("rejects mixed alphanumeric input", "12abc", Nothing)       , ("rejects empty input", "", Nothing)       , ("rejects unicode input", "日本語", Nothing)+      , ("rejects a hexadecimal exact value", "0x2", Nothing)+      , ("rejects an octal exact value", "0o2", Nothing)+      , ("rejects a space-padded exact value", " 2", Nothing)+      , ("rejects a space-padded range bound", "1.. 2", Nothing)       , ("parses a simple range", "1..5", Just (MtRange (Just 1) (Just 5)))       , ("parses a range with equal bounds", "3..3", Just (MtRange (Just 3) (Just 3)))       , ("parses a range with a zero start", "0..10", Just (MtRange (Just 0) (Just 10)))@@ -56,6 +60,14 @@       , ("rejects a range with more than one '..' separator", "3.4..5", Nothing)       , ("rejects a maximum-only range with a negative bound", "..-5", Nothing)       , ("rejects a minimum-only range with a negative bound", "-5..", Nothing)+      , ("parses an exact value of maxBound Int", "9223372036854775807", Just (MtExact 9223372036854775807))+      , ("rejects an exact value above maxBound Int", "18446744073709551617", Nothing)+      , ("rejects an exact value just above maxBound Int", "9223372036854775808", Nothing)+      , ("rejects a negative value that wraps around to a positive Int", "-18446744073709551611", Nothing)+      , ("rejects a maximum-only range with a bound above maxBound Int", "..18446744073709551617", Nothing)+      , ("rejects a minimum-only range with a bound above maxBound Int", "18446744073709551617..", Nothing)+      , ("rejects a range with a maximum above maxBound Int", "1..18446744073709551617", Nothing)+      , ("parses a range ending at maxBound Int", "1..9223372036854775807", Just (MtRange (Just 1) (Just 9223372036854775807)))       ]       (\(desc, input, expected) -> it desc ((readMaybe input :: Maybe Must) `shouldBe` expected)) 
test/PrinterSpec.hs view
@@ -6,6 +6,7 @@ module PrinterSpec where  import AST+import Bytes (numToBts) import Control.Monad (forM_) import Data.Map.Strict qualified as Map import Encoding (Encoding (..))@@ -18,6 +19,7 @@ import Sugar (SugarType (..)) import Test.Hspec (Spec, describe, expectationFailure, it, shouldBe, shouldContain, shouldNotContain, shouldSatisfy) import Test.Hspec.QuickCheck (prop)+import Test.QuickCheck (chooseInt, forAll) import Yaml (ExtraArgument (..))  spec :: Spec@@ -102,6 +104,11 @@             salty `shouldContain` "Q.bytes("             salty `shouldContain` printBytes bts       )++  describe "printExpression writes a whole number as a double (#2133)" $+    prop "with a zero after the point" $+      forAll (chooseInt (-1000000, 1000000)) $ \whole ->+        printExpression' (DataNumber (numToBts (fromIntegral whole))) (SWEET, UNICODE, SINGLELINE, defaultMargin) `shouldBe` show whole ++ ".0"    describe "printExpression keeps a non-finite double without a name in byte form" $     forM_
test/RandomSpec.hs view
@@ -119,7 +119,7 @@    describe "randomString %d space exhaustion" $     it "raises an error instead of looping forever once the 10000-value space is exhausted" $ do-      result <- timeout 10000000 (try (mapM_ (const (randomString "%d")) [1 :: Int .. 10000]) :: IO (Either SomeException ()))+      result <- timeout 10000000 (try (mapM_ (const (randomString "%d")) [1 :: Int .. 10001]) :: IO (Either SomeException ()))       case result of         Just (Left _) -> pure ()         Just (Right _) -> expectationFailure "expected an error once the %d space is exhausted"
test/RegexpSpec.hs view
@@ -55,6 +55,9 @@       , ("handles alternation", "cat|dog", "I have a dog", True)       , ("handles unicode input", "日本語", "これは日本語です", True)       , ("handles case sensitive match", "Hello", "hello", False)+      , ("matches non-ASCII letters with \\w", "^\\w+$", "héllo", True)+      , ("matches non-ASCII digits with \\d", "^\\d+$", "١٢٣", True)+      , ("matches non-ASCII letters with [[:alpha:]]", "^[[:alpha:]]+$", "héllo", True)       ]       (\(desc, pattern, input, expected) -> it desc $ R.match (utf8 pattern) (utf8 input) `shouldReturn` expected) @@ -85,6 +88,19 @@         , ["only"]         , "$999999999999999999999999999999999999999999"         )+      ,+        ( "keeps a group reference that wraps to group one literal"+        , "$18446744073709551617"+        , ["only", "a"]+        , "$18446744073709551617"+        )+      ,+        ( "keeps a group reference that wraps to group zero literal"+        , "$18446744073709551616"+        , ["only", "a"]+        , "$18446744073709551616"+        )+      , ("substitutes the highest existing group", "$1", ["only", "a"], "a")       , ("handles replacement without group references", "plain", ["x"], "plain")       , ("handles empty replacement", "", ["x"], "")       , ("handles empty groups list with reference", "$0", [], "$0")
test/RenderSpec.hs view
@@ -11,6 +11,8 @@ import Data.Text qualified as T import Render (Render (render)) import Test.Hspec+import Test.Hspec.QuickCheck (prop)+import Test.QuickCheck (chooseInt, forAll)  xiExpr :: EXPRESSION xiExpr = EX_XI XI@@ -219,8 +221,11 @@         , "ξ(\n  a ↦ ξ\n)"         )       , ("EX_STRING", EX_STRING "hi" (TAB 0) [], "\"hi\"")-      , ("EX_NUMBER integer", EX_NUMBER (Left 42) (TAB 0) [], "42")-      , ("EX_NUMBER double", EX_NUMBER (Right 3.5) (TAB 0) [], "3.5")+      , ("EX_NUMBER whole", EX_NUMBER 42 (TAB 0) [], "42.0")+      , ("EX_NUMBER double", EX_NUMBER 3.5 (TAB 0) [], "3.5")+      , ("EX_NUMBER negative zero", EX_NUMBER (-0.0) (TAB 0) [], "-0.0")+      , ("EX_NUMBER below ten million", EX_NUMBER 9999999 (TAB 0) [], "9999999.0")+      , ("EX_NUMBER ten million", EX_NUMBER 10000000 (TAB 0) [], "1.0e7")       , ("EX_NONFINITE nan", EX_NONFINITE Φ NfNan (TAB 0) [], "Φ.nan")       , ("EX_NONFINITE pinf", EX_NONFINITE Φ NfPinf (TAB 0) [], "Φ.pinf")       , ("EX_NONFINITE ninf ascii", EX_NONFINITE Q NfNinf (TAB 0) [], "Q.ninf")@@ -232,6 +237,13 @@       , ("EX_BYTES", EX_BYTES (BT_ONE "1F"), "1F-")       ]       (\(desc, node, expected) -> it desc (render node `shouldBe` expected))++  describe "render EX_NUMBER as show does" $ do+    prop "for any double" $+      \value -> render (EX_NUMBER value (TAB 0) []) `shouldBe` T.pack (show value)+    prop "for a whole number on either side of ten million" $+      forAll (chooseInt (-20000000, 20000000)) $ \whole ->+        render (EX_NUMBER (fromIntegral whole) (TAB 0) []) `shouldBe` T.pack (show (fromIntegral whole :: Double))    describe "render [ATTRIBUTE]" $     it "joins with comma space" (render [AT_LABEL "a", AT_LABEL "b"] `shouldBe` "a, b")
test/RewriterSpec.hs view
@@ -10,7 +10,7 @@ module RewriterSpec where  import AST (Argument (ArTau), Attribute (AtLabel, AtRho), Binding (BiMeta, BiTau, BiVoid), Expression (ExApplication, ExDispatch, ExFormation, ExRoot, ExTermination, ExXi))-import Control.Exception (SomeException)+import Control.Exception (IOException, SomeException, throw) import Control.Monad (forM_, unless) import Data.Aeson import Data.Char (isSpace)@@ -114,6 +114,12 @@         , (5, 1, True)         , Right (\(rewrittens, _) -> fst (NE.last rewrittens) == ExFormation [BiTau (AtLabel "t") ExTermination])         )+      ,+        ( "does not exceed without --depth-sensitive when rewriting finishes in exactly --max-cycles cycles"+        , "⟦ t ↦ ⊥.a ⟧"+        , (5, 1, False)+        , Right (\(_, exceeded) -> not exceeded)+        )       ]       ( \(desc, input', (maxDepth, maxCycles, depthSensitive), expected) -> it desc $ do           expr <- parseExpressionThrows input'@@ -124,6 +130,30 @@               result <- action               result `shouldSatisfy` predicate       )++  describe "--max-cycles at the boundary, with rules that keep applying" $ do+    let steps =+          [ direct "te" False (\_ expr -> [ExXi | ExTermination <- [expr]])+          , direct "ex" False (\_ expr -> [ExRoot | ExXi <- [expr]])+          , direct "rt" False (\_ expr -> [ExTermination | ExRoot <- [expr]])+          ]+        context depthSensitive = RewriteContext ExRoot 5 1 depthSensitive Nothing (building linked) (_normal linked) (every steps) MtDisabled Nothing dontSaveStep dontSaveMade+    it "exceeds without --depth-sensitive when a rule still applies at --max-cycles" $ do+      (_, exceeded) <- rewrite ExXi steps (context False)+      exceeded `shouldBe` True+    it "throws with --depth-sensitive when a rule still applies at --max-cycles" $+      rewrite ExXi steps (context True)+        `shouldThrow` (\exc -> "--max-cycles=1" `isInfixOf` show (exc :: SomeException))++  describe "a rule that raises while applicable is probed at the boundary" $ do+    let steps = [Step "boom" (\_ _ -> throw (userError "boom"))]+        context depthSensitive = RewriteContext ExRoot 5 0 depthSensitive Nothing (building linked) (_normal linked) (every steps) MtDisabled Nothing dontSaveStep dontSaveMade+    it "treats the raise as not applicable without --depth-sensitive" $ do+      (_, exceeded) <- rewrite ExXi steps (context False)+      exceeded `shouldBe` False+    it "propagates the raise with --depth-sensitive" $+      rewrite ExXi steps (context True)+        `shouldThrow` (\exc -> "boom" `isInfixOf` show (exc :: IOException))    describe "--must once --max-cycles stops the run" $     forM_
test/RuleSpec.hs view
@@ -63,6 +63,36 @@                       ++ printSubsts matched                 )       )+  describe "an 'or' condition does not depend on the order of its items" $ do+    let ctx = RuleContext buildTerm Nothing (_normal linked)+        rule :: Yaml.Condition -> Yaml.Rule+        rule cond =+          Yaml.Rule+            "or-order"+            Nothing+            Nothing+            (ExFormation [BiTau (AtLabel "s") (ExMeta "e1")])+            (ExFormation [BiTau (AtLabel "ok") (ExFormation [])])+            (Just cond)+            Nothing+            Nothing+        term = ExFormation [BiTau (AtLabel "s") (ExFormation [BiVoid (AtLabel "q")])]+        inner = ExFormation [BiVoid (AtLabel "q")]+        raising = Yaml.Matches "x" (ExMeta "e1")+        met = Yaml.Eq (Yaml.CmpNum (Yaml.Literal 1)) (Yaml.CmpNum (Yaml.Literal 1))+        unmatched = Yaml.Eq (Yaml.CmpNum (Yaml.Literal 1)) (Yaml.CmpNum (Yaml.Literal 2))+        buildE1 cond = mapM (buildExpressionThrows (ExMeta "e1")) =<< matchExpressionWithRule term (rule cond) ctx+    it "keeps a later item met when an earlier one raises" $+      buildE1 (Yaml.Or [raising, met]) `shouldReturn` [inner]+    it "keeps an earlier item met when a later one raises" $+      buildE1 (Yaml.Or [met, raising]) `shouldReturn` [inner]+    it "keeps a last item met after a middle one raises" $+      buildE1 (Yaml.Or [unmatched, raising, met]) `shouldReturn` [inner]+    it "stays not met when no item is met" $+      buildE1 (Yaml.Or [raising, unmatched]) `shouldReturn` []+    it "stays not met under 'not' when an item raises" $+      buildE1 (Yaml.Not (Yaml.Or [raising, unmatched])) `shouldReturn` []+   describe "isNF determines normal form" $ do     let ctx = RuleContext buildTerm Nothing (_normal linked)     forM_
test/SugarSpec.hs view
@@ -192,12 +192,12 @@         )       ,         ( "EX_NUMBER with no extra rho expands into the Q.number(Q.bytes(...)) form"-        , EX_NUMBER (Left 42) (TAB 1) []+        , EX_NUMBER 42 (TAB 1) []         , "Φ.number(\n    φ ↦ Φ.bytes(\n      φ ↦ ⟦\n        Δ ⤍ 40-45-00-00-00-00-00-00\n      ⟧\n    )\n  )"         )       ,         ( "EX_NUMBER preserves an extra rho argument carried alongside the primitive"-        , EX_NUMBER (Left 42) (TAB 1) [ArTau AtRho (ExDispatch ExXi (AtLabel "y"))]+        , EX_NUMBER 42 (TAB 1) [ArTau AtRho (ExDispatch ExXi (AtLabel "y"))]         , "Φ.number(\n    φ ↦ Φ.bytes(\n      φ ↦ ⟦\n        Δ ⤍ 40-45-00-00-00-00-00-00\n      ⟧\n    )\n  )(\n    ρ ↦ ξ.y\n  )"         )       ,@@ -671,7 +671,7 @@       "a sweet numeric literal expands into Q.number(Q.bytes(...)) when salted"       $ do         let number = DataNumber (BtMany ["40", "45", "00", "00", "00", "00", "00", "00"])-        printExpression' number (config SWEET) `shouldBe` "42"+        printExpression' number (config SWEET) `shouldBe` "42.0"         printExpression' number (config SALTY) `shouldBe` "Φ.number( φ ↦ Φ.bytes( φ ↦ ⟦ Δ ⤍ 40-45-00-00-00-00-00-00 ⟧ ) )"     it       "a sweet string literal expands into Q.string(Q.bytes(...)) when salted"@@ -698,10 +698,10 @@         printExpression' meetChain (config SWEET) `shouldBe` "\\phinoMeet{2}{ \\phinoAgain{a:1} }:x"         printExpression' meetChain (config SALTY) `shouldBe` "⟦ x ↦ \\phinoMeet{2}{ \\phinoAgain{a:1} } ⟧"     it "a ρ applied to a number literal after another argument keeps its place when sweet" $-      printExpression' (applied (ArTau (AtLabel "x") ExXi) (ArTau AtRho ExTermination)) (config SWEET) `shouldBe` "42( x ↦ ξ, ρ ↦ ⊥ )"+      printExpression' (applied (ArTau (AtLabel "x") ExXi) (ArTau AtRho ExTermination)) (config SWEET) `shouldBe` "42.0( x ↦ ξ, ρ ↦ ⊥ )"     it "a ρ applied to a number literal after another argument keeps its place when salted" $       printExpression' (applied (ArTau (AtLabel "x") ExXi) (ArTau AtRho ExTermination)) (config SALTY) `shouldBe` "Φ.number( φ ↦ Φ.bytes( φ ↦ ⟦ Δ ⤍ 40-45-00-00-00-00-00-00 ⟧ ) )( x ↦ ξ )( ρ ↦ ⊥ )"     it "a ρ applied straight to a number literal is dropped when sweet" $-      printExpression' (applied (ArTau AtRho ExTermination) (ArTau (AtLabel "x") ExXi)) (config SWEET) `shouldBe` "42( x ↦ ξ )"+      printExpression' (applied (ArTau AtRho ExTermination) (ArTau (AtLabel "x") ExXi)) (config SWEET) `shouldBe` "42.0( x ↦ ξ )"     it "a ρ applied straight to a number literal keeps its place when salted" $       printExpression' (applied (ArTau AtRho ExTermination) (ArTau (AtLabel "x") ExXi)) (config SALTY) `shouldBe` "Φ.number( φ ↦ Φ.bytes( φ ↦ ⟦ Δ ⤍ 40-45-00-00-00-00-00-00 ⟧ ) )( ρ ↦ ⊥ )( x ↦ ξ )"
test/XMIRSpec.hs view
@@ -415,7 +415,7 @@     it "includes a decimal comment for a number when comments aren't omitted" $ do       expr <- parseExpressionThrows "[[ x -> 5 ]]"       xmir' <- expressionToXMIR expr commentedContext-      printXMIR xmir' `shouldContain` "<!-- 5 -->"+      printXMIR xmir' `shouldContain` "<!-- 5.0 -->"      it "includes a quoted comment for a string when comments aren't omitted" $ do       expr <- parseExpressionThrows "[[ x -> \"foo\" ]]"
test/YamlSpec.hs view
@@ -105,12 +105,17 @@   describe "rejects a meta that nothing binds" $     forM_       [ ("in 'when'", "name: w\npattern: '⟦ x ↦ 𝑒1 ⟧'\nresult: '⟦ y ↦ 𝑒1 ⟧'\nwhen:\n  not:\n    eq: ['𝑒9', '𝑒1']", "'e9'")+      , ("in 'when', which runs before the 'where' step that binds it", "name: l\npattern: '⟦ x ↦ 𝑒1 ⟧'\nresult: '⟦ y ↦ 𝑒1 ⟧'\nwhen:\n  not:\n    eq: ['𝑒2', '𝑒1']\nwhere:\n  - meta: '𝑒2'\n    function: concat\n    args: ['𝑒1']", "'e2'")       , ("in 'result'", "name: r\npattern: '⟦ x ↦ 𝑒1, 𝐵1 ⟧'\nresult: '⟦ y ↦ 𝑒9, 𝐵1 ⟧'", "'e9'")       , ("in a 'where' argument", "name: a\npattern: '⟦ x ↦ 𝑒1 ⟧'\nresult: '⟦ x ↦ 𝑒2 ⟧'\nwhere:\n  - meta: '𝑒2'\n    function: concat\n    args: ['𝑒7']", "'e7'")       ]       ( \(desc, yaml, meta) ->           it desc ((decodeYaml' yaml :: Either Yaml.ParseException Rule) `shouldSatisfy` failsWith ("reads the meta " ++ meta))       )++  it "accepts a meta in 'having' that a 'where' step binds" $+    (decodeYaml' "name: l\npattern: '⟦ x ↦ 𝑒1 ⟧'\nresult: '⟦ y ↦ 𝑒1 ⟧'\nhaving:\n  not:\n    eq: ['𝑒2', '𝑒1']\nwhere:\n  - meta: '𝑒2'\n    function: concat\n    args: ['𝑒1']" :: Either Yaml.ParseException Rule)+      `shouldSatisfy` isRight    it "accepts a meta that an earlier 'where' step binds" $     (decodeYaml' "name: ok\npattern: '⟦ x ↦ 𝑒1 ⟧'\nresult: '⟦ x ↦ 𝑒3 ⟧'\nwhere:\n  - meta: '𝑒2'\n    function: concat\n    args: ['𝑒1']\n  - meta: '𝑒3'\n    function: concat\n    args: ['𝑒2']" :: Either Yaml.ParseException Rule)