phino 0.0.0.7 → 0.0.0.8
raw patch · 12 files changed
+436/−151 lines, 12 filesdep +xml-conduit
Dependencies added: xml-conduit
Files
- phino.cabal +10/−5
- src/CLI.hs +16/−6
- src/Condition.hs +1/−2
- src/Pretty.hs +117/−0
- src/Printer.hs +13/−107
- src/Rewriter.hs +7/−7
- src/XMIR.hs +219/−0
- test/CLISpec.hs +26/−16
- test/ConditionSpec.hs +2/−2
- test/PrinterSpec.hs +3/−3
- test/RewriterSpec.hs +3/−3
- test/XMIRSpec.hs +19/−0
phino.cabal view
@@ -1,7 +1,7 @@ cabal-version: 3.0 name: phino-version: 0.0.0.7+version: 0.0.0.8 license: MIT synopsis: Command-Line Manipulator of 𝜑-Calculus Expressions description: Please see the README on GitHub at <https://github.com/objectionary/phino#readme>@@ -30,12 +30,14 @@ Matcher, Builder, Replacer,- Printer, Rewriter, Yaml, Condition, Misc,- Logger+ Logger,+ Printer,+ Pretty,+ XMIR hs-source-dirs: src other-modules: Paths_phino@@ -58,7 +60,8 @@ prettyprinter, optparse-applicative, vector,- random+ random,+ xml-conduit ^>=1.10 default-language: Haskell2010 -- Executable using the library@@ -89,6 +92,7 @@ ConditionSpec, YamlSpec, MiscSpec,+ XMIRSpec, Paths_phino autogen-modules: Paths_phino@@ -109,7 +113,8 @@ yaml, text, silently,- directory+ directory,+ xml-conduit ^>=1.10 build-tool-depends: hspec-discover:hspec-discover default-language: Haskell2010
src/CLI.hs view
@@ -11,7 +11,7 @@ import Control.Exception (Exception (displayException), SomeException, handle, throw, throwIO) import Control.Exception.Base import Control.Monad (when)-import Data.Char (toUpper)+import Data.Char (toLower, toUpper) import Data.List (intercalate) import Data.Version (showVersion) import Logger@@ -20,7 +20,7 @@ import Options.Applicative import Parser (parseProgramThrows) import Paths_phino (version)-import Printer (printProgram)+import Printer (PrintFormat (PHI, XMIR), printProgram) import Rewriter (rewrite) import System.Exit (ExitCode (..), exitFailure) import System.IO (getContents')@@ -47,18 +47,26 @@ data OptsRewrite = OptsRewrite { rules :: [FilePath], phiInput :: Maybe FilePath,+ printFormat :: PrintFormat, normalize :: Bool, nothing :: Bool, shuffle :: Bool, maxDepth :: Integer } +parsePrintFormat :: ReadM PrintFormat+parsePrintFormat = eitherReader $ \format -> case map toLower format of+ "xmir" -> Right XMIR+ "phi" -> Right PHI+ _ -> Left "invalid output format: expected 'xmir' or 'phi'"+ rewriteParser :: Parser Command rewriteParser = CmdRewrite <$> ( OptsRewrite <$> many (strOption (long "rule" <> metavar "FILE" <> help "Path to custom rule")) <*> optional (strOption (long "phi-input" <> metavar "FILE" <> help "Path .phi file with 𝜑-expression"))+ <*> option parsePrintFormat (long "output" <> metavar "FORMAT" <> help "Program output format" <> value PHI <> showDefault) <*> switch (long "normalize" <> help "Use built-in normalization rules") <*> switch (long "nothing" <> help "Desugar provided 𝜑-expression") <*> switch (long "shuffle" <> help "Shuffle rules before applying")@@ -68,8 +76,8 @@ commandParser :: Parser Command commandParser = hsubparser (command "rewrite" (info rewriteParser (progDesc "Rewrite the expression"))) -readLogLevel :: String -> Either String LogLevel-readLogLevel lvl = case map toUpper lvl of+parseLogLevel :: ReadM LogLevel+parseLogLevel = eitherReader $ \lvl -> case map toUpper lvl of "DEBUG" -> Right DEBUG "INFO" -> Right INFO "WARNING" -> Right WARNING@@ -83,7 +91,7 @@ appParser = App <$> option- (eitherReader readLogLevel)+ parseLogLevel ( long "log-level" <> metavar "LEVEL" <> help ("Log level (" <> intercalate ", " (map show [DEBUG, INFO, WARNING, ERROR, NONE]) <> ")")@@ -145,7 +153,9 @@ else pure ordered program <- parseProgramThrows prog rewritten <- rewrite' program rules' 1- putStrLn (printProgram rewritten)+ logDebug (printf "Printing rewritten 𝜑-program as %s" (show printFormat))+ out <- printProgram rewritten printFormat+ putStrLn out where rewrite' :: Program -> [Y.Rule] -> Integer -> IO Program rewrite' prog rules count = do
src/Condition.hs view
@@ -7,11 +7,10 @@ import Builder (buildAttribute, buildBinding) import Data.Aeson (FromJSON) import qualified Data.Map.Strict as M-import GHC.Generics import GHC.IO (unsafePerformIO) import Matcher import Misc (allPathsIn)-import Printer (printCondition, printExpression, printSubstitutions)+import Pretty (prettyExpression, prettySubsts) import Yaml (normalizationRules) import qualified Yaml as Y
+ src/Pretty.hs view
@@ -0,0 +1,117 @@+{-# LANGUAGE FlexibleInstances #-}++-- SPDX-FileCopyrightText: Copyright (c) 2025 Objectionary.com+-- SPDX-License-Identifier: MIT++module Pretty+ ( prettyExpression,+ prettyProgram,+ prettyAttribute,+ prettySubsts,+ prettyBinding,+ )+where++import Ast+import qualified Data.Map.Strict as Map+import Matcher+import Prettyprinter+import Prettyprinter.Render.String (renderString)++prettyMeta :: String -> Doc ann+prettyMeta meta = pretty "!" <> pretty meta++prettyArrow :: Doc ann+prettyArrow = pretty "↦"++prettyDashedArrow :: Doc ann+prettyDashedArrow = pretty "⤍"++instance Pretty Attribute where+ pretty (AtLabel name) = pretty name+ pretty (AtAlpha index) = pretty "α" <> pretty index+ pretty AtRho = pretty "ρ"+ pretty AtPhi = pretty "φ"+ pretty (AtMeta meta) = prettyMeta meta++instance Pretty Binding where+ pretty (BiTau attr expr) = pretty attr <+> prettyArrow <+> pretty expr+ pretty (BiMeta meta) = prettyMeta meta+ pretty (BiDelta bytes) = pretty "Δ" <+> prettyDashedArrow <+> pretty bytes+ pretty (BiMetaDelta meta) = pretty "Δ" <+> prettyDashedArrow <+> prettyMeta meta+ pretty (BiVoid attr) = pretty attr <+> prettyArrow <+> pretty "∅"+ pretty (BiLambda func) = pretty "λ" <+> prettyDashedArrow <+> pretty func+ pretty (BiMetaLambda meta) = pretty "λ" <+> prettyDashedArrow <+> prettyMeta meta++instance {-# OVERLAPPING #-} Pretty [Binding] where+ pretty bindings = vsep (punctuate comma (map pretty bindings))++instance Pretty Expression where+ pretty (ExFormation []) = pretty "⟦⟧"+ pretty (ExFormation [binding]) = case binding of+ BiTau _ _ -> vsep [pretty "⟦", indent 2 (pretty binding), pretty "⟧"]+ _ -> pretty "⟦" <+> pretty binding <+> pretty "⟧"+ pretty (ExFormation bindings) = vsep [pretty "⟦", indent 2 (pretty bindings), pretty "⟧"]+ pretty ExThis = pretty "ξ"+ pretty ExGlobal = pretty "Φ"+ pretty ExTermination = pretty "⊥"+ pretty (ExMeta meta) = prettyMeta meta+ pretty (ExApplication expr tau) = pretty expr <> vsep [lparen, indent 2 (pretty tau), rparen]+ pretty (ExDispatch expr attr) = pretty expr <> pretty "." <> pretty attr+ pretty (ExMetaTail expr meta) = pretty expr <+> pretty "*" <+> prettyMeta meta++instance Pretty Program where+ pretty (Program expr) = pretty "Φ" <+> prettyArrow <+> pretty expr++instance Pretty Tail where+ pretty (TaApplication tau) = vsep [lparen, indent 2 (pretty tau), rparen]+ pretty (TaDispatch attr) = pretty "." <> pretty attr++instance Pretty MetaValue where+ pretty (MvAttribute attr) = pretty attr+ pretty (MvBytes bytes) = pretty bytes+ pretty (MvBindings []) = pretty "[]"+ pretty (MvBindings bindings) = vsep [pretty "[", indent 2 (pretty bindings), pretty "]"]+ pretty (MvFunction func) = pretty func+ pretty (MvExpression expr) = pretty expr+ pretty (MvTail tails) = vsep (punctuate comma (map pretty tails))++instance Pretty Subst where+ pretty (Subst mp) =+ vsep+ [ lparen,+ indent+ 2+ ( vsep+ ( punctuate+ comma+ ( map+ (\(key, value) -> prettyMeta key <+> pretty ">>" <+> pretty value)+ (Map.toList mp)+ )+ )+ ),+ rparen+ ]++instance {-# OVERLAPPING #-} Pretty [Subst] where+ pretty [] = pretty "[]"+ pretty substs = vsep [pretty "[", indent 2 (vsep (punctuate comma (map pretty substs))), pretty "]"]++render :: (Pretty a) => a -> String+render printable = renderString (layoutPretty defaultLayoutOptions (pretty printable))++prettyBinding :: Binding -> String+prettyBinding = render++prettyAttribute :: Attribute -> String+prettyAttribute = render++prettySubsts :: [Subst] -> String+prettySubsts = render++prettyExpression :: Expression -> String+prettyExpression = render++prettyProgram :: Program -> String+prettyProgram = render
src/Printer.hs view
@@ -1,115 +1,21 @@-{-# LANGUAGE FlexibleInstances #-}-{-# LANGUAGE InstanceSigs #-}- -- SPDX-FileCopyrightText: Copyright (c) 2025 Objectionary.com -- SPDX-License-Identifier: MIT -module Printer (printExpression, printProgram, printSubstitutions, printCondition) where+module Printer where import Ast-import qualified Data.Map.Strict as Map-import Matcher-import Prettyprinter-import Prettyprinter.Render.String (renderString)-import Text.Printf (vFmt)-import Yaml (Condition)-import qualified Yaml as Y--prettyMeta :: String -> Doc ann-prettyMeta meta = pretty "!" <> pretty meta--prettyArrow :: Doc ann-prettyArrow = pretty "↦"--prettyDashedArrow :: Doc ann-prettyDashedArrow = pretty "⤍"--instance Pretty Attribute where- pretty (AtLabel name) = pretty name- pretty (AtAlpha index) = pretty "α" <> pretty index- pretty AtRho = pretty "ρ"- pretty AtPhi = pretty "φ"- pretty (AtMeta meta) = prettyMeta meta--instance Pretty Binding where- pretty (BiTau attr expr) = pretty attr <+> prettyArrow <+> pretty expr- pretty (BiMeta meta) = prettyMeta meta- pretty (BiDelta bytes) = pretty "Δ" <+> prettyDashedArrow <+> pretty bytes- pretty (BiMetaDelta meta) = pretty "Δ" <+> prettyDashedArrow <+> prettyMeta meta- pretty (BiVoid attr) = pretty attr <+> prettyArrow <+> pretty "∅"- pretty (BiLambda func) = pretty "λ" <+> prettyDashedArrow <+> pretty func- pretty (BiMetaLambda meta) = pretty "λ" <+> prettyDashedArrow <+> prettyMeta meta--instance {-# OVERLAPPING #-} Pretty [Binding] where- pretty bindings = vsep (punctuate comma (map pretty bindings))--instance Pretty Expression where- pretty (ExFormation []) = pretty "⟦⟧"- pretty (ExFormation [binding]) = case binding of- BiTau _ _ -> vsep [pretty "⟦", indent 2 (pretty binding), pretty "⟧"]- _ -> pretty "⟦" <+> pretty binding <+> pretty "⟧"- pretty (ExFormation bindings) = vsep [pretty "⟦", indent 2 (pretty bindings), pretty "⟧"]- pretty ExThis = pretty "ξ"- pretty ExGlobal = pretty "Φ"- pretty ExTermination = pretty "⊥"- pretty (ExMeta meta) = prettyMeta meta- pretty (ExApplication expr tau) = pretty expr <> vsep [lparen, indent 2 (pretty tau), rparen]- pretty (ExDispatch expr attr) = pretty expr <> pretty "." <> pretty attr- pretty (ExMetaTail expr meta) = pretty expr <+> pretty "*" <+> prettyMeta meta--instance Pretty Program where- pretty (Program expr) = pretty "Φ" <+> prettyArrow <+> pretty expr--instance Pretty Tail where- pretty (TaApplication tau) = vsep [lparen, indent 2 (pretty tau), rparen]- pretty (TaDispatch attr) = pretty "." <> pretty attr--instance Pretty MetaValue where- pretty (MvAttribute attr) = pretty attr- pretty (MvBytes bytes) = pretty bytes- pretty (MvBindings []) = pretty "[]"- pretty (MvBindings bindings) = vsep [pretty "[", indent 2 (pretty bindings), pretty "]"]- pretty (MvFunction func) = pretty func- pretty (MvExpression expr) = pretty expr- pretty (MvTail tails) = vsep (punctuate comma (map pretty tails))--instance Pretty Subst where- pretty (Subst mp) =- vsep- [ lparen,- indent- 2- ( vsep- ( punctuate- comma- ( map- (\(key, value) -> prettyMeta key <+> pretty ">>" <+> pretty value)- (Map.toList mp)- )- )- ),- rparen- ]--instance Pretty Condition where- pretty (Y.NF expr) = pretty "NF(" <+> pretty expr <+> rparen--instance {-# OVERLAPPING #-} Pretty [Subst] where- pretty :: [Subst] -> Doc ann- pretty [] = pretty "[]"- pretty substs = vsep [pretty "[", indent 2 (vsep (punctuate comma (map pretty substs))), pretty "]"]--prettyPrint :: (Pretty a) => a -> String-prettyPrint printable = renderString (layoutPretty defaultLayoutOptions (pretty printable))--printCondition :: Condition -> String-printCondition = prettyPrint+import Pretty (prettyProgram)+import XMIR (programToXMIR, printXMIR) -printSubstitutions :: [Subst] -> String-printSubstitutions = prettyPrint+data PrintFormat = XMIR | PHI+ deriving (Eq) -printExpression :: Expression -> String-printExpression = prettyPrint+instance Show PrintFormat where+ show XMIR = "xmir"+ show PHI = "phi" -printProgram :: Program -> String-printProgram = prettyPrint+printProgram :: Program -> PrintFormat -> IO String+printProgram prog PHI = pure (prettyProgram prog)+printProgram prog XMIR = do+ xmir <- programToXMIR prog+ pure (printXMIR xmir)
src/Rewriter.hs view
@@ -18,7 +18,7 @@ import Matcher (MetaValue (MvAttribute, MvBindings, MvExpression), Subst (Subst), combine, combineMany, matchProgram, substEmpty, substSingle) import Misc (ensuredFile) import Parser (parseProgram, parseProgramThrows)-import Printer (printExpression, printProgram, printSubstitutions)+import Pretty (prettyExpression, prettyProgram, prettySubsts) import Replacer (replaceProgram) import Text.Printf import qualified Yaml as Y@@ -32,14 +32,14 @@ show CouldNotBuild {..} = printf "Couldn't build given expression with provided substitutions\n--Expression: %s\n--Substitutions: %s"- (printExpression expr)- (printSubstitutions substs)+ (prettyExpression expr)+ (prettySubsts substs) show CouldNotReplace {..} = printf "Couldn't replace expression in program by pattern\nProgram: %s\n--Pattern: %s\n--Result: %s"- (printProgram prog)- (printExpression ptn)- (printExpression res)+ (prettyProgram prog)+ (prettyExpression ptn)+ (prettyExpression res) -- Build pattern and result expression and replace patterns to results in given program buildAndReplace :: Program -> Expression -> Expression -> [Subst] -> IO Program@@ -83,6 +83,6 @@ pure program Just matched -> do let substs = extended matched- logDebug (printf "Rule has been matched, substitutions are:\n%s" (printSubstitutions substs))+ logDebug (printf "Rule has been matched, substitutions are:\n%s" (prettySubsts substs)) replaced substs rewrite prog rest
+ src/XMIR.hs view
@@ -0,0 +1,219 @@+{-# LANGUAGE DeriveAnyClass #-}+{-# LANGUAGE RecordWildCards #-}++-- SPDX-FileCopyrightText: Copyright (c) 2025 Objectionary.com+-- SPDX-License-Identifier: MIT++module XMIR (programToXMIR, printXMIR) where++import Ast+import Control.Exception (throwIO)+import Control.Exception.Base (Exception)+import qualified Data.Bifunctor+import Data.List (intercalate)+import qualified Data.List+import Data.Map (Map)+import qualified Data.Map as M+import Data.Maybe (catMaybes, mapMaybe)+import Data.Text (Text)+import qualified Data.Text as T+import qualified Data.Text.Lazy as TL+import qualified Data.Text.Lazy.Builder as TB+import Debug.Trace (trace)+import Pretty (prettyAttribute, prettyBinding, prettyExpression, prettyProgram)+import Text.Printf (printf)+import Text.XML++data XMIRException+ = UnsupportedExpression {expr :: Expression}+ | UnsupportedBinding {binding :: Binding}+ deriving (Exception)++instance Show XMIRException where+ show UnsupportedExpression {..} = printf "XMIR does not support such expression: %s" (prettyExpression expr)+ show UnsupportedBinding {..} = printf "XMIR does not support such bindings: %s" (prettyBinding binding)++toName :: String -> Name+toName str = Name (T.pack str) Nothing Nothing++element :: String -> [(String, String)] -> [Node] -> Element+element name attrs children = do+ let name' = toName name+ attrs' = M.fromList (map (Data.Bifunctor.bimap toName T.pack) attrs)+ Element name' attrs' children++object :: [(String, String)] -> [Node] -> Node+object attrs children = NodeElement (element "o" attrs children)++expression :: Expression -> IO (String, [Node])+expression ExThis = pure ("$", [])+expression ExGlobal = pure ("Q", [])+expression (ExFormation bds) = do+ nested <- nestedBindings bds+ pure ("", nested)+expression (ExDispatch expr attr) = do+ (base, children) <- expression expr+ let attr' = prettyAttribute attr+ if null base+ then pure ('.' : attr', [object [] children])+ else+ if head base == '.' || not (null children)+ then pure ('.' : attr', [object [("base", base)] children])+ else pure (base ++ ('.' : attr'), children)+expression (ExApplication expr (BiTau attr texpr)) = do+ (base, children) <- expression expr+ (base', children') <- expression texpr+ let as = prettyAttribute attr+ attrs =+ if null base'+ then [("as", as)]+ else [("as", as), ("base", base')]+ pure (base, object attrs children' : children)+expression (ExApplication (ExFormation bds) tau) = throwIO (UnsupportedExpression (ExApplication (ExFormation bds) tau))+expression expr = throwIO (UnsupportedExpression expr)++nestedBindings :: [Binding] -> IO [Node]+nestedBindings bds = catMaybes <$> mapM formationBinding bds++formationBinding :: Binding -> IO (Maybe Node)+formationBinding (BiTau (AtLabel label) (ExFormation bds)) = do+ inners <- nestedBindings bds+ pure (Just (object [("name", label)] inners))+formationBinding (BiTau (AtLabel label) expr) = do+ (base, children) <- expression expr+ pure (Just (object [("name", label), ("base", base)] (reverse children)))+formationBinding (BiTau AtRho _) = pure Nothing+formationBinding (BiDelta bytes) = pure (Just (NodeContent (T.pack bytes)))+formationBinding (BiLambda func) = pure (Just (object [("name", "λ")] []))+formationBinding (BiVoid AtRho) = pure Nothing+formationBinding (BiVoid AtPhi) = pure (Just (object [("name", "φ"), ("base", "∅")] []))+formationBinding (BiVoid (AtLabel label)) = pure (Just (object [("name", label), ("base", "∅")] []))+formationBinding binding = throwIO (UnsupportedBinding binding)++rootExpression :: Expression -> IO Node+rootExpression (ExFormation [bd, BiVoid AtRho]) = do+ [bd'] <- nestedBindings [bd]+ pure bd'+rootExpression expr = throwIO (UnsupportedExpression expr)++-- Extract package from given expression+-- The function returns tuple (X, Y), where+-- - X: list of package parts+-- - Y: root object expression+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')+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')+getPackage (ExFormation [BiTau attr expr, BiLambda "Package", BiVoid AtRho]) = pure ([], ExFormation [BiTau attr expr, BiVoid AtRho])+getPackage (ExFormation [bd, BiVoid AtRho]) = pure ([], ExFormation [bd, BiVoid AtRho])+getPackage expr = throwIO (userError (printf "Can't extract package from given expression:\n %s" (prettyExpression expr)))++metasWithPackage :: String -> [Node]+metasWithPackage pckg =+ [ NodeElement+ ( element+ "metas"+ []+ [ NodeElement+ ( element+ "meta"+ []+ [ NodeElement (element "head" [] [NodeContent (T.pack "package")]),+ NodeElement (element "tail" [] [NodeContent (T.pack pckg)]),+ NodeElement (element "part" [] [NodeContent (T.pack pckg)])+ ]+ )+ ]+ )+ | not (null pckg)+ ]++programToXMIR :: Program -> IO Document+programToXMIR (Program expr) = do+ (pckg, expr') <- getPackage expr+ root <- rootExpression expr'+ pure+ ( Document+ (Prologue [] Nothing [])+ ( element+ "object"+ [("xmlns:xsi", "http://www.w3.org/2001/XMLSchema-instance")]+ ( NodeElement (element "listing" [] [NodeContent (T.pack (prettyProgram (Program expr)))])+ : root+ : metasWithPackage (intercalate "." pckg)+ )+ )+ []+ )++-- Add indentation (2 spaces per level).+indent :: Int -> TB.Builder+indent n = TB.fromText (T.replicate n (T.pack " "))++newline :: TB.Builder+newline = TB.fromString "\n"++printElement :: Int -> Element -> TB.Builder+printElement indentLevel (Element name attrs nodes)+ | null nodes =+ indent indentLevel+ <> TB.fromString "<"+ <> TB.fromText (nameLocalName name)+ <> attrsText+ <> TB.fromString "/>"+ <> newline+ | all isTextNode nodes =+ indent indentLevel+ <> TB.fromString "<"+ <> TB.fromText (nameLocalName name)+ <> attrsText+ <> TB.fromString ">"+ <> mconcat (map printRawText nodes)+ <> TB.fromString "</"+ <> TB.fromText (nameLocalName name)+ <> TB.fromString ">"+ <> newline+ | otherwise =+ indent indentLevel+ <> TB.fromString "<"+ <> TB.fromText (nameLocalName name)+ <> attrsText+ <> TB.fromString ">"+ <> newline+ <> mconcat (map (printNode (indentLevel + 1)) nodes)+ <> indent indentLevel+ <> TB.fromString "</"+ <> TB.fromText (nameLocalName name)+ <> TB.fromString ">"+ <> newline+ where+ attrsText =+ mconcat+ [ TB.fromString " " <> TB.fromText (nameLocalName k) <> TB.fromString "=\"" <> TB.fromText v <> TB.fromString "\""+ | (k, v) <- M.toList attrs+ ]++ isTextNode (NodeContent _) = True+ isTextNode _ = False++ printRawText (NodeContent t) = TB.fromText t+ printRawText _ = mempty++printNode :: Int -> Node -> TB.Builder+printNode _ (NodeContent t) = TB.fromText t -- print text exactly as-is+printNode i (NodeElement e) = printElement i e -- pretty-print elements+printNode i (NodeComment t) = indent i <> TB.fromString "<!-- " <> TB.fromText t <> TB.fromString " -->" <> newline+printNode _ _ = mempty++printXMIR :: Document -> String+printXMIR (Document _ root _) =+ TL.unpack+ ( TB.toLazyText+ ( TB.fromString "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"+ <> newline+ <> printElement 0 root+ )+ )
test/CLISpec.hs view
@@ -8,7 +8,7 @@ import CLI (runCLI) import Control.Exception-import Control.Monad (unless)+import Control.Monad (forM_, unless) import Data.List (isInfixOf) import Data.Version (showVersion) import GHC.IO.Handle@@ -63,12 +63,16 @@ where cleanup (fp, _) = removeFile fp -testCLI :: [String] -> String -> Expectation-testCLI args output = do+testCLI :: [String] -> [String] -> Expectation+testCLI args outputs = do out <- capture_ (runCLI args)- unless (output `isInfixOf` out) $- expectationFailure- ("Expected that output contains:\n" ++ output ++ "\nbut got:\n" ++ out)+ forM_+ outputs+ ( \output ->+ unless (output `isInfixOf` out) $+ expectationFailure+ ("Expected that output contains:\n" ++ output ++ "\nbut got:\n" ++ out)+ ) testCLIFailed :: [String] -> String -> Expectation testCLIFailed args output = withStdin "" $ do@@ -79,7 +83,7 @@ spec :: Spec spec = do it "prints version" $ do- testCLI ["--version"] (showVersion version)+ testCLI ["--version"] [showVersion version] it "prints help" $ do output <- capture_ (runCLI ["--help"])@@ -87,26 +91,26 @@ output `shouldContain` "Usage:" it "prints debug info with --log-level=DEBUG" $ do- withStdin "Q -> [[]]" $ testCLI ["rewrite", "--nothing", "--log-level=DEBUG"] "[DEBUG]:"+ withStdin "Q -> [[]]" $ testCLI ["rewrite", "--nothing", "--log-level=DEBUG"] ["[DEBUG]:"] describe "rewrites" $ do it "desugares with --nothing flag from file" $ testCLI ["rewrite", "--nothing", "--phi-input=test-resources/cli/desugar.phi"]- "Φ ↦ ⟦\n foo ↦ Φ.org.eolang,\n ρ ↦ ∅\n⟧"+ ["Φ ↦ ⟦\n foo ↦ Φ.org.eolang,\n ρ ↦ ∅\n⟧"] it "desugares with --nothing flag from stdin" $ withStdin "{[[foo ↦ QQ]]}" $- testCLI ["rewrite", "--nothing"] "Φ ↦ ⟦\n foo ↦ Φ.org.eolang,\n ρ ↦ ∅\n⟧"+ testCLI ["rewrite", "--nothing"] ["Φ ↦ ⟦\n foo ↦ Φ.org.eolang,\n ρ ↦ ∅\n⟧"] it "rewrites with single rule" $ withStdin "{T(x -> Q.y)}" $- testCLI ["rewrite", "--rule=resources/dc.yaml"] "Φ ↦ ⊥"+ testCLI ["rewrite", "--rule=resources/dc.yaml"] ["Φ ↦ ⊥"] it "normalizes with --normalize flag" $ testCLI ["rewrite", "--normalize", "--phi-input=test-resources/cli/normalize.phi"]- ( unlines+ [ unlines [ "Φ ↦ ⟦", " x ↦ ⟦", " ρ ↦ ⟦",@@ -117,7 +121,7 @@ " ρ ↦ ∅", "⟧" ]- )+ ] it "fails with negative --max-depth" $ testCLIFailed@@ -129,11 +133,11 @@ ["rewrite"] "no --rule, no --normalize, no --nothing are provided" - it "normalizes from stdin" $ do+ it "normalizes from stdin" $ withStdin "Φ ↦ ⟦ a ↦ ⟦ b ↦ ∅ ⟧ (b ↦ [[ ]]) ⟧" $ testCLI ["rewrite", "--normalize"]- ( unlines+ [ unlines [ "Φ ↦ ⟦", " a ↦ ⟦", " b ↦ ⟦ ρ ↦ ∅ ⟧,",@@ -142,4 +146,10 @@ " ρ ↦ ∅", "⟧" ]- )+ ]++ it "rewrites as XMIR" $+ withStdin "Q -> [[ x -> Q.y ]]" $+ testCLI+ ["rewrite", "--nothing", "--output=xmir"]+ ["<?xml version=\"1.0\" encoding=\"UTF-8\"?>", "<object", " <o base=\"Q.y\" name=\"x\"/>"]
test/ConditionSpec.hs view
@@ -14,7 +14,7 @@ import GHC.Generics import Matcher (matchProgram) import Misc-import Printer (printSubstitutions)+import Pretty (prettySubsts) import System.FilePath import Test.Hspec (Spec, describe, expectationFailure, it, runIO) import Yaml qualified@@ -42,6 +42,6 @@ (null met) ( expectationFailure $ "List of substitution after condition check must be not empty\nOriginal substitutions:\n"- ++ printSubstitutions matched+ ++ prettySubsts matched ) )
test/PrinterSpec.hs view
@@ -8,7 +8,7 @@ import Matcher (MetaValue (MvAttribute, MvExpression), substEmpty, substSingle) import Parser (parseProgramThrows) import Prettyprinter-import Printer+import Pretty import Test.Hspec (Example (Arg), Expectation, Spec, SpecWith, describe, it, runIO, shouldBe) test :: (Pretty a) => (a -> String) -> [(String, String, a)] -> SpecWith (Arg Expectation)@@ -35,11 +35,11 @@ "Φ ↦ ⟦\n Δ ⤍ 00-,\n λ ⤍ F,\n ρ ↦ ∅,\n !B,\n φ ↦ ⟦\n y ↦ ∅,\n ρ ↦ ∅\n ⟧\n⟧" ) ]- test printProgram useCases+ test prettyProgram useCases describe "print substitution" $ test- printSubstitutions+ prettySubsts [ ("[()]", "[\n (\n \n )\n]", [substEmpty]), ("[(!e >> Q.x)]", "[\n (\n !e >> Φ.x\n )\n]", [substSingle "e" (MvExpression (ExDispatch ExGlobal (AtLabel "x")))]), ("[(!a >> x)]", "[\n (\n !a >> x\n )\n]", [substSingle "a" (MvAttribute (AtLabel "x"))])
test/RewriterSpec.hs view
@@ -16,7 +16,7 @@ import GHC.Generics import Misc (allPathsIn, ensuredFile) import Parser (parseProgramThrows)-import Printer (printProgram)+import Pretty (prettyProgram) import Rewriter (rewrite) import System.FilePath (makeRelative, replaceExtension, (</>)) import Test.Hspec (Spec, describe, expectationFailure, it, pending, runIO)@@ -97,8 +97,8 @@ unless (rewritten == result') $ expectationFailure ( "Wrong rewritten program. Expected:\n"- ++ printProgram result'+ ++ prettyProgram result' ++ "\nGot:\n"- ++ printProgram rewritten+ ++ prettyProgram rewritten ) )
+ test/XMIRSpec.hs view
@@ -0,0 +1,19 @@+-- SPDX-FileCopyrightText: Copyright (c) 2025 Objectionary.com+-- SPDX-License-Identifier: MIT++module XMIRSpec where++import Test.Hspec (Spec, it, shouldBe, runIO)+import XMIR+import Misc (ensuredFile)+import Parser (parseProgramThrows)+import qualified Data.Text as T++spec :: Spec+spec = do+ phi <- runIO $ readFile =<< ensuredFile "test-resources/xmir/program.phi"+ xmir <- runIO $ readFile =<< ensuredFile "test-resources/xmir/program.xmir"+ prog <- runIO (parseProgramThrows phi)+ doc <- runIO $ programToXMIR prog+ let xmir' = printXMIR doc+ it "prints valid xmir" $ T.stripEnd (T.pack xmir) `shouldBe` T.stripEnd (T.pack xmir')