From 2e9077a067dfeebe6abd3f14f5ebe57fa1ff8359 Mon Sep 17 00:00:00 2001 From: Danila Danko Date: Sun, 25 Feb 2024 14:13:27 +0300 Subject: [PATCH 01/42] feat: use transform-phi and metrics-phi subcommands --- eo-phi-normalizer/app/Main.hs | 146 +++++++++++------- .../src/Language/EO/Phi/Metrics/Collect.hs | 3 +- 2 files changed, 92 insertions(+), 57 deletions(-) diff --git a/eo-phi-normalizer/app/Main.hs b/eo-phi-normalizer/app/Main.hs index ef2d16776..7d8ce6456 100644 --- a/eo-phi-normalizer/app/Main.hs +++ b/eo-phi-normalizer/app/Main.hs @@ -1,13 +1,17 @@ +{-# LANGUAGE DataKinds #-} {-# LANGUAGE DeriveAnyClass #-} {-# LANGUAGE DeriveGeneric #-} +{-# LANGUAGE DuplicateRecordFields #-} {-# LANGUAGE LambdaCase #-} {-# LANGUAGE OverloadedLists #-} {-# LANGUAGE OverloadedRecordDot #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE RecordWildCards #-} +{-# LANGUAGE TypeOperators #-} +{-# OPTIONS_GHC -Wno-partial-fields #-} {-# OPTIONS_GHC -Wno-type-defaults #-} -module Main where +module Main (main) where import Control.Monad (unless, when) import Data.Foldable (forM_) @@ -15,15 +19,18 @@ import Data.Foldable (forM_) import Control.Lens ((^.)) import Data.Aeson (ToJSON) import Data.Aeson.Text (encodeToLazyText) -import Data.List (nub) +import Data.Function ((&)) +import Data.List (nub, stripPrefix) +import Data.Maybe (fromMaybe) import Data.Text.Lazy.Lens import Language.EO.Phi (Object (Formation), Program (Program), defaultMain, parseProgram, printTree) +import Language.EO.Phi.Metrics.Collect (collectMetrics) import Language.EO.Phi.Rules.Common (Context (..), applyRules, applyRulesChain) import Language.EO.Phi.Rules.Yaml (RuleSet (rules, title), convertRule, parseRuleSetFromFile) import Options.Generic import System.IO (IOMode (WriteMode), hClose, hPutStr, hPutStrLn, openFile, stdout) -data CLINamedParams = CLINamedParams +data CLI'TransformPhi'Params = CLI'TransformPhi'Params { chain :: Bool , rulesYaml :: Maybe String , outPath :: Maybe String @@ -32,17 +39,32 @@ data CLINamedParams = CLINamedParams } deriving (Generic, Show, ParseRecord, Read, ParseField) -instance ParseFields CLINamedParams where +instance ParseFields CLI'TransformPhi'Params where parseFields _ _ _ _ = - CLINamedParams + CLI'TransformPhi'Params <$> parseFields (Just "Print out steps of reduction") (Just "chain") (Just 'c') Nothing <*> parseFields (Just "Path to the Yaml file with custom rules") (Just "rules-yaml") Nothing Nothing <*> parseFields (Just "Output file path (defaults to stdout)") (Just "output") (Just 'o') Nothing <*> parseFields (Just "Print a single normalized expression") (Just "single") (Just 's') Nothing <*> parseFields (Just "Print JSON") (Just "json") (Just 'j') Nothing -data CLIOptions = CLIOptions CLINamedParams (Maybe FilePath) - deriving (Generic, Show, ParseRecord) +data CLI + = CLI'TransformPhi CLI'TransformPhi'Params (Maybe FilePath) + | CLI'MetricsPhi (Maybe FilePath) + deriving (Generic, Show) + +modifiers :: Modifiers +modifiers = + lispCaseModifiers + { constructorNameModifier = \x -> + stripPrefix "CLI'" x + & fromMaybe "" + & lispCaseModifiers.constructorNameModifier + , shortNameModifier = firstLetter + } + +instance ParseRecord CLI where + parseRecord = parseRecordWithModifiers modifiers data StructuredJSON = StructuredJSON { input :: String @@ -50,62 +72,74 @@ data StructuredJSON = StructuredJSON } deriving (Generic, ToJSON) -encodeString :: (ToJSON a) => a -> String -encodeString = (^. unpacked) . encodeToLazyText +encodeToJSONString :: (ToJSON a) => a -> String +encodeToJSONString = (^. unpacked) . encodeToLazyText main :: IO () main = do opts <- getRecord "Normalizer" - let (CLIOptions params inPath) = opts - let (CLINamedParams{..}) = params - case rulesYaml of - Just path -> do - handle <- maybe (pure stdout) (`openFile` WriteMode) outPath - let logStr = hPutStr handle - let logStrLn = hPutStrLn handle - ruleSet <- parseRuleSetFromFile path - unless (single || json) $ logStrLn ruleSet.title + case opts of + CLI'MetricsPhi inPath -> do src <- maybe getContents readFile inPath let progOrError = parseProgram src case progOrError of Left err -> error ("An error occurred parsing the input program: " <> err) - Right input@(Program bindings) -> do - let results - | chain = applyRulesChain (Context (convertRule <$> ruleSet.rules) [Formation bindings]) (Formation bindings) - | otherwise = pure <$> applyRules (Context (convertRule <$> ruleSet.rules) [Formation bindings]) (Formation bindings) - uniqueResults = nub results - totalResults = length uniqueResults - when (null uniqueResults || null (head uniqueResults)) $ error "Could not normalize the program" - let printFormationAsProgramOrObject = \case - Formation bindings' -> printTree $ Program bindings' - x -> printTree x - if single - then - logStrLn - . (if json then encodeString else id) - . printFormationAsProgramOrObject - $ head (head uniqueResults) - else do - if json + Right a -> do + let handle = stdout + let logStrLn = hPutStrLn handle + metrics = collectMetrics a + logStrLn $ encodeToJSONString metrics + CLI'TransformPhi params inPath -> do + let (CLI'TransformPhi'Params{..}) = params + case rulesYaml of + Just path -> do + handle <- maybe (pure stdout) (`openFile` WriteMode) outPath + let logStr = hPutStr handle + logStrLn = hPutStrLn handle + ruleSet <- parseRuleSetFromFile path + unless (single || json) $ logStrLn ruleSet.title + src <- maybe getContents readFile inPath + let progOrError = parseProgram src + case progOrError of + Left err -> error ("An error occurred parsing the input program: " <> err) + Right input@(Program bindings) -> do + let + results + | chain = applyRulesChain (Context (convertRule <$> ruleSet.rules) [Formation bindings]) (Formation bindings) + | otherwise = pure <$> applyRules (Context (convertRule <$> ruleSet.rules) [Formation bindings]) (Formation bindings) + uniqueResults = nub results + totalResults = length uniqueResults + when (null uniqueResults || null (head uniqueResults)) $ error "Could not normalize the program" + let printFormationAsProgramOrObject = \case + Formation bindings' -> printTree $ Program bindings' + x -> printTree x + if single then - logStrLn . encodeString $ - StructuredJSON - { input = printTree input - , results = (printFormationAsProgramOrObject <$>) <$> results - } + logStrLn + . (if json then encodeToJSONString else id) + . printFormationAsProgramOrObject + $ head (head uniqueResults) else do - logStrLn "Input:" - logStrLn (printTree input) - logStrLn "====================================================" - forM_ (zip [1 ..] uniqueResults) $ \(i, steps) -> do - logStrLn $ - "Result " <> show i <> " out of " <> show totalResults <> ":" - let n = length steps - forM_ (zip [1 ..] steps) $ \(k, step) -> do - when chain $ - logStr ("[ " <> show k <> " / " <> show n <> " ]") - logStrLn . printFormationAsProgramOrObject $ step - logStrLn "----------------------------------------------------" - hClose handle - -- TODO #48:15m still need to consider `chain` (should rewrite/change defaultMain to mainWithOptions) - Nothing -> defaultMain + if json + then + logStrLn . encodeToJSONString $ + StructuredJSON + { input = printTree input + , results = (printFormationAsProgramOrObject <$>) <$> results + } + else do + logStrLn "Input:" + logStrLn (printTree input) + logStrLn "====================================================" + forM_ (zip [1 ..] uniqueResults) $ \(i, steps) -> do + logStrLn $ + "Result " <> show i <> " out of " <> show totalResults <> ":" + let n = length steps + forM_ (zip [1 ..] steps) $ \(k, step) -> do + when chain $ + logStr ("[ " <> show k <> " / " <> show n <> " ]") + logStrLn . printFormationAsProgramOrObject $ step + logStrLn "----------------------------------------------------" + hClose handle + -- TODO #48:15m still need to consider `chain` (should rewrite/change defaultMain to mainWithOptions) + Nothing -> defaultMain diff --git a/eo-phi-normalizer/src/Language/EO/Phi/Metrics/Collect.hs b/eo-phi-normalizer/src/Language/EO/Phi/Metrics/Collect.hs index 3df16ca63..2e0d3630f 100644 --- a/eo-phi-normalizer/src/Language/EO/Phi/Metrics/Collect.hs +++ b/eo-phi-normalizer/src/Language/EO/Phi/Metrics/Collect.hs @@ -17,6 +17,7 @@ import Control.Lens ((+=)) import Control.Monad (forM_) import Control.Monad.State (State, execState) import Data.Aeson (FromJSON) +import Data.Aeson.Types (ToJSON) import Data.Generics.Labels () import GHC.Generics (Generic) import Language.EO.Phi.Rules.Common () @@ -28,7 +29,7 @@ data Metrics = Metrics , formations :: Int , dispatches :: Int } - deriving (Generic, Show, FromJSON, Eq) + deriving (Generic, Show, FromJSON, ToJSON, Eq) defaultMetrics :: Metrics defaultMetrics = From 98e00a677f571c41b2d2fd068e86014f78ce75bc Mon Sep 17 00:00:00 2001 From: Danila Danko Date: Sun, 25 Feb 2024 14:13:47 +0300 Subject: [PATCH 02/42] fix: executable name --- eo-phi-normalizer/eo-phi-normalizer.cabal | 2 +- eo-phi-normalizer/package.yaml | 2 +- hie.yaml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/eo-phi-normalizer/eo-phi-normalizer.cabal b/eo-phi-normalizer/eo-phi-normalizer.cabal index 77e08d162..89b12078c 100644 --- a/eo-phi-normalizer/eo-phi-normalizer.cabal +++ b/eo-phi-normalizer/eo-phi-normalizer.cabal @@ -69,7 +69,7 @@ library , yaml default-language: Haskell2010 -executable normalize-phi +executable normalizer main-is: Main.hs other-modules: Paths_eo_phi_normalizer diff --git a/eo-phi-normalizer/package.yaml b/eo-phi-normalizer/package.yaml index 3850d34f4..a41ed41c8 100644 --- a/eo-phi-normalizer/package.yaml +++ b/eo-phi-normalizer/package.yaml @@ -75,7 +75,7 @@ library: - Language.EO.Phi.Rules.Syntax.Skel executables: - normalize-phi: + normalizer: main: Main.hs source-dirs: app ghc-options: diff --git a/hie.yaml b/hie.yaml index debcb134c..f293a5698 100644 --- a/hie.yaml +++ b/hie.yaml @@ -4,7 +4,7 @@ cradle: component: "eo-phi-normalizer:lib" - path: "eo-phi-normalizer/app/Main.hs" - component: "eo-phi-normalizer:exe:normalize-phi" + component: "eo-phi-normalizer:exe:normalizer" - path: "eo-phi-normalizer/test" component: "eo-phi-normalizer:test:eo-phi-normalizer-test" From 8dbf6adce49cfa8ed79f01663dea5d88047d4f64 Mon Sep 17 00:00:00 2001 From: Danila Danko Date: Mon, 26 Feb 2024 16:22:51 +0300 Subject: [PATCH 03/42] fix: switch to optparse-applicative --- eo-phi-normalizer/eo-phi-normalizer.cabal | 2 +- eo-phi-normalizer/package.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/eo-phi-normalizer/eo-phi-normalizer.cabal b/eo-phi-normalizer/eo-phi-normalizer.cabal index 89b12078c..dd0f26c8e 100644 --- a/eo-phi-normalizer/eo-phi-normalizer.cabal +++ b/eo-phi-normalizer/eo-phi-normalizer.cabal @@ -93,7 +93,7 @@ executable normalizer , generic-lens , lens , mtl - , optparse-generic + , optparse-applicative , string-interpolate , yaml default-language: Haskell2010 diff --git a/eo-phi-normalizer/package.yaml b/eo-phi-normalizer/package.yaml index a41ed41c8..896ffa136 100644 --- a/eo-phi-normalizer/package.yaml +++ b/eo-phi-normalizer/package.yaml @@ -84,7 +84,7 @@ executables: - -with-rtsopts=-N dependencies: - eo-phi-normalizer - - optparse-generic + - optparse-applicative tests: eo-phi-normalizer-test: From dcfe91ba7aa6d669bab52656f676d2cb1b4c6780 Mon Sep 17 00:00:00 2001 From: Danila Danko Date: Mon, 26 Feb 2024 16:24:02 +0300 Subject: [PATCH 04/42] refactor: cli --- eo-phi-normalizer/app/Main.hs | 276 +++++++++++++++++++++------------- 1 file changed, 174 insertions(+), 102 deletions(-) diff --git a/eo-phi-normalizer/app/Main.hs b/eo-phi-normalizer/app/Main.hs index 7d8ce6456..63167f3b1 100644 --- a/eo-phi-normalizer/app/Main.hs +++ b/eo-phi-normalizer/app/Main.hs @@ -1,13 +1,16 @@ +{-# LANGUAGE ApplicativeDo #-} +{-# LANGUAGE ConstraintKinds #-} {-# LANGUAGE DataKinds #-} {-# LANGUAGE DeriveAnyClass #-} {-# LANGUAGE DeriveGeneric #-} {-# LANGUAGE DuplicateRecordFields #-} +{-# LANGUAGE ImplicitParams #-} {-# LANGUAGE LambdaCase #-} {-# LANGUAGE OverloadedLists #-} {-# LANGUAGE OverloadedRecordDot #-} {-# LANGUAGE OverloadedStrings #-} +{-# LANGUAGE QuasiQuotes #-} {-# LANGUAGE RecordWildCards #-} -{-# LANGUAGE TypeOperators #-} {-# OPTIONS_GHC -Wno-partial-fields #-} {-# OPTIONS_GHC -Wno-type-defaults #-} @@ -19,127 +22,196 @@ import Data.Foldable (forM_) import Control.Lens ((^.)) import Data.Aeson (ToJSON) import Data.Aeson.Text (encodeToLazyText) -import Data.Function ((&)) -import Data.List (nub, stripPrefix) -import Data.Maybe (fromMaybe) +import Data.List (nub) +import Data.String.Interpolate (i) import Data.Text.Lazy.Lens -import Language.EO.Phi (Object (Formation), Program (Program), defaultMain, parseProgram, printTree) +import GHC.Generics (Generic) +import Language.EO.Phi (Object (Formation), Program (Program), parseProgram, printTree) import Language.EO.Phi.Metrics.Collect (collectMetrics) -import Language.EO.Phi.Rules.Common (Context (..), applyRules, applyRulesChain) +import Language.EO.Phi.Rules.Common (applyRules, applyRulesChain) +import Language.EO.Phi.Rules.Common qualified as Common import Language.EO.Phi.Rules.Yaml (RuleSet (rules, title), convertRule, parseRuleSetFromFile) -import Options.Generic -import System.IO (IOMode (WriteMode), hClose, hPutStr, hPutStrLn, openFile, stdout) +import Options.Applicative +import Options.Applicative.Types qualified as Optparse (Context (..)) +import System.IO (IOMode (WriteMode), hPutStr, hPutStrLn, openFile, stdout) -data CLI'TransformPhi'Params = CLI'TransformPhi'Params +data CLI'TransformPhi = CLI'TransformPhi { chain :: Bool - , rulesYaml :: Maybe String - , outPath :: Maybe String + , rulesPath :: String + , outputFile :: Maybe String , single :: Bool , json :: Bool + , inputFile :: Maybe FilePath + , program :: Maybe String } - deriving (Generic, Show, ParseRecord, Read, ParseField) + deriving (Show) -instance ParseFields CLI'TransformPhi'Params where - parseFields _ _ _ _ = - CLI'TransformPhi'Params - <$> parseFields (Just "Print out steps of reduction") (Just "chain") (Just 'c') Nothing - <*> parseFields (Just "Path to the Yaml file with custom rules") (Just "rules-yaml") Nothing Nothing - <*> parseFields (Just "Output file path (defaults to stdout)") (Just "output") (Just 'o') Nothing - <*> parseFields (Just "Print a single normalized expression") (Just "single") (Just 's') Nothing - <*> parseFields (Just "Print JSON") (Just "json") (Just 'j') Nothing +data CLI'MetricsPhi = CLI'MetricsPhi + { json :: Bool + , inputFile :: Maybe FilePath + , outputFile :: Maybe FilePath + , program :: Maybe String + } + deriving (Show) data CLI - = CLI'TransformPhi CLI'TransformPhi'Params (Maybe FilePath) - | CLI'MetricsPhi (Maybe FilePath) - deriving (Generic, Show) - -modifiers :: Modifiers -modifiers = - lispCaseModifiers - { constructorNameModifier = \x -> - stripPrefix "CLI'" x - & fromMaybe "" - & lispCaseModifiers.constructorNameModifier - , shortNameModifier = firstLetter - } - -instance ParseRecord CLI where - parseRecord = parseRecordWithModifiers modifiers + = CLI'TransformPhi' CLI'TransformPhi + | CLI'MetricsPhi' CLI'MetricsPhi + deriving (Show) + +_FILE :: String +_FILE = "FILE" + +_PROGRAM :: String +_PROGRAM = "PROGRAM" + +fileMetavar :: Mod OptionFields a +fileMetavar = metavar _FILE + +inputFileLongName :: String +inputFileLongName = "input-file" + +inputFileOption :: Parser (Maybe FilePath) +inputFileOption = optional $ strOption (long inputFileLongName <> short 'i' <> help [i|#{_FILE} to read input from. You must specify either this option or #{_PROGRAM}.|] <> fileMetavar) + +outputFileOption :: Parser (Maybe String) +outputFileOption = optional $ strOption (long "output-file" <> short 'o' <> help [i|Output to #{_FILE}. Output to stdout otherwise.|] <> fileMetavar) + +programArg :: Parser (Maybe String) +programArg = optional $ strArgument (metavar _PROGRAM <> help "Program to work with.") + +jsonSwitch :: Parser Bool +jsonSwitch = switch (long "json" <> short 'j' <> help "Output JSON.") + +cli'TransformPhi :: Parser CLI'TransformPhi +cli'TransformPhi = do + rulesPath <- strOption (long "rules" <> short 'r' <> help [i|#{_FILE} with user-defined rules.|] <> fileMetavar) + inputFile <- inputFileOption + program <- programArg + chain <- switch (long "chain" <> short 'c' <> help "Output transformation steps.") + json <- jsonSwitch + outputFile <- outputFileOption + single <- switch (long "single" <> short 's' <> help "Output a single expression.") + pure CLI'TransformPhi{..} + +cli'MetricsPhi :: Parser CLI'MetricsPhi +cli'MetricsPhi = do + json <- jsonSwitch + inputFile <- inputFileOption + outputFile <- outputFileOption + program <- programArg + pure CLI'MetricsPhi{..} + +metricsParserInfo :: ParserInfo CLI +metricsParserInfo = info (CLI'MetricsPhi' <$> cli'MetricsPhi) (progDesc "Collect metrics for a PHI program.") + +transformParserInfo :: ParserInfo CLI +transformParserInfo = info (CLI'TransformPhi' <$> cli'TransformPhi) (progDesc "Transform a PHI program.") + +transformCommandName :: String +transformCommandName = "transform" + +metricsCommandName :: String +metricsCommandName = "metrics" + +cli :: Parser CLI +cli = + hsubparser + ( command transformCommandName transformParserInfo + <> command metricsCommandName metricsParserInfo + ) + +cliOpts :: ParserInfo CLI +cliOpts = + info + (cli <**> helper) + (fullDesc <> progDesc "Work with PHI expressions.") data StructuredJSON = StructuredJSON { input :: String - , results :: [[String]] + , output :: [[String]] } deriving (Generic, ToJSON) encodeToJSONString :: (ToJSON a) => a -> String encodeToJSONString = (^. unpacked) . encodeToLazyText +pprefs :: ParserPrefs +pprefs = prefs (showHelpOnEmpty <> showHelpOnError) + +type Context = (?parserContext :: Optparse.Context) + +die :: (Context) => String -> IO a +die message = do + handleParseResult . Failure $ + parserFailure pprefs cliOpts (ErrorMsg message) [?parserContext] + +getProgram :: (Context) => Maybe FilePath -> Maybe String -> IO Program +getProgram inputFile expression = do + src <- + case (inputFile, expression) of + (Just inputFile', Nothing) -> readFile inputFile' + (Nothing, Just expression') -> pure expression' + _ -> die [i|You must specify either -#{head inputFileLongName}|--#{inputFileLongName} #{_FILE} or #{_PROGRAM}|] + case parseProgram src of + Left err -> die [i|"An error occurred parsing the input program: #{err}|] + Right program -> pure program + +getLoggers :: Maybe FilePath -> IO (String -> IO (), String -> IO ()) +getLoggers outputFile = do + handle <- maybe (pure stdout) (`openFile` WriteMode) outputFile + pure (hPutStrLn handle, hPutStr handle) + main :: IO () main = do - opts <- getRecord "Normalizer" + opts <- customExecParser pprefs cliOpts case opts of - CLI'MetricsPhi inPath -> do - src <- maybe getContents readFile inPath - let progOrError = parseProgram src - case progOrError of - Left err -> error ("An error occurred parsing the input program: " <> err) - Right a -> do - let handle = stdout - let logStrLn = hPutStrLn handle - metrics = collectMetrics a - logStrLn $ encodeToJSONString metrics - CLI'TransformPhi params inPath -> do - let (CLI'TransformPhi'Params{..}) = params - case rulesYaml of - Just path -> do - handle <- maybe (pure stdout) (`openFile` WriteMode) outPath - let logStr = hPutStr handle - logStrLn = hPutStrLn handle - ruleSet <- parseRuleSetFromFile path - unless (single || json) $ logStrLn ruleSet.title - src <- maybe getContents readFile inPath - let progOrError = parseProgram src - case progOrError of - Left err -> error ("An error occurred parsing the input program: " <> err) - Right input@(Program bindings) -> do - let - results - | chain = applyRulesChain (Context (convertRule <$> ruleSet.rules) [Formation bindings]) (Formation bindings) - | otherwise = pure <$> applyRules (Context (convertRule <$> ruleSet.rules) [Formation bindings]) (Formation bindings) - uniqueResults = nub results - totalResults = length uniqueResults - when (null uniqueResults || null (head uniqueResults)) $ error "Could not normalize the program" - let printFormationAsProgramOrObject = \case - Formation bindings' -> printTree $ Program bindings' - x -> printTree x - if single - then - logStrLn - . (if json then encodeToJSONString else id) - . printFormationAsProgramOrObject - $ head (head uniqueResults) - else do - if json - then - logStrLn . encodeToJSONString $ - StructuredJSON - { input = printTree input - , results = (printFormationAsProgramOrObject <$>) <$> results - } - else do - logStrLn "Input:" - logStrLn (printTree input) - logStrLn "====================================================" - forM_ (zip [1 ..] uniqueResults) $ \(i, steps) -> do - logStrLn $ - "Result " <> show i <> " out of " <> show totalResults <> ":" - let n = length steps - forM_ (zip [1 ..] steps) $ \(k, step) -> do - when chain $ - logStr ("[ " <> show k <> " / " <> show n <> " ]") - logStrLn . printFormationAsProgramOrObject $ step - logStrLn "----------------------------------------------------" - hClose handle - -- TODO #48:15m still need to consider `chain` (should rewrite/change defaultMain to mainWithOptions) - Nothing -> defaultMain + CLI'MetricsPhi' CLI'MetricsPhi{..} -> do + let ?parserContext = Optparse.Context metricsCommandName metricsParserInfo + program' <- getProgram inputFile program + (logStrLn, _) <- getLoggers outputFile + let metrics = collectMetrics program' + logStrLn $ encodeToJSONString metrics + CLI'TransformPhi' CLI'TransformPhi{..} -> do + let ?parserContext = Optparse.Context transformCommandName transformParserInfo + program' <- getProgram inputFile program + (logStrLn, logStr) <- getLoggers outputFile + ruleSet <- parseRuleSetFromFile rulesPath + unless (single || json) $ logStrLn ruleSet.title + let Program bindings = program' + results + | chain = applyRulesChain (Common.Context (convertRule <$> ruleSet.rules) [Formation bindings]) (Formation bindings) + | otherwise = pure <$> applyRules (Common.Context (convertRule <$> ruleSet.rules) [Formation bindings]) (Formation bindings) + uniqueResults = nub results + totalResults = length uniqueResults + when (null uniqueResults || null (head uniqueResults)) $ die [i|Could not normalize the #{_PROGRAM}.|] + let printAsProgramOrAsObject = \case + Formation bindings' -> printTree $ Program bindings' + x -> printTree x + if single + then + logStrLn + . (if json then encodeToJSONString else id) + . printAsProgramOrAsObject + $ head (head uniqueResults) + else do + if json + then + logStrLn . encodeToJSONString $ + StructuredJSON + { input = printTree program' + , output = (printAsProgramOrAsObject <$>) <$> results + } + else do + logStrLn "Input:" + logStrLn (printTree program') + logStrLn "====================================================" + forM_ (zip [1 ..] uniqueResults) $ \(index, steps) -> do + logStrLn $ + "Result " <> show index <> " out of " <> show totalResults <> ":" + let n = length steps + forM_ (zip [1 ..] steps) $ \(k, step) -> do + when chain $ + logStr ("[ " <> show k <> " / " <> show n <> " ]") + logStrLn . printAsProgramOrAsObject $ step + logStrLn "----------------------------------------------------" From becae09dfe3ebed01a05451618bec9e71be3b175 Mon Sep 17 00:00:00 2001 From: Danila Danko Date: Mon, 26 Feb 2024 17:17:06 +0300 Subject: [PATCH 05/42] fix: remove unused option --- eo-phi-normalizer/app/Main.hs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/eo-phi-normalizer/app/Main.hs b/eo-phi-normalizer/app/Main.hs index 63167f3b1..706e44f6a 100644 --- a/eo-phi-normalizer/app/Main.hs +++ b/eo-phi-normalizer/app/Main.hs @@ -47,8 +47,7 @@ data CLI'TransformPhi = CLI'TransformPhi deriving (Show) data CLI'MetricsPhi = CLI'MetricsPhi - { json :: Bool - , inputFile :: Maybe FilePath + { inputFile :: Maybe FilePath , outputFile :: Maybe FilePath , program :: Maybe String } @@ -96,7 +95,6 @@ cli'TransformPhi = do cli'MetricsPhi :: Parser CLI'MetricsPhi cli'MetricsPhi = do - json <- jsonSwitch inputFile <- inputFileOption outputFile <- outputFileOption program <- programArg From dd5098755e76bb69929f572391b83e3ba0c0cf4c Mon Sep 17 00:00:00 2001 From: Danila Danko Date: Tue, 27 Feb 2024 02:23:21 +0300 Subject: [PATCH 06/42] feat: script to run mdsh --- flake.lock | 72 +++++++++++++++++++++++++++++++++++++++++++++++++++++- flake.nix | 21 ++++++++++++++-- 2 files changed, 90 insertions(+), 3 deletions(-) diff --git a/flake.lock b/flake.lock index bb01383d7..d3cf6bbfa 100644 --- a/flake.lock +++ b/flake.lock @@ -16,6 +16,24 @@ "type": "github" } }, + "flake-utils": { + "inputs": { + "systems": "systems" + }, + "locked": { + "lastModified": 1705309234, + "narHash": "sha256-uNRRNRKmJyCRC/8y1RqBkqWBLM034y4qN7EprSdmgyA=", + "owner": "numtide", + "repo": "flake-utils", + "rev": "1ef2e671c3b0c19053962c07dbda38332dcebf26", + "type": "github" + }, + "original": { + "owner": "numtide", + "repo": "flake-utils", + "type": "github" + } + }, "flakes": { "locked": { "lastModified": 1704589266, @@ -43,11 +61,63 @@ "url": "https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.2.0/maven-wrapper-3.2.0.jar" } }, + "mdsh": { + "inputs": { + "flake-utils": "flake-utils", + "nixpkgs": "nixpkgs" + }, + "locked": { + "lastModified": 1708988125, + "narHash": "sha256-nx9+tTLliKBPT/TSKTBSYOYxglewOKiw+gYLBvV+9Bg=", + "owner": "deemp", + "repo": "mdsh", + "rev": "ab378bfd391e5147e29fb0812e1dea7e079a1559", + "type": "github" + }, + "original": { + "owner": "deemp", + "ref": "update-flake", + "repo": "mdsh", + "type": "github" + } + }, + "nixpkgs": { + "locked": { + "lastModified": 1708847675, + "narHash": "sha256-RUZ7KEs/a4EzRELYDGnRB6i7M1Izii3JD/LyzH0c6Tg=", + "owner": "nixos", + "repo": "nixpkgs", + "rev": "2a34566b67bef34c551f204063faeecc444ae9da", + "type": "github" + }, + "original": { + "owner": "nixos", + "ref": "nixpkgs-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, "root": { "inputs": { "eoc": "eoc", "flakes": "flakes", - "maven-wrapper-jar": "maven-wrapper-jar" + "maven-wrapper-jar": "maven-wrapper-jar", + "mdsh": "mdsh" + } + }, + "systems": { + "locked": { + "lastModified": 1681028828, + "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", + "owner": "nix-systems", + "repo": "default", + "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", + "type": "github" + }, + "original": { + "owner": "nix-systems", + "repo": "default", + "type": "github" } } }, diff --git a/flake.nix b/flake.nix index 03b4f8d99..5312f3587 100644 --- a/flake.nix +++ b/flake.nix @@ -11,6 +11,7 @@ flake = false; url = "https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.2.0/maven-wrapper-3.2.0.jar"; }; + mdsh.url = "github:deemp/mdsh/update-flake"; }; outputs = inputs: inputs.flakes.makeFlake { inputs = { @@ -18,7 +19,7 @@ haskell-tools drv-tools devshell flakes-tools nixpkgs formatter slimlock; - inherit (inputs) eoc maven-wrapper-jar; + inherit (inputs) eoc maven-wrapper-jar mdsh; }; perSystem = { inputs, system }: let @@ -33,6 +34,7 @@ inherit (inputs.drv-tools.lib.${system}) mkShellApps; inherit (inputs.flakes-tools.lib.${system}) mkFlakesTools; inherit (inputs.haskell-tools.lib.${system}) toolsGHC; + mdsh = inputs.mdsh.packages.${system}.default; # --- Parameters --- @@ -96,6 +98,7 @@ cabal stack pkgs.gh + mdsh # `cabal` already has a `ghc` on its `PATH`, # so you may remove `ghc` from this list. # Then, you can access `ghc` like `cabal exec -- ghc --version`. @@ -131,7 +134,7 @@ }; }; - # + # # --- Haskell package --- @@ -165,6 +168,20 @@ description = "Run pipeline"; excludeShellChecks = [ "SC2139" ]; }; + + mdsh = { + runtimeInputs = [ + mdsh + pkgs.nodePackages.prettier + stack + ]; + text = '' + export LANG=C.utf8 + mdsh + mdsh -i site/docs/src/user-defined-rules.md --work_dir . + prettier -w "**/*.md" + ''; + }; }; # --- Devshells --- From 3b6086e26f8a08ee5557a35768d77cc608c6b9d1 Mon Sep 17 00:00:00 2001 From: Danila Danko Date: Tue, 27 Feb 2024 02:23:52 +0300 Subject: [PATCH 07/42] chore: update mds --- README.md | 74 +++++++++++++++---------- site/docs/src/user-defined-rules.md | 85 ++++++++++++++++++++++++----- 2 files changed, 118 insertions(+), 41 deletions(-) diff --git a/README.md b/README.md index ec3f483fe..c2026e8d7 100644 --- a/README.md +++ b/README.md @@ -55,22 +55,21 @@ stack uninstall Learn about `normalize-phi` options. -```sh -normalize-phi --help +```$ as console +normalizer --help ``` ```console -Normalizer +Usage: normalizer COMMAND -Usage: normalize-phi [-c|--chain] [--rules-yaml STRING] [-o|--output STRING] - [-s|--single] [STRING] + Work with PHI expressions. Available options: -h,--help Show this help text - -c,--chain Print out steps of reduction - --rules-yaml STRING Path to the Yaml file with custom rules - -o,--output STRING Output file path (defaults to stdout) - -s,--single Print a single normalized expression + +Available commands: + transform Transform a PHI program. + metrics Collect metrics for a PHI program. ``` ### Sample program @@ -78,7 +77,7 @@ Available options: Save a $\varphi$-calculus program to a file. This program will be used in subsequent commands. -```sh +```$ cat > program.phi < +## CLI + +Use the `transform` command to transform `PHI` programs. + +```$ as console +normalizer transform --help +``` + +```console +Usage: normalizer transform (-r|--rules FILE) [-i|--input-file FILE] [PROGRAM] + [-c|--chain] [-j|--json] [-o|--output-file FILE] + [-s|--single] + + Transform a PHI program. + +Available options: + -r,--rules FILE FILE with user-defined rules. + -i,--input-file FILE FILE to read input from. You must specify either this + option or PROGRAM. + PROGRAM Program to work with. + -c,--chain Output transformation steps. + -j,--json Output JSON. + -o,--output-file FILE Output to FILE. Output to stdout otherwise. + -s,--single Output a single expression. + -h,--help Show this help text +``` + ## Use rules {{#include ./common/normalize-phi-options.md}} @@ -69,17 +96,17 @@ Normalize a 𝜑-expression from `program.phi` using the [yegor.yaml](#yegoryaml There can be multiple numbered results that correspond to multiple rule application sequences. -```sh -normalize-phi --rules-yaml ./eo-phi-normalizer/test/eo/phi/rules/yegor.yaml program.phi +```$ as console +normalizer transform --rules ./eo-phi-normalizer/test/eo/phi/rules/yegor.yaml -i program.phi ``` ```console Rule set based on Yegor's draft Input: -{ a ↦ ⟦ b ↦ ⟦ c ↦ ∅, d ↦ ⟦ φ ↦ ξ.ρ.c ⟧ ⟧, e ↦ ξ.b (c ↦ ⟦ ⟧).d ⟧.e } +{ ⟦ a ↦ ⟦ b ↦ ⟦ c ↦ ∅, d ↦ ⟦ φ ↦ ξ.ρ.c ⟧ ⟧, e ↦ ξ.b (c ↦ ⟦ ⟧).d ⟧.e ⟧ } ==================================================== Result 1 out of 1: -⟦ a ↦ ξ.b (c ↦ ⟦ ⟧).d (ρ ↦ ⟦ b ↦ ⟦ c ↦ ∅, d ↦ ⟦ φ ↦ ξ.ρ.c ⟧ ⟧ ⟧) ⟧ +{ ⟦ a ↦ ξ.b (c ↦ ⟦ ⟧).d (ρ ↦ ⟦ b ↦ ⟦ d ↦ ⟦ φ ↦ ξ.ρ.c, ν ↦ ⟦ Δ ⤍ 00- ⟧ ⟧, c ↦ ∅, ν ↦ ⟦ Δ ⤍ 00- ⟧ ⟧ ⟧) ⟧ } ---------------------------------------------------- ``` @@ -87,18 +114,50 @@ Result 1 out of 1: Use `--chain` to see numbered normalization steps for each normalization result. -```sh -normalize-phi --chain --rules-yaml ./eo-phi-normalizer/test/eo/phi/rules/yegor.yaml program.phi +```$ as console +normalizer transform --chain --rules ./eo-phi-normalizer/test/eo/phi/rules/yegor.yaml --input-file program.phi ``` ```console Rule set based on Yegor's draft Input: -{ a ↦ ⟦ b ↦ ⟦ c ↦ ∅, d ↦ ⟦ φ ↦ ξ.ρ.c ⟧ ⟧, e ↦ ξ.b (c ↦ ⟦ ⟧).d ⟧.e } +{ ⟦ a ↦ ⟦ b ↦ ⟦ c ↦ ∅, d ↦ ⟦ φ ↦ ξ.ρ.c ⟧ ⟧, e ↦ ξ.b (c ↦ ⟦ ⟧).d ⟧.e ⟧ } ==================================================== -Result 1 out of 1: -[ 1 / 2 ]⟦ a ↦ ⟦ b ↦ ⟦ c ↦ ∅, d ↦ ⟦ φ ↦ ξ.ρ.c ⟧ ⟧, e ↦ ξ.b (c ↦ ⟦ ⟧).d ⟧.e ⟧ -[ 2 / 2 ]⟦ a ↦ ξ.b (c ↦ ⟦ ⟧).d (ρ ↦ ⟦ b ↦ ⟦ c ↦ ∅, d ↦ ⟦ φ ↦ ξ.ρ.c ⟧ ⟧ ⟧) ⟧ +Result 1 out of 6: +[ 1 / 4 ]{ ⟦ a ↦ ⟦ b ↦ ⟦ c ↦ ∅, d ↦ ⟦ φ ↦ ξ.ρ.c ⟧ ⟧, e ↦ ξ.b (c ↦ ⟦ ⟧).d ⟧.e ⟧ } +[ 2 / 4 ]{ ⟦ a ↦ ξ.b (c ↦ ⟦ ⟧).d (ρ ↦ ⟦ b ↦ ⟦ c ↦ ∅, d ↦ ⟦ φ ↦ ξ.ρ.c ⟧ ⟧ ⟧) ⟧ } +[ 3 / 4 ]{ ⟦ a ↦ ξ.b (c ↦ ⟦ ⟧).d (ρ ↦ ⟦ b ↦ ⟦ c ↦ ∅, d ↦ ⟦ φ ↦ ξ.ρ.c ⟧, ν ↦ ⟦ Δ ⤍ 00- ⟧ ⟧ ⟧) ⟧ } +[ 4 / 4 ]{ ⟦ a ↦ ξ.b (c ↦ ⟦ ⟧).d (ρ ↦ ⟦ b ↦ ⟦ d ↦ ⟦ φ ↦ ξ.ρ.c, ν ↦ ⟦ Δ ⤍ 00- ⟧ ⟧, c ↦ ∅, ν ↦ ⟦ Δ ⤍ 00- ⟧ ⟧ ⟧) ⟧ } +---------------------------------------------------- +Result 2 out of 6: +[ 1 / 4 ]{ ⟦ a ↦ ⟦ b ↦ ⟦ c ↦ ∅, d ↦ ⟦ φ ↦ ξ.ρ.c ⟧ ⟧, e ↦ ξ.b (c ↦ ⟦ ⟧).d ⟧.e ⟧ } +[ 2 / 4 ]{ ⟦ a ↦ ξ.b (c ↦ ⟦ ⟧).d (ρ ↦ ⟦ b ↦ ⟦ c ↦ ∅, d ↦ ⟦ φ ↦ ξ.ρ.c ⟧ ⟧ ⟧) ⟧ } +[ 3 / 4 ]{ ⟦ a ↦ ξ.b (c ↦ ⟦ ⟧).d (ρ ↦ ⟦ b ↦ ⟦ d ↦ ⟦ φ ↦ ξ.ρ.c, ν ↦ ⟦ Δ ⤍ 00- ⟧ ⟧, c ↦ ∅ ⟧ ⟧) ⟧ } +[ 4 / 4 ]{ ⟦ a ↦ ξ.b (c ↦ ⟦ ⟧).d (ρ ↦ ⟦ b ↦ ⟦ d ↦ ⟦ φ ↦ ξ.ρ.c, ν ↦ ⟦ Δ ⤍ 00- ⟧ ⟧, c ↦ ∅, ν ↦ ⟦ Δ ⤍ 00- ⟧ ⟧ ⟧) ⟧ } +---------------------------------------------------- +Result 3 out of 6: +[ 1 / 4 ]{ ⟦ a ↦ ⟦ b ↦ ⟦ c ↦ ∅, d ↦ ⟦ φ ↦ ξ.ρ.c ⟧ ⟧, e ↦ ξ.b (c ↦ ⟦ ⟧).d ⟧.e ⟧ } +[ 2 / 4 ]{ ⟦ a ↦ ⟦ b ↦ ⟦ c ↦ ∅, d ↦ ⟦ φ ↦ ξ.ρ.c ⟧, ν ↦ ⟦ Δ ⤍ 00- ⟧ ⟧, e ↦ ξ.b (c ↦ ⟦ ⟧).d ⟧.e ⟧ } +[ 3 / 4 ]{ ⟦ a ↦ ξ.b (c ↦ ⟦ ⟧).d (ρ ↦ ⟦ b ↦ ⟦ c ↦ ∅, d ↦ ⟦ φ ↦ ξ.ρ.c ⟧, ν ↦ ⟦ Δ ⤍ 00- ⟧ ⟧ ⟧) ⟧ } +[ 4 / 4 ]{ ⟦ a ↦ ξ.b (c ↦ ⟦ ⟧).d (ρ ↦ ⟦ b ↦ ⟦ d ↦ ⟦ φ ↦ ξ.ρ.c, ν ↦ ⟦ Δ ⤍ 00- ⟧ ⟧, c ↦ ∅, ν ↦ ⟦ Δ ⤍ 00- ⟧ ⟧ ⟧) ⟧ } +---------------------------------------------------- +Result 4 out of 6: +[ 1 / 4 ]{ ⟦ a ↦ ⟦ b ↦ ⟦ c ↦ ∅, d ↦ ⟦ φ ↦ ξ.ρ.c ⟧ ⟧, e ↦ ξ.b (c ↦ ⟦ ⟧).d ⟧.e ⟧ } +[ 2 / 4 ]{ ⟦ a ↦ ⟦ b ↦ ⟦ c ↦ ∅, d ↦ ⟦ φ ↦ ξ.ρ.c ⟧, ν ↦ ⟦ Δ ⤍ 00- ⟧ ⟧, e ↦ ξ.b (c ↦ ⟦ ⟧).d ⟧.e ⟧ } +[ 3 / 4 ]{ ⟦ a ↦ ⟦ b ↦ ⟦ d ↦ ⟦ φ ↦ ξ.ρ.c, ν ↦ ⟦ Δ ⤍ 00- ⟧ ⟧, c ↦ ∅, ν ↦ ⟦ Δ ⤍ 00- ⟧ ⟧, e ↦ ξ.b (c ↦ ⟦ ⟧).d ⟧.e ⟧ } +[ 4 / 4 ]{ ⟦ a ↦ ξ.b (c ↦ ⟦ ⟧).d (ρ ↦ ⟦ b ↦ ⟦ d ↦ ⟦ φ ↦ ξ.ρ.c, ν ↦ ⟦ Δ ⤍ 00- ⟧ ⟧, c ↦ ∅, ν ↦ ⟦ Δ ⤍ 00- ⟧ ⟧ ⟧) ⟧ } +---------------------------------------------------- +Result 5 out of 6: +[ 1 / 4 ]{ ⟦ a ↦ ⟦ b ↦ ⟦ c ↦ ∅, d ↦ ⟦ φ ↦ ξ.ρ.c ⟧ ⟧, e ↦ ξ.b (c ↦ ⟦ ⟧).d ⟧.e ⟧ } +[ 2 / 4 ]{ ⟦ a ↦ ⟦ b ↦ ⟦ d ↦ ⟦ φ ↦ ξ.ρ.c, ν ↦ ⟦ Δ ⤍ 00- ⟧ ⟧, c ↦ ∅ ⟧, e ↦ ξ.b (c ↦ ⟦ ⟧).d ⟧.e ⟧ } +[ 3 / 4 ]{ ⟦ a ↦ ξ.b (c ↦ ⟦ ⟧).d (ρ ↦ ⟦ b ↦ ⟦ d ↦ ⟦ φ ↦ ξ.ρ.c, ν ↦ ⟦ Δ ⤍ 00- ⟧ ⟧, c ↦ ∅ ⟧ ⟧) ⟧ } +[ 4 / 4 ]{ ⟦ a ↦ ξ.b (c ↦ ⟦ ⟧).d (ρ ↦ ⟦ b ↦ ⟦ d ↦ ⟦ φ ↦ ξ.ρ.c, ν ↦ ⟦ Δ ⤍ 00- ⟧ ⟧, c ↦ ∅, ν ↦ ⟦ Δ ⤍ 00- ⟧ ⟧ ⟧) ⟧ } +---------------------------------------------------- +Result 6 out of 6: +[ 1 / 4 ]{ ⟦ a ↦ ⟦ b ↦ ⟦ c ↦ ∅, d ↦ ⟦ φ ↦ ξ.ρ.c ⟧ ⟧, e ↦ ξ.b (c ↦ ⟦ ⟧).d ⟧.e ⟧ } +[ 2 / 4 ]{ ⟦ a ↦ ⟦ b ↦ ⟦ d ↦ ⟦ φ ↦ ξ.ρ.c, ν ↦ ⟦ Δ ⤍ 00- ⟧ ⟧, c ↦ ∅ ⟧, e ↦ ξ.b (c ↦ ⟦ ⟧).d ⟧.e ⟧ } +[ 3 / 4 ]{ ⟦ a ↦ ⟦ b ↦ ⟦ d ↦ ⟦ φ ↦ ξ.ρ.c, ν ↦ ⟦ Δ ⤍ 00- ⟧ ⟧, c ↦ ∅, ν ↦ ⟦ Δ ⤍ 00- ⟧ ⟧, e ↦ ξ.b (c ↦ ⟦ ⟧).d ⟧.e ⟧ } +[ 4 / 4 ]{ ⟦ a ↦ ξ.b (c ↦ ⟦ ⟧).d (ρ ↦ ⟦ b ↦ ⟦ d ↦ ⟦ φ ↦ ξ.ρ.c, ν ↦ ⟦ Δ ⤍ 00- ⟧ ⟧, c ↦ ∅, ν ↦ ⟦ Δ ⤍ 00- ⟧ ⟧ ⟧) ⟧ } ---------------------------------------------------- ``` @@ -106,10 +165,10 @@ Result 1 out of 1: Use `--single` to print a single normalized program. -```sh -normalize-phi --single --rules-yaml ./eo-phi-normalizer/test/eo/phi/rules/yegor.yaml program.phi +```$ as console +normalizer transform --single --rules ./eo-phi-normalizer/test/eo/phi/rules/yegor.yaml --input-file program.phi ``` ```console -⟦ a ↦ ξ.b (c ↦ ⟦ ⟧).d (ρ ↦ ⟦ b ↦ ⟦ c ↦ ∅, d ↦ ⟦ φ ↦ ξ.ρ.c ⟧ ⟧ ⟧) ⟧ +{ ⟦ a ↦ ξ.b (c ↦ ⟦ ⟧).d (ρ ↦ ⟦ b ↦ ⟦ d ↦ ⟦ φ ↦ ξ.ρ.c, ν ↦ ⟦ Δ ⤍ 00- ⟧ ⟧, c ↦ ∅, ν ↦ ⟦ Δ ⤍ 00- ⟧ ⟧ ⟧) ⟧ } ``` From 129f2bc88849c2347b68d50210aca439e6687afa Mon Sep 17 00:00:00 2001 From: Danila Danko Date: Tue, 27 Feb 2024 02:24:21 +0300 Subject: [PATCH 08/42] feat: add markdownlint config --- .markdownlint.jsonc | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 .markdownlint.jsonc diff --git a/.markdownlint.jsonc b/.markdownlint.jsonc new file mode 100644 index 000000000..1344b312f --- /dev/null +++ b/.markdownlint.jsonc @@ -0,0 +1,3 @@ +{ + "MD013": false +} From 49f0076032ca9ab60184c0ae2768798793b5d005 Mon Sep 17 00:00:00 2001 From: Danila Danko Date: Tue, 27 Feb 2024 21:46:01 +0300 Subject: [PATCH 09/42] refactor: make command for sample program mdsh-runnable --- site/docs/src/common/sample-program.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/site/docs/src/common/sample-program.md b/site/docs/src/common/sample-program.md index 05e56084b..1b35d6a58 100644 --- a/site/docs/src/common/sample-program.md +++ b/site/docs/src/common/sample-program.md @@ -1,7 +1,7 @@ Save a `PHI` program to a file. This program will be used in subsequent commands. -```sh +```$ cat > program.phi < Date: Tue, 27 Feb 2024 21:51:09 +0300 Subject: [PATCH 10/42] refactor: move `user-defined rules` to `normalizer transform` --- .../transform.md} | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) rename site/docs/src/{user-defined-rules.md => commands/transform.md} (97%) diff --git a/site/docs/src/user-defined-rules.md b/site/docs/src/commands/transform.md similarity index 97% rename from site/docs/src/user-defined-rules.md rename to site/docs/src/commands/transform.md index 75315bb06..c645361bc 100644 --- a/site/docs/src/user-defined-rules.md +++ b/site/docs/src/commands/transform.md @@ -1,4 +1,6 @@ -# User-defined rules +# normalizer transform + +Jump to [CLI](#cli). ## `MetaPHI` @@ -47,8 +49,6 @@ Each rule has the following structure: ## CLI -Use the `transform` command to transform `PHI` programs. - ```$ as console normalizer transform --help ``` @@ -62,8 +62,9 @@ Usage: normalizer transform (-r|--rules FILE) [-i|--input-file FILE] [PROGRAM] Available options: -r,--rules FILE FILE with user-defined rules. - -i,--input-file FILE FILE to read input from. You must specify either this - option or PROGRAM. + -i,--input-file FILE FILE to read input from. When FILE is -, read from + stdin. You must specify either this option or + PROGRAM. PROGRAM Program to work with. -c,--chain Output transformation steps. -j,--json Output JSON. @@ -72,10 +73,6 @@ Available options: -h,--help Show this help text ``` -## Use rules - -{{#include ./common/normalize-phi-options.md}} - ### Sample program {{#include ./common/sample-program.md}} From b125e0be03fe92957561537f0974218fbe13d96c Mon Sep 17 00:00:00 2001 From: Danila Danko Date: Tue, 27 Feb 2024 21:51:50 +0300 Subject: [PATCH 11/42] refactor: remove symlink to readme --- site/docs/src/README.md | 1 - 1 file changed, 1 deletion(-) delete mode 120000 site/docs/src/README.md diff --git a/site/docs/src/README.md b/site/docs/src/README.md deleted file mode 120000 index 8a33348c7..000000000 --- a/site/docs/src/README.md +++ /dev/null @@ -1 +0,0 @@ -../../../README.md \ No newline at end of file From cd264b9b406f47d373268584638a5ff5f808fab1 Mon Sep 17 00:00:00 2001 From: Danila Danko Date: Tue, 27 Feb 2024 22:03:47 +0300 Subject: [PATCH 12/42] refactor: remove normalize-phi options page --- site/docs/src/common/normalize-phi-options.md | 19 ------------------- 1 file changed, 19 deletions(-) delete mode 100644 site/docs/src/common/normalize-phi-options.md diff --git a/site/docs/src/common/normalize-phi-options.md b/site/docs/src/common/normalize-phi-options.md deleted file mode 100644 index 2d1ece1ea..000000000 --- a/site/docs/src/common/normalize-phi-options.md +++ /dev/null @@ -1,19 +0,0 @@ -Learn about `normalize-phi` options. - -```sh -normalize-phi --help -``` - -```console -Normalizer - -Usage: normalize-phi [-c|--chain] [--rules-yaml STRING] [-o|--output STRING] - [-s|--single] [STRING] - -Available options: - -h,--help Show this help text - -c,--chain Print out steps of reduction - --rules-yaml STRING Path to the Yaml file with custom rules - -o,--output STRING Output file path (defaults to stdout) - -s,--single Print a single normalized expression -``` From 75c7f33618b315a72cdc58cd54d13d4d47b5d567 Mon Sep 17 00:00:00 2001 From: Danila Danko Date: Tue, 27 Feb 2024 22:14:25 +0300 Subject: [PATCH 13/42] refactor: move `normalizer transform` docs --- site/docs/src/commands/{transform.md => normalizer-transform.md} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename site/docs/src/commands/{transform.md => normalizer-transform.md} (100%) diff --git a/site/docs/src/commands/transform.md b/site/docs/src/commands/normalizer-transform.md similarity index 100% rename from site/docs/src/commands/transform.md rename to site/docs/src/commands/normalizer-transform.md From fa266d9427d4a5b4b168fe138715dd9b740a1b92 Mon Sep 17 00:00:00 2001 From: Danila Danko Date: Tue, 27 Feb 2024 23:05:45 +0300 Subject: [PATCH 14/42] fix: write to file strictly --- eo-phi-normalizer/app/Main.hs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/eo-phi-normalizer/app/Main.hs b/eo-phi-normalizer/app/Main.hs index 706e44f6a..23af87869 100644 --- a/eo-phi-normalizer/app/Main.hs +++ b/eo-phi-normalizer/app/Main.hs @@ -33,7 +33,7 @@ import Language.EO.Phi.Rules.Common qualified as Common import Language.EO.Phi.Rules.Yaml (RuleSet (rules, title), convertRule, parseRuleSetFromFile) import Options.Applicative import Options.Applicative.Types qualified as Optparse (Context (..)) -import System.IO (IOMode (WriteMode), hPutStr, hPutStrLn, openFile, stdout) +import System.IO (IOMode (WriteMode), hFlush, hPutStr, hPutStrLn, openFile, stdout) data CLI'TransformPhi = CLI'TransformPhi { chain :: Bool @@ -158,7 +158,10 @@ getProgram inputFile expression = do getLoggers :: Maybe FilePath -> IO (String -> IO (), String -> IO ()) getLoggers outputFile = do handle <- maybe (pure stdout) (`openFile` WriteMode) outputFile - pure (hPutStrLn handle, hPutStr handle) + pure + ( \x -> hPutStrLn handle x >> hFlush handle + , \x -> hPutStr handle x >> hFlush handle + ) main :: IO () main = do From fe803d27a58984cafd9157da399d2b5d4bf1c058 Mon Sep 17 00:00:00 2001 From: Danila Danko Date: Tue, 27 Feb 2024 23:06:07 +0300 Subject: [PATCH 15/42] feat: support read from stdin --- eo-phi-normalizer/app/Main.hs | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/eo-phi-normalizer/app/Main.hs b/eo-phi-normalizer/app/Main.hs index 23af87869..2c8441406 100644 --- a/eo-phi-normalizer/app/Main.hs +++ b/eo-phi-normalizer/app/Main.hs @@ -33,7 +33,7 @@ import Language.EO.Phi.Rules.Common qualified as Common import Language.EO.Phi.Rules.Yaml (RuleSet (rules, title), convertRule, parseRuleSetFromFile) import Options.Applicative import Options.Applicative.Types qualified as Optparse (Context (..)) -import System.IO (IOMode (WriteMode), hFlush, hPutStr, hPutStrLn, openFile, stdout) +import System.IO (IOMode (WriteMode), getContents', hFlush, hPutStr, hPutStrLn, openFile, stdout) data CLI'TransformPhi = CLI'TransformPhi { chain :: Bool @@ -71,7 +71,7 @@ inputFileLongName :: String inputFileLongName = "input-file" inputFileOption :: Parser (Maybe FilePath) -inputFileOption = optional $ strOption (long inputFileLongName <> short 'i' <> help [i|#{_FILE} to read input from. You must specify either this option or #{_PROGRAM}.|] <> fileMetavar) +inputFileOption = optional $ strOption (long inputFileLongName <> short 'i' <> help [i|#{_FILE} to read input from. When #{_FILE} is -, read from stdin. You must specify either this option or #{_PROGRAM}.|] <> fileMetavar) outputFileOption :: Parser (Maybe String) outputFileOption = optional $ strOption (long "output-file" <> short 'o' <> help [i|Output to #{_FILE}. Output to stdout otherwise.|] <> fileMetavar) @@ -148,7 +148,10 @@ getProgram :: (Context) => Maybe FilePath -> Maybe String -> IO Program getProgram inputFile expression = do src <- case (inputFile, expression) of - (Just inputFile', Nothing) -> readFile inputFile' + (Just inputFile', Nothing) -> + if inputFile' == "-" + then getContents' + else readFile inputFile' (Nothing, Just expression') -> pure expression' _ -> die [i|You must specify either -#{head inputFileLongName}|--#{inputFileLongName} #{_FILE} or #{_PROGRAM}|] case parseProgram src of From ddf3c7bca7f0d1feb59128454a8c1ca2ff8ea394 Mon Sep 17 00:00:00 2001 From: Danila Danko Date: Tue, 27 Feb 2024 23:08:18 +0300 Subject: [PATCH 16/42] refactor: move from readme to site --- README.md | 395 +----------------------------------------------------- 1 file changed, 1 insertion(+), 394 deletions(-) diff --git a/README.md b/README.md index c2026e8d7..862aee592 100644 --- a/README.md +++ b/README.md @@ -5,398 +5,5 @@ Command line normalizer of 𝜑-calculus expressions (as produced by the [EO compiler](https://github.com/objectionary/eo)). -## About +Consult the [project documentation](https://www.objectionary.com/normalizer/) for more details. -This project aims to apply term rewriting techniques to "simplify" an input 𝜑-expression -and prepare it for further optimization passes. The simplification procedure is expected -to be a form of partial evaluation and normalization. -Contrary to traditional normalization in λ-calculus, we aim at rewriting rules that would -help reduce certain metrics of expressions. In particular, we are interested in reducing -attribute access (`t.a`) that amounts to _dynamic dispatch_. - -## Install - -You can install the `normalizer-phi` executable globally via [stack](https://docs.haskellstack.org/en/stable). -Then, the `normalize-phi` executable will be available on `PATH`. - -### Install from the repository - -```sh -git clone https://github.com/objectionary/normalizer -cd normalizer -export LANG=C.UTF-8 -stack install normalize-phi -normalize-phi --help -``` - -### Install from Hackage - -```sh -stack update -export LANG=C.UTF-8 -stack install --resolver lts-22.11 eo-phi-normalizer -``` - -### Uninstall - -Learn where `stack` installs programs. - -```sh -stack path --programs -``` - -Learn how to uninstall a program. - -```sh -stack uninstall -``` - -## Use - -Learn about `normalize-phi` options. - -```$ as console -normalizer --help -``` - -```console -Usage: normalizer COMMAND - - Work with PHI expressions. - -Available options: - -h,--help Show this help text - -Available commands: - transform Transform a PHI program. - metrics Collect metrics for a PHI program. -``` - -### Sample program - -Save a $\varphi$-calculus program to a file. -This program will be used in subsequent commands. - -```$ -cat > program.phi <`. - -## Rulesets - -A ruleset describes a set of user-defined rewriting rules. - -Here is a sample ruleset (see the full ruleset in [yegor.yaml](./eo-phi-normalizer/test/eo/phi/rules/yegor.yaml)). - -```yaml -title: "Rule set based on Yegor's draft" -rules: - - name: Rule 6 - description: "Accessing an α-binding" - pattern: | - ⟦ !a ↦ !n, !B ⟧.!a - result: | - !n(ρ ↦ ⟦ !B ⟧) - when: - - nf: ["!n"] - tests: - - name: Should match - input: ⟦ hello ↦ ⟦⟧ ⟧.hello - output: ⟦⟧(ρ ↦ ⟦⟧) - matches: true - - name: Shouldn't match - input: ⟦ ⟧.hello - output: "" - matches: false -``` - -A ruleset has a number of rules. Each rule describes a `pattern`, `when` to apply that pattern, and a `result`. - -The `pattern` has metavariables denoted as `!`. -The `pattern` is matched against an `input` expression. The rules in the `when` list help avoid unwanted matches. For example, `nf: ["!n"]` means that only an expression in a normal form can be matched with `!n`. - -When there is a match, the matched parts of the expression are bound to metavariables. Next, these metavariables are used to construct the `result`. - -Additionally, there are unit tests for rules. Each unit test provides `input` and `output` expressions. An `output` expression is not reused in other tests, so it is safe to let it be an empty string when no match can happen. - -## Develop - -We recommend using [stack](https://docs.haskellstack.org/en/stable/README/) for quick local development and testing. - -Clone this project and run `stack build`. - -```sh -git clone https://github.com/objectionary/normalizer -cd normalizer -stack build -``` - -### Run - -Run the executable via `stack run`. - -```$ as console -stack run normalizer -- --help -``` - -```console -Usage: normalizer COMMAND - - Work with PHI expressions. - -Available options: - -h,--help Show this help text - -Available commands: - transform Transform a PHI program. - metrics Collect metrics for a PHI program. -``` - -Or, omit the executable name. - -```$ as console -stack run -- --help -``` - -```console -Usage: normalizer COMMAND - - Work with PHI expressions. - -Available options: - -h,--help Show this help text - -Available commands: - transform Transform a PHI program. - metrics Collect metrics for a PHI program. -``` - -### Test - -Run all tests - -```sh -stack test -``` - -## Contribute - -### pre-commit - -We use [pre-commit](https://pre-commit.com/) to ensure code quality. - -Collaborators **MUST** set them up before commiting any code to our repository. - -Otherwise, the triggered CI jobs will fail. - -#### Set up pre-commit - -##### Single command - -```console -pip3 install -pre-commit install -stack install fourmolu -chmod +x scripts/run-fourmolu.sh -``` - -##### Step by step - -1. Install [Python 3](https://www.python.org/downloads/) (e.g., Python 3.10). -1. [Install pre-commit](https://pre-commit.com/#1-install-pre-commit). - - Alternatively, run `pip3 install`. -1. [Install the git hook scripts](https://pre-commit.com/#3-install-the-git-hook-scripts). -1. Install [fourmolu](https://github.com/fourmolu/fourmolu). - - ```console - stack install fourmolu - ``` - - - You can remove `fourmolu` later (see [SO post](https://stackoverflow.com/a/38639959)) - -1. Make a script executable. - - ```console - chmod +x scripts/run-fourmolu.sh - ``` - -#### pre-commit configs - -See [docs](https://pre-commit.com/#adding-pre-commit-plugins-to-your-project). - -See [.pre-commit-config.yaml](.pre-commit-config.yaml). - -You can run a specific hook (see [docs](https://pre-commit.com/#pre-commit-run)): - -```console -pre-commit run -c .pre-commit-config.yaml fourmolu-format --all -``` - -#### pre-commit workflow - -- `pre-commit` runs before a commit (at the [pre-commit phase](https://git-scm.com/book/en/v2/Customizing-Git-Git-Hooks#_committing_workflow_hooks)) - - > The pre-commit hook is run first, before you even type in a commit message. It's used to inspect the snapshot that's about to be committed, to see if you've forgotten something, to make sure tests run, or to examine whatever you need to inspect in the code. Exiting non-zero from this hook aborts the commit ... - -- `pre-commit` stashes ([link](https://git-scm.com/docs/git-stash)) unstaged ([link](https://git-scm.com/book/en/v2/Getting-Started-What-is-Git%3F#_the_three_states)) files. - - ```console - [WARNING] Unstaged files detected. - [INFO] Stashing unstaged files to /home/eyjafjallajokull/.cache/pre-commit/patch1705090051-437857. - ``` - -- `pre-commit` runs hooks. -- A hook may exit with an error, e.g.: - - ```md - Format Haskell (.hs) files...............................................Failed - - - hook id: fourmolu - - exit code: 102 - - files were modified by this hook - ``` - - - In case of the [fourmolu](https://github.com/fourmolu/fourmolu) formatter, - it's assumed that formatting a formatted `Haskell` file doesn't modify it. - However, `pre-commit` runs the `fourmolu` hook and reports that it has modified some files. - This error won't allow you to commit. - -- `pre-commit` unstashes files. - -- You should stage all changes so that `pre-commit` does not complain. - - - In case of `fourmolu`, stage the formatted code regions. - -- Now, you can commit. From f63185f83ec6162bcde2c342dca3348a660a68b6 Mon Sep 17 00:00:00 2001 From: Danila Danko Date: Tue, 27 Feb 2024 23:09:48 +0300 Subject: [PATCH 17/42] feat: installation page --- site/docs/src/installation.md | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) create mode 100644 site/docs/src/installation.md diff --git a/site/docs/src/installation.md b/site/docs/src/installation.md new file mode 100644 index 000000000..8241bce77 --- /dev/null +++ b/site/docs/src/installation.md @@ -0,0 +1,35 @@ +# Installation + +Install the `normalizer` executable globally via [stack](https://docs.haskellstack.org/en/stable). +Then, the `normalizer` executable will be available on `PATH`. + +## Install from the repository + +```sh +git clone https://github.com/objectionary/normalizer +cd normalizer +export LANG=C.UTF-8 +stack install normalizer +``` + +## Install from Hackage + +```sh +stack update +export LANG=C.UTF-8 +stack install --resolver lts-22.11 eo-phi-normalizer +``` + +## Uninstall + +Learn where `stack` installs programs. + +```sh +stack path --programs +``` + +Learn how to uninstall a program. + +```sh +stack uninstall +``` From f0cab743b599c46fd59ee7899ffd9488e4326798 Mon Sep 17 00:00:00 2001 From: Danila Danko Date: Tue, 27 Feb 2024 23:18:27 +0300 Subject: [PATCH 18/42] feat: contributing page --- site/docs/src/contributing.md | 149 ++++++++++++++++++++++++++++++++++ 1 file changed, 149 insertions(+) create mode 100644 site/docs/src/contributing.md diff --git a/site/docs/src/contributing.md b/site/docs/src/contributing.md new file mode 100644 index 000000000..ebd39e43a --- /dev/null +++ b/site/docs/src/contributing.md @@ -0,0 +1,149 @@ +# Contributing + +We recommend using [stack](https://docs.haskellstack.org/en/stable/README/) for quick local development and testing. + +Clone this project and run `stack build`. + +```sh +git clone https://github.com/objectionary/normalizer +cd normalizer +stack build +``` + +## Run + +Run the executable via `stack run`. + +```$ as console +stack run normalizer -- --help +``` + +```console +Usage: normalizer COMMAND + + Work with PHI expressions. + +Available options: + -h,--help Show this help text + +Available commands: + transform Transform a PHI program. + metrics Collect metrics for a PHI program. +``` + +Or, omit the executable name. + +```$ as console +stack run -- --help +``` + +```console +Usage: normalizer COMMAND + + Work with PHI expressions. + +Available options: + -h,--help Show this help text + +Available commands: + transform Transform a PHI program. + metrics Collect metrics for a PHI program. +``` + +## Test + +Run all tests + +```sh +stack test +``` + +## Contribute + +## pre-commit + +We use [pre-commit](https://pre-commit.com/) to ensure code quality. + +Collaborators **MUST** set them up before commiting any code to our repository. + +Otherwise, the triggered CI jobs will fail. + +### Set up pre-commit + +#### Single command + +```console +pip3 install +pre-commit install +stack install fourmolu +chmod +x scripts/run-fourmolu.sh +``` + +#### Step by step + +1. Install [Python 3](https://www.python.org/downloads/) (e.g., Python 3.10). +1. [Install pre-commit](https://pre-commit.com/#1-install-pre-commit). + - Alternatively, run `pip3 install`. +1. [Install the git hook scripts](https://pre-commit.com/#3-install-the-git-hook-scripts). +1. Install [fourmolu](https://github.com/fourmolu/fourmolu). + + ```console + stack install fourmolu + ``` + + - You can remove `fourmolu` later (see [SO post](https://stackoverflow.com/a/38639959)) + +1. Make a script executable. + + ```console + chmod +x scripts/run-fourmolu.sh + ``` + +### pre-commit configs + +See [docs](https://pre-commit.com/#adding-pre-commit-plugins-to-your-project). + +See [.pre-commit-config.yaml](.pre-commit-config.yaml). + +You can run a specific hook (see [docs](https://pre-commit.com/#pre-commit-run)): + +```console +pre-commit run -c .pre-commit-config.yaml fourmolu-format --all +``` + +### pre-commit workflow + +- `pre-commit` runs before a commit (at the [pre-commit phase](https://git-scm.com/book/en/v2/Customizing-Git-Git-Hooks#_committing_workflow_hooks)) + + > The pre-commit hook is run first, before you even type in a commit message. It's used to inspect the snapshot that's about to be committed, to see if you've forgotten something, to make sure tests run, or to examine whatever you need to inspect in the code. Exiting non-zero from this hook aborts the commit ... + +- `pre-commit` stashes ([link](https://git-scm.com/docs/git-stash)) unstaged ([link](https://git-scm.com/book/en/v2/Getting-Started-What-is-Git%3F#_the_three_states)) files. + + ```console + [WARNING] Unstaged files detected. + [INFO] Stashing unstaged files to /home/eyjafjallajokull/.cache/pre-commit/patch1705090051-437857. + ``` + +- `pre-commit` runs hooks. +- A hook may exit with an error, e.g.: + + ```md + Format Haskell (.hs) files...............................................Failed + + - hook id: fourmolu + - exit code: 102 + - files were modified by this hook + ``` + + - In case of the [fourmolu](https://github.com/fourmolu/fourmolu) formatter, + it's assumed that formatting a formatted `Haskell` file doesn't modify it. + However, `pre-commit` runs the `fourmolu` hook and reports that it has modified some files. + This error won't allow you to commit. + +- `pre-commit` unstashes files. + +- You should stage all changes so that `pre-commit` does not complain. + + - In case of `fourmolu`, stage the formatted code regions. + +- Now, you can commit. From d0bd6d6d4f8ce5c75fc710513def423e78751e0f Mon Sep 17 00:00:00 2001 From: Danila Danko Date: Tue, 27 Feb 2024 23:18:43 +0300 Subject: [PATCH 19/42] feat: normalizer-metrics page --- site/docs/src/commands/normalizer-metrics.md | 89 ++++++++++++++++++++ 1 file changed, 89 insertions(+) create mode 100644 site/docs/src/commands/normalizer-metrics.md diff --git a/site/docs/src/commands/normalizer-metrics.md b/site/docs/src/commands/normalizer-metrics.md new file mode 100644 index 000000000..ac85c9068 --- /dev/null +++ b/site/docs/src/commands/normalizer-metrics.md @@ -0,0 +1,89 @@ +# normalizer metrics + +- [normalizer metrics](#normalizer-metrics) + - [Metrics](#metrics) + - [PHI grammar](#phi-grammar) + - [Object formations](#object-formations) + - [Object applications](#object-applications) + - [Dynamic dispatches](#dynamic-dispatches) + - [Dataless formations](#dataless-formations) + - [CLI](#cli) + +## Metrics + +We count: + +- [Object applications](#object-applications) +- [Object formations](#object-formations) +- [Dynamic dispatches](#dynamic-dispatches) +- [Dataless formations](#dataless-formations) + +### PHI grammar + +![phi-grammar](../media/phi-grammar.png) + +### Object formations + +- `⟦ d ↦ ∅, c ↦ ∅ ⟧` + +### Object applications + +- `ξ.b(c ↦ ⟦ ⟧)` + +### Dynamic dispatches + +- `ξ.ρ.c` + +### Dataless formations + +- `Primitive formation` - a formation that has a Δ-attribute. + - `⟦ Δ ⤍ 00- ⟧` +- `Dataless formation` - not primitive and does not have attributes bound to primitive formations. + - `⟦ d ↦ ⟦ φ ↦ ξ.ρ.c, ν ↦ ⟦ Δ ⤍ 00- ⟧ ⟧, c ↦ ∅ ⟧` + +## Environment + +{{#include ../common/sample-program.md}} + +## CLI + +### --help + +```$ as console +normalizer metrics --help +``` + +```console +Usage: normalizer metrics [-i|--input-file FILE] [-o|--output-file FILE] + [PROGRAM] + + Collect metrics for a PHI program. + +Available options: + -i,--input-file FILE FILE to read input from. When FILE is -, read from + stdin. You must specify either this option or + PROGRAM. + -o,--output-file FILE Output to FILE. Output to stdout otherwise. + PROGRAM Program to work with. + -h,--help Show this help text +``` + +### --input-file program.phi + +```$ as json +normalizer metrics --input-file program.phi +``` + +```json +{ "applications": 1, "dataless": 5, "dispatches": 5, "formations": 5 } +``` + +### --input-file - + +```$ as json +cat program.phi | normalizer metrics --input-file - +``` + +```json +{ "applications": 1, "dataless": 5, "dispatches": 5, "formations": 5 } +``` From e82869bf48028f80a07d18d98cfe5f9d60dc8cfa Mon Sep 17 00:00:00 2001 From: Danila Danko Date: Tue, 27 Feb 2024 23:19:08 +0300 Subject: [PATCH 20/42] refactor: normalizer-transform page --- .../docs/src/commands/normalizer-transform.md | 101 ++++++++++++++---- 1 file changed, 82 insertions(+), 19 deletions(-) diff --git a/site/docs/src/commands/normalizer-transform.md b/site/docs/src/commands/normalizer-transform.md index c645361bc..1b6b5fba3 100644 --- a/site/docs/src/commands/normalizer-transform.md +++ b/site/docs/src/commands/normalizer-transform.md @@ -1,6 +1,17 @@ # normalizer transform -Jump to [CLI](#cli). +- [normalizer transform](#normalizer-transform) + - [`MetaPHI`](#metaphi) + - [phi-paper rules](#phi-paper-rules) + - [yegor.yaml](#yegoryaml) + - [Normal form](#normal-form) + - [Environment](#environment) + - [Repository](#repository) + - [Sample program](#sample-program) + - [CLI](#cli) + - [`--rules`](#rules) + - [`--chain`](#chain) + - [`--single`](#single) ## `MetaPHI` @@ -45,7 +56,23 @@ Each rule has the following structure: ### Normal form - +An expression is in normal form when no rule can be applied to that expression. + +## Environment + +### Repository + +The commands in the following sections access files that are available in the project repository. +Clone and enter the repository directory. + +```sh +git clone https://github.com/objectionary/normalizer +cd normalizer +``` + +### Sample program + +{{#include ../common/sample-program.md}} ## CLI @@ -73,21 +100,7 @@ Available options: -h,--help Show this help text ``` -### Sample program - -{{#include ./common/sample-program.md}} - -### Prepare environment - -The commands in the following sections access files that are available in the project repository. -Clone and enter the repository directory. - -```sh -git clone https://github.com/objectionary/normalizer -cd normalizer -``` - -#### `--ruleset-yaml` +### `--rules` Normalize a 𝜑-expression from `program.phi` using the [yegor.yaml](#yegoryaml) rules. @@ -107,7 +120,7 @@ Result 1 out of 1: ---------------------------------------------------- ``` -#### `--chain` +### `--chain` Use `--chain` to see numbered normalization steps for each normalization result. @@ -158,7 +171,7 @@ Result 6 out of 6: ---------------------------------------------------- ``` -#### `--single` +### `--single` Use `--single` to print a single normalized program. @@ -169,3 +182,53 @@ normalizer transform --single --rules ./eo-phi-normalizer/test/eo/phi/rules/yego ```console { ⟦ a ↦ ξ.b (c ↦ ⟦ ⟧).d (ρ ↦ ⟦ b ↦ ⟦ d ↦ ⟦ φ ↦ ξ.ρ.c, ν ↦ ⟦ Δ ⤍ 00- ⟧ ⟧, c ↦ ∅, ν ↦ ⟦ Δ ⤍ 00- ⟧ ⟧ ⟧) ⟧ } ``` + +### `--json` + +```$ as json +normalizer transform --json --chain --rules ./eo-phi-normalizer/test/eo/phi/rules/yegor.yaml --input-file program.phi +``` + +```json +{ + "input": "{ ⟦ a ↦ ⟦ b ↦ ⟦ c ↦ ∅, d ↦ ⟦ φ ↦ ξ.ρ.c ⟧ ⟧, e ↦ ξ.b (c ↦ ⟦ ⟧).d ⟧.e ⟧ }", + "output": [ + [ + "{ ⟦ a ↦ ⟦ b ↦ ⟦ c ↦ ∅, d ↦ ⟦ φ ↦ ξ.ρ.c ⟧ ⟧, e ↦ ξ.b (c ↦ ⟦ ⟧).d ⟧.e ⟧ }", + "{ ⟦ a ↦ ξ.b (c ↦ ⟦ ⟧).d (ρ ↦ ⟦ b ↦ ⟦ c ↦ ∅, d ↦ ⟦ φ ↦ ξ.ρ.c ⟧ ⟧ ⟧) ⟧ }", + "{ ⟦ a ↦ ξ.b (c ↦ ⟦ ⟧).d (ρ ↦ ⟦ b ↦ ⟦ c ↦ ∅, d ↦ ⟦ φ ↦ ξ.ρ.c ⟧, ν ↦ ⟦ Δ ⤍ 00- ⟧ ⟧ ⟧) ⟧ }", + "{ ⟦ a ↦ ξ.b (c ↦ ⟦ ⟧).d (ρ ↦ ⟦ b ↦ ⟦ d ↦ ⟦ φ ↦ ξ.ρ.c, ν ↦ ⟦ Δ ⤍ 00- ⟧ ⟧, c ↦ ∅, ν ↦ ⟦ Δ ⤍ 00- ⟧ ⟧ ⟧) ⟧ }" + ], + [ + "{ ⟦ a ↦ ⟦ b ↦ ⟦ c ↦ ∅, d ↦ ⟦ φ ↦ ξ.ρ.c ⟧ ⟧, e ↦ ξ.b (c ↦ ⟦ ⟧).d ⟧.e ⟧ }", + "{ ⟦ a ↦ ξ.b (c ↦ ⟦ ⟧).d (ρ ↦ ⟦ b ↦ ⟦ c ↦ ∅, d ↦ ⟦ φ ↦ ξ.ρ.c ⟧ ⟧ ⟧) ⟧ }", + "{ ⟦ a ↦ ξ.b (c ↦ ⟦ ⟧).d (ρ ↦ ⟦ b ↦ ⟦ d ↦ ⟦ φ ↦ ξ.ρ.c, ν ↦ ⟦ Δ ⤍ 00- ⟧ ⟧, c ↦ ∅ ⟧ ⟧) ⟧ }", + "{ ⟦ a ↦ ξ.b (c ↦ ⟦ ⟧).d (ρ ↦ ⟦ b ↦ ⟦ d ↦ ⟦ φ ↦ ξ.ρ.c, ν ↦ ⟦ Δ ⤍ 00- ⟧ ⟧, c ↦ ∅, ν ↦ ⟦ Δ ⤍ 00- ⟧ ⟧ ⟧) ⟧ }" + ], + [ + "{ ⟦ a ↦ ⟦ b ↦ ⟦ c ↦ ∅, d ↦ ⟦ φ ↦ ξ.ρ.c ⟧ ⟧, e ↦ ξ.b (c ↦ ⟦ ⟧).d ⟧.e ⟧ }", + "{ ⟦ a ↦ ⟦ b ↦ ⟦ c ↦ ∅, d ↦ ⟦ φ ↦ ξ.ρ.c ⟧, ν ↦ ⟦ Δ ⤍ 00- ⟧ ⟧, e ↦ ξ.b (c ↦ ⟦ ⟧).d ⟧.e ⟧ }", + "{ ⟦ a ↦ ξ.b (c ↦ ⟦ ⟧).d (ρ ↦ ⟦ b ↦ ⟦ c ↦ ∅, d ↦ ⟦ φ ↦ ξ.ρ.c ⟧, ν ↦ ⟦ Δ ⤍ 00- ⟧ ⟧ ⟧) ⟧ }", + "{ ⟦ a ↦ ξ.b (c ↦ ⟦ ⟧).d (ρ ↦ ⟦ b ↦ ⟦ d ↦ ⟦ φ ↦ ξ.ρ.c, ν ↦ ⟦ Δ ⤍ 00- ⟧ ⟧, c ↦ ∅, ν ↦ ⟦ Δ ⤍ 00- ⟧ ⟧ ⟧) ⟧ }" + ], + [ + "{ ⟦ a ↦ ⟦ b ↦ ⟦ c ↦ ∅, d ↦ ⟦ φ ↦ ξ.ρ.c ⟧ ⟧, e ↦ ξ.b (c ↦ ⟦ ⟧).d ⟧.e ⟧ }", + "{ ⟦ a ↦ ⟦ b ↦ ⟦ c ↦ ∅, d ↦ ⟦ φ ↦ ξ.ρ.c ⟧, ν ↦ ⟦ Δ ⤍ 00- ⟧ ⟧, e ↦ ξ.b (c ↦ ⟦ ⟧).d ⟧.e ⟧ }", + "{ ⟦ a ↦ ⟦ b ↦ ⟦ d ↦ ⟦ φ ↦ ξ.ρ.c, ν ↦ ⟦ Δ ⤍ 00- ⟧ ⟧, c ↦ ∅, ν ↦ ⟦ Δ ⤍ 00- ⟧ ⟧, e ↦ ξ.b (c ↦ ⟦ ⟧).d ⟧.e ⟧ }", + "{ ⟦ a ↦ ξ.b (c ↦ ⟦ ⟧).d (ρ ↦ ⟦ b ↦ ⟦ d ↦ ⟦ φ ↦ ξ.ρ.c, ν ↦ ⟦ Δ ⤍ 00- ⟧ ⟧, c ↦ ∅, ν ↦ ⟦ Δ ⤍ 00- ⟧ ⟧ ⟧) ⟧ }" + ], + [ + "{ ⟦ a ↦ ⟦ b ↦ ⟦ c ↦ ∅, d ↦ ⟦ φ ↦ ξ.ρ.c ⟧ ⟧, e ↦ ξ.b (c ↦ ⟦ ⟧).d ⟧.e ⟧ }", + "{ ⟦ a ↦ ⟦ b ↦ ⟦ d ↦ ⟦ φ ↦ ξ.ρ.c, ν ↦ ⟦ Δ ⤍ 00- ⟧ ⟧, c ↦ ∅ ⟧, e ↦ ξ.b (c ↦ ⟦ ⟧).d ⟧.e ⟧ }", + "{ ⟦ a ↦ ξ.b (c ↦ ⟦ ⟧).d (ρ ↦ ⟦ b ↦ ⟦ d ↦ ⟦ φ ↦ ξ.ρ.c, ν ↦ ⟦ Δ ⤍ 00- ⟧ ⟧, c ↦ ∅ ⟧ ⟧) ⟧ }", + "{ ⟦ a ↦ ξ.b (c ↦ ⟦ ⟧).d (ρ ↦ ⟦ b ↦ ⟦ d ↦ ⟦ φ ↦ ξ.ρ.c, ν ↦ ⟦ Δ ⤍ 00- ⟧ ⟧, c ↦ ∅, ν ↦ ⟦ Δ ⤍ 00- ⟧ ⟧ ⟧) ⟧ }" + ], + [ + "{ ⟦ a ↦ ⟦ b ↦ ⟦ c ↦ ∅, d ↦ ⟦ φ ↦ ξ.ρ.c ⟧ ⟧, e ↦ ξ.b (c ↦ ⟦ ⟧).d ⟧.e ⟧ }", + "{ ⟦ a ↦ ⟦ b ↦ ⟦ d ↦ ⟦ φ ↦ ξ.ρ.c, ν ↦ ⟦ Δ ⤍ 00- ⟧ ⟧, c ↦ ∅ ⟧, e ↦ ξ.b (c ↦ ⟦ ⟧).d ⟧.e ⟧ }", + "{ ⟦ a ↦ ⟦ b ↦ ⟦ d ↦ ⟦ φ ↦ ξ.ρ.c, ν ↦ ⟦ Δ ⤍ 00- ⟧ ⟧, c ↦ ∅, ν ↦ ⟦ Δ ⤍ 00- ⟧ ⟧, e ↦ ξ.b (c ↦ ⟦ ⟧).d ⟧.e ⟧ }", + "{ ⟦ a ↦ ξ.b (c ↦ ⟦ ⟧).d (ρ ↦ ⟦ b ↦ ⟦ d ↦ ⟦ φ ↦ ξ.ρ.c, ν ↦ ⟦ Δ ⤍ 00- ⟧ ⟧, c ↦ ∅, ν ↦ ⟦ Δ ⤍ 00- ⟧ ⟧ ⟧) ⟧ }" + ] + ] +} +``` From 0f3df58989f42a057b3d54f9d645f8f58ff3dd74 Mon Sep 17 00:00:00 2001 From: Danila Danko Date: Tue, 27 Feb 2024 23:19:18 +0300 Subject: [PATCH 21/42] feat: normalizer page --- site/docs/src/commands/normalizer.md | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 site/docs/src/commands/normalizer.md diff --git a/site/docs/src/commands/normalizer.md b/site/docs/src/commands/normalizer.md new file mode 100644 index 000000000..045acedbb --- /dev/null +++ b/site/docs/src/commands/normalizer.md @@ -0,0 +1,20 @@ +# normalizer + +See commands supported by the `normalizer` executable. + +```$ as console +normalizer --help +``` + +```console +Usage: normalizer COMMAND + + Work with PHI expressions. + +Available options: + -h,--help Show this help text + +Available commands: + transform Transform a PHI program. + metrics Collect metrics for a PHI program. +``` From f7fede49975af1113b09b66b551e6ada1e42a009 Mon Sep 17 00:00:00 2001 From: Danila Danko Date: Tue, 27 Feb 2024 23:19:44 +0300 Subject: [PATCH 22/42] feat: introduction page --- site/docs/src/introduction.md | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 site/docs/src/introduction.md diff --git a/site/docs/src/introduction.md b/site/docs/src/introduction.md new file mode 100644 index 000000000..ae124429e --- /dev/null +++ b/site/docs/src/introduction.md @@ -0,0 +1,7 @@ +# Normalizer for 𝜑-calculus + +Command line normalizer of 𝜑-calculus (`PHI`) expressions (as produced by the [EO compiler](https://github.com/objectionary/eo)). + +This project aims to apply term rewriting techniques to "simplify" an input `PHI` expression and prepare it for further optimization passes. The simplification procedure will be a form of partial evaluation and normalization. + +Contrary to traditional normalization in λ-calculus, we aim at rewriting rules that would help reduce certain metrics of expressions (see [Metrics](./commands/normalizer-metrics.md)). From 42ef088c63c9b500668af1a5e4f7f8fd38e89567 Mon Sep 17 00:00:00 2001 From: Danila Danko Date: Tue, 27 Feb 2024 23:20:07 +0300 Subject: [PATCH 23/42] fix: include new sections into summary --- site/docs/src/SUMMARY.md | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/site/docs/src/SUMMARY.md b/site/docs/src/SUMMARY.md index 1a904800c..81c29f477 100644 --- a/site/docs/src/SUMMARY.md +++ b/site/docs/src/SUMMARY.md @@ -1,4 +1,8 @@ # Summary -- [README](./README.md) -- [User-defined rules](./user-defined-rules.md) +- [Introduction](./introduction.md) +- [Installation](./installation.md) +- [Commands](./commands.md) + - [normalizer transform](./commands/normalizer-transform.md) + - [normalizer metrics](./commands/normalizer-metrics.md) +- [Contributing](./contributing.md) From f26482a34b2e3cc3842cc7790a4118f6d31f45fb Mon Sep 17 00:00:00 2001 From: Danila Danko Date: Tue, 27 Feb 2024 23:20:27 +0300 Subject: [PATCH 24/42] feat: include phi-grammar --- site/docs/src/media/phi-grammar.png | Bin 0 -> 105642 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 site/docs/src/media/phi-grammar.png diff --git a/site/docs/src/media/phi-grammar.png b/site/docs/src/media/phi-grammar.png new file mode 100644 index 0000000000000000000000000000000000000000..02aafb6ae0c90faec36a4f647b8902edfeb1c1b9 GIT binary patch literal 105642 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 Date: Tue, 27 Feb 2024 23:24:09 +0300 Subject: [PATCH 25/42] fix: update dependencies --- eo-phi-normalizer/eo-phi-normalizer.cabal | 4 ++++ eo-phi-normalizer/package.yaml | 2 ++ 2 files changed, 6 insertions(+) diff --git a/eo-phi-normalizer/eo-phi-normalizer.cabal b/eo-phi-normalizer/eo-phi-normalizer.cabal index dd0f26c8e..cf3ce383e 100644 --- a/eo-phi-normalizer/eo-phi-normalizer.cabal +++ b/eo-phi-normalizer/eo-phi-normalizer.cabal @@ -66,6 +66,7 @@ library , lens , mtl , string-interpolate + , text , yaml default-language: Haskell2010 @@ -85,6 +86,7 @@ executable normalizer BNFC:bnfc >=2.9.4.1 build-depends: aeson + , aeson-pretty , array >=0.5.5.0 , base >=4.7 && <5 , directory @@ -95,6 +97,7 @@ executable normalizer , mtl , optparse-applicative , string-interpolate + , text , yaml default-language: Haskell2010 @@ -131,5 +134,6 @@ test-suite eo-phi-normalizer-test , lens , mtl , string-interpolate + , text , yaml default-language: Haskell2010 diff --git a/eo-phi-normalizer/package.yaml b/eo-phi-normalizer/package.yaml index 896ffa136..a724babff 100644 --- a/eo-phi-normalizer/package.yaml +++ b/eo-phi-normalizer/package.yaml @@ -46,6 +46,7 @@ dependencies: - string-interpolate - lens - generic-lens + - text default-extensions: - ImportQualifiedPost @@ -85,6 +86,7 @@ executables: dependencies: - eo-phi-normalizer - optparse-applicative + - aeson-pretty tests: eo-phi-normalizer-test: From 566f0aa5f82dff386f4c970a84e556f9f8a9f9c2 Mon Sep 17 00:00:00 2001 From: Danila Danko Date: Tue, 27 Feb 2024 23:28:29 +0300 Subject: [PATCH 26/42] fix: pretty-print json output --- eo-phi-normalizer/app/Main.hs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/eo-phi-normalizer/app/Main.hs b/eo-phi-normalizer/app/Main.hs index 2c8441406..6ce6a1e10 100644 --- a/eo-phi-normalizer/app/Main.hs +++ b/eo-phi-normalizer/app/Main.hs @@ -21,9 +21,10 @@ import Data.Foldable (forM_) import Control.Lens ((^.)) import Data.Aeson (ToJSON) -import Data.Aeson.Text (encodeToLazyText) +import Data.Aeson.Encode.Pretty (Config (..), Indent (..), defConfig, encodePrettyToTextBuilder') import Data.List (nub) import Data.String.Interpolate (i) +import Data.Text.Internal.Builder (toLazyText) import Data.Text.Lazy.Lens import GHC.Generics (Generic) import Language.EO.Phi (Object (Formation), Program (Program), parseProgram, printTree) @@ -132,7 +133,7 @@ data StructuredJSON = StructuredJSON deriving (Generic, ToJSON) encodeToJSONString :: (ToJSON a) => a -> String -encodeToJSONString = (^. unpacked) . encodeToLazyText +encodeToJSONString = (^. unpacked) . toLazyText . encodePrettyToTextBuilder' defConfig{confIndent = Spaces 2} pprefs :: ParserPrefs pprefs = prefs (showHelpOnEmpty <> showHelpOnError) From 5a61fe05e35cfc3e51ad4f329ddb069e1d0331d5 Mon Sep 17 00:00:00 2001 From: Danila Danko Date: Tue, 27 Feb 2024 23:28:57 +0300 Subject: [PATCH 27/42] refactor: format --- README.md | 1 - site/docs/src/commands/normalizer-metrics.md | 14 ++++++++++++-- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 862aee592..f4224c1a2 100644 --- a/README.md +++ b/README.md @@ -6,4 +6,3 @@ Command line normalizer of 𝜑-calculus expressions (as produced by the [EO compiler](https://github.com/objectionary/eo)). Consult the [project documentation](https://www.objectionary.com/normalizer/) for more details. - diff --git a/site/docs/src/commands/normalizer-metrics.md b/site/docs/src/commands/normalizer-metrics.md index ac85c9068..07a0fbe1c 100644 --- a/site/docs/src/commands/normalizer-metrics.md +++ b/site/docs/src/commands/normalizer-metrics.md @@ -75,7 +75,12 @@ normalizer metrics --input-file program.phi ``` ```json -{ "applications": 1, "dataless": 5, "dispatches": 5, "formations": 5 } +{ + "applications": 1, + "dataless": 5, + "dispatches": 5, + "formations": 5 +} ``` ### --input-file - @@ -85,5 +90,10 @@ cat program.phi | normalizer metrics --input-file - ``` ```json -{ "applications": 1, "dataless": 5, "dispatches": 5, "formations": 5 } +{ + "applications": 1, + "dataless": 5, + "dispatches": 5, + "formations": 5 +} ``` From 561007864b1e8ee1219607840a39585c0ac6566f Mon Sep 17 00:00:00 2001 From: Danila Danko Date: Tue, 27 Feb 2024 23:29:35 +0300 Subject: [PATCH 28/42] fix: run mdsh script on more files --- flake.nix | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/flake.nix b/flake.nix index 5312f3587..97f0a424f 100644 --- a/flake.nix +++ b/flake.nix @@ -178,7 +178,19 @@ text = '' export LANG=C.utf8 mdsh - mdsh -i site/docs/src/user-defined-rules.md --work_dir . + # create sample program + mdsh -i site/docs/src/common/sample-program.md --work_dir . + # run commands on it + + ${lib.concatMapStringsSep "\n" + (x: "mdsh -i site/docs/src/${x} --work_dir .") + [ + "commands/normalizer.md" + "commands/normalizer-transform.md" + "commands/normalizer-metrics.md" + "contributing.md" + ] + } prettier -w "**/*.md" ''; }; From 03c42238d67e84da306fdb939c9429a91e0885df Mon Sep 17 00:00:00 2001 From: Danila Danko Date: Tue, 27 Feb 2024 23:30:00 +0300 Subject: [PATCH 29/42] feat: add mdbook to devshell --- flake.nix | 1 + 1 file changed, 1 insertion(+) diff --git a/flake.nix b/flake.nix index 97f0a424f..4de7dc746 100644 --- a/flake.nix +++ b/flake.nix @@ -99,6 +99,7 @@ stack pkgs.gh mdsh + pkgs.mdbook # `cabal` already has a `ghc` on its `PATH`, # so you may remove `ghc` from this list. # Then, you can access `ghc` like `cabal exec -- ghc --version`. From bd3b55df81f05e9e889194d563d77896db29fb54 Mon Sep 17 00:00:00 2001 From: Danila Danko Date: Tue, 27 Feb 2024 23:31:46 +0300 Subject: [PATCH 30/42] fix: include `normalizer` into summary --- site/docs/src/SUMMARY.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/site/docs/src/SUMMARY.md b/site/docs/src/SUMMARY.md index 81c29f477..7ce79d7f3 100644 --- a/site/docs/src/SUMMARY.md +++ b/site/docs/src/SUMMARY.md @@ -2,7 +2,7 @@ - [Introduction](./introduction.md) - [Installation](./installation.md) -- [Commands](./commands.md) +- [normalizer](./commands/normalizer.md) - [normalizer transform](./commands/normalizer-transform.md) - [normalizer metrics](./commands/normalizer-metrics.md) - [Contributing](./contributing.md) From 4c3dd3fd194a7a5823ec25010c9f0a7fb6ddd8d8 Mon Sep 17 00:00:00 2001 From: Danila Danko Date: Tue, 27 Feb 2024 23:32:52 +0300 Subject: [PATCH 31/42] refactor: remove toc --- site/docs/src/commands/normalizer-metrics.md | 9 --------- site/docs/src/commands/normalizer-transform.md | 13 ------------- 2 files changed, 22 deletions(-) diff --git a/site/docs/src/commands/normalizer-metrics.md b/site/docs/src/commands/normalizer-metrics.md index 07a0fbe1c..41b72e4f3 100644 --- a/site/docs/src/commands/normalizer-metrics.md +++ b/site/docs/src/commands/normalizer-metrics.md @@ -1,14 +1,5 @@ # normalizer metrics -- [normalizer metrics](#normalizer-metrics) - - [Metrics](#metrics) - - [PHI grammar](#phi-grammar) - - [Object formations](#object-formations) - - [Object applications](#object-applications) - - [Dynamic dispatches](#dynamic-dispatches) - - [Dataless formations](#dataless-formations) - - [CLI](#cli) - ## Metrics We count: diff --git a/site/docs/src/commands/normalizer-transform.md b/site/docs/src/commands/normalizer-transform.md index 1b6b5fba3..44ea319b4 100644 --- a/site/docs/src/commands/normalizer-transform.md +++ b/site/docs/src/commands/normalizer-transform.md @@ -1,18 +1,5 @@ # normalizer transform -- [normalizer transform](#normalizer-transform) - - [`MetaPHI`](#metaphi) - - [phi-paper rules](#phi-paper-rules) - - [yegor.yaml](#yegoryaml) - - [Normal form](#normal-form) - - [Environment](#environment) - - [Repository](#repository) - - [Sample program](#sample-program) - - [CLI](#cli) - - [`--rules`](#rules) - - [`--chain`](#chain) - - [`--single`](#single) - ## `MetaPHI` You can define [rewrite rules](https://en.wikipedia.org/wiki/Rewriting#Term_rewriting_systems) for the `PHI` language (the $\varphi$-calculus language) using `YAML` and the `MetaPHI` language that is a superset of `PHI`. From 97ea0a0c1c69f7f09a26147a07233a0faf6da7d4 Mon Sep 17 00:00:00 2001 From: Danila Danko Date: Tue, 27 Feb 2024 23:46:53 +0300 Subject: [PATCH 32/42] feat: commands page --- site/docs/src/commands.md | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 site/docs/src/commands.md diff --git a/site/docs/src/commands.md b/site/docs/src/commands.md new file mode 100644 index 000000000..3d4a5c032 --- /dev/null +++ b/site/docs/src/commands.md @@ -0,0 +1,3 @@ +# Commands + +This section lists commands and options that you can use when you work with `normalizer`. From d035d6e8a797e378e3c89b82f34af6e2ef1b7959 Mon Sep 17 00:00:00 2001 From: Danila Danko Date: Tue, 27 Feb 2024 23:47:08 +0300 Subject: [PATCH 33/42] feat: links to commands pages --- site/docs/src/introduction.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/site/docs/src/introduction.md b/site/docs/src/introduction.md index ae124429e..4fa4e8dd6 100644 --- a/site/docs/src/introduction.md +++ b/site/docs/src/introduction.md @@ -2,6 +2,6 @@ Command line normalizer of 𝜑-calculus (`PHI`) expressions (as produced by the [EO compiler](https://github.com/objectionary/eo)). -This project aims to apply term rewriting techniques to "simplify" an input `PHI` expression and prepare it for further optimization passes. The simplification procedure will be a form of partial evaluation and normalization. +This project aims to apply term rewriting techniques to "simplify" an input `PHI` expression and prepare it for further optimization passes. The simplification procedure will be a form of partial evaluation and normalization (see [normalizer transform](./commands/normalizer-transform.md)). -Contrary to traditional normalization in λ-calculus, we aim at rewriting rules that would help reduce certain metrics of expressions (see [Metrics](./commands/normalizer-metrics.md)). +Contrary to traditional normalization in λ-calculus, we aim at rewriting rules that would help reduce certain metrics of expressions (see [normalizer metrics](./commands/normalizer-metrics.md)). From b2ceb5d8d8bb67a1bf2dda64184d8d5f10b4a4bf Mon Sep 17 00:00:00 2001 From: Danila Danko Date: Tue, 27 Feb 2024 23:47:21 +0300 Subject: [PATCH 34/42] feat: top-level `commands` section --- site/docs/src/SUMMARY.md | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/site/docs/src/SUMMARY.md b/site/docs/src/SUMMARY.md index 7ce79d7f3..d27b77ad6 100644 --- a/site/docs/src/SUMMARY.md +++ b/site/docs/src/SUMMARY.md @@ -2,7 +2,8 @@ - [Introduction](./introduction.md) - [Installation](./installation.md) -- [normalizer](./commands/normalizer.md) - - [normalizer transform](./commands/normalizer-transform.md) - - [normalizer metrics](./commands/normalizer-metrics.md) +- [Commands](./commands.md) + - [normalizer](./commands/normalizer.md) + - [normalizer transform](./commands/normalizer-transform.md) + - [normalizer metrics](./commands/normalizer-metrics.md) - [Contributing](./contributing.md) From bc4cc5602b6cfae8b1491ee1f3f19fad3a695fa9 Mon Sep 17 00:00:00 2001 From: Danila Danko Date: Wed, 28 Feb 2024 21:24:15 +0300 Subject: [PATCH 35/42] refactor: remove ImplicitParams --- eo-phi-normalizer/app/Main.hs | 27 ++++++++++++--------------- 1 file changed, 12 insertions(+), 15 deletions(-) diff --git a/eo-phi-normalizer/app/Main.hs b/eo-phi-normalizer/app/Main.hs index 6ce6a1e10..23ce01622 100644 --- a/eo-phi-normalizer/app/Main.hs +++ b/eo-phi-normalizer/app/Main.hs @@ -4,7 +4,6 @@ {-# LANGUAGE DeriveAnyClass #-} {-# LANGUAGE DeriveGeneric #-} {-# LANGUAGE DuplicateRecordFields #-} -{-# LANGUAGE ImplicitParams #-} {-# LANGUAGE LambdaCase #-} {-# LANGUAGE OverloadedLists #-} {-# LANGUAGE OverloadedRecordDot #-} @@ -138,15 +137,13 @@ encodeToJSONString = (^. unpacked) . toLazyText . encodePrettyToTextBuilder' def pprefs :: ParserPrefs pprefs = prefs (showHelpOnEmpty <> showHelpOnError) -type Context = (?parserContext :: Optparse.Context) - -die :: (Context) => String -> IO a -die message = do +die :: Optparse.Context -> String -> IO a +die parserContext message = do handleParseResult . Failure $ - parserFailure pprefs cliOpts (ErrorMsg message) [?parserContext] + parserFailure pprefs cliOpts (ErrorMsg message) [parserContext] -getProgram :: (Context) => Maybe FilePath -> Maybe String -> IO Program -getProgram inputFile expression = do +getProgram :: Optparse.Context -> Maybe FilePath -> Maybe String -> IO Program +getProgram parserContext inputFile expression = do src <- case (inputFile, expression) of (Just inputFile', Nothing) -> @@ -154,9 +151,9 @@ getProgram inputFile expression = do then getContents' else readFile inputFile' (Nothing, Just expression') -> pure expression' - _ -> die [i|You must specify either -#{head inputFileLongName}|--#{inputFileLongName} #{_FILE} or #{_PROGRAM}|] + _ -> die parserContext [i|You must specify either -#{head inputFileLongName}|--#{inputFileLongName} #{_FILE} or #{_PROGRAM}|] case parseProgram src of - Left err -> die [i|"An error occurred parsing the input program: #{err}|] + Left err -> die parserContext [i|"An error occurred parsing the input program: #{err}|] Right program -> pure program getLoggers :: Maybe FilePath -> IO (String -> IO (), String -> IO ()) @@ -172,14 +169,14 @@ main = do opts <- customExecParser pprefs cliOpts case opts of CLI'MetricsPhi' CLI'MetricsPhi{..} -> do - let ?parserContext = Optparse.Context metricsCommandName metricsParserInfo - program' <- getProgram inputFile program + let parserContext = Optparse.Context metricsCommandName metricsParserInfo + program' <- getProgram parserContext inputFile program (logStrLn, _) <- getLoggers outputFile let metrics = collectMetrics program' logStrLn $ encodeToJSONString metrics CLI'TransformPhi' CLI'TransformPhi{..} -> do - let ?parserContext = Optparse.Context transformCommandName transformParserInfo - program' <- getProgram inputFile program + let parserContext = Optparse.Context transformCommandName transformParserInfo + program' <- getProgram parserContext inputFile program (logStrLn, logStr) <- getLoggers outputFile ruleSet <- parseRuleSetFromFile rulesPath unless (single || json) $ logStrLn ruleSet.title @@ -189,7 +186,7 @@ main = do | otherwise = pure <$> applyRules (Common.Context (convertRule <$> ruleSet.rules) [Formation bindings]) (Formation bindings) uniqueResults = nub results totalResults = length uniqueResults - when (null uniqueResults || null (head uniqueResults)) $ die [i|Could not normalize the #{_PROGRAM}.|] + when (null uniqueResults || null (head uniqueResults)) $ die parserContext [i|Could not normalize the #{_PROGRAM}.|] let printAsProgramOrAsObject = \case Formation bindings' -> printTree $ Program bindings' x -> printTree x From 1d064139a508a4a62fd3f6b8b94f184c0cded0e0 Mon Sep 17 00:00:00 2001 From: Danila Danko Date: Wed, 28 Feb 2024 21:27:38 +0300 Subject: [PATCH 36/42] refactor: use MultiWayIf --- eo-phi-normalizer/app/Main.hs | 57 ++++++++++++++++++----------------- 1 file changed, 30 insertions(+), 27 deletions(-) diff --git a/eo-phi-normalizer/app/Main.hs b/eo-phi-normalizer/app/Main.hs index 23ce01622..e2eaae796 100644 --- a/eo-phi-normalizer/app/Main.hs +++ b/eo-phi-normalizer/app/Main.hs @@ -5,6 +5,7 @@ {-# LANGUAGE DeriveGeneric #-} {-# LANGUAGE DuplicateRecordFields #-} {-# LANGUAGE LambdaCase #-} +{-# LANGUAGE MultiWayIf #-} {-# LANGUAGE OverloadedLists #-} {-# LANGUAGE OverloadedRecordDot #-} {-# LANGUAGE OverloadedStrings #-} @@ -190,30 +191,32 @@ main = do let printAsProgramOrAsObject = \case Formation bindings' -> printTree $ Program bindings' x -> printTree x - if single - then - logStrLn - . (if json then encodeToJSONString else id) - . printAsProgramOrAsObject - $ head (head uniqueResults) - else do - if json - then - logStrLn . encodeToJSONString $ - StructuredJSON - { input = printTree program' - , output = (printAsProgramOrAsObject <$>) <$> results - } - else do - logStrLn "Input:" - logStrLn (printTree program') - logStrLn "====================================================" - forM_ (zip [1 ..] uniqueResults) $ \(index, steps) -> do - logStrLn $ - "Result " <> show index <> " out of " <> show totalResults <> ":" - let n = length steps - forM_ (zip [1 ..] steps) $ \(k, step) -> do - when chain $ - logStr ("[ " <> show k <> " / " <> show n <> " ]") - logStrLn . printAsProgramOrAsObject $ step - logStrLn "----------------------------------------------------" + if + | single && json -> + logStrLn + . encodeToJSONString + . printAsProgramOrAsObject + $ head (head uniqueResults) + | single -> + logStrLn + . printAsProgramOrAsObject + $ head (head uniqueResults) + | json -> + logStrLn . encodeToJSONString $ + StructuredJSON + { input = printTree program' + , output = (printAsProgramOrAsObject <$>) <$> results + } + | otherwise -> do + logStrLn "Input:" + logStrLn (printTree program') + logStrLn "====================================================" + forM_ (zip [1 ..] uniqueResults) $ \(index, steps) -> do + logStrLn $ + "Result " <> show index <> " out of " <> show totalResults <> ":" + let n = length steps + forM_ (zip [1 ..] steps) $ \(k, step) -> do + when chain $ + logStr ("[ " <> show k <> " / " <> show n <> " ]") + logStrLn . printAsProgramOrAsObject $ step + logStrLn "----------------------------------------------------" From 5ecd8031272281f4243db772a9cafee3671f5bd6 Mon Sep 17 00:00:00 2001 From: Danila Danko Date: Wed, 28 Feb 2024 22:09:14 +0300 Subject: [PATCH 37/42] fix: names --- eo-phi-normalizer/app/Main.hs | 38 +++++++++++++++++------------------ 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/eo-phi-normalizer/app/Main.hs b/eo-phi-normalizer/app/Main.hs index e2eaae796..d5ed5ad06 100644 --- a/eo-phi-normalizer/app/Main.hs +++ b/eo-phi-normalizer/app/Main.hs @@ -59,33 +59,33 @@ data CLI | CLI'MetricsPhi' CLI'MetricsPhi deriving (Show) -_FILE :: String -_FILE = "FILE" +fileMetavarName :: String +fileMetavarName = "FILE" -_PROGRAM :: String -_PROGRAM = "PROGRAM" +programMetavarName :: String +programMetavarName = "PROGRAM" fileMetavar :: Mod OptionFields a -fileMetavar = metavar _FILE +fileMetavar = metavar fileMetavarName -inputFileLongName :: String -inputFileLongName = "input-file" +inputFileOptionLongName :: String +inputFileOptionLongName = "input-file" inputFileOption :: Parser (Maybe FilePath) -inputFileOption = optional $ strOption (long inputFileLongName <> short 'i' <> help [i|#{_FILE} to read input from. When #{_FILE} is -, read from stdin. You must specify either this option or #{_PROGRAM}.|] <> fileMetavar) +inputFileOption = optional $ strOption (long inputFileOptionLongName <> short 'i' <> help [i|#{fileMetavarName} to read input from. When #{fileMetavarName} is -, read from stdin. You must specify either this option or #{programMetavarName}.|] <> fileMetavar) outputFileOption :: Parser (Maybe String) -outputFileOption = optional $ strOption (long "output-file" <> short 'o' <> help [i|Output to #{_FILE}. Output to stdout otherwise.|] <> fileMetavar) +outputFileOption = optional $ strOption (long "output-file" <> short 'o' <> help [i|Output to #{fileMetavarName}. Output to stdout otherwise.|] <> fileMetavar) programArg :: Parser (Maybe String) -programArg = optional $ strArgument (metavar _PROGRAM <> help "Program to work with.") +programArg = optional $ strArgument (metavar programMetavarName <> help "Program to work with.") jsonSwitch :: Parser Bool jsonSwitch = switch (long "json" <> short 'j' <> help "Output JSON.") -cli'TransformPhi :: Parser CLI'TransformPhi -cli'TransformPhi = do - rulesPath <- strOption (long "rules" <> short 'r' <> help [i|#{_FILE} with user-defined rules.|] <> fileMetavar) +cliTransformPhiParser :: Parser CLI'TransformPhi +cliTransformPhiParser = do + rulesPath <- strOption (long "rules" <> short 'r' <> help [i|#{fileMetavarName} with user-defined rules.|] <> fileMetavar) inputFile <- inputFileOption program <- programArg chain <- switch (long "chain" <> short 'c' <> help "Output transformation steps.") @@ -94,18 +94,18 @@ cli'TransformPhi = do single <- switch (long "single" <> short 's' <> help "Output a single expression.") pure CLI'TransformPhi{..} -cli'MetricsPhi :: Parser CLI'MetricsPhi -cli'MetricsPhi = do +cliMetricsPhiParser :: Parser CLI'MetricsPhi +cliMetricsPhiParser = do inputFile <- inputFileOption outputFile <- outputFileOption program <- programArg pure CLI'MetricsPhi{..} metricsParserInfo :: ParserInfo CLI -metricsParserInfo = info (CLI'MetricsPhi' <$> cli'MetricsPhi) (progDesc "Collect metrics for a PHI program.") +metricsParserInfo = info (CLI'MetricsPhi' <$> cliMetricsPhiParser) (progDesc "Collect metrics for a PHI program.") transformParserInfo :: ParserInfo CLI -transformParserInfo = info (CLI'TransformPhi' <$> cli'TransformPhi) (progDesc "Transform a PHI program.") +transformParserInfo = info (CLI'TransformPhi' <$> cliTransformPhiParser) (progDesc "Transform a PHI program.") transformCommandName :: String transformCommandName = "transform" @@ -152,7 +152,7 @@ getProgram parserContext inputFile expression = do then getContents' else readFile inputFile' (Nothing, Just expression') -> pure expression' - _ -> die parserContext [i|You must specify either -#{head inputFileLongName}|--#{inputFileLongName} #{_FILE} or #{_PROGRAM}|] + _ -> die parserContext [i|You must specify either -#{head inputFileOptionLongName}|--#{inputFileOptionLongName} #{fileMetavarName} or #{programMetavarName}|] case parseProgram src of Left err -> die parserContext [i|"An error occurred parsing the input program: #{err}|] Right program -> pure program @@ -187,7 +187,7 @@ main = do | otherwise = pure <$> applyRules (Common.Context (convertRule <$> ruleSet.rules) [Formation bindings]) (Formation bindings) uniqueResults = nub results totalResults = length uniqueResults - when (null uniqueResults || null (head uniqueResults)) $ die parserContext [i|Could not normalize the #{_PROGRAM}.|] + when (null uniqueResults || null (head uniqueResults)) $ die parserContext [i|Could not normalize the #{programMetavarName}.|] let printAsProgramOrAsObject = \case Formation bindings' -> printTree $ Program bindings' x -> printTree x From c1592612900c707ab09fa2ba316b233857409452 Mon Sep 17 00:00:00 2001 From: Danila Danko Date: Wed, 28 Feb 2024 22:36:04 +0300 Subject: [PATCH 38/42] refactor: dataless formation definition --- site/docs/src/commands/normalizer-metrics.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/site/docs/src/commands/normalizer-metrics.md b/site/docs/src/commands/normalizer-metrics.md index 41b72e4f3..601e4dd45 100644 --- a/site/docs/src/commands/normalizer-metrics.md +++ b/site/docs/src/commands/normalizer-metrics.md @@ -27,10 +27,12 @@ We count: ### Dataless formations -- `Primitive formation` - a formation that has a Δ-attribute. +- `Primitive formation` - a formation that has a `Δ` attribute. - `⟦ Δ ⤍ 00- ⟧` -- `Dataless formation` - not primitive and does not have attributes bound to primitive formations. +- `Dataless formation` - not primitive and does not have attributes that map to primitive formations. - `⟦ d ↦ ⟦ φ ↦ ξ.ρ.c, ν ↦ ⟦ Δ ⤍ 00- ⟧ ⟧, c ↦ ∅ ⟧` + - the outermost formation with attributes `d` and `c` is dataless because it is not primitive and its attributes do not map to primitive formations. + - `d` is not dataless because it has an attribute `ν` that maps to a primitive formation. ## Environment From b69a7a1bdb584ae94e104a15af2a94e5168f3d75 Mon Sep 17 00:00:00 2001 From: Danila Danko Date: Wed, 28 Feb 2024 22:51:47 +0300 Subject: [PATCH 39/42] refactor: simplify reading the program in the `transform` command --- eo-phi-normalizer/app/Main.hs | 42 +++++++++-------------------------- 1 file changed, 11 insertions(+), 31 deletions(-) diff --git a/eo-phi-normalizer/app/Main.hs b/eo-phi-normalizer/app/Main.hs index d5ed5ad06..77bab9cc4 100644 --- a/eo-phi-normalizer/app/Main.hs +++ b/eo-phi-normalizer/app/Main.hs @@ -43,14 +43,12 @@ data CLI'TransformPhi = CLI'TransformPhi , single :: Bool , json :: Bool , inputFile :: Maybe FilePath - , program :: Maybe String } deriving (Show) data CLI'MetricsPhi = CLI'MetricsPhi { inputFile :: Maybe FilePath , outputFile :: Maybe FilePath - , program :: Maybe String } deriving (Show) @@ -62,23 +60,14 @@ data CLI fileMetavarName :: String fileMetavarName = "FILE" -programMetavarName :: String -programMetavarName = "PROGRAM" - fileMetavar :: Mod OptionFields a fileMetavar = metavar fileMetavarName -inputFileOptionLongName :: String -inputFileOptionLongName = "input-file" - -inputFileOption :: Parser (Maybe FilePath) -inputFileOption = optional $ strOption (long inputFileOptionLongName <> short 'i' <> help [i|#{fileMetavarName} to read input from. When #{fileMetavarName} is -, read from stdin. You must specify either this option or #{programMetavarName}.|] <> fileMetavar) - outputFileOption :: Parser (Maybe String) outputFileOption = optional $ strOption (long "output-file" <> short 'o' <> help [i|Output to #{fileMetavarName}. Output to stdout otherwise.|] <> fileMetavar) -programArg :: Parser (Maybe String) -programArg = optional $ strArgument (metavar programMetavarName <> help "Program to work with.") +inputFileArg :: Parser (Maybe String) +inputFileArg = optional $ strArgument (metavar fileMetavarName <> help [i|#{fileMetavarName} to read input from. When not specified, read from stdin.|]) jsonSwitch :: Parser Bool jsonSwitch = switch (long "json" <> short 'j' <> help "Output JSON.") @@ -86,19 +75,17 @@ jsonSwitch = switch (long "json" <> short 'j' <> help "Output JSON.") cliTransformPhiParser :: Parser CLI'TransformPhi cliTransformPhiParser = do rulesPath <- strOption (long "rules" <> short 'r' <> help [i|#{fileMetavarName} with user-defined rules.|] <> fileMetavar) - inputFile <- inputFileOption - program <- programArg chain <- switch (long "chain" <> short 'c' <> help "Output transformation steps.") json <- jsonSwitch outputFile <- outputFileOption single <- switch (long "single" <> short 's' <> help "Output a single expression.") + inputFile <- inputFileArg pure CLI'TransformPhi{..} cliMetricsPhiParser :: Parser CLI'MetricsPhi cliMetricsPhiParser = do - inputFile <- inputFileOption + inputFile <- inputFileArg outputFile <- outputFileOption - program <- programArg pure CLI'MetricsPhi{..} metricsParserInfo :: ParserInfo CLI @@ -143,18 +130,11 @@ die parserContext message = do handleParseResult . Failure $ parserFailure pprefs cliOpts (ErrorMsg message) [parserContext] -getProgram :: Optparse.Context -> Maybe FilePath -> Maybe String -> IO Program -getProgram parserContext inputFile expression = do - src <- - case (inputFile, expression) of - (Just inputFile', Nothing) -> - if inputFile' == "-" - then getContents' - else readFile inputFile' - (Nothing, Just expression') -> pure expression' - _ -> die parserContext [i|You must specify either -#{head inputFileOptionLongName}|--#{inputFileOptionLongName} #{fileMetavarName} or #{programMetavarName}|] +getProgram :: Optparse.Context -> Maybe FilePath -> IO Program +getProgram parserContext inputFile = do + src <- maybe getContents' readFile inputFile case parseProgram src of - Left err -> die parserContext [i|"An error occurred parsing the input program: #{err}|] + Left err -> die parserContext [i|"An error occurred when parsing the input program: #{err}|] Right program -> pure program getLoggers :: Maybe FilePath -> IO (String -> IO (), String -> IO ()) @@ -171,13 +151,13 @@ main = do case opts of CLI'MetricsPhi' CLI'MetricsPhi{..} -> do let parserContext = Optparse.Context metricsCommandName metricsParserInfo - program' <- getProgram parserContext inputFile program + program' <- getProgram parserContext inputFile (logStrLn, _) <- getLoggers outputFile let metrics = collectMetrics program' logStrLn $ encodeToJSONString metrics CLI'TransformPhi' CLI'TransformPhi{..} -> do let parserContext = Optparse.Context transformCommandName transformParserInfo - program' <- getProgram parserContext inputFile program + program' <- getProgram parserContext inputFile (logStrLn, logStr) <- getLoggers outputFile ruleSet <- parseRuleSetFromFile rulesPath unless (single || json) $ logStrLn ruleSet.title @@ -187,7 +167,7 @@ main = do | otherwise = pure <$> applyRules (Common.Context (convertRule <$> ruleSet.rules) [Formation bindings]) (Formation bindings) uniqueResults = nub results totalResults = length uniqueResults - when (null uniqueResults || null (head uniqueResults)) $ die parserContext [i|Could not normalize the #{programMetavarName}.|] + when (null uniqueResults || null (head uniqueResults)) $ die parserContext [i|Could not normalize the program.|] let printAsProgramOrAsObject = \case Formation bindings' -> printTree $ Program bindings' x -> printTree x From 3b87fd7d6fd917f6fe1dffa0bd2cc24362cc271e Mon Sep 17 00:00:00 2001 From: Danila Danko Date: Wed, 28 Feb 2024 22:57:37 +0300 Subject: [PATCH 40/42] chore: update CLI docs --- site/docs/src/commands/normalizer-metrics.md | 17 +++++++---------- .../docs/src/commands/normalizer-transform.md | 19 ++++++++----------- 2 files changed, 15 insertions(+), 21 deletions(-) diff --git a/site/docs/src/commands/normalizer-metrics.md b/site/docs/src/commands/normalizer-metrics.md index 601e4dd45..dceb21c33 100644 --- a/site/docs/src/commands/normalizer-metrics.md +++ b/site/docs/src/commands/normalizer-metrics.md @@ -47,24 +47,21 @@ normalizer metrics --help ``` ```console -Usage: normalizer metrics [-i|--input-file FILE] [-o|--output-file FILE] - [PROGRAM] +Usage: normalizer metrics [FILE] [-o|--output-file FILE] Collect metrics for a PHI program. Available options: - -i,--input-file FILE FILE to read input from. When FILE is -, read from - stdin. You must specify either this option or - PROGRAM. + FILE FILE to read input from. When not specified, read + from stdin. -o,--output-file FILE Output to FILE. Output to stdout otherwise. - PROGRAM Program to work with. -h,--help Show this help text ``` -### --input-file program.phi +### PROGRAM ```$ as json -normalizer metrics --input-file program.phi +normalizer metrics program.phi ``` ```json @@ -76,10 +73,10 @@ normalizer metrics --input-file program.phi } ``` -### --input-file - +### PROGRAM not specified ```$ as json -cat program.phi | normalizer metrics --input-file - +cat program.phi | normalizer metrics ``` ```json diff --git a/site/docs/src/commands/normalizer-transform.md b/site/docs/src/commands/normalizer-transform.md index 44ea319b4..4effe443a 100644 --- a/site/docs/src/commands/normalizer-transform.md +++ b/site/docs/src/commands/normalizer-transform.md @@ -68,22 +68,19 @@ normalizer transform --help ``` ```console -Usage: normalizer transform (-r|--rules FILE) [-i|--input-file FILE] [PROGRAM] - [-c|--chain] [-j|--json] [-o|--output-file FILE] - [-s|--single] +Usage: normalizer transform (-r|--rules FILE) [-c|--chain] [-j|--json] + [-o|--output-file FILE] [-s|--single] [FILE] Transform a PHI program. Available options: -r,--rules FILE FILE with user-defined rules. - -i,--input-file FILE FILE to read input from. When FILE is -, read from - stdin. You must specify either this option or - PROGRAM. - PROGRAM Program to work with. -c,--chain Output transformation steps. -j,--json Output JSON. -o,--output-file FILE Output to FILE. Output to stdout otherwise. -s,--single Output a single expression. + FILE FILE to read input from. When not specified, read + from stdin. -h,--help Show this help text ``` @@ -94,7 +91,7 @@ Normalize a 𝜑-expression from `program.phi` using the [yegor.yaml](#yegoryaml There can be multiple numbered results that correspond to multiple rule application sequences. ```$ as console -normalizer transform --rules ./eo-phi-normalizer/test/eo/phi/rules/yegor.yaml -i program.phi +normalizer transform --rules ./eo-phi-normalizer/test/eo/phi/rules/yegor.yaml program.phi ``` ```console @@ -112,7 +109,7 @@ Result 1 out of 1: Use `--chain` to see numbered normalization steps for each normalization result. ```$ as console -normalizer transform --chain --rules ./eo-phi-normalizer/test/eo/phi/rules/yegor.yaml --input-file program.phi +normalizer transform --chain --rules ./eo-phi-normalizer/test/eo/phi/rules/yegor.yaml program.phi ``` ```console @@ -163,7 +160,7 @@ Result 6 out of 6: Use `--single` to print a single normalized program. ```$ as console -normalizer transform --single --rules ./eo-phi-normalizer/test/eo/phi/rules/yegor.yaml --input-file program.phi +normalizer transform --single --rules ./eo-phi-normalizer/test/eo/phi/rules/yegor.yaml program.phi ``` ```console @@ -173,7 +170,7 @@ normalizer transform --single --rules ./eo-phi-normalizer/test/eo/phi/rules/yego ### `--json` ```$ as json -normalizer transform --json --chain --rules ./eo-phi-normalizer/test/eo/phi/rules/yegor.yaml --input-file program.phi +normalizer transform --json --chain --rules ./eo-phi-normalizer/test/eo/phi/rules/yegor.yaml program.phi ``` ```json From f3c2c36ee6b89288f7aa40876d23db3cc3bbba77 Mon Sep 17 00:00:00 2001 From: Danila Danko Date: Wed, 28 Feb 2024 23:28:54 +0300 Subject: [PATCH 41/42] fix: CLI option descriptions --- eo-phi-normalizer/app/Main.hs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/eo-phi-normalizer/app/Main.hs b/eo-phi-normalizer/app/Main.hs index 77bab9cc4..2aca46c7d 100644 --- a/eo-phi-normalizer/app/Main.hs +++ b/eo-phi-normalizer/app/Main.hs @@ -64,17 +64,17 @@ fileMetavar :: Mod OptionFields a fileMetavar = metavar fileMetavarName outputFileOption :: Parser (Maybe String) -outputFileOption = optional $ strOption (long "output-file" <> short 'o' <> help [i|Output to #{fileMetavarName}. Output to stdout otherwise.|] <> fileMetavar) +outputFileOption = optional $ strOption (long "output-file" <> short 'o' <> help [i|Output to #{fileMetavarName}. When this option is not specified, output to stdout.|] <> fileMetavar) inputFileArg :: Parser (Maybe String) -inputFileArg = optional $ strArgument (metavar fileMetavarName <> help [i|#{fileMetavarName} to read input from. When not specified, read from stdin.|]) +inputFileArg = optional $ strArgument (metavar fileMetavarName <> help [i|#{fileMetavarName} to read input from. When no #{fileMetavarName} is specified, read from stdin.|]) jsonSwitch :: Parser Bool jsonSwitch = switch (long "json" <> short 'j' <> help "Output JSON.") cliTransformPhiParser :: Parser CLI'TransformPhi cliTransformPhiParser = do - rulesPath <- strOption (long "rules" <> short 'r' <> help [i|#{fileMetavarName} with user-defined rules.|] <> fileMetavar) + rulesPath <- strOption (long "rules" <> short 'r' <> help [i|#{fileMetavarName} with user-defined rules. Must be specified.|] <> fileMetavar) chain <- switch (long "chain" <> short 'c' <> help "Output transformation steps.") json <- jsonSwitch outputFile <- outputFileOption From fbb182b23fdd5a902eb1f77a2b7d62c9f755e636 Mon Sep 17 00:00:00 2001 From: Danila Danko Date: Wed, 28 Feb 2024 23:29:04 +0300 Subject: [PATCH 42/42] chore: update docs --- site/docs/src/commands/normalizer-metrics.md | 13 ++--- .../docs/src/commands/normalizer-transform.md | 53 +++++++++++++------ 2 files changed, 44 insertions(+), 22 deletions(-) diff --git a/site/docs/src/commands/normalizer-metrics.md b/site/docs/src/commands/normalizer-metrics.md index dceb21c33..08b57de5d 100644 --- a/site/docs/src/commands/normalizer-metrics.md +++ b/site/docs/src/commands/normalizer-metrics.md @@ -40,7 +40,7 @@ We count: ## CLI -### --help +### `--help` ```$ as console normalizer metrics --help @@ -52,13 +52,14 @@ Usage: normalizer metrics [FILE] [-o|--output-file FILE] Collect metrics for a PHI program. Available options: - FILE FILE to read input from. When not specified, read - from stdin. - -o,--output-file FILE Output to FILE. Output to stdout otherwise. + FILE FILE to read input from. When no FILE is specified, + read from stdin. + -o,--output-file FILE Output to FILE. When this option is not specified, + output to stdout. -h,--help Show this help text ``` -### PROGRAM +### `FILE` ```$ as json normalizer metrics program.phi @@ -73,7 +74,7 @@ normalizer metrics program.phi } ``` -### PROGRAM not specified +### `FILE` not specified (read from stdin) ```$ as json cat program.phi | normalizer metrics diff --git a/site/docs/src/commands/normalizer-transform.md b/site/docs/src/commands/normalizer-transform.md index 4effe443a..22b39f4c5 100644 --- a/site/docs/src/commands/normalizer-transform.md +++ b/site/docs/src/commands/normalizer-transform.md @@ -63,6 +63,8 @@ cd normalizer ## CLI +### `--help` + ```$ as console normalizer transform --help ``` @@ -74,13 +76,14 @@ Usage: normalizer transform (-r|--rules FILE) [-c|--chain] [-j|--json] Transform a PHI program. Available options: - -r,--rules FILE FILE with user-defined rules. + -r,--rules FILE FILE with user-defined rules. Must be specified. -c,--chain Output transformation steps. -j,--json Output JSON. - -o,--output-file FILE Output to FILE. Output to stdout otherwise. + -o,--output-file FILE Output to FILE. When this option is not specified, + output to stdout. -s,--single Output a single expression. - FILE FILE to read input from. When not specified, read - from stdin. + FILE FILE to read input from. When no FILE is specified, + read from stdin. -h,--help Show this help text ``` @@ -155,18 +158,6 @@ Result 6 out of 6: ---------------------------------------------------- ``` -### `--single` - -Use `--single` to print a single normalized program. - -```$ as console -normalizer transform --single --rules ./eo-phi-normalizer/test/eo/phi/rules/yegor.yaml program.phi -``` - -```console -{ ⟦ a ↦ ξ.b (c ↦ ⟦ ⟧).d (ρ ↦ ⟦ b ↦ ⟦ d ↦ ⟦ φ ↦ ξ.ρ.c, ν ↦ ⟦ Δ ⤍ 00- ⟧ ⟧, c ↦ ∅, ν ↦ ⟦ Δ ⤍ 00- ⟧ ⟧ ⟧) ⟧ } -``` - ### `--json` ```$ as json @@ -216,3 +207,33 @@ normalizer transform --json --chain --rules ./eo-phi-normalizer/test/eo/phi/rule ] } ``` + +### `--single` + +```$ as console +normalizer transform --single --rules ./eo-phi-normalizer/test/eo/phi/rules/yegor.yaml program.phi +``` + +```console +{ ⟦ a ↦ ξ.b (c ↦ ⟦ ⟧).d (ρ ↦ ⟦ b ↦ ⟦ d ↦ ⟦ φ ↦ ξ.ρ.c, ν ↦ ⟦ Δ ⤍ 00- ⟧ ⟧, c ↦ ∅, ν ↦ ⟦ Δ ⤍ 00- ⟧ ⟧ ⟧) ⟧ } +``` + +### `--single` `--json` + +```$ as console +normalizer transform --single --json --rules ./eo-phi-normalizer/test/eo/phi/rules/yegor.yaml program.phi +``` + +```console +"{ ⟦ a ↦ ξ.b (c ↦ ⟦ ⟧).d (ρ ↦ ⟦ b ↦ ⟦ d ↦ ⟦ φ ↦ ξ.ρ.c, ν ↦ ⟦ Δ ⤍ 00- ⟧ ⟧, c ↦ ∅, ν ↦ ⟦ Δ ⤍ 00- ⟧ ⟧ ⟧) ⟧ }" +``` + +### `FILE` not specified (read from stdin) + +```$ as console +cat program.phi | normalizer transform --single --json --rules ./eo-phi-normalizer/test/eo/phi/rules/yegor.yaml +``` + +```console +"{ ⟦ a ↦ ξ.b (c ↦ ⟦ ⟧).d (ρ ↦ ⟦ b ↦ ⟦ d ↦ ⟦ φ ↦ ξ.ρ.c, ν ↦ ⟦ Δ ⤍ 00- ⟧ ⟧, c ↦ ∅, ν ↦ ⟦ Δ ⤍ 00- ⟧ ⟧ ⟧) ⟧ }" +```