packages feed

phino 0.0.0.67 → 0.0.0.68

raw patch · 35 files changed

+743/−366 lines, 35 filesdep ~basedep ~timePVP: major bump suggested

API removals or changes: PVP suggests a major version bump

Dependency ranges changed: base, time

API changes (from Hackage documentation)

- LaTeX: instance LaTeX.ToLaTeX GHC.Base.String
- Render: instance Render.Render GHC.Num.Integer.Integer
- Render: instance Render.Render GHC.Types.Double
+ CLI.Parsers: cliArgsParser :: Parser CliArgs
+ CLI.Parsers: optPin :: Parser (Maybe String)
+ CLI.Types: CliArgs :: Maybe String -> Command -> CliArgs
+ CLI.Types: VersionMismatch :: String -> String -> CmdException
+ CLI.Types: [_command] :: CliArgs -> Command
+ CLI.Types: [_pin] :: CliArgs -> Maybe String
+ CLI.Types: [_update] :: OptsRewrite -> Bool
+ CLI.Types: data CliArgs
+ LaTeX: instance LaTeX.ToLaTeX Data.Text.Internal.Text
+ Render: instance Render.Render Data.Text.Internal.Text
- AST: AtLabel :: String -> Attribute
+ AST: AtLabel :: Text -> Attribute
- AST: AtMeta :: String -> Attribute
+ AST: AtMeta :: Text -> Attribute
- AST: BiLambda :: String -> Binding
+ AST: BiLambda :: Text -> Binding
- AST: BiMeta :: String -> Binding
+ AST: BiMeta :: Text -> Binding
- AST: BiMetaLambda :: String -> Binding
+ AST: BiMetaLambda :: Text -> Binding
- AST: BtMeta :: String -> Bytes
+ AST: BtMeta :: Text -> Bytes
- AST: ExMeta :: String -> Expression
+ AST: ExMeta :: Text -> Expression
- AST: ExMetaTail :: Expression -> String -> Expression
+ AST: ExMetaTail :: Expression -> Text -> Expression
- CLI.Parsers: parserInfo :: ParserInfo Command
+ CLI.Parsers: parserInfo :: ParserInfo CliArgs
- CLI.Types: OptsRewrite :: LogLevel -> Int -> IOFormat -> IOFormat -> SugarType -> LineFormat -> Must -> Bool -> Bool -> Bool -> Bool -> Bool -> Bool -> Bool -> Bool -> Bool -> Bool -> Int -> Int -> Int -> Maybe Int -> Maybe Int -> [FilePath] -> [String] -> [String] -> String -> String -> Maybe String -> Maybe String -> Maybe String -> Maybe String -> Maybe FilePath -> Maybe FilePath -> Maybe FilePath -> OptsRewrite
+ CLI.Types: OptsRewrite :: LogLevel -> Int -> IOFormat -> IOFormat -> SugarType -> LineFormat -> Must -> Bool -> Bool -> Bool -> Bool -> Bool -> Bool -> Bool -> Bool -> Bool -> Bool -> Bool -> Int -> Int -> Int -> Maybe Int -> Maybe Int -> [FilePath] -> [String] -> [String] -> String -> String -> Maybe String -> Maybe String -> Maybe String -> Maybe String -> Maybe FilePath -> Maybe FilePath -> Maybe FilePath -> OptsRewrite
- CST: AT_LABEL :: String -> ATTRIBUTE
+ CST: AT_LABEL :: Text -> ATTRIBUTE
- CST: META :: EXCLAMATION -> META_HEAD -> String -> META
+ CST: META :: EXCLAMATION -> META_HEAD -> Text -> META
- CST: PA_LAMBDA :: String -> PAIR
+ CST: PA_LAMBDA :: Text -> PAIR
- CST: PA_LAMBDA' :: String -> PAIR
+ CST: PA_LAMBDA' :: Text -> PAIR
- CST: [label] :: ATTRIBUTE -> String
+ CST: [label] :: ATTRIBUTE -> Text
- CST: [rest] :: META -> String
+ CST: [rest] :: META -> Text
- CST: metaTail :: String -> String
+ CST: metaTail :: Text -> Text
- Matcher: MvFunction :: String -> MetaValue
+ Matcher: MvFunction :: Text -> MetaValue
- Matcher: Subst :: Map String MetaValue -> Subst
+ Matcher: Subst :: Map Text MetaValue -> Subst
- Matcher: substSingle :: String -> MetaValue -> Subst
+ Matcher: substSingle :: Text -> MetaValue -> Subst
- Misc: matchDataObject :: Expression -> Maybe (String, Bytes)
+ Misc: matchDataObject :: Expression -> Maybe (Text, Bytes)
- Misc: pattern BaseObject :: String -> Expression
+ Misc: pattern BaseObject :: Text -> Expression
- Misc: pattern DataObject :: String -> Bytes -> Expression
+ Misc: pattern DataObject :: Text -> Bytes -> Expression
- Render: render :: Render a => a -> String
+ Render: render :: Render a => a -> Text
- Render: renderFunc :: Render a => String -> a -> String
+ Render: renderFunc :: Render a => Text -> a -> Text

Files

README.md view
@@ -1,3 +1,8 @@+<!--+SPDX-FileCopyrightText: Copyright (c) 2025 Objectionary.com+SPDX-License-Identifier: MIT+-->+ # Command-Line Manipulator of 𝜑-Calculus Expressions  [![DevOps By Rultor.com](https://www.rultor.com/b/objectionary/phino)](https://www.rultor.com/p/objectionary/phino)@@ -29,7 +34,7 @@  ```bash cabal update-cabal install --overwrite-policy=always phino-0.0.0.66+cabal install --overwrite-policy=always phino-0.0.0.67 phino --version ``` @@ -78,6 +83,14 @@ 0.0.0.0 ``` +You can ensure scripts are run with a specific version of `phino` using+the `--pin` global option. It exits with an error when the version supplied+doesn't match the installed one:++```bash+phino --pin=0.0.0.67 dataize hello.phi+```+ ## Dataize  Then, you dataize the program:@@ -339,6 +352,81 @@ Incorrect usage of meta variables in 𝜑-expression patterns leads to parsing errors. +## Benchmark++To run performance benchmarks, you need [Java 8+][java] and [curl][curl].+Maven is downloaded automatically on first run via `benchmark/mvnw`.++The benchmark uses the compiled [`Native`][jna-native] class from+[JNA][jna] — a large real-world Java class — as its test input.+On first run, `make bench` downloads the class, disassembles it to+[XMIR][xmir] via [jeo-maven-plugin][jeo], converts it to 𝜑 using+`phino rewrite`, and caches the results in `benchmark/tmp/`.+Subsequent runs skip straight to the benchmarks.++```bash+make bench+```++<!-- benchmark_begin -->++```text+=== parse/phi ===+  warmup:     3 iterations+  batches:    10 x 1+  total:      1031210.405 μs+  avg:        103121.040 μs+  min:        93623.015 μs+  max:        126930.202 μs+  std dev:    13077.810 μs+=== parse/xmir ===+  warmup:     3 iterations+  batches:    10 x 1+  total:      6217322.833 μs+  avg:        621732.283 μs+  min:        554694.491 μs+  max:        755833.179 μs+  std dev:    55468.859 μs+=== rewrite/normalize ===+  warmup:     3 iterations+  batches:    10 x 1+  total:      312902.089 μs+  avg:        31290.209 μs+  min:        29879.534 μs+  max:        34353.613 μs+  std dev:    1360.426 μs+=== print/sweet/multiline ===+  warmup:     3 iterations+  batches:    10 x 1+  total:      3650499.458 μs+  avg:        365049.946 μs+  min:        336866.083 μs+  max:        392609.052 μs+  std dev:    14447.496 μs+=== print/sweet/flat ===+  warmup:     3 iterations+  batches:    10 x 1+  total:      3666297.385 μs+  avg:        366629.738 μs+  min:        342205.526 μs+  max:        380537.332 μs+  std dev:    10914.667 μs+=== print/salty/multiline ===+  warmup:     3 iterations+  batches:    10 x 1+  total:      11397878.380 μs+  avg:        1139787.838 μs+  min:        1105293.829 μs+  max:        1179067.130 μs+  std dev:    23579.734 μs+```++The results were calculated in [this GHA job][benchmark-gha]+on 2026-05-17 at 21:49,+on Linux with 4 CPUs.++<!-- benchmark_end -->+ ## How to Contribute  Fork repository, make changes, then send us a [pull request][guidelines].@@ -365,3 +453,9 @@ [guidelines]: https://www.yegor256.com/2014/04/15/github-guidelines.html [xmir]: https://news.eolang.org/2022-11-25-xmir-guide.html [latex]: https://en.wikipedia.org/wiki/LaTeX+[java]: https://www.java.com/en/download/+[curl]: https://curl.se/+[jna]: https://github.com/java-native-access/jna+[jna-native]: https://github.com/java-native-access/jna/blob/master/src/com/sun/jna/Native.java+[jeo]: https://github.com/objectionary/jeo-maven-plugin+[benchmark-gha]: https://github.com/objectionary/phino/actions/runs/26003582110
+ benchmark/Main.hs view
@@ -0,0 +1,102 @@+-- SPDX-FileCopyrightText: Copyright (c) 2025 Objectionary.com+-- SPDX-License-Identifier: MIT++module Main where++import AST (Expression (ExGlobal))+import Control.Exception (evaluate)+import Control.Monad (replicateM, replicateM_)+import Data.Time.Clock+import Deps (dontSaveStep)+import Encoding (Encoding (UNICODE))+import Functions (buildTerm)+import Lining (LineFormat (MULTILINE, SINGLELINE))+import Margin (defaultMargin)+import Must (Must (MtDisabled))+import Parser (parseProgramThrows)+import Printer (printProgram')+import Rewriter (RewriteContext (RewriteContext), rewrite)+import Sugar (SugarType (SALTY, SWEET))+import Text.Printf (printf)+import XMIR (parseXMIRThrows, xmirToPhi)+import Yaml (normalizationRules)++warmups :: Int+warmups = 3++iterations :: Int+iterations = 10++targetBatchMs :: Double+targetBatchMs = 20.0++rewriteCtx :: RewriteContext+rewriteCtx =+  RewriteContext+    ExGlobal+    100+    100+    False+    buildTerm+    MtDisabled+    Nothing+    dontSaveStep++timeAction :: IO a -> IO Double+timeAction action = do+  start <- getCurrentTime+  _ <- evaluate =<< action+  end <- getCurrentTime+  pure (realToFrac (diffUTCTime end start) * 1e6)++timeBatch :: Int -> IO a -> IO Double+timeBatch batch action = do+  start <- getCurrentTime+  replicateM_ batch (evaluate =<< action)+  end <- getCurrentTime+  pure (realToFrac (diffUTCTime end start) * 1e6 / fromIntegral batch)++calibrate :: IO a -> IO Int+calibrate action = do+  t <- timeAction action+  pure (max 1 (round (targetBatchMs * 1000.0 / t)))++stdDev :: [Double] -> Double -> Double+stdDev xs avg = sqrt (sum (map (\x -> (x - avg) ^ (2 :: Int)) xs) / fromIntegral (length xs))++runBench :: String -> IO a -> IO ()+runBench name action = do+  replicateM_ warmups action+  batch <- calibrate action+  times <- replicateM iterations (timeBatch batch action)+  let total = sum times * fromIntegral batch+      avg = sum times / fromIntegral iterations+      mn = minimum times+      mx = maximum times+      sd = stdDev times avg+  putStrLn $ "=== " ++ name ++ " ==="+  putStrLn $ printf "  warmup:     %d iterations" warmups+  putStrLn $ printf "  batches:    %d x %d" iterations batch+  putStrLn $ printf "  total:      %.3f μs" total+  putStrLn $ printf "  avg:        %.3f μs" avg+  putStrLn $ printf "  min:        %.3f μs" mn+  putStrLn $ printf "  max:        %.3f μs" mx+  putStrLn $ printf "  std dev:    %.3f μs" sd++main :: IO ()+main = do+  src <- readFile "benchmark/tmp/native.phi"+  xsrc <- readFile "benchmark/tmp/Native.xmir"+  prog <- parseProgramThrows src+  runBench "parse/phi" (parseProgramThrows src)+  runBench "parse/xmir" (parseXMIRThrows xsrc >>= xmirToPhi)+  runBench "rewrite/normalize" (rewrite prog normalizationRules rewriteCtx)+  runBench+    "print/sweet/multiline"+    (evaluate (length (printProgram' prog (SWEET, UNICODE, MULTILINE, defaultMargin))))+  runBench+    "print/sweet/flat"+    (evaluate (length (printProgram' prog (SWEET, UNICODE, SINGLELINE, defaultMargin))))+  runBench+    "print/salty/multiline"+    (evaluate (length (printProgram' prog (SALTY, UNICODE, MULTILINE, defaultMargin))))
phino.cabal view
@@ -1,6 +1,6 @@ cabal-version: 3.0 name: phino-version: 0.0.0.67+version: 0.0.0.68 license: MIT synopsis: Command-Line Manipulator of 𝜑-Calculus Expressions description: Please see the README on GitHub at <https://github.com/objectionary/phino#readme>@@ -93,7 +93,7 @@     regex-pcre-builtin >=0.95.2 && <0.96,     scientific >=0.3.7 && <0.4,     text >=2.0.2 && <2.2,-    time >=1.12 && <1.16,+    time >=1.12 && <1.17,     utf8-string >=1.0.2 && <1.1,     vector >=0.13.0 && <0.14,     xml-conduit >=1.9 && <1.11,@@ -166,11 +166,24 @@     process >=1.6.17 && <1.7,     silently >=1.2.5 && <1.3,     text >=2.0.2 && <2.2,-    time >=1.12 && <1.16,+    time >=1.12 && <1.17,     xml-conduit >=1.9 && <1.11,     yaml >=0.11.8 && <0.12,    build-tool-depends:     hspec-discover:hspec-discover >=2.11.0 && <2.12++  default-language: Haskell2010++-- Benchmark suite+benchmark bench+  import: warnings+  type: exitcode-stdio-1.0+  main-is: Main.hs+  hs-source-dirs: benchmark+  build-depends:+    base >=4.18.3.0 && <5,+    phino,+    time >=1.12 && <1.17,    default-language: Haskell2010
src/AST.hs view
@@ -6,6 +6,8 @@ -- This module represents AST tree for parsed phi-calculus program module AST where +import Data.Text (Text)+import qualified Data.Text as T import GHC.Generics (Generic)  newtype Program = Program Expression@@ -18,8 +20,8 @@   | ExTermination   | ExApplication Expression Binding   | ExDispatch Expression Attribute-  | ExMeta String-  | ExMetaTail Expression String+  | ExMeta Text+  | ExMetaTail Expression Text   | ExPhiMeet (Maybe String) Int Expression   | ExPhiAgain (Maybe String) Int Expression   deriving (Eq, Ord, Show, Generic)@@ -28,36 +30,36 @@   = BiTau Attribute Expression   | BiDelta Bytes   | BiVoid Attribute-  | BiLambda String-  | BiMeta String-  | BiMetaLambda String+  | BiLambda Text+  | BiMeta Text+  | BiMetaLambda Text   deriving (Eq, Ord, Show, Generic)  data Bytes   = BtEmpty   | BtOne String   | BtMany [String]-  | BtMeta String+  | BtMeta Text   deriving (Eq, Ord, Show, Generic)  data Attribute-  = AtLabel String+  = AtLabel Text   | AtAlpha Int   | AtPhi   | AtRho   | AtLambda   | AtDelta-  | AtMeta String+  | AtMeta Text   deriving (Eq, Generic, Ord)  instance Show Attribute where-  show (AtLabel label) = label+  show (AtLabel label) = T.unpack label   show (AtAlpha idx) = 'α' : show idx   show AtRho = "ρ"   show AtPhi = "φ"   show AtDelta = "Δ"   show AtLambda = "λ"-  show (AtMeta meta) = '!' : meta+  show (AtMeta meta) = '!' : T.unpack meta  countNodes :: Expression -> Int countNodes (ExFormation bds) = 1 + sum (map nodesInBinding bds) + length bds
src/Builder.hs view
@@ -26,6 +26,8 @@ import Control.Exception (Exception, throwIO) import Data.List.NonEmpty (NonEmpty (..)) import qualified Data.Map.Strict as Map+import Data.Text (Text)+import qualified Data.Text as T import Matcher import Misc (uniqueBindings) import Printer@@ -38,8 +40,8 @@   | CouldNotBuildBytes {_bts :: Bytes, _msg :: String}   deriving (Exception) -metaMsg :: String -> String-metaMsg = printf "meta '%s' is either does not exist or refers to an inappropriate term"+metaMsg :: Text -> String+metaMsg = printf "meta '%s' is either does not exist or refers to an inappropriate term" . T.unpack  type Built a = Either String a 
src/CLI.hs view
@@ -10,9 +10,11 @@ import CLI.Parsers import CLI.Runners import CLI.Types-import Control.Exception.Base (Exception (displayException), SomeException, fromException, handle)+import Control.Exception.Base (Exception (displayException), SomeException, fromException, handle, throwIO)+import Data.Version (showVersion) import Logger import Options.Applicative+import Paths_phino (version) import System.Exit (ExitCode (..), exitFailure)  handler :: SomeException -> IO ()@@ -32,11 +34,20 @@         CmdMatch OptsMatch{_logLevel, _logLines} -> (_logLevel, _logLines)    in setLogConfig level lns +checkPin :: Maybe String -> IO ()+checkPin Nothing = pure ()+checkPin (Just expected)+  | expected == actual = pure ()+  | otherwise = throwIO (VersionMismatch expected actual)+  where+    actual = showVersion version+ runCLI :: [String] -> IO () runCLI args = handle handler $ do-  cmd <- handleParseResult (execParserPure defaultPrefs parserInfo args)-  setLogger cmd-  case cmd of+  CliArgs{_pin, _command} <- handleParseResult (execParserPure defaultPrefs parserInfo args)+  checkPin _pin+  setLogger _command+  case _command of     CmdRewrite opts -> runRewrite opts     CmdDataize opts -> runDataize opts     CmdExplain opts -> runExplain opts
src/CLI/Parsers.hs view
@@ -282,6 +282,7 @@             <*> optDepthSensitive             <*> optNonumber             <*> switch (long "in-place" <> help "Edit file in-place instead of printing to output")+            <*> switch (long "update" <> help "Skip rewriting if --target file is newer than the input file")             <*> optSequence             <*> optCanonize             <*> optCompress@@ -344,8 +345,21 @@         <> command "match" (info matchParser (progDesc "Match 𝜑-program against provided pattern and build matched substitutions"))     ) -parserInfo :: ParserInfo Command+optPin :: Parser (Maybe String)+optPin =+  optional+    ( strOption+        ( long "pin"+            <> metavar "VERSION"+            <> help "Fail if this version doesn't match the version of phino"+        )+    )++cliArgsParser :: Parser CliArgs+cliArgsParser = CliArgs <$> optPin <*> commandParser++parserInfo :: ParserInfo CliArgs parserInfo =   info-    (commandParser <**> helper <**> simpleVersioner (showVersion version))+    (cliArgsParser <**> helper <**> simpleVersioner (showVersion version))     (fullDesc <> header "Phino - CLI Manipulator of 𝜑-Calculus Expressions")
src/CLI/Runners.hs view
@@ -29,6 +29,8 @@ import qualified Printer as P import Rewriter import Rule (RuleContext (..), matchProgramWithRule)+import System.Directory (doesFileExist, getModificationTime)+import System.Exit (exitSuccess) import Text.Printf (printf) import XMIR import qualified Yaml as Y@@ -36,6 +38,7 @@ runRewrite :: OptsRewrite -> IO () runRewrite OptsRewrite{..} = do   validateOpts+  checkUpdate   excluded <- validatedDispatches "hide" _hide   included <- validatedDispatches "show" _show   [loc] <- validatedDispatches "locator" [_locator]@@ -64,6 +67,9 @@     validateOpts = do       when (_inPlace && isNothing _inputFile) (invalidCLIArguments "The option --in-place requires an input file")       when (_inPlace && isJust _targetFile) (invalidCLIArguments "The options --in-place and --target cannot be used together")+      when (_update && _inPlace) (invalidCLIArguments "The options --update and --in-place cannot be used together")+      when (_update && isNothing _targetFile) (invalidCLIArguments "The option --update requires --target")+      when (_update && isNothing _inputFile) (invalidCLIArguments "The option --update requires an input file")       when (length _show > 1) (invalidCLIArguments "The option --show can be used only once")       validateLatexOptions         _outputFormat@@ -72,6 +78,16 @@         [(_meetPopularity, "meet-popularity"), (_meetLength, "meet-length")]       validateMust' _must       validateXmirOptions _outputFormat [(_omitListing, "omit-listing"), (_omitComments, "omit-comments")] _focus+    checkUpdate :: IO ()+    checkUpdate = case (_update, _inputFile, _targetFile) of+      (True, Just src, Just tgt) -> do+        exists <- doesFileExist tgt+        when exists $ do+          newer <- (>) <$> getModificationTime tgt <*> getModificationTime src+          when newer $ do+            logDebug (printf "Target '%s' is newer than source '%s', skipping rewriting (--update)" tgt src)+            exitSuccess+      _ -> pure ()     validateBreakpoint :: Maybe String -> [Y.Rule] -> IO ()     validateBreakpoint Nothing _ = pure ()     validateBreakpoint (Just rule) rules =
src/CLI/Types.hs view
@@ -38,6 +38,7 @@   | CouldNotDataize   | CouldNotPrintExpressionInXMIR   | EmptySubstsOnMatch+  | VersionMismatch String String   deriving (Exception)  instance Show CmdException where@@ -46,6 +47,8 @@   show CouldNotDataize = "Could not dataize given program"   show CouldNotPrintExpressionInXMIR = "Could not print expression with --output=xmir, only program printing is allowed"   show EmptySubstsOnMatch = "Provided pattern was not matched, no substitutions are built"+  show (VersionMismatch expected actual) =+    printf "Version mismatch: --pin requires '%s', but this is phino %s" expected actual  data Command   = CmdRewrite OptsRewrite@@ -54,6 +57,11 @@   | CmdMerge OptsMerge   | CmdMatch OptsMatch +data CliArgs = CliArgs+  { _pin :: Maybe String+  , _command :: Command+  }+ data IOFormat = XMIR | PHI | LATEX   deriving (Eq) @@ -117,6 +125,7 @@   , _depthSensitive :: Bool   , _nonumber :: Bool   , _inPlace :: Bool+  , _update :: Bool   , _sequence :: Bool   , _canonize :: Bool   , _compress :: Bool
src/CST.hs view
@@ -1,6 +1,7 @@ {-# LANGUAGE DuplicateRecordFields #-} {-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE MultiParamTypeClasses #-}+{-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE RecordWildCards #-} {-# OPTIONS_GHC -Wno-partial-fields -Wno-name-shadowing #-} @@ -11,6 +12,7 @@  import AST import Data.Maybe (isJust)+import qualified Data.Text as T import Misc import qualified Yaml as Y @@ -92,7 +94,7 @@ data EXCLAMATION = EXCL | NO_EXCL   deriving (Eq, Show) -data META = META {excl :: EXCLAMATION, hd :: META_HEAD, rest :: String}+data META = META {excl :: EXCLAMATION, hd :: META_HEAD, rest :: T.Text}   deriving (Eq, Show)  data TAB@@ -113,8 +115,8 @@   = PA_TAU {attr :: ATTRIBUTE, arrow :: ARROW, expr :: EXPRESSION}   | PA_FORMATION {attr :: ATTRIBUTE, voids :: [ATTRIBUTE], arrow :: ARROW, expr :: EXPRESSION}   | PA_VOID {attr :: ATTRIBUTE, arrow :: ARROW, void :: VOID}-  | PA_LAMBDA {func :: String}-  | PA_LAMBDA' {func :: String} -- ASCII version of PA_LAMBDA+  | PA_LAMBDA {func :: T.Text}+  | PA_LAMBDA' {func :: T.Text} -- ASCII version of PA_LAMBDA   | PA_META_LAMBDA {meta :: META}   | PA_META_LAMBDA' {meta :: META} -- ASCII version of PA_META_LAMBDA'   | PA_DELTA {bytes :: BYTES}@@ -167,7 +169,7 @@   deriving (Eq, Show)  data ATTRIBUTE-  = AT_LABEL {label :: String}+  = AT_LABEL {label :: T.Text}   | AT_ALPHA {alpha :: ALPHA, idx :: Int}   | AT_RHO {rho :: RHO}   | AT_PHI {phi :: PHI}@@ -257,8 +259,8 @@ toCST' :: ToCST a b => a -> b toCST' = (`toCST` (0, EOL)) -metaTail :: String -> String-metaTail = drop 1+metaTail :: T.Text -> T.Text+metaTail = T.drop 1  -- This class is used to convert AST to CST -- CST is created with sugar and unicode@@ -340,7 +342,7 @@                   (TAB tabs)                   next     where-      primitives :: [String]+      primitives :: [T.Text]       primitives = ["number", "string"]       withoutRhosInPrimitives :: Expression -> [Binding] -> ([Binding], [Binding])       withoutRhosInPrimitives _ [] = ([], [])@@ -437,7 +439,7 @@   toCST (BiDelta bts) ctx = PA_DELTA (toCST bts ctx)   toCST (BiLambda func) _ = PA_LAMBDA func   toCST (BiMetaLambda mt) _ = PA_META_LAMBDA (META EXCL F (metaTail mt))-  toCST (BiMeta mt) _ = error $ "BiMeta binding " ++ mt ++ " cannot be converted to PAIR"+  toCST (BiMeta mt) _ = error $ "BiMeta binding " ++ T.unpack mt ++ " cannot be converted to PAIR"  instance ToCST Binding APP_BINDING where   toCST bd@(BiTau _ _) ctx = APP_BINDING (toCST bd ctx :: PAIR)
src/Canonizer.hs view
@@ -7,13 +7,14 @@ module Canonizer (canonize) where  import AST+import qualified Data.Text as T import Rewriter (Rewritten)  canonizeBindings :: [Binding] -> Int -> ([Binding], Int) canonizeBindings [] idx = ([], idx) canonizeBindings ((BiLambda _) : rest) idx =   let (bds', idx') = canonizeBindings rest (idx + 1)-   in (BiLambda ('F' : show idx) : bds', idx')+   in (BiLambda (T.pack ('F' : show idx)) : bds', idx') canonizeBindings (BiTau attr expr : rest) idx =   let (expr', idx') = canonizeExpression expr idx       (bds', idx'') = canonizeBindings rest idx'
src/Dataize.hs view
@@ -1,5 +1,6 @@ {-# LANGUAGE DuplicateRecordFields #-} {-# LANGUAGE LambdaCase #-}+{-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE RecordWildCards #-} {-# OPTIONS_GHC -Wno-name-shadowing #-} {-# OPTIONS_GHC -Wno-unused-record-wildcards #-}@@ -15,6 +16,7 @@ import Data.List (partition) import Data.List.NonEmpty (NonEmpty (..)) import qualified Data.List.NonEmpty as NE+import qualified Data.Text as T import Deps (BuildTermFunc, SaveStepFunc, Term (TeAttribute)) import Locator (locatedExpression, withLocatedExpression) import Matcher (substEmpty)@@ -87,7 +89,7 @@ -- and expr is expression which program refers to. -- If Q refers to formation which contains binding with attribute == tau - -- the expression from this binding is returned.-phiDispatch :: String -> Expression -> Maybe (Expression, String)+phiDispatch :: T.Text -> Expression -> Maybe (Expression, String) phiDispatch tau expr = case expr of   ExFormation bds -> boundExpr bds   _ -> Nothing@@ -213,7 +215,7 @@   pure bts _dataize _ _ = throwIO (userError "Can't call _dataize from atoms with non-formation program") -atom :: String -> Expression -> DataizeContext -> IO Expression+atom :: T.Text -> Expression -> DataizeContext -> IO Expression atom "L_number_plus" self ctx = do   left <- _dataize (ExDispatch self (AtLabel "x")) ctx   right <- _dataize (ExDispatch self AtRho) ctx@@ -245,4 +247,4 @@         then pure (DataNumber (numToBts first))         else pure (ExDispatch self (AtLabel "y"))     _ -> pure ExTermination-atom func _ _ = throwIO (userError (printf "Atom '%s' does not exist" func))+atom func _ _ = throwIO (userError (printf "Atom '%s' does not exist" (T.unpack func)))
src/Functions.hs view
@@ -12,6 +12,7 @@ import qualified Data.ByteString.Char8 as B import Data.Functor import qualified Data.Set as Set+import qualified Data.Text as T import Deps import GHC.IO (unsafePerformIO) import Logger (logDebug)@@ -75,7 +76,7 @@ _randomTau args subst = do   attrs <- argsToAttrs args   tau <- randomTau attrs-  pure (TeAttribute (AtLabel tau))+  pure (TeAttribute (AtLabel (T.pack tau)))   where     argsToAttrs :: [Y.ExtraArgument] -> IO [String]     argsToAttrs [] = pure []@@ -186,7 +187,7 @@       throwIO         ( userError             ( printf-                "Couldn't convert given expression to 'Φ̇.string' object, only 'Φ̇.number' or 'Φ̇.string' are allowed\n%s"+                "Couldn't convert given expression to 'Φ.string' object, only 'Φ.number' or 'Φ.string' are allowed\n%s"                 (printExpression ex)             )         )@@ -194,7 +195,7 @@ _string [Y.ArgAttribute attr] subst = do   attr' <- buildAttributeThrows attr subst   pure (TeExpression (DataString (strToBts (printAttribute attr'))))-_string _ _ = throwIO (userError "Function string() requires exactly 1 argument as attribute or data expression (Φ̇.number or Φ̇.string)")+_string _ _ = throwIO (userError "Function string() requires exactly 1 argument as attribute or data expression (Φ.number or Φ.string)")  _number :: BuildTermMethod _number [Y.ArgExpression expr] subst = do@@ -203,8 +204,8 @@     DataString bts -> do       num <- parseNumberThrows (btsToUnescapedStr bts)       pure (TeExpression num)-    _ -> throwIO (userError (printf "Function number() expects expression to be 'Φ̇.string', but got:\n%s" (printExpression expr')))-_number _ _ = throwIO (userError "Function number() requires exactly 1 argument as 'Φ̇.string'")+    _ -> throwIO (userError (printf "Function number() expects expression to be 'Φ.string', but got:\n%s" (printExpression expr')))+_number _ _ = throwIO (userError "Function number() requires exactly 1 argument as 'Φ.string'")  _sum :: BuildTermMethod _sum args subst = do
src/LaTeX.hs view
@@ -1,6 +1,7 @@ {-# LANGUAGE AllowAmbiguousTypes #-} {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE RecordWildCards #-}  -- SPDX-FileCopyrightText: Copyright (c) 2025 Objectionary.com@@ -23,6 +24,7 @@ import CST import Data.List (intercalate, nub) import Data.Maybe (isJust)+import qualified Data.Text as T import Encoding import Lining import Locator (locatedExpression)@@ -124,7 +126,7 @@     popularity = toDouble _meetPopularity / 100.0  renderToLatex :: (ToSalty a, ToASCII a, ToSingleLine a, ToLaTeX a, WithMargin a, Render a) => a -> LatexContext -> String-renderToLatex renderable LatexContext{..} = render (toLaTeX $ withLineFormat _line $ withMargin _margin $ withEncoding ASCII $ withSugarType _sugar renderable)+renderToLatex renderable LatexContext{..} = T.unpack $ render (toLaTeX $ withLineFormat _line $ withMargin _margin $ withEncoding ASCII $ withSugarType _sugar renderable)  phiquation :: LatexContext -> String phiquation LatexContext{_nonumber = True} = "phiquation*"@@ -195,8 +197,8 @@     , ending False ctx     ] -piped :: String -> String-piped str = "|" <> str <> "|"+piped :: T.Text -> T.Text+piped str = "|" <> toLaTeX str <> "|"  class ToLaTeX a where   toLaTeX :: a -> a@@ -218,7 +220,7 @@   toLaTeX expr = expr  instance ToLaTeX ATTRIBUTE where-  toLaTeX AT_LABEL{..} = AT_LABEL (piped (toLaTeX label))+  toLaTeX AT_LABEL{..} = AT_LABEL (piped label)   toLaTeX AT_META{..} = AT_META (toLaTeX meta)   toLaTeX AT_LAMBDA{} = AT_LAMBDA LAMBDA'   toLaTeX AT_REST{} = AT_REST DOTS'@@ -239,8 +241,8 @@  instance ToLaTeX PAIR where   toLaTeX PA_DELTA{..} = PA_DELTA' bytes-  toLaTeX PA_LAMBDA{..} = PA_LAMBDA' (piped (toLaTeX func))-  toLaTeX PA_LAMBDA'{..} = PA_LAMBDA' (piped (toLaTeX func))+  toLaTeX PA_LAMBDA{..} = PA_LAMBDA' (piped func)+  toLaTeX PA_LAMBDA'{..} = PA_LAMBDA' (piped func)   toLaTeX PA_VOID{..} = PA_VOID (toLaTeX attr) arrow void   toLaTeX PA_TAU{..} = PA_TAU (toLaTeX attr) arrow (toLaTeX expr)   toLaTeX PA_FORMATION{..} = PA_FORMATION (toLaTeX attr) (map toLaTeX voids) arrow (toLaTeX expr)@@ -252,8 +254,8 @@  instance ToLaTeX META where   toLaTeX META{..} =-    let idx = readMaybe rest :: Maybe Int-        rest' = if not (null rest) && length rest <= 2 && isJust idx then '_' : rest else rest+    let idx = readMaybe (T.unpack rest) :: Maybe Int+        rest' = if not (T.null rest) && T.length rest <= 2 && isJust idx then T.cons '_' rest else rest      in META NO_EXCL (toLaTeX hd) rest'  instance ToLaTeX META_HEAD where@@ -271,17 +273,14 @@   toLaTeX AAS_EXPR{..} = AAS_EXPR eol tab (toLaTeX expr) (toLaTeX args)   toLaTeX args = args -instance ToLaTeX String where-  toLaTeX = escapeUnprintedChars+instance ToLaTeX T.Text where+  toLaTeX = T.concatMap escape     where-      escapeUnprintedChars :: String -> String-      escapeUnprintedChars [] = []-      escapeUnprintedChars (ch : rest) = case ch of-        '$' -> "\\char36{}" <> escapeUnprintedChars rest-        '@' -> "\\char64{}" <> escapeUnprintedChars rest-        '^' -> "\\char94{}" <> escapeUnprintedChars rest-        '_' -> "\\char95{}" <> escapeUnprintedChars rest-        _ -> ch : escapeUnprintedChars rest+      escape '$' = "\\char36{}"+      escape '@' = "\\char64{}"+      escape '^' = "\\char94{}"+      escape '_' = "\\char95{}"+      escape ch = T.singleton ch  instance ToLaTeX SET where   toLaTeX ST_BINDING{..} = ST_BINDING (toLaTeX binding)
src/Margin.hs view
@@ -8,6 +8,7 @@ module Margin (defaultMargin, withMargin, WithMargin) where  import CST+import qualified Data.Text as T import Lining (ToSingleLine (..)) import Render (Render (..)) @@ -42,7 +43,7 @@     let single = toSingleLine ex         main = withMargin' cfg expr         singleMain = toSingleLine main-        extra' = length (last (lines (render main))) + 4 -- 2 spaces + 2 braces around argument+        extra' = T.length (last (T.lines (render main))) + 4 -- 2 spaces + 2 braces around argument         arg = withMargin' (indt, margin) tau         singleArg = toSingleLine arg      in if@@ -54,7 +55,7 @@     let single = toSingleLine ex         main = withMargin' cfg expr         singleMain = toSingleLine main-        extra' = length (last (lines (render main))) + 4 -- 2 spaces + 2 braces around arguments+        extra' = T.length (last (T.lines (render main))) + 4 -- 2 spaces + 2 braces around arguments         exprs = withMargin' (indt, margin) args         singleExprs = toSingleLine exprs      in if@@ -66,7 +67,7 @@     let single = toSingleLine ex         main = withMargin' cfg expr         singleMain = toSingleLine main-        extra' = length (last (lines (render main))) + 4 -- 2 spaces + 2 braces around arguments+        extra' = T.length (last (T.lines (render main))) + 4 -- 2 spaces + 2 braces around arguments         taus' = withMargin' (indt, margin) taus         singleTaus = toSingleLine taus'      in if@@ -118,4 +119,4 @@   withMargin' _ = id  lengthOf :: Render a => a -> Int-lengthOf renderable = length (render renderable)+lengthOf renderable = T.length (render renderable)
src/Matcher.hs view
@@ -11,6 +11,7 @@ import Data.Map.Strict (Map) import qualified Data.Map.Strict as Map import Data.Maybe (catMaybes)+import Data.Text (Text)  -- Meta value -- The right part of substitution@@ -18,7 +19,7 @@   = MvAttribute Attribute -- !a   | MvBytes Bytes -- !b   | MvBindings [Binding] -- !B-  | MvFunction String -- !F+  | MvFunction Text -- !F   | MvExpression Expression Expression -- !e, the second expression is scope, which is closest formation   | MvTail [Tail] -- !t   deriving (Eq, Show)@@ -32,7 +33,7 @@  -- Substitution -- Shows the match of meta name to meta value-newtype Subst = Subst (Map String MetaValue)+newtype Subst = Subst (Map Text MetaValue)   deriving (Eq, Show)  -- Empty substitution@@ -40,7 +41,7 @@ substEmpty = Subst Map.empty  -- Singleton substitution with one (key -> value) pair-substSingle :: String -> MetaValue -> Subst+substSingle :: Text -> MetaValue -> Subst substSingle key value = Subst (Map.singleton key value)  defaultScope :: Expression@@ -51,7 +52,7 @@ combine :: Subst -> Subst -> Maybe Subst combine (Subst a) (Subst b) = combine' (Map.toList b) a   where-    combine' :: [(String, MetaValue)] -> Map String MetaValue -> Maybe Subst+    combine' :: [(Text, MetaValue)] -> Map Text MetaValue -> Maybe Subst     combine' [] acc = Just (Subst acc)     combine' ((key, MvExpression tgt scope) : rest) acc = case Map.lookup key acc of       Just (MvExpression expr' _)
src/Misc.hs view
@@ -3,6 +3,7 @@ {-# LANGUAGE PatternSynonyms #-} {-# LANGUAGE RecordWildCards #-} {-# LANGUAGE ViewPatterns #-}+{-# OPTIONS_GHC -Wno-incomplete-uni-patterns #-}  -- SPDX-FileCopyrightText: Copyright (c) 2025 Objectionary.com -- SPDX-License-Identifier: MIT@@ -42,12 +43,12 @@ import qualified Data.Aeson.Key as Key import qualified Data.Aeson.KeyMap as KeyMap import Data.Binary.IEEE754-import Data.Bits (Bits (shiftL), (.|.))+import Data.Bits (Bits (shiftL, shiftR), (.&.), (.|.)) import qualified Data.ByteString as B import Data.ByteString.Builder (toLazyByteString, word64BE) import Data.ByteString.Lazy (unpack) import qualified Data.ByteString.Lazy.UTF8 as U-import Data.Char (isPrint, ord)+import Data.Char (chr, isDigit, isPrint, ord) import Data.List (intercalate) import Data.Maybe (catMaybes) import qualified Data.Set as Set@@ -75,22 +76,22 @@   show FileDoesNotExist{..} = printf "File '%s' does not exist" _file   show DirectoryDoesNotExist{..} = printf "Directory '%s' does not exist" _dir -matchBaseObject :: Expression -> Maybe String+matchBaseObject :: Expression -> Maybe T.Text matchBaseObject (ExDispatch ExGlobal (AtLabel label)) = Just label matchBaseObject _ = Nothing -pattern BaseObject :: String -> Expression+pattern BaseObject :: T.Text -> Expression pattern BaseObject label <- (matchBaseObject -> Just label)   where     BaseObject label = ExDispatch ExGlobal (AtLabel label)  -- Minimal matcher function (required for view pattern)-matchDataObject :: Expression -> Maybe (String, Bytes)+matchDataObject :: Expression -> Maybe (T.Text, Bytes) matchDataObject (ExApplication outer (BiTau (AtAlpha 0) inner)) = case (matchOuter outer, matchInner inner) of   (Just label, Just bts) -> Just (label, bts)   _ -> Nothing   where-    matchOuter :: Expression -> Maybe String+    matchOuter :: Expression -> Maybe T.Text     matchOuter (BaseObject label) = Just label     matchOuter (ExPhiAgain _ _ (BaseObject label)) = Just label     matchOuter _ = Nothing@@ -118,7 +119,7 @@ pattern DataNumber :: Bytes -> Expression pattern DataNumber bts = DataObject "number" bts -pattern DataObject :: String -> Bytes -> Expression+pattern DataObject :: T.Text -> Bytes -> Expression pattern DataObject label bts <- (matchDataObject -> Just (label, bts))   where     DataObject label bts =@@ -250,22 +251,35 @@ -- [64,20,0,0,0,0,0,0] btsToWord8 :: Bytes -> [Word8] btsToWord8 BtEmpty = []-btsToWord8 (BtOne bt) = case readHex bt of-  [(hex, "")] -> [fromIntegral (hex :: Integer)]+btsToWord8 (BtOne bt) = [hexByte bt]+btsToWord8 (BtMany bts) = map hexByte bts+btsToWord8 (BtMeta mt) = error $ "Cannot convert meta bytes to Word8; " ++ T.unpack mt++hexByte :: String -> Word8+hexByte [hi, lo] = (nibble hi `shiftL` 4) .|. nibble lo+  where+    nibble c+      | isDigit c = fromIntegral (ord c - ord '0')+      | c >= 'A' && c <= 'F' = fromIntegral (ord c - ord 'A' + 10)+      | c >= 'a' && c <= 'f' = fromIntegral (ord c - ord 'a' + 10)+      | otherwise = error ("Invalid hex digit: " ++ [c])+hexByte bt = case readHex bt of+  [(hex, "")] -> fromIntegral (hex :: Integer)   _ -> error $ "Invalid hex byte; " ++ bt-btsToWord8 (BtMany []) = []-btsToWord8 (BtMany (bt : bts)) =-  case btsToWord8 (BtOne bt) of-    [byte] -> byte : btsToWord8 (BtMany bts)-    _ -> error $ "Invalid hex byte; " ++ bt-btsToWord8 (BtMeta mt) = error $ "Cannot convert meta bytes to Word8; " ++ mt  -- >>> word8ToBytes [64, 20, 0] -- BtMany ["40","14","00"] word8ToBytes :: [Word8] -> Bytes word8ToBytes [] = BtEmpty-word8ToBytes [w8] = BtOne (printf "%02X" w8)-word8ToBytes bts = BtMany (map (printf "%02X") bts)+word8ToBytes [w8] = BtOne (toHex w8)+word8ToBytes bts = BtMany (map toHex bts)++toHex :: Word8 -> String+toHex w = [digit (w `shiftR` 4), digit (w .&. 0x0F)]+  where+    digit n+      | n < 10 = chr (fromIntegral n + ord '0')+      | otherwise = chr (fromIntegral n + ord 'A' - 10)  -- Convert Bytes back to Double -- >>> btsToNum (BtMany ["40", "14", "00", "00", "00", "00", "00", "00"])
src/Parser.hs view
@@ -26,6 +26,7 @@ import Control.Monad (guard) import Data.Char (isAsciiLower, isDigit) import Data.Scientific (toRealFloat)+import qualified Data.Text as T import Data.Void import GHC.Char import Misc@@ -58,7 +59,7 @@   show CouldNotParseProgram{..} = printf "Couldn't parse given phi program, cause: %s" message   show CouldNotParseExpression{..} = printf "Couldn't parse given phi expression, cause: %s" message   show CouldNotParseAttribute{..} = printf "Couldn't parse given attribute, cause: %s" message-  show CouldNotParseNumber{..} = printf "Couldn't parse given number to 'Φ̇.number', cause: %s" message+  show CouldNotParseNumber{..} = printf "Couldn't parse given number to 'Φ.number', cause: %s" message  -- White space consumer whiteSpace :: Parser ()@@ -72,11 +73,12 @@ symbol :: String -> Parser String symbol = L.symbol whiteSpace -label' :: Parser String+-- Parsed as String then packed to Text once; BiLambda keeps String so function stays String+label' :: Parser T.Text label' = lexeme $ do   first <- oneOf ['a' .. 'z']   rest <- many (satisfy (`notElem` " \r\n\t,.|':;!?][}{)(⟧⟦") <?> "allowed character")-  return (first : rest)+  return (T.pack (first : rest))  function :: Parser String function = lexeme $ do@@ -115,21 +117,22 @@ metaSuffix :: Parser String metaSuffix = lexeme (many (oneOf ('_' : '-' : ['0' .. '9'] ++ ['a' .. 'z'] ++ ['A' .. 'Z']) <?> "meta suffix")) -meta :: Char -> Parser String+-- Meta variable names are packed to Text once here; all AST meta fields are Text+meta :: Char -> Parser T.Text meta ch = do   _ <- char '!'   c <- char ch   suf <- metaSuffix-  return (c : suf)+  return (T.pack (c : suf)) -meta' :: Char -> String -> Parser String+meta' :: Char -> String -> Parser T.Text meta' ch uni =   choice     [ meta ch     , do         _ <- symbol uni         suf <- metaSuffix-        return (ch : suf)+        return (T.pack (ch : suf))     ]  byte :: Parser String@@ -286,7 +289,7 @@     , try metaBinding     , try $ do         _ <- lambda-        BiLambda <$> function+        BiLambda . T.pack <$> function     , do         _ <- lambda         BiMetaLambda <$> meta 'F'
src/Printer.hs view
@@ -24,6 +24,7 @@ import CST import Data.List (intercalate) import qualified Data.Map.Strict as Map+import qualified Data.Text as T import Encoding import Lining import Margin (defaultMargin, withMargin)@@ -42,19 +43,19 @@ logPrintConfig = (SWEET, UNICODE, SINGLELINE, defaultMargin)  printProgram' :: Program -> PrintConfig -> String-printProgram' prog (sugar, encoding, line, margin) = render (withLineFormat line $ withMargin margin $ withEncoding encoding $ withSugarType sugar $ programToCST prog)+printProgram' prog (sugar, encoding, line, margin) = T.unpack $ render (withLineFormat line $ withMargin margin $ withEncoding encoding $ withSugarType sugar $ programToCST prog)  printProgram :: Program -> String printProgram prog = printProgram' prog defaultPrintConfig  printExpression' :: Expression -> PrintConfig -> String-printExpression' ex (sugar, encoding, line, margin) = render (withLineFormat line $ withMargin margin $ withEncoding encoding $ withSugarType sugar $ expressionToCST ex)+printExpression' ex (sugar, encoding, line, margin) = T.unpack $ render (withLineFormat line $ withMargin margin $ withEncoding encoding $ withSugarType sugar $ expressionToCST ex)  printExpression :: Expression -> String printExpression ex = printExpression' ex defaultPrintConfig  printAttribute' :: Attribute -> Encoding -> String-printAttribute' att encoding = render (withEncoding encoding (toCST att (0, NO_EOL) :: ATTRIBUTE))+printAttribute' att encoding = T.unpack $ render (withEncoding encoding (toCST att (0, NO_EOL) :: ATTRIBUTE))  printAttribute :: Attribute -> String printAttribute att =@@ -68,7 +69,7 @@ printBinding bd = printBinding' bd defaultPrintConfig  printBytes :: Bytes -> String-printBytes bts = render (toCST bts (0, NO_EOL) :: BYTES)+printBytes bts = T.unpack $ render (toCST bts (0, NO_EOL) :: BYTES)  printExtraArg' :: ExtraArgument -> PrintConfig -> String printExtraArg' (ArgAttribute att) (_, encoding, _, _) = printAttribute' att encoding@@ -88,14 +89,14 @@ printMetaValue (MvExpression ex _) config = printExpression' ex config printMetaValue (MvBytes bts) _ = printBytes bts printMetaValue (MvBindings bds) config = printExpression' (ExFormation bds) config-printMetaValue (MvFunction fun) _ = fun+printMetaValue (MvFunction fun) _ = T.unpack fun printMetaValue (MvTail tails) config = intercalate "," (map (`printTail` config) tails)  printSubst :: Subst -> PrintConfig -> String printSubst (Subst mp) config =   intercalate     "\n"-    (map (\(key, value) -> key <> " >> " <> printMetaValue value config) (Map.toList mp))+    (map (\(key, value) -> T.unpack key <> " >> " <> printMetaValue value config) (Map.toList mp))  printSubsts' :: [Subst] -> PrintConfig -> String printSubsts' [] _ = "------"
src/Render.hs view
@@ -1,5 +1,6 @@ {-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE NamedFieldPuns #-}+{-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE RecordWildCards #-}  -- SPDX-FileCopyrightText: Copyright (c) 2025 Objectionary.com@@ -8,26 +9,25 @@ module Render where  import CST-import Data.List (intercalate)-import Text.Printf (printf)+import Data.Text (Text)+import qualified Data.Text as T+import qualified Data.Text.Lazy as TL+import qualified Data.Text.Lazy.Builder as TLB  class Render a where-  render :: a -> String+  render :: a -> Text  instance Render String where-  render str = str+  render = T.pack -instance Render Integer where-  render = show+instance Render Text where+  render = id  instance Render Int where-  render = show--instance Render Double where-  render = show+  render = T.pack . show  instance Render Char where-  render ch = [ch]+  render = T.singleton  instance Render LCB where   render LCB = "{"@@ -102,7 +102,7 @@ instance Render BYTES where   render BT_EMPTY = "--"   render (BT_ONE bte) = render bte <> "-"-  render (BT_MANY bts) = intercalate "-" bts+  render (BT_MANY bts) = T.intercalate "-" (map render bts)   render (BT_META mt) = render mt  instance Render EXCLAMATION where@@ -130,7 +130,7 @@   render ALPHA' = "~"  instance Render TAB where-  render TAB{..} = concat (replicate indent "  ")+  render TAB{..} = T.replicate indent "  "   render TAB' = " "   render NO_TAB = "" @@ -143,19 +143,22 @@   render PA_FORMATION{voids = [], attr, arrow, expr} = render (PA_TAU attr arrow expr)   render PA_FORMATION{..} = render attr <> "(" <> render voids <> ")" <> render SPACE <> render arrow <> render SPACE <> render expr   render PA_LAMBDA{..} = render LAMBDA <> render SPACE <> render DASHED_ARROW <> render SPACE <> render func-  render PA_LAMBDA'{..} = "L> " <> func+  render PA_LAMBDA'{..} = "L> " <> render func   render PA_VOID{..} = render attr <> render SPACE <> render arrow <> render SPACE <> render void   render PA_DELTA{..} = render DELTA <> render SPACE <> render DASHED_ARROW <> render SPACE <> render bytes   render PA_DELTA'{..} = "D> " <> render bytes   render PA_META_LAMBDA{..} = render LAMBDA <> render SPACE <> render DASHED_ARROW <> render SPACE <> render meta-  render PA_META_LAMBDA'{..} = render "L> " <> render meta+  render PA_META_LAMBDA'{..} = "L> " <> render meta   render PA_META_DELTA{..} = render DELTA <> render SPACE <> render DASHED_ARROW <> render SPACE <> render meta-  render PA_META_DELTA'{..} = render "D> " <> render meta+  render PA_META_DELTA'{..} = "D> " <> render meta  instance Render BINDINGS where-  render BDS_EMPTY{} = ""-  render BDS_PAIR{..} = render COMMA <> render eol <> render tab <> render pair <> render bindings-  render BDS_META{..} = render COMMA <> render eol <> render tab <> render meta <> render bindings+  render = TL.toStrict . TLB.toLazyText . binds+    where+      binds :: BINDINGS -> TLB.Builder+      binds BDS_EMPTY{} = mempty+      binds BDS_PAIR{..} = TLB.fromText (render COMMA <> render eol <> render tab <> render pair) <> binds bindings+      binds BDS_META{..} = TLB.fromText (render COMMA <> render eol <> render tab <> render meta) <> binds bindings  instance Render APP_BINDING where   render APP_BINDING{..} = render pair@@ -169,8 +172,11 @@   render APP_ARG{..} = render expr <> render args  instance Render APP_ARGS where-  render AAS_EMPTY = ""-  render AAS_EXPR{..} = render COMMA <> render eol <> render tab <> render expr <> render args+  render = TL.toStrict . TLB.toLazyText . arguments+    where+      arguments :: APP_ARGS -> TLB.Builder+      arguments AAS_EMPTY = mempty+      arguments AAS_EXPR{..} = TLB.fromText (render COMMA <> render eol <> render tab <> render expr) <> arguments args  instance Render EXPRESSION where   render EX_GLOBAL{..} = render global@@ -182,18 +188,18 @@   render EX_APPLICATION{..} = render expr <> render space <> "(" <> render eol <> render tab <> render tau <> render eol' <> render tab' <> ")"   render EX_APPLICATION_TAUS{..} = render expr <> render space <> "(" <> render eol <> render tab <> render taus <> render eol' <> render tab' <> ")"   render EX_APPLICATION_EXPRS{..} = render expr <> render space <> "(" <> render eol <> render tab <> render args <> render eol' <> render tab' <> ")"-  render EX_STRING{..} = '"' : render str <> "\""-  render EX_NUMBER{..} = either show show num+  render EX_STRING{..} = "\"" <> render str <> "\""+  render EX_NUMBER{..} = either (T.pack . show) (T.pack . show) num   render EX_META{..} = render meta   render EX_META_TAIL{..} = render expr <> " * " <> render meta-  render EX_PHI_MEET{..} = "\\phiMeet{" <> maybe "" (++ ":") prefix <> render idx <> "}{ " <> render expr <> " }"-  render EX_PHI_AGAIN{..} = "\\phiAgain{" <> maybe "" (++ ":") prefix <> render idx <> "}"+  render EX_PHI_MEET{..} = "\\phiMeet{" <> maybe "" (\p -> T.pack p <> ":") prefix <> render idx <> "}{ " <> render expr <> " }"+  render EX_PHI_AGAIN{..} = "\\phiAgain{" <> maybe "" (\p -> T.pack p <> ":") prefix <> render idx <> "}"  instance Render [ATTRIBUTE] where-  render attrs = intercalate ", " (map render attrs)+  render attrs = T.intercalate ", " (map render attrs)  instance Render ATTRIBUTE where-  render AT_LABEL{..} = label+  render AT_LABEL{..} = render label   render AT_ALPHA{..} = render alpha <> render idx   render AT_RHO{..} = render rho   render AT_PHI{..} = render phi@@ -208,16 +214,16 @@  instance Render SET where   render ST_BINDING{..} = render binding-  render ST_ATTRIBUTES{..} = printf "[ %s ]" (intercalate ", " (map render attrs))+  render ST_ATTRIBUTES{..} = "[ " <> T.intercalate ", " (map render attrs) <> " ]"  instance Render LOGIC_OPERATOR where   render AND = "\\;\\text{and}\\;"   render OR = "\\;\\text{or}\\;"  instance Render NUMBER where-  render INDEX{..} = printf "\\indexof{ %s }" (render attr)-  render LENGTH{..} = printf "\\vert %s \\vert" (render binding)-  render LITERAL{..} = show num+  render INDEX{..} = "\\indexof{ " <> render attr <> " }"+  render LENGTH{..} = "\\vert " <> render binding <> " \\vert"+  render LITERAL{..} = T.pack (show num)  instance Render COMPARABLE where   render CMP_ATTR{..} = render attr@@ -229,23 +235,23 @@   render NOT_EQUAL = "\\not="  instance Render CONDITION where-  render CO_BELONGS{..} = render attr <> render SPACE <> render belongs <> render SPACE <> render set+  render CO_BELONGS{..} = render attr <> " " <> render belongs <> " " <> render set   render CO_LOGIC{conditions = [cond]} = render cond-  render CO_LOGIC{..} = intercalate (printf " %s " (render operator)) (map renderWrapped conditions)+  render CO_LOGIC{..} = T.intercalate (" " <> render operator <> " ") (map renderWrapped conditions)     where-      renderWrapped :: CONDITION -> String+      renderWrapped :: CONDITION -> Text       renderWrapped CO_LOGIC{conditions = [cond]} = render cond-      renderWrapped cond@CO_LOGIC{} = printf "( %s )" (render cond)+      renderWrapped cond@CO_LOGIC{} = "( " <> render cond <> " )"       renderWrapped cond = render cond-  render CO_NF{..} = printf "\\isnormal{ %s }" (render expr)+  render CO_NF{..} = "\\isnormal{ " <> render expr <> " }"   render CO_NOT{..} = renderFunc "not" condition-  render CO_COMPARE{..} = render left <> render SPACE <> render equal <> render SPACE <> render right-  render CO_MATCHES{..} = printf "matches( %s, %s )" regex (render expr)-  render CO_PART_OF{..} = printf "part-of( %s, %s )" (render expr) (render binding)+  render CO_COMPARE{..} = render left <> " " <> render equal <> " " <> render right+  render CO_MATCHES{..} = "matches( " <> T.pack regex <> ", " <> render expr <> " )"+  render CO_PART_OF{..} = "part-of( " <> render expr <> ", " <> render binding <> " )"   render CO_EMPTY = "" -renderFunc :: Render a => String -> a -> String-renderFunc func renderable = printf "%s( %s )" func (render renderable)+renderFunc :: Render a => Text -> a -> Text+renderFunc func renderable = func <> "( " <> render renderable <> " )"  instance Render EXTRA_ARG where   render ARG_ATTR{..} = render attr@@ -254,6 +260,6 @@   render ARG_BYTES{..} = render bytes  instance Render EXTRA where-  render EXTRA{func = "contextualize", args = arg : rest, ..} = printf "$ %s \\coloneqq \\ctx{ %s }{ %s } $" (render meta) (render arg) (intercalate ", " (map render rest))-  render EXTRA{func = "scope", args = arg : _, ..} = printf "$ %s \\coloneqq \\scopeof{ %s } $" (render meta) (render arg)-  render EXTRA{..} = printf "$ %s \\coloneqq %s( %s ) $" (render meta) func (intercalate ", " (map render args))+  render EXTRA{func = "contextualize", args = arg : rest, ..} = "$ " <> render meta <> " \\coloneqq \\ctx{ " <> render arg <> " }{ " <> T.intercalate ", " (map render rest) <> " } $"+  render EXTRA{func = "scope", args = arg : _, ..} = "$ " <> render meta <> " \\coloneqq \\scopeof{ " <> render arg <> " } $"+  render EXTRA{..} = "$ " <> render meta <> " \\coloneqq " <> T.pack func <> "( " <> T.intercalate ", " (map render args) <> " ) $"
src/Sugar.hs view
@@ -1,5 +1,6 @@ {-# LANGUAGE InstanceSigs #-} {-# LANGUAGE NamedFieldPuns #-}+{-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE PatternSynonyms #-} {-# LANGUAGE RecordWildCards #-} {-# OPTIONS_GHC -Wno-name-shadowing #-}
src/XMIR.hs view
@@ -142,10 +142,10 @@ formationBinding :: Binding -> XmirContext -> IO (Maybe Node) formationBinding (BiTau (AtLabel label) (ExFormation bds)) ctx = do   inners <- nestedBindings bds ctx-  pure (Just (object [("name", label)] inners))+  pure (Just (object [("name", T.unpack label)] inners)) formationBinding (BiTau (AtLabel label) expr) ctx = do   (base, children) <- expression expr ctx-  pure (Just (object [("name", label), ("base", base)] children))+  pure (Just (object [("name", T.unpack label), ("base", base)] children)) formationBinding (BiTau AtPhi expr) ctx = do   (base, children) <- expression expr ctx   pure (Just (object [("name", show AtPhi), ("base", base)] children))@@ -154,7 +154,7 @@ formationBinding (BiLambda _) _ = pure (Just (object [("name", show AtLambda)] [])) formationBinding (BiVoid AtRho) _ = pure Nothing formationBinding (BiVoid AtPhi) _ = pure (Just (object [("name", show AtPhi), ("base", "∅")] []))-formationBinding (BiVoid (AtLabel label)) _ = pure (Just (object [("name", label), ("base", "∅")] []))+formationBinding (BiVoid (AtLabel label)) _ = pure (Just (object [("name", T.unpack label), ("base", "∅")] [])) formationBinding binding _ = throwIO (UnsupportedBinding binding)  nestedBindings :: [Binding] -> XmirContext -> IO [Node]@@ -207,10 +207,10 @@     getPackage :: Expression -> IO ([String], Expression)     getPackage (ExFormation [BiTau (AtLabel label) (ExFormation [bd, BiLambda "Package", BiVoid AtRho]), BiVoid AtRho]) = do       (pckg, expr') <- getPackage (ExFormation [bd, BiLambda "Package", BiVoid AtRho])-      pure (label : pckg, expr')+      pure (T.unpack label : pckg, expr')     getPackage (ExFormation [BiTau (AtLabel label) (ExFormation [bd, BiLambda "Package", BiVoid AtRho]), BiLambda "Package", BiVoid AtRho]) = do       (pckg, expr') <- getPackage (ExFormation [bd, BiLambda "Package", BiVoid AtRho])-      pure (label : pckg, expr')+      pure (T.unpack label : pckg, expr')     getPackage (ExFormation [BiTau at ex, BiLambda "Package", BiVoid AtRho]) = pure ([], ExFormation [BiTau at ex, BiVoid AtRho])     getPackage (ExFormation [bd, BiVoid AtRho]) = pure ([], ExFormation [bd, BiVoid AtRho])     getPackage ex = throwIO (userError (printf "Can't extract package from given expression:\n %s" (printExpression ex)))@@ -378,7 +378,7 @@               if null pckg                 then pure (Program (ExFormation [obj, BiVoid AtRho]))                 else-                  let bd = foldr (\part acc -> BiTau (AtLabel part) (ExFormation [acc, BiLambda "Package", BiVoid AtRho])) obj pckg+                  let bd = foldr (\part acc -> BiTau (AtLabel (T.pack part)) (ExFormation [acc, BiLambda "Package", BiVoid AtRho])) obj pckg                    in pure (Program (ExFormation [bd, BiVoid AtRho]))           | otherwise -> throwIO (InvalidXMIRFormat "Expected single <object> element" doc)         _ -> throwIO (InvalidXMIRFormat "NodeElement is expected as root element" doc)@@ -390,17 +390,17 @@       name <- getAttr "name" cur       bds <- mapM (`xmirToFormationBinding` (name : fqn)) (cur C.$/ C.element (toName "o")) >>= uniqueBindings'       case name of-        "λ" -> pure (BiLambda (intercalate "_" ("L" : reverse fqn)))+        "λ" -> pure (BiLambda (T.pack (intercalate "_" ("L" : reverse fqn))))         ('α' : _) -> throwIO (InvalidXMIRFormat "Formation child @name can't start with α" cur)         "φ" -> pure (BiTau AtPhi (ExFormation (withVoidRho bds)))-        _ -> pure (BiTau (AtLabel name) (ExFormation (withVoidRho bds)))+        _ -> pure (BiTau (AtLabel (T.pack name)) (ExFormation (withVoidRho bds)))   | otherwise = do       name <- getAttr "name" cur       base <- getAttr "base" cur       attr <- case name of         "φ" -> pure AtPhi         ('α' : _) -> throwIO (InvalidXMIRFormat "Formation child @name can't start with α" cur)-        _ -> pure (AtLabel name)+        _ -> pure (AtLabel (T.pack name))       case base of         "∅" -> pure (BiVoid attr)         _ -> do@@ -496,7 +496,7 @@   ch : _     | ch `notElem` ['a' .. 'z'] -> throwIO (InvalidXMIRFormat (printf "The attribute '%s' must start with ['a'..'z']" attr) cur)     | '.' `elem` attr -> throwIO (InvalidXMIRFormat "Attribute can't contain dots" cur)-    | otherwise -> pure (AtLabel attr)+    | otherwise -> pure (AtLabel (T.pack attr))   _ -> throwIO (InvalidXMIRFormat (printf "Invalid attribute given: %s" attr) cur)  hasAttr :: String -> C.Cursor -> Bool
test/ASTSpec.hs view
@@ -1,3 +1,4 @@+{-# LANGUAGE OverloadedStrings #-} {-# OPTIONS_GHC -Wno-incomplete-uni-patterns #-}  -- SPDX-FileCopyrightText: Copyright (c) 2025 Objectionary.com
test/BuilderSpec.hs view
@@ -1,3 +1,5 @@+{-# LANGUAGE OverloadedStrings #-}+ -- SPDX-FileCopyrightText: Copyright (c) 2025 Objectionary.com -- SPDX-License-Identifier: MIT @@ -8,10 +10,11 @@ import Control.Monad import Data.Either (isLeft) import Data.Map.Strict qualified as Map+import Data.Text qualified as T import Matcher import Test.Hspec (Example (Arg), Expectation, Spec, SpecWith, anyException, describe, it, shouldBe, shouldSatisfy, shouldThrow) -test :: (Show a, Eq a) => (a -> Subst -> Either String (a, a)) -> [(String, a, [(String, MetaValue)], Either String (a, a))] -> SpecWith (Arg Expectation)+test :: (Show a, Eq a) => (a -> Subst -> Either String (a, a)) -> [(String, a, [(T.Text, MetaValue)], Either String (a, a))] -> SpecWith (Arg Expectation) test function useCases =   forM_ useCases $ \(desc, expr, mp, res) ->     it desc $ function expr (Subst (Map.fromList mp)) `shouldBe` res
test/CLISpec.hs view
@@ -10,11 +10,12 @@ import Control.Exception import Control.Monad (forM_, unless, when) import Data.List (intercalate, isInfixOf)+import Data.Time.Clock (addUTCTime, getCurrentTime) import Data.Time.Clock.POSIX (getPOSIXTime) import Data.Version (showVersion) import GHC.IO.Handle import Paths_phino (version)-import System.Directory (createDirectoryIfMissing, doesDirectoryExist, doesFileExist, getTemporaryDirectory, listDirectory, removeDirectoryRecursive, removeFile)+import System.Directory (createDirectoryIfMissing, doesDirectoryExist, doesFileExist, getTemporaryDirectory, listDirectory, removeDirectoryRecursive, removeFile, setModificationTime) import System.Exit (ExitCode (ExitFailure)) import System.FilePath ((</>)) import System.IO@@ -71,6 +72,13 @@     (openTempFile "." pattern)     (\(path, _) -> removeFile path) +withTempFileContent :: String -> String -> (FilePath -> IO a) -> IO a+withTempFileContent pattern content action =+  withTempFile pattern $ \(path, h) -> do+    hPutStr h content+    hClose h+    action path+ testCLI' :: [String] -> [String] -> Either ExitCode () -> Expectation testCLI' args outputs exit = do   (out, result) <- withStdout (try (runCLI args) :: IO (Either ExitCode ()))@@ -110,6 +118,25 @@       ["--help"]       ["Phino - CLI Manipulator of 𝜑-Calculus Expressions", "Usage:"] +  describe "--pin" $ do+    it "succeeds when --pin matches actual version" $+      withStdin "Q -> [[ ]]" $+        testCLISucceeded+          ["--pin=" ++ showVersion version, "rewrite", "--sweet"]+          ["{⟦⟧}"]++    it "fails when --pin doesn't match actual version" $+      withStdin "Q -> [[ ]]" $+        testCLIFailed+          ["--pin=9.9.9.9", "rewrite"]+          ["Version mismatch: --pin requires '9.9.9.9', but this is phino " ++ showVersion version]++    it "fails when --pin is empty" $+      withStdin "Q -> [[ ]]" $+        testCLIFailed+          ["--pin=", "rewrite"]+          ["Version mismatch: --pin requires ''"]+   it "prints debug info with --log-level=DEBUG" $     withStdin "{[[]]}" $       testCLISucceeded ["rewrite", "--log-level=DEBUG"] ["[DEBUG]:"]@@ -154,6 +181,24 @@             ["rewrite", "--in-place", "--target=output.phi", path]             ["--in-place and --target cannot be used together"] +      it "when --update is used without --target" $+        withStdin "Q -> [[ ]]" $+          testCLIFailed+            ["rewrite", "--update"]+            ["--update requires --target"]++      it "when --update is used without an input file" $+        withStdin "Q -> [[ ]]" $+          testCLIFailed+            ["rewrite", "--update", "--target=output.phi"]+            ["--update requires an input file"]++      it "when --update is used with --in-place" $+        withStdin "Q -> [[ ]]" $+          testCLIFailed+            ["rewrite", "--update", "--in-place", "input.phi"]+            ["--update and --in-place cannot be used together"]+       it "with --depth-sensitive" $         withStdin "Q -> [[ x -> \"x\"]]" $           testCLIFailed@@ -693,6 +738,30 @@         testCLISucceeded ["rewrite", rule "simple.yaml", "--in-place", "--sweet", path] []         content <- readFile path         content `shouldBe` "{⟦ x ↦ \"bar\" ⟧}"++    it "skips rewriting with --update when target is newer than source" $+      withTempFileContent "src-XXXXXX.phi" "Q -> [[ x -> \"foo\" ]]" $ \src ->+        withTempFileContent "tgt-XXXXXX.phi" "ORIGINAL" $ \tgt -> do+          now <- getCurrentTime+          setModificationTime src (addUTCTime (-60) now)+          setModificationTime tgt now+          testCLISucceeded+            ["rewrite", rule "simple.yaml", "--update", "--sweet", "--target=" ++ tgt, src]+            []+          content <- readFile tgt+          content `shouldBe` "ORIGINAL"++    it "rewrites with --update when source is newer than target" $+      withTempFileContent "src-XXXXXX.phi" "Q -> [[ x -> \"foo\" ]]" $ \src ->+        withTempFileContent "tgt-XXXXXX.phi" "ORIGINAL" $ \tgt -> do+          now <- getCurrentTime+          setModificationTime tgt (addUTCTime (-60) now)+          setModificationTime src now+          testCLISucceeded+            ["rewrite", rule "simple.yaml", "--update", "--sweet", "--target=" ++ tgt, src]+            []+          content <- readFile tgt+          content `shouldBe` "{⟦ x ↦ \"bar\" ⟧}"      it "rewrites with cycles" $       withStdin "Q -> [[ x -> \"x\" ]]" $
test/CSTSpec.hs view
@@ -1,5 +1,6 @@ {-# LANGUAGE DeriveAnyClass #-} {-# LANGUAGE DeriveGeneric #-}+{-# LANGUAGE OverloadedStrings #-}  -- SPDX-FileCopyrightText: Copyright (c) 2025 Objectionary.com -- SPDX-License-Identifier: MIT@@ -10,6 +11,7 @@ import CST import Control.Monad (forM_) import Data.Aeson+import Data.Text qualified as T import Data.Yaml qualified as Yaml import Encoding (Encoding (ASCII), withEncoding) import GHC.Generics (Generic)@@ -24,7 +26,7 @@  data CSTPack = CSTPack   { program :: String-  , result :: String+  , result :: T.Text   }   deriving (Generic, Show, FromJSON) 
test/ConditionSpec.hs view
@@ -1,3 +1,5 @@+{-# LANGUAGE OverloadedStrings #-}+ -- SPDX-FileCopyrightText: Copyright (c) 2025 Objectionary.com -- SPDX-License-Identifier: MIT 
test/DataizeSpec.hs view
@@ -1,3 +1,5 @@+{-# LANGUAGE OverloadedStrings #-}+ -- SPDX-FileCopyrightText: Copyright (c) 2025 Objectionary.com -- SPDX-License-Identifier: MIT 
test/FunctionsSpec.hs view
@@ -1,3 +1,5 @@+{-# LANGUAGE OverloadedStrings #-}+ -- SPDX-FileCopyrightText: Copyright (c) 2025 Objectionary.com -- SPDX-License-Identifier: MIT 
test/MatcherSpec.hs view
@@ -1,6 +1,4 @@-{-# LANGUAGE FlexibleInstances #-}-{-# LANGUAGE TypeFamilies #-}-{-# LANGUAGE TypeOperators #-}+{-# LANGUAGE OverloadedStrings #-}  -- SPDX-FileCopyrightText: Copyright (c) 2025 Objectionary.com -- SPDX-License-Identifier: MIT@@ -11,16 +9,12 @@ import Control.Monad (forM_) import Data.Map.Strict qualified as Map import Data.Maybe (fromMaybe)+import Data.Text qualified as T import Matcher import Test.Hspec (Example (Arg), Expectation, Spec, SpecWith, describe, it, shouldBe) -class Expected e where-  type ExpectedResult e-  toExpected :: e -> ExpectedResult e--instance Expected [[(String, MetaValue)]] where-  type ExpectedResult [[(String, MetaValue)]] = [Subst]-  toExpected = map (Subst . Map.fromList)+substs :: [[(T.Text, MetaValue)]] -> [Subst]+substs = map (Subst . Map.fromList)  maybeCombined :: Subst -> Subst -> Subst maybeCombined first second =@@ -29,13 +23,12 @@     (combine first second)  test ::-  (Expected e, ExpectedResult e ~ r, Eq r, Show r) =>-  (a -> a -> b -> r) ->-  [(String, a, a, b, e)] ->+  (a -> a -> b -> [Subst]) ->+  [(String, a, a, b, [Subst])] ->   SpecWith (Arg Expectation) test function useCases =-  forM_ useCases $ \(desc, ptn, tgt, scope, mp) ->-    it desc $ function ptn tgt scope `shouldBe` toExpected mp+  forM_ useCases $ \(desc, ptn, tgt, scope, expected) ->+    it desc $ function ptn tgt scope `shouldBe` expected  spec :: Spec spec = do@@ -47,7 +40,7 @@         , ExGlobal         , ExFormation [BiTau AtPhi ExGlobal, BiTau AtRho ExGlobal]         , defaultScope-        , [[], []]+        , substs [[], []]         )       ,         ( "Q.!a => [[ @ -> Q.y, ^ -> [[ a -> Q.w ]], @ -> Q.y ]] => [(a >> y), (a >> w), (a >> y)]"@@ -58,7 +51,7 @@             , BiTau AtPhi (ExDispatch ExGlobal (AtLabel "y"))             ]         , defaultScope-        , [[("a", MvAttribute (AtLabel "y"))], [("a", MvAttribute (AtLabel "w"))], [("a", MvAttribute (AtLabel "y"))]]+        , substs [[("a", MvAttribute (AtLabel "y"))], [("a", MvAttribute (AtLabel "w"))], [("a", MvAttribute (AtLabel "y"))]]         )       ,         ( "[[!a -> Q.org.!a]] => [[f -> [[x -> Q.org.x]], t -> [[y -> Q.org.y]] => [(!a >> x), (!a >> y)]"@@ -68,27 +61,27 @@             , BiTau (AtLabel "t") (ExFormation [BiTau (AtLabel "y") (ExDispatch (ExDispatch ExGlobal (AtLabel "org")) (AtLabel "y"))])             ]         , defaultScope-        , [[("a", MvAttribute (AtLabel "x"))], [("a", MvAttribute (AtLabel "y"))]]+        , substs [[("a", MvAttribute (AtLabel "x"))], [("a", MvAttribute (AtLabel "y"))]]         )       ,         ( "!e => [[x -> Q]] => [(!e >> [[x -> Q]] ), (!e >> Q)]"         , ExMeta "e"         , ExFormation [BiTau (AtLabel "x") ExGlobal]         , defaultScope-        ,-          [ [("e", MvExpression (ExFormation [BiTau (AtLabel "x") ExGlobal]) defaultScope)]-          , [("e", MvExpression ExGlobal (ExFormation [BiTau (AtLabel "x") ExGlobal]))]-          ]+        , substs+            [ [("e", MvExpression (ExFormation [BiTau (AtLabel "x") ExGlobal]) defaultScope)]+            , [("e", MvExpression ExGlobal (ExFormation [BiTau (AtLabel "x") ExGlobal]))]+            ]         )       ,         ( "!e.!a => Q.org.eolang => [(!e >> Q.org, !a >> eolang), (!e >> Q, !a >> org)]"         , ExDispatch (ExMeta "e") (AtMeta "a")         , ExDispatch (ExDispatch ExGlobal (AtLabel "org")) (AtLabel "eolang")         , defaultScope-        ,-          [ [("e", MvExpression (ExDispatch ExGlobal (AtLabel "org")) defaultScope), ("a", MvAttribute (AtLabel "eolang"))]-          , [("e", MvExpression ExGlobal defaultScope), ("a", MvAttribute (AtLabel "org"))]-          ]+        , substs+            [ [("e", MvExpression (ExDispatch ExGlobal (AtLabel "org")) defaultScope), ("a", MvAttribute (AtLabel "eolang"))]+            , [("e", MvExpression ExGlobal defaultScope), ("a", MvAttribute (AtLabel "org"))]+            ]         )       ,         ( "⟦!B1, !a ↦ ∅, !B2⟧.!a => ⟦ x ↦ ξ.t, t ↦ ∅ ⟧.t(ρ ↦ ⟦ x ↦ ξ.t, t ↦ ∅ ⟧) => [(!B1 >> ⟦x ↦ ξ.t⟧, !a >> t, !B2 >> ⟦⟧ )]"@@ -111,13 +104,13 @@                 )             )         , defaultScope-        ,-          [-            [ ("B1", MvBindings [BiTau (AtLabel "x") (ExDispatch ExThis (AtLabel "t"))])-            , ("a", MvAttribute (AtLabel "t"))-            , ("B2", MvBindings [])+        , substs+            [+              [ ("B1", MvBindings [BiTau (AtLabel "x") (ExDispatch ExThis (AtLabel "t"))])+              , ("a", MvAttribute (AtLabel "t"))+              , ("B2", MvBindings [])+              ]             ]-          ]         )       ,         ( "somebody"@@ -154,52 +147,52 @@                 )             ]         , defaultScope-        ,-          [+        , substs             [-              ( "e0"-              , MvExpression-                  ExGlobal-                  ( ExFormation-                      [ BiTau (AtLabel "a") ExGlobal-                      , BiTau (AtLabel "b") ExThis-                      ]-                  )-              )-            ,-              ( "e-first"-              , MvExpression-                  ExThis-                  ( ExFormation-                      [ BiTau (AtLabel "a") ExGlobal-                      , BiTau (AtLabel "b") ExThis-                      ]-                  )-              )-            ,-              ( "e-second"-              , MvExpression-                  (ExFormation [BiVoid AtPhi])-                  ( ExFormation-                      [ BiTau (AtLabel "a") ExGlobal-                      , BiTau (AtLabel "b") (ExFormation [BiVoid AtPhi])-                      ]-                  )-              )+              [+                ( "e0"+                , MvExpression+                    ExGlobal+                    ( ExFormation+                        [ BiTau (AtLabel "a") ExGlobal+                        , BiTau (AtLabel "b") ExThis+                        ]+                    )+                )+              ,+                ( "e-first"+                , MvExpression+                    ExThis+                    ( ExFormation+                        [ BiTau (AtLabel "a") ExGlobal+                        , BiTau (AtLabel "b") ExThis+                        ]+                    )+                )+              ,+                ( "e-second"+                , MvExpression+                    (ExFormation [BiVoid AtPhi])+                    ( ExFormation+                        [ BiTau (AtLabel "a") ExGlobal+                        , BiTau (AtLabel "b") (ExFormation [BiVoid AtPhi])+                        ]+                    )+                )+              ]             ]-          ]         )       ]    describe "matchAttribute: attribute => attribute => substitution" $     forM_-      [ ("~1 => ~1 => [()]", AtAlpha 1, AtAlpha 1, [[]])-      , ("!a => ^ => [(!a >> ^)]", AtMeta "a", AtRho, [[("a", MvAttribute AtRho)]])-      , ("!a => @ => [(!a >> @)]", AtMeta "a", AtPhi, [[("a", MvAttribute AtPhi)]])-      , ("~0 => [] => [()]", AtAlpha 0, AtLabel "x", [])+      [ ("~1 => ~1 => [()]", AtAlpha 1, AtAlpha 1, substs [[]])+      , ("!a => ^ => [(!a >> ^)]", AtMeta "a", AtRho, substs [[("a", MvAttribute AtRho)]])+      , ("!a => @ => [(!a >> @)]", AtMeta "a", AtPhi, substs [[("a", MvAttribute AtPhi)]])+      , ("~0 => [] => [()]", AtAlpha 0, AtLabel "x", substs [])       ]-      ( \(desc, ptn, tgt, mp) ->-          it desc $ matchAttribute ptn tgt `shouldBe` toExpected mp+      ( \(desc, ptn, tgt, expected) ->+          it desc $ matchAttribute ptn tgt `shouldBe` expected       )    describe "matchBindings: [binding] => [binding] => substitution" $@@ -210,114 +203,114 @@         , []         , []         , defaultScope-        , [[]]+        , substs [[]]         )       ,         ( "[[!B]] => T:[[x -> ?, D> 01-, L> Func]] => (!B >> T)"         , [BiMeta "B"]         , [BiVoid (AtLabel "x"), BiDelta (BtOne "01"), BiLambda "Func"]         , defaultScope-        , [[("B", MvBindings [BiVoid (AtLabel "x"), BiDelta (BtOne "01"), BiLambda "Func"])]]+        , substs [[("B", MvBindings [BiVoid (AtLabel "x"), BiDelta (BtOne "01"), BiLambda "Func"])]]         )       ,         ( "[[D> 00-]] => [[D> 00-, L> Func]] => []"         , [BiDelta (BtOne "00")]         , [BiDelta (BtOne "00"), BiLambda "Func"]         , defaultScope-        , []+        , substs []         )       ,         ( "[[y -> ?, !a -> ?]] => [[y -> ?, x -> ?]] => (!a >> x)"         , [BiVoid (AtLabel "y"), BiVoid (AtMeta "a")]         , [BiVoid (AtLabel "y"), BiVoid (AtLabel "x")]         , defaultScope-        , [[("a", MvAttribute (AtLabel "x"))]]+        , substs [[("a", MvAttribute (AtLabel "x"))]]         )       ,         ( "[[!B, x -> ?]] => [[x -> ?]] => (!B >> [[]])"         , [BiMeta "B", BiVoid (AtLabel "x")]         , [BiVoid (AtLabel "x")]         , defaultScope-        , [[("B", MvBindings [])]]+        , substs [[("B", MvBindings [])]]         )       ,         ( "[[!B1, x -> ?, !B2]] => [[x -> ?, y -> ?]] => (!B1 >> [[]], !B2 >> [[y -> ?]])"         , [BiMeta "B1", BiVoid (AtLabel "x"), BiMeta "B2"]         , [BiVoid (AtLabel "x"), BiVoid (AtLabel "y")]         , defaultScope-        , [[("B1", MvBindings []), ("B2", MvBindings [BiVoid (AtLabel "y")])]]+        , substs [[("B1", MvBindings []), ("B2", MvBindings [BiVoid (AtLabel "y")])]]         )       ,         ( "[[!B1, !x -> ?, !B2]] => [[y -> ?, D> -> 00-, L> Func]] => (!x >> y, !B1 >> [[]], !B2 >> [[D> -> 00-, L> Func]])"         , [BiMeta "B1", BiVoid (AtMeta "x"), BiMeta "B2"]         , [BiVoid (AtLabel "y"), BiDelta (BtOne "00"), BiLambda "Func"]         , defaultScope-        , [[("B1", MvBindings []), ("B2", MvBindings [BiDelta (BtOne "00"), BiLambda "Func"]), ("x", MvAttribute (AtLabel "y"))]]+        , substs [[("B1", MvBindings []), ("B2", MvBindings [BiDelta (BtOne "00"), BiLambda "Func"]), ("x", MvAttribute (AtLabel "y"))]]         )       ,         ( "[[!x -> ?, !y -> ?]] => [[a -> ?, b -> ?]] => (!x >> a, !y >> b)"         , [BiVoid (AtMeta "x"), BiVoid (AtMeta "y")]         , [BiVoid (AtLabel "a"), BiVoid (AtLabel "b")]         , defaultScope-        , [[("x", MvAttribute (AtLabel "a")), ("y", MvAttribute (AtLabel "b"))]]+        , substs [[("x", MvAttribute (AtLabel "a")), ("y", MvAttribute (AtLabel "b"))]]         )       ,         ( "[[t -> ?, !B]] => [[t -> ?, x -> Q, y -> $]] => (!B >> [[x -> Q, y -> $]])"         , [BiVoid (AtLabel "t"), BiMeta "B"]         , [BiVoid (AtLabel "t"), BiTau (AtLabel "x") ExGlobal, BiTau (AtLabel "y") ExThis]         , defaultScope-        , [[("B", MvBindings [BiTau (AtLabel "x") ExGlobal, BiTau (AtLabel "y") ExThis])]]+        , substs [[("B", MvBindings [BiTau (AtLabel "x") ExGlobal, BiTau (AtLabel "y") ExThis])]]         )       ,         ( "[[!B, z -> Q]] => [[x -> Q, y -> $, z -> Q]] => (!B >> [[x -> Q, y -> $]])"         , [BiMeta "B", BiTau (AtLabel "z") ExGlobal]         , [BiTau (AtLabel "x") ExGlobal, BiTau (AtLabel "y") ExThis, BiTau (AtLabel "z") ExGlobal]         , defaultScope-        , [[("B", MvBindings [BiTau (AtLabel "x") ExGlobal, BiTau (AtLabel "y") ExThis])]]+        , substs [[("B", MvBindings [BiTau (AtLabel "x") ExGlobal, BiTau (AtLabel "y") ExThis])]]         )       ,         ( "[[L> Func, D> 00-]] => [[D> 00-, L> Func]] => []"         , [BiLambda "Func", BiDelta (BtOne "00")]         , [BiDelta (BtOne "00"), BiLambda "Func"]         , defaultScope-        , []+        , substs []         )       ,         ( "[[t -> ?, !B]] => [[x -> ?, t -> ?]] => []"         , [BiVoid (AtLabel "t"), BiMeta "B"]         , [BiVoid (AtLabel "x"), BiVoid (AtLabel "t")]         , defaultScope-        , []+        , substs []         )       ,         ( "[[!B, !a -> ?]] => [[x -> ?, y -> ?]] => (!a >> y, !B >> [[ x -> ? ]] )"         , [BiMeta "B", BiVoid (AtMeta "a")]         , [BiVoid (AtLabel "x"), BiVoid (AtLabel "y")]         , defaultScope-        , [[("a", MvAttribute (AtLabel "y")), ("B", MvBindings [BiVoid (AtLabel "x")])]]+        , substs [[("a", MvAttribute (AtLabel "y")), ("B", MvBindings [BiVoid (AtLabel "x")])]]         )       ,         ( "[[!B1, !a -> ?, !B2]] => [[ x -> ?, y -> ?, z -> ? ]] => [(!B1 >> [[]], !a >> x, !B2 >> [[ y -> ?, z -> ? ]]), (...), (...)]"         , [BiMeta "B1", BiVoid (AtMeta "a"), BiMeta "B2"]         , [BiVoid (AtLabel "x"), BiVoid (AtLabel "y"), BiVoid (AtLabel "z")]         , defaultScope-        ,-          [-            [ ("B1", MvBindings [])-            , ("a", MvAttribute (AtLabel "x"))-            , ("B2", MvBindings [BiVoid (AtLabel "y"), BiVoid (AtLabel "z")])-            ]-          ,-            [ ("B1", MvBindings [BiVoid (AtLabel "x")])-            , ("a", MvAttribute (AtLabel "y"))-            , ("B2", MvBindings [BiVoid (AtLabel "z")])-            ]-          ,-            [ ("B1", MvBindings [BiVoid (AtLabel "x"), BiVoid (AtLabel "y")])-            , ("a", MvAttribute (AtLabel "z"))-            , ("B2", MvBindings [])+        , substs+            [+              [ ("B1", MvBindings [])+              , ("a", MvAttribute (AtLabel "x"))+              , ("B2", MvBindings [BiVoid (AtLabel "y"), BiVoid (AtLabel "z")])+              ]+            ,+              [ ("B1", MvBindings [BiVoid (AtLabel "x")])+              , ("a", MvAttribute (AtLabel "y"))+              , ("B2", MvBindings [BiVoid (AtLabel "z")])+              ]+            ,+              [ ("B1", MvBindings [BiVoid (AtLabel "x"), BiVoid (AtLabel "y")])+              , ("a", MvAttribute (AtLabel "z"))+              , ("B2", MvBindings [])+              ]             ]-          ]         )       ,         ( "[[!B1, !a1 -> ?, !B2, !a2 -> ?, !B3]] => [[ a -> ?, b -> ?, x -> ?, y -> ?, z -> ? ]] => [10 substs]"@@ -330,113 +323,113 @@           , BiVoid (AtLabel "z")           ]         , defaultScope-        ,-          [-            [ ("B1", MvBindings [])-            , ("a1", MvAttribute (AtLabel "a"))-            , ("B2", MvBindings [])-            , ("a2", MvAttribute (AtLabel "b"))-            , ("B3", MvBindings [BiVoid (AtLabel "x"), BiVoid (AtLabel "y"), BiVoid (AtLabel "z")])-            ]-          ,-            [ ("B1", MvBindings [])-            , ("a1", MvAttribute (AtLabel "a"))-            , ("B2", MvBindings [BiVoid (AtLabel "b")])-            , ("a2", MvAttribute (AtLabel "x"))-            , ("B3", MvBindings [BiVoid (AtLabel "y"), BiVoid (AtLabel "z")])-            ]-          ,-            [ ("B1", MvBindings [])-            , ("a1", MvAttribute (AtLabel "a"))-            , ("B2", MvBindings [BiVoid (AtLabel "b"), BiVoid (AtLabel "x")])-            , ("a2", MvAttribute (AtLabel "y"))-            , ("B3", MvBindings [BiVoid (AtLabel "z")])-            ]-          ,-            [ ("B1", MvBindings [])-            , ("a1", MvAttribute (AtLabel "a"))-            , ("B2", MvBindings [BiVoid (AtLabel "b"), BiVoid (AtLabel "x"), BiVoid (AtLabel "y")])-            , ("a2", MvAttribute (AtLabel "z"))-            , ("B3", MvBindings [])-            ]-          ,-            [ ("B1", MvBindings [BiVoid (AtLabel "a")])-            , ("a1", MvAttribute (AtLabel "b"))-            , ("B2", MvBindings [])-            , ("a2", MvAttribute (AtLabel "x"))-            , ("B3", MvBindings [BiVoid (AtLabel "y"), BiVoid (AtLabel "z")])-            ]-          ,-            [ ("B1", MvBindings [BiVoid (AtLabel "a")])-            , ("a1", MvAttribute (AtLabel "b"))-            , ("B2", MvBindings [BiVoid (AtLabel "x")])-            , ("a2", MvAttribute (AtLabel "y"))-            , ("B3", MvBindings [BiVoid (AtLabel "z")])-            ]-          ,-            [ ("B1", MvBindings [BiVoid (AtLabel "a")])-            , ("a1", MvAttribute (AtLabel "b"))-            , ("B2", MvBindings [BiVoid (AtLabel "x"), BiVoid (AtLabel "y")])-            , ("a2", MvAttribute (AtLabel "z"))-            , ("B3", MvBindings [])-            ]-          ,-            [ ("B1", MvBindings [BiVoid (AtLabel "a"), BiVoid (AtLabel "b")])-            , ("a1", MvAttribute (AtLabel "x"))-            , ("B2", MvBindings [])-            , ("a2", MvAttribute (AtLabel "y"))-            , ("B3", MvBindings [BiVoid (AtLabel "z")])-            ]-          ,-            [ ("B1", MvBindings [BiVoid (AtLabel "a"), BiVoid (AtLabel "b")])-            , ("a1", MvAttribute (AtLabel "x"))-            , ("B2", MvBindings [BiVoid (AtLabel "y")])-            , ("a2", MvAttribute (AtLabel "z"))-            , ("B3", MvBindings [])-            ]-          ,-            [ ("B1", MvBindings [BiVoid (AtLabel "a"), BiVoid (AtLabel "b"), BiVoid (AtLabel "x")])-            , ("a1", MvAttribute (AtLabel "y"))-            , ("B2", MvBindings [])-            , ("a2", MvAttribute (AtLabel "z"))-            , ("B3", MvBindings [])+        , substs+            [+              [ ("B1", MvBindings [])+              , ("a1", MvAttribute (AtLabel "a"))+              , ("B2", MvBindings [])+              , ("a2", MvAttribute (AtLabel "b"))+              , ("B3", MvBindings [BiVoid (AtLabel "x"), BiVoid (AtLabel "y"), BiVoid (AtLabel "z")])+              ]+            ,+              [ ("B1", MvBindings [])+              , ("a1", MvAttribute (AtLabel "a"))+              , ("B2", MvBindings [BiVoid (AtLabel "b")])+              , ("a2", MvAttribute (AtLabel "x"))+              , ("B3", MvBindings [BiVoid (AtLabel "y"), BiVoid (AtLabel "z")])+              ]+            ,+              [ ("B1", MvBindings [])+              , ("a1", MvAttribute (AtLabel "a"))+              , ("B2", MvBindings [BiVoid (AtLabel "b"), BiVoid (AtLabel "x")])+              , ("a2", MvAttribute (AtLabel "y"))+              , ("B3", MvBindings [BiVoid (AtLabel "z")])+              ]+            ,+              [ ("B1", MvBindings [])+              , ("a1", MvAttribute (AtLabel "a"))+              , ("B2", MvBindings [BiVoid (AtLabel "b"), BiVoid (AtLabel "x"), BiVoid (AtLabel "y")])+              , ("a2", MvAttribute (AtLabel "z"))+              , ("B3", MvBindings [])+              ]+            ,+              [ ("B1", MvBindings [BiVoid (AtLabel "a")])+              , ("a1", MvAttribute (AtLabel "b"))+              , ("B2", MvBindings [])+              , ("a2", MvAttribute (AtLabel "x"))+              , ("B3", MvBindings [BiVoid (AtLabel "y"), BiVoid (AtLabel "z")])+              ]+            ,+              [ ("B1", MvBindings [BiVoid (AtLabel "a")])+              , ("a1", MvAttribute (AtLabel "b"))+              , ("B2", MvBindings [BiVoid (AtLabel "x")])+              , ("a2", MvAttribute (AtLabel "y"))+              , ("B3", MvBindings [BiVoid (AtLabel "z")])+              ]+            ,+              [ ("B1", MvBindings [BiVoid (AtLabel "a")])+              , ("a1", MvAttribute (AtLabel "b"))+              , ("B2", MvBindings [BiVoid (AtLabel "x"), BiVoid (AtLabel "y")])+              , ("a2", MvAttribute (AtLabel "z"))+              , ("B3", MvBindings [])+              ]+            ,+              [ ("B1", MvBindings [BiVoid (AtLabel "a"), BiVoid (AtLabel "b")])+              , ("a1", MvAttribute (AtLabel "x"))+              , ("B2", MvBindings [])+              , ("a2", MvAttribute (AtLabel "y"))+              , ("B3", MvBindings [BiVoid (AtLabel "z")])+              ]+            ,+              [ ("B1", MvBindings [BiVoid (AtLabel "a"), BiVoid (AtLabel "b")])+              , ("a1", MvAttribute (AtLabel "x"))+              , ("B2", MvBindings [BiVoid (AtLabel "y")])+              , ("a2", MvAttribute (AtLabel "z"))+              , ("B3", MvBindings [])+              ]+            ,+              [ ("B1", MvBindings [BiVoid (AtLabel "a"), BiVoid (AtLabel "b"), BiVoid (AtLabel "x")])+              , ("a1", MvAttribute (AtLabel "y"))+              , ("B2", MvBindings [])+              , ("a2", MvAttribute (AtLabel "z"))+              , ("B3", MvBindings [])+              ]             ]-          ]         )       ]    describe "matchExpression: expression => pattern => substitution" $     test       matchExpression'-      [ ("$ => $ => [()]", ExThis, ExThis, defaultScope, [[]])-      , ("Q => Q => [()]", ExGlobal, ExGlobal, defaultScope, [[]])+      [ ("$ => $ => [()]", ExThis, ExThis, defaultScope, substs [[]])+      , ("Q => Q => [()]", ExGlobal, ExGlobal, defaultScope, substs [[]])       ,         ( "!e => Q => [(!e >> Q)]"         , ExMeta "e"         , ExGlobal         , defaultScope-        , [[("e", MvExpression ExGlobal defaultScope)]]+        , substs [[("e", MvExpression ExGlobal defaultScope)]]         )       ,         ( "!e => Q.org(x -> $) => [(!e >> Q.org(x -> $))]"         , ExMeta "e"         , ExApplication (ExDispatch ExGlobal (AtLabel "org")) (BiTau (AtLabel "x") ExThis)         , defaultScope-        , [[("e", MvExpression (ExApplication (ExDispatch ExGlobal (AtLabel "org")) (BiTau (AtLabel "x") ExThis)) defaultScope)]]+        , substs [[("e", MvExpression (ExApplication (ExDispatch ExGlobal (AtLabel "org")) (BiTau (AtLabel "x") ExThis)) defaultScope)]]         )       ,         ( "!e1.x => Q.org.x => [(!e1 >> Q.org)]"         , ExDispatch (ExMeta "e1") (AtLabel "x")         , ExDispatch (ExDispatch ExGlobal (AtLabel "org")) (AtLabel "x")         , defaultScope-        , [[("e1", MvExpression (ExDispatch ExGlobal (AtLabel "org")) defaultScope)]]+        , substs [[("e1", MvExpression (ExDispatch ExGlobal (AtLabel "org")) defaultScope)]]         )       ,         ( "!e.org.!a => $.org.x => [(!e >> $, !a >> x)]"         , ExDispatch (ExDispatch (ExMeta "e") (AtLabel "org")) (AtMeta "a")         , ExDispatch (ExDispatch ExThis (AtLabel "org")) (AtLabel "x")         , defaultScope-        , [[("e", MvExpression ExThis defaultScope), ("a", MvAttribute (AtLabel "x"))]]+        , substs [[("e", MvExpression ExThis defaultScope), ("a", MvAttribute (AtLabel "x"))]]         )       ,         ( "[[!a -> !e, !B]].!a => [[x -> Q, y -> $]].x => [(!a >> x, !e >> Q, !B >> [y -> $])]"@@ -449,50 +442,50 @@             )             (AtLabel "x")         , defaultScope-        ,-          [-            [ ("a", MvAttribute (AtLabel "x"))-            ,-              ( "e"-              , MvExpression-                  ExGlobal-                  ( ExFormation-                      [ BiTau (AtLabel "x") ExGlobal-                      , BiTau (AtLabel "y") ExThis-                      ]-                  )-              )-            , ("B", MvBindings [BiTau (AtLabel "y") ExThis])+        , substs+            [+              [ ("a", MvAttribute (AtLabel "x"))+              ,+                ( "e"+                , MvExpression+                    ExGlobal+                    ( ExFormation+                        [ BiTau (AtLabel "x") ExGlobal+                        , BiTau (AtLabel "y") ExThis+                        ]+                    )+                )+              , ("B", MvBindings [BiTau (AtLabel "y") ExThis])+              ]             ]-          ]         )       ,         ( "Q * !t => Q.org => [(!t >> [.org])]"         , ExMetaTail ExGlobal "t"         , ExDispatch ExGlobal (AtLabel "x")         , defaultScope-        , [[("t", MvTail [TaDispatch (AtLabel "x")])]]+        , substs [[("t", MvTail [TaDispatch (AtLabel "x")])]]         )       ,         ( "Q * !t => Q.org(x -> [[]]) => [(!t >> [.org, (x -> [[]])])]"         , ExMetaTail ExGlobal "t"         , ExApplication (ExDispatch ExGlobal (AtLabel "org")) (BiTau (AtLabel "x") defaultScope)         , defaultScope-        , [[("t", MvTail [TaDispatch (AtLabel "org"), TaApplication (BiTau (AtLabel "x") defaultScope)])]]+        , substs [[("t", MvTail [TaDispatch (AtLabel "org"), TaApplication (BiTau (AtLabel "x") defaultScope)])]]         )       ,         ( "Q.!a * !t => Q.org.eolang(x -> [[]]) => [(!a >> org, !t >> [ .eolang, ( x -> [[ ]] ) ])]"         , ExMetaTail (ExDispatch ExGlobal (AtMeta "a")) "t"         , ExApplication (ExDispatch (ExDispatch ExGlobal (AtLabel "org")) (AtLabel "eolang")) (BiTau (AtLabel "x") defaultScope)         , defaultScope-        , [[("a", MvAttribute (AtLabel "org")), ("t", MvTail [TaDispatch (AtLabel "eolang"), TaApplication (BiTau (AtLabel "x") defaultScope)])]]+        , substs [[("a", MvAttribute (AtLabel "org")), ("t", MvTail [TaDispatch (AtLabel "eolang"), TaApplication (BiTau (AtLabel "x") defaultScope)])]]         )       ,         ( "Q.x(y -> $ * !t1) * !t2 => Q.x(y -> $.q).p => [(!t1 >> [.q], !t2 >> [.p])]"         , ExMetaTail (ExApplication (ExDispatch ExGlobal (AtLabel "x")) (BiTau (AtLabel "y") (ExMetaTail ExThis "t1"))) "t2"         , ExDispatch (ExApplication (ExDispatch ExGlobal (AtLabel "x")) (BiTau (AtLabel "y") (ExDispatch ExThis (AtLabel "q")))) (AtLabel "p")         , defaultScope-        , [[("t1", MvTail [TaDispatch (AtLabel "q")]), ("t2", MvTail [TaDispatch (AtLabel "p")])]]+        , substs [[("t1", MvTail [TaDispatch (AtLabel "q")]), ("t2", MvTail [TaDispatch (AtLabel "p")])]]         )       ,         ( "[[!B1, !a ↦ !e1, !B2]](!a ↦ !e2) => ⟦ t ↦ ξ.k, x ↦ ξ.t, k ↦ ∅ ⟧(x ↦ ξ) => [(!B1 >> [[ t -> $.k ]], !a >> x, !B2 >> [[ k -> ? ]], !e1 >> $.t, !e2 >> $)]"@@ -506,25 +499,25 @@             )             (BiTau (AtLabel "x") ExThis)         , defaultScope-        ,-          [-            [ ("B1", MvBindings [BiTau (AtLabel "t") (ExDispatch ExThis (AtLabel "k"))])-            , ("a", MvAttribute (AtLabel "x"))-            , ("B2", MvBindings [BiVoid (AtLabel "k")])-            ,-              ( "e1"-              , MvExpression-                  (ExDispatch ExThis (AtLabel "t"))-                  ( ExFormation-                      [ BiTau (AtLabel "t") (ExDispatch ExThis (AtLabel "k"))-                      , BiTau (AtLabel "x") (ExDispatch ExThis (AtLabel "t"))-                      , BiVoid (AtLabel "k")-                      ]-                  )-              )-            , ("e2", MvExpression ExThis defaultScope)+        , substs+            [+              [ ("B1", MvBindings [BiTau (AtLabel "t") (ExDispatch ExThis (AtLabel "k"))])+              , ("a", MvAttribute (AtLabel "x"))+              , ("B2", MvBindings [BiVoid (AtLabel "k")])+              ,+                ( "e1"+                , MvExpression+                    (ExDispatch ExThis (AtLabel "t"))+                    ( ExFormation+                        [ BiTau (AtLabel "t") (ExDispatch ExThis (AtLabel "k"))+                        , BiTau (AtLabel "x") (ExDispatch ExThis (AtLabel "t"))+                        , BiVoid (AtLabel "k")+                        ]+                    )+                )+              , ("e2", MvExpression ExThis defaultScope)+              ]             ]-          ]         )       ] 
test/MiscSpec.hs view
@@ -1,3 +1,5 @@+{-# LANGUAGE OverloadedStrings #-}+ -- SPDX-FileCopyrightText: Copyright (c) 2025 Objectionary.com -- SPDX-License-Identifier: MIT 
test/ParserSpec.hs view
@@ -1,3 +1,4 @@+{-# LANGUAGE OverloadedStrings #-} -- SPDX-FileCopyrightText: Copyright (c) 2025 Objectionary.com -- SPDX-License-Identifier: MIT {-# OPTIONS_GHC -Wno-unrecognised-pragmas #-}
test/PrinterSpec.hs view
@@ -1,3 +1,5 @@+{-# LANGUAGE OverloadedStrings #-}+ -- SPDX-FileCopyrightText: Copyright (c) 2025 Objectionary.com -- SPDX-License-Identifier: MIT 
test/ReplacerSpec.hs view
@@ -1,3 +1,5 @@+{-# LANGUAGE OverloadedStrings #-}+ -- SPDX-FileCopyrightText: Copyright (c) 2025 Objectionary.com -- SPDX-License-Identifier: MIT 
test/RuleSpec.hs view
@@ -1,5 +1,6 @@ {-# LANGUAGE DeriveAnyClass #-} {-# LANGUAGE DeriveGeneric #-}+{-# LANGUAGE OverloadedStrings #-}  -- SPDX-FileCopyrightText: Copyright (c) 2025 Objectionary.com -- SPDX-License-Identifier: MIT