phino-0.0.113: src/Deps.hs
-- SPDX-FileCopyrightText: Copyright (c) 2025 Objectionary.com
-- SPDX-License-Identifier: MIT
-- The main goal of this module is breaking cyclic dependency:
-- Dataize -> Functions -> Rewriter -> Dataize
-- Here we provide custom type BuildTermFunc and add it to
-- RewriteContext and DataizeContext. Now Dataize and Rewrite depends
-- only on Term module. This allows us to use Rewriter and Dataize in
-- Functions module because Rewriter does not depend on Functions anymore.
module Deps where
import AST
import Data.List (intercalate)
import qualified Data.Text as T
import Logger (logDebug)
import Matcher
import System.Directory (createDirectoryIfMissing)
import System.FilePath
import System.IO (Handle, hPutStrLn)
import Text.Printf (printf)
import Yaml
data Term
= TeExpression Expression
| TeAttribute Attribute
| TeBytes Bytes
| TeBindings [Binding]
type BuildTermMethod = [ExtraArgument] -> Subst -> IO Term
-- The state π threaded through the Morphing π(n, e, s), Dataization π»(n, e, s)
-- and Evaluation πΌ(b, s) functions. The calculus does not yet fix what a state
-- is, so it is a plain string for now. Unlike the universe π, which is immutable
-- and threaded unchanged, the state is mutable: πΌ takes a state π 1 and returns a
-- new one π 2, and π/π» propagate that change to their callers. Only the rules
-- that fire an atom β 'ml' (morphing) and 'fire' (dataization) β can change
-- the state; every other rule threads it through untouched.
type State = String
-- Like 'BuildTermMethod', but it also takes the incoming state and returns the
-- new state alongside the term. Lives here next to 'BuildTermMethod' so the two
-- stay together.
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
writeFile path content
logDebug (printf "Saved step '%d' to '%s'" step path)
dontSaveStep :: SaveStepFunc
dontSaveStep = saveStep Nothing "" (\_ -> pure "") 0
-- One firing of an atom, the way the Evaluation function πΌ sees it: the name of
-- the Ξ» function, the formation it fired against with the Ξ» binding removed, and
-- the term it produced.
data Evaluation = Evaluation
{ _function :: T.Text
, _arguments :: Expression
, _result :: Expression
}
type SaveEvalFunc = Evaluation -> IO ()
-- Append one evaluation to the protocol as a single tab-separated line: the Ξ»
-- function name, its argument formation and its result. Both expressions are
-- rendered by the caller, which flattens them, so a record never spills over
-- more than one line. The handle stays open for the whole run, since a run may
-- fire thousands of atoms and reopening the file for each of them buys nothing.
saveEval :: Handle -> (Expression -> IO String) -> SaveEvalFunc
saveEval handle render (Evaluation func bindings outcome) = do
rendered <- mapM render [bindings, outcome]
hPutStrLn handle (intercalate "\t" (T.unpack func : rendered))
logDebug (printf "Saved the evaluation of '%s'" (T.unpack func))
dontSaveEval :: SaveEvalFunc
dontSaveEval _ = pure ()