phino-0.0.157: src/Deps.hs
{-# LANGUAGE OverloadedRecordDot #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TupleSections #-}
-- SPDX-FileCopyrightText: Copyright (c) 2025 Objectionary.com
-- SPDX-License-Identifier: MIT
module Deps where
import AST
import Bytes (btsSize, btsToNum)
import Control.Monad (foldM, guard, unless, when, zipWithM, (>=>))
import Control.Monad.Trans.Class (lift)
import Control.Monad.Trans.State.Strict (StateT, get, modify, put, runStateT)
import Data.Bifunctor (bimap, first, second)
import Data.Either (lefts, rights)
import Data.IORef (IORef, readIORef, writeIORef)
import Data.List (intercalate, sortOn)
import qualified Data.Map.Strict as Map
import Data.Maybe (fromMaybe, listToMaybe, mapMaybe)
import qualified Data.Text as T
import Files (overwrite)
import Forks (Refusal (..), Side (..))
import GHC.Clock (getMonotonicTime)
import Logger (logDebug, logInfo)
import Matcher
import Printer (printAlpha, printAttribute, printFunction)
import System.Directory (createDirectoryIfMissing)
import System.FilePath
import System.IO (Handle, hPutStrLn)
import Text.Printf (printf)
import XMIR (escapeXML, escapeXMLText)
import Yaml (ExtraArgument)
data Term
= TeExpression Expression
| TeAttribute Attribute
| TeBytes Bytes
| TeBindings [Binding]
type BuildTermMethod = [ExtraArgument] -> Subst -> IO Term
data State = State
{ _manufactured :: Maybe Int
, _stuck :: Maybe T.Text
}
type BuildTermMethodS = [ExtraArgument] -> Subst -> IO (Term, State)
type BuildTermFunc = String -> BuildTermMethod
type SaveStepFunc = Expression -> IO ()
saveStep :: Maybe FilePath -> String -> (Expression -> IO String) -> Int -> SaveStepFunc
saveStep Nothing _ _ _ _ = pure ()
saveStep (Just dir) ext render step expr = do
createDirectoryIfMissing True dir
let path = dir </> printf "%05d.%s" step ext
content <- render expr
overwrite path content
logDebug (printf "Saved step '%d' to '%s'" step path)
dontSaveStep :: SaveStepFunc
dontSaveStep = saveStep Nothing "" (\_ -> pure "") 0
type SaveMadeFunc = Expression -> Expression -> IO ()
dontSaveMade :: SaveMadeFunc
dontSaveMade _ _ = pure ()
data Judgment
= Normalization
| Morphing
| Dataization
| Evaluation
| Contextualization
deriving (Eq, Show)
letter :: Judgment -> String
letter Normalization = "𝒩"
letter Morphing = "𝕄"
letter Dataization = "𝔻"
letter Evaluation = "𝔼"
letter Contextualization = "𝒞"
opened :: Judgment -> String
opened Normalization = "normalize"
opened Morphing = "morph"
opened Dataization = "dataize"
opened Evaluation = "evaluate"
opened Contextualization = "contextualize"
data Acyclic
= Proven
| Plausible
deriving (Bounded, Enum, Eq, Show)
certainty :: Acyclic -> String
certainty Proven = "proven"
certainty Plausible = "plausible"
refused :: Refusal -> String
refused refusal = case refusal of
Unnamed -> "the join names no otherwise"
Unformed side -> printf "the %s side is no formation" (handed side)
Holding side attr -> printf "the %s side has %s" (handed side) (printAttribute attr)
where
handed :: Side -> String
handed Lefthand = "left"
handed Righthand = "right"
dotted :: [Attribute] -> String
dotted = concatMap (('.' :) . printAttribute)
data Evaluation
= EvRun Judgment T.Text
| EvFiring Int T.Text Judgment Expression
| EvStarted Int Judgment Expression
| EvDelta Int Bytes
| EvManufactured Int Int
| EvLooped Int Judgment Acyclic Expression Expression
| EvStuck Int T.Text Judgment Expression
| EvStall Int T.Text
| EvStuckOn Int T.Text
| EvStarved Int Int Judgment Expression
| EvTimeout Int Int Judgment Expression
| EvSpent Int Int Judgment Expression
| EvData Int T.Text Expression (Either Int Bytes)
| EvTerm Int T.Text Judgment Expression Expression
| EvBound Int T.Text Expression Expression
| EvReshaped Int T.Text T.Text Expression Expression
| EvSymbolized Int Int Bytes
| EvMinted Int Int [Either Int Bytes]
| EvDeferred Int Int Judgment Expression (Maybe Expression) Expression
| EvApplied Int Judgment Expression Expression Expression
| EvBuilt Int Expression
| EvAnswer Int Expression
| EvMorphed Int Expression
| EvJoined Int (Either T.Text Expression) Int (Int, Int)
| EvDiverged Int (Expression, Expression) (Expression, Expression) [Attribute] Refusal
| EvFabricated Int (Expression, Expression) (Expression, Expression) Expression
| EvTaken Int [Expression] Expression
| EvWorld Expression
type SaveEvalFunc = Evaluation -> IO ()
resited :: Expression -> Expression -> Evaluation -> Evaluation
resited from to = record
where
record :: Evaluation -> Evaluation
record (EvFiring depth key judgment site) = EvFiring depth key judgment (moved site)
record (EvStarted depth judgment site) = EvStarted depth judgment (moved site)
record (EvLooped depth judgment mode self site) = EvLooped depth judgment mode self (moved site)
record (EvStarved depth limit judgment site) = EvStarved depth limit judgment (moved site)
record (EvTimeout depth limit judgment site) = EvTimeout depth limit judgment (moved site)
record (EvSpent depth limit judgment site) = EvSpent depth limit judgment (moved site)
record (EvDeferred depth sym judgment copy call site) = EvDeferred depth sym judgment copy call (moved site)
record (EvApplied depth judgment call object site) = EvApplied depth judgment call object (moved site)
record other = other
moved :: Expression -> Expression
moved site
| site == from = to
| otherwise = site
ieee :: Bytes -> Maybe String
ieee bytes
| btsSize bytes == 8 = Just (show (either fromIntegral id (btsToNum bytes) :: Double))
| otherwise = Nothing
tier :: Evaluation -> Int
tier EvRun{} = 0
tier (EvFiring depth _ _ _) = depth
tier (EvStarted depth _ _) = depth
tier (EvDelta depth _) = depth
tier (EvManufactured depth _) = depth
tier (EvLooped depth _ _ _ _) = depth
tier (EvStuck depth _ _ _) = depth
tier (EvStall depth _) = depth
tier (EvStuckOn depth _) = depth
tier (EvStarved depth _ _ _) = depth
tier (EvTimeout depth _ _ _) = depth
tier (EvSpent depth _ _ _) = depth
tier (EvData depth _ _ _) = depth
tier (EvTerm depth _ _ _ _) = depth
tier (EvBound depth _ _ _) = depth
tier (EvReshaped depth _ _ _ _) = depth
tier (EvSymbolized depth _ _) = depth
tier (EvMinted depth _ _) = depth
tier (EvDeferred depth _ _ _ _ _) = depth
tier (EvApplied depth _ _ _ _) = depth
tier (EvBuilt depth _) = depth
tier (EvAnswer depth _) = depth
tier (EvMorphed depth _) = depth
tier (EvJoined depth _ _ _) = depth
tier (EvDiverged depth _ _ _ _) = depth
tier (EvFabricated depth _ _ _) = depth
tier (EvTaken depth _ _) = depth
tier EvWorld{} = maxBound
type Named a = Map.Map Int [(Expression, a)]
namedLookup :: Expression -> Named a -> Maybe a
namedLookup term names = lookup term (Map.findWithDefault [] (hashExpression term) names)
namedInsert :: forall a. Expression -> a -> Named a -> Named a
namedInsert term naming = Map.alter renamed (hashExpression term)
where
renamed :: Maybe [(Expression, a)] -> Maybe [(Expression, a)]
renamed entries = Just ((term, naming) : filter ((/= term) . fst) (fromMaybe [] entries))
abbreviated :: Named Expression -> Expression -> Expression
abbreviated names term
| Map.null names = term
| otherwise = fromMaybe (abbreviatedInside names term) (namedLookup term names)
abbreviatedInside :: Named Expression -> Expression -> Expression
abbreviatedInside names (ExFormation bds) = ExFormation (map binding bds)
where
binding :: Binding -> Binding
binding (BiTau attr expr) = BiTau attr (abbreviated names expr)
binding bd = bd
abbreviatedInside names (ExApplication expr (ArTau attr arg)) = ExApplication (abbreviated names expr) (ArTau attr (abbreviated names arg))
abbreviatedInside names (ExApplication expr (ArAlpha alpha arg)) = ExApplication (abbreviated names expr) (ArAlpha alpha (abbreviated names arg))
abbreviatedInside names (ExDispatch expr attr) = ExDispatch (abbreviated names expr) attr
abbreviatedInside _ term = term
data Protocol = Protocol
{ _fired :: Int
, _named :: Named T.Text
, _open :: [(Int, Int)]
, _begun :: Bool
, _made :: Named Expression
, _counted :: Map.Map Int Int
, _built :: Map.Map Int Int
, _deltas :: Map.Map Int Int
, _found :: Maybe (Either Int Bytes, String)
, _blocks :: [(Int, Maybe String, String, Bool)]
, _answered :: [(Int, Expression, String)]
}
emptyProtocol :: Protocol
emptyProtocol = Protocol 0 Map.empty [] False Map.empty Map.empty Map.empty Map.empty Nothing [] []
data Nesting = Nesting
{ _fires :: Int
, _openedAt :: [(Int, Int)]
, _closing :: [(Int, String)]
, _objects :: Named Expression
, _numbered :: Map.Map Int Int
, _datums :: Map.Map Int Int
, _held :: Maybe (Either Int Bytes, String)
, _heads :: [(Int, (Judgment, Expression))]
, _pending :: [(Int, Judgment, Expression)]
, _sources :: [(Int, Expression, String)]
, _world :: Map.Map Attribute Expression
, _shapes :: Map.Map [Maybe Attribute] [(Expression, [Binding])]
, _opaque :: Int
}
type Naming = StateT (Nesting, [String]) IO
emptyNesting :: Nesting
emptyNesting = Nesting 0 [] [] Map.empty Map.empty Map.empty Nothing [] [] [] Map.empty Map.empty 0
saveEval :: Handle -> IORef Protocol -> (Expression -> IO String) -> (Expression -> IO String) -> SaveEvalFunc
saveEval handle cursor printed printed' report = do
made <- atomicModify cursor (fmap (first (forgotten report) . headed) . written (tier report) report . beside (tier report) report . outer (tier report))
mapM_ saved made
where
forgotten :: Evaluation -> Protocol -> Protocol
forgotten EvDelta{} protocol = protocol
forgotten EvWorld{} protocol = protocol
forgotten _ protocol = protocol{_found = Nothing}
beside :: Int -> Evaluation -> Protocol -> Protocol
beside _ EvStarted{} protocol = protocol
beside _ EvMinted{} protocol = protocol
beside at _ protocol = protocol{_blocks = dropWhile (\(level, _, _, _) -> level >= at) protocol._blocks}
headed :: (Protocol, Maybe String) -> (Protocol, [String])
headed (protocol, Nothing) = (protocol, [])
headed (protocol, Just line) =
( protocol{_blocks = [(level, heading, opening, True) | (level, heading, opening, _) <- protocol._blocks]}
, [opening | (_, _, opening, False) <- reverse protocol._blocks] ++ lines line
)
render :: Expression -> IO String
render term = do
protocol <- readIORef cursor
printed (abbreviated protocol._made term)
located :: Protocol -> Expression -> IO String
located protocol site = case [naming | (_, built, naming) <- protocol._answered, built == site] of
naming : _ -> pure naming
[] -> render site
context :: Protocol -> Judgment -> Expression -> IO [String]
context protocol judgment site = do
locator <- located protocol site
let said = printf "%s(%s)" (letter judgment) locator
pure [said | innermost protocol /= Just said]
innermost :: Protocol -> Maybe String
innermost protocol = case protocol._blocks of
(_, heading, _, _) : _ -> heading
[] -> Nothing
remarked :: String -> [String] -> String
remarked line [] = line
remarked line remarks = printf "%s # %s" line (intercalate ", " remarks)
salted :: Expression -> IO String
salted term = do
protocol <- readIORef cursor
printed' (abbreviated protocol._made term)
saved :: String -> IO ()
saved line = do
hPutStrLn handle line
logDebug (printf "Saved one line of the protocol: %s" (dropWhile (== ' ') line))
written :: Int -> Evaluation -> Protocol -> IO (Protocol, Maybe String)
written _ (EvRun judgment locator) protocol =
pure (protocol{_begun = True, _blocks = [(0, Just heading, heading ++ ":", True)]}, Just (heading ++ ":"))
where
heading :: String
heading = printf "%s(%s)" (letter judgment) (T.unpack locator)
written at (EvFiring depth key judgment site) protocol = do
opening <- indented at . remarked (printf "𝔼(%s):" (T.unpack key)) <$> context protocol judgment site
pure
( protocol
{ _fired = firings
, _open = (depth, firings) : protocol._open
, _blocks = (at, Nothing, opening, True) : protocol._blocks
}
, Just opening
)
where
firings :: Int
firings = protocol._fired + 1
written at (EvStarted _ judgment site) protocol = do
locator <- located protocol site
let heading = printf "%s(%s)" (letter judgment) locator
inner = dropWhile (\(level, _, _, _) -> level > at) protocol._blocks
pure $ case inner of
(level, Just heading', _, _) : _ | level == at && heading' == heading -> (protocol{_blocks = inner}, Nothing)
_ -> (protocol{_blocks = (at, Just heading, indented at (heading ++ ":"), False) : dropWhile (\(level, _, _, _) -> level >= at) inner}, Nothing)
written at (EvDelta _ bytes) protocol = do
datum <- render (ExBytes bytes)
let index = maybe 1 (+ 1) (Map.lookup (opener protocol) protocol._deltas)
naming :: String
naming = printf "%s·%d" (labelled protocol sigil) index
pure (protocol{_deltas = Map.insert (opener protocol) index protocol._deltas, _found = Just (Right bytes, naming)}, Just (indented at (remarked (printf "%s := %s" naming datum) (maybe [] pure (ieee bytes)))))
written _ EvManufactured{} protocol = pure (protocol, Nothing)
written at (EvLooped _ judgment mode self site) protocol = do
form <- render self
remarks <- context protocol judgment site
pure (protocol, Just (indented at (remarked (printf "looped %s" form) (remarks ++ [certainty mode]))))
written at (EvStuck _ key judgment self) protocol = do
form <- render self
pure (protocol, Just (indented at (printf "unanswered %s # %s(%s)" (T.unpack key) (letter judgment) form)))
written at (EvStall _ key) protocol =
pure (protocol, Just (indented at (printf "stall %s" (T.unpack key))))
written at (EvStuckOn _ key) protocol =
pure (protocol, Just (indented at (printf "stuck %s" (T.unpack key))))
written at (EvStarved _ limit judgment site) protocol = do
remarks <- context protocol judgment site
pure (protocol, Just (indented at (remarked (printf "starved %d" limit) remarks)))
written at (EvTimeout _ limit judgment site) protocol = do
remarks <- context protocol judgment site
pure (protocol, Just (indented at (remarked (printf "timeout %d" limit) remarks)))
written at (EvSpent _ limit judgment site) protocol = do
remarks <- context protocol judgment site
pure (protocol, Just (indented at (remarked (printf "spent %d" limit) remarks)))
written at (EvData _ spelling operand value) protocol = do
datum <- maybe (spelt value) pure (found value protocol._found)
line <- commented (printf "%s := %s" (labelled protocol spelling) datum) Dataization operand
pure (protocol, Just (indented at line))
written at (EvTerm _ spelling judgment operand term) protocol = do
let naming = labelled protocol spelling
(protocol', value) <- valued protocol naming term
line <- commented (printf "%s := %s" naming value) judgment operand
pure (protocol', Just (indented at line))
written at (EvBound _ spelling source term) protocol = do
let naming = labelled protocol spelling
(protocol', value) <- valued protocol naming term
line <- commented' (printf "%s := %s" naming value) source
pure (protocol', Just (indented at line))
written at (EvReshaped _ spelling block source term) protocol = do
let naming = labelled protocol spelling
(protocol', value) <- valued protocol naming term
line <- printf "%s := %s # %s(%s)" naming value (T.unpack block) <$> salted source
pure (protocol', Just (indented at line))
written at (EvSymbolized _ symbol bytes) protocol = do
datum <- render (ExBytes bytes)
pure (protocol, Just (indented at (remarked (printf "symbolized %s := %s" (printFunction (FnSymbol symbol)) datum) (maybe [] pure (ieee bytes)))))
written _ EvMinted{} protocol = pure (protocol, Nothing)
written at (EvDeferred _ symbol judgment copy call site) protocol = do
form <- maybe (render copy) (printed . abbreviatedInside protocol._made) call
remarks <- context protocol judgment site
pure (protocol, Just (indented at (remarked (printf "deferred %s := %s" (printFunction (FnSymbol symbol)) form) remarks)))
written at (EvApplied _ judgment call object site) protocol = do
let (index, counted) = numbered protocol
aliased :: Expression
aliased = alias (opener protocol) index
form <- printed (abbreviatedInside protocol._made call)
remarks <- context protocol judgment site
pure (counted{_made = namedInsert call aliased (namedInsert object aliased counted._made)}, Just (indented at (remarked (printf "%s·%d := %s" (labelled protocol answer) index form) remarks)))
written at (EvBuilt depth term) protocol = do
let (index, counted) = numbered protocol
value <- borrowed protocol term
let naming = printf "%s·%d" (labelled protocol answer) index
pure (counted{_built = Map.insert (opener protocol) index counted._built, _answered = (depth, term, naming) : counted._answered}, Just (indented at (printf "%s := %s # %s" naming value (T.unpack answer))))
written at (EvAnswer _ term) protocol = do
let (index, counted) = numbered protocol
stem :: String
stem = labelled protocol answer
naming :: String
naming = printf "%s·%d" stem index
(protocol', value) <- valued counted naming term
pure (protocol', Just (indented at (printf "%s := %s # 𝕄(%s·%d)" naming value stem (Map.findWithDefault 1 (opener protocol) protocol._built))))
written _ EvMorphed{} protocol = pure (protocol, Nothing)
written at (EvJoined _ condition symbol (left, right)) protocol = do
on <- either (pure . labelled protocol) salted condition
pure (protocol, Just (indented at (printf "joined %s := %s ? %s : %s" (printFunction (FnSymbol symbol)) on (printFunction (FnSymbol left)) (printFunction (FnSymbol right)))))
written at (EvDiverged _ left right path refusal) protocol = do
one <- sided protocol left
two <- sided protocol right
pure (protocol, Just (indented at (printf "diverged %s vs %s # %s" one two (explained path refusal))))
written at (EvFabricated _ left right term) protocol = do
one <- sided protocol left
two <- sided protocol right
body <- printed (abbreviatedInside protocol._made term)
let (index, counted) = numbered protocol
pure (counted{_made = namedInsert term (alias (opener protocol) index) counted._made}, Just (indented at (printf "fabricated %s·%d := %s and %s # %s" (labelled protocol answer) index one two body)))
written at (EvTaken _ sides term) protocol = do
let (index, counted) = numbered protocol
naming :: String
naming = printf "%s·%d" (labelled protocol answer) index
(protocol', value) <- valued counted naming term
remarks <- mapM sourced sides
pure (protocol', Just (indented at (remarked (printf "%s := %s" naming value) remarks)))
written _ EvWorld{} protocol = pure (protocol, Nothing)
spelt :: Either Int Bytes -> IO String
spelt (Left symbol) = printf "𝔻(%s)" <$> render (standing symbol)
spelt (Right bytes) = render (ExBytes bytes)
valued :: Protocol -> String -> Expression -> IO (Protocol, String)
valued protocol naming term = case denoted term of
Nothing -> (,) protocol <$> render term
Just _ -> do
value <- maybe (render term) (pure . T.unpack) (namedLookup term protocol._named)
pure (protocol{_named = namedInsert term (T.pack naming) protocol._named}, value)
borrowed :: Protocol -> Expression -> IO String
borrowed protocol term = case namedLookup term protocol._named of
Nothing -> render term
Just naming -> pure (T.unpack naming)
explained :: [Attribute] -> Refusal -> String
explained [] refusal = refused refusal
explained path refusal = printf "they differ at %s, and %s" (dotted path) (refused refusal)
sided :: Protocol -> (Expression, Expression) -> IO String
sided _ (_, term@(ExFormation [BiLambda (FnSymbol _)])) = render term
sided protocol (side@(ExMeta _), _) = labelled protocol . T.pack <$> salted side
sided protocol (_, term) = borrowed protocol term
commented :: String -> Judgment -> Expression -> IO String
commented line judgment operand = printf "%s # %s(%s)" line (letter judgment) <$> salted operand
commented' :: String -> Expression -> IO String
commented' line source = printf "%s # %s" line <$> salted source
sourced :: Expression -> IO String
sourced side@(ExMeta _) = salted side
sourced side = printf "%s(%s)" (letter Morphing) <$> salted side
outer :: Int -> Protocol -> Protocol
outer depth protocol = protocol{_open = dropWhile ((>= depth) . fst) protocol._open, _answered = dropWhile (\(level, _, _) -> level > depth) protocol._answered}
labelled :: Protocol -> T.Text -> String
labelled protocol spelling = printf "%s·%d" (T.unpack spelling) (opener protocol)
opener :: Protocol -> Int
opener protocol = maybe 0 snd (listToMaybe protocol._open)
numbered :: Protocol -> (Int, Protocol)
numbered protocol = (index, protocol{_counted = Map.insert (opener protocol) index protocol._counted})
where
index :: Int
index = maybe 1 (+ 1) (Map.lookup (opener protocol) protocol._counted)
endEval :: Handle -> IORef Protocol -> Double -> IO ()
endEval handle cursor began = do
protocol <- readIORef cursor
when protocol._begun $ do
now <- getMonotonicTime
let taken = milliseconds began now
hPutStrLn handle (printf "msec %d" taken)
hPutStrLn handle (printf "firings %d" protocol._fired)
hPutStrLn handle (printf "fps %d" (perSecond protocol._fired taken))
milliseconds :: Double -> Double -> Int
milliseconds began now = round ((now - began) * 1000)
perSecond :: Int -> Int -> Int
perSecond firings taken = round (fromIntegral firings * 1000 / fromIntegral (max 1 taken) :: Double)
saveEvalXml :: Handle -> IORef Nesting -> (Expression -> IO String) -> SaveEvalFunc
saveEvalXml handle cursor printed report = do
written <- atomicModify cursor (flushed . beside (tier report) report . outer (tier report) >=> opened' (tier report))
mapM_ (hPutStrLn handle) written
logDebug (printf "Saved %d line(s) of the XML protocol" (length written))
where
opened' :: Int -> (Nesting, [String]) -> IO (Nesting, [String])
opened' at (nesting, openings) = bimap (forgotten report) (openings ++) <$> elements at report nesting
beside :: Int -> Evaluation -> Nesting -> Nesting
beside _ EvStarted{} nesting = nesting
beside at _ nesting = nesting{_heads = dropWhile ((>= at) . fst) nesting._heads, _pending = dropWhile (\(level, _, _) -> level >= at) nesting._pending}
flushed :: Nesting -> IO (Nesting, [String])
flushed nesting
| silent report = pure (nesting, [])
| otherwise = foldM pended (nesting{_pending = []}, []) (reverse nesting._pending)
pended :: (Nesting, [String]) -> (Int, Judgment, Expression) -> IO (Nesting, [String])
pended (nesting, openings) (level, judgment, site) = do
(nesting', lines') <- staged level nesting (printf "<%s at=\"%s\">" (opened judgment) . escapeXML <$> located level site)
pure (nesting'{_closing = (level, opened judgment) : nesting'._closing}, openings ++ lines')
silent :: Evaluation -> Bool
silent EvStarted{} = True
silent EvWorld{} = True
silent _ = False
forgotten :: Evaluation -> Nesting -> Nesting
forgotten EvDelta{} nesting = nesting
forgotten EvManufactured{} nesting = nesting
forgotten EvWorld{} nesting = nesting
forgotten _ nesting = nesting{_held = Nothing}
staged :: Int -> Nesting -> Naming String -> IO (Nesting, [String])
staged depth nesting naming = do
(element, (nesting', records)) <- runStateT naming (nesting, [])
pure (nesting', map (indentedXml depth) (reverse (element : records)))
placed :: Int -> Nesting -> Naming String -> IO (Nesting, [String])
placed depth nesting naming = second (closers ++) <$> staged depth nesting{_closing = kept} naming
where
(kept, closers) = closed depth nesting._closing
elements :: Int -> Evaluation -> Nesting -> IO (Nesting, [String])
elements _ (EvRun judgment locator) nesting =
pure
( nesting{_closing = (0, opened judgment) : (if null nesting._closing then [(-1, "protocol")] else kept)}
, ["<?xml version=\"1.0\" encoding=\"UTF-8\"?>" | null nesting._closing]
++ ["<protocol>" | null nesting._closing]
++ closers
++ [run]
)
where
(kept, closers) = closed 0 nesting._closing
run :: String
run = indentedXml 0 (printf "<%s at=\"%s\">" (opened judgment) (quoted locator))
elements at (EvFiring depth key judgment site) nesting = do
(fired, lines') <- placed at nesting (printf "<evaluate λ=\"%s\" by=\"%s\" at=\"%s\">" (quoted key) (opened judgment) . escapeXML <$> located depth site)
pure (fired{_fires = fired._fires + 1, _openedAt = (depth, fired._fires + 1) : fired._openedAt, _closing = (at, "evaluate") : fired._closing}, lines')
elements at (EvStarted _ judgment site) nesting =
pure $ case inner of
(level, opening) : _ | level == at && opening == (judgment, site) -> (nesting{_heads = inner, _pending = pending}, [])
_ -> (nesting{_heads = (at, (judgment, site)) : dropWhile ((>= at) . fst) inner, _pending = (at, judgment, site) : dropWhile (\(level, _, _) -> level >= at) pending, _closing = kept}, closers)
where
inner :: [(Int, (Judgment, Expression))]
inner = dropWhile ((> at) . fst) nesting._heads
pending :: [(Int, Judgment, Expression)]
pending = dropWhile (\(level, _, _) -> level > at) nesting._pending
(kept, closers) = closed at nesting._closing
elements at (EvLooped depth judgment mode self site) nesting =
placed at nesting $ do
locator <- located depth site
origin <- sole depth self
pure (enclosed "looped" (printf "<looped by=\"%s\" match=\"%s\" at=\"%s\"" (opened judgment) (certainty mode) (escapeXML locator)) origin)
elements at (EvStuck depth key judgment self) nesting =
placed at nesting (printf "<unanswered λ=\"%s\" by=\"%s\">%s</unanswered>" (quoted key) (opened judgment) . escapeXMLText <$> leaf depth self)
elements at (EvStall _ key) nesting =
placed at nesting (pure (printf "<stalled λ=\"%s\"/>" (quoted key)))
elements at (EvStuckOn _ key) nesting =
placed at nesting (pure (printf "<stuck λ=\"%s\"/>" (quoted key)))
elements at (EvStarved depth limit judgment site) nesting =
placed at nesting (printf "<starved limit=\"%d\" by=\"%s\" at=\"%s\"/>" limit (opened judgment) . escapeXML <$> located depth site)
elements at (EvTimeout depth limit judgment site) nesting =
placed at nesting (printf "<timeout limit=\"%d\" by=\"%s\" at=\"%s\"/>" limit (opened judgment) . escapeXML <$> located depth site)
elements at (EvSpent depth limit judgment site) nesting =
placed at nesting (printf "<spent limit=\"%d\" by=\"%s\" at=\"%s\"/>" limit (opened judgment) . escapeXML <$> located depth site)
elements at (EvDelta _ bytes) nesting = do
datum <- printed (ExBytes bytes)
let (naming, nesting') = stored (Right bytes) nesting
placed at nesting' (pure (printf "<dataized meta=\"%s\"%s>%s</dataized>" (escapeXML naming) (maybe "" (printf " number=\"%s\"" . escapeXML) (ieee bytes) :: String) (escapeXMLText datum)))
elements at (EvManufactured _ symbol) nesting = placed at nesting' (pure (printf "<manufactured meta=\"%s\">%s</manufactured>" (escapeXML naming) (sigma symbol)))
where
(naming, nesting') = stored (Left symbol) nesting
elements at (EvData _ spelling _ value) nesting =
placed at nesting $ case (found value nesting._held, value) of
(Just naming, _) -> pure (bound (escapeXMLText naming))
(Nothing, Left symbol) -> pure (bound (sigma symbol))
(Nothing, Right bytes) -> bound . escapeXMLText <$> lift (printed (ExBytes bytes))
where
bound :: String -> String
bound = printf "<bound meta=\"%s\">%s</bound>" (escapeXML (labelled nesting spelling))
elements at (EvTerm depth spelling _ _ term) nesting = placed at nesting (bind depth nesting spelling term)
elements at (EvBound depth spelling _ term) nesting = placed at nesting (bind depth nesting spelling term)
elements at (EvReshaped depth spelling _ _ term) nesting = placed at nesting (bind depth nesting spelling term)
elements at (EvSymbolized _ symbol bytes) nesting = do
datum <- printed (ExBytes bytes)
placed at nesting (pure (printf "<symbolized symbol=\"%s\"%s>%s</symbolized>" (sigma symbol) (maybe "" (printf " number=\"%s\"" . escapeXML) (ieee bytes) :: String) (escapeXMLText datum)))
elements at (EvMinted _ symbol operands) nesting = do
spelledOperands <- mapM spelled' operands
placed at nesting (pure (minted spelledOperands))
where
spelled' :: Either Int Bytes -> IO String
spelled' (Left fresh') = pure (sigma fresh')
spelled' (Right bytes) = printed (ExBytes bytes)
minted :: [String] -> String
minted [] = printf "<minted symbol=\"%s\"/>" (sigma symbol)
minted spelt = printf "<minted symbol=\"%s\">%s</minted>" (sigma symbol) (escapeXMLText (unwords spelt))
elements at (EvDeferred depth symbol judgment copy call site) nesting =
placed at nesting $ do
locator <- located depth site
origin <- maybe (sole depth copy) (called depth) call
pure (enclosed "deferred" (printf "<deferred symbol=\"%s\" by=\"%s\" at=\"%s\"" (sigma symbol) (opened judgment) (escapeXML locator)) origin)
elements at (EvApplied depth judgment call object site) nesting =
placed at nesting $ do
locator <- located depth site
(origin, given) <- parted depth call
(naming, aliased) <- counted
modify (first (\nesting' -> nesting'{_objects = namedInsert call aliased (namedInsert object aliased nesting'._objects)}))
pure (printf "<applied meta=\"%s\" by=\"%s\" at=\"%s\" of=\"%s\">%s</applied>" (escapeXML naming) (opened judgment) (escapeXML locator) (escapeXML origin) given)
elements at (EvBuilt depth term) nesting =
placed at nesting $ do
body <- leaf depth term
(naming, _) <- counted
modify (first (\nesting' -> nesting'{_sources = (depth, term, naming) : nesting'._sources}))
pure (printf "<built meta=\"%s\">%s</built>" (escapeXML naming) (escapeXMLText body))
elements at (EvAnswer depth term) nesting = placed at nesting (concluded "answered" depth term)
elements at (EvMorphed depth term) nesting = placed at nesting (concluded "morphed" depth term)
elements at (EvJoined _ condition symbol (left, right)) nesting =
placed at nesting $ do
on <- either (pure . labelled nesting) (lift . traced) condition
pure (printf "<joined symbol=\"%s\" on=\"%s\" left=\"%s\" right=\"%s\"/>" (sigma symbol) (escapeXML on) (sigma left) (sigma right))
elements at (EvDiverged depth left right path refusal) nesting =
placed at nesting $ do
one <- sided depth nesting left
two <- sided depth nesting right
pure (printf "<diverged left=\"%s\" right=\"%s\"%s reason=\"%s\"/>" (escapeXML one) (escapeXML two) (if null path then "" else printf " at=\"%s\"" (escapeXML (dotted path)) :: String) (escapeXML (refused refusal)))
elements at (EvFabricated depth left right term) nesting =
placed at nesting $ do
one <- sided depth nesting left
two <- sided depth nesting right
body <- leaf depth term
(naming, _) <- counted
pure (printf "<fabricated meta=\"%s\" left=\"%s\" right=\"%s\">%s</fabricated>" (escapeXML naming) (escapeXML one) (escapeXML two) (escapeXMLText body))
elements at (EvTaken depth _ term) nesting = placed at nesting (concluded "answered" depth term)
elements _ (EvWorld world) nesting = pure (known world nesting, [])
sided :: Int -> Nesting -> (Expression, Expression) -> Naming String
sided depth _ (_, term@(ExFormation [BiLambda (FnSymbol _)])) = leaf depth term
sided _ nesting (side@(ExMeta _), _) = labelled nesting . T.pack <$> lift (spelled side)
sided depth _ (_, term) = leaf depth term
outer :: Int -> Nesting -> Nesting
outer depth nesting = nesting{_openedAt = dropWhile ((>= depth) . fst) nesting._openedAt, _sources = dropWhile (\(level, _, _) -> level > depth) nesting._sources}
labelled :: Nesting -> T.Text -> String
labelled nesting spelling = printf "%s·%d" (T.unpack spelling) (opener nesting)
stored :: Either Int Bytes -> Nesting -> (String, Nesting)
stored value nesting = (naming, nesting{_datums = Map.insert (opener nesting) index nesting._datums, _held = Just (value, naming)})
where
index :: Int
index = maybe 1 (+ 1) (Map.lookup (opener nesting) nesting._datums)
naming :: String
naming = printf "%s·%d" (labelled nesting sigil) index
opener :: Nesting -> Int
opener nesting = maybe 0 snd (listToMaybe nesting._openedAt)
numbered :: Nesting -> (Int, Nesting)
numbered nesting = (index, nesting{_numbered = Map.insert (opener nesting) index nesting._numbered})
where
index :: Int
index = maybe 1 (+ 1) (Map.lookup (opener nesting) nesting._numbered)
counted :: Naming (String, Expression)
counted = do
(nesting, records) <- get
let (index, nesting') = numbered nesting
put (nesting', records)
pure (printf "%s·%d" (labelled nesting answer) index, alias (opener nesting) index)
recorded :: Expression -> (String -> String) -> Naming Expression
recorded term element = do
(naming, aliased) <- counted
modify (bimap (\nesting -> nesting{_objects = namedInsert term aliased nesting._objects}) (element (escapeXML naming) :))
pure aliased
opaque :: Expression -> Naming Expression
opaque term = do
(nesting, records) <- get
let meta = ExMeta (T.pack (printf "e%d" (nesting._opaque + 1)))
put (nesting{_opaque = nesting._opaque + 1, _objects = namedInsert term meta nesting._objects}, records)
pure meta
located :: Int -> Expression -> Naming String
located depth site = do
(nesting, _) <- get
maybe (leaf depth site) pure (listToMaybe [naming | (_, built, naming) <- nesting._sources, built == site])
concluded :: String -> Int -> Expression -> Naming String
concluded tag depth term = do
body <- leaf depth term
(naming, _) <- counted
pure (printf "<%s meta=\"%s\">%s</%s>" tag (escapeXML naming) (escapeXMLText body) tag)
bind :: Int -> Nesting -> T.Text -> Expression -> Naming String
bind depth nesting spelling term = printf "<bound meta=\"%s\">%s</bound>" (escapeXML (labelled nesting spelling)) . escapeXMLText <$> leaf depth term
leaf :: Int -> Expression -> Naming String
leaf depth term = titled depth term >>= lift . spelled
titled :: Int -> Expression -> Naming Expression
titled depth term = do
(nesting, _) <- get
maybe (coined depth term) pure (namedLookup term nesting._objects)
coined :: Int -> Expression -> Naming Expression
coined _ term@(ExFormation [BiLambda (FnSymbol _)]) = pure term
coined _ term@(ExFormation [BiDelta _]) = pure term
coined depth term@(ExFormation bds) = do
(nesting, _) <- get
maybe (anonymous depth term bds) (cloned depth term) (copied nesting bds)
coined depth term@(ExDispatch inner attr) = do
head' <- titled depth inner
if pathed head' then pure (ExDispatch head' attr) else opaque term
coined depth term@(ExApplication _ _) = do
(nesting, _) <- get
let (head', arguments) = chained nesting._objects term
origin <- leaf depth head'
given <- mapM (argument depth) arguments
recorded term (\naming -> printf "<applied meta=\"%s\" of=\"%s\">%s</applied>" naming (escapeXML origin) (concat given))
coined _ term
| plain term = pure term
| otherwise = opaque term
cloned :: Int -> Expression -> (Expression, [(Attribute, Expression)]) -> Naming Expression
cloned depth term (origin, []) = do
path <- titled depth origin
modify (first (\nesting -> nesting{_objects = namedInsert term path nesting._objects}))
pure path
cloned depth term (origin, bound) = do
path <- leaf depth origin
given <- mapM (\(attr, value) -> titled depth value >>= lift . attributed (printAttribute attr)) bound
recorded term (\naming -> printf "<formed meta=\"%s\" of=\"%s\">%s</formed>" naming (escapeXML path) (concat given))
anonymous :: Int -> Expression -> [Binding] -> Naming Expression
anonymous depth term bds = do
parts <- mapM (part depth) bds
let marks = concat (lefts parts)
inner = concat (rights parts)
recorded term (\naming -> if null inner then printf "<formed meta=\"%s\"%s/>" naming marks else printf "<formed meta=\"%s\"%s>%s</formed>" naming marks inner)
part :: Int -> Binding -> Naming (Either String String)
part depth (BiTau attr value) = Right <$> (titled depth value >>= lift . attributed (printAttribute attr))
part _ (BiVoid attr) = pure (Right (printf "<void attr=\"%s\"/>" (escapeXML (printAttribute attr))))
part _ (BiLambda func) = pure (Left (printf " λ=\"%s\"" (escapeXML (printFunction func))))
part _ (BiDelta bytes) = Left . printf " Δ=\"%s\"" . escapeXML <$> lift (printed (ExBytes bytes))
part _ _ = pure (Right "")
argument :: Int -> Argument -> Naming String
argument depth (ArTau attr value) = titled depth value >>= lift . attributed (printAttribute attr)
argument depth (ArAlpha alpha value) = titled depth value >>= lift . attributed (printAlpha alpha)
attributed :: String -> Expression -> IO String
attributed name ExTermination = pure (printf "<bottom attr=\"%s\"/>" (escapeXML name))
attributed name value = tagged (kind value) . escapeXMLText <$> spelled value
where
tagged :: String -> String -> String
tagged tag text = printf "<%s attr=\"%s\">%s</%s>" tag (escapeXML name) text tag
kind :: Expression -> String
kind (ExFormation [BiLambda (FnSymbol _)]) = "symbol"
kind (ExFormation [BiDelta _]) = "datum"
kind (ExBytes _) = "datum"
kind (ExMeta _) = "meta"
kind _ = "ref"
chained :: Named Expression -> Expression -> (Expression, [Argument])
chained objects (ExApplication inner arg) = case inner of
ExApplication _ _ | Nothing <- namedLookup inner objects -> second (++ [arg]) (chained objects inner)
_ -> (inner, [arg])
chained _ term = (term, [])
parted :: Int -> Expression -> Naming (String, String)
parted depth (ExApplication term arg) = (,) <$> leaf depth term <*> argument depth arg
parted depth term = (,"") <$> leaf depth term
called :: Int -> Expression -> Naming (String, String)
called depth term = do
path <- leaf depth object
given <- mapM passed arguments
pure (path, printf "<with>%s</with>" (concat given))
where
object :: Expression
arguments :: [(Attribute, Expression)]
(object, arguments) = invoked term
sole :: Int -> Expression -> Naming (String, String)
sole depth term = (,"") <$> leaf depth term
enclosed :: String -> String -> (String, String) -> String
enclosed _ opening (origin, "") = printf "%s of=\"%s\"/>" opening (escapeXML origin)
enclosed tag opening (origin, inner) = printf "%s of=\"%s\">%s</%s>" opening (escapeXML origin) inner tag
invoked :: Expression -> (Expression, [(Attribute, Expression)])
invoked (ExApplication term (ArTau attr value)) = second (++ [(attr, value)]) (invoked term)
invoked term = (term, [])
passed :: (Attribute, Expression) -> Naming String
passed (attr, value) = valued value >>= lift . attributed (printAttribute attr)
valued :: Expression -> Naming Expression
valued value = do
(nesting, _) <- get
case (namedLookup value nesting._objects, symbolic value) of
(Just aliased, _) -> pure aliased
(Nothing, Just symbol) -> pure (standing symbol)
(Nothing, Nothing) -> opaque value
symbolic :: Expression -> Maybe Int
symbolic (ExFormation [BiLambda (FnSymbol idx)]) = Just idx
symbolic (ExFormation bds) = listToMaybe [body | BiTau AtPhi body <- bds] >>= symbolic
symbolic (ExApplication _ (ArTau AtPhi body)) = symbolic body
symbolic _ = Nothing
spelled :: Expression -> IO String
spelled (ExFormation [BiLambda (FnSymbol idx)]) = pure (sigma idx)
spelled (ExFormation [BiDelta bytes]) = printed (ExBytes bytes)
spelled (ExDispatch inner attr)
| sigmatic inner = printf "%s.%s" <$> spelled inner <*> pure (printAttribute attr)
spelled term = printed term
traced :: Expression -> IO String
traced ExXi = pure "ξ"
traced (ExDispatch inner attr) = printf "%s.%s" <$> traced inner <*> pure (printAttribute attr)
traced term = printed term
sigmatic :: Expression -> Bool
sigmatic (ExFormation [BiLambda (FnSymbol _)]) = True
sigmatic (ExDispatch inner _) = sigmatic inner
sigmatic _ = False
known :: Expression -> Nesting -> Nesting
known world@(ExFormation bds) nesting = nesting{_world = world', _shapes = Map.fromListWith (flip (++)) [(shape kept, [(path, kept)]) | (path, kept) <- breadth [(ExDispatch ExRoot attr, inner) | (attr, ExFormation inner) <- Map.toList world']], _objects = namedInsert world ExRoot nesting._objects}
where
world' :: Map.Map Attribute Expression
world' = Map.union (Map.fromList [(attr, value) | BiTau attr value <- bds]) nesting._world
breadth :: [(Expression, [Binding])] -> [(Expression, [Binding])]
breadth [] = []
breadth level = [(path, filter (not . rho) inner) | (path, inner) <- level] ++ breadth [(ExDispatch path attr, inner') | (path, inner) <- level, BiTau attr (ExFormation inner') <- inner, attr /= AtRho]
known _ nesting = nesting
shape :: [Binding] -> [Maybe Attribute]
shape = map label
where
label :: Binding -> Maybe Attribute
label (BiTau attr _) = Just attr
label (BiVoid attr) = Just attr
label _ = Nothing
rho :: Binding -> Bool
rho (BiTau AtRho _) = True
rho (BiVoid AtRho) = True
rho _ = False
copied :: Nesting -> [Binding] -> Maybe (Expression, [(Attribute, Expression)])
copied nesting bds = snd <$> listToMaybe (sortOn fst (mapMaybe matched candidates))
where
own :: [Binding]
own = filter (not . rho) bds
parents :: [Expression]
parents = [value | BiTau AtRho value <- bds]
candidates :: [(Expression, [Binding])]
candidates = case parents of
[] -> Map.findWithDefault [] (shape own) nesting._shapes
value : _ -> [(ExDispatch value attr, filter (not . rho) origin) | BiTau attr (ExFormation origin) <- fromMaybe [] (declared value), attr /= AtRho]
matched :: (Expression, [Binding]) -> Maybe (Int, (Expression, [(Attribute, Expression)]))
matched (path, kept) = do
guard (length kept == length own)
given <- concat <$> zipWithM bound kept own
guard (or (zipWith (==) kept own) || not (null parents) && length given == length [() | BiVoid _ <- kept])
pure (length given, (path, given))
declared :: Expression -> Maybe [Binding]
declared ExRoot = Just [BiTau attr value | (attr, value) <- Map.toList nesting._world]
declared (ExFormation inner) = Just inner
declared (ExApplication target _) = declared target
declared (ExDispatch target attr) = do
outer' <- declared target
listToMaybe [inner | BiTau attr' (ExFormation inner) <- outer', attr' == attr]
declared _ = Nothing
bound :: Binding -> Binding -> Maybe [(Attribute, Expression)]
bound (BiVoid attr) (BiTau attr' value)
| attr == attr' = Just [(attr, value)]
bound (BiTau attr origin) (BiTau attr' value)
| attr == attr' = Just [(attr, value) | origin /= value]
bound origin binding
| origin == binding = Just []
| otherwise = Nothing
pathed :: Expression -> Bool
pathed ExRoot = True
pathed ExXi = True
pathed (ExMeta _) = True
pathed (ExDispatch inner _) = pathed inner
pathed term = sigmatic term
plain :: Expression -> Bool
plain ExRoot = True
plain ExXi = True
plain ExTermination = True
plain (ExMeta _) = True
plain (ExBytes _) = True
plain _ = False
sigma :: Int -> String
sigma = printFunction . FnSymbol
quoted :: T.Text -> String
quoted = escapeXML . T.unpack
endEvalXml :: Handle -> IORef Nesting -> Double -> IO ()
endEvalXml handle cursor began = do
nesting <- readIORef cursor
mapM_ (hPutStrLn handle) (snd (closed 0 nesting._closing))
unless (null nesting._closing) $ do
now <- getMonotonicTime
let taken = milliseconds began now
hPutStrLn handle (indentedXml 0 (printf "<msec>%d</msec>" taken))
hPutStrLn handle (indentedXml 0 (printf "<firings>%d</firings>" nesting._fires))
hPutStrLn handle (indentedXml 0 (printf "<fps>%d</fps>" (perSecond nesting._fires taken)))
hPutStrLn handle "</protocol>"
writeIORef cursor nesting{_closing = []}
closed :: Int -> [(Int, String)] -> ([(Int, String)], [String])
closed depth open = (kept, [indentedXml level (printf "</%s>" element) | (level, element) <- shut])
where
(shut, kept) = span ((>= depth) . fst) open
indented :: Int -> String -> String
indented depth line = replicate (2 * depth) ' ' ++ line
indentedXml :: Int -> String -> String
indentedXml depth = indented (depth + 1)
atomicModify :: IORef a -> (a -> IO (a, b)) -> IO b
atomicModify ref action = readIORef ref >>= action >>= \(value, made) -> writeIORef ref value >> pure made
standing :: Int -> Expression
standing symbol = ExFormation [BiLambda (FnSymbol symbol)]
answer :: T.Text
answer = "𝑛"
sigil :: T.Text
sigil = "𝛿"
found :: Either Int Bytes -> Maybe (Either Int Bytes, String) -> Maybe String
found value (Just (held, naming))
| value == held = Just naming
found _ _ = Nothing
alias :: Int -> Int -> Expression
alias firing index = ExMeta (T.pack (printf "n·%d·%d" firing index))
dontSaveEval :: SaveEvalFunc
dontSaveEval _ = pure ()
data Progress = Progress
{ _began :: Double
, _told :: Maybe Double
, _formations :: Int
, _firings :: Int
}
emptyProgress :: Double -> Progress
emptyProgress began = Progress began Nothing 0 0
progressed :: IORef Progress -> Double -> (Expression -> IO String) -> SaveEvalFunc -> SaveEvalFunc
progressed cursor interval render record evaluation = do
record evaluation
mapM_ reported (sited evaluation)
where
sited :: Evaluation -> Maybe (Progress -> Progress, Expression)
sited (EvFiring _ _ _ site) = Just (\progress -> progress{_firings = progress._firings + 1}, site)
sited (EvStarted _ Dataization site) = Just (\progress -> progress{_formations = progress._formations + 1}, site)
sited _ = Nothing
reported :: (Progress -> Progress, Expression) -> IO ()
reported (counted, site) = do
now <- getMonotonicTime
progress <- counted <$> readIORef cursor
if maybe True (\told -> now - told >= interval) progress._told
then do
locator <- render site
logInfo (printf "Started %d dataizations and fired %d λ functions in %.0fs, now at %s" progress._formations progress._firings (now - progress._began) locator)
writeIORef cursor progress{_told = Just now}
else writeIORef cursor progress