tricu

A lazy functional language based on Tree Calculus
Log | Files | Refs | README | LICENSE

Spec.hs (170289B)


      1 module Main where
      2 
      3 import WorkspaceTests (workspaceTests)
      4 import IOBoundaryTests (ioBoundaryTests)
      5 import PortableCompilerTests (portableCompilerTests)
      6 import ExecutableLoadingTests (executableLoadingTests)
      7 import IODriverTests (ioDriverTests)
      8 import IOUTF8Tests (ioUTF8Tests)
      9 import IOByteStreamTests (ioByteStreamTests)
     10 import IOSyncTests (ioSyncTests)
     11 import IndexedWriterTests (indexedWriterTests)
     12 import SnapshotTests (snapshotTests)
     13 import IOResolverTests (ioResolverTests)
     14 import IONetworkTests (ioNetworkTests)
     15 import IOFilesystemTests (ioFilesystemTests)
     16 import IOPositionTests (ioPositionTests)
     17 import IOStandardTests (ioStandardTests)
     18 import IOStorageTests (ioStorageTests)
     19 import IOTaskTests (ioTaskTests)
     20 import IOClockTests (ioClockTests)
     21 import IOProcessTests (ioProcessTests)
     22 import IOContextTests (ioContextTests)
     23 import IOProtocolTests (ioProtocolTests)
     24 import IOTests (ioTests)
     25 import LibraryProgramTests (maybeTests, providedLibraries, contractsTests, arithmetic)
     26 import qualified ParserValueTests
     27 import qualified LibraryValueTests
     28 import PortableTestCompiler (compileFixtureFile, compileFixtureDefinitions)
     29 import ReferenceFixtures
     30 import PackageTests (packageTests)
     31 import InteractionInspectionTests (interactionInspectionTests)
     32 import REPL
     33 import Runtime
     34 import TreeObservation (forceTreeShared)
     35 import qualified NodeTable as Nodes
     36 import Wire
     37 import SupportFixture (canonicalEnvs, verifyReuseProviders, verifySupportSources, verifyRelocatedSupportSources, verifyIOSupportSources, verifyCompilerIOSupportSources, verifyCanonicalIOSupportSources, verifyBinary64SupportSources,
     38   verifySupportExports, verifySupportDependencies, readFixtureSourceBytes, readTestSource, readPinnedSource)
     39 import ContentStore
     40 import ContentStore.Bundle
     41 import Module.Manifest
     42 import IODriver (runIO)
     43 
     44 import Control.Exception      (bracket, evaluate, try, SomeException)
     45 import Control.Concurrent     (forkIO, newEmptyMVar, putMVar, takeMVar)
     46 import System.IO.Unsafe       (unsafePerformIO)
     47 import System.Mem.StableName (makeStableName)
     48 import Data.IORef (IORef, newIORef, modifyIORef', readIORef, atomicModifyIORef')
     49 import Control.Monad         (foldM, forM, forM_, unless)
     50 import Control.Monad.IO.Class (liftIO)
     51 import System.IO.Temp         (withSystemTempDirectory)
     52 import qualified System.Timeout as Timeout
     53 import System.Directory      (createDirectory, createFileLink, doesFileExist, doesDirectoryExist, listDirectory)
     54 import System.FilePath       ((</>))
     55 import Data.Bits              (xor)
     56 import Data.List              (find, isInfixOf, isPrefixOf, stripPrefix)
     57 import Data.Text              (Text, unpack, pack)
     58 import Data.Text.Encoding     (decodeUtf8, encodeUtf8)
     59 import Data.Word              (Word8)
     60 import Test.Tasty
     61 import Test.Tasty.HUnit
     62 
     63 import Data.ByteString (ByteString)
     64 import qualified Data.Foldable as Foldable
     65 import qualified Data.ByteString as BS
     66 import qualified Data.Map as Map
     67 import qualified Data.Sequence as Seq
     68 import qualified Data.Set as Set
     69 import qualified Data.Vector as V
     70 
     71 main :: IO ()
     72 main = do
     73   putStrLn "Reference-artifact tests: authenticated compiler/support snapshots, not working-tree candidate coverage."
     74   defaultMain tests
     75 
     76 tests :: TestTree
     77 tests = testGroup "Tricu Tests"
     78   [ workspaceTests
     79   , ioBoundaryTests
     80   , portableCompilerTests
     81   , executableLoadingTests
     82   , runtimePresentationTests
     83   , interactionInspectionTests
     84   , packageTests
     85   , evaluationDemandTests
     86   , arithmetic
     87   , providedLibraries
     88   , contractsTests
     89   , maybeTests
     90   , fileEval
     91   , demos
     92   , decoding
     93   , byteMarshallingTests
     94   , sourceObjectCodecTests
     95   , sharedTreeDecodingTests
     96   , indexedTreeTransportTests
     97   , verifiedTreePublicationTests
     98   , wireTests
     99   , tricuReaderTests
    100   , LibraryValueTests.byteListUtilities (fixtureExports "bytes") (fixtureExports "base")
    101   , ParserValueTests.binaryParserTests (fixtureExports "binary") (fixtureExports "parser") (fixtureExports "base")
    102   , ParserValueTests.portableParserTests (fixtureExports "parser") (fixtureExports "base")
    103   , locatedLexerTests
    104   , compilerFixtureTests
    105   , digestHexTests
    106   , gateLexerTests
    107   , gateParserTests
    108   , gateProgramTests
    109   , equationHelperTests
    110   , sourceGraphTests
    111   , gateOrderingTests
    112   , gateUnitTests
    113   , gateResolutionTests
    114   , gateCompilationTests
    115   , LibraryValueTests.httpParsingTests (fixtureExports "http") (fixtureExports "io_http")
    116   , registryContractTests
    117   , localControlTests
    118   , contentStoreTests
    119   , artifactSafetyTests
    120   , ioDriverTests
    121   , ioTests
    122   , ioClockTests
    123     , ioProcessTests
    124     , ioContextTests
    125   , ioProtocolTests
    126   , ioTaskTests
    127   , ioUTF8Tests
    128   , ioByteStreamTests
    129   , ioSyncTests
    130   , indexedWriterTests
    131   , snapshotTests
    132   , ioResolverTests
    133   , ioNetworkTests
    134   , ioFilesystemTests
    135   , ioPositionTests
    136   , ioStandardTests
    137   , ioStorageTests
    138   ]
    139 
    140 
    141 -- Constructor display is a runtime presentation format, not a frontend AST.
    142 runtimePresentationTests :: TestTree
    143 runtimePresentationTests = testGroup "Runtime presentation"
    144   [ testCase "AST leaf constructor golden" $ formatT AST Leaf @?= "TLeaf"
    145   , testCase "AST stem constructor golden" $ formatT AST (Stem Leaf) @?= "TStem TLeaf"
    146   , testCase "AST nested constructor golden" $
    147       formatT AST (Fork (Stem Leaf) (Fork Leaf Leaf)) @?=
    148         "TFork (TStem TLeaf) (TFork TLeaf TLeaf)"
    149   ]
    150 
    151 -- Demand uses exceptions rather than unbounded divergent reductions.
    152 -- Physical sharing checks the Haskell implementation, not a tree ABI promise.
    153 evaluationDemandTests :: TestTree
    154 evaluationDemandTests = localOption (mkTimeout 30000000) $ testGroup "Evaluation demand boundaries"
    155   [ testCase "constant application discards its argument" $
    156       apply (Fork Leaf (ofNumber 23)) poison @?= ofNumber 23
    157   , testCase "constructor application exposes only the outer constructor" $ do
    158       case apply Leaf poison of
    159         Stem _ -> pure ()
    160         _ -> assertFailure "expected Stem"
    161       case apply (Stem poison) poison of
    162         Fork _ _ -> pure ()
    163         _ -> assertFailure "expected Fork"
    164   , testCase "triage demands shape but not unused handlers or children" $ do
    165       let value = ofNumber 23
    166           constant = Fork Leaf value
    167       apply (Fork (Fork value poison) poison) Leaf @?= value
    168       apply (Fork (Fork poison constant) poison) (Stem poison) @?= value
    169       apply (Fork (Fork poison poison) (Fork Leaf constant))
    170         (Fork poison poison) @?= value
    171       demanded (evaluate (apply (Fork (Fork Leaf Leaf) Leaf) poison))
    172   , testCase "distribution retains the duplicated argument's physical sharing" $ do
    173       -- t (t t) t c -> t c (t c). Both occurrences reference c.
    174       input <- newIORef (Fork (ofNumber 23) (ofNumber 24)) >>= readIORef
    175       case apply (Fork (Stem Leaf) Leaf) input of
    176         Fork left (Stem right) -> do
    177           a <- evaluate left >>= makeStableName
    178           b <- evaluate right >>= makeStableName
    179           assertBool "distribution copied the argument" (a == b)
    180         _ -> assertFailure "unexpected distribution result"
    181   , testCase "host list decoding checks the whole spine but not elements" $ do
    182       fmap length (toList (Fork poison Leaf)) @?= Right 1
    183       demanded (evaluate (toList (Fork Leaf poison)))
    184   , testCase "complete flat serialization demands hidden children" $
    185       demanded (evaluate (BS.length (encodeTreeTerm (Fork Leaf poison))))
    186   , testCase "complete indexed serialization demands hidden children" $
    187       demanded (buildBundleShared [("root", Fork Leaf poison)])
    188   , testCase "full observation visits a shared depth-80 DAG within a bounded interval" $ do
    189       let tower 0 = Leaf
    190           tower n = let child = tower (n - 1) in Fork child child
    191       outcome <- Timeout.timeout 2000000 (forceTreeShared (tower (80 :: Int)))
    192       outcome @?= Just ()
    193   , testCase "full observation demands hidden children and does not retain failures" $ do
    194       demanded (forceTreeShared (Fork Leaf poison))
    195       forceTreeShared (Fork Leaf (Stem Leaf))
    196   , testCase "full observation handles deep trees without expanding a recursive call stack" $ do
    197       let chain = foldr (\_ child -> Stem child) Leaf [1..65536 :: Int]
    198       outcome <- Timeout.timeout 2000000 (forceTreeShared chain)
    199       outcome @?= Just ()
    200   , testCase "stable local bindings and ordinary applications discard unused inputs" $ do
    201       forM_ ["main input = (ignored : 23) input",
    202              "main input = let unused = input in 23"] $ \source -> do
    203         function <- compileLocalControl [] source
    204         apply function poison @?= ofNumber 23
    205   , testCase "stable function consumes a finite prefix of an infinite producer" $ do
    206       function <- compileLocalControl [] $ unlines
    207         [ "main input = (let rec ones x = t x (ones x) in case (ones input) of"
    208         , "  [head | _] = head"
    209         , "  _ = 0)" ]
    210       apply function (ofNumber 23) @?= ofNumber 23
    211   ]
    212   where
    213     poison = error "evaluation-demand sentinel" :: T
    214     demanded :: IO a -> Assertion
    215     demanded action = do
    216       outcome <- try (action >> pure ()) :: IO (Either SomeException ())
    217       case outcome of
    218         Left err -> assertBool (show err) ("evaluation-demand sentinel" `isInfixOf` show err)
    219         Right () -> assertFailure "expected the observer to demand the poisoned subtree"
    220 
    221 -- Host-only negative controls: no source compiler or retained toolchain needed.
    222 artifactSafetyTests :: TestTree
    223 artifactSafetyTests = testGroup "artifact safety" $
    224   [ testGroup "alias components"
    225       [ testCase (show name) $ withSystemTempDirectory "tricu-alias-safety" $ \dir -> do
    226           let store = StorePath (dir </> "store")
    227               ref = treeTermRef (hashObject treeTermDomain (encodeTreeTerm Leaf))
    228           forM_ [NameAlias, ModuleAlias, PackageAlias] $ \kind -> do
    229             blocked "invalid alias name" (writeAlias store kind name ref)
    230             blocked "invalid alias name" (readAlias store kind name)
    231           doesDirectoryExist (unStorePath store) >>= (@?= False)
    232       | name <- ["", ".", "..", "../escape", "a/../../escape", "/absolute",
    233                  "a\\b", "C:escape", "C:\\escape", "a\NULb", "a\nb", "a\rb"]
    234       ]
    235   , testCase "flat import counts exponential DAGs without expansion" $ do
    236       let base = buildBundle [("root", Leaf)]
    237           graph = base { bundleNodes = Seq.fromList (BNLeaf : [BNFork (i-1) (i-1) | i <- [1..10000]])
    238                        , bundleRoots = [10000]
    239                        , bundleManifest = (bundleManifest base) { manifestRoots = [BundleRoot 10000 "default"],
    240                            manifestExports = [BundleExport "root" 10000 "term" "arboricx.abi.tree.v1"] } }
    241       case checkFlatImportSize defaultFlatImportLimit graph of
    242         Left err -> assertBool err ("flat import byte limit exceeded" `isInfixOf` err)
    243         Right () -> assertFailure "expanded size was not bounded"
    244   , testCase "flat byte accounting matches exact small serializations" $
    245       forM_ [Leaf, Stem Leaf, Fork (Stem Leaf) (Stem Leaf)] $ \tree -> do
    246         let bundle = buildBundle [("a", tree), ("b", tree)]
    247             size = 2 * fromIntegral (BS.length (encodeTreeTerm tree))
    248         checkFlatImportSize size bundle @?= Right ()
    249         assertBool "one byte under budget must fail" (either (const True) (const False) (checkFlatImportSize (size-1) bundle))
    250   , testCase "duplicate export names rejected by bundle and stored manifest readers" $ do
    251       let bundle = buildBundle [("same", Leaf), ("same", Stem Leaf)]
    252           manifest = ModuleManifest [] [ModuleExport "same" (treeTermRef (nodeHash NLeaf)) "arboricx.abi.tree.v1",
    253                                         ModuleExport "same" (treeTermRef (nodeHash NLeaf)) "arboricx.abi.tree.v1"]
    254       verifyBundle bundle @?= Left "duplicate export name"
    255       decodeManifest (encodeManifest manifest) @?= Left "duplicate export name"
    256   , testCase "qualified and symbolic aliases remain usable" $
    257       withSystemTempDirectory "tricu-alias-valid" $ \dir -> do
    258         let store = StorePath dir
    259             ref = treeTermRef (hashObject treeTermDomain (encodeTreeTerm Leaf))
    260         forM_ ["native_math.isNat?", "++", "hello world", "λ", ".hidden", "a..b"] $ \name -> do
    261           writeAlias store NameAlias name ref
    262           readAlias store NameAlias name >>= (@?= Just ref)
    263   , testCase "alias absolute paths cannot read or overwrite an outside sentinel" $
    264       withSystemTempDirectory "tricu-alias-sentinel" $ \dir -> do
    265         let store = StorePath (dir </> "store")
    266             outside = dir </> "sentinel"
    267             name = pack outside
    268             ref = treeTermRef (hashObject treeTermDomain (encodeTreeTerm Leaf))
    269         BS.writeFile outside "unchanged"
    270         blocked "invalid alias name" (writeAlias store NameAlias name ref)
    271         blocked "invalid alias name" (readAlias store NameAlias name)
    272         BS.readFile outside >>= (@?= "unchanged")
    273   , testGroup "object path hashes"
    274       [ testCase (show h) $ withSystemTempDirectory "tricu-hash-safety" $ \dir ->
    275           do
    276             blocked "invalid object hash" (getObject (StorePath dir) h)
    277             blocked "invalid object hash" (evaluate (objectPath (StorePath dir) h))
    278       | h <- ["", "../escape", "../../escape", "/absolute", "abc", pack (replicate 64 'A'),
    279               pack (replicate 63 'a'), pack (replicate 65 'a'), pack (replicate 64 'g')]
    280       ]
    281   , testCase "object paths cannot read an absolute outside sentinel" $
    282       withSystemTempDirectory "tricu-hash-sentinel" $ \dir -> do
    283         let outside = dir </> "sentinel"
    284         BS.writeFile outside "not a CAS object"
    285         blocked "invalid object hash" (getObject (StorePath (dir </> "store")) (pack outside))
    286   , testCase "cached authenticated objects are immutable snapshots" $ fresh $ \store -> do
    287       h <- putTreeTerm store Leaf
    288       resolver <- cachedFilesystemResolver store
    289       resolverObject resolver (treeTermRef h) >>= (@?= Just (encodeTreeTerm Leaf))
    290       BS.writeFile (objectPath store h) (encodeTreeTerm (Stem Leaf))
    291       resolverObject resolver (treeTermRef h) >>= (@?= Just (encodeTreeTerm Leaf))
    292       blocked "digest mismatch" (resolverObject (filesystemResolver store) (treeTermRef h))
    293   , testCase "object resolution verifies bytes from custom resolver callbacks" $ fresh $ \store -> do
    294       h <- putTreeTerm store Leaf
    295       mh <- putManifest store (ModuleManifest [] [ModuleExport "x" (treeTermRef h) "arboricx.abi.tree.v1"])
    296       let resolver = (filesystemResolver store) { resolverObject = \_ -> pure (Just (encodeTreeTerm (Stem Leaf))) }
    297       blocked "digest mismatch" (resolveObjectByHash resolver (unDomain treeTermDomain) h)
    298   , testGroup "execution contract"
    299       [ testCase label $ do
    300           let base = buildBundle [("first", Leaf), ("second", Stem Leaf)]
    301               bundle = base { bundleManifest = change (bundleManifest base), bundleManifestBytes = BS.empty }
    302           case verifyBundle bundle of
    303             Left err -> assertBool err (needle `isInfixOf` err)
    304             Right () -> assertFailure "unsupported execution contract accepted"
    305       | (label, needle, change) <-
    306           [("evaluation", "runtime evaluation", \m -> m { manifestRuntime = (manifestRuntime m) { runtimeEvaluation = "call-by-value" } }),
    307            ("export kind", "export kind", \m -> m { manifestExports = map (\e -> e { exportKind = "foreign" }) (manifestExports m) }),
    308            ("second export ABI", "export ABI", \m -> m { manifestExports = case manifestExports m of
    309                [a,b] -> [a,b { exportAbi = "foreign.abi" }]; es -> es })]
    310       ]
    311   ] ++
    312   [ testGroup label
    313       [ testCase "tampered manifest" $ fresh $ \store -> do
    314           h <- putManifest store (ModuleManifest [] [])
    315           BS.writeFile (objectPath store h) (encodeManifest (ModuleManifest [] [ModuleExport "x" (treeTermRef h) "arboricx.abi.tree.v1"]))
    316           resolver <- makeResolver store
    317           blocked "digest mismatch" (resolveManifest resolver h)
    318           blocked "digest mismatch" (getManifest store h)
    319       , testCase "tampered exported term" $ fresh $ \store -> do
    320           h <- putTreeTerm store Leaf
    321           mh <- putManifest store (ModuleManifest [] [ModuleExport "x" (treeTermRef h) "arboricx.abi.tree.v1"])
    322           BS.writeFile (objectPath store h) (encodeTreeTerm (Stem Leaf))
    323           resolver <- makeResolver store
    324           blocked "digest mismatch" (resolveObjectByHash resolver (unDomain treeTermDomain) h)
    325           blocked "digest mismatch" (getTreeTerm store h)
    326       , testCase "tampered Merkle child" $ fresh $ \store -> do
    327           h <- putTree store (Stem Leaf)
    328           BS.writeFile (objectPath store (nodeHash NLeaf)) (serializeNode (NStem (pack (replicate 64 '0'))))
    329           resolver <- makeResolver store
    330           blocked "digest mismatch" (resolveTree resolver h)
    331           blocked "digest mismatch" (getTree store h)
    332       , testCase "tampered self-cycle rejected before hydration" $ fresh $ \store -> do
    333           h <- putTree store Leaf
    334           BS.writeFile (objectPath store h) (serializeNode (NStem h))
    335           resolver <- makeResolver store
    336           completed <- Timeout.timeout 1000000 (blocked "digest mismatch" (resolveTree resolver h))
    337           completed @?= Just ()
    338       , testCase "failed verification never poisons the cache" $ fresh $ \store -> do
    339           h <- putTreeTerm store Leaf
    340           resolver <- makeResolver store
    341           BS.writeFile (objectPath store h) (encodeTreeTerm (Stem Leaf))
    342           blocked "digest mismatch" (resolverObject resolver (treeTermRef h))
    343           BS.writeFile (objectPath store h) (encodeTreeTerm Leaf)
    344           resolverObject resolver (treeTermRef h) >>= (@?= Just (encodeTreeTerm Leaf))
    345       , testCase "wrong object domain" $ fresh $ \store -> do
    346           h <- putTreeTerm store Leaf
    347           resolver <- makeResolver store
    348           blocked "digest mismatch" (resolverObject resolver (ObjectRef (unDomain manifestDomain) h))
    349       -- Export ABI rejection is exercised against the production executable in
    350       -- executable_arguments.py, not the retired source-module resolver.
    351       , testCase "valid objects and genuine missing objects" $ fresh $ \store -> do
    352           h <- putTreeTerm store (Stem Leaf)
    353           mh <- putManifest store (ModuleManifest [] [ModuleExport "x" (treeTermRef h) "arboricx.abi.tree.v1"])
    354           nh <- putTree store (Stem Leaf)
    355           resolver <- makeResolver store
    356           resolveManifest resolver mh >>= (@?= Just (ModuleManifest [] [ModuleExport "x" (treeTermRef h) "arboricx.abi.tree.v1"]))
    357           resolveTree resolver nh >>= (@?= Just (Stem Leaf))
    358           resolveObjectByHash resolver (unDomain treeTermDomain) h >>= (@?= Just (encodeTreeTerm (Stem Leaf)))
    359           loadExecutableObject store (treeTermRef h) >>= (@?= Stem Leaf)
    360           resolverObject resolver (treeTermRef (pack (replicate 64 '0'))) >>= (@?= Nothing)
    361       ]
    362       | (label, makeResolver) <- [("filesystem integrity", pure . filesystemResolver), ("cached integrity", cachedFilesystemResolver)]
    363   ]
    364   where
    365     treeTermRef = ObjectRef (unDomain treeTermDomain)
    366     fresh f = withSystemTempDirectory "tricu-integrity" (f . StorePath)
    367     blocked needle action = do
    368       outcome <- try (action >> pure ()) :: IO (Either SomeException ())
    369       case outcome of
    370         Left err -> assertBool (show err) (needle `isInfixOf` show err)
    371         Right () -> assertFailure ("expected rejection: " ++ needle)
    372 
    373 sharedTreeDecodingTests :: TestTree
    374 sharedTreeDecodingTests = testGroup "shared inert tree decoding"
    375   [ testCase "small trees preserve exact structure and canonical bytes" $
    376       forM_ (trees 3) $ \tree -> do
    377         let bytes = encodeTreeTerm tree
    378         decodeTreeTermShared bytes @?= Right tree
    379         fmap encodeTreeTerm (decodeTreeTermShared bytes) @?= Right bytes
    380   , testCase "integer interning distinguishes constructors, pair order and wide IDs" $ do
    381       let keys = [Nodes.LeafKey] ++ map Nodes.StemKey values ++
    382                  [Nodes.ForkKey a b | a <- values, b <- values]
    383           values = [minBound, -1, 0, 1, 65535, maxBound] ++
    384                    map fromInteger [2147483647, 2147483648, 4294967295, 4294967296]
    385           entries = zip keys [(0 :: Int)..]
    386           table = foldl (\acc (key, value) -> Nodes.insert key value acc) Nodes.empty entries
    387           expected = Map.fromList entries
    388       forM_ (Map.toList expected) $ \(key, value) ->
    389         Nodes.lookup key table @?= Just value
    390       Nodes.lookup (Nodes.ForkKey 17 19) table @?= Nothing
    391       Nodes.lookup (Nodes.StemKey 17) table @?= Nothing
    392       let updated = Nodes.insert (Nodes.ForkKey 0 1) 999 table
    393       Nodes.lookup (Nodes.ForkKey 0 1) updated @?= Just 999
    394       Nodes.lookup (Nodes.ForkKey 1 0) updated @?= Map.lookup (Nodes.ForkKey 1 0) expected
    395   , testCase "deep shared decoding preserves bytes without recursive reference decoding" $ do
    396       let bytes = BS.replicate 65536 1 <> BS.singleton 0
    397       decoded <- either assertFailure pure (decodeTreeTermShared bytes)
    398       encodeTreeTerm decoded @?= bytes
    399   , testCase "malformed bytes retain ordinary decoder errors" $
    400       forM_ (concat [sequence (replicate n [0, 1, 2, 255]) | n <- [0..4]]) $ \tags ->
    401         decodeTreeTermShared (BS.pack tags) @?= decodeTreeTerm (BS.pack tags)
    402   , testCase "repeated branches regain physical sharing without application" $ do
    403       let tower 0 = Fork (Stem Leaf) Leaf
    404           tower n = let child = tower (n - 1) in Fork child child
    405           value = tower (14 :: Int)
    406       decoded <- either assertFailure pure (decodeTreeTermShared (encodeTreeTerm value))
    407       let check (Fork left right) = do
    408             a <- evaluate left >>= makeStableName
    409             b <- evaluate right >>= makeStableName
    410             assertBool "equal branches were separately allocated" (a == b)
    411           check _ = assertFailure "missing fork"
    412       check decoded
    413       case decoded of
    414         Fork left _ -> check left
    415         _ -> assertFailure "missing root"
    416   ]
    417   where
    418     trees 0 = [Leaf]
    419     trees n = let children = trees (n - 1)
    420               in Leaf : map Stem children ++ [Fork a b | a <- children, b <- children]
    421 
    422 -- Indexed IO transports inert normal forms. The tiny exponential logical tree
    423 -- is deliberately never unfolded for equality/encoding by these controls.
    424 indexedTreeTransportTests :: TestTree
    425 indexedTreeTransportTests = testGroup "indexed tree transport"
    426   [ testCase "canonical bytes agree with independent ordinary writer" $
    427       forM_ (trees 2) $ \tree -> do
    428         encoded <- encodeIndexedTree tree
    429         encoded @?= encodeBundle (buildBundle [("root", tree)])
    430         decodeIndexedTree encoded @?= Right tree
    431   , testCase "shared depth-80 tree encodes within a bounded interval" $ do
    432       let tower 0 = Leaf
    433           tower n = let child = tower (n - 1) in Fork child child
    434       measured <- Timeout.timeout 2000000 $ do
    435         payload <- encodeIndexedTree (tower (80 :: Int))
    436         size <- evaluate (BS.length payload)
    437         assertBool "DAG unexpectedly expanded" (size < 2048)
    438         bundle <- either assertFailure pure (decodeBundle payload)
    439         Seq.length (bundleNodes bundle) @?= 81
    440         value <- either assertFailure pure (decodeIndexedTree payload)
    441         case value of
    442           Fork left right -> do
    443             a <- evaluate left >>= makeStableName
    444             b <- evaluate right >>= makeStableName
    445             assertBool "decoded branches lost sharing" (a == b)
    446           _ -> assertFailure "missing fork"
    447       measured @?= Just ()
    448   , testCase "shared writer is name-neutral and byte-identical to indexed import" $
    449       withSystemTempDirectory "tricu-indexed-import" $ \dir -> do
    450         let tree = Fork (Stem Leaf) (Stem Leaf)
    451         encoded <- encodeIndexedTree tree
    452         [(name, pin)] <- unpackIndexedBundleToStore (StorePath dir)
    453           (encodeBundle (buildBundle [("publicName", tree)]))
    454         name @?= "publicName"
    455         pin @?= hashObject indexedTreeDomain encoded
    456   ]
    457   where
    458     trees 0 = [Leaf]
    459     trees n = let xs = trees (n - 1) in Leaf : map Stem xs ++ [Fork a b | a <- xs, b <- xs]
    460 
    461 -- Indexed snapshot/writer/staging suites cover current native transport. Flat
    462 -- v1 file actions and grant-based publication are deliberately retired.
    463 verifiedTreePublicationTests :: TestTree
    464 verifiedTreePublicationTests = testGroup "Source-owned indexed publication"
    465   [ testCase "sourceObjectPutTree creates canonical shards and reuses objects" $
    466       withSystemTempDirectory "tricu-tree-library" $ \dir -> do
    467         env <- evaluateCompilerFile "./lib/source/object.tri"
    468         let root = dir </> "cas"
    469             tree = Fork Leaf (Stem Leaf)
    470             payload = encodeBundle (buildBundle [("root", tree)])
    471             expected = unpack (hashObject indexedTreeDomain payload)
    472             action = Fork (ofString "tricuIO") (Fork (ofNumber 2)
    473               (apply (apply (env Map.! "sourceObjectPutTree") (ofString root)) tree))
    474             path = root </> "objects" </> take 3 expected </> expected
    475         runIO action >>= (@?= Right (ioOkResult (ofString expected)))
    476         runIO action >>= (@?= Right (ioOkResult (ofString expected)))
    477         BS.readFile path >>= (@?= payload)
    478   ]
    479 sourceObjectCodecTests :: TestTree
    480 sourceObjectCodecTests = testGroup "Portable tree object codec"
    481   [ testCase "canonical preorder bytes, suffix law and complete round trip" $
    482       withSystemTempDirectory "source-object-codec" $ \dir -> do
    483         env <- evaluateCompilerFile "./lib/source/object.tri"
    484         let encoder = env Map.! "sourceObjectEncode"
    485             fixed = env Map.! "sourceObjectEncodeOnto"
    486             decoder = env Map.! "sourceObjectDecodeComplete"
    487             suffix = BS.pack [7, 0, 255]
    488             seeds = [Leaf, Stem Leaf, Fork Leaf Leaf]
    489             small = seeds ++ [Fork l r | l <- seeds, r <- seeds]
    490             leftHeavy = iterate (\tree -> Fork tree Leaf) Leaf !! 64
    491             rightHeavy = iterate (Fork Leaf) Leaf !! 64
    492             balanced = iterate (\tree -> Fork tree tree) Leaf !! 8
    493         forM_ (small ++ [leftHeavy, rightHeavy, balanced]) $ \tree -> do
    494           let bytes = encodeTreeTerm tree
    495               encoded = apply encoder tree
    496           toBytes encoded @?= Right bytes
    497           toBytes (apply (apply fixed tree) (ofBytes suffix)) @?=
    498             Right (bytes <> suffix)
    499           apply decoder encoded @?= Fork (Stem Leaf) (Fork tree Leaf)
    500   ]
    501 
    502 fileEval :: TestTree
    503 fileEval = testGroup "Portable source file tests"
    504   [ testCase "Forks" $ check "./test/fork.tri" (Fork Leaf Leaf)
    505   , testCase "File ends with comment" $ check "./test/comments-1.tri" (Fork (Stem Leaf) Leaf)
    506   , testCase "Mapping and Equality" $ check "./test/map.tri" (Stem Leaf)
    507   , testCase "Eval and decoding string" $ check "./test/string.tri" (ofString "String test!")
    508   ]
    509   where
    510     check path expected = do
    511       source <- readFile path
    512       let external = ofList [Fork (ofString name) value | (name,value) <- Map.toList (fixtureExports "prelude")]
    513       foldl apply (fixtureExports "source.unit" Map.! "compileGateProgram")
    514         [external, ofString source] @?= parserOk expected Leaf
    515 
    516 -- All of our demo tests are also module tests
    517 demos :: TestTree
    518 demos = testGroup "Test provided demo functionality"
    519   [ testCase "Constructor-safe lowering matches the authored Boolean not tree" $ do
    520       env <- evaluateCompilerFile "./demos/equality.tri"
    521       -- Constructor-only K pruning intentionally changes inspectable function
    522       -- trees. This specimen now equals its authored TC form; that is not a
    523       -- structural-equivalence promise for arbitrary extensional functions.
    524       env Map.! "main" @?= Stem Leaf
    525       env Map.! "not_TC?" @?=
    526         Fork (Fork (Stem Leaf) (Fork Leaf Leaf)) (Fork Leaf (Fork Leaf Leaf))
    527       env Map.! "not_Lambda?" @?= env Map.! "not_TC?"
    528       forM_ ["not_TC?", "not_Lambda?"] $ \name -> do
    529         apply (env Map.! name) Leaf @?= Stem Leaf
    530         apply (env Map.! name) (Stem Leaf) @?= Leaf
    531       env Map.! "bothTrueEqual?" @?= Stem Leaf
    532       env Map.! "bothFalseEqual?" @?= Stem Leaf
    533   , testCase "Convert values back to source code demo" $ do
    534       res     <- liftIO $ ((Map.! "main") <$> evaluateCompilerFile "./demos/toSource.tri")
    535       -- Native portable foundation trees, not the Haskell recovery cohort.
    536       decodeResult res @?= "\"(t (t (t t) (t t t)) (t t (t t t)))\""
    537   , testCase "Determining the size of stable-compiled functions" $ do
    538       env <- evaluateCompilerFile "./demos/size.tri"
    539       -- Count the actual executable independently of the compiled size worker.
    540       -- This is a representation check of the retained stable artifact,
    541       -- not a parity requirement across compiler backends.
    542       let nodes Leaf = 1 :: Integer
    543           nodes (Stem child) = 1 + nodes child
    544           nodes (Fork left right) = 1 + nodes left + nodes right
    545           actual = nodes (env Map.! "size")
    546       -- Constructor-K publication: the independent host count changes from
    547       -- 1845 to 1233; the compiled worker must still count its actual tree.
    548       actual @?= 1233
    549       env Map.! "main" @?= ofNumber actual
    550   , testCase "Level Order Traversal demo" $ do
    551       res     <- liftIO $ ((Map.! "main") <$> evaluateCompilerFile "./demos/levelOrderTraversal.tri")
    552       decodeResult res @?= "\"\n1 \n2 3 \n4 5 6 7 \n8 11 10 9 12 \""
    553   , testCase "Contract effect demo with do notation" $ do
    554       res     <- liftIO $ ((Map.! "main") <$> evaluateCompilerFile "./demos/contractEffects.tri")
    555       decodeResult res @?= "[t t, 10]"
    556   , testCase "Safe base wrappers demo" $ do
    557       res     <- liftIO $ ((Map.! "main") <$> evaluateCompilerFile "./demos/contractBasics.tri")
    558       decodeResult res @?= "[3, t t, t, t t]"
    559   ]
    560 
    561 decoding :: TestTree
    562 decoding = testGroup "Decoding Tests"
    563   [ testCase "Decode Leaf" $ do
    564       decodeResult Leaf @?= "t"
    565 
    566   , testCase "Decode list of non-ASCII numbers" $ do
    567       let input = ofList [ofNumber 1, ofNumber 14, ofNumber 6]
    568       decodeResult input @?= "[1, 14, 6]"
    569 
    570   , testCase "Decode list of ASCII numbers as a string" $ do
    571       let input = ofList [ofNumber 97, ofNumber 98, ofNumber 99]
    572       decodeResult input @?= "\"abc\""
    573 
    574   , testCase "Decode small number" $ do
    575       decodeResult (ofNumber 42) @?= "42"
    576 
    577   , testCase "Decode large number" $ do
    578       decodeResult (ofNumber 9999) @?= "9999"
    579 
    580   , testCase "Decode string in list" $ do
    581       let input = ofList [ofString "hello", ofString "world"]
    582       decodeResult input @?= "[\"hello\", \"world\"]"
    583 
    584   , testCase "Decode mixed list with strings" $ do
    585       let input = ofList [ofString "hello", ofNumber 42, ofString "world"]
    586       decodeResult input @?= "[\"hello\", 42, \"world\"]"
    587 
    588   , testCase "Decode nested lists with strings" $ do
    589       let input = ofList [ofList [ofString "nested"], ofString "string"]
    590       decodeResult input @?= "[[\"nested\"], \"string\"]"
    591   ]
    592 
    593 -- --------------------------------------------------------------------------
    594 -- Byte marshalling tests
    595 -- --------------------------------------------------------------------------
    596 
    597 byteMarshallingTests :: TestTree
    598 byteMarshallingTests = testGroup "Byte Marshalling Tests"
    599   [ testCase "shared byte encodings equal canonical numerals for all octets" $ do
    600       mapM_ (\n -> ofByte (fromIntegral n) @?= ofNumber n) [0..255]
    601 
    602   , testCase "byte buffer preserves order and multiplicity across repeated octets" $ do
    603       let values = concat (replicate 17 [0..255]) :: [Word8]
    604           bytes = BS.pack values
    605       ofBytes bytes @?= ofList (map (ofNumber . toInteger) values)
    606       toBytes (ofBytes bytes) @?= Right bytes
    607 
    608   , testCase "ofByte / toByte round-trip: 0" $ do
    609       let w8 = (0 :: Word8)
    610       toByte (ofByte w8) @?= Right w8
    611 
    612   , testCase "ofByte / toByte round-trip: 1" $ do
    613       let w8 = (1 :: Word8)
    614       toByte (ofByte w8) @?= Right w8
    615 
    616   , testCase "ofByte / toByte round-trip: 127" $ do
    617       let w8 = (127 :: Word8)
    618       toByte (ofByte w8) @?= Right w8
    619 
    620   , testCase "ofByte / toByte round-trip: 128" $ do
    621       let w8 = (128 :: Word8)
    622       toByte (ofByte w8) @?= Right w8
    623 
    624   , testCase "ofByte / toByte round-trip: 255" $ do
    625       let w8 = (255 :: Word8)
    626       toByte (ofByte w8) @?= Right w8
    627 
    628   , testCase "toByte rejects value > 255" $ do
    629       -- ofNumber 256 = Fork Leaf (Fork Leaf Leaf) — value 256
    630       toByte (ofNumber 256) @?= Left "Byte value out of range: 256"
    631 
    632   , testCase "toByte accepts Leaf" $ do
    633       toByte (Leaf) @?= Right 0
    634 
    635   , testCase "toByte rejects non-number tree" $ do
    636       toByte (Stem Leaf) @?= Left "Invalid Tree Calculus number"
    637       toByte (Stem (Stem Leaf)) @?= Left "Invalid Tree Calculus number"
    638 
    639   , testCase "ofBytes / toBytes round-trip: empty ByteString" $ do
    640       toBytes (ofBytes BS.empty) @?= Right BS.empty
    641 
    642   , testCase "ofBytes / toBytes round-trip: [0x00]" $ do
    643       toBytes (ofBytes (BS.pack [0x00])) @?= Right (BS.pack [0x00])
    644 
    645   , testCase "ofBytes / toBytes round-trip: [0xff]" $ do
    646       toBytes (ofBytes (BS.pack [0xff])) @?= Right (BS.pack [0xff])
    647 
    648   , testCase "ofBytes / toBytes round-trip: mixed bytes" $ do
    649       let bytes = BS.pack [0x00, 0x01, 0x7f, 0x80, 0xff, 0x41, 0x42, 0x43]
    650       toBytes (ofBytes bytes) @?= Right bytes
    651 
    652   , testCase "toBytes rejects non-list tree" $ do
    653       -- Leaf is a valid list (empty), so this won't work.
    654       -- Stem Leaf is not a list.
    655       toBytes (Stem Leaf) @?= Left "Invalid Tree Calculus list"
    656 
    657   , testCase "toBytes rejects list containing invalid byte (>255)" $ do
    658       -- [ofNumber 256, ofNumber 1] — first element is > 255
    659       let badList = ofList [ofNumber 256, ofNumber 1]
    660       toBytes badList @?= Left "Byte value out of range: 256"
    661 
    662   , testCase "nodePayloadToTreeBytes / treeBytesToNodePayload: Leaf payload" $ do
    663       -- Leaf payload is 0x00 (1 byte)
    664       let payload = BS.pack [0x00]
    665       treeBytesToNodePayload (nodePayloadToTreeBytes payload) @?= Right payload
    666 
    667   , testCase "nodePayloadToTreeBytes / treeBytesToNodePayload: Stem payload" $ do
    668       -- Stem payload: 0x01 || 32-byte hash = 33 bytes
    669       let payload = BS.pack (0x01 : replicate 32 0x42)
    670       treeBytesToNodePayload (nodePayloadToTreeBytes payload) @?= Right payload
    671 
    672   , testCase "nodePayloadToTreeBytes / treeBytesToNodePayload: Fork payload" $ do
    673       -- Fork payload: 0x02 || 32-byte hash || 32-byte hash = 65 bytes
    674       let payload = BS.pack (0x02 : replicate 64 0x42)
    675       treeBytesToNodePayload (nodePayloadToTreeBytes payload) @?= Right payload
    676 
    677   , testCase "hashToTreeBytes / treeBytesToHash round-trip" $ do
    678       -- Use a known 32-byte hash (SHA256 of "")
    679       let hashStr :: MerkleHash
    680           hashStr = "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"
    681       case hashToTreeBytes hashStr of
    682         Left err -> assertFailure $ "hashToTreeBytes failed: " ++ err
    683         Right tree -> treeBytesToHash tree @?= Right hashStr
    684 
    685   , testCase "hashToTreeBytes rejects invalid hex hash" $ do
    686       hashToTreeBytes "not-a-hash" @?= Left "Invalid hex MerkleHash"
    687 
    688   , testCase "hashToTreeBytes rejects non-32-byte hash" $ do
    689       -- "00" decodes to 1 byte, not 32
    690       hashToTreeBytes "00" @?= Left "Hash raw bytes must be 32 bytes"
    691 
    692   , testCase "treeBytesToHash rejects wrong byte count" $ do
    693       -- Only 16 bytes, not 32
    694       let t16 = ofBytes (BS.pack [0x41 | _ <- [1..16]])
    695       treeBytesToHash t16 @?= Left "Expected exactly 32 byte elements for hash"
    696   ]
    697 
    698 -- --------------------------------------------------------------------------
    699 -- Content store tests
    700 -- --------------------------------------------------------------------------
    701 
    702 contentStoreTests :: TestTree
    703 contentStoreTests = testGroup "Content Store Tests"
    704   [ testCase "Filesystem CAS: put/get object and sharded path" $
    705       withSystemTempDirectory "tricu-store" $ \dir -> do
    706         let store = StorePath dir
    707             domain = Domain "test.object.v1"
    708             payload = BS.pack [1, 2, 3, 4]
    709         h <- putObject store domain payload
    710         shardForHash h @?= take 3 (unpack h)
    711         objectPath store h @?= dir </> "objects" </> take 3 (unpack h) </> unpack h
    712         doesFileExist (objectPath store h) >>= (@?= True)
    713         getObject store h >>= (@?= Just payload)
    714 
    715   , testCase "Filesystem CAS: idempotent object writes" $
    716       withSystemTempDirectory "tricu-store" $ \dir -> do
    717         let store = StorePath dir
    718             domain = Domain "test.object.v1"
    719             payload = BS.pack [9, 8, 7]
    720         h1 <- putObject store domain payload
    721         h2 <- putObject store domain payload
    722         h1 @?= h2
    723         countStoredObjects store >>= (@?= 1)
    724 
    725   , testCase "Filesystem CAS: putTree/getTree round trip" $
    726       withSystemTempDirectory "tricu-store" $ \dir -> do
    727         let store = StorePath dir
    728             term = Fork (Stem Leaf) (Fork Leaf (Stem Leaf))
    729             leafHash = nodeHash NLeaf
    730             stemHash = nodeHash (NStem leafHash)
    731             rightHash = nodeHash (NFork leafHash stemHash)
    732             expectedRoot = nodeHash (NFork stemHash rightHash)
    733         root <- putTree store term
    734         root @?= expectedRoot
    735         getTree store root >>= (@?= Just term)
    736 
    737   , testCase "Filesystem CAS: shared subtrees are deduplicated" $
    738       withSystemTempDirectory "tricu-store" $ \dir -> do
    739         let store = StorePath dir
    740             shared = Stem Leaf
    741             term = Fork shared shared
    742         _ <- putTree store term
    743         countStoredObjects store >>= (@?= 3)
    744 
    745   , testCase "Workspace aliases: write/read/list object refs" $
    746       withSystemTempDirectory "tricu-store" $ \dir -> do
    747         let store = StorePath dir
    748             ref = ObjectRef "arboricx.tree-root.v1" "abc123"
    749         writeAlias store NameAlias "main" ref
    750         readAlias store NameAlias "main" >>= (@?= Just ref)
    751         listAliases store NameAlias >>= (@?= [("main", ref)])
    752 
    753   , testCase "Module manifests: put/get round trip through CAS" $
    754       withSystemTempDirectory "tricu-store" $ \dir -> do
    755         let store = StorePath dir
    756             term = Fork Leaf (Stem Leaf)
    757             manifestFor root = ModuleManifest []
    758               [ ModuleExport
    759                   "main"
    760                   (ObjectRef (unDomain treeTermDomain) root)
    761                   "arboricx.abi.tree.v1"
    762               ]
    763         root <- putTreeTerm store term
    764         h <- putManifest store (manifestFor root)
    765         getManifest store h >>= (@?= Just (manifestFor root))
    766 
    767   , testCase "ObjectResolver: resolves manifests and trees without filesystem coupling" $ do
    768       let term = Fork Leaf (Stem Leaf)
    769           leafH = nodeHash NLeaf
    770           stemH = nodeHash (NStem leafH)
    771           rootH = nodeHash (NFork leafH stemH)
    772           termH = hashObject treeTermDomain (encodeTreeTerm term)
    773           manifest = ModuleManifest []
    774             [ ModuleExport
    775                 "value"
    776                 (ObjectRef (unDomain treeTermDomain) termH)
    777                 "arboricx.abi.tree.v1"
    778               ]
    779           manifestBytes = encodeManifest manifest
    780           manifestH = hashObject manifestDomain manifestBytes
    781           objects = Map.fromList
    782             [ (("arboricx.merkle.node.v1", leafH), serializeNode NLeaf)
    783             , (("arboricx.merkle.node.v1", stemH), serializeNode (NStem leafH))
    784             , (("arboricx.merkle.node.v1", rootH), serializeNode (NFork leafH stemH))
    785             , ((unDomain treeTermDomain, termH), encodeTreeTerm term)
    786             , ((unDomain manifestDomain, manifestH), manifestBytes)
    787             ]
    788           hydrate objs h = case deserializeNode <$> Map.lookup ("arboricx.merkle.node.v1", h) objs of
    789             Nothing -> return Nothing
    790             Just NLeaf -> return (Just Leaf)
    791             Just (NStem child) -> fmap Stem <$> hydrate objs child
    792             Just (NFork left right) -> do
    793               l <- hydrate objs left
    794               r <- hydrate objs right
    795               return $ Fork <$> l <*> r
    796           resolver = ObjectResolver
    797             { resolverAlias = \kind name -> return $ if kind == ModuleAlias && name == "demo"
    798                 then Just (ObjectRef (unDomain manifestDomain) manifestH)
    799                 else Nothing
    800             , resolverObject = \ref -> return $ Map.lookup (objectRefKind ref, objectRefHash ref) objects
    801             , resolverManifest = \h -> return $ do
    802                 bytes <- Map.lookup (unDomain manifestDomain, h) objects
    803                 either (const Nothing) Just (decodeManifest bytes)
    804             , resolverTree = hydrate objects
    805             }
    806       resolverAlias resolver ModuleAlias "demo" >>= (@?= Just (ObjectRef (unDomain manifestDomain) manifestH))
    807       resolveManifest resolver manifestH >>= (@?= Just manifest)
    808       resolveTree resolver rootH >>= (@?= Just term)
    809 
    810   , testCase "Arboricx bundle: unpack transport bundle into CAS tree terms" $
    811       withSystemTempDirectory "tricu-store" $ \dir -> do
    812         let store = StorePath dir
    813             term = Fork (Stem Leaf) Leaf
    814             bundle = buildBundle [("main", term)]
    815         exports <- unpackBundleToStore store (encodeBundle bundle)
    816         case exports of
    817           [("main", root)] -> getTreeTerm store root >>= (@?= Just term)
    818           other -> assertFailure $ "unexpected exports: " ++ show other
    819 
    820   , testCase "Indexed CAS: extract roots without labels or unrelated nodes affecting identity" $ do
    821       let term = Fork (Stem Leaf) (Stem Leaf)
    822           first = buildBundle [("alpha", term)]
    823           second = buildBundle [("unrelated", ofNumber 99), ("beta", term)]
    824           [ex] = manifestExports (bundleManifest first)
    825           [_, other] = manifestExports (bundleManifest second)
    826       a <- either assertFailure pure (extractBundleRoot first $ exportRoot ex)
    827       b <- either assertFailure pure (extractBundleRoot second $ exportRoot other)
    828       encodeBundle a @?= encodeBundle b
    829       decodeIndexedTree (encodeBundle a) @?= Right term
    830       hashObject indexedTreeDomain (encodeBundle a) /= hashObject treeTermDomain (encodeTreeTerm term) @? "distinct object identities"
    831 
    832   , testCase "Indexed CAS: exponentially shared logical tree stays an indexed graph" $ do
    833       let base = buildBundle [("unused", Leaf)]
    834           graph = base { bundleNodes = Seq.fromList
    835             (BNLeaf : [BNFork (i - 1) (i - 1) | i <- [1..80]]) }
    836       root <- either assertFailure pure (extractBundleRoot graph 80)
    837       Seq.length (bundleNodes root) @?= 81
    838       BS.length (encodeBundle root) < 2048 @? "graph storage must not expand 2^81-1 logical nodes"
    839       case decodeIndexedTree (encodeBundle root) of
    840         Right (Fork _ _) -> pure ()
    841         other -> assertFailure $ "expected shared fork, got " ++ either id (const "non-fork") other
    842 
    843   , testCase "Indexed CAS: multiple exports become independently loadable objects" $
    844       withSystemTempDirectory "tricu-indexed-store" $ \dir -> do
    845         let store = StorePath dir
    846             terms = [("left", ofNumber 23), ("right", ofNumber 24)]
    847         roots <- unpackIndexedBundleToStore store (encodeBundle $ buildBundle terms)
    848         map fst roots @?= map fst terms
    849         forM_ (zip roots terms) $ \((_, digest), (_, term)) -> do
    850           payload <- getObject store digest >>= maybe (assertFailure "missing indexed object" >> pure BS.empty) pure
    851           hashObject indexedTreeDomain payload @?= digest
    852           decodeIndexedTree payload @?= Right term
    853 
    854   , testCase "Indexed CAS: reject malformed graphs and ambiguous root containers" $ do
    855       let leaf = buildBundle [("root", Leaf)]
    856           forward = leaf { bundleNodes = Seq.singleton (BNStem 0) }
    857           multiple = buildBundle [("a", Leaf), ("b", Stem Leaf)]
    858       case extractBundleRoot forward 0 of
    859         Left _ -> pure ()
    860         Right _ -> assertFailure "accepted cyclic node"
    861       case extractBundleRoot leaf 1 of
    862         Left _ -> pure ()
    863         Right _ -> assertFailure "accepted out-of-range root"
    864       case decodeIndexedTree (encodeBundle multiple) of
    865         Left _ -> pure ()
    866         Right _ -> assertFailure "accepted multiple root container"
    867 
    868   , testCase "Streaming pack: exact legacy bundle bytes across chunk boundaries" $ do
    869       let terms = [(pack ("value" ++ show n), ofNumber n) | n <- [0..128]] ++
    870                   [("alias", ofNumber 97), ("tree", Fork (Stem Leaf) (Fork Leaf Leaf))]
    871           packed chunkSize = do
    872             builder <- foldM (appendTerm chunkSize) emptyTreeTermBundle terms
    873             pure (finishTreeTermBundle builder)
    874           appendTerm chunkSize builder (name, term) = do
    875             let chunks bytes | BS.null bytes = []
    876                              | otherwise = let (first, rest) = BS.splitAt chunkSize bytes
    877                                            in first : chunks rest
    878             cursor <- foldM (flip feedTreeTerm) (startTreeTerm builder) (chunks (encodeTreeTerm term))
    879             endTreeTerm name cursor
    880       forM_ [1, 2, 3, 7, 65536] $ \chunkSize -> do
    881         actual <- either assertFailure pure (packed chunkSize)
    882         encodeBundle actual @?= encodeBundle (buildBundle terms)
    883         verifyBundle actual @?= Right ()
    884       encodeBundle (finishTreeTermBundle emptyTreeTermBundle) @?= encodeBundle (buildBundle [])
    885 
    886   , testCase "Streaming pack: rejects truncated, unknown and trailing tree bytes" $ do
    887       let parse bytes = feedTreeTerm bytes (startTreeTerm emptyTreeTermBundle) >>= endTreeTerm "main"
    888           failure bytes expected = case parse bytes of
    889             Left problem -> problem @?= expected
    890             Right _ -> assertFailure "accepted malformed canonical tree"
    891       forM_ [[], [1], [2,0], [2,1,0]] $ \bytes ->
    892         failure (BS.pack bytes) "unexpected end of tree term"
    893       failure (BS.pack [3]) "unknown tree term tag: 3"
    894       failure (BS.pack [0,0]) "trailing bytes after tree term"
    895       failure (BS.pack [1,0,3]) "trailing bytes after tree term"
    896 
    897   , testCase "Streaming pack: deep input does not need recursive tree construction" $ do
    898       let depth = 16384
    899       cursor <- either assertFailure pure $
    900         feedTreeTerm (BS.replicate depth 1 <> BS.singleton 0) (startTreeTerm emptyTreeTermBundle)
    901       builder <- either assertFailure pure (endTreeTerm "main" cursor)
    902       let bundle = finishTreeTermBundle builder
    903       Seq.length (bundleNodes bundle) @?= depth + 1
    904       bundleRoots bundle @?= [fromIntegral depth]
    905       verifyBundle bundle @?= Right ()
    906 
    907   , testCase "Streaming pack: CAS digest verification and byte-equivalent shared exports" $
    908       withSystemTempDirectory "tricu-streaming-pack" $ \dir -> do
    909         let store = StorePath dir
    910             terms = [("a", Fork (Stem Leaf) (Stem Leaf)), ("b", Stem Leaf),
    911                      ("alias", Fork (Stem Leaf) (Stem Leaf))]
    912         roots <- forM terms $ \(name, term) -> (,) name <$> putTreeTerm store term
    913         bundle <- packBundleFromStore store roots
    914         encodeBundle bundle @?= encodeBundle (buildBundle terms)
    915         -- Cross the actual file reader's 64 KiB boundary with unfinished frames.
    916         let depth = 65537
    917         deep <- putObject store treeTermDomain (BS.replicate depth 1 <> BS.singleton 0)
    918         deepBundle <- packBundleFromStore store [("deep", deep), ("alias", deep)]
    919         Seq.length (bundleNodes deepBundle) @?= depth + 1
    920         bundleRoots deepBundle @?= replicate 2 (fromIntegral depth)
    921         verifyBundle deepBundle @?= Right ()
    922         let root = snd (head roots)
    923         BS.writeFile (objectPath store root) (encodeTreeTerm Leaf)
    924         outcome <- try (packBundleFromStore store roots) :: IO (Either SomeException Bundle)
    925         case outcome of
    926           Left problem -> show problem `containsAll` ["Executable tree digest mismatch", unpack root]
    927           Right _ -> assertFailure "packed corrupted CAS bytes"
    928         forM_ [BS.empty, BS.pack [2,0], BS.pack [3], BS.pack [0,0]] $ \bytes -> do
    929           malformed <- putObject store treeTermDomain bytes
    930           rejected <- try (packBundleFromStore store [("bad", malformed)]) :: IO (Either SomeException Bundle)
    931           case rejected of
    932             Left _ -> pure ()
    933             Right _ -> assertFailure "packed malformed but correctly hashed CAS bytes"
    934 
    935   , testCase "Arboricx bundle: pack CAS tree terms into transport bundle" $
    936       withSystemTempDirectory "tricu-store" $ \dir -> do
    937         let store = StorePath dir
    938             term = Fork Leaf (Stem Leaf)
    939         root <- putTreeTerm store term
    940         bundle <- packBundleFromStore store [("main", root)]
    941         bundleRoots bundle @?= [2]
    942         let terms = reconstructBundleTermsForTest (bundleNodes bundle)
    943         case manifestExports (bundleManifest bundle) of
    944           [exported] -> do
    945             exportName exported @?= "main"
    946             terms V.! fromIntegral (exportRoot exported) @?= term
    947           other -> assertFailure $ "unexpected exports: " ++ show other
    948   ]
    949 
    950 reconstructBundleTermsForTest :: Seq.Seq BundleNode -> V.Vector T
    951 reconstructBundleTermsForTest nodes = V.fromList (go <$> Foldable.toList nodes)
    952   where
    953     built = V.fromList (go <$> Foldable.toList nodes)
    954     go BNLeaf = Leaf
    955     go (BNStem child) = Stem (built V.! fromIntegral child)
    956     go (BNFork left right) = Fork (built V.! fromIntegral left) (built V.! fromIntegral right)
    957 
    958 countStoredObjects :: StorePath -> IO Int
    959 countStoredObjects store@(StorePath root) = do
    960   ensureStore store
    961   shards <- listDirectory (root </> "objects")
    962   fmap sum $ forM shards $ \shard -> do
    963     files <- listDirectory (root </> "objects" </> shard)
    964     return (length files)
    965 
    966 -- --------------------------------------------------------------------------
    967 -- Wire module tests
    968 -- --------------------------------------------------------------------------
    969 
    970 -- | Helper: create a temporary file-backed DB, store a term, return the
    971 
    972 wireTests :: TestTree
    973 wireTests = testGroup "Wire Tests"
    974   [ testCase "Indexed bundle: header and manifest declare indexed format" $ do
    975       let term = Leaf
    976           bundle = buildBundle [("main", term)]
    977           wireData = encodeBundle bundle
    978       BS.take 8 wireData @?= BS.pack [0x41, 0x52, 0x42, 0x4f, 0x52, 0x49, 0x43, 0x58]
    979       case decodeBundle wireData of
    980         Left err -> assertFailure $ "decodeBundle failed: " ++ err
    981         Right decoded -> do
    982           let manifest = bundleManifest decoded
    983               tree = manifestTree manifest
    984               hashSpec = treeNodeHash tree
    985           manifestSchema manifest @?= "arboricx.bundle.manifest.v1"
    986           manifestBundleType manifest @?= "tree-calculus-executable-object"
    987           manifestClosure manifest @?= ClosureComplete
    988           treeCalculus tree @?= "tree-calculus.v1"
    989           treeNodePayload tree @?= "arboricx.indexed.payload.v1"
    990           nodeHashAlgorithm hashSpec @?= "indexed"
    991           nodeHashDomain hashSpec @?= "arboricx.indexed.node.v1"
    992           bundleRoots decoded @?= bundleRoots bundle
    993           case manifestExports manifest of
    994             [exported] -> do
    995               exportName exported @?= "main"
    996               exportRoot exported @?= head (bundleRoots bundle)
    997               exportKind exported @?= "term"
    998               exportAbi exported @?= "arboricx.abi.tree.v1"
    999             exports -> assertFailure $ "Expected one export, got: " ++ show exports
   1000 
   1001   , testCase "Indexed bundle: deterministic encoding" $ do
   1002       let term = Stem (Stem Leaf)
   1003           bundle1 = buildBundle [("main", term)]
   1004           bundle2 = buildBundle [("main", term)]
   1005       encodeBundle bundle1 @?= encodeBundle bundle2
   1006 
   1007   , testCase "Indexed bundle: renaming export changes bytes" $ do
   1008       let term = Leaf
   1009           mainBundle = buildBundle [("main", term)]
   1010           renamedBundle = buildBundle [("validate", term)]
   1011       encodeBundle mainBundle /= encodeBundle renamedBundle @? "different export names should produce different bytes"
   1012       -- But nodes are identical
   1013       bundleNodes mainBundle @?= bundleNodes renamedBundle
   1014 
   1015   , testCase "Indexed bundle: verify rejects out-of-bounds root" $ do
   1016       let term = Leaf
   1017           bundle = buildBundle [("main", term)]
   1018           badBundle = bundle { bundleRoots = [99] }
   1019       case verifyBundle badBundle of
   1020         Left err -> assertBool ("Expected bounds error, got: " ++ err) ("out of bounds" `isInfixOf` err)
   1021         Right () -> assertFailure "Expected out-of-bounds root to be rejected"
   1022 
   1023   , testCase "Indexed bundle: verify rejects out-of-bounds child index" $ do
   1024       let bundle = Bundle
   1025             { bundleVersion = 1000
   1026             , bundleRoots = [1]
   1027             , bundleNodes = Seq.fromList [BNLeaf, BNStem 99]
   1028             , bundleManifest = (bundleManifest $ buildBundle [("main", Leaf)])
   1029                 { manifestRoots = [BundleRoot 1 "default"]
   1030                 , manifestExports = [BundleExport "main" 1 "term" "arboricx.abi.tree.v1"]
   1031                 }
   1032             , bundleManifestBytes = BS.empty
   1033             }
   1034       case verifyBundle bundle of
   1035         Left err -> assertBool ("Expected bounds error, got: " ++ err) ("references child 99" `isInfixOf` err)
   1036         Right () -> assertFailure "Expected out-of-bounds child to be rejected"
   1037 
   1038   , testCase "Indexed bundle: verify rejects acyclic (forward reference)" $ do
   1039       let bundle = Bundle
   1040             { bundleVersion = 1000
   1041             , bundleRoots = [1]
   1042             , bundleNodes = Seq.fromList [BNStem 1, BNLeaf]  -- index 0 refers to 1 (forward)
   1043             , bundleManifest = (bundleManifest $ buildBundle [("main", Leaf)])
   1044                 { manifestRoots = [BundleRoot 1 "default"]
   1045                 , manifestExports = [BundleExport "main" 1 "term" "arboricx.abi.tree.v1"]
   1046                 }
   1047             , bundleManifestBytes = BS.empty
   1048             }
   1049       case verifyBundle bundle of
   1050         Left err -> assertBool ("Expected acyclicity error, got: " ++ err) ("references child 1" `isInfixOf` err)
   1051         Right () -> assertFailure "Expected forward reference to be rejected"
   1052 
   1053   , testCase "Indexed bundle: verify rejects duplicate nodes" $ do
   1054       let bundle = Bundle
   1055             { bundleVersion = 1000
   1056             , bundleRoots = [0]
   1057             , bundleNodes = Seq.fromList [BNLeaf, BNLeaf]
   1058             , bundleManifest = (bundleManifest $ buildBundle [("main", Leaf)])
   1059                 { manifestRoots = [BundleRoot 0 "default"]
   1060                 , manifestExports = [BundleExport "main" 0 "term" "arboricx.abi.tree.v1"]
   1061                 }
   1062             , bundleManifestBytes = BS.empty
   1063             }
   1064       case verifyBundle bundle of
   1065         Left err -> assertBool ("Expected duplicate error, got: " ++ err) ("duplicate" `isInfixOf` err)
   1066         Right () -> assertFailure "Expected duplicate nodes to be rejected"
   1067 
   1068   , testCase "Indexed bundle: unpack into filesystem CAS" $
   1069       withSystemTempDirectory "tricu-store" $ \dir -> do
   1070         let term = Leaf
   1071             bundle = buildBundle [("validateEmail", term)]
   1072             wireData = encodeBundle bundle
   1073             store = StorePath dir
   1074         roots <- unpackBundleToStore store wireData
   1075         case roots of
   1076           [("validateEmail", root)] -> getTreeTerm store root >>= (@?= Just term)
   1077           other -> assertFailure $ "unexpected roots: " ++ show other
   1078 
   1079   , testCase "Indexed bundle: round-trip decode and verify" $ do
   1080       let term = Stem (Stem Leaf)
   1081           bundle = buildBundle [("main", term)]
   1082           wireData = encodeBundle bundle
   1083       case decodeBundle wireData of
   1084         Left err -> assertFailure $ "decodeBundle failed: " ++ err
   1085         Right decoded -> case verifyBundle decoded of
   1086           Left err -> assertFailure $ "verifyBundle failed: " ++ err
   1087           Right () -> do
   1088             bundleRoots decoded @?= bundleRoots bundle
   1089             Seq.length (bundleNodes decoded) @?= Seq.length (bundleNodes bundle)
   1090 
   1091   , testCase "Indexed bundle: unsupported manifest semantics rejected" $ do
   1092       let term = Leaf
   1093           bundle = buildBundle [("main", term)]
   1094           manifest = bundleManifest bundle
   1095           partialBundle = bundle
   1096             { bundleManifest = manifest { manifestClosure = ClosurePartial }
   1097             , bundleManifestBytes = BS.empty
   1098             }
   1099           capabilityBundle = bundle
   1100             { bundleManifest = manifest
   1101                 { manifestRuntime = (manifestRuntime manifest)
   1102                     { runtimeCapabilities = ["host.io"] }
   1103                 }
   1104             , bundleManifestBytes = BS.empty
   1105             }
   1106           wrongHashBundle = bundle
   1107             { bundleManifest = manifest
   1108                 { manifestTree = (manifestTree manifest)
   1109                     { treeNodeHash = (treeNodeHash $ manifestTree manifest)
   1110                         { nodeHashAlgorithm = "blake3" }
   1111                     }
   1112                 }
   1113             , bundleManifestBytes = BS.empty
   1114             }
   1115       case verifyBundle partialBundle of
   1116         Left err -> assertBool ("Expected closure error, got: " ++ err) ("closure = complete" `isInfixOf` err)
   1117         Right () -> assertFailure "Expected partial closure to be rejected"
   1118       case verifyBundle capabilityBundle of
   1119         Left err -> assertBool ("Expected capability error, got: " ++ err) ("capabilities" `isInfixOf` err)
   1120         Right () -> assertFailure "Expected runtime capabilities to be rejected"
   1121       case verifyBundle wrongHashBundle of
   1122         Left err -> assertBool ("Expected hash algorithm error, got: " ++ err) ("node hash algorithm" `isInfixOf` err)
   1123         Right () -> assertFailure "Expected unsupported node hash algorithm to be rejected"
   1124     ]
   1125 
   1126 -- --------------------------------------------------------------------------
   1127 -- Tricu reader tests
   1128 -- Smoke-test the tricu-native Arboricx reader against indexed bundles.
   1129 -- --------------------------------------------------------------------------
   1130 
   1131 tricuReaderTests :: TestTree
   1132 tricuReaderTests = testGroup "Tricu Reader Tests"
   1133   [ testCase "Tricu reader parses indexed bundle (id fixture)" $ check "id"
   1134   , testCase "Tricu reader parses indexed bundle (append fixture)" $ check "append"
   1135   , testCase "Tricu reader parses indexed bundle (bool fixtures)" $ mapM_ check ["true", "false"]
   1136   ]
   1137   where
   1138     check name = do
   1139       bytes <- BS.readFile ("./test/fixtures/" ++ name ++ ".arboricx")
   1140       let reply = foldl apply (fixtureExports "arboricx" Map.! "runArboricx") [ofBytes bytes, Leaf]
   1141       apply (fixtureExports "base" Map.! "fst") reply @?= trueT
   1142 
   1143 -- --------------------------------------------------------------------------
   1144 -- Byte-list utility tests
   1145 -- Expected values built with canonical Haskell-side T constructors.
   1146 -- --------------------------------------------------------------------------
   1147 
   1148 -- | Helpers for byte-list test expectations.
   1149 
   1150 trueT  :: T
   1151 trueT  = Stem Leaf
   1152 
   1153 falseT :: T
   1154 falseT = Leaf
   1155 
   1156 pairT :: T -> T -> T
   1157 pairT = Fork
   1158 
   1159 portableReply :: Bool -> T -> T -> Bool -> Bool -> T
   1160 portableReply success payload rest consumed committed =
   1161   pairT (if success then trueT else falseT)
   1162     (pairT payload (pairT rest (pairT
   1163       (if consumed then trueT else falseT)
   1164       (if committed then trueT else falseT))))
   1165 
   1166 portableDiagnostic :: T -> T -> T -> T -> T -> T
   1167 portableDiagnostic code expected found location contexts =
   1168   pairT code (pairT expected (pairT found (pairT location contexts)))
   1169 
   1170 -- --------------------------------------------------------------------------
   1171 -- Reusable, located character-scanning primitives
   1172 -- --------------------------------------------------------------------------
   1173 
   1174 locatedLexerTests :: TestTree
   1175 locatedLexerTests = testGroup "Located character-scanning library"
   1176   [ testCase "checked native scanner handles abstract input" $ do
   1177         env <- evaluateCompilerFile "./test/lexerScanImport.tri"
   1178         case Map.lookup "scanner" env of
   1179           Nothing -> assertFailure "missing portable scanner"
   1180           Just scanner -> do
   1181             apply scanner (ofString "aa!") @?=
   1182               portableReply True
   1183                 (pairT (ofList [ofNumber 97, ofNumber 97])
   1184                   (gatePosition 1 3 2))
   1185                 (ofString "!") True False
   1186             apply scanner Leaf @?=
   1187               portableReply True (pairT Leaf (gatePosition 1 1 0)) Leaf False False
   1188             apply scanner (ofString "a\nx") @?=
   1189               portableReply True
   1190                 (pairT (ofList [ofNumber 97]) (gatePosition 1 2 1))
   1191                 (ofString "\nx") True False
   1192         case Map.lookup "dispatch" env of
   1193           Nothing -> assertFailure "missing ordered dispatch"
   1194           Just dispatch -> do
   1195             apply dispatch (ofNumber 97) @?= ofNumber 1
   1196             apply dispatch (ofNumber 98) @?= ofNumber 3
   1197   ]
   1198 
   1199 -- --------------------------------------------------------------------------
   1200 -- Single-expression feasibility-gate source lexer
   1201 -- --------------------------------------------------------------------------
   1202 
   1203 gateLexerEnv :: Env
   1204 gateLexerEnv = fixtureExports "source.lexer"
   1205 {-# NOINLINE gateLexerEnv #-}
   1206 
   1207 gatePosition :: Integer -> Integer -> Integer -> T
   1208 gatePosition line column offset =
   1209   pairT (ofNumber line) (pairT (ofNumber column) (ofNumber offset))
   1210 
   1211 gateToken :: Integer -> T -> T -> T -> T
   1212 gateToken kind payload start end =
   1213   pairT (ofNumber kind) (pairT payload (pairT start end))
   1214 
   1215 gateLexerTests :: TestTree
   1216 gateLexerTests = testGroup "Feasibility-gate source lexer"
   1217   [ testCase "module export normalizes under abstract source input" $
   1218       withSystemTempDirectory "gate-lexer-import" $ \dir -> do
   1219         env <- evaluateCompilerFile "./test/sourceLexerImport.tri"
   1220         Map.lookup "main" env @?= Just Leaf
   1221   , testCase "keyword, identifiers, and punctuation carry spans" $ do
   1222       check "t (foo : turtle)"
   1223         (portableReply True
   1224           (ofList
   1225             [ gateToken 1 (ofString "t") (gatePosition 1 1 0) (gatePosition 1 2 1)
   1226             , gateToken 3 Leaf (gatePosition 1 3 2) (gatePosition 1 4 3)
   1227             , gateToken 2 (ofString "foo") (gatePosition 1 4 3) (gatePosition 1 7 6)
   1228             , gateToken 5 Leaf (gatePosition 1 8 7) (gatePosition 1 9 8)
   1229             , gateToken 2 (ofString "turtle") (gatePosition 1 10 9) (gatePosition 1 16 15)
   1230             , gateToken 4 Leaf (gatePosition 1 16 15) (gatePosition 1 17 16)
   1231             ]) Leaf True False)
   1232   , testCase "decimal digits accumulate and retain leading-zero span" $ do
   1233       check "009 x" (portableReply True
   1234         (ofList
   1235           [ gateToken 7 (ofNumber 9) (gatePosition 1 1 0) (gatePosition 1 4 3)
   1236           , gateToken 2 (ofString "x") (gatePosition 1 5 4) (gatePosition 1 6 5)
   1237           ]) Leaf True False)
   1238   , testCase "quoted string decodes escapes and retains raw source span" $ do
   1239       let input = "\"a\\n\\\"b\""
   1240       check input (portableReply True
   1241         (ofList [gateToken 8 (ofString "a\n\"b")
   1242           (gatePosition 1 1 0) (gatePosition 1 9 8)]) Leaf True False)
   1243   , testCase "source string escape repertoire is preserved" $ do
   1244       let input = "\"\\t\\r\\f\\b\\\\\\'\""
   1245           decoded = "\t\r\f\b\\'"
   1246           width = fromIntegral (length input)
   1247       check input (portableReply True
   1248         (ofList [gateToken 8 (ofString decoded)
   1249           (gatePosition 1 1 0) (gatePosition 1 (width + 1) width)])
   1250         Leaf True False)
   1251   , testCase "bracket punctuation retains both half-open spans" $ do
   1252       check "[t]" (portableReply True
   1253         (ofList
   1254           [ gateToken 9 Leaf (gatePosition 1 1 0) (gatePosition 1 2 1)
   1255           , gateToken 1 (ofString "t") (gatePosition 1 2 1) (gatePosition 1 3 2)
   1256           , gateToken 10 Leaf (gatePosition 1 3 2) (gatePosition 1 4 3)
   1257           ]) Leaf True False)
   1258   , testCase "equation commas are located tokens, not expression-list syntax" $
   1259       check "[t, t]" (portableReply True (ofList
   1260         [ gateToken 9 Leaf (gatePosition 1 1 0) (gatePosition 1 2 1)
   1261         , gateToken 1 (ofString "t") (gatePosition 1 2 1) (gatePosition 1 3 2)
   1262         , gateToken 20 Leaf (gatePosition 1 3 2) (gatePosition 1 4 3)
   1263         , gateToken 1 (ofString "t") (gatePosition 1 5 4) (gatePosition 1 6 5)
   1264         , gateToken 10 Leaf (gatePosition 1 6 5) (gatePosition 1 7 6)
   1265         ]) Leaf True False)
   1266   , testCase "comment delimiters inside strings remain literal" $
   1267       check "\"--|-\"" (portableReply True
   1268         (ofList [gateToken 8 (ofString "--|-")
   1269           (gatePosition 1 1 0) (gatePosition 1 7 6)]) Leaf True False)
   1270   , testCase "invalid and unfinished string escapes return diagnostics" $ do
   1271       check "\"\\q\"" (portableReply False
   1272         (portableDiagnostic (ofNumber 6) (ofNumber 34) (ofNumber 113)
   1273           (gatePosition 1 3 2) Leaf) (ofString "\\q\"") True False)
   1274       check "\"hi" (portableReply False
   1275         (portableDiagnostic (ofNumber 5) (ofNumber 34) Leaf
   1276           (gatePosition 1 1 0) Leaf) Leaf True False)
   1277   , testCase "line comment preserves newline token and locations" $ do
   1278       check "t --hello\nx" (portableReply True
   1279         (ofList
   1280           [ gateToken 1 (ofString "t") (gatePosition 1 1 0) (gatePosition 1 2 1)
   1281           , gateToken 6 Leaf (gatePosition 1 10 9) (gatePosition 2 1 10)
   1282           , gateToken 2 (ofString "x") (gatePosition 2 1 10) (gatePosition 2 2 11)
   1283           ]) Leaf True False)
   1284   , testCase "block comment consumes internal newlines but preserves end span" $ do
   1285       let input = "t |-ab\nc-| t"
   1286       check input (portableReply True
   1287         (ofList
   1288           [ gateToken 1 (ofString "t") (gatePosition 1 1 0) (gatePosition 1 2 1)
   1289           , gateToken 1 (ofString "t") (gatePosition 2 5 11) (gatePosition 2 6 12)
   1290           ]) Leaf True False)
   1291   , testCase "empty block comment consumes both delimiters" $
   1292       check "|- -|t" (portableReply True
   1293         (ofList [gateToken 1 (ofString "t")
   1294           (gatePosition 1 6 5) (gatePosition 1 7 6)]) Leaf True False)
   1295   , testCase "unterminated block comment returns a structured diagnostic" $
   1296       check "t |-oops" (portableReply False
   1297         (portableDiagnostic (ofNumber 5) (ofString "-|") Leaf
   1298           (gatePosition 1 3 2) Leaf) Leaf True False)
   1299   , testCase "comment at EOF leaves no phantom token" $
   1300       check "t --bye" (portableReply True
   1301         (ofList [gateToken 1 (ofString "t")
   1302           (gatePosition 1 1 0) (gatePosition 1 2 1)]) Leaf True False)
   1303   , testCase "lone dash remains a located lexical error" $
   1304       check "-" (portableReply False
   1305         (portableDiagnostic (ofNumber 4) Leaf (ofNumber 45)
   1306           (gatePosition 1 1 0) Leaf) (ofString "-") False False)
   1307   , testCase "newline advances source position" $ do
   1308       check "t\nfoo"
   1309         (portableReply True
   1310           (ofList
   1311             [ gateToken 1 (ofString "t") (gatePosition 1 1 0) (gatePosition 1 2 1)
   1312             , gateToken 6 Leaf (gatePosition 1 2 1) (gatePosition 2 1 2)
   1313             , gateToken 2 (ofString "foo") (gatePosition 2 1 2) (gatePosition 2 4 5)
   1314             ]) Leaf True False)
   1315   , testCase "invalid character returns structured diagnostic at actual input" $
   1316       check "t #"
   1317         (portableReply False
   1318           (portableDiagnostic (ofNumber 4) Leaf (ofNumber 35)
   1319             (gatePosition 1 3 2) Leaf) (ofString "#") True False)
   1320   , testCase "explicit scalar BOM is rejected, matching browser character input" $
   1321       apply (gateLexerEnv Map.! "lexGateSource") (ofString "\xfeff\&t") @?=
   1322         portableReply False
   1323           (portableDiagnostic (ofNumber 4) Leaf (ofNumber 65279)
   1324             (gatePosition 1 1 0) Leaf) (ofString "\xfeff\&t") False False
   1325   , testCase "empty input yields empty tokens and no consumption" $
   1326       check ""
   1327         (portableReply True Leaf Leaf False False)
   1328   , testCase "tab is rejected at its source position" $
   1329       check "t\tfoo"
   1330         (portableReply False
   1331           (portableDiagnostic (ofNumber 4) Leaf (ofNumber 9)
   1332             (gatePosition 1 2 1) Leaf) (ofString "\tfoo") True False)
   1333   , testCase "pipe operators retain token kinds and two-character spans" $ do
   1334       check "<| |>" (portableReply True
   1335         (ofList
   1336           [ gateToken 15 Leaf (gatePosition 1 1 0) (gatePosition 1 3 2)
   1337           , gateToken 16 Leaf (gatePosition 1 4 3) (gatePosition 1 6 5)
   1338           ]) Leaf True False)
   1339   , testCase "do and bind arrow have independent located token kinds" $ do
   1340       check "do <-" (portableReply True
   1341         (ofList
   1342           [ gateToken 17 (ofString "do")
   1343               (gatePosition 1 1 0) (gatePosition 1 3 2)
   1344           , gateToken 18 Leaf (gatePosition 1 4 3) (gatePosition 1 6 5)
   1345           ]) Leaf True False)
   1346   , testCase "ASCII letter/digit range boundaries remain recognized" $ do
   1347       check "AZaz09" (portableReply True
   1348         (ofList [gateToken 2 (ofString "AZaz09")
   1349           (gatePosition 1 1 0) (gatePosition 1 7 6)]) Leaf True False)
   1350   , testCase "longer ASCII identifier scans without discarding suffix" $
   1351       let name = replicate 32 'a'
   1352       in check (name ++ " t")
   1353         (portableReply True
   1354           (ofList
   1355             [ gateToken 2 (ofString name) (gatePosition 1 1 0) (gatePosition 1 33 32)
   1356             , gateToken 1 (ofString "t") (gatePosition 1 34 33) (gatePosition 1 35 34)
   1357             ]) Leaf True False)
   1358   ]
   1359   where
   1360     check input expected =
   1361       apply (gateLexerEnv Map.! "lexGateSource") (ofString input) @?= expected
   1362 
   1363 -- --------------------------------------------------------------------------
   1364 -- Single-expression feasibility-gate surface parser
   1365 -- --------------------------------------------------------------------------
   1366 
   1367 -- Library paths select authenticated published interfaces. Unmapped test units
   1368 -- use the portable compiler; neither path interprets historical frontend syntax.
   1369 evaluateCompilerFile :: FilePath -> IO Env
   1370 evaluateCompilerFile path =
   1371   case [legacy | (legacy, _, source) <- fst compiledLibraries,
   1372                source == maybe path id (stripPrefix "./" path)] of
   1373     [legacy] -> pure (fixtureExports legacy)
   1374     [] -> compileFixtureFile path
   1375     _ -> error "ambiguous compiler fixture module"
   1376 
   1377 -- Test wrappers are compiled by the retained portable stable unit API; library
   1378 -- payloads come from authenticated fixtures. Packing remains a host mechanism.
   1379 packCompilerFile :: FilePath -> FilePath -> [Text] -> IO ()
   1380 packCompilerFile input output names = do
   1381   env <- evaluateCompilerFile input
   1382   let selected = [(name, env Map.! unpack name) | name <- names]
   1383   BS.writeFile output (encodeBundle (buildBundle selected))
   1384 
   1385 -- Immutable pre-optimization worker from the accepted d7b3c37 generation.
   1386 -- No source compilation or fresh module builds enter the fast gate. Keeping
   1387 -- this separate from the current compiler fixture makes fallback comparisons
   1388 -- independent of the implementation under test.
   1389 hashHexReference :: Env
   1390 hashHexReference = unsafePerformIO $ do
   1391   bytes <- BS.readFile "test/fixtures/hash-hex-reference.arboricx"
   1392   let digest = unpack (hashObject (Domain "tricu.hash-hex-reference.v1") bytes)
   1393   unless (digest == "b5419c577b5a3e8a4458261bfa6934a43cce40eb86e24326aa9a33d6d0552e81") $
   1394     error "hash-hex reference fixture changed; do not refresh it from the candidate"
   1395   bundle <- either error pure (decodeBundle bytes)
   1396   either error pure (verifyBundle bundle)
   1397   let terms = reconstructBundleTerms (bundleNodes bundle)
   1398   pure $ Map.fromList
   1399     [(unpack (exportName entry), terms V.! fromIntegral (exportRoot entry)) |
   1400       entry <- manifestExports (bundleManifest bundle)]
   1401 {-# NOINLINE hashHexReference #-}
   1402 
   1403 digestHexTests :: TestTree
   1404 digestHexTests = testGroup "guarded digest hex"
   1405   [ testCase "all canonical byte values match integer oracle and old kernel" $
   1406       forM_ [0..255] $ \value -> do
   1407         let input = ofList [ofNumber value]
   1408             expected = if (value >= 48 && value <= 57) ||
   1409                           (value >= 97 && value <= 102) then Stem Leaf else Leaf
   1410         apply old input @?= expected
   1411         apply current input @?= expected
   1412   , testCase "wider, noncanonical and malformed trees retain old behavior" $
   1413       forM_ unusual $ \input -> apply current input @?= apply old input
   1414   , testCase "complete digests and late invalid bytes retain old behavior" $
   1415       forM_ strings $ \input -> apply current (ofString input) @?= apply old (ofString input)
   1416   , testCase "public digest length and alphabet checks remain exact" $ do
   1417       let valid = snd compilerFixture Map.! "source_object.sourceObjectHashValid"
   1418       forM_ [0,1,63,64,65] $ \size ->
   1419         apply valid (ofString (replicate size 'a')) @?= if size == 64 then Stem Leaf else Leaf
   1420       forM_ ['A','G','/', '\NUL', '\255'] $ \byte ->
   1421         apply valid (ofString (replicate 63 'a' ++ [byte])) @?= Leaf
   1422   ]
   1423   where
   1424     fix = hashHexReference Map.! "fix"
   1425     old = apply fix (hashHexReference Map.! "worker")
   1426     current = snd compilerFixture Map.! "source_object.sourceObjectHashHex"
   1427     pad Leaf count = foldr (\_ rest -> Fork Leaf rest) Leaf [1..count]
   1428     pad (Fork bit rest) count = Fork bit (pad rest count)
   1429     pad other _ = other
   1430     unusual =
   1431       map (ofList . (:[]) . ofNumber) [256,257,511,512,65535] ++
   1432       [ofList [pad (ofNumber value) count] |
   1433         value <- [0,1,47,48,49,57,58,96,97,102,103,255], count <- [1,2]] ++
   1434       map (ofList . (:[]))
   1435         [Stem Leaf, Stem (Stem Leaf), Fork (Fork Leaf Leaf) Leaf,
   1436          Fork (Stem (Stem Leaf)) Leaf, Fork Leaf (Stem Leaf)] ++
   1437       [Leaf, Stem Leaf, Stem (Stem Leaf),
   1438        Fork (ofNumber 97) (Stem Leaf), Fork (ofNumber 103) (Stem Leaf)]
   1439     strings = ["0123456789abcdef", replicate 64 'a', replicate 64 '0',
   1440       replicate 64 'f', concat (replicate 4 "0123456789abcdef"),
   1441       'A' : replicate 63 'a', replicate 63 'a' ++ "G",
   1442       replicate 31 'a' ++ "\NUL" ++ replicate 32 'a',
   1443       replicate 31 'a' ++ "\255" ++ replicate 32 'a']
   1444 
   1445 compilerFixtureTests :: TestTree
   1446 compilerFixtureTests = testGroup "self-built reference compiler fixture"
   1447   [ testCase "matches all recorded reference source inputs" $ do
   1448       length (fst compilerFixture) @?= 34
   1449       assertBool "missing stable binding-pass exports"
   1450         (all (`Map.member` snd compilerFixture)
   1451           ["source_binding.bindingTransform", "source_binding.bindingInspect",
   1452            "source_binding_analysis.analyze", "source_binding_support.choose",
   1453            "source_binding_support.not"])
   1454       assertBool "fixture-only patterns still in compiler closure"
   1455         (all (\(_, alias, _) -> alias /= "patterns") (fst compilerFixture))
   1456       assertBool "patterns missing from support cohort"
   1457         (any (\(_, alias, _) -> alias == "patterns") (fst supportFixture))
   1458       assertBool "missing name index exports"
   1459         (all (`Map.member` snd compilerFixture)
   1460           ["source_index.sourceIndexBuild", "source_index.sourceIndexGet", "source_index.sourceIndexPut"])
   1461       assertBool "missing unit compiler export"
   1462         (Map.member "source_unit.compileSource" (snd compilerFixture))
   1463   , testCase "direct fixture interfaces exclude imports and reject unknown modules" $ do
   1464       let http = fixtureExports "http"
   1465       assertBool "missing owned HTTP export" (Map.member "parseHeaders" http)
   1466       forM_ ["append", "kernel", "source_unit.compileSource"] $ \name ->
   1467         assertBool ("leaked imported/injected name: " ++ name) (Map.notMember name http)
   1468       rejected <- try (evaluate (Map.size (fixtureExports "missing.fixture"))) :: IO (Either SomeException Int)
   1469       case rejected of
   1470         Left _ -> pure ()
   1471         Right _ -> assertFailure "unknown fixture module silently accepted"
   1472   , testCase "portable wrapper compilation keeps unused definitions lazy" $ do
   1473       env <- compileFixtureDefinitions Map.empty
   1474         "unused = (x : x x) (x : x x)\nmain = t"
   1475       Map.keys env @?= ["main", "unused"]
   1476       Map.lookup "main" env @?= Just Leaf
   1477   , testCase "portable wrapper compilation rejects invalid source without fallback" $ do
   1478       forM_ ["main = missing", "main = t\nmain = t", "t", "main = #"] $ \source -> do
   1479         rejected <- try (compileFixtureDefinitions Map.empty source) :: IO (Either SomeException Env)
   1480         case rejected of
   1481           Left err -> assertBool "expected portable diagnostic"
   1482             ("portable test compilation failed" `isInfixOf` show err)
   1483           Right _ -> assertFailure ("accepted invalid wrapper: " ++ source)
   1484   , testCase "portable wrapper compilation uses only explicit externals" $ do
   1485       env <- compileFixtureDefinitions (Map.singleton "outside" (Stem Leaf)) "main = outside t"
   1486       Map.keys env @?= ["main"]
   1487       Map.lookup "main" env @?= Just (Fork Leaf Leaf)
   1488       rejected <- try (compileFixtureDefinitions (Map.singleton "outside" Leaf) "outside = t")
   1489         :: IO (Either SomeException Env)
   1490       case rejected of
   1491         Left _ -> pure ()
   1492         Right _ -> assertFailure "wrapper shadows an imported name"
   1493   , testCase "every reference compiler module authors native headers" $ do
   1494       forM_ (fst compilerFixture) $ \(_, _, path) -> do
   1495         source <- readTestSource path
   1496         assertBool ("missing native export: " ++ path)
   1497           (any ("!export " `isPrefixOf`) (lines source))
   1498         assertBool ("legacy import: " ++ path)
   1499           (not (any ("!import \"" `isPrefixOf`) (lines source)))
   1500   , testCase "reference source rejects corrupt bytes and unsafe digests" $
   1501       withSystemTempDirectory "tricu-reference-source" $ \dir -> do
   1502         let bytes = BS.pack [120,32,61,32,116,10]
   1503             pin = unpack (hashObject (Domain (pack "arboricx.source.v1")) bytes)
   1504             path = dir </> (pin ++ ".tri")
   1505             rejected action = do
   1506               result <- try action :: IO (Either SomeException BS.ByteString)
   1507               case result of
   1508                 Left _ -> pure ()
   1509                 Right _ -> assertFailure "invalid reference source accepted"
   1510         rejected (readPinnedSource dir pin)
   1511         BS.writeFile path bytes
   1512         readPinnedSource dir pin >>= (@?= bytes)
   1513         BS.writeFile path (bytes <> BS.singleton 32)
   1514         rejected (readPinnedSource dir pin)
   1515         rejected (readPinnedSource dir "../source")
   1516   , testCase "native prelude retains exactly its dependency re-exports" $ do
   1517       let exports = snd compilerFixture
   1518           owned alias = Map.fromList
   1519             [(drop (length alias + 1) name, tree) | (name, tree) <- Map.toList exports,
   1520               (alias ++ ".") `isPrefixOf` name]
   1521       owned "prelude" @?= Map.unions (map owned ["base", "bytes", "conversions"])
   1522   , testCase "native base effect interpreter and guard use the rebuilt kernels" $ do
   1523       env <- pure (fixtureExports "base")
   1524       let pureValue value = Fork (ofNumber 0) value
   1525           success value rest = Fork (Stem Leaf) (Fork value rest)
   1526           failure value = Fork Leaf (Fork value Leaf)
   1527           value = ofNumber 42
   1528           exception = Fork (ofNumber 99)
   1529             (Fork (ofString "wanted") (Fork value Leaf))
   1530           handler = apply (env Map.! "const")
   1531             (apply (env Map.! "const") (pureValue (ofNumber 9)))
   1532           handled = foldl apply (env Map.! "handleM") [ofString "wanted", handler, exception]
   1533           predicate = apply (env Map.! "const") (Fork Leaf Leaf)
   1534       apply (env Map.! "runM") (pureValue value) @?= success value Leaf
   1535       apply (env Map.! "runM") exception @?= failure value
   1536       apply (env Map.! "runM") (Stem Leaf) @?= Stem Leaf
   1537       handled @?= pureValue (ofNumber 9)
   1538       foldl apply (env Map.! "guardC") [ofString "bad", predicate, value, ofNumber 7]
   1539         @?= success value (ofNumber 7)
   1540   , testCase "mixed support reuse requires all provider trees, including unused ones" $ do
   1541       let before = Map.fromList [("P.used",1),("P.unused",2)]
   1542           source = "!import P as X\n!export f\nf x = X.used x\n"
   1543       verifyReuseProviders before before source @?= Right ()
   1544       forM_ ["P.used", "P.unused"] $ \name ->
   1545         verifyReuseProviders before (Map.insert name 3 before) source @?=
   1546           Left "provider kernels changed: P"
   1547       forM_ [Map.insert "P.extra" 4 before, Map.delete "P.unused" before, Map.empty] $ \after ->
   1548         verifyReuseProviders before after source @?= Left "full export set changed: P"
   1549       verifyReuseProviders Map.empty before source @?= Left "missing original provider: P"
   1550   , testCase "mixed support reuse no longer exempts shadowed imports" $ do
   1551       let before = Map.fromList [("P.used",1),("P.unused",2)]
   1552           shadowed = "!import P as X\n!export f\nf used = used\n"
   1553       verifyReuseProviders before before shadowed @?= Right ()
   1554       verifyReuseProviders before (Map.insert "P.used" 4 before) shadowed @?=
   1555         Left "provider kernels changed: P"
   1556   , testCase "mixed support reuse checks standalone reexports and all import forms" $ do
   1557       let before = Map.fromList [("P.used",1),("P.unused",2),("PQ.other",3)]
   1558       forM_ ["!import P", "!import P *", "!import P as X", "!export * from P",
   1559              "  !import P *", "!import P *\n!export * from P"] $ \header -> do
   1560         let source = header ++ "\n!export f\nf x = x\n"
   1561         verifyReuseProviders before before source @?= Right ()
   1562         verifyReuseProviders before (Map.insert "PQ.other" 4 before) source @?= Right ()
   1563         verifyReuseProviders before (Map.insert "P.used" 4 before) source @?=
   1564           Left "provider kernels changed: P"
   1565       verifyReuseProviders before before "!export * from Missing\n" @?=
   1566         Left "missing original provider: Missing"
   1567   , testCase "mixed support reuse rejects changed namespace collision outcomes" $ do
   1568       let before = Map.fromList [("P.used",1),("Q.used",1)]
   1569           source = "!import P *\n!import Q *\n!export f\nf x = x\n"
   1570       verifyReuseProviders before before source @?= Right ()
   1571       verifyReuseProviders before (Map.insert "Q.used" 2 before) source @?=
   1572         Left "provider kernels changed: Q"
   1573   , testCase "mixed support reuse rejects unsupported dependency directives" $ do
   1574       let before = Map.singleton "P.used" 1
   1575       forM_ ["!import", "!import P as", "!import P * extra", "!import P as *",
   1576              "!import \"P\" *", "!export * from", "!export * from P extra",
   1577              "!source P", "!unknown P", "!import " ++ replicate 64 'a' ++ " *"] $ \header ->
   1578         assertBool ("accepted unsupported reuse header: " ++ header)
   1579           (case verifyReuseProviders before before (header ++ "\n") of Left _ -> True; _ -> False)
   1580   , testCase "mixed support reuse compares canonical trees, not bundle-local node indices" $ do
   1581       let original = buildBundle [("P.used", Stem Leaf), ("P.unused", Fork Leaf Leaf)]
   1582           reordered = buildBundle [("Q.extra", ofNumber 42), ("P.unused", Fork Leaf Leaf),
   1583                                    ("P.used", Stem Leaf)]
   1584           changed = buildBundle [("P.used", Stem Leaf), ("P.unused", Fork Leaf (Stem Leaf))]
   1585       forM_ [original, reordered, changed] $ \bundle -> verifyBundle bundle @?= Right ()
   1586       case canonicalEnvs [original, reordered, changed] of
   1587         [before, same, different] -> do
   1588           verifyReuseProviders before same "!import P *\n" @?= Right ()
   1589           verifyReuseProviders before different "!import P *\n" @?=
   1590             Left "provider kernels changed: P"
   1591         _ -> assertFailure "incorrect canonical environment count"
   1592   , testCase "mixed support reuse permits provider-free unchanged programs" $
   1593       verifyReuseProviders Map.empty Map.empty "-- no dependencies\n!export main\nmain = t\n" @?= Right ()
   1594   , testCase "support publication allows new rebuilt sources, not relabeled retained sources" $ do
   1595       let old = [["module","http","http","lib/http.tri","old-source"]]
   1596           new = [["module","http","http","lib/http.tri","new-source"]]
   1597       verifySupportSources old new (Set.singleton "http") @?= Right ()
   1598       verifySupportSources old old Set.empty @?= Right ()
   1599       verifySupportSources old new Set.empty @?=
   1600         Left "retained source fingerprint changed: http"
   1601       verifySupportSources old [["module","other","http","lib/http.tri","new-source"]]
   1602         (Set.singleton "http") @?= Left "support cohort identity changed"
   1603   , testCase "compiler-to-support relocation requires exact identity and a rebuild" $ do
   1604       let old = [["module","http","http","lib/http.tri","old-source"]]
   1605           added = ["module","patterns","patterns","lib/patterns.tri","new-source"]
   1606           declared = ["relocated","compiler-library","patterns","patterns","lib/patterns.tri"]
   1607           current = old ++ [added, declared]
   1608       verifyRelocatedSupportSources old current (Set.singleton "patterns") @?= Right ()
   1609       forM_ [old ++ [added], old ++ [added, declared, declared],
   1610              old ++ [added, declared, ["module","extra","extra","lib/extra.tri","pin"]],
   1611              old ++ [["module","patterns","patterns","lib/other.tri","pin"], declared]] $ \bad ->
   1612         assertBool "unauthorized relocation accepted"
   1613           (case verifyRelocatedSupportSources old bad (Set.singleton "patterns") of Left _ -> True; _ -> False)
   1614       verifyRelocatedSupportSources old current Set.empty @?= Left "invalid patterns cohort relocation"
   1615       verifySupportSources old current (Set.singleton "patterns") @?= Left "support cohort identity changed"
   1616   , testCase "IO support additions require declarations, exact membership and rebuilds" $ do
   1617       original <- map words . lines <$> readFile "test/fixtures/support-origin.v1"
   1618       current <- map words . lines <$> readFile "test/fixtures/support-cohort-v4.v1"
   1619       let built = Set.fromList [name | ["rebuilt",name,_,_,_] <- current]
   1620           declarations = [row | row <- current, take 1 row == ["added"]]
   1621       length declarations @?= 10
   1622       verifyIOSupportSources original current built @?= Right ()
   1623       forM_ [filter ((/= ["added"]) . take 1) current,
   1624              current ++ [head declarations],
   1625              current ++ [["module","unexpected","unexpected","lib/unexpected.tri","pin"]],
   1626              map (\row -> if take 3 row == ["module","io_v2","io_v2"]
   1627                        then ["module","io_v2","io_v2","lib/wrong.tri","pin"] else row) current] $ \bad ->
   1628         assertBool "unauthorized support growth accepted"
   1629           (case verifyIOSupportSources original bad built of Left _ -> True; _ -> False)
   1630       assertBool "unrebuilt IO addition accepted"
   1631         (case verifyIOSupportSources original current (Set.delete "io_v2" built) of Left _ -> True; _ -> False)
   1632   , testCase "compiler IO cohort has exact disjoint ownership and rebuilt support" $ do
   1633       original <- map words . lines <$> readFile "test/fixtures/support-origin.v1"
   1634       old <- map words . lines <$> readFile "test/fixtures/support-cohort-v4.v1"
   1635       let moved = [("io_v2","lib/io-v2.tri"), ("io_v2_storage","lib/io-v2/storage.tri"),
   1636                    ("io_v2_text","lib/io-v2/text.tri")]
   1637           names = map fst moved
   1638           keep row = case row of
   1639             ["module",_,name,_,_] -> name `notElem` names
   1640             ["added",name,_,_] -> name `notElem` names
   1641             _ -> True
   1642           ioRow = ["module","io","io","lib/io.tri","source"]
   1643           current = filter keep old ++ [ioRow, ["added","io","io","lib/io.tri"]] ++
   1644             [["compiler-provider",name,path] | (name,path) <- moved]
   1645           compiler = [["module",name,name,path,"source"] |
   1646             (name,path) <- moved ++ [("compiler_io","lib/source/io-v2.tri")]]
   1647           built = Set.fromList [name | ["module",_,name,_,_] <- current]
   1648           check c rows b = verifyCompilerIOSupportSources c original rows b
   1649       check compiler current built @?= Right ()
   1650       forM_ [tail compiler, compiler ++ [ioRow], compiler ++ [head compiler],
   1651              map (\row -> if take 3 row == ["module","io_v2","io_v2"]
   1652                then ["module","io_v2","io_v2","lib/wrong.tri","source"] else row) compiler] $ \bad ->
   1653         assertBool "incorrect compiler ownership accepted" (case check bad current built of Left _ -> True; _ -> False)
   1654       forM_ [filter ((/= ["compiler-provider"]) . take 1) current,
   1655              current ++ [["compiler-provider","io_v2","lib/io-v2.tri"]],
   1656              current ++ [["module","io_v2","io_v2","lib/io-v2.tri","source"]],
   1657              filter (/= ioRow) current] $ \bad ->
   1658         assertBool "incorrect support ownership accepted" (case check compiler bad built of Left _ -> True; _ -> False)
   1659       assertBool "unrebuilt general IO accepted"
   1660         (case check compiler current (Set.delete "io" built) of Left _ -> True; _ -> False)
   1661   , testCase "canonical IO ownership rejects extra providers and incomplete rebuilds" $ do
   1662       original <- map words . lines <$> readFile "test/fixtures/support-origin.v1"
   1663       let moved = [("io","lib/io.tri"), ("io_storage","lib/io/storage.tri"), ("io_text","lib/io/text.tri")]
   1664           additions = [("arboricx_handlers","lib/arboricx/handlers.tri"),
   1665             ("arboricx_streaming","lib/arboricx/streaming.tri"),
   1666             ("handler_artifacts","lib/handlers/artifacts.tri"), ("handler_log","lib/handlers/log.tri"),
   1667             ("handlers","lib/handlers.tri"), ("io_artifacts","lib/io/artifacts.tri"),
   1668             ("io_http","lib/io/http-transport.tri")]
   1669           modules = [["module",name,name,path,"source"] | (name,path) <- additions]
   1670           patternRow = ["module","patterns","patterns","lib/patterns.tri","source"]
   1671           current = original ++ [patternRow, ["relocated","compiler-library","patterns","patterns","lib/patterns.tri"]] ++
   1672             modules ++ [["added",name,name,path] | (name,path) <- additions] ++
   1673             [["compiler-provider",name,path] | (name,path) <- moved]
   1674           compiler = [["module",name,name,path,"source"] | (name,path) <- moved ++ [("compiler_io","lib/source/io.tri")]]
   1675           built = Set.fromList ("patterns":map fst additions)
   1676           check = verifyCanonicalIOSupportSources
   1677       check compiler original current built @?= Right ()
   1678       forM_ [tail compiler, compiler ++ [head compiler]] $ \bad ->
   1679         assertBool "incorrect canonical compiler ownership accepted"
   1680           (case check bad original current built of Left _ -> True; _ -> False)
   1681       forM_ [current ++ [["module","io","io","lib/io.tri","source"]],
   1682              current ++ [["compiler-provider","extra","lib/extra.tri"]],
   1683              filter ((/= ["added"]) . take 1) current] $ \bad ->
   1684         assertBool "incorrect canonical support ownership accepted"
   1685           (case check compiler original bad built of Left _ -> True; _ -> False)
   1686       assertBool "unrebuilt canonical addition accepted"
   1687         (case check compiler original current (Set.delete "io_artifacts" built) of Left _ -> True; _ -> False)
   1688   , testCase "binary64 cohort requires exact identities, ownership and complete rebuilds" $ do
   1689       original <- map words . lines <$> readFile "test/fixtures/support-origin.v1"
   1690       current <- map words . lines <$> readFile "test/fixtures/support-library.v1"
   1691       compiler <- map words . lines <$> readFile "test/fixtures/compiler-library.v1"
   1692       let names = "binary64":"demos_binary64":map ("binary64_" ++)
   1693             ["adders","chains","fast","finite","ieee","kernels","layouts",
   1694              "near","product_quotient","quantize","working"]
   1695           built = Set.fromList [name | ["rebuilt",name,_,_,_] <- current]
   1696           check = verifyBinary64SupportSources compiler original
   1697           rejects rows b = assertBool "invalid binary64 cohort accepted"
   1698             (case check rows b of Left _ -> True; _ -> False)
   1699       check current built @?= Right ()
   1700       forM_ names $ \name -> do
   1701         let owns ["module",_,alias,_,_] = alias == name
   1702             owns _ = False
   1703             declares ["added",_,alias,_] = alias == name
   1704             declares _ = False
   1705             rows = filter owns current
   1706         rejects (filter (not . owns) current) built
   1707         rejects (filter (not . declares) current) built
   1708         rejects (current ++ rows) built
   1709         rejects current (Set.delete name built)
   1710         rejects (map (\row -> if owns row then take 3 row ++ ["lib/wrong.tri","source"] else row) current) built
   1711         assertBool "duplicate compiler ownership accepted"
   1712           (case verifyBinary64SupportSources (compiler ++ rows) original current built of Left _ -> True; _ -> False)
   1713       rejects (current ++ [["module","extra","extra","lib/extra.tri","source"]]) built
   1714       assertBool "historical V6 validator accepted V7 membership"
   1715         (case verifyCanonicalIOSupportSources compiler original current built of Left _ -> True; _ -> False)
   1716   , testCase "rebuilt export sets follow the new manifest, not the old interface" $ do
   1717       let manifest kind names = unlines ("arboricx.module-manifest.v1" :
   1718             ["export " ++ name ++ " " ++ kind ++ " hash arboricx.abi.tree.v1" | name <- names])
   1719           names = Set.fromList ["parseHeaders", "httpAsciiBit"]
   1720       forM_ ["arboricx.tree-term.v1", "arboricx.indexed-tree.v1"] $ \kind -> do
   1721         verifySupportExports names (manifest kind (Set.toList names)) @?= Right ()
   1722         forM_ [["parseHeaders"], ["parseHeaders", "httpAsciiBit", "byte7BitsOr"],
   1723                ["parseHeaders", "httpAsciiBit", "httpAsciiBit"]] $ \bad ->
   1724           verifySupportExports names (manifest kind bad) @?= Left "rebuilt manifest export set mismatch"
   1725       verifySupportExports names (manifest "unknown-kind" (Set.toList names))
   1726         @?= Left "rebuilt manifest export set mismatch"
   1727       verifySupportExports names (unlines ["arboricx.module-manifest.v1",
   1728         "export parseHeaders arboricx.tree-term.v1 hash arboricx.abi.tree.v1",
   1729         "export httpAsciiBit arboricx.indexed-tree.v1 hash arboricx.abi.tree.v1"])
   1730         @?= Left "rebuilt manifest export set mismatch"
   1731   , testCase "rebuilt consumers reject stale support provider manifests" $ do
   1732       let providers = Map.singleton "http" "new-manifest"
   1733           rows pin = [["local","http",pin,"facade-source"],
   1734                       ["pin","http",pin,"parseHeaders","tree"]]
   1735       verifySupportDependencies providers (rows "new-manifest") @?= Right ()
   1736       verifySupportDependencies providers (rows "old-manifest") @?=
   1737         Left "stale support dependency: http"
   1738       verifySupportDependencies providers
   1739         [["local","http","new-manifest","source"], ["pin","http","old-manifest","f","tree"]]
   1740         @?= Left "stale support dependency: http"
   1741       verifySupportDependencies providers
   1742         (replicate 2 ["local","http","new-manifest","source"]) @?= Left "duplicate local dependency"
   1743   , testCase "native staging preserves namespaces and explicit exports" $ do
   1744       env <- pure (fixtureExports "source.map")
   1745       let row = pairT (ofString "example") (pairT (ofString "example")
   1746                   (pairT (ofString "example.tri")
   1747                     (pairT (ofList [ofString "native_math"]) (ofString "native"))))
   1748           source = ofString "!import native_math as N\n!export main\nmain = N.add 2 3\n"
   1749           prepare text = apply (apply (apply (env Map.! "sourceMapPrepareBody") row) text) Leaf
   1750       prepare source @?= pairT (Stem Leaf) (pairT source Leaf)
   1751       apply (apply (env Map.! "sourceMapOverlay") row) source @?= source
   1752       forM_ ["", "!source other\n", "!source extra\n"] $ \bad ->
   1753         apply (fixtureExports "base" Map.! "fst") (prepare (ofString bad)) @?= Leaf
   1754   , testCase "shared native header derives ordered deduplicated edges and masks positions" $ do
   1755       env <- pure (fixtureExports "source.module")
   1756       let header = "-- lead\r\n!import math\n!export * from base\n!import math as M\n!source build_only\n!export main\n"
   1757           body = "main = missing\n"
   1758           masked = map (\c -> if c == '\n' || c == '\r' then c else ' ') header ++ body
   1759           wordsT = ofList . map ofString
   1760           staged = pairT (wordsT ["math", "base", "build_only"])
   1761             (pairT (ofList [wordsT ["math"], wordsT ["math", "as", "M"]])
   1762               (pairT (ofList [wordsT ["*", "from", "base"], wordsT ["main"]]) (ofString masked)))
   1763       apply (env Map.! "sourceModuleHeaderStage") (ofString (header ++ body)) @?=
   1764         pairT (Stem Leaf) (pairT staged Leaf)
   1765   , testCase "native header rejects redundant edges and bare hashes at original locations" $ do
   1766       env <- pure (fixtureExports "source.module")
   1767       let bad sources = forM_ sources $ \source ->
   1768             apply (fixtureExports "base" Map.! "fst") (apply (env Map.! "sourceModuleHeaderStage") (ofString source)) @?= Leaf
   1769       bad ["!source dep\n!import dep\n", "!import dep\n!source dep\n",
   1770            "!source dep\n!export * from dep\n", "!source dep\n!source dep\n",
   1771            "!import " ++ replicate 64 'a' ++ "\n", "!use dep *\n", "!import \"dep\" !Local\n"]
   1772       let outcome = apply (env Map.! "sourceModuleHeaderStage")
   1773             (ofString "-- lead\n\n!use dep *\n")
   1774           problem = apply (fixtureExports "base" Map.! "fst") (apply (fixtureExports "base" Map.! "snd") outcome)
   1775       apply (fixtureExports "parser" Map.! "parserDiagnosticLocation") problem @?= gatePosition 3 1 9
   1776   , testCase "native math exports are public only and general equality remains" $ do
   1777       env <- pure (fixtureExports "math.native")
   1778       assertBool "missing math decimal" (Map.member "decimal" env)
   1779       let owned = [name | name <- Map.keys (snd compilerFixture), "native_math." `isPrefixOf` name]
   1780       length owned @?= 27
   1781       forM_ ["divMod", "quotient", "remainder", "power"] $ \name ->
   1782         assertBool ("missing native arithmetic export: " ++ name) (Map.member name env)
   1783       assertBool "private math helper exported" (not ("native_math.compareStep" `elem` owned))
   1784       prelude <- pure (fixtureExports "prelude")
   1785       assertBool "general equality removed" (Map.member "equal?" prelude)
   1786   ]
   1787 
   1788 gateParserEnv :: Env
   1789 gateParserEnv = fixtureExports "source.parser"
   1790 {-# NOINLINE gateParserEnv #-}
   1791 
   1792 gateAst :: Integer -> T -> T -> T -> T
   1793 gateAst kind payload start end =
   1794   pairT (ofNumber kind) (pairT payload (pairT start end))
   1795 
   1796 gateParserTests :: TestTree
   1797 gateParserTests = testGroup "Feasibility-gate source parser"
   1798   [ testCase "self-built parser export executes through its wrapper" $ do
   1799       env <- evaluateCompilerFile "./test/sourceGateParserImport.tri"
   1800       Map.lookup "main" env @?= Just
   1801         (portableReply True
   1802           (gateAst 1 Leaf (gatePosition 1 1 0) (gatePosition 1 2 1))
   1803           Leaf True False)
   1804   , testCase "leaf retains its source span" $
   1805       check "t" (portableReply True
   1806         (gateAst 1 Leaf (gatePosition 1 1 0) (gatePosition 1 2 1))
   1807         Leaf True False)
   1808   , testCase "decimal integer is a located value" $
   1809       check "007" (portableReply True
   1810         (gateAst 6 (ofNumber 7) (gatePosition 1 1 0) (gatePosition 1 4 3))
   1811         Leaf True False)
   1812   , testCase "string is a located decoded value" $
   1813       check "\"hi\"" (portableReply True
   1814         (gateAst 7 (ofString "hi") (gatePosition 1 1 0) (gatePosition 1 5 4))
   1815         Leaf True False)
   1816   , testCase "empty list retains its brackets and canonical empty payload" $
   1817       check "[]" (portableReply True
   1818         (gateAst 8 Leaf (gatePosition 1 1 0) (gatePosition 1 3 2))
   1819         Leaf True False)
   1820   , testCase "list elements are separate atoms, not an application" $
   1821       let leaf1 = gateAst 1 Leaf (gatePosition 1 2 1) (gatePosition 1 3 2)
   1822           leaf2 = gateAst 1 Leaf (gatePosition 1 4 3) (gatePosition 1 5 4)
   1823       in check "[t t]" (portableReply True
   1824         (gateAst 8 (ofList [leaf1, leaf2])
   1825           (gatePosition 1 1 0) (gatePosition 1 6 5)) Leaf True False)
   1826   , testCase "nested lists and grouped applications retain distinct spans" $
   1827       let innerLeaf = gateAst 1 Leaf
   1828             (gatePosition 1 3 2) (gatePosition 1 4 3)
   1829           innerList = gateAst 8 (ofList [innerLeaf])
   1830             (gatePosition 1 2 1) (gatePosition 1 5 4)
   1831           left = gateAst 1 Leaf (gatePosition 1 7 6) (gatePosition 1 8 7)
   1832           right = gateAst 1 Leaf (gatePosition 1 9 8) (gatePosition 1 10 9)
   1833           app = gateAst 3 (pairT left right)
   1834             (gatePosition 1 7 6) (gatePosition 1 10 9)
   1835           group = gateAst 5 app (gatePosition 1 6 5) (gatePosition 1 11 10)
   1836       in check "[[t] (t t)]" (portableReply True
   1837         (gateAst 8 (ofList [innerList, group])
   1838           (gatePosition 1 1 0) (gatePosition 1 12 11)) Leaf True False)
   1839   , testCase "unclosed list reports the expected closing bracket" $
   1840       check "[t" (portableReply False
   1841         (portableDiagnostic (ofNumber 2) (ofNumber 10) Leaf Leaf Leaf)
   1842         Leaf True False)
   1843   , testCase "unexpected separator retains its token and location" $
   1844       check "[t : t]" (portableReply False
   1845         (portableDiagnostic (ofNumber 2) (ofNumber 10) (ofNumber 5)
   1846           (gatePosition 1 4 3) Leaf)
   1847         (ofList
   1848           [ gateToken 5 Leaf (gatePosition 1 4 3) (gatePosition 1 5 4)
   1849           , gateToken 1 (ofString "t")
   1850               (gatePosition 1 6 5) (gatePosition 1 7 6)
   1851           , gateToken 10 Leaf (gatePosition 1 7 6) (gatePosition 1 8 7)
   1852           ]) True False)
   1853   , testCase "application is left associative" $
   1854       let name = gateAst 2 (ofString "f") (gatePosition 1 1 0) (gatePosition 1 2 1)
   1855           arg1 = gateAst 1 Leaf (gatePosition 1 3 2) (gatePosition 1 4 3)
   1856           arg2 = gateAst 1 Leaf (gatePosition 1 5 4) (gatePosition 1 6 5)
   1857           first = gateAst 3 (pairT name arg1) (gatePosition 1 1 0) (gatePosition 1 4 3)
   1858       in check "f t t" (portableReply True
   1859         (gateAst 3 (pairT first arg2) (gatePosition 1 1 0) (gatePosition 1 6 5))
   1860         Leaf True False)
   1861   , testCase "let retains binding, value, body, and complete span" $
   1862       let name = gateToken 2 (ofString "x")
   1863             (gatePosition 1 5 4) (gatePosition 1 6 5)
   1864           value = gateAst 1 Leaf (gatePosition 1 9 8) (gatePosition 1 10 9)
   1865           body = gateAst 2 (ofString "x")
   1866             (gatePosition 1 14 13) (gatePosition 1 15 14)
   1867       in check "let x = t in x" (portableReply True
   1868         (gateAst 9 (pairT name (pairT value body))
   1869           (gatePosition 1 1 0) (gatePosition 1 15 14)) Leaf True False)
   1870   , testCase "where retains the span of the whole expression" $
   1871       let name = gateToken 2 (ofString "x")
   1872             (gatePosition 1 9 8) (gatePosition 1 10 9)
   1873           value = gateAst 1 Leaf (gatePosition 1 13 12) (gatePosition 1 14 13)
   1874           body = gateAst 2 (ofString "x")
   1875             (gatePosition 1 1 0) (gatePosition 1 2 1)
   1876       in check "x where x = t" (portableReply True
   1877         (gateAst 9 (pairT name (pairT value body))
   1878           (gatePosition 1 1 0) (gatePosition 1 14 13)) Leaf True False)
   1879   , testCase "a top-level newline does not silently join application operands" $
   1880       check "t\n t" (portableReply False
   1881         (portableDiagnostic (ofNumber 2) Leaf (ofNumber 6)
   1882           (gatePosition 1 2 1) Leaf)
   1883         (ofList
   1884           [ gateToken 6 Leaf
   1885               (gatePosition 1 2 1) (gatePosition 2 1 2)
   1886           , gateToken 1 (ofString "t")
   1887               (gatePosition 2 2 3) (gatePosition 2 3 4)
   1888           ]) True False)
   1889   , testCase "parentheses permit multiline application with original spans" $
   1890       let left = gateAst 1 Leaf
   1891             (gatePosition 1 2 1) (gatePosition 1 3 2)
   1892           right = gateAst 1 Leaf
   1893             (gatePosition 2 2 4) (gatePosition 2 3 5)
   1894           application = gateAst 3 (pairT left right)
   1895             (gatePosition 1 2 1) (gatePosition 2 3 5)
   1896       in check "(t\n t)" (portableReply True
   1897         (gateAst 5 application
   1898           (gatePosition 1 1 0) (gatePosition 2 4 6)) Leaf True False)
   1899   , testCase "pipe retains source order and direction before elaboration" $
   1900       let value = gateAst 6 (ofNumber 2)
   1901             (gatePosition 1 1 0) (gatePosition 1 2 1)
   1902           name = gateAst 2 (ofString "f")
   1903             (gatePosition 1 6 5) (gatePosition 1 7 6)
   1904       in check "2 |> f" (portableReply True
   1905         (gateAst 10 (pairT (ofNumber 16) (pairT value name))
   1906           (gatePosition 1 1 0) (gatePosition 1 7 6)) Leaf True False)
   1907   , testCase "do retains operator, located binders, and statements" $
   1908       let op = gateAst 2 (ofString "b")
   1909             (gatePosition 1 4 3) (gatePosition 1 5 4)
   1910           name = gateToken 2 (ofString "x")
   1911             (gatePosition 2 3 7) (gatePosition 2 4 8)
   1912           action = gateAst 6 (ofNumber 2)
   1913             (gatePosition 2 8 12) (gatePosition 2 9 13)
   1914           body = gateAst 2 (ofString "x")
   1915             (gatePosition 3 3 16) (gatePosition 3 4 17)
   1916       in check "do b\n  x <- 2\n  x" (portableReply True
   1917         (gateAst 11 (pairT op (ofList
   1918           [pairT (Stem name) action, pairT Leaf body]))
   1919           (gatePosition 1 1 0) (gatePosition 3 4 17)) Leaf True False)
   1920   , testCase "single-argument lambda preserves binder and body" $
   1921       let body = gateAst 2 (ofString "x") (gatePosition 1 5 4) (gatePosition 1 6 5)
   1922       in check "x : x" (portableReply True
   1923         (gateAst 4 (pairT (ofString "x") body)
   1924           (gatePosition 1 1 0) (gatePosition 1 6 5)) Leaf True False)
   1925   , testCase "multiple same-line lambda binders elaborate in source order" $
   1926       let body = gateAst 2 (ofString "a")
   1927             (gatePosition 1 7 6) (gatePosition 1 8 7)
   1928           inner = gateAst 4 (pairT (ofString "b") body)
   1929             (gatePosition 1 3 2) (gatePosition 1 8 7)
   1930       in check "a b : a" (portableReply True
   1931         (gateAst 4 (pairT (ofString "a") inner)
   1932           (gatePosition 1 1 0) (gatePosition 1 8 7)) Leaf True False)
   1933   , testCase "lambda body may start on a later line" $
   1934       let body = gateAst 2 (ofString "x")
   1935             (gatePosition 2 3 6) (gatePosition 2 4 7)
   1936       in check "x :\n  x" (portableReply True
   1937         (gateAst 4 (pairT (ofString "x") body)
   1938           (gatePosition 1 1 0) (gatePosition 2 4 7)) Leaf True False)
   1939   , testCase "nested lambdas associate to the right" $
   1940       let body = gateAst 2 (ofString "x") (gatePosition 1 9 8) (gatePosition 1 10 9)
   1941           inner = gateAst 4 (pairT (ofString "y") body)
   1942             (gatePosition 1 5 4) (gatePosition 1 10 9)
   1943       in check "x : y : x" (portableReply True
   1944         (gateAst 4 (pairT (ofString "x") inner)
   1945           (gatePosition 1 1 0) (gatePosition 1 10 9)) Leaf True False)
   1946   , testCase "grouping preserves its outer span" $
   1947       let leaf = gateAst 1 Leaf (gatePosition 1 2 1) (gatePosition 1 3 2)
   1948           group = gateAst 5 leaf (gatePosition 1 1 0) (gatePosition 1 4 3)
   1949           arg = gateAst 1 Leaf (gatePosition 1 5 4) (gatePosition 1 6 5)
   1950       in check "(t) t" (portableReply True
   1951         (gateAst 3 (pairT group arg)
   1952           (gatePosition 1 1 0) (gatePosition 1 6 5)) Leaf True False)
   1953   , testCase "empty input reports expected atom" $
   1954       check "" (portableReply False
   1955         (portableDiagnostic (ofNumber 2) (ofNumber 7) Leaf Leaf Leaf)
   1956         Leaf False False)
   1957   , testCase "opened group fails after consumption" $
   1958       check "t (" (portableReply False
   1959         (portableDiagnostic (ofNumber 2) (ofNumber 7) Leaf Leaf Leaf)
   1960         Leaf True False)
   1961   , testCase "missing lambda body is a consumed parse failure" $
   1962       check "x : " (portableReply False
   1963         (portableDiagnostic (ofNumber 2) (ofNumber 7) Leaf Leaf Leaf)
   1964         Leaf True False)
   1965   , testCase "unmatched close paren retains its span" $
   1966       check "t)" (portableReply False
   1967         (portableDiagnostic (ofNumber 2) Leaf (ofNumber 4)
   1968           (gatePosition 1 2 1) Leaf)
   1969         (ofList [gateToken 4 Leaf (gatePosition 1 2 1) (gatePosition 1 3 2)])
   1970         True False)
   1971   , testCase "lexical error passes through the source parser" $
   1972       check "t #" (portableReply False
   1973         (portableDiagnostic (ofNumber 4) Leaf (ofNumber 35)
   1974           (gatePosition 1 3 2) Leaf)
   1975         (ofString "#") True False)
   1976   , testCase "newline does not turn into application whitespace" $
   1977       check "t\nfoo" (portableReply False
   1978         (portableDiagnostic (ofNumber 2) Leaf (ofNumber 6)
   1979           (gatePosition 1 2 1) Leaf)
   1980         (ofList
   1981           [ gateToken 6 Leaf (gatePosition 1 2 1) (gatePosition 2 1 2)
   1982           , gateToken 2 (ofString "foo") (gatePosition 2 1 2) (gatePosition 2 4 5)
   1983           ]) True False)
   1984   ]
   1985   where
   1986     check input expected =
   1987       apply (gateParserEnv Map.! "parseGateSource") (ofString input) @?= expected
   1988 
   1989 -- --------------------------------------------------------------------------
   1990 -- Program-level feasibility-gate parsing (definitions and final expression)
   1991 -- --------------------------------------------------------------------------
   1992 
   1993 equationHelperEnv :: Env
   1994 equationHelperEnv = fixtureExports "source.equations"
   1995 {-# NOINLINE equationHelperEnv #-}
   1996 
   1997 equationHelperTests :: TestTree
   1998 equationHelperTests = testGroup "Equation helper pattern adoption"
   1999   [ testCase "expected token consumes one cell and preserves suffix" $
   2000       expect 7 input @?= portableReply True token suffix True False
   2001   , testCase "wrong token preserves full input and original location" $
   2002       expect 2 input @?= portableReply False
   2003         (portableDiagnostic (ofNumber 2) (ofNumber 2) (ofNumber 7) start Leaf)
   2004         input False False
   2005   , testCase "empty token input has unchanged end diagnostic" $
   2006       expect 2 Leaf @?= endReply 2 Leaf
   2007   , testCase "non-list token input has unchanged end diagnostic" $
   2008       expect 2 (Stem Leaf) @?= endReply 2 (Stem Leaf)
   2009   , testCase "labelled error preserves token kind span and full remainder" $
   2010       labelled input @?= portableReply False
   2011         (portableDiagnostic (ofNumber 48) label (ofNumber 7) start Leaf)
   2012         input False False
   2013   , testCase "labelled empty error retains protocol flags" $
   2014       labelled Leaf @?= labelledEnd Leaf
   2015   , testCase "labelled non-list error retains original input" $
   2016       labelled (Stem Leaf) @?= labelledEnd (Stem Leaf)
   2017   , testCase "empty pattern prefix returns supplied terminator" $
   2018       list Leaf @?= nil
   2019   , testCase "non-list pattern prefix returns supplied terminator" $
   2020       list (Stem Leaf) @?= nil
   2021   , testCase "pattern prefix has independent nested payload and span golden" $
   2022       list (ofList [left, right]) @?= joined left (joined right nil)
   2023   , testCase "improper pattern suffix retains prior non-list termination" $
   2024       list (Fork left (Stem Leaf)) @?= joined left nil
   2025   , testCase "empty pattern list retains an opaque supplied tail" $
   2026       apply (fixtureExports "base" Map.! "fst")
   2027         (call "sourceEquationList" [Leaf, Fork (ofNumber 23)
   2028           (error "unused suffix demanded")]) @?= ofNumber 23
   2029   ]
   2030   where
   2031     call name args = foldl apply (equationHelperEnv Map.! name) args
   2032     start = gatePosition 2 3 5
   2033     end = gatePosition 2 4 6
   2034     token = gateToken 7 (ofNumber 23) start end
   2035     suffix = ofList [Leaf]
   2036     input = Fork token suffix
   2037     label = ofString "pattern"
   2038     expect kind value = call "sourceEquationExpect" [ofNumber kind, value]
   2039     labelled value = call "sourceEquationError" [label, value]
   2040     endReply kind value = portableReply False
   2041       (portableDiagnostic (ofNumber 2) (ofNumber kind) Leaf Leaf Leaf)
   2042       value False False
   2043     labelledEnd value = portableReply False
   2044       (portableDiagnostic (ofNumber 48) label Leaf Leaf Leaf) value False False
   2045     patternNode tag payload a b = gateAst 13 (pairT (ofNumber tag) payload) a b
   2046     nil = patternNode 1 Leaf (gatePosition 2 7 9) (gatePosition 2 8 10)
   2047     left = patternNode 4 (ofString "x") start end
   2048     right = patternNode 4 (ofString "y") (gatePosition 2 5 7) (gatePosition 2 6 8)
   2049     joined a b = patternNode 3 (pairT a b)
   2050       (case a of Fork _ (Fork _ (Fork x _)) -> x; _ -> error "invalid golden")
   2051       (gatePosition 2 8 10)
   2052     list value = call "sourceEquationList" [value, nil]
   2053 
   2054 gateProgramEnv :: Env
   2055 gateProgramEnv = fixtureExports "source.program"
   2056 {-# NOINLINE gateProgramEnv #-}
   2057 
   2058 gateProgramTests :: TestTree
   2059 gateProgramTests = testGroup "Feasibility-gate program parser"
   2060   [ testCase "portable wrapper normalizes the definition parser" $ do
   2061       env <- evaluateCompilerFile
   2062         "./test/sourceGateProgramImport.tri"
   2063       Map.lookup "main" env @?= Just programResult
   2064   , testCase "definition arguments elaborate to located lambdas" $ do
   2065       check "id x = x\nid t" programResult
   2066   , testCase "an identifier-led expression without assignment stays an expression" $
   2067       let name = gateAst 2 (ofString "f")
   2068             (gatePosition 1 1 0) (gatePosition 1 2 1)
   2069           leaf = gateAst 1 Leaf
   2070             (gatePosition 1 3 2) (gatePosition 1 4 3)
   2071           expression = gateAst 3 (pairT name leaf)
   2072             (gatePosition 1 1 0) (gatePosition 1 4 3)
   2073       in check "f t" (portableReply True
   2074            (pairT Leaf (Stem expression)) Leaf True False)
   2075   , testCase "unfinished definition fails after consuming the assignment" $
   2076       check "id x =" (portableReply False
   2077         (portableDiagnostic (ofNumber 2) (ofNumber 7) Leaf Leaf Leaf)
   2078         Leaf True False)
   2079   , testCase "an empty unit has no definitions or final expression" $
   2080       check "" (portableReply True (pairT Leaf Leaf) Leaf False False)
   2081   ]
   2082   where
   2083     programResult =
   2084       let name = gateToken 2 (ofString "id")
   2085             (gatePosition 1 1 0) (gatePosition 1 3 2)
   2086           bodyName = gateAst 2 (ofString "x")
   2087             (gatePosition 1 8 7) (gatePosition 1 9 8)
   2088           body = gateAst 4 (pairT (ofString "x") bodyName)
   2089             (gatePosition 1 4 3) (gatePosition 1 9 8)
   2090           expressionName = gateAst 2 (ofString "id")
   2091             (gatePosition 2 1 9) (gatePosition 2 3 11)
   2092           leaf = gateAst 1 Leaf
   2093             (gatePosition 2 4 12) (gatePosition 2 5 13)
   2094           expression = gateAst 3 (pairT expressionName leaf)
   2095             (gatePosition 2 1 9) (gatePosition 2 5 13)
   2096       in portableReply True
   2097            (pairT (ofList [pairT name body]) (Stem expression)) Leaf True False
   2098     check input expected =
   2099       apply (gateProgramEnv Map.! "parseGateProgram") (ofString input) @?= expected
   2100 
   2101 -- --------------------------------------------------------------------------
   2102 -- Portable, stable dependency ordering and cycle witnesses
   2103 -- --------------------------------------------------------------------------
   2104 
   2105 sourceGraphEnv :: Env
   2106 sourceGraphEnv = fixtureExports "graph"
   2107 {-# NOINLINE sourceGraphEnv #-}
   2108 
   2109 sourceGraphTests :: TestTree
   2110 sourceGraphTests = testGroup "Source dependency graph"
   2111   [ testCase "checked native graph sorts dependencies before dependents" $ do
   2112         env <- evaluateCompilerFile "./test/sourceGraphImport.tri"
   2113         Map.lookup "main" env @?= Just
   2114           (parserOk (ofList (map ofString ["leaf", "inner", "outer"])) Leaf)
   2115   , testCase "sort preserves source order among simultaneously ready nodes" $
   2116       check [("b", []), ("a", [])]
   2117         (parserOk (ofList [ofString "b", ofString "a"]) Leaf)
   2118   , testCase "cycle witness is in the cycle, not just a dependent" $
   2119       check [("outside", ["inside"]), ("inside", ["last"]), ("last", ["inside"])]
   2120         (parserErr (ofString "inside") Leaf)
   2121   ]
   2122   where
   2123     check entries expected =
   2124       apply (sourceGraphEnv Map.! "graphSort")
   2125         (ofList [Fork (ofString name) (ofList (map ofString dependencies))
   2126                 | (name, dependencies) <- entries]) @?= expected
   2127 
   2128 -- --------------------------------------------------------------------------
   2129 -- Program-level free names and dependency ordering
   2130 -- --------------------------------------------------------------------------
   2131 
   2132 gateOrderingEnv :: Env
   2133 gateOrderingEnv = fixtureExports "source.order"
   2134 {-# NOINLINE gateOrderingEnv #-}
   2135 
   2136 gateOrderingTests :: TestTree
   2137 gateOrderingTests = testGroup "Feasibility-gate definition ordering"
   2138   [ testCase "portable wrapper orders forward references" $ do
   2139       env <- evaluateCompilerFile
   2140         "./test/sourceGateOrderImport.tri"
   2141       Map.lookup "main" env @?= Just (parserOk
   2142         (ofList [ofString "f", ofString "main"]) Leaf)
   2143   , testCase "unused definitions still have their names checked" $
   2144       check Leaf "main = t\nbroken = u" (parserErr
   2145         (portableDiagnostic (ofNumber 20) (ofString "u") Leaf
   2146           (gatePosition 2 10 18) Leaf)
   2147         (gateAst 2 (ofString "u")
   2148           (gatePosition 2 10 18) (gatePosition 2 11 19)))
   2149   , testCase "lexical binder shadows a definition and external name" $
   2150       check (ofList [Fork (ofString "x") Leaf]) "x = t\nf x = x" (parserOk
   2151         (ofList [ofString "x", ofString "f"]) Leaf)
   2152   , testCase "top-level self reference returns a cycle witness" $
   2153       check Leaf "f = f" (parserErr
   2154         (portableDiagnostic (ofNumber 31) (ofString "acyclic")
   2155           (ofString "f") (gatePosition 1 1 0)
   2156           (ofList [ofString "f"])) Leaf)
   2157   , testCase "duplicate definition reports the later name location" $
   2158       let name = gateToken 2 (ofString "f")
   2159             (gatePosition 2 1 6) (gatePosition 2 2 7)
   2160           body = gateAst 1 Leaf
   2161             (gatePosition 2 5 10) (gatePosition 2 6 11)
   2162       in check Leaf "f = t\nf = t" (parserErr
   2163            (portableDiagnostic (ofNumber 30) (ofString "f")
   2164              (ofString "f") (gatePosition 2 1 6) Leaf)
   2165            (pairT name body))
   2166   , testCase "a final expression also checks unresolved names" $
   2167       check Leaf "f = t\nu" (parserErr
   2168         (portableDiagnostic (ofNumber 20) (ofString "u") Leaf
   2169           (gatePosition 2 1 6) Leaf)
   2170         (gateAst 2 (ofString "u")
   2171           (gatePosition 2 1 6) (gatePosition 2 2 7)))
   2172   ]
   2173   where
   2174     check environment source expected =
   2175       foldl apply (gateOrderingEnv Map.! "orderGateSource") [environment, ofString source]
   2176         @?= expected
   2177 
   2178 -- --------------------------------------------------------------------------
   2179 -- End-to-end program compilation through ordered definitions
   2180 -- --------------------------------------------------------------------------
   2181 
   2182 gateUnitEnv :: Env
   2183 gateUnitEnv = unsafePerformIO $ evaluateCompilerFile "./lib/source/unit.tri"
   2184 {-# NOINLINE gateUnitEnv #-}
   2185 
   2186 gateUnitTests :: TestTree
   2187 gateUnitTests = testGroup "Feasibility-gate program compiler"
   2188   [ testCase "real source files compile without rewriting or relexing" $ do
   2189       let corpus =
   2190             [ ("./test/fork.tri", [], Fork Leaf Leaf)
   2191             , ("./test/comments-1.tri", [], Fork (Stem Leaf) Leaf)
   2192             , ("./test/map.tri", ["map", "append", "equal?", "head"], trueT)
   2193             ]
   2194       forM_ corpus $ \(path, names, expected) -> do
   2195         source <- readFile path
   2196         let external = ofList [Fork (ofString name) (fixtureExports "base" Map.! name) | name <- names]
   2197         foldl apply (fixtureExports "source.unit" Map.! "compileGateProgram")
   2198           [external, ofString source] @?= parserOk expected Leaf
   2199   , testCase "portable size counts leaf, stem, and fork arguments" $ do
   2200       source <- readFile "./test/size.tri"
   2201       let definitions = unlines (init (lines source))
   2202           external = ofList [Fork (ofString name) (fixtureExports "base" Map.! name)
   2203                             | name <- ["y", "triage"]]
   2204       forM_ [("t", 1), ("t t", 2), ("t t t", 3)] $ \(argument, count) -> do
   2205         let input = definitions ++ "size (" ++ argument ++ ")\n"
   2206         foldl apply (fixtureExports "source.unit" Map.! "compileGateProgram")
   2207           [external, ofString input] @?= parserOk (ofNumber count) Leaf
   2208   , testCase "real file inputs match the natively packed self-built artifact" $
   2209       withSystemTempDirectory "gate-file-corpus" $ \dir -> do
   2210         let path = dir </> "file-matches.arboricx"
   2211             corpus =
   2212               [ ("./test/fork.tri", parserOk (Fork Leaf Leaf) Leaf)
   2213               , ("./test/comments-1.tri",
   2214                   parserOk (Fork (Stem Leaf) Leaf) Leaf)
   2215               , ("./test/map.tri", parserOk trueT Leaf)
   2216               , -- Optimized unit lowering with constructor-K-compiled y/triage.
   2217               -- Independently counted the emitted size tree on the host: 500
   2218               -- nodes (formerly 680 with the old support representations).
   2219               ("./test/size.tri", parserOk (ofNumber 500) Leaf)
   2220               ]
   2221         packCompilerFile
   2222           "./test/sourceGateFileCompiler.tri" path [pack "matchesFile"]
   2223         bytes <- BS.readFile path
   2224         case decodeBundle bytes of
   2225           Left diagnostic -> assertFailure diagnostic
   2226           Right bundle -> do
   2227             let artifact = reconstructBundleTerms (bundleNodes bundle)
   2228                   V.! fromIntegral (head (bundleRoots bundle))
   2229             forM_ corpus $ \(fixture, expected) -> do
   2230               source <- readFile fixture
   2231               apply (apply artifact (ofString source)) expected @?= trueT
   2232   , testCase "self-built local binding compiler executes through its wrapper" $ do
   2233       env <- evaluateCompilerFile
   2234         "./test/sourceGateLocalImport.tri"
   2235       Map.lookup "main" env @?= Just (parserOk Leaf Leaf)
   2236   , testCase "independent program goldens match the Haskell bundle" $
   2237       withSystemTempDirectory "gate-unit-goldens" $ \dir -> do
   2238         let path = dir </> "program-matches.arboricx"
   2239         packCompilerFile
   2240           "./test/sourceGateUnitCompiler.tri" path [pack "matchesExpected"]
   2241         bytes <- BS.readFile path
   2242         goldenEnv <- evaluateCompilerFile "./test/sourceGateExpected.tri"
   2243         let name = gateToken 2 (ofString "f")
   2244               (gatePosition 2 1 6) (gatePosition 2 2 7)
   2245             body = gateAst 1 Leaf
   2246               (gatePosition 2 5 10) (gatePosition 2 6 11)
   2247             cycleError = parserErr
   2248               (portableDiagnostic (ofNumber 31) (ofString "acyclic")
   2249                 (ofString "a") (gatePosition 1 1 0)
   2250                 (ofList [ofString "a"])) Leaf
   2251             cases =
   2252               [ ("main = id 42\nid x = x", "goldenFortyTwo",
   2253                   parserOk (ofNumber 42) Leaf)
   2254               , ("id x = x\nid 42", "goldenFortyTwo",
   2255                   parserOk (ofNumber 42) Leaf)
   2256               , ("main = 2\n3", "goldenTwo", parserOk (ofNumber 2) Leaf)
   2257               , ("main = [(id 2)]\nid x = x", "goldenProgramList",
   2258                   parserOk (ofList [ofNumber 2]) Leaf)
   2259               , ("helper = t", "goldenMissingRoot", parserErr
   2260                   (portableDiagnostic (ofNumber 32) (ofString "main")
   2261                     Leaf Leaf Leaf) Leaf)
   2262               , ("a = b\nb = a\nmain = t", "goldenProgramCycle", cycleError)
   2263               , ("f = t\nf = t", "goldenProgramDuplicate", parserErr
   2264                   (portableDiagnostic (ofNumber 30) (ofString "f")
   2265                     (ofString "f") (gatePosition 2 1 6) Leaf)
   2266                   (pairT name body))
   2267               , ("main = u", "goldenProgramUnknown", parserErr
   2268                   (portableDiagnostic (ofNumber 20) (ofString "u") Leaf
   2269                     (gatePosition 1 8 7) Leaf)
   2270                   (gateAst 2 (ofString "u")
   2271                     (gatePosition 1 8 7) (gatePosition 1 9 8)))
   2272               , ("unused = (x : x x) (x : x x)\nmain = t", "goldenLeaf",
   2273                   parserOk Leaf Leaf)
   2274               , ("main = let x = t in x", "goldenLeaf", parserOk Leaf Leaf)
   2275               , ("main = x where x = t", "goldenLeaf", parserOk Leaf Leaf)
   2276               , ("main = let id x = x in id 2", "goldenTwo",
   2277                   parserOk (ofNumber 2) Leaf)
   2278               , ("main = value where value = helper t\nhelper x = x",
   2279                   "goldenLeaf", parserOk Leaf Leaf)
   2280               , ("main = let x = x in x", "goldenLocalMissing", parserErr
   2281                   (portableDiagnostic (ofNumber 20) (ofString "x") Leaf
   2282                     (gatePosition 1 16 15) Leaf)
   2283                   (gateAst 2 (ofString "x")
   2284                     (gatePosition 1 16 15) (gatePosition 1 17 16)))
   2285               , ("main = let x = t x", "goldenLetMissingIn", parserErr
   2286                   (portableDiagnostic (ofNumber 2) (ofNumber 13)
   2287                     Leaf Leaf Leaf) Leaf)
   2288               , ("main = (t\n t)", "goldenMultilineGroup",
   2289                   parserOk (Stem Leaf) Leaf)
   2290               , ("main = [(t\n t)]", "goldenMultilineList",
   2291                   parserOk (ofList [Stem Leaf]) Leaf)
   2292               , ("main = (x\n where x = t)", "goldenLeaf",
   2293                   parserOk Leaf Leaf)
   2294               , ("main =\n  t", "goldenLeaf", parserOk Leaf Leaf)
   2295               , ("main =\n  t\n  t", "goldenMultilineGroup", parserOk (Stem Leaf) Leaf)
   2296               , ("main =\n  (x : x)\n    2", "goldenTwo",
   2297                   parserOk (ofNumber 2) Leaf)
   2298               , ("main =\n  id\n    t\nid x = x", "goldenLeaf",
   2299                   parserOk Leaf Leaf)
   2300               , ("main = value\nwhere value = t", "goldenLeaf",
   2301                   parserOk Leaf Leaf)
   2302               , ("main =\n  value\n  where value = t", "goldenLeaf",
   2303                   parserOk Leaf Leaf)
   2304               , ("main =\n", "goldenMissingDefinitionBody", parserErr
   2305                   (portableDiagnostic (ofNumber 40)
   2306                     (ofString "definition body") Leaf Leaf Leaf) Leaf)
   2307               , ("main = let x =\n  2\nin x", "goldenTwo",
   2308                   parserOk (ofNumber 2) Leaf)
   2309               , ("main = let x = t in\n  x", "goldenLeaf",
   2310                   parserOk Leaf Leaf)
   2311               , ("main = let\n  id x =\n    x\nin id 2", "goldenTwo",
   2312                   parserOk (ofNumber 2) Leaf)
   2313               , ("main =\n  let x =\n    t\n  in x", "goldenLeaf",
   2314                   parserOk Leaf Leaf)
   2315               , ("main = t where x =\n  2", "goldenLeaf",
   2316                   parserOk Leaf Leaf)
   2317               , ("main = id 2 where\n  id x =\n    x", "goldenTwo",
   2318                   parserOk (ofNumber 2) Leaf)
   2319               , ("main = let x =\n  (y : y)\n    2\nin x", "goldenTwo",
   2320                   parserOk (ofNumber 2) Leaf)
   2321               , ("main = let id x = x in\n  id\n    2", "goldenTwo",
   2322                   parserOk (ofNumber 2) Leaf)
   2323               , ("main = let x =\n", "goldenMissingLocalValue", parserErr
   2324                   (portableDiagnostic (ofNumber 40)
   2325                     (ofString "local binding value") Leaf Leaf Leaf) Leaf)
   2326               , ("main = let x = t in\n", "goldenMissingLocalBody", parserErr
   2327                   (portableDiagnostic (ofNumber 40)
   2328                     (ofString "local binding body") Leaf Leaf Leaf) Leaf)
   2329               , ("main = 2 |> (x : x)", "goldenTwo",
   2330                   parserOk (ofNumber 2) Leaf)
   2331               , ("main = (x : x) <| 2", "goldenTwo",
   2332                   parserOk (ofNumber 2) Leaf)
   2333               , ("main = 2 |> (x : x) |> (x : x)", "goldenTwo",
   2334                   parserOk (ofNumber 2) Leaf)
   2335               , ("main = t <| t t", "goldenPipeApplication",
   2336                   parserOk (Stem (Stem Leaf)) Leaf)
   2337               , ("main = 2 |> id\nid x = x", "goldenTwo",
   2338                   parserOk (ofNumber 2) Leaf)
   2339               , ("main = 2 |> id where id x = x", "goldenTwo",
   2340                   parserOk (ofNumber 2) Leaf)
   2341               , ("main = 2 |> missing", "goldenPipeUnknown", parserErr
   2342                   (portableDiagnostic (ofNumber 20) (ofString "missing")
   2343                     Leaf (gatePosition 1 13 12) Leaf)
   2344                   (gateAst 2 (ofString "missing")
   2345                     (gatePosition 1 13 12) (gatePosition 1 20 19)))
   2346               , ("main = 2 |>", "goldenPipeMissingOperand", parserErr
   2347                   (portableDiagnostic (ofNumber 2) (ofNumber 7)
   2348                     Leaf Leaf Leaf) Leaf)
   2349               , ("f = 2\nf |> let id x = x in id", "goldenTwo",
   2350                   parserOk (ofNumber 2) Leaf)
   2351               , ("main = 2\n  |> (x : x)", "goldenTwo",
   2352                   parserOk (ofNumber 2) Leaf)
   2353               , ("main = 2 |>\n  (x : x)", "goldenTwo",
   2354                   parserOk (ofNumber 2) Leaf)
   2355               , ("main = do bind\n  x <- 2\n  x\nbind a f = f a",
   2356                   "goldenTwo", parserOk (ofNumber 2) Leaf)
   2357               , ("main = do unused\n  2", "goldenTwo",
   2358                   parserOk (ofNumber 2) Leaf)
   2359               , ("main = do bind\n  1\n  2\nbind a f = f a",
   2360                   "goldenTwo", parserOk (ofNumber 2) Leaf)
   2361               , ("main = do bind\n  x <- 2\n  y <- x\n  y\nbind a f = f a",
   2362                   "goldenTwo", parserOk (ofNumber 2) Leaf)
   2363               , ("main = do bind\n  x <- 2\n  do bind\n    y <- x\n    y\nbind a f = f a",
   2364                   "goldenTwo", parserOk (ofNumber 2) Leaf)
   2365               , ("main = do bind\n  x <- (y : y)\n         2\n  x\nbind a f = f a",
   2366                   "goldenTwo", parserOk (ofNumber 2) Leaf)
   2367               , ("main = let _ = 42 in do bind\n  1\n  _\nbind a f = f a",
   2368                   "goldenOne", parserOk (ofNumber 1) Leaf)
   2369               , ("main = do bind\n  x <- 2", "goldenDoMissingFinal", parserErr
   2370                   (portableDiagnostic (ofNumber 40)
   2371                     (ofString "do final expression") Leaf
   2372                     (gatePosition 2 3 17) Leaf) Leaf)
   2373               , ("main = do bind\n  x <- missing\n  x\nbind a f = f a",
   2374                   "goldenDoUnboundAction", parserErr
   2375                     (portableDiagnostic (ofNumber 20) (ofString "missing")
   2376                       Leaf (gatePosition 2 8 22) Leaf)
   2377                     (gateAst 2 (ofString "missing")
   2378                       (gatePosition 2 8 22) (gatePosition 2 15 29)))
   2379               ]
   2380         case decodeBundle bytes of
   2381           Left diagnostic -> assertFailure diagnostic
   2382           Right bundle -> do
   2383             let artifact = reconstructBundleTerms (bundleNodes bundle)
   2384                   V.! fromIntegral (head (bundleRoots bundle))
   2385             forM_ cases $ \(source, goldenName, expected) -> do
   2386               Map.lookup goldenName goldenEnv @?= Just expected
   2387               assertBool ("program golden mismatch: " ++ goldenName
   2388                 ++ " for " ++ show source)
   2389                 (apply (apply artifact (ofString source)) expected == trueT)
   2390             case Map.lookup "goldenProgramCycleWrongPosition" goldenEnv of
   2391               Nothing -> assertFailure "missing program wrong-position control"
   2392               Just wrong ->
   2393                 apply (apply artifact (ofString "a = b\nb = a\nmain = t")) wrong
   2394                   @?= falseT
   2395   , testCase "output goldens match the portable Haskell bundle" $
   2396       withSystemTempDirectory "gate-unit-output-goldens" $ \dir -> do
   2397         let path = dir </> "output-matches.arboricx"
   2398         packCompilerFile
   2399           "./test/sourceGateUnitCompiler.tri" path [pack "matchesOutput"]
   2400         bytes <- BS.readFile path
   2401         goldenEnv <- evaluateCompilerFile "./test/sourceGateExpected.tri"
   2402         case decodeBundle bytes of
   2403           Left diagnostic -> assertFailure diagnostic
   2404           Right bundle -> do
   2405             let artifact = reconstructBundleTerms (bundleNodes bundle)
   2406                   V.! fromIntegral (head (bundleRoots bundle))
   2407                 cases =
   2408                   [ ("main = value\nvalue = t", "goldenOutputForward",
   2409                       parserOk (pairT Leaf (ofList
   2410                         [ pairT (ofString "value") Leaf
   2411                         , pairT (ofString "main") Leaf
   2412                         ])) Leaf)
   2413                   , ("2", "goldenOutputExpression",
   2414                       parserOk (pairT (ofNumber 2) Leaf) Leaf)
   2415                   ]
   2416             forM_ cases $ \(source, goldenName, expected) -> do
   2417               Map.lookup goldenName goldenEnv @?= Just expected
   2418               apply (apply artifact (ofString source)) expected @?= trueT
   2419   , testCase "output includes root and definitions in dependency order" $ do
   2420       let source = "main = value\nvalue = t"
   2421           output = foldl apply (gateUnitEnv Map.! "compileGateProgramOutput") [Leaf, ofString source]
   2422           definitions = ofList
   2423             [ pairT (ofString "value") Leaf
   2424             , pairT (ofString "main") Leaf
   2425             ]
   2426       output @?= parserOk (pairT Leaf definitions) Leaf
   2427       check Leaf source (parserOk Leaf Leaf)
   2428   , testCase "empty output environment still carries the selected root" $
   2429       foldl apply (gateUnitEnv Map.! "compileGateProgramOutput") [Leaf, ofString "t"] @?=
   2430           parserOk (pairT Leaf Leaf) Leaf
   2431   , testCase "output entrypoint preserves structured diagnostics" $
   2432       foldl apply (gateUnitEnv Map.! "compileGateProgramOutput") [Leaf, ofString "helper = t"] @?=
   2433           parserErr (portableDiagnostic (ofNumber 32)
   2434             (ofString "main") Leaf Leaf Leaf) Leaf
   2435   , testCase "public compileSource normalizes with abstract environment and source" $ do
   2436       env <- evaluateCompilerFile
   2437         "./test/sourcePublicCompiler.tri"
   2438       Map.member "matchesSource" env @?= True
   2439       let check environment source expected = do
   2440             foldl apply (fixtureExports "source.unit" Map.! "compileSource")
   2441               [environment, ofString source] @?= expected
   2442             foldl apply (env Map.! "matchesSource")
   2443               [environment, ofString source, expected] @?= trueT
   2444       assertBool "test wrapper leaked imported compiler" (Map.notMember "compileSource" env)
   2445       check Leaf "main = value\nvalue = t"
   2446         (parserOk (pairT Leaf (ofList
   2447           [pairT (ofString "value") Leaf, pairT (ofString "main") Leaf])) Leaf)
   2448       check (ofList [Fork (ofString "host") (Stem Leaf)]) "host"
   2449         (parserOk (pairT (Stem Leaf) Leaf) Leaf)
   2450       check Leaf "helper = t"
   2451         (parserErr (portableDiagnostic (ofNumber 32)
   2452           (ofString "main") Leaf Leaf Leaf) Leaf)
   2453       foldl apply (env Map.! "matchesSource")
   2454         [Leaf, ofString "main = t", parserOk (pairT (Stem Leaf) Leaf) Leaf] @?= falseT
   2455   , testCase "public compileSource is an exportable two-argument bundle" $
   2456       withSystemTempDirectory "public-source-artifact" $ \dir -> do
   2457         let path = dir </> "compile-source.arboricx"
   2458         packCompilerFile
   2459           "./lib/source/unit.tri" path [pack "compileSource"]
   2460         bytes <- BS.readFile path
   2461         case decodeBundle bytes of
   2462           Left diagnostic -> assertFailure diagnostic
   2463           Right bundle -> do
   2464             let artifact = reconstructBundleTerms (bundleNodes bundle)
   2465                   V.! fromIntegral (head (bundleRoots bundle))
   2466                 cases =
   2467                   [ (Leaf, "2", parserOk (pairT (ofNumber 2) Leaf) Leaf)
   2468                   , (Leaf, "main = value\nvalue = t", parserOk
   2469                       (pairT Leaf (ofList [pairT (ofString "value") Leaf,
   2470                         pairT (ofString "main") Leaf])) Leaf)
   2471                   , (ofList [pairT (ofString "host") (Stem Leaf)],
   2472                       "host", parserOk (pairT (Stem Leaf) Leaf) Leaf)
   2473                   , (Leaf, "helper = t", parserErr
   2474                       (portableDiagnostic (ofNumber 32)
   2475                         (ofString "main") Leaf Leaf Leaf) Leaf)
   2476                   , (Leaf, "#", parserErr
   2477                       (portableDiagnostic (ofNumber 4) Leaf (ofNumber 35)
   2478                         (gatePosition 1 1 0) Leaf) (ofString "#"))
   2479                   , (Leaf, "!import \"x\" NS", parserErr
   2480                       (portableDiagnostic (ofNumber 4) Leaf (ofNumber 33)
   2481                         (gatePosition 1 1 0) Leaf)
   2482                       (ofString "!import \"x\" NS"))
   2483                   ]
   2484             forM_ cases $ \(environment, source, expected) -> do
   2485               let compiled = apply (apply artifact environment) (ofString source)
   2486               compiled @?= expected
   2487               -- Repeated fixed inputs have structurally identical complete
   2488               -- outputs, not merely the same selected root.
   2489               apply (apply artifact environment) (ofString source) @?= compiled
   2490             forM_
   2491               [ ("./test/fork.tri", Fork Leaf Leaf)
   2492               , ("./test/comments-1.tri", Fork (Stem Leaf) Leaf)
   2493               ] $ \(fixture, root) -> do
   2494                 source <- readFile fixture
   2495                 apply (apply artifact Leaf) (ofString source) @?=
   2496                   parserOk (pairT root
   2497                     (ofList [pairT (ofString "main") root])) Leaf
   2498             let externalNames = ["map", "append", "equal?", "head", "y", "triage"]
   2499                 external = ofList
   2500                   [ pairT (ofString name) (fixtureExports "base" Map.! name)
   2501                   | name <- externalNames ]
   2502             stringSource <- readFile "./test/string.tri"
   2503             apply (apply artifact external) (ofString stringSource) @?=
   2504               parserOk (pairT (ofString "String test!") Leaf) Leaf
   2505             mapSource <- readFile "./test/map.tri"
   2506             apply (apply artifact external) (ofString mapSource) @?=
   2507               parserOk (pairT trueT (ofList
   2508                 [ pairT (ofString "x")
   2509                     (ofList [ofString "Successfully concatenated two strings!"])
   2510                 , pairT (ofString "main") trueT
   2511                 ])) Leaf
   2512   , testCase "public bundle functions agree by application, not tree shape" $
   2513       withSystemTempDirectory "public-source-functions" $ \dir -> do
   2514         let path = dir </> "compile-source.arboricx"
   2515         packCompilerFile
   2516           "./lib/source/unit.tri" path [pack "compileSource"]
   2517         bytes <- BS.readFile path
   2518         comparatorEnv <- evaluateCompilerFile "./test/sourcePublicCompiler.tri"
   2519         case decodeBundle bytes of
   2520           Left diagnostic -> assertFailure diagnostic
   2521           Right bundle -> do
   2522             let compiler = reconstructBundleTerms (bundleNodes bundle)
   2523                   V.! fromIntegral (head (bundleRoots bundle))
   2524                 comparator = comparatorEnv Map.! "matchesSourceApplicationArtifact"
   2525                 cases =
   2526                   [ ("main x = x", ofNumber 2, ofNumber 2)
   2527                   , ("main x = t", Stem Leaf, Leaf)
   2528                   , ("main x = let id a = a in id x", Stem Leaf, Stem Leaf)
   2529                   , ("main x = do bind\n  y <- x\n  y\nbind a f = f a",
   2530                       ofNumber 2, ofNumber 2)
   2531                   ]
   2532             forM_ cases $ \(source, input, expected) -> do
   2533               foldl apply comparator
   2534                 [compiler, Leaf, ofString source, input, expected] @?= trueT
   2535             let rootComparator = comparatorEnv Map.! "matchesSourceRootArtifact"
   2536                 unused = "unused = (x : x x) (x : x x)\nmain = t"
   2537             unusedRoot <- Timeout.timeout (30 * 1000000) $
   2538               evaluate (foldl apply rootComparator
   2539                 [compiler, Leaf, ofString unused, Leaf] == trueT)
   2540             unusedRoot @?= Just True
   2541   , testCase "self-built compiler wrapper handles a forward reference" $ do
   2542       env <- evaluateCompilerFile
   2543         "./test/sourceGateUnitImport.tri"
   2544       Map.lookup "main" env @?= Just (parserOk Leaf Leaf)
   2545   , testCase "forward definitions with arguments evaluate before main" $ do
   2546       let source = "main = id 42\nid x = x"
   2547       check Leaf source (parserOk (ofNumber 42) Leaf)
   2548   , testCase "final expression selects root in absence of main" $
   2549       check Leaf "id x = x\nid t" (parserOk Leaf Leaf)
   2550   , testCase "a local main wins over a final expression" $
   2551       check Leaf "main = 2\n3" (parserOk (ofNumber 2) Leaf)
   2552   , testCase "provided environment is visible inside definitions" $
   2553       check (ofList [Fork (ofString "host") (Stem Leaf)]) "main = host t"
   2554         (parserOk (Fork Leaf Leaf) Leaf)
   2555   , testCase "multiple definition arguments preserve binder order" $
   2556       let source = "main x y = x"
   2557           program = foldl apply (gateUnitEnv Map.! "compileGateProgram") [Leaf, ofString source]
   2558       in case program of
   2559            Fork tag (Fork fun Leaf) | tag == trueT ->
   2560              apply (apply fun Leaf) (Stem Leaf) @?= Leaf
   2561            other -> assertFailure ("Expected compiled two-argument function, got " ++ show other)
   2562   , testCase "recursive top-level function and triage compile from source" $ do
   2563       let source = "triage leaf stem fork = t (t leaf stem) fork\n"
   2564             ++ "main xs = y (self : rest : triage t (h : self h) (a : b : self a) rest) xs"
   2565           external = ofList [Fork (ofString "y") (gateCompileBaseEnv Map.! "y")]
   2566           compiled = foldl apply (gateUnitEnv Map.! "compileGateProgram") [external, ofString source]
   2567       case compiled of
   2568         Fork tag (Fork fun Leaf) | tag == trueT -> do
   2569           apply fun Leaf @?= Leaf
   2570           apply fun (Stem Leaf) @?= Leaf
   2571           apply fun (Fork (Stem Leaf) Leaf) @?= Leaf
   2572         other -> assertFailure ("Expected recursive source function, got " ++ show other)
   2573   , testCase "recursive function, y, and triage compile entirely from source" $ do
   2574       let source = "y = ((mut : wait : fun : wait mut (x : fun (wait mut x))) "
   2575             ++ "(x : x x) (a0 : a1 : a2 : t (t a0) (t t a2) a1))\n"
   2576             ++ "triage leaf stem fork = t (t leaf stem) fork\n"
   2577             ++ "main xs = y (self : rest : triage t (h : self h) "
   2578             ++ "(a : b : self a) rest) xs"
   2579           compiled = foldl apply (gateUnitEnv Map.! "compileGateProgram") [Leaf, ofString source]
   2580       case compiled of
   2581         Fork tag (Fork fun Leaf) | tag == trueT -> do
   2582           apply fun Leaf @?= Leaf
   2583           apply fun (Stem Leaf) @?= Leaf
   2584           apply fun (Fork (Stem Leaf) Leaf) @?= Leaf
   2585         other -> assertFailure ("Expected fully sourced recursive function, got " ++ show other)
   2586   , testCase "indented definition bodies preserve continuation layout" $ do
   2587       let sources =
   2588             [ ("main =\n  t", Leaf)
   2589             , ("main =\n  (x : x)\n    2", ofNumber 2)
   2590             , ("main =\n  id\n    t\nid x = x", Leaf)
   2591             , ("main =\n  id\n  23\nid x = x", ofNumber 23)
   2592             , ("main =\n    id\n  23\nid x = x", ofNumber 23)
   2593             , ("main = id\n  23\nid x = x", ofNumber 23)
   2594             , ("main = value\nwhere value = t", Leaf)
   2595             , ("main =\n  value\n  where value = t", Leaf)
   2596             ]
   2597       forM_ sources $ \(source, value) -> do
   2598         check Leaf source (parserOk value Leaf)
   2599   , testCase "multiline grouped applications retain expected results" $ do
   2600       let sources =
   2601             [ ("main = (t\n t)", Stem Leaf)
   2602             , ("main = [(t\n t)]", ofList [Stem Leaf])
   2603             , ("main = (let x = t in x\n t)", Stem Leaf)
   2604             , ("main = (x\n where x = t)", Leaf)
   2605             ]
   2606       forM_ sources $ \(source, value) -> do
   2607         check Leaf source (parserOk value Leaf)
   2608   , testCase "an unindented do block has a located diagnostic" $
   2609       let diagnostic = portableDiagnostic (ofNumber 40)
   2610             (ofString "indented do block") (ofNumber 2)
   2611             (gatePosition 2 1 15) Leaf
   2612           reply = apply (gateProgramEnv Map.! "parseGateProgram")
   2613             (ofString "main = do bind\nx <- 2\n  x")
   2614       in apply (fixtureExports "parser" Map.! "parserReplyPayload") reply @?= diagnostic
   2615   , testCase "pipe followed by a local assignment stays a final expression" $
   2616       check Leaf "f = 2\nf |> let id x = x in id"
   2617         (parserOk (ofNumber 2) Leaf)
   2618   , testCase "explicit-bind do with nested statements retains expected results" $ do
   2619       let sources =
   2620             [ "main = do bind\n  x <- 2\n  x\nbind a f = f a"
   2621             , "main = do unused\n  2"
   2622             , "main = do bind\n  1\n  2\nbind a f = f a"
   2623             , "main = do bind\n  x <- 2\n  y <- x\n  y\nbind a f = f a"
   2624             , "main = do bind\n  x <- 2\n  do bind\n    y <- x\n    y\nbind a f = f a"
   2625             ]
   2626       forM_ sources $ \source -> do
   2627         check Leaf source (parserOk (ofNumber 2) Leaf)
   2628   , testCase "pipe directions, precedence, and local names retain expected results" $ do
   2629       let sources =
   2630             [ ("main = 2 |> (x : x)", ofNumber 2)
   2631             , ("main = (x : x) <| 2", ofNumber 2)
   2632             , ("main = 2 |> (x : x) |> (x : x)", ofNumber 2)
   2633             , ("main = t <| t t", Stem (Stem Leaf))
   2634             , ("main = 2 |> id\nid x = x", ofNumber 2)
   2635             , ("main = 2 |> id where id x = x", ofNumber 2)
   2636             , ("main = 2\n  |> (x : x)", ofNumber 2)
   2637             , ("main = 2 |>\n  (x : x)", ofNumber 2)
   2638             ]
   2639       forM_ sources $ \(source, value) -> do
   2640         check Leaf source (parserOk value Leaf)
   2641   , testCase "multiline local let/where layout retains expected results" $ do
   2642       let sources =
   2643             [ ("main = let x =\n  2\nin x", ofNumber 2)
   2644             , ("main = let x = t in\n  x", Leaf)
   2645             , ("main = let\n  id x =\n    x\nin id 2", ofNumber 2)
   2646             , ("main =\n  let x =\n    t\n  in x", Leaf)
   2647             , ("main = t where x =\n  2", Leaf)
   2648             , ("main = id 2 where\n  id x =\n    x", ofNumber 2)
   2649             , ("main = let x =\n  (y : y)\n    2\nin x", ofNumber 2)
   2650             , ("main = let id x = x in\n  id\n    2", ofNumber 2)
   2651             ]
   2652       forM_ sources $ \(source, value) -> do
   2653         check Leaf source (parserOk value Leaf)
   2654   , testCase "local bindings preserve lexical scope and argument order" $ do
   2655       let sources =
   2656             [ ("main = let x = 2 in x", ofNumber 2)
   2657             , ("main = let id x = x in id 2", ofNumber 2)
   2658             , ("main = id 2 where id x = x", ofNumber 2)
   2659             , ("result where result = t", Leaf)
   2660             , ("main = let x = t in (y : x) 2", Leaf)
   2661             ]
   2662       forM_ sources $ \(source, value) -> do
   2663         check Leaf source (parserOk value Leaf)
   2664   , testCase "local binding value has no self-reference" $
   2665       check Leaf "main = let x = x in x" (parserErr
   2666         (portableDiagnostic (ofNumber 20) (ofString "x") Leaf
   2667           (gatePosition 1 16 15) Leaf)
   2668         (gateAst 2 (ofString "x")
   2669           (gatePosition 1 16 15) (gatePosition 1 17 16)))
   2670   , testCase "a local binder shadows a top-level definition" $
   2671       check Leaf "main = local where local = t\nlocal = 2"
   2672         (parserOk Leaf Leaf)
   2673   , testCase "local free names order forward-referenced definitions" $
   2674       check Leaf "main = value where value = helper t\nhelper x = x"
   2675         (parserOk Leaf Leaf)
   2676   , testCase "lists may refer to forward definitions" $
   2677       check Leaf "main = [(id 2)]\nid x = x"
   2678         (parserOk (ofList [ofNumber 2]) Leaf)
   2679   , testCase "unused divergent body is checked but not demanded" $ do
   2680       let source = "unused = (x : x x) (x : x x)\nmain = t"
   2681       check Leaf source (parserOk Leaf Leaf)
   2682   , testCase "a next-line body cannot occupy its owner's column" $ do
   2683       let reply = apply (gateProgramEnv Map.! "parseGateProgram")
   2684             (ofString "main =\nt")
   2685           diagnostic = portableDiagnostic (ofNumber 40)
   2686             (ofString "definition body") (ofNumber 1) (gatePosition 2 1 7) Leaf
   2687       apply (fixtureExports "parser" Map.! "parserReplyPayload") reply @?= diagnostic
   2688   , testCase "missing multiline definition body is a structured diagnostic" $
   2689       check Leaf "main =\n" (parserErr
   2690         (portableDiagnostic (ofNumber 40) (ofString "definition body")
   2691           Leaf Leaf Leaf) Leaf)
   2692   , testCase "missing local value or body returns a structured diagnostic" $ do
   2693       check Leaf "main = let x =\n" (parserErr
   2694         (portableDiagnostic (ofNumber 40)
   2695           (ofString "local binding value") Leaf Leaf Leaf) Leaf)
   2696       check Leaf "main = let x = t in\n" (parserErr
   2697         (portableDiagnostic (ofNumber 40)
   2698           (ofString "local binding body") Leaf Leaf Leaf) Leaf)
   2699   , testCase "no root is a structured diagnostic" $
   2700       check Leaf "helper = t" (parserErr
   2701         (portableDiagnostic (ofNumber 32) (ofString "main") Leaf Leaf Leaf)
   2702         Leaf)
   2703   , testCase "cycle fails before result selection" $
   2704       check Leaf "a = b\nb = a\nmain = t" (parserErr
   2705         (portableDiagnostic (ofNumber 31) (ofString "acyclic")
   2706           (ofString "a") (gatePosition 1 1 0)
   2707           (ofList [ofString "a"])) Leaf)
   2708   ]
   2709   where
   2710     check external source expected =
   2711       foldl apply (gateUnitEnv Map.! "compileGateProgram") [external, ofString source]
   2712         @?= expected
   2713 
   2714 -- --------------------------------------------------------------------------
   2715 -- Single-expression feasibility-gate name resolution
   2716 -- --------------------------------------------------------------------------
   2717 
   2718 gateResolveEnv :: Env
   2719 gateResolveEnv = fixtureExports "source.resolve"
   2720 {-# NOINLINE gateResolveEnv #-}
   2721 
   2722 gateCore :: Integer -> T -> T -> T -> T
   2723 gateCore = gateAst
   2724 
   2725 gateResolutionTests :: TestTree
   2726 gateResolutionTests = testGroup "Feasibility-gate resolution"
   2727   [ testCase "self-built resolver export executes through its wrapper" $ do
   2728       env <- evaluateCompilerFile "./test/sourceGateResolveImport.tri"
   2729       let bound = gateCore 2 (ofNumber 0) (gatePosition 1 5 4) (gatePosition 1 6 5)
   2730           lam = gateCore 4 bound (gatePosition 1 1 0) (gatePosition 1 6 5)
   2731       Map.lookup "main" env @?= Just (parserOk lam Leaf)
   2732   , testCase "nearest lexical binder has index zero" $
   2733       let bound = gateCore 2 (ofNumber 0) (gatePosition 1 5 4) (gatePosition 1 6 5)
   2734       in check Leaf "x : x" (parserOk
   2735         (gateCore 4 bound (gatePosition 1 1 0) (gatePosition 1 6 5)) Leaf)
   2736   , testCase "outer lexical binder has index one" $
   2737       let bound = gateCore 2 (ofNumber 1) (gatePosition 1 9 8) (gatePosition 1 10 9)
   2738           inner = gateCore 4 bound (gatePosition 1 5 4) (gatePosition 1 10 9)
   2739       in check Leaf "x : y : x" (parserOk
   2740         (gateCore 4 inner (gatePosition 1 1 0) (gatePosition 1 10 9)) Leaf)
   2741   , testCase "local binding lowers to an applied scoped lambda" $
   2742       let value = gateCore 1 Leaf
   2743             (gatePosition 1 9 8) (gatePosition 1 10 9)
   2744           bound = gateCore 2 (ofNumber 0)
   2745             (gatePosition 1 14 13) (gatePosition 1 15 14)
   2746           scoped = gateCore 4 bound
   2747             (gatePosition 1 1 0) (gatePosition 1 15 14)
   2748       in check Leaf "let x = t in x" (parserOk
   2749         (gateCore 3 (pairT scoped value)
   2750           (gatePosition 1 1 0) (gatePosition 1 15 14)) Leaf)
   2751   , testCase "forward pipe elaborates to reversed core application" $
   2752       let function = gateCore 5 (Stem Leaf)
   2753             (gatePosition 1 6 5) (gatePosition 1 7 6)
   2754           argument = gateCore 6 (ofNumber 2)
   2755             (gatePosition 1 1 0) (gatePosition 1 2 1)
   2756       in check (ofList [Fork (ofString "f") (Stem Leaf)]) "2 |> f" (parserOk
   2757         (gateCore 3 (pairT function argument)
   2758           (gatePosition 1 1 0) (gatePosition 1 7 6)) Leaf)
   2759   , testCase "number becomes a resolved tree literal" $
   2760       check Leaf "42" (parserOk
   2761         (gateCore 6 (ofNumber 42) (gatePosition 1 1 0) (gatePosition 1 3 2)) Leaf)
   2762   , testCase "string becomes a resolved tree literal" $
   2763       check Leaf "\"hi\"" (parserOk
   2764         (gateCore 7 (ofString "hi") (gatePosition 1 1 0) (gatePosition 1 5 4)) Leaf)
   2765   , testCase "list elements become individually resolved core nodes" $
   2766       let leaf = gateCore 1 Leaf (gatePosition 1 2 1) (gatePosition 1 3 2)
   2767           number = gateCore 6 (ofNumber 2)
   2768             (gatePosition 1 4 3) (gatePosition 1 5 4)
   2769       in check Leaf "[t 2]" (parserOk
   2770         (gateCore 8 (ofList [leaf, number])
   2771           (gatePosition 1 1 0) (gatePosition 1 6 5)) Leaf)
   2772   , testCase "unresolved list element preserves its own span" $
   2773       check Leaf "[u]" (parserErr
   2774         (portableDiagnostic (ofNumber 20) (ofString "u") Leaf
   2775           (gatePosition 1 2 1) Leaf)
   2776         (gateAst 2 (ofString "u")
   2777           (gatePosition 1 2 1) (gatePosition 1 3 2)))
   2778   , testCase "supplied external tree becomes a core value" $
   2779       let external = gateCore 5 (Stem Leaf)
   2780             (gatePosition 1 1 0) (gatePosition 1 2 1)
   2781           leaf = gateCore 1 Leaf (gatePosition 1 3 2) (gatePosition 1 4 3)
   2782       in check (ofList [Fork (ofString "f") (Stem Leaf)]) "f t" (parserOk
   2783         (gateCore 3 (pairT external leaf)
   2784           (gatePosition 1 1 0) (gatePosition 1 4 3)) Leaf)
   2785   , testCase "lexical binder shadows an external value" $
   2786       let bound = gateCore 2 (ofNumber 0) (gatePosition 1 5 4) (gatePosition 1 6 5)
   2787       in check (ofList [Fork (ofString "x") (Stem Leaf)]) "x : x" (parserOk
   2788         (gateCore 4 bound (gatePosition 1 1 0) (gatePosition 1 6 5)) Leaf)
   2789   , testCase "unresolved name reports its original position" $
   2790       check Leaf "u" (parserErr
   2791         (portableDiagnostic (ofNumber 20) (ofString "u") Leaf
   2792           (gatePosition 1 1 0) Leaf)
   2793         (gateAst 2 (ofString "u") (gatePosition 1 1 0) (gatePosition 1 2 1)))
   2794   , testCase "grouping carries its span into the core" $
   2795       check Leaf "(t)" (parserOk
   2796         (gateCore 1 Leaf (gatePosition 1 1 0) (gatePosition 1 4 3)) Leaf)
   2797   ]
   2798   where
   2799     check environment input expected =
   2800       foldl apply (gateResolveEnv Map.! "resolveGateSource") [environment, ofString input] @?= expected
   2801 
   2802 -- --------------------------------------------------------------------------
   2803 -- Reference S/K/I compiler for the feasibility gate
   2804 -- --------------------------------------------------------------------------
   2805 
   2806 gateCompileEnv :: Env
   2807 gateCompileEnv = fixtureExports "source.compile"
   2808 {-# NOINLINE gateCompileEnv #-}
   2809 
   2810 gateCompileBaseEnv :: Env
   2811 gateCompileBaseEnv = fixtureExports "base"
   2812 {-# NOINLINE gateCompileBaseEnv #-}
   2813 
   2814 gateCompilationTests :: TestTree
   2815 gateCompilationTests = testGroup "Feasibility-gate source compiler"
   2816   [ testCase "Haskell reducer executes the exported conformance artifact" $
   2817       withSystemTempDirectory "gate-conforms-bundle" $ \dir -> do
   2818         let path = dir </> "conforms.arboricx"
   2819         packCompilerFile "./test/sourceGateCompiler.tri"
   2820           path [pack "conforms"]
   2821         bytes <- BS.readFile path
   2822         case decodeBundle bytes of
   2823           Left diagnostic -> assertFailure diagnostic
   2824           Right bundle -> do
   2825             let artifact = reconstructBundleTerms (bundleNodes bundle)
   2826                   V.! fromIntegral (head (bundleRoots bundle))
   2827                 corpus =
   2828                   [ ("t", "t", trueT)
   2829                   , ("t t", "t", falseT)
   2830                   , ("t t t", "t t t", trueT)
   2831                   , ("(x : x) t", "t", trueT)
   2832                   , ("(x : t) (t t)", "t", trueT)
   2833                   , ("u", "u", trueT)
   2834                   , ("u", "t", falseT)
   2835                   , (nestedIdentity 16, "t", trueT)
   2836                   , (nestedBinders 16, "t", trueT)
   2837                   ]
   2838             forM_ corpus $ \(source, expectedSource, expected) ->
   2839               apply (apply artifact (ofString source)) (ofString expectedSource)
   2840                 @?= expected
   2841   , testCase "independent golden results match the Haskell artifact" $
   2842       withSystemTempDirectory "gate-golden-bundle" $ \dir -> do
   2843         let path = dir </> "matches.arboricx"
   2844         packCompilerFile "./test/sourceGateCompiler.tri"
   2845           path [pack "matchesExpected"]
   2846         bytes <- BS.readFile path
   2847         goldenEnv <- evaluateCompilerFile "./test/sourceGateExpected.tri"
   2848         let unknown = parserErr
   2849               (portableDiagnostic (ofNumber 20) (ofString "u") Leaf
   2850                 (gatePosition 1 1 0) Leaf)
   2851               (gateAst 2 (ofString "u") (gatePosition 1 1 0) (gatePosition 1 2 1))
   2852             unsupported = parserErr
   2853               (portableDiagnostic (ofNumber 4) Leaf (ofNumber 35)
   2854                 (gatePosition 1 1 0) Leaf)
   2855               (ofString "#")
   2856             badEscape = parserErr
   2857               (portableDiagnostic (ofNumber 6) (ofNumber 34) (ofNumber 113)
   2858                 (gatePosition 1 3 2) Leaf)
   2859               (ofString "\\q\"")
   2860             unfinishedString = parserErr
   2861               (portableDiagnostic (ofNumber 5) (ofNumber 34) Leaf
   2862                 (gatePosition 1 1 0) Leaf) Leaf
   2863             unfinishedBlock = parserErr
   2864               (portableDiagnostic (ofNumber 5) (ofString "-|") Leaf
   2865                 (gatePosition 1 1 0) Leaf) Leaf
   2866             unknownInList = parserErr
   2867               (portableDiagnostic (ofNumber 20) (ofString "u") Leaf
   2868                 (gatePosition 1 2 1) Leaf)
   2869               (gateAst 2 (ofString "u")
   2870                 (gatePosition 1 2 1) (gatePosition 1 3 2))
   2871             missingListClose = parserErr
   2872               (portableDiagnostic (ofNumber 2) (ofNumber 10) Leaf Leaf Leaf)
   2873               Leaf
   2874             comma = parserErr
   2875               (portableDiagnostic (ofNumber 2) (ofNumber 10) (ofNumber 20)
   2876                 (gatePosition 1 3 2) Leaf)
   2877               (ofList
   2878                 [ gateToken 20 Leaf (gatePosition 1 3 2) (gatePosition 1 4 3)
   2879                 , gateToken 1 (ofString "t") (gatePosition 1 5 4) (gatePosition 1 6 5)
   2880                 , gateToken 10 Leaf (gatePosition 1 6 5) (gatePosition 1 7 6)
   2881                 ])
   2882             golden =
   2883               [ ("t", "goldenLeaf", parserOk Leaf Leaf)
   2884               , ("t t", "goldenStem", parserOk (Stem Leaf) Leaf)
   2885               , ("t t t", "goldenFork", parserOk (Fork Leaf Leaf) Leaf)
   2886               , ("(x : x) t", "goldenLeaf", parserOk Leaf Leaf)
   2887               , ("42", "goldenFortyTwo", parserOk (ofNumber 42) Leaf)
   2888               , ("\"hi\"", "goldenHi", parserOk (ofString "hi") Leaf)
   2889               , ("|-note-| \"hi\"", "goldenHi", parserOk (ofString "hi") Leaf)
   2890               , ("u", "goldenUnknown", unknown)
   2891               , ("#", "goldenUnsupported", unsupported)
   2892               , ("\"\\q\"", "goldenBadEscape", badEscape)
   2893               , ("\"hi", "goldenUnfinishedString", unfinishedString)
   2894               , ("|-oops", "goldenUnfinishedBlock", unfinishedBlock)
   2895               , ("[]", "goldenLeaf", parserOk Leaf Leaf)
   2896               , ("[t 2]", "goldenListTwo",
   2897                   parserOk (ofList [Leaf, ofNumber 2]) Leaf)
   2898               , ("[t\n2]", "goldenListTwo",
   2899                   parserOk (ofList [Leaf, ofNumber 2]) Leaf)
   2900               , ("[[t] (t t)]", "goldenNestedList",
   2901                   parserOk (ofList [ofList [Leaf], Stem Leaf]) Leaf)
   2902               , ("[u]", "goldenUnknownInList", unknownInList)
   2903               , ("[t", "goldenMissingListClose", missingListClose)
   2904               , ("[t, t]", "goldenComma", comma)
   2905               ]
   2906         case decodeBundle bytes of
   2907           Left diagnostic -> assertFailure diagnostic
   2908           Right bundle -> do
   2909             let artifact = reconstructBundleTerms (bundleNodes bundle)
   2910                   V.! fromIntegral (head (bundleRoots bundle))
   2911             forM_ golden $ \(source, name, expected) -> do
   2912               Map.lookup name goldenEnv @?= Just expected
   2913               apply (apply artifact (ofString source)) expected @?= trueT
   2914             case Map.lookup "goldenUnknownWrongPosition" goldenEnv of
   2915               Nothing -> assertFailure "missing incorrect-position control"
   2916               Just wrong -> do
   2917                 apply (apply artifact (ofString "u")) wrong @?= falseT
   2918                 apply (apply artifact (ofString "t t"))
   2919                   (parserOk Leaf Leaf) @?= falseT
   2920   , testCase "self-built resolver export executes through its wrapper" $ do
   2921       env <- evaluateCompilerFile "./test/sourceGateCompileImport.tri"
   2922       Map.lookup "main" env @?= Just (parserOk Leaf Leaf)
   2923   , testCase "leaf compiles to raw Leaf" $
   2924       check "t" (parserOk Leaf Leaf)
   2925   , testCase "decimal literal compiles to the canonical number tree" $ do
   2926       check "42" (parserOk (ofNumber 42) Leaf)
   2927       check "000" (parserOk Leaf Leaf)
   2928   , testCase "list element boundaries distinguish atoms from grouped applications" $ do
   2929       check "[t t]" (parserOk (ofList [Leaf, Leaf]) Leaf)
   2930       check "[(t t)]" (parserOk (ofList [Stem Leaf]) Leaf)
   2931       check "[t\n2]" (parserOk (ofList [Leaf, ofNumber 2]) Leaf)
   2932   , testCase "lists compile to canonical Fork cells" $ do
   2933       check "[]" (parserOk Leaf Leaf)
   2934       check "[t 2]" (parserOk (ofList [Leaf, ofNumber 2]) Leaf)
   2935       check "[[t] (t t)]" (parserOk
   2936         (ofList [ofList [Leaf], Stem Leaf]) Leaf)
   2937       check "[t\n2]" (parserOk (ofList [Leaf, ofNumber 2]) Leaf)
   2938       check "(x : [x]) t" (parserOk (ofList [Leaf]) Leaf)
   2939   , testCase "string literal compiles to the canonical character list" $ do
   2940       check "\"hi\"" (parserOk (ofString "hi") Leaf)
   2941       check "(x : x) \"hi\"" (parserOk (ofString "hi") Leaf)
   2942   , testCase "comments and literals compose without a special host lexer" $ do
   2943       check "42 --ok" (parserOk (ofNumber 42) Leaf)
   2944       check "|-note-| \"hi\"" (parserOk (ofString "hi") Leaf)
   2945   , testCase "application evaluates to raw Stem and Fork" $ do
   2946       check "t t" (parserOk (Stem Leaf) Leaf)
   2947       check "t t t" (parserOk (Fork Leaf Leaf) Leaf)
   2948   , testCase "identity behaves correctly under arguments" $
   2949       checkFunction "x : x" [ofNumber 5] (ofNumber 5)
   2950   , testCase "constant behaves correctly under arguments" $
   2951       checkFunction "x : t" [ofNumber 5] Leaf
   2952   , testCase "nested lambdas preserve the outer argument" $
   2953       checkFunction "x : y : x" [ofNumber 5, ofNumber 8] (ofNumber 5)
   2954   , testCase "pruned bracket agrees with the retained reference" $ do
   2955       let term kind payload = Fork (ofNumber kind) (Fork payload Leaf)
   2956           var = term 2 Leaf
   2957           leaf = term 1 Leaf
   2958           app f x = term 3 (Fork f x)
   2959           terms = [var, leaf, app var leaf, app leaf var, app var var]
   2960           compiled method value = apply (gateCompileEnv Map.! "sourceRunTerm")
   2961             (apply (gateCompileEnv Map.! method) value)
   2962       forM_ terms $ \term ->
   2963         case (compiled "sourceBracketReference" term,
   2964               compiled "sourceBracket" term) of
   2965           (Fork referenceTag (Fork reference Leaf),
   2966            Fork optimizedTag (Fork optimized Leaf))
   2967             | referenceTag == trueT && optimizedTag == trueT ->
   2968                 forM_ [Leaf, Stem Leaf] $ \argument ->
   2969                   apply optimized argument @?= apply reference argument
   2970           other -> assertFailure ("Expected two bracketed trees, got " ++ show other)
   2971   , testCase "star eta and retained B/C reference agree when applied" $ do
   2972       let compiled method source =
   2973             case foldl apply (gateResolveEnv Map.! "resolveGateSource") [Leaf, ofString source] of
   2974               Fork tag (Fork core Leaf) | tag == trueT -> apply (gateCompileEnv Map.! method) core
   2975               other -> error ("reference corpus failed resolution: " ++ show other)
   2976           corpus =
   2977             [ ("x : x", [ofNumber 5], ofNumber 5)
   2978             , ("x y : x", [ofNumber 5, ofNumber 8], ofNumber 5)
   2979             , ("x y : y", [ofNumber 5, ofNumber 8], ofNumber 8)
   2980             , ("x : x x", [Stem Leaf], Fork Leaf (Stem Leaf))
   2981             , ("f x : f x", [Stem Leaf, Leaf], Fork Leaf Leaf)
   2982             , ("f x y : f (x y)", [Stem Leaf, Stem Leaf, Leaf],
   2983                 Fork Leaf (Fork Leaf Leaf))
   2984             , ("x : [x 2]", [Leaf], ofList [Leaf, ofNumber 2])
   2985             , ("a : (a : a)", [Leaf, Stem Leaf], Stem Leaf)
   2986             ]
   2987       forM_ corpus $ \(source, arguments, expected) ->
   2988         forM_ ["sourceCompileCoreReference", "sourceCompileCore",
   2989                 "sourceCompileCoreKiselyov"] $ \method ->
   2990           case compiled method source of
   2991             Fork tag (Fork fn Leaf) | tag == trueT ->
   2992               foldl apply fn arguments @?= expected
   2993             other -> assertFailure ("Failed to compile " ++ method ++ " "
   2994               ++ source ++ ": " ++ show other)
   2995   , testCase "application inside a lambda behaves correctly" $
   2996       checkFunction "f : x : f x" [Stem Leaf, ofNumber 8] (Fork Leaf (ofNumber 8))
   2997   , testCase "external tree is applied without host lookup" $
   2998       foldl apply (gateCompileEnv Map.! "compileGateSource")
   2999         [ofList [Fork (ofString "f") (Stem Leaf)], ofString "f t"] @?=
   3000           parserOk (Fork Leaf Leaf) Leaf
   3001   , testCase "backend compiles a recursive source function" $ do
   3002       let source = "xs : y (self : rest : triage t (h : self h) (a : b : self a) rest) xs"
   3003           external = ofList [Fork (ofString name) (gateCompileBaseEnv Map.! name)
   3004                             | name <- ["y", "triage"]]
   3005           compiled = foldl apply (gateCompileEnv Map.! "compileGateSource") [external, ofString source]
   3006       case compiled of
   3007         Fork tag (Fork compiled Leaf) | tag == trueT -> do
   3008           apply compiled Leaf @?= Leaf
   3009           apply compiled (Stem Leaf) @?= Leaf
   3010           apply compiled (Fork (Stem Leaf) Leaf) @?= Leaf
   3011         other -> assertFailure ("Expected compiled recursive function, got " ++ show other)
   3012   , testCase "unresolved name stays a diagnostic" $
   3013       check "u" (parserErr
   3014         (portableDiagnostic (ofNumber 20) (ofString "u") Leaf
   3015           (gatePosition 1 1 0) Leaf)
   3016         (gateAst 2 (ofString "u") (gatePosition 1 1 0) (gatePosition 1 2 1)))
   3017   ]
   3018   where
   3019     nestedIdentity depth = iterate (\body -> "(x : x) (" ++ body ++ ")") "t" !! depth
   3020     nestedBinders depth = "(" ++ concatMap (\i -> "x" ++ show i ++ " : ")
   3021       [0 .. depth - 1] ++ "x0) " ++ unwords (replicate depth "t")
   3022     compile input = foldl apply (gateCompileEnv Map.! "compileGateSource") [Leaf, ofString input]
   3023     check input expected = compile input @?= expected
   3024     checkFunction input args expected = case compile input of
   3025       Fork tag (Fork fn Leaf) | tag == trueT -> foldl apply fn args @?= expected
   3026       other -> assertFailure ("Expected compiled function, got " ++ show other)
   3027 
   3028 -- --------------------------------------------------------------------------
   3029 -- Binary parser combinator tests
   3030 -- --------------------------------------------------------------------------
   3031 
   3032 parserOk :: T -> T -> T
   3033 parserOk val rest = Fork trueT (Fork val rest)
   3034 
   3035 parserErr :: T -> T -> T
   3036 parserErr code rest = Fork falseT (Fork code rest)
   3037 
   3038 -- --------------------------------------------------------------------------
   3039 -- IO driver tests
   3040 -- --------------------------------------------------------------------------
   3041 
   3042 registryContractTests :: TestTree
   3043 registryContractTests = testCase "Registry hash contract: bounded rejection before service dispatch" $
   3044   withSystemTempDirectory "registry-contract" $ \dir -> do
   3045     env <- evaluateCompilerFile "./lib/arboricx/server.tri"
   3046     let call name = foldl apply (env Map.! name)
   3047         http name = foldl apply (fixtureExports "http" Map.! name)
   3048         valid = concat (replicate 4 "0123456789abcdef")
   3049         hash = ofString valid
   3050         diagnostic = ofString "expected 64 lowercase hexadecimal hash characters"
   3051         ok value rest = Fork (Stem Leaf) (Fork value rest)
   3052         err value rest = Fork Leaf (Fork value rest)
   3053         root = ofString "/registry"
   3054         expectedPath = "/registry/objects/012/" ++ valid ++ ".arboricx"
   3055         invalid = ["", replicate 63 'a', replicate 65 'a', replicate 64 'A',
   3056                    'g' : tail valid, take 32 valid ++ "g" ++ drop 33 valid,
   3057                    take 63 valid ++ "g", "../secret", "abc/../../secret",
   3058                    take 63 valid ++ "/", replicate 65536 'a']
   3059     -- All host byte values, not only selected examples.
   3060     forM_ [0..255] $ \byte -> do
   3061       let expected = if byte `elem` map (toInteger . fromEnum) "0123456789abcdef"
   3062                      then Stem Leaf else Leaf
   3063       call "bundleHexByte?" [ofNumber byte] @?= expected
   3064     forM_ ("0123456789abcdef" :: String) $ \digit ->
   3065       call "bundleHashValid?" [ofString (replicate 64 digit)] @?= Stem Leaf
   3066     call "bundleHashC" [hash, ofNumber 7] @?= ok hash (ofNumber 7)
   3067     forM_ invalid $ \bad ->
   3068       call "bundleHashC" [ofString bad, ofNumber 7] @?= err diagnostic (ofNumber 7)
   3069     -- Arbitrary malformed trees are rejected as well as canonical byte lists.
   3070     forM_ [Stem Leaf, Fork (Stem Leaf) Leaf,
   3071            foldr Fork (Stem Leaf) (replicate 64 (ofNumber 97)),
   3072            Fork (Fork (Fork Leaf Leaf) Leaf) (ofString (tail valid))] $ \bad ->
   3073       call "bundleHashValid?" [bad] @?= Leaf
   3074     -- A 65th cons is sufficient evidence: neither its byte nor tail is forced.
   3075     let oversized = foldr Fork
   3076           (Fork (error "65th byte inspected") (error "oversized suffix inspected"))
   3077           (replicate 64 (ofNumber 97))
   3078     call "bundleHashValid?" [oversized] @?= Leaf
   3079     let malformedByte = foldr Fork
   3080           (Fork (error "extra byte bit inspected") (error "extra byte suffix inspected"))
   3081           [Stem Leaf, Leaf, Leaf, Leaf, Leaf, Stem Leaf, Stem Leaf]
   3082     call "bundleHexByte?" [malformedByte] @?= Leaf
   3083     call "bundleObjectPath" [root, hash] @?= ofString expectedPath
   3084     let artifacts = fixtureExports "handler_artifacts"
   3085         handlers = fixtureExports "handlers"
   3086         observe value = apply (handlers Map.! "view") (apply (artifacts Map.! "load") value)
   3087         completed value = Fork (ofNumber 100) value
   3088         invalidReply = err (ofList [ofString "invalid-digest", diagnostic]) Leaf
   3089     -- Independent service-boundary oracle: invalid input performs no request;
   3090     -- valid input emits exactly one load and preserves both ordinary Results.
   3091     forM_ invalid $ \bad -> observe (ofString bad) @?= completed invalidReply
   3092     case observe hash of
   3093       Fork tag (Fork event (Fork labels (Fork continuation Leaf))) -> do
   3094         tag @?= ofNumber 106
   3095         event @?= ofList [ofString "artifacts", ofString "load", hash]
   3096         labels @?= Leaf
   3097         forM_ [ok (ofString "bundle") Leaf, err (ofString "missing") Leaf] $ \reply ->
   3098           apply continuation reply @?= completed reply
   3099       _ -> assertFailure "expected one artifact-load service request"
   3100 
   3101 -- Tiny source probes run through the authenticated portable compiler fixture.
   3102 -- Force complete generated trees before applying deliberately partial host data.
   3103 localControlTests :: TestTree
   3104 localControlTests = testGroup "Local control construction and non-strictness"
   3105   [ testCase "case constructs code, skips unselected work and preserves wildcard laziness" $ do
   3106       selected <- compileLocalControl [] $ unlines
   3107         [ "main input = case input of"
   3108         , "  t = 23"
   3109         , "  _ = let rec loop x = loop x in loop t" ]
   3110       apply selected Leaf @?= ofNumber 23
   3111       ignored <- compileLocalControl [] "main input = case input of\n  _ = 23"
   3112       apply ignored (error "wildcard forced its subject") @?= ofNumber 23
   3113       first <- compileLocalControl [] "main input = case input of\n  (head, _) = head\n  _ = t"
   3114       apply first (Fork (ofNumber 23) (error "case forced an ignored sibling")) @?= ofNumber 23
   3115   , testCase "local recursive hash worker rejects poisoned byte/tail with bounded inspection" $ do
   3116       server <- evaluateCompilerFile "./lib/arboricx/server.tri"
   3117       math <- evaluateCompilerFile "./lib/math/native.tri"
   3118       function <- compileLocalControl
   3119         [("true", Stem Leaf), ("false", Leaf),
   3120          ("hex", server Map.! "bundleHexByte?"), ("subtract", math Map.! "subtract")] $ unlines
   3121         [ "main hash = (let rec"
   3122         , "  walk [] 0 = true"
   3123         , "  walk _ 0 = false"
   3124         , "  walk [byte | rest] remaining = case (hex byte) of"
   3125         , "    ^true = walk rest (subtract remaining 1)"
   3126         , "    _ = false"
   3127         , "  walk _ _ = false"
   3128         , "  in walk hash 64)" ]
   3129       apply function (ofString (replicate 64 'a')) @?= Stem Leaf
   3130       let oversized = foldr Fork
   3131             (Fork (error "local worker inspected byte 65") (error "local worker inspected suffix"))
   3132             (replicate 64 (ofNumber 97))
   3133           malformedByte = foldr Fork
   3134             (Fork (error "local worker inspected extra bit") (error "local worker inspected numeral suffix"))
   3135             [Stem Leaf, Leaf, Leaf, Leaf, Leaf, Stem Leaf, Stem Leaf]
   3136       apply function oversized @?= Leaf
   3137       apply function (Fork malformedByte (error "local worker continued after malformed byte")) @?= Leaf
   3138   ]
   3139 
   3140 compileLocalControl :: [(String, T)] -> String -> IO T
   3141 compileLocalControl bindings source = do
   3142   unit <- evaluateCompilerFile "./lib/source/unit.tri"
   3143   let external = ofList [Fork (ofString name) value | (name,value) <- bindings]
   3144       answer = apply (apply (unit Map.! "compileStableModuleDefinitions") external) (ofString source)
   3145       forceTree Leaf = ()
   3146       forceTree (Stem child) = forceTree child
   3147       forceTree (Fork left right) = forceTree left `seq` forceTree right
   3148   case answer of
   3149     Fork (Stem Leaf) (Fork (Fork (Fork name function) Leaf) Leaf) -> do
   3150       name @?= ofString "main"
   3151       evaluate (forceTree function)
   3152       pure function
   3153     other -> assertFailure ("local control compilation failed: " ++ show other) >> pure Leaf
   3154 
   3155 containsAll :: String -> [String] -> Assertion
   3156 containsAll text needles =
   3157   forM_ needles $ \needle ->
   3158     assertBool ("expected " ++ show needle ++ " in: " ++ text) (needle `isInfixOf` text)
   3159 
   3160 
   3161 ioOkResult :: T -> T
   3162 ioOkResult val = Fork (Stem Leaf) (Fork val Leaf)
   3163 
   3164 ioErrResult :: String -> T
   3165 ioErrResult msg = Fork Leaf (Fork (ofString msg) Leaf)