eo-phi-normalizer-0.3.1: src/Language/EO/Phi/Dataize.hs
{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE QuasiQuotes #-}
{-# LANGUAGE TupleSections #-}
{-# LANGUAGE ViewPatterns #-}
{-# OPTIONS_GHC -Wno-unrecognised-pragmas #-}
{-# HLINT ignore "Redundant fmap" #-}
module Language.EO.Phi.Dataize where
import Control.Arrow (ArrowChoice (left))
import Data.List (singleton)
import Data.Maybe (listToMaybe)
import Data.String.Interpolate (i)
import Language.EO.Phi (Binding (DeltaEmptyBinding, EmptyBinding))
import Language.EO.Phi.Rules.Common
import Language.EO.Phi.Syntax.Abs (
AlphaIndex (AlphaIndex),
Attribute (Alpha, Phi, Rho),
Binding (AlphaBinding, DeltaBinding, LambdaBinding),
Bytes (Bytes),
Function (Function),
Object (Application, Formation, ObjectDispatch, Termination),
)
-- | Perform one step of dataization to the object (if possible).
dataizeStep :: Context -> Object -> (Context, Either Object Bytes)
dataizeStep ctx obj = snd $ head $ runChain (dataizeStepChain obj) ctx -- FIXME: head is bad
dataizeStep' :: Context -> Object -> Either Object Bytes
dataizeStep' ctx obj = snd (dataizeStep ctx obj)
-- | State of evaluation is not needed yet, but it might be in the future
type EvaluationState = ()
-- | Recursively perform normalization and dataization until we get bytes in the end.
dataizeRecursively :: Context -> Object -> Either Object Bytes
dataizeRecursively ctx obj = snd $ dataizeRecursivelyChain' ctx obj
dataizeStepChain' :: Context -> Object -> ([(String, Either Object Bytes)], Either Object Bytes)
dataizeStepChain' ctx obj = snd <$> head (runChain (dataizeStepChain obj) ctx) -- FIXME: head is bad
-- | Perform one step of dataization to the object (if possible), reporting back individiual steps.
dataizeStepChain :: Object -> DataizeChain (Context, Either Object Bytes)
dataizeStepChain obj@(Formation bs)
| Just (DeltaBinding bytes) <- listToMaybe [b | b@(DeltaBinding _) <- bs]
, not hasEmpty = do
logStep "Found bytes" (Right bytes)
ctx <- getContext
return (ctx, Right bytes)
| Just (LambdaBinding (Function funcName)) <- listToMaybe [b | b@(LambdaBinding _) <- bs]
, not hasEmpty = do
logStep ("Evaluating lambda '" <> funcName <> "'") (Left obj)
(obj', _state) <- evaluateBuiltinFunChain funcName obj ()
ctx <- getContext
return (ctx, Left obj')
| Just (AlphaBinding Phi decoratee) <- listToMaybe [b | b@(AlphaBinding Phi _) <- bs]
, not hasEmpty = do
logStep "Dataizing inside phi" (Left decoratee)
ctx <- getContext
let extendedContext = (extendContextWith obj ctx){currentAttr = Phi}
logStep "Dataizing inside phi" (Left decoratee)
withContext extendedContext $ dataizeStepChain decoratee
| otherwise = do
logStep "No change to formation" (Left obj)
ctx <- getContext
return (ctx, Left obj)
where
isEmpty (EmptyBinding _) = True
isEmpty DeltaEmptyBinding = True
isEmpty _ = False
hasEmpty = any isEmpty bs
dataizeStepChain (Application obj bindings) = do
logStep "Dataizing inside application" (Left obj)
modifyContext (\c -> c{dataizePackage = False}) $ do
(ctx, obj') <- dataizeStepChain obj
return (ctx, left (`Application` bindings) obj')
dataizeStepChain (ObjectDispatch obj attr) = do
logStep "Dataizing inside dispatch" (Left obj)
modifyContext (\c -> c{dataizePackage = False}) $ do
(ctx, obj') <- dataizeStepChain obj
return (ctx, left (`ObjectDispatch` attr) obj')
dataizeStepChain obj = do
logStep "Nothing to dataize" (Left obj)
ctx <- getContext
return (ctx, Left obj)
dataizeRecursivelyChain' :: Context -> Object -> ([(String, Either Object Bytes)], Either Object Bytes)
dataizeRecursivelyChain' ctx obj = head (runChain (dataizeRecursivelyChain obj) ctx)
-- | Recursively perform normalization and dataization until we get bytes in the end,
-- reporting intermediate steps
dataizeRecursivelyChain :: Object -> DataizeChain (Either Object Bytes)
dataizeRecursivelyChain obj = do
ctx <- getContext
msplit (transformNormLogs (applyRulesChain obj)) >>= \case
Nothing -> do
logStep "No rules applied" (Left obj)
return (Left obj)
-- We trust that all chains lead to the same result due to confluence
Just (normObj, _alternatives) -> do
(ctx', step) <- dataizeStepChain normObj
case step of
(Left stillObj)
| stillObj == normObj && ctx `sameContext` ctx' -> return step -- dataization changed nothing
| otherwise -> withContext ctx' $ dataizeRecursivelyChain stillObj -- partially dataized
bytes -> return bytes
-- | Given normalization context, a function on data (bytes interpreted as integers), an object,
-- and the current state of evaluation, returns the new object and a possibly modified state along with intermediate steps.
evaluateDataizationFunChain :: (Int -> Int -> Int) -> Object -> EvaluationState -> DataizeChain (Object, EvaluationState)
evaluateDataizationFunChain func obj _state = do
let o_rho = ObjectDispatch obj Rho
let o_a0 = ObjectDispatch obj (Alpha (AlphaIndex "α0"))
logStep "Evaluating LHS" (Left o_rho)
lhs <- dataizeRecursivelyChain o_rho
logStep "Evaluating RHS" (Left o_a0)
rhs <- dataizeRecursivelyChain o_a0
result <- case (lhs, rhs) of
(Right l, Right r) -> do
let (Bytes bytes) = intToBytes (bytesToInt r `func` bytesToInt l)
resultObj = [i|Φ.org.eolang.float(Δ ⤍ #{bytes})|]
logStep "Evaluated function" (Left resultObj)
return resultObj
_ -> do
logStep "Couldn't find bytes in one or both of LHS and RHS" (Left Termination)
return Termination
return (result, ())
-- | Like `evaluateDataizationFunChain` but specifically for the built-in functions.
-- This function is not safe. It returns undefined for unknown functions
evaluateBuiltinFunChain :: String -> Object -> EvaluationState -> DataizeChain (Object, EvaluationState)
evaluateBuiltinFunChain "Plus" obj = evaluateDataizationFunChain (+) obj
evaluateBuiltinFunChain "Times" obj = evaluateDataizationFunChain (*) obj
evaluateBuiltinFunChain "Package" (Formation bindings) = do
\state -> do
fmap dataizePackage getContext >>= \case
True -> do
let (packageBindings, restBindings) = span isPackage bindings
bs <- mapM dataizeBindingChain restBindings
logStep "Dataized 'Package' siblings" (Left $ Formation (bs ++ packageBindings))
return (Formation (bs ++ packageBindings), state)
False ->
return (Formation bindings, state)
where
isPackage (LambdaBinding (Function "Package")) = True
isPackage _ = False
dataizeBindingChain (AlphaBinding attr o) = do
dataizationResult <- dataizeRecursivelyChain o
return (AlphaBinding attr (either id (Formation . singleton . DeltaBinding) dataizationResult))
dataizeBindingChain b = return b
evaluateBuiltinFunChain _ obj = \state -> return (obj, state)
-- | Like `evaluateDataizationFun` but specifically for the built-in functions.
-- This function is not safe. It returns undefined for unknown functions
evaluateBuiltinFun :: Context -> String -> Object -> EvaluationState -> (Object, EvaluationState)
evaluateBuiltinFun ctx name obj state = snd $ head $ runChain (evaluateBuiltinFunChain name obj state) ctx -- FIXME: head is bad