phino 0.0.0.49 → 0.0.0.50
raw patch · 26 files changed
+1207/−642 lines, 26 filesPVP: major bump suggested
API removals or changes: PVP suggests a major version bump
API changes (from Hackage documentation)
- CST: EX_APPLICATION' :: EXPRESSION -> EOL -> TAB -> APP_ARG -> EOL -> TAB -> EXPRESSION
- CST: instance CST.HasEOL CST.APP_ARG
- CST: instance CST.HasEOL CST.APP_ARGS
- CST: instance CST.HasEOL CST.BINDING
- CST: instance CST.HasEOL CST.BINDINGS
- CST: instance CST.HasEOL CST.EXPRESSION
- CST: instance CST.HasEOL CST.PAIR
- Logger: setLogLevel :: LogLevel -> IO ()
- Rewriter: Rewritten :: Program -> Maybe Rule -> Rewritten
- Rewriter: [_sequence] :: RewriteContext -> Bool
- Rewriter: [maybeRule] :: Rewritten -> Maybe Rule
- Rewriter: [program] :: Rewritten -> Program
- Rewriter: data Rewritten
- Rewriter: instance GHC.Classes.Eq Rewriter.Rewritten
+ CST: APP_BINDING :: PAIR -> APP_BINDING
+ CST: EX_APPLICATION_EXPRS :: EXPRESSION -> EOL -> TAB -> APP_ARG -> EOL -> TAB -> Integer -> EXPRESSION
+ CST: EX_APPLICATION_TAUS :: EXPRESSION -> EOL -> TAB -> BINDING -> EOL -> TAB -> Integer -> EXPRESSION
+ CST: PA_FORMATION :: ATTRIBUTE -> [ATTRIBUTE] -> ARROW -> EXPRESSION -> PAIR
+ CST: [rhos] :: EXPRESSION -> [Binding]
+ CST: [tau] :: EXPRESSION -> APP_BINDING
+ CST: [taus] :: EXPRESSION -> BINDING
+ CST: [voids] :: PAIR -> [ATTRIBUTE]
+ CST: inlinedEOL :: Bool -> EOL
+ CST: instance CST.HasEOL AST.Binding
+ CST: instance CST.HasEOL AST.Expression
+ CST: instance CST.HasEOL [AST.Binding]
+ CST: instance CST.HasEOL [AST.Expression]
+ CST: instance CST.ToCST AST.Binding CST.APP_BINDING
+ CST: instance GHC.Classes.Eq CST.APP_BINDING
+ CST: instance GHC.Show.Show CST.APP_BINDING
+ CST: newtype APP_BINDING
+ CST: tabOfEOL :: EOL -> Integer -> TAB
+ Condition: instance GHC.Exception.Type.Exception Condition.ConditionException
+ Condition: instance GHC.Show.Show Condition.ConditionException
+ Condition: parseCondition :: String -> Either String Condition
+ Condition: parseConditionThrows :: String -> IO Condition
+ Encoding: instance Encoding.ToASCII CST.APP_BINDING
+ LaTeX: instance LaTeX.ToLaTeX CST.APP_BINDING
+ Lining: instance Lining.ToSingleLine CST.APP_BINDING
+ Logger: setLogConfig :: LogLevel -> Integer -> IO ()
+ Misc: matchDataObject :: Expression -> Maybe (String, Bytes)
+ Misc: pattern BaseObject :: String -> Expression
+ Must: validateMust :: Must -> Maybe String
+ Parser: PhiParser :: Parser Attribute -> Parser Binding -> Parser Expression -> Parser String -> PhiParser
+ Parser: [_attribute] :: PhiParser -> Parser Attribute
+ Parser: [_binding] :: PhiParser -> Parser Binding
+ Parser: [_expression] :: PhiParser -> Parser Expression
+ Parser: [_string] :: PhiParser -> Parser String
+ Parser: data PhiParser
+ Parser: phiParser :: PhiParser
+ Printer: printSubsts' :: [Subst] -> PrintConfig -> String
+ Render: instance Render.Render CST.APP_BINDING
+ Rewriter: type Rewritten = (Program, Maybe String)
+ Sugar: instance Sugar.ToSalty CST.APP_BINDING
+ Yaml: instance GHC.Classes.Eq Yaml.Comparable
+ Yaml: instance GHC.Classes.Eq Yaml.Condition
+ Yaml: instance GHC.Classes.Eq Yaml.Number
- CST: EX_APPLICATION :: EXPRESSION -> EOL -> TAB -> BINDING -> EOL -> TAB -> EXPRESSION
+ CST: EX_APPLICATION :: EXPRESSION -> EOL -> TAB -> APP_BINDING -> EOL -> TAB -> Integer -> EXPRESSION
- CST: EX_NUMBER :: Either Integer Double -> TAB -> EXPRESSION
+ CST: EX_NUMBER :: Either Integer Double -> TAB -> [Binding] -> EXPRESSION
- CST: EX_STRING :: String -> TAB -> EXPRESSION
+ CST: EX_STRING :: String -> TAB -> [Binding] -> EXPRESSION
- CST: [bindings] :: EXPRESSION -> BINDING
+ CST: [bindings] :: BINDINGS -> BINDINGS
- CST: [indent] :: TAB -> Integer
+ CST: [indent] :: EXPRESSION -> Integer
- CST: toCST :: ToCST a b => a -> Integer -> b
+ CST: toCST :: ToCST a b => a -> Integer -> EOL -> b
- Dataize: dataize :: Program -> DataizeContext -> IO (Maybe Bytes)
+ Dataize: dataize :: Program -> DataizeContext -> IO Dataized
- Dataize: dataize' :: Expression -> DataizeContext -> IO (Maybe Bytes)
+ Dataize: dataize' :: Dataizable -> DataizeContext -> IO Dataized
- Dataize: morph :: Expression -> DataizeContext -> IO (Maybe Expression)
+ Dataize: morph :: Morphed -> DataizeContext -> IO Morphed
- Rewriter: RewriteContext :: Integer -> Integer -> Bool -> Bool -> BuildTermFunc -> Must -> SaveStepFunc -> RewriteContext
+ Rewriter: RewriteContext :: Integer -> Integer -> Bool -> BuildTermFunc -> Must -> SaveStepFunc -> RewriteContext
Files
- phino.cabal +3/−1
- src/Builder.hs +0/−4
- src/CLI.hs +321/−204
- src/CST.hs +174/−108
- src/Condition.hs +163/−0
- src/Dataize.hs +82/−66
- src/Encoding.hs +7/−2
- src/Functions.hs +1/−1
- src/Hide.hs +2/−4
- src/LaTeX.hs +12/−17
- src/Lining.hs +7/−2
- src/Logger.hs +19/−7
- src/Misc.hs +15/−4
- src/Must.hs +14/−1
- src/Parser.hs +64/−53
- src/Printer.hs +8/−7
- src/Render.hs +9/−6
- src/Rewriter.hs +15/−24
- src/Sugar.hs +74/−52
- src/Yaml.hs +3/−3
- test/CLISpec.hs +136/−18
- test/ConditionSpec.hs +51/−0
- test/DataizeSpec.hs +19/−9
- test/HideSpec.hs +1/−5
- test/ParserSpec.hs +4/−40
- test/RewriterSpec.hs +3/−4
phino.cabal view
@@ -1,6 +1,6 @@ cabal-version: 3.0 name: phino-version: 0.0.0.49+version: 0.0.0.50 license: MIT synopsis: Command-Line Manipulator of 𝜑-Calculus Expressions description: Please see the README on GitHub at <https://github.com/objectionary/phino#readme>@@ -35,6 +35,7 @@ AST Builder CLI+ Condition CST Dataize Deps@@ -112,6 +113,7 @@ other-modules: BuilderSpec CLISpec+ ConditionSpec CSTSpec DataizeSpec FunctionsSpec
src/Builder.hs view
@@ -43,10 +43,6 @@ type Built a = Either String a --- @todo #277:30min Error messages are too verbose. Now, if we can't build expression or binding, we--- throw an exception and just print whole expression or binding to console.--- If this elements are big, it's just a mess and error message became unreadable. It would be nice to--- print expression or binding in some reduce way, removing some parts or printing only first N lines instance Show BuildException where show CouldNotBuildExpression {..} = printf
src/CLI.hs view
@@ -10,16 +10,19 @@ module CLI (runCLI) where import AST+import Condition (parseConditionThrows) import Control.Exception (Exception (displayException), SomeException, handle, throw, throwIO) import Control.Exception.Base import Control.Monad (when, (>=>)) import Data.Char (toLower, toUpper)+import Data.Foldable (for_)+import qualified Data.Foldable import Data.Functor ((<&>)) import Data.List (intercalate)-import Data.Maybe (isJust, isNothing)+import Data.Maybe (fromJust, isJust, isNothing) import Data.Version (showVersion) import Dataize (DataizeContext (DataizeContext), dataize)-import Deps (saveStep)+import Deps (SaveStepFunc, saveStep) import Encoding (Encoding (ASCII, UNICODE)) import Functions (buildTerm) import qualified Functions@@ -30,13 +33,13 @@ import Merge (merge) import Misc (ensuredFile) import qualified Misc-import Must (Must (..))+import Must (Must (..), validateMust) import Options.Applicative import Parser (parseExpressionThrows, parseProgramThrows) import Paths_phino (version)-import Printer (printExpression') import qualified Printer as P import Rewriter (RewriteContext (RewriteContext), Rewritten (..), rewrite')+import Rule (RuleContext (RuleContext), matchProgramWithRule) import Sugar import System.Exit (ExitCode (..), exitFailure) import System.IO (getContents')@@ -51,7 +54,8 @@ xmirCtx :: XmirContext, nonumber :: Bool, expression :: Maybe String,- label :: Maybe String+ label :: Maybe String,+ outputFormat :: IOFormat } data CmdException@@ -70,6 +74,7 @@ | CmdDataize OptsDataize | CmdExplain OptsExplain | CmdMerge OptsMerge+ | CmdMatch OptsMatch data IOFormat = XMIR | PHI | LATEX deriving (Eq)@@ -81,18 +86,28 @@ data OptsDataize = OptsDataize { logLevel :: LogLevel,+ logLines :: Integer, inputFormat :: IOFormat,+ outputFormat :: IOFormat, sugarType :: SugarType, flat :: LineFormat,+ omitListing :: Bool,+ omitComments :: Bool,+ nonumber :: Bool,+ sequence :: Bool,+ depthSensitive :: Bool, maxDepth :: Integer, maxCycles :: Integer,- depthSensitive :: Bool,+ hide :: [String],+ expression :: Maybe String,+ label :: Maybe String, stepsDir :: Maybe FilePath, inputFile :: Maybe FilePath } data OptsExplain = OptsExplain { logLevel :: LogLevel,+ logLines :: Integer, rules :: [FilePath], normalize :: Bool, shuffle :: Bool,@@ -101,25 +116,26 @@ data OptsRewrite = OptsRewrite { logLevel :: LogLevel,- rules :: [FilePath],+ logLines :: Integer, inputFormat :: IOFormat, outputFormat :: IOFormat, sugarType :: SugarType, flat :: LineFormat,+ must :: Must, normalize :: Bool, shuffle :: Bool, omitListing :: Bool, omitComments :: Bool, depthSensitive :: Bool, nonumber :: Bool,- must :: Must,- maxDepth :: Integer,- maxCycles :: Integer, inPlace :: Bool, sequence :: Bool,+ maxDepth :: Integer,+ maxCycles :: Integer,+ rules :: [FilePath],+ hide :: [String], expression :: Maybe String, label :: Maybe String,- hide :: [String], targetFile :: Maybe FilePath, stepsDir :: Maybe FilePath, inputFile :: Maybe FilePath@@ -127,6 +143,7 @@ data OptsMerge = OptsMerge { logLevel :: LogLevel,+ logLines :: Integer, inputFormat :: IOFormat, outputFormat :: IOFormat, sugarType :: SugarType,@@ -137,6 +154,21 @@ inputs :: [FilePath] } +data OptsMatch = OptsMatch+ { logLevel :: LogLevel,+ logLines :: Integer,+ sugarType :: SugarType,+ flat :: LineFormat,+ pattern :: Maybe String,+ when' :: Maybe String,+ inputFile :: Maybe FilePath+ }++validateIntegerOption :: (Integer -> Bool) -> String -> Integer -> ReadM Integer+validateIntegerOption cmp msg num+ | cmp num = return num+ | otherwise = readerError msg+ optLogLevel :: Parser LogLevel optLogLevel = option@@ -159,6 +191,18 @@ "NONE" -> Right NONE _ -> Left $ "unknown log-level: " <> lvl +optLogLines :: Parser Integer+optLogLines =+ option+ (auto >>= validateIntegerOption (>= -1) "--log-lines must be >= -1")+ (long "log-lines" <> metavar "LINES" <> help "Amount of lines printed to console per each log operation (0 - print nothing, -1 - no limits)" <> value 25 <> showDefault)++optRule :: Parser [FilePath]+optRule = many (strOption (long "rule" <> metavar "[FILE]" <> help "Path to custom rule"))++optInputFormat :: Parser IOFormat+optInputFormat = option (parseIOFormat "input") (long "input" <> metavar "FORMAT" <> help "Program input format (phi, xmir)" <> value PHI <> showDefault)+ parseIOFormat :: String -> ReadM IOFormat parseIOFormat type' = eitherReader $ \format -> case (map toLower format, type') of ("xmir", _) -> Right XMIR@@ -166,27 +210,45 @@ ("latex", "output") -> Right LATEX _ -> Left (printf "The value '%s' can't be used for '--%s' option, use --help to check possible values" format type') +optOutputFormat :: Parser IOFormat+optOutputFormat =+ option+ (parseIOFormat "output")+ (long "output" <> metavar "FORMAT" <> help (printf "Result and intermediate (see %s option(s)) programs output format (phi, xmir, latex)" _intermediateOptions) <> value PHI <> showDefault)+ argInputFile :: Parser (Maybe FilePath) argInputFile = optional (argument str (metavar "FILE" <> help "Path to input file")) optMaxDepth :: Parser Integer-optMaxDepth = option auto (long "max-depth" <> metavar "DEPTH" <> help "Maximum number of rewriting iterations per rule" <> value 25 <> showDefault)+optMaxDepth =+ option+ (auto >>= validateIntegerOption (> 0) "--max-depth must be positive")+ (long "max-depth" <> metavar "DEPTH" <> help "Maximum number of rewriting iterations per rule" <> value 25 <> showDefault) optMaxCycles :: Parser Integer-optMaxCycles = option auto (long "max-cycles" <> metavar "CYCLES" <> help "Maximum number of rewriting cycles across all rules" <> value 25 <> showDefault)--optInputFormat :: Parser IOFormat-optInputFormat = option (parseIOFormat "input") (long "input" <> metavar "FORMAT" <> help "Program input format (phi, xmir)" <> value PHI <> showDefault)--optOutputFormat :: Parser IOFormat-optOutputFormat = option (parseIOFormat "output") (long "output" <> metavar "FORMAT" <> help "Program output format (phi, xmir, latex)" <> value PHI <> showDefault)+optMaxCycles =+ option+ (auto >>= validateIntegerOption (> 0) "--max-cycles must be positive")+ (long "max-cycles" <> metavar "CYCLES" <> help "Maximum number of rewriting cycles across all rules" <> value 25 <> showDefault) optDepthSensitive :: Parser Bool optDepthSensitive = switch (long "depth-sensitive" <> help "Fail if rewriting is not finished after reaching max attempts (see --max-cycles or --max-depth)") -optRule :: Parser [FilePath]-optRule = many (strOption (long "rule" <> metavar "[FILE]" <> help "Path to custom rule"))+optNonumber :: Parser Bool+optNonumber = switch (long "nonumber" <> help "Turn off equation auto numbering in LaTeX rendering (see --output option)") +optSequence :: Parser Bool+optSequence = switch (long "sequence" <> help "Result output contains all intermediate 𝜑-programs concatenated with EOL")++optExpression :: Parser (Maybe String)+optExpression = optional (strOption (long "expression" <> metavar "NAME" <> help "Name for 'phiExpression' element when rendering to LaTeX (see --output option)"))++optLabel :: Parser (Maybe String)+optLabel = optional (strOption (long "label" <> metavar "NAME" <> help "Name for 'label' element when rendering to LaTeX (see --output option)"))++optHide :: Parser [String]+optHide = many (strOption (long "hide" <> metavar "FQN" <> help "Location of object to exclude from result and intermediate programs after rewriting. Must be a valid dispatch expression; e.g. Q.org.eolang"))+ optNormalize :: Parser Bool optNormalize = switch (long "normalize" <> help "Use built-in normalization rules") @@ -199,14 +261,14 @@ optShuffle :: Parser Bool optShuffle = switch (long "shuffle" <> help "Shuffle rules before applying") -optSugar :: [String] -> Parser SugarType-optSugar opts = flag SALTY SWEET (long "sweet" <> help (printf "Print result and intermediate (see %s option(s)) 𝜑-programs using syntax sugar" (intercalate ", " opts)))+optSugar :: Parser SugarType+optSugar = flag SALTY SWEET (long "sweet" <> help (printf "Print result and intermediate (see %s option(s)) 𝜑-programs using syntax sugar" _intermediateOptions)) optSugar' :: Parser SugarType optSugar' = flag SALTY SWEET (long "sweet" <> help "Print result 𝜑-program using syntax sugar") -optLineFormat :: [String] -> Parser LineFormat-optLineFormat opts = flag MULTILINE SINGLELINE (long "flat" <> help (printf "Print result and intermediate (see %s option(s)) 𝜑-programs in one line" (intercalate ", " opts)))+optLineFormat :: Parser LineFormat+optLineFormat = flag MULTILINE SINGLELINE (long "flat" <> help (printf "Print result and intermediate (see %s option(s)) 𝜑-programs in one line" _intermediateOptions)) optLineFormat' :: Parser LineFormat optLineFormat' = flag MULTILINE SINGLELINE (long "flat" <> help "Print result 𝜑-program in one line")@@ -217,11 +279,15 @@ optOmitComments :: Parser Bool optOmitComments = switch (long "omit-comments" <> help "Omit comments in XMIR output") +_intermediateOptions :: String+_intermediateOptions = intercalate ", " ["--sequence", "--steps-dir"]+ explainParser :: Parser Command explainParser = CmdExplain <$> ( OptsExplain <$> optLogLevel+ <*> optLogLines <*> optRule <*> optNormalize <*> optShuffle@@ -230,64 +296,73 @@ dataizeParser :: Parser Command dataizeParser =- let steps = ["--steps-dir"]- in CmdDataize- <$> ( OptsDataize- <$> optLogLevel- <*> optInputFormat- <*> optSugar steps- <*> optLineFormat steps- <*> optMaxDepth- <*> optMaxCycles- <*> optDepthSensitive- <*> optStepsDir- <*> argInputFile- )+ CmdDataize+ <$> ( OptsDataize+ <$> optLogLevel+ <*> optLogLines+ <*> optInputFormat+ <*> optOutputFormat+ <*> optSugar+ <*> optLineFormat+ <*> optOmitListing+ <*> optOmitComments+ <*> optNonumber+ <*> optSequence+ <*> optDepthSensitive+ <*> optMaxDepth+ <*> optMaxCycles+ <*> optHide+ <*> optExpression+ <*> optLabel+ <*> optStepsDir+ <*> argInputFile+ ) rewriteParser :: Parser Command rewriteParser =- let opts = ["--sequence", "--steps-dir"]- in CmdRewrite- <$> ( OptsRewrite- <$> optLogLevel- <*> optRule- <*> optInputFormat- <*> optOutputFormat- <*> optSugar opts- <*> optLineFormat opts- <*> optNormalize- <*> optShuffle- <*> optOmitListing- <*> optOmitComments- <*> optDepthSensitive- <*> switch (long "nonumber" <> help "Turn off equation auto numbering in LaTeX rendering")- <*> option- auto- ( long "must"- <> metavar "RANGE"- <> help "Must-rewrite range (e.g., '3', '..5', '3..', '3..5'). Stops execution if number of rules applied is not in range. Use 0 to disable."- <> value MtDisabled- <> showDefaultWith show- )- <*> optMaxDepth- <*> optMaxCycles- <*> switch (long "in-place" <> help "Edit file in-place instead of printing to output")- <*> switch (long "sequence" <> help "Result output contains all intermediate 𝜑-programs concatenated with EOL")- <*> optional (strOption (long "expression" <> metavar "NAME" <> help "Name for 'phiExpression' element when rendering to LaTeX"))- <*> optional (strOption (long "label" <> metavar "NAME" <> help "Name for 'label' element when rendering to LaTeX"))- <*> many (strOption (long "hide" <> metavar "FQN" <> help "Location of object to exclude from result program after rewriting, must be a valid dispatch expression; e.g. Q.org.eolang"))- <*> optTarget- <*> optStepsDir- <*> argInputFile- )+ CmdRewrite+ <$> ( OptsRewrite+ <$> optLogLevel+ <*> optLogLines+ <*> optInputFormat+ <*> optOutputFormat+ <*> optSugar+ <*> optLineFormat+ <*> option+ auto+ ( long "must"+ <> metavar "RANGE"+ <> help "Must-rewrite range (e.g., '3', '..5', '3..', '3..5'). Stops execution if number of rules applied is not in range. Use 0 to disable."+ <> value MtDisabled+ <> showDefaultWith show+ )+ <*> optNormalize+ <*> optShuffle+ <*> optOmitListing+ <*> optOmitComments+ <*> optDepthSensitive+ <*> optNonumber+ <*> switch (long "in-place" <> help "Edit file in-place instead of printing to output")+ <*> optSequence+ <*> optMaxDepth+ <*> optMaxCycles+ <*> optRule+ <*> optHide+ <*> optExpression+ <*> optLabel+ <*> optTarget+ <*> optStepsDir+ <*> argInputFile+ ) mergeParser :: Parser Command mergeParser = CmdMerge <$> ( OptsMerge <$> optLogLevel+ <*> optLogLines <*> optInputFormat- <*> optOutputFormat+ <*> option (parseIOFormat "output") (long "output" <> metavar "FORMAT" <> help (printf "Result program output format (phi, xmir, latex)") <> value PHI <> showDefault) <*> optSugar' <*> optLineFormat' <*> optOmitListing@@ -296,6 +371,19 @@ <*> many (argument str (metavar "[FILE]" <> help "Paths to input files")) ) +matchParser :: Parser Command+matchParser =+ CmdMatch+ <$> ( OptsMatch+ <$> optLogLevel+ <*> optLogLines+ <*> optSugar+ <*> optLineFormat+ <*> optional (strOption (long "pattern" <> metavar "EXPRESSION" <> help "Pattern expression to match against"))+ <*> optional (strOption (long "when" <> metavar "CONDITION" <> help "Predicate for matched substitutions"))+ <*> argInputFile+ )+ commandParser :: Parser Command commandParser = hsubparser@@ -303,6 +391,7 @@ <> command "dataize" (info dataizeParser (progDesc "Dataize the 𝜑-program")) <> command "explain" (info explainParser (progDesc "Explain rules in LaTeX format")) <> command "merge" (info mergeParser (progDesc "Merge 𝜑-programs into single one by merging their top level formations"))+ <> command "match" (info matchParser (progDesc "Match 𝜑-program against provided pattern and build matched substitutions")) ) parserInfo :: ParserInfo Command@@ -318,14 +407,15 @@ logError (displayException e) exitFailure -setLogLevel' :: Command -> IO ()-setLogLevel' cmd =- let level = case cmd of- CmdRewrite OptsRewrite {logLevel} -> logLevel- CmdDataize OptsDataize {logLevel} -> logLevel- CmdExplain OptsExplain {logLevel} -> logLevel- CmdMerge OptsMerge {logLevel} -> logLevel- in setLogLevel level+setLogger :: Command -> IO ()+setLogger cmd =+ let (level, lines) = case cmd of+ CmdRewrite OptsRewrite {logLevel, logLines} -> (logLevel, logLines)+ CmdDataize OptsDataize {logLevel, logLines} -> (logLevel, logLines)+ CmdExplain OptsExplain {logLevel, logLines} -> (logLevel, logLines)+ CmdMerge OptsMerge {logLevel, logLines} -> (logLevel, logLines)+ CmdMatch OptsMatch {logLevel, logLines} -> (logLevel, logLines)+ in setLogConfig level lines invalidCLIArguments :: String -> IO a invalidCLIArguments msg = throwIO (InvalidCLIArguments msg)@@ -333,21 +423,22 @@ runCLI :: [String] -> IO () runCLI args = handle handler $ do cmd <- handleParseResult (execParserPure defaultPrefs parserInfo args)- setLogLevel' cmd+ setLogger cmd case cmd of CmdRewrite OptsRewrite {..} -> do validateOpts- exclude <- traverse (parseExpressionThrows >=> canBeHidden) hide+ exclude <- expressionsToHide hide logDebug (printf "Amount of rewriting cycles across all the rules: %d, per rule: %d" maxCycles maxDepth) input <- readInput inputFile rules' <- getRules normalize shuffle rules program <- parseProgram input inputFormat let listing = if null rules' then const input else (\prog -> P.printProgram' prog (sugarType, UNICODE, flat)) xmirCtx = XmirContext omitListing omitComments listing- printProgCtx = PrintProgCtx sugarType flat xmirCtx nonumber expression label- rewrittens <- rewrite' program rules' (context printProgCtx) <&> (`H.hide` exclude)+ printCtx = PrintProgCtx sugarType flat xmirCtx nonumber expression label outputFormat+ rewrittens <- rewrite' program rules' (context printCtx) <&> (`H.hide` exclude)+ let rewrittens' = if sequence then rewrittens else [last rewrittens] logDebug (printf "Printing rewritten 𝜑-program as %s" (show outputFormat))- progs <- printRewrittens printProgCtx rewrittens+ progs <- printRewrittens printCtx rewrittens' output targetFile progs where validateOpts :: IO ()@@ -358,18 +449,8 @@ when (inPlace && isJust targetFile) (invalidCLIArguments "--in-place and --target cannot be used together")- when- (nonumber && outputFormat /= LATEX)- (invalidCLIArguments "The --nonumber option can stay together with --output=latex only")- when- (isJust expression && outputFormat /= LATEX)- (invalidCLIArguments "The --expression option can stay together with --output=latex only")- when- (isJust label && outputFormat /= LATEX)- (invalidCLIArguments "The --label option can stay together with --output=latex only")- validateMaxDepth maxDepth- validateMaxCycles maxCycles- validateMust must+ validateLatexOptions outputFormat nonumber expression label+ validateMust' must validateXmirOptions outputFormat omitListing omitComments output :: Maybe FilePath -> String -> IO () output target prog = case (inPlace, target, inputFile) of@@ -390,46 +471,32 @@ maxDepth maxCycles depthSensitive- sequence buildTerm must- (saveStep stepsDir (ioToExtension outputFormat) (printProgram outputFormat ctx))- printRewrittens :: PrintProgramContext -> [Rewritten] -> IO String- printRewrittens ctx rewrittens- | outputFormat == LATEX = pure (rewrittensToLatex rewrittens (LatexContext sugarType flat nonumber expression label))- | otherwise = mapM (printProgram outputFormat ctx . program) rewrittens <&> intercalate "\n"- canBeHidden :: Expression -> IO Expression- canBeHidden expr = canBeHidden' expr expr- where- canBeHidden' :: Expression -> Expression -> IO Expression- canBeHidden' exp@(ExDispatch ExGlobal _) _ = pure exp- canBeHidden' exp@(ExDispatch expr attr) full = canBeHidden' expr full >> pure exp- canBeHidden' _ full =- invalidCLIArguments- ( printf- "Only dispatch expression started with Φ (or Q) can be used in --hide, but given: %s"- (printExpression' full P.logPrintConfig)- )+ (saveStepFunc stepsDir ctx) CmdDataize OptsDataize {..} -> do validateOpts+ exclude <- expressionsToHide hide input <- readInput inputFile prog <- parseProgram input inputFormat- dataized <- dataize prog (context prog)- maybe (throwIO CouldNotDataize) (putStrLn . P.printBytes) dataized+ let printCtx = PrintProgCtx sugarType flat defaultXmirContext nonumber expression label outputFormat+ (maybeBytes, seq) <- dataize prog (context prog printCtx)+ when sequence (putStrLn =<< printRewrittens printCtx (H.hide seq exclude))+ putStrLn (maybe (P.printExpression ExTermination) P.printBytes maybeBytes) where validateOpts :: IO () validateOpts = do- validateMaxDepth maxDepth- validateMaxCycles maxCycles- context :: Program -> DataizeContext- context program =+ validateLatexOptions outputFormat nonumber expression label+ validateXmirOptions outputFormat omitListing omitComments+ context :: Program -> PrintProgramContext -> DataizeContext+ context program ctx = DataizeContext program maxDepth maxCycles depthSensitive buildTerm- (saveStep stepsDir (ioToExtension PHI) (printProgram PHI (PrintProgCtx sugarType flat defaultXmirContext False Nothing Nothing)))+ (saveStepFunc stepsDir ctx) CmdExplain OptsExplain {..} -> do validateOpts rules' <- getRules normalize shuffle rules@@ -448,8 +515,8 @@ prog <- merge progs let listing = const (P.printProgram' prog (sugarType, UNICODE, flat)) xmirCtx = XmirContext omitListing omitComments listing- printProgCtx = PrintProgCtx sugarType flat xmirCtx False Nothing Nothing- prog' <- printProgram outputFormat printProgCtx prog+ printProgCtx = PrintProgCtx sugarType flat xmirCtx False Nothing Nothing outputFormat+ prog' <- printProgram printProgCtx prog output targetFile prog' where validateOpts :: IO ()@@ -458,84 +525,134 @@ (null inputs) (throwIO (InvalidCLIArguments "At least one input file must be specified for 'merge' command")) validateXmirOptions outputFormat omitListing omitComments+ CmdMatch OptsMatch {..} -> do+ input <- readInput inputFile+ prog <- parseProgram input PHI+ if isNothing pattern+ then logInfo "The --pattern is not provided, no substitutions are built"+ else do+ ptn <- parseExpressionThrows (fromJust pattern)+ condition <- traverse parseConditionThrows when'+ substs <- matchProgramWithRule prog (rule ptn condition) (RuleContext buildTerm)+ if null substs+ then logInfo "Provided pattern was not matched, no substitutions are built"+ else putStrLn (P.printSubsts' substs (sugarType, UNICODE, flat))+ where+ rule :: Expression -> Maybe Y.Condition -> Y.Rule+ rule ptn cnd = Y.Rule Nothing Nothing ptn ExGlobal cnd Nothing Nothing++-- Prepare saveStepFunc+saveStepFunc :: Maybe FilePath -> PrintProgramContext -> SaveStepFunc+saveStepFunc stepsDir ctx@PrintProgCtx {..} = saveStep stepsDir ioToExt (printProgram ctx) where- validateXmirOptions :: IOFormat -> Bool -> Bool -> IO ()- validateXmirOptions outputFormat omitListing omitComments = do- when- (outputFormat /= XMIR && omitListing)- (invalidCLIArguments "--omit-listing can be used only with --output-format=xmir")- when- (outputFormat /= XMIR && omitComments)- (invalidCLIArguments "--omit-comments can be used only with --output-format=xmir")- validateIntArgument :: Integer -> (Integer -> Bool) -> String -> IO ()- validateIntArgument num cmp msg =- when- (cmp num)- (invalidCLIArguments msg)- validateMaxDepth :: Integer -> IO ()- validateMaxDepth depth = validateIntArgument depth (<= 0) "--max-depth must be positive"- validateMaxCycles :: Integer -> IO ()- validateMaxCycles cycles = validateIntArgument cycles (<= 0) "--max-cycles must be positive"- validateMust :: Must -> IO ()- validateMust MtDisabled = pure ()- validateMust (MtExact n) = validateIntArgument n (<= 0) "--must exact value must be positive"- validateMust (MtRange minVal maxVal) = do- maybe (pure ()) (\n -> validateIntArgument n (< 0) "--must minimum must be non-negative") minVal- maybe (pure ()) (\n -> validateIntArgument n (< 0) "--must maximum must be non-negative") maxVal- case (minVal, maxVal) of- (Just min, Just max)- | min > max ->- invalidCLIArguments- (printf "--must range invalid: minimum (%d) is greater than maximum (%d)" min max)- _ -> pure ()- readInput :: Maybe FilePath -> IO String- readInput inputFile' = case inputFile' of- Just pth -> do- logDebug (printf "Reading from file: '%s'" pth)- readFile =<< ensuredFile pth- Nothing -> do- logDebug "Reading from stdin"- getContents' `catch` (\(e :: SomeException) -> throwIO (CouldNotReadFromStdin (show e)))- parseProgram :: String -> IOFormat -> IO Program- parseProgram phi PHI = parseProgramThrows phi- parseProgram xmir XMIR = do- doc <- parseXMIRThrows xmir- xmirToPhi doc- printProgram :: IOFormat -> PrintProgramContext -> Program -> IO String- printProgram PHI PrintProgCtx {..} prog = pure (P.printProgram' prog (sugar, UNICODE, line))- printProgram XMIR PrintProgCtx {..} prog = programToXMIR prog xmirCtx <&> printXMIR- printProgram LATEX PrintProgCtx {..} prog = pure (programToLaTeX prog (LatexContext sugar line nonumber expression label))- getRules :: Bool -> Bool -> [FilePath] -> IO [Y.Rule]- getRules normalize shuffle rules = do- ordered <-- if normalize+ ioToExt :: String+ ioToExt+ | outputFormat == LATEX = "tex"+ | otherwise = show outputFormat++-- Get list of expressions which must be hidden in printed programs+expressionsToHide :: [String] -> IO [Expression]+expressionsToHide = traverse (parseExpressionThrows >=> canBeHidden)+ where+ canBeHidden :: Expression -> IO Expression+ canBeHidden expr = canBeHidden' expr expr+ canBeHidden' :: Expression -> Expression -> IO Expression+ canBeHidden' exp@(ExDispatch ExGlobal _) _ = pure exp+ canBeHidden' exp@(ExDispatch expr attr) full = canBeHidden' expr full >> pure exp+ canBeHidden' _ full =+ invalidCLIArguments+ ( printf+ "Only dispatch expression started with Φ (or Q) can be used in --hide, but given: %s"+ (P.printExpression' full P.logPrintConfig)+ )++-- Validate LaTeX options+validateLatexOptions :: IOFormat -> Bool -> Maybe String -> Maybe String -> IO ()+validateLatexOptions outputFormat nonumber expression label = do+ when+ (nonumber && outputFormat /= LATEX)+ (invalidCLIArguments "The --nonumber option can stay together with --output=latex only")+ when+ (isJust expression && outputFormat /= LATEX)+ (invalidCLIArguments "The --expression option can stay together with --output=latex only")+ when+ (isJust label && outputFormat /= LATEX)+ (invalidCLIArguments "The --label option can stay together with --output=latex only")++-- Validate 'must' option+validateMust' :: Must -> IO ()+validateMust' must = for_ (validateMust must) invalidCLIArguments++-- Validate options for output to XMIR+validateXmirOptions :: IOFormat -> Bool -> Bool -> IO ()+validateXmirOptions outputFormat omitListing omitComments = do+ when+ (outputFormat /= XMIR && omitListing)+ (invalidCLIArguments "--omit-listing can be used only with --output=xmir")+ when+ (outputFormat /= XMIR && omitComments)+ (invalidCLIArguments "--omit-comments can be used only with --output=xmir")++-- Read input from file or stdin+readInput :: Maybe FilePath -> IO String+readInput inputFile' = case inputFile' of+ Just pth -> do+ logDebug (printf "Reading from file: '%s'" pth)+ readFile =<< ensuredFile pth+ Nothing -> do+ logDebug "Reading from stdin"+ getContents' `catch` (\(e :: SomeException) -> throwIO (CouldNotReadFromStdin (show e)))++-- Parse program from String input depending on input IO format+parseProgram :: String -> IOFormat -> IO Program+parseProgram phi PHI = parseProgramThrows phi+parseProgram xmir XMIR = do+ doc <- parseXMIRThrows xmir+ xmirToPhi doc++printRewrittens :: PrintProgramContext -> [Rewritten] -> IO String+printRewrittens ctx@PrintProgCtx {..} rewrittens+ | outputFormat == LATEX = pure (rewrittensToLatex rewrittens (LatexContext sugar line nonumber expression label))+ | otherwise = mapM (printProgram ctx . fst) rewrittens <&> intercalate "\n"++-- Convert program to corresponding String format+printProgram :: PrintProgramContext -> Program -> IO String+printProgram PrintProgCtx {..} prog = case outputFormat of+ PHI -> pure (P.printProgram' prog (sugar, UNICODE, line))+ XMIR -> programToXMIR prog xmirCtx <&> printXMIR+ LATEX -> pure (programToLaTeX prog (LatexContext sugar line nonumber expression label))++-- Get rules for rewriting depending on provided flags+getRules :: Bool -> Bool -> [FilePath] -> IO [Y.Rule]+getRules normalize shuffle rules = do+ ordered <-+ if normalize+ then do+ let rules' = normalizationRules+ logDebug (printf "The --normalize option is provided, %d built-it normalization rules are used" (length rules'))+ pure rules'+ else+ if null rules then do- let rules' = normalizationRules- logDebug (printf "The --normalize option is provided, %d built-it normalization rules are used" (length rules'))- pure rules'- else- if null rules- then do- logDebug "No --rule and no --normalize options are provided, no rules are used"- pure []- else do- logDebug (printf "Using rules from files: [%s]" (intercalate ", " rules))- yamls <- mapM ensuredFile rules- mapM Y.yamlRule yamls- if shuffle- then do- logDebug "The --shuffle option is provided, rules are used in random order"- Misc.shuffle ordered- else pure ordered- output :: Maybe FilePath -> String -> IO ()- output target content = case target of- Nothing -> do- logDebug "The option '--target' is not specified, printing to console..."- putStrLn content- Just file -> do- logDebug (printf "The option '--target' is specified, printing to '%s'..." file)- writeFile file content- logInfo (printf "The command result was saved in '%s'" file)- ioToExtension :: IOFormat -> String- ioToExtension LATEX = "tex"- ioToExtension format = show format+ logDebug "No --rule and no --normalize options are provided, no rules are used"+ pure []+ else do+ logDebug (printf "Using rules from files: [%s]" (intercalate ", " rules))+ yamls <- mapM ensuredFile rules+ mapM Y.yamlRule yamls+ if shuffle+ then do+ logDebug "The --shuffle option is provided, rules are used in random order"+ Misc.shuffle ordered+ else pure ordered++-- Output content+output :: Maybe FilePath -> String -> IO ()+output target content = case target of+ Nothing -> do+ logDebug "The option '--target' is not specified, printing to console..."+ putStrLn content+ Just file -> do+ logDebug (printf "The option '--target' is specified, printing to '%s'..." file)+ writeFile file content+ logInfo (printf "The command result was saved in '%s'" file)
src/CST.hs view
@@ -2,8 +2,6 @@ {-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE InstanceSigs #-} {-# LANGUAGE MultiParamTypeClasses #-}-{-# LANGUAGE NamedFieldPuns #-}-{-# LANGUAGE RecordWildCards #-} -- SPDX-FileCopyrightText: Copyright (c) 2025 Objectionary.com -- SPDX-License-Identifier: MIT@@ -12,6 +10,7 @@ import AST import Data.List (intercalate)+import Data.Maybe (isJust) import Misc data LCB = LCB | BIG_LCB@@ -104,6 +103,7 @@ data PAIR = PA_TAU {attr :: ATTRIBUTE, arrow :: ARROW, expr :: EXPRESSION}+ | PA_FORMATION {attr :: ATTRIBUTE, voids :: [ATTRIBUTE], arrow :: ARROW, expr :: EXPRESSION} | PA_VOID {attr :: ATTRIBUTE, arrow :: ARROW, void :: VOID} | PA_LAMBDA {func :: String} | PA_LAMBDA' {func :: String} -- ASCII version of PA_LAMBDA@@ -115,6 +115,9 @@ | PA_META_DELTA' {meta :: META} -- ASCII version of PA_META_DELTA deriving (Eq, Show) +newtype APP_BINDING = APP_BINDING {pair :: PAIR}+ deriving (Eq, Show)+ data BINDING = BI_PAIR {pair :: PAIR, bindings :: BINDINGS, tab :: TAB} | BI_EMPTY {tab :: TAB}@@ -139,16 +142,17 @@ data EXPRESSION = EX_GLOBAL {global :: GLOBAL}- | EX_DEF_PACKAGE {pckg :: DEF_PACKAGE}+ | EX_DEF_PACKAGE {pckg :: DEF_PACKAGE} -- sugar for Q.org.eolang | EX_XI {xi :: XI} | EX_ATTR {attr :: ATTRIBUTE} -- sugar for $.x -> just x | EX_TERMINATION {termination :: TERMINATION} | EX_FORMATION {lsb :: LSB, eol :: EOL, tab :: TAB, binding :: BINDING, eol' :: EOL, tab' :: TAB, rsb :: RSB} | EX_DISPATCH {expr :: EXPRESSION, attr :: ATTRIBUTE}- | EX_APPLICATION {expr :: EXPRESSION, eol :: EOL, tab :: TAB, bindings :: BINDING, eol' :: EOL, tab' :: TAB}- | EX_APPLICATION' {expr :: EXPRESSION, eol :: EOL, tab :: TAB, args :: APP_ARG, eol' :: EOL, tab' :: TAB}- | EX_STRING {str :: String, tab :: TAB}- | EX_NUMBER {num :: Either Integer Double, tab :: TAB}+ | EX_APPLICATION {expr :: EXPRESSION, eol :: EOL, tab :: TAB, tau :: APP_BINDING, eol' :: EOL, tab' :: TAB, indent :: Integer} -- e(a1 -> e1)+ | EX_APPLICATION_TAUS {expr :: EXPRESSION, eol :: EOL, tab :: TAB, taus :: BINDING, eol' :: EOL, tab' :: TAB, indent :: Integer} -- e(a1 -> e1)(a2 -> e2)(...)+ | EX_APPLICATION_EXPRS {expr :: EXPRESSION, eol :: EOL, tab :: TAB, args :: APP_ARG, eol' :: EOL, tab' :: TAB, indent :: Integer} -- e(e1, e2, ...)+ | EX_STRING {str :: String, tab :: TAB, rhos :: [Binding]}+ | EX_NUMBER {num :: Either Integer Double, tab :: TAB, rhos :: [Binding]} | EX_META {meta :: META} | EX_META_TAIL {expr :: EXPRESSION, meta :: META} deriving (Eq, Show)@@ -164,31 +168,31 @@ deriving (Eq, Show) programToCST :: Program -> PROGRAM-programToCST prog = toCST prog 0+programToCST prog = toCST prog 0 EOL expressionToCST :: Expression -> EXPRESSION-expressionToCST expr = toCST expr 0+expressionToCST expr = toCST expr 0 EOL -- This class is used to convert AST to CST -- CST is created with sugar and unicode -- All further transformations much consider that class ToCST a b where- toCST :: a -> Integer -> b+ toCST :: a -> Integer -> EOL -> b instance ToCST Program PROGRAM where- toCST (Program expr) tabs = PR_SWEET LCB (toCST expr tabs) RCB+ toCST (Program expr) tabs eol = PR_SWEET LCB (toCST expr tabs eol) RCB instance ToCST Expression EXPRESSION where- toCST ExGlobal _ = EX_GLOBAL Φ- toCST ExThis _ = EX_XI XI- toCST (ExMeta mt) _ = EX_META (MT_EXPRESSION (tail mt))- toCST (ExMetaTail expr mt) tabs = EX_META_TAIL (toCST expr tabs) (MT_TAIL (tail mt))- toCST ExTermination _ = EX_TERMINATION DEAD- toCST (ExFormation [BiVoid AtRho]) _ = toCST (ExFormation []) 0- toCST (ExFormation []) _ = EX_FORMATION LSB NO_EOL NO_TAB (BI_EMPTY NO_TAB) NO_EOL NO_TAB RSB- toCST (ExFormation bds) tabs =+ toCST ExGlobal _ _ = EX_GLOBAL Φ+ toCST ExThis _ _ = EX_XI XI+ toCST (ExMeta mt) _ _ = EX_META (MT_EXPRESSION (tail mt))+ toCST (ExMetaTail expr mt) tabs eol = EX_META_TAIL (toCST expr tabs eol) (MT_TAIL (tail mt))+ toCST ExTermination _ _ = EX_TERMINATION DEAD+ toCST (ExFormation [BiVoid AtRho]) _ eol = toCST (ExFormation []) 0 eol+ toCST (ExFormation []) _ _ = EX_FORMATION LSB NO_EOL NO_TAB (BI_EMPTY NO_TAB) NO_EOL NO_TAB RSB+ toCST (ExFormation bds) tabs eol = let next = tabs + 1- bds' = toCST (withoutLastVoidRho bds) next :: BINDING+ bds' = toCST (withoutLastVoidRho bds) next eol :: BINDING in EX_FORMATION LSB EOL@@ -203,33 +207,70 @@ withoutLastVoidRho [BiVoid AtRho] = [] withoutLastVoidRho (bd : [BiVoid AtRho]) = [bd] withoutLastVoidRho (bd : bds') = bd : withoutLastVoidRho bds'- toCST (DataString bts) tabs = EX_STRING (btsToStr bts) (TAB tabs)- toCST (DataNumber bts) tabs = EX_NUMBER (btsToNum bts) (TAB tabs)- toCST (ExDispatch (ExDispatch ExGlobal (AtLabel "org")) (AtLabel "eolang")) _ = EX_DEF_PACKAGE Φ̇- toCST (ExDispatch ExThis attr) tabs = EX_ATTR (toCST attr tabs)- toCST (ExDispatch expr attr) tabs = EX_DISPATCH (toCST expr tabs) (toCST attr tabs)- toCST app@(ExApplication _ _) tabs =+ toCST (DataString bts) tabs _ = EX_STRING (btsToStr bts) (TAB tabs) []+ toCST (DataNumber bts) tabs _ = EX_NUMBER (btsToNum bts) (TAB tabs) []+ toCST (ExDispatch (ExDispatch ExGlobal (AtLabel "org")) (AtLabel "eolang")) _ _ = EX_DEF_PACKAGE Φ̇+ toCST (ExDispatch ExThis attr) tabs eol = EX_ATTR (toCST attr tabs eol)+ toCST (ExDispatch expr attr) tabs eol = EX_DISPATCH (toCST expr tabs eol) (toCST attr tabs eol)+ -- Since we convert AST to CST in sweet notation, here we're trying to get rid of unnecessary rho bindings+ -- in primitives (more details here: https://github.com/objectionary/phino/issues/451)+ -- If we find something similar to:+ -- `QQ.number(~0 -> QQ.bytes(...), ^ -> ..., ^ -> ...)`+ -- We remove unnecessary rho bindings and save them to EX_STRING or EX_NUMBER so they can be successfully+ -- converted to salty notation without losing information.+ -- In the end we just get CST with data primitive which is printed correctly.+ -- If given application is not such primitive - we just convert it to one of the applications:+ -- 1. either with pure expression with arguments, which means there are incremented only alpha bindings+ -- 2. or with just bindings+ toCST app@(ExApplication exp tau) tabs eol = let (expr, taus, exprs) = complexApplication app+ expr' = toCST expr tabs eol :: EXPRESSION next = tabs + 1- expr'' = toCST expr tabs :: EXPRESSION- in if null exprs- then- EX_APPLICATION- (toCST expr tabs)- EOL- (TAB next)- (toCST (reverse taus) next)- EOL- (TAB tabs)+ (taus', rhos) = withoutRhosInPrimitives expr taus+ obj = ExApplication expr (head taus')+ in if length taus' == 1 && isJust (matchDataObject obj)+ then applicationToPrimitive obj tabs rhos else- EX_APPLICATION'- (toCST expr tabs)- EOL- (TAB next)- (toCST (reverse exprs) next)- EOL- (TAB tabs)+ if null exprs+ then+ let eol' = inlinedEOL (not (hasEOL taus))+ in EX_APPLICATION_TAUS+ expr'+ eol'+ (tabOfEOL eol' next)+ (toCST taus next eol' :: BINDING)+ eol'+ (tabOfEOL eol' tabs)+ next+ else+ let eol' = inlinedEOL (not (hasEOL exprs))+ in EX_APPLICATION_EXPRS+ expr'+ eol'+ (tabOfEOL eol' next)+ (toCST exprs next eol')+ eol'+ (tabOfEOL eol' tabs)+ next where+ primitives :: [String]+ primitives = ["number", "string"]+ withoutRhosInPrimitives :: Expression -> [Binding] -> ([Binding], [Binding])+ withoutRhosInPrimitives _ [] = ([], [])+ withoutRhosInPrimitives obj@(BaseObject label) bds@(rho@(BiTau AtRho _) : rest)+ | label `elem` primitives =+ let (bds', rhos) = withoutRhosInPrimitives obj rest+ in (bds', rho : rhos)+ | otherwise = (bds, [])+ withoutRhosInPrimitives obj@(BaseObject label) bds@(bd : rest)+ | label `elem` primitives =+ let (bds', rhos) = withoutRhosInPrimitives obj rest+ in (bd : bds', rhos)+ | otherwise = (bds, [])+ withoutRhosInPrimitives _ bds = (bds, [])+ applicationToPrimitive :: Expression -> Integer -> [Binding] -> EXPRESSION+ applicationToPrimitive (DataNumber bts) tabs = EX_NUMBER (btsToNum bts) (TAB tabs)+ applicationToPrimitive (DataString bts) tabs = EX_STRING (btsToStr bts) (TAB tabs) -- Here we unroll nested application sequence into flat structure -- The returned tuple consists of: -- 1. deepest start expression@@ -237,89 +278,114 @@ -- 3. list of expressions which are applied to start expression with default -- alpha attributes (~0 -> e1, ~1 -> e2, ...) complexApplication :: Expression -> (Expression, [Binding], [Expression])- complexApplication (ExApplication (ExApplication expr tau) tau') =- let (before, taus, exprs) = complexApplication (ExApplication expr tau)- taus' = tau' : taus- in if null exprs- then (before, taus', [])- else case tau' of- BiTau (AtAlpha idx) expr' ->- if idx == fromIntegral (length exprs)- then (before, taus', expr' : exprs)- else (before, taus', [])- _ -> (before, taus', [])- complexApplication (ExApplication expr (BiTau (AtAlpha 0) expr')) = (expr, [BiTau (AtAlpha 0) expr'], [expr'])- complexApplication (ExApplication expr tau) = (expr, [tau], [])+ complexApplication expr =+ let (expr', taus', exprs') = complexApplication' expr+ in (expr', reverse taus', reverse exprs')+ where+ complexApplication' :: Expression -> (Expression, [Binding], [Expression])+ complexApplication' (ExApplication (ExApplication expr tau) tau') =+ let (before, taus, exprs) = complexApplication' (ExApplication expr tau)+ taus' = tau' : taus+ in if null exprs+ then (before, taus', [])+ else case tau' of+ BiTau (AtAlpha idx) expr' ->+ if idx == fromIntegral (length exprs)+ then (before, taus', expr' : exprs)+ else (before, taus', [])+ _ -> (before, taus', [])+ complexApplication' (ExApplication expr (BiTau (AtAlpha 0) expr')) = (expr, [BiTau (AtAlpha 0) expr'], [expr'])+ complexApplication' (ExApplication expr tau) = (expr, [tau], []) instance ToCST [Expression] APP_ARG where- toCST (expr : exprs) tabs = APP_ARG (toCST expr tabs) (toCST exprs tabs)+ toCST (expr : exprs) tabs eol = APP_ARG (toCST expr tabs eol) (toCST exprs tabs eol) instance ToCST [Expression] APP_ARGS where- toCST [] _ = AAS_EMPTY- toCST (expr : exprs) tabs = AAS_EXPR EOL (TAB tabs) (toCST expr tabs) (toCST exprs tabs)+ toCST [] _ _ = AAS_EMPTY+ toCST (expr : exprs) tabs eol = AAS_EXPR eol (tabOfEOL eol tabs) (toCST expr tabs eol) (toCST exprs tabs eol) instance ToCST [Binding] BINDING where- toCST [] tabs = BI_EMPTY (TAB tabs)- toCST (BiMeta mt : bds) tabs = BI_META (MT_BINDING (tail mt)) (toCST bds tabs) (TAB tabs)- toCST (bd : bds) tabs = BI_PAIR (toCST bd tabs) (toCST bds tabs) (TAB tabs)+ toCST [] tabs _ = BI_EMPTY (TAB tabs)+ toCST (BiMeta mt : bds) tabs eol = BI_META (MT_BINDING (tail mt)) (toCST bds tabs eol) (tabOfEOL eol tabs)+ toCST (bd : bds) tabs eol = BI_PAIR (toCST bd tabs eol) (toCST bds tabs eol) (tabOfEOL eol tabs) instance ToCST [Binding] BINDINGS where- toCST [] tabs = BDS_EMPTY (TAB tabs)- toCST (BiMeta mt : bds) tabs = BDS_META EOL (TAB tabs) (MT_BINDING (tail mt)) (toCST bds tabs)- toCST (bd : bds) tabs = BDS_PAIR EOL (TAB tabs) (toCST bd tabs) (toCST bds tabs)+ toCST [] tabs _ = BDS_EMPTY (TAB tabs)+ toCST (BiMeta mt : bds) tabs eol = BDS_META eol (tabOfEOL eol tabs) (MT_BINDING (tail mt)) (toCST bds tabs eol)+ toCST (bd : bds) tabs eol = BDS_PAIR eol (tabOfEOL eol tabs) (toCST bd tabs eol) (toCST bds tabs eol) instance ToCST Binding PAIR where- toCST (BiTau attr exp) tabs = PA_TAU (toCST attr tabs) ARROW (toCST exp tabs)- toCST (BiVoid attr) tabs = PA_VOID (toCST attr tabs) ARROW EMPTY- toCST (BiDelta bts) tabs = PA_DELTA (toCST bts tabs)- toCST (BiLambda func) _ = PA_LAMBDA func- toCST (BiMetaLambda mt) _ = PA_META_LAMBDA (MT_FUNCTION (tail mt))+ toCST (BiTau attr exp@(ExFormation bds)) tabs eol =+ let voids' = voids bds+ attr' = toCST attr tabs eol+ in if null voids'+ then PA_TAU attr' ARROW (toCST exp tabs eol)+ else+ let (_voids, _bds) = if length voids' == length bds && last voids' == AtRho then (init voids', []) else (voids', drop (length voids') bds)+ in PA_FORMATION+ attr'+ (map (\at -> toCST at tabs eol) _voids)+ ARROW+ (toCST (ExFormation _bds) tabs eol)+ where+ voids :: [Binding] -> [Attribute]+ voids [] = []+ voids (bd : bds) = case bd of+ BiVoid attr -> attr : voids bds+ _ -> []+ toCST (BiTau attr exp) tabs eol = PA_TAU (toCST attr tabs eol) ARROW (toCST exp tabs eol)+ toCST (BiVoid attr) tabs eol = PA_VOID (toCST attr tabs eol) ARROW EMPTY+ toCST (BiDelta bts) tabs eol = PA_DELTA (toCST bts tabs eol)+ toCST (BiLambda func) _ _ = PA_LAMBDA func+ toCST (BiMetaLambda mt) _ _ = PA_META_LAMBDA (MT_FUNCTION (tail mt)) +instance ToCST Binding APP_BINDING where+ toCST bd@(BiTau _ _) tabs eol = APP_BINDING (toCST bd tabs eol :: PAIR)+ instance ToCST Bytes BYTES where- toCST BtEmpty _ = BT_EMPTY- toCST (BtOne byte) _ = BT_ONE byte- toCST (BtMany bts) _ = BT_MANY bts- toCST (BtMeta mt) _ = BT_META (MT_BYTES (tail mt))+ toCST BtEmpty _ _ = BT_EMPTY+ toCST (BtOne byte) _ _ = BT_ONE byte+ toCST (BtMany bts) _ _ = BT_MANY bts+ toCST (BtMeta mt) _ _ = BT_META (MT_BYTES (tail mt)) instance ToCST Attribute ATTRIBUTE where- toCST (AtLabel label) _ = AT_LABEL label- toCST (AtAlpha idx) _ = AT_ALPHA ALPHA idx- toCST AtPhi _ = AT_PHI PHI- toCST AtRho _ = AT_RHO RHO- toCST AtDelta _ = AT_DELTA DELTA- toCST AtLambda _ = AT_LAMBDA LAMBDA- toCST (AtMeta mt) _ = AT_META (MT_ATTRIBUTE (tail mt))+ toCST (AtLabel label) _ _ = AT_LABEL label+ toCST (AtAlpha idx) _ _ = AT_ALPHA ALPHA idx+ toCST AtPhi _ _ = AT_PHI PHI+ toCST AtRho _ _ = AT_RHO RHO+ toCST AtDelta _ _ = AT_DELTA DELTA+ toCST AtLambda _ _ = AT_LAMBDA LAMBDA+ toCST (AtMeta mt) _ _ = AT_META (MT_ATTRIBUTE (tail mt)) -class HasEOL a where- hasEOL :: a -> Bool+inlinedEOL :: Bool -> EOL+inlinedEOL True = NO_EOL+inlinedEOL False = EOL -instance HasEOL EXPRESSION where- hasEOL EX_FORMATION {eol = NO_EOL, binding, eol' = NO_EOL} = hasEOL binding- hasEOL EX_FORMATION {..} = True- hasEOL EX_DISPATCH {..} = hasEOL expr- hasEOL EX_APPLICATION {eol = NO_EOL, expr, bindings, eol' = NO_EOL} = hasEOL expr || hasEOL bindings- hasEOL EX_APPLICATION {..} = True- hasEOL EX_APPLICATION' {eol = NO_EOL, expr, args, eol' = NO_EOL} = hasEOL expr || hasEOL args- hasEOL EX_APPLICATION' {..} = True- hasEOL _ = False+tabOfEOL :: EOL -> Integer -> TAB+tabOfEOL EOL indent = TAB indent+tabOfEOL NO_EOL _ = TAB' -instance HasEOL BINDING where- hasEOL BI_EMPTY {..} = False- hasEOL BI_PAIR {..} = hasEOL pair || hasEOL bindings+class HasEOL a where+ hasEOL :: a -> Bool -instance HasEOL BINDINGS where- hasEOL BDS_PAIR {eol = NO_EOL, pair, bindings} = hasEOL pair || hasEOL bindings- hasEOL BDS_PAIR {..} = True- hasEOL BDS_EMPTY {..} = False+instance HasEOL [Binding] where+ hasEOL [] = False+ hasEOL (bd : rest) = hasEOL bd || hasEOL rest -instance HasEOL PAIR where- hasEOL PA_TAU {..} = hasEOL expr- hasEOL _ = False+instance HasEOL Binding where+ hasEOL (BiTau _ expr) = hasEOL expr+ hasEOL bd = False -instance HasEOL APP_ARG where- hasEOL APP_ARG {..} = hasEOL expr || hasEOL args+instance HasEOL [Expression] where+ hasEOL [] = False+ hasEOL (expr : rest) = hasEOL expr || hasEOL rest -instance HasEOL APP_ARGS where- hasEOL AAS_EMPTY = False- hasEOL AAS_EXPR {eol = NO_EOL, expr, args} = hasEOL expr || hasEOL args- hasEOL AAS_EXPR {..} = True+instance HasEOL Expression where+ hasEOL (ExFormation []) = False+ hasEOL (ExFormation _) = True+ hasEOL (DataNumber _) = False+ hasEOL (DataString _) = False+ hasEOL (BaseObject _) = False+ hasEOL (ExDispatch expr _) = hasEOL expr+ hasEOL (ExApplication expr tau) = hasEOL expr || hasEOL tau+ hasEOL expr = False
+ src/Condition.hs view
@@ -0,0 +1,163 @@+{-# LANGUAGE DeriveAnyClass #-}+{-# LANGUAGE RecordWildCards #-}++-- SPDX-FileCopyrightText: Copyright (c) 2025 Objectionary.com+-- SPDX-License-Identifier: MIT++module Condition (parseCondition, parseConditionThrows) where++import Control.Exception (Exception)+import Control.Exception.Base (throwIO)+import Data.Void (Void)+import Parser (PhiParser (..), phiParser)+import Text.Megaparsec+import Text.Megaparsec.Char+import qualified Text.Megaparsec.Char.Lexer as L+import qualified Yaml as Y+import Text.Printf (printf)++newtype ConditionException = CouldNotParseCondition {message :: String}+ deriving (Exception)++instance Show ConditionException where+ show CouldNotParseCondition {..} = printf "Couldn't parse given condition, cause: %s" message++type Parser = Parsec Void String++-- White space consumer+whiteSpace :: Parser ()+whiteSpace = L.space hspace1 empty empty++-- Lexeme that ignores white spaces after+lexeme :: Parser a -> Parser a+lexeme = L.lexeme whiteSpace++-- Strict symbol (or sequence of symbols) with ignored white spaces after+symbol :: String -> Parser String+symbol = L.symbol whiteSpace++lparen :: Parser String+lparen = symbol "("++rparen :: Parser String+rparen = symbol ")"++comma :: Parser String+comma = symbol ","++number :: Parser Y.Number+number =+ choice+ [ do+ _ <- symbol "ordinal" >> lparen+ attr <- _attribute phiParser+ _ <- rparen+ return (Y.Ordinal attr),+ do+ _ <- symbol "length" >> lparen+ bd <- _binding phiParser+ _ <- rparen+ return (Y.Length bd),+ do+ sign <- optional (choice [char '-', char '+'])+ unsigned <- lexeme L.decimal+ return+ ( Y.Literal+ ( case sign of+ Just '-' -> negate unsigned+ _ -> unsigned+ )+ )+ ]++comparable :: Parser Y.Comparable+comparable =+ choice+ [ try $ Y.CmpNum <$> number,+ try $ Y.CmpAttr <$> _attribute phiParser,+ Y.CmpExpr <$> _expression phiParser+ ]++condition :: Parser Y.Condition+condition =+ choice+ [ do+ _ <- symbol "and" >> lparen+ args <- condition `sepBy1` comma+ _ <- rparen+ return (Y.And args),+ do+ _ <- symbol "or" >> lparen+ args <- condition `sepBy1` comma+ _ <- rparen+ return (Y.Or args),+ do+ _ <- symbol "in" >> lparen+ attr <- _attribute phiParser+ _ <- comma+ bd <- _binding phiParser+ _ <- rparen+ return (Y.In attr bd),+ do+ _ <- symbol "not" >> lparen+ cond <- condition+ _ <- rparen+ return (Y.Not cond),+ do+ _ <- symbol "alpha" >> lparen+ attr <- _attribute phiParser+ _ <- rparen+ return (Y.Alpha attr),+ do+ _ <- symbol "eq" >> lparen+ left <- comparable+ _ <- comma+ right <- comparable+ _ <- rparen+ return (Y.Eq left right),+ do+ _ <- symbol "nf" >> lparen+ expr <- _expression phiParser+ _ <- rparen+ return (Y.NF expr),+ do+ _ <- symbol "xi" >> lparen+ expr <- _expression phiParser+ _ <- rparen+ return (Y.XI expr),+ do+ _ <- symbol "matches" >> lparen+ ptn <- _string phiParser+ _ <- comma+ expr <- _expression phiParser+ _ <- rparen+ return (Y.Matches ptn expr),+ do+ _ <- symbol "part-of" >> lparen+ expr <- _expression phiParser+ _ <- comma+ bd <- _binding phiParser+ _ <- rparen+ return (Y.PartOf expr bd)+ ]++parseCondition :: String -> Either String Y.Condition+parseCondition input = do+ let parsed =+ runParser+ ( do+ _ <- whiteSpace+ p <- condition+ _ <- eof+ return p+ )+ "condition"+ input+ case parsed of+ Right parsed' -> Right parsed'+ Left err -> Left (errorBundlePretty err)++parseConditionThrows :: String -> IO Y.Condition+parseConditionThrows cnd = case parseCondition cnd of+ Right condition -> pure condition+ Left err -> throwIO (CouldNotParseCondition err)
src/Dataize.hs view
@@ -1,3 +1,4 @@+{-# LANGUAGE DuplicateRecordFields #-} {-# LANGUAGE LambdaCase #-} {-# LANGUAGE RecordWildCards #-} @@ -9,16 +10,23 @@ import AST import Builder (contextualize) import Control.Exception (throwIO)+import Data.IntMap (restrictKeys) import Data.List (partition) import Deps (BuildTermFunc, SaveStepFunc) import Misc import Must (Must (..))-import Rewriter (RewriteContext (RewriteContext), Rewritten (Rewritten, program), rewrite')+import Rewriter (RewriteContext (RewriteContext), Rewritten, rewrite') import Rule (RuleContext (RuleContext), isNF) import Text.Printf (printf) import XMIR (XmirContext (XmirContext)) import Yaml (normalizationRules) +type Dataized = (Maybe Bytes, [Rewritten])++type Dataizable = (Expression, [Rewritten])++type Morphed = Dataizable+ data DataizeContext = DataizeContext { _program :: Program, _maxDepth :: Integer,@@ -34,7 +42,6 @@ _maxDepth _maxCycles _depthSensitive- False _buildTerm MtDisabled _saveStep@@ -56,50 +63,58 @@ maybePhi :: [Binding] -> (Maybe Binding, [Binding]) maybePhi = maybeBinding (\case (BiTau AtPhi _) -> True; _ -> False) -formation :: [Binding] -> DataizeContext -> IO (Maybe Expression)+-- Resolve formation for LAMBDA Morphing rule.+-- If formation contains λ binding, the called atom+-- result is returned.+formation :: [Binding] -> DataizeContext -> IO (Maybe (Expression, String)) formation bds ctx = do let (lambda, bds') = maybeLambda bds case lambda of Just (BiLambda func) -> do- obj' <- atom func (ExFormation bds') ctx- case obj' of- Just obj -> pure (Just obj)- _ -> pure Nothing+ obj <- atom func (ExFormation bds') ctx+ pure (Just (obj, "M(lambda)")) _ -> pure Nothing -phiDispatch :: String -> Expression -> Maybe Expression-phiDispatch attr expr = case expr of+-- Resolve dispatch from global object (Q.tau) for PHI Morphing rule.+-- Here tau is the name of the attribute which is taken from Q+-- and expr is expression which program refers to.+-- If Q refers to formation which contains binding with attribute == tau -+-- the expression from this binding is returned.+phiDispatch :: String -> Expression -> Maybe (Expression, String)+phiDispatch tau expr = case expr of ExFormation bds -> boundExpr bds _ -> Nothing where- boundExpr :: [Binding] -> Maybe Expression+ boundExpr :: [Binding] -> Maybe (Expression, String) boundExpr [] = Nothing boundExpr (bd : bds) = case bd of- BiTau (AtLabel attr') expr' -> if attr' == attr then Just expr' else boundExpr bds+ BiTau (AtLabel attr) expr' -> if attr == tau then Just (expr', "M(phi)") else boundExpr bds _ -> boundExpr bds -withTail :: Expression -> DataizeContext -> IO (Maybe Expression)+-- Resolve tail PHI and LAMBDA Morphing rules.+-- Tail MUST start with dispatch, that's why most of the applications return Nothing+withTail :: Expression -> DataizeContext -> IO (Maybe (Expression, String)) withTail (ExApplication (ExFormation _) _) _ = pure Nothing withTail (ExApplication (ExDispatch ExGlobal _) _) _ = pure Nothing withTail (ExApplication expr tau) ctx = do- exp' <- withTail expr ctx- case exp' of- Just exp -> pure (Just (ExApplication exp tau))+ tailed <- withTail expr ctx+ case tailed of+ Just (exp, rule) -> pure (Just (ExApplication exp tau, rule)) _ -> pure Nothing withTail (ExDispatch (ExFormation bds) attr) ctx = do- obj' <- formation bds ctx- case obj' of- Just obj -> pure (Just (ExDispatch obj attr))+ tailed <- formation bds ctx+ case tailed of+ Just (obj, rule) -> pure (Just (ExDispatch obj attr, rule)) _ -> pure Nothing withTail (ExFormation bds) ctx = formation bds ctx withTail (ExDispatch (ExDispatch ExGlobal (AtLabel label)) attr) (DataizeContext {_program = Program expr}) = case phiDispatch label expr of- Just obj -> pure (Just (ExDispatch obj attr))+ Just (obj, rule) -> pure (Just (ExDispatch obj attr, rule)) _ -> pure Nothing withTail (ExDispatch ExGlobal (AtLabel label)) (DataizeContext {_program = Program expr}) = pure (phiDispatch label expr) withTail (ExDispatch expr attr) ctx = do- exp' <- withTail expr ctx- case exp' of- Just exp -> pure (Just (ExDispatch exp attr))+ tailed <- withTail expr ctx+ case tailed of+ Just (exp, rule) -> pure (Just (ExDispatch exp attr, rule)) _ -> pure Nothing withTail _ _ = pure Nothing @@ -114,24 +129,26 @@ -- NMZ: M(e1) -> M(e2) if e2 := N(e1) and e1 != e2 -- LAMBDA: M([B1, λ -> F, B2] * t) -> M(e2 * t) if e3 := [B1,B2] and e2 := F(e3) -- PHI: M(Q.tau * t) -> M(e * t) if Q -> [B1, tau -> e, B2], t is tail started with dispatch--- M(e) -> nothing otherwise-morph :: Expression -> DataizeContext -> IO (Maybe Expression)-morph ExTermination _ = pure (Just ExTermination) -- PRIM-morph (ExFormation bds) ctx = do- resolved <- withTail (ExFormation bds) ctx+-- M(e) -> ⊥ otherwise+morph :: Morphed -> DataizeContext -> IO Morphed+morph (ExTermination, seq) _ = pure (ExTermination, leadsTo seq "M(prim)" ExTermination) -- PRIM+morph (form@(ExFormation bds), seq) ctx = do+ resolved <- withTail form ctx case resolved of- Just obj -> morph obj ctx -- LAMBDA or PHI- _ -> pure (Just (ExFormation bds)) -- PRIM-morph expr ctx = do+ Just (expr, rule) -> morph (expr, leadsTo seq rule expr) ctx -- LAMBDA or PHI+ _ -> pure (form, leadsTo seq "M(prim)" form) -- PRIM+morph (expr, seq) ctx = do resolved <- withTail expr ctx case resolved of- Just obj -> morph obj ctx+ Just (expr', rule) -> morph (expr', leadsTo seq rule expr') ctx _ -> if isNF expr (RuleContext (_buildTerm ctx))- then pure Nothing+ then morph (ExTermination, seq) ctx -- PRIM else do- [Rewritten {program = Program expr'}] <- rewrite' (Program expr) normalizationRules (switchContext ctx) -- NMZ- morph expr' ctx+ rewrittens' <- rewrite' (Program expr) normalizationRules (switchContext ctx) -- NMZ+ let _seq = reverse rewrittens' <> tail seq+ (Program expr') = fst (head _seq)+ morph (expr', _seq) ctx -- The goal of 'dataize' function is retrieve bytes from given expression. --@@ -139,62 +156,61 @@ -- BOX: D([B1, 𝜑 -> e, B2]) -> D(С(e)) if [B1,B2] has no delta/lambda, where С(e) - contextualization -- NORM: D(e1) -> D(e2) if e2 := M(e1) and e1 is not primitive -- nothing otherwise-dataize :: Program -> DataizeContext -> IO (Maybe Bytes)-dataize (Program expr) = dataize' expr+dataize :: Program -> DataizeContext -> IO Dataized+dataize (Program expr) ctx = do+ (maybeBytes, seq) <- dataize' (expr, [(Program expr, Nothing)]) ctx+ pure (maybeBytes, reverse seq) -dataize' :: Expression -> DataizeContext -> IO (Maybe Bytes)-dataize' ExTermination _ = pure Nothing-dataize' (ExFormation bds) ctx = case maybeDelta bds of- (Just (BiDelta bytes), _) -> pure (Just bytes)+dataize' :: Dataizable -> DataizeContext -> IO Dataized+dataize' (ExTermination, seq) _ = pure (Nothing, seq)+dataize' (ExFormation bds, seq) ctx = case maybeDelta bds of+ (Just (BiDelta bytes), _) -> pure (Just bytes, seq) (Nothing, _) -> case maybePhi bds of (Just (BiTau AtPhi expr), bds') -> case maybeLambda bds' of (Just (BiLambda _), _) -> throwIO (userError "The 𝜑 and λ can't be present in formation at the same time") (_, _) -> let expr' = contextualize expr (ExFormation bds)- in dataize' expr' ctx+ in dataize' (expr', leadsTo seq "contextualize" expr') ctx (Nothing, _) -> case maybeLambda bds of- (Just (BiLambda _), _) -> do- morphed' <- morph (ExFormation bds) ctx- case morphed' of- Just morphed -> dataize' morphed ctx- _ -> pure Nothing- (Nothing, _) -> pure Nothing-dataize' expr prog = do- morphed' <- morph expr prog- case morphed' of- Just morphed -> dataize' morphed prog- _ -> pure Nothing+ (Just (BiLambda _), _) -> morph (ExFormation bds, seq) ctx >>= (`dataize'` ctx)+ (Nothing, _) -> pure (Nothing, seq)+dataize' dataizable ctx = morph dataizable ctx >>= (`dataize'` ctx) -atom :: String -> Expression -> DataizeContext -> IO (Maybe Expression)+leadsTo :: [Rewritten] -> String -> Expression -> [Rewritten]+leadsTo seq rule expr =+ let (prog, _) : rest = seq+ in (Program expr, Nothing) : (prog, Just rule) : rest++atom :: String -> Expression -> DataizeContext -> IO Expression atom "L_org_eolang_number_plus" self ctx = do- left <- dataize' (ExDispatch self (AtLabel "x")) ctx- right <- dataize' (ExDispatch self AtRho) ctx+ (left, _) <- dataize (Program (ExDispatch self (AtLabel "x"))) ctx+ (right, _) <- dataize (Program (ExDispatch self AtRho)) ctx case (left, right) of (Just left', Just right') -> do let first = either toDouble id (btsToNum left') second = either toDouble id (btsToNum right') sum = first + second- pure (Just (DataNumber (numToBts sum)))- _ -> pure Nothing+ pure (DataNumber (numToBts sum))+ _ -> pure ExTermination atom "L_org_eolang_number_times" self ctx = do- left <- dataize' (ExDispatch self (AtLabel "x")) ctx- right <- dataize' (ExDispatch self AtRho) ctx+ (left, _) <- dataize (Program (ExDispatch self (AtLabel "x"))) ctx+ (right, _) <- dataize (Program (ExDispatch self AtRho)) ctx case (left, right) of (Just left', Just right') -> do let first = either toDouble id (btsToNum left') second = either toDouble id (btsToNum right') sum = first * second- pure (Just (DataNumber (numToBts sum)))- _ -> pure Nothing+ pure (DataNumber (numToBts sum))+ _ -> pure ExTermination atom "L_org_eolang_number_eq" self ctx = do- x <- dataize' (ExDispatch self (AtLabel "x")) ctx- rho <- dataize' (ExDispatch self AtRho) ctx+ (x, _) <- dataize (Program (ExDispatch self (AtLabel "x"))) ctx+ (rho, _) <- dataize (Program (ExDispatch self AtRho)) ctx case (x, rho) of (Just x', Just rho') -> do let self' = either toDouble id (btsToNum rho') first = either toDouble id (btsToNum x') if self' == first- then pure (Just (DataNumber (numToBts first)))- else pure (Just (ExDispatch self (AtLabel "y")))- _ -> pure Nothing+ then pure (DataNumber (numToBts first))+ else pure (ExDispatch self (AtLabel "y"))+ _ -> pure ExTermination atom func _ _ = throwIO (userError (printf "Atom '%s' does not exist" func))
src/Encoding.hs view
@@ -29,12 +29,16 @@ toASCII EX_TERMINATION {..} = EX_TERMINATION T toASCII EX_FORMATION {..} = EX_FORMATION LSB' eol tab (toASCII binding) eol' tab' RSB' toASCII EX_DISPATCH {..} = EX_DISPATCH (toASCII expr) (toASCII attr)- toASCII EX_APPLICATION {..} = EX_APPLICATION (toASCII expr) eol tab (toASCII bindings) eol' tab'- toASCII EX_APPLICATION' {..} = EX_APPLICATION' (toASCII expr) eol tab (toASCII args) eol' tab'+ toASCII EX_APPLICATION {..} = EX_APPLICATION (toASCII expr) eol tab (toASCII tau) eol' tab' indent+ toASCII EX_APPLICATION_TAUS {..} = EX_APPLICATION_TAUS (toASCII expr) eol tab (toASCII taus) eol' tab' indent+ toASCII EX_APPLICATION_EXPRS {..} = EX_APPLICATION_EXPRS (toASCII expr) eol tab (toASCII args) eol' tab' indent toASCII EX_META {..} = EX_META (MT_EXPRESSION' (rest meta)) toASCII EX_META_TAIL {..} = EX_META_TAIL (toASCII expr) (MT_TAIL (rest meta)) toASCII expr = expr +instance ToASCII APP_BINDING where+ toASCII APP_BINDING {..} = APP_BINDING (toASCII pair)+ instance ToASCII BINDING where toASCII BI_PAIR {..} = BI_PAIR (toASCII pair) (toASCII bindings) tab toASCII BI_META {..} = BI_META (MT_BINDING' (rest meta)) (toASCII bindings) tab@@ -54,6 +58,7 @@ instance ToASCII PAIR where toASCII PA_TAU {..} = PA_TAU (toASCII attr) ARROW' (toASCII expr)+ toASCII PA_FORMATION{..} = PA_FORMATION (toASCII attr) (map toASCII voids) ARROW' (toASCII expr) toASCII PA_VOID {..} = PA_VOID (toASCII attr) ARROW' QUESTION toASCII PA_LAMBDA {..} = PA_LAMBDA' func toASCII PA_DELTA {..} = PA_DELTA' bytes
src/Functions.hs view
@@ -108,7 +108,7 @@ _concat :: BuildTermMethod _concat args subst = do- args' <- traverse (\arg -> argToString arg subst) args+ args' <- traverse (`argToString` subst) args pure (TeExpression (DataString (strToBts (concat args')))) _sed :: BuildTermMethod
src/Hide.hs view
@@ -1,5 +1,3 @@-{-# LANGUAGE RecordWildCards #-}- -- SPDX-FileCopyrightText: Copyright (c) 2025 Objectionary.com -- SPDX-License-Identifier: MIT @@ -35,10 +33,10 @@ hiddenBindings (bd@(BiTau attr form@(ExFormation _)) : bds) attrs@(attr' : rest) | attr == attr' = BiTau attr (hiddenFormation form rest) : bds | otherwise = bd : hiddenBindings bds attrs- hiddenBindings bds _ = bds+ hiddenBindings (bd : bds) attrs = bd : hiddenBindings bds attrs hiddenFormation expr attr = expr hide' prog (fqn : rest) = prog hide :: [Rewritten] -> [Expression] -> [Rewritten] hide [] _ = []-hide (Rewritten {..} : rest) exprs = Rewritten (hide' program exprs) maybeRule : hide rest exprs+hide ((program, maybeRule) : rest) exprs = (hide' program exprs, maybeRule) : hide rest exprs
src/LaTeX.hs view
@@ -40,28 +40,18 @@ let equation = phiquation ctx in concat [ printf "\\begin{%s}\n" equation,- case label of- Just lbl -> printf "\\label{%s}\n" lbl- _ -> "",- case expression of- Just expr -> printf "\\phiExpression{%s} " expr- _ -> "",+ maybe "" (printf "\\label{%s}\n") label,+ maybe "" (printf "\\phiExpression{%s} ") expression, intercalate "\n \\leadsto " ( map- ( \Rewritten {..} ->+ ( \(program, maybeName) -> let prog = renderToLatex program ctx- unknown = "unknown"- maybeTo = case maybeRule of- Just (Y.Rule {..}) -> Just (map toLower (fromMaybe unknown name))- _ -> Nothing- in case maybeTo of- Just name -> printf "%s \\leadsto_{\\nameref{r:%s}}" prog name- _ -> prog+ in maybe prog (printf "%s \\leadsto_{\\nameref{r:%s}}" prog) maybeName ) rewrittens ),- printf "\n\\end{%s}" equation+ printf ".\n\\end{%s}" equation ] programToLaTeX :: Program -> LatexContext -> String@@ -90,8 +80,9 @@ instance ToLaTeX EXPRESSION where toLaTeX EX_ATTR {..} = EX_ATTR (toLaTeX attr) toLaTeX EX_FORMATION {..} = EX_FORMATION lsb eol tab (toLaTeX binding) eol' tab' rsb- toLaTeX EX_APPLICATION {..} = EX_APPLICATION (toLaTeX expr) eol tab (toLaTeX bindings) eol' tab'- toLaTeX EX_APPLICATION' {..} = EX_APPLICATION' (toLaTeX expr) eol tab (toLaTeX args) eol' tab'+ toLaTeX EX_APPLICATION {..} = EX_APPLICATION (toLaTeX expr) eol tab (toLaTeX tau) eol' tab' indent+ toLaTeX EX_APPLICATION_TAUS {..} = EX_APPLICATION_TAUS (toLaTeX expr) eol tab (toLaTeX taus) eol' tab' indent+ toLaTeX EX_APPLICATION_EXPRS {..} = EX_APPLICATION_EXPRS (toLaTeX expr) eol tab (toLaTeX args) eol' tab' indent toLaTeX EX_DISPATCH {..} = EX_DISPATCH (toLaTeX expr) (toLaTeX attr) toLaTeX expr = expr @@ -99,6 +90,9 @@ toLaTeX AT_LABEL {..} = AT_LABEL (piped (toLaTeX label)) toLaTeX attr = attr +instance ToLaTeX APP_BINDING where+ toLaTeX APP_BINDING {..} = APP_BINDING (toLaTeX pair)+ instance ToLaTeX BINDING where toLaTeX BI_PAIR {..} = BI_PAIR (toLaTeX pair) (toLaTeX bindings) tab toLaTeX bd = bd@@ -113,6 +107,7 @@ toLaTeX PA_LAMBDA' {..} = PA_LAMBDA' (piped (toLaTeX func)) toLaTeX PA_VOID {..} = PA_VOID (toLaTeX attr) arrow void toLaTeX PA_TAU {..} = PA_TAU (toLaTeX attr) arrow (toLaTeX expr)+ toLaTeX PA_FORMATION {..} = PA_FORMATION (toLaTeX attr) (map toLaTeX voids) arrow (toLaTeX expr) toLaTeX pair = pair instance ToLaTeX APP_ARG where
src/Lining.hs view
@@ -26,10 +26,14 @@ toSingleLine EX_FORMATION {lsb, binding = bd@BI_EMPTY {..}, rsb} = EX_FORMATION lsb NO_EOL NO_TAB bd NO_EOL NO_TAB rsb toSingleLine EX_FORMATION {..} = EX_FORMATION lsb NO_EOL TAB' (toSingleLine binding) NO_EOL TAB' rsb toSingleLine EX_DISPATCH {..} = EX_DISPATCH (toSingleLine expr) attr- toSingleLine EX_APPLICATION {..} = EX_APPLICATION (toSingleLine expr) NO_EOL TAB' (toSingleLine bindings) NO_EOL TAB'- toSingleLine EX_APPLICATION' {..} = EX_APPLICATION' (toSingleLine expr) NO_EOL TAB' (toSingleLine args) NO_EOL TAB'+ toSingleLine EX_APPLICATION {..} = EX_APPLICATION (toSingleLine expr) NO_EOL TAB' (toSingleLine tau) NO_EOL TAB' indent+ toSingleLine EX_APPLICATION_TAUS {..} = EX_APPLICATION_TAUS (toSingleLine expr) NO_EOL TAB' (toSingleLine taus) NO_EOL TAB' indent+ toSingleLine EX_APPLICATION_EXPRS {..} = EX_APPLICATION_EXPRS (toSingleLine expr) NO_EOL TAB' (toSingleLine args) NO_EOL TAB' indent toSingleLine expr = expr +instance ToSingleLine APP_BINDING where+ toSingleLine APP_BINDING {..} = APP_BINDING (toSingleLine pair)+ instance ToSingleLine BINDING where toSingleLine BI_PAIR {..} = BI_PAIR (toSingleLine pair) (toSingleLine bindings) TAB' toSingleLine BI_META {..} = BI_META meta (toSingleLine bindings) TAB'@@ -42,6 +46,7 @@ instance ToSingleLine PAIR where toSingleLine PA_TAU {..} = PA_TAU attr arrow (toSingleLine expr)+ toSingleLine PA_FORMATION {..} = PA_FORMATION attr voids arrow (toSingleLine expr) toSingleLine pair = pair instance ToSingleLine APP_ARG where
src/Logger.hs view
@@ -1,3 +1,5 @@+{-# LANGUAGE RecordWildCards #-}+ -- SPDX-FileCopyrightText: Copyright (c) 2025 Objectionary.com -- SPDX-License-Identifier: MIT @@ -6,32 +8,42 @@ logInfo, logWarning, logError,- setLogLevel,+ setLogConfig, LogLevel (DEBUG, INFO, WARNING, ERROR, NONE), ) where import Control.Monad (when) import Data.IORef (IORef, newIORef, readIORef, writeIORef)+import qualified Data.List as DL import GHC.IO (unsafePerformIO) import System.IO data LogLevel = DEBUG | INFO | WARNING | ERROR | NONE deriving (Show, Ord, Eq, Bounded, Enum, Read) -newtype Logger = Logger {level :: LogLevel}+data Logger = Logger {level :: LogLevel, lines :: Int} logger :: IORef Logger {-# NOINLINE logger #-}-logger = unsafePerformIO (newIORef (Logger INFO))+logger = unsafePerformIO (newIORef (Logger INFO 25)) -setLogLevel :: LogLevel -> IO ()-setLogLevel lvl = writeIORef logger (Logger lvl)+setLogConfig :: LogLevel -> Integer -> IO ()+setLogConfig lvl lines = writeIORef logger (Logger lvl (fromIntegral lines)) logMessage :: LogLevel -> String -> IO () logMessage lvl message = do- log <- readIORef logger- when (lvl >= level log) $ hPutStrLn stderr ("[" ++ show lvl ++ "]: " ++ message)+ Logger {..} <- readIORef logger+ when+ (lvl >= level && lines /= 0)+ ( let lines' = DL.lines message+ toPrint = take lines lines'+ msg+ | lines == -1 = [message]+ | length lines' > lines = toPrint ++ ["---| log is limited by --log-lines=" ++ show lines ++ " option |---"]+ | otherwise = toPrint+ in hPutStrLn stderr ("[" ++ show lvl ++ "]: " ++ DL.intercalate "\n" msg)+ ) logDebug, logInfo, logWarning, logError :: String -> IO () logDebug = logMessage DEBUG
src/Misc.hs view
@@ -26,9 +26,11 @@ uniqueBindings, uniqueBindings', validateYamlObject,+ matchDataObject, pattern DataObject, pattern DataString, pattern DataNumber,+ pattern BaseObject, ) where @@ -69,15 +71,24 @@ show FileDoesNotExist {..} = printf "File '%s' does not exist" file show DirectoryDoesNotExist {..} = printf "Directory '%s' does not exist" dir +matchBaseObject :: Expression -> Maybe String+matchBaseObject (ExDispatch (ExDispatch (ExDispatch ExGlobal (AtLabel "org")) (AtLabel "eolang")) (AtLabel label)) = Just label+matchBaseObject _ = Nothing++pattern BaseObject :: String -> Expression+pattern BaseObject label <- (matchBaseObject -> Just label)+ where+ BaseObject label = ExDispatch (ExDispatch (ExDispatch ExGlobal (AtLabel "org")) (AtLabel "eolang")) (AtLabel label)+ -- Minimal matcher function (required for view pattern) matchDataObject :: Expression -> Maybe (String, Bytes) matchDataObject ( ExApplication- (ExDispatch (ExDispatch (ExDispatch ExGlobal (AtLabel "org")) (AtLabel "eolang")) (AtLabel label))+ (BaseObject label) ( BiTau (AtAlpha 0) ( ExApplication- (ExDispatch (ExDispatch (ExDispatch ExGlobal (AtLabel "org")) (AtLabel "eolang")) (AtLabel "bytes"))+ (BaseObject "bytes") ( BiTau (AtAlpha 0) (ExFormation [BiDelta bts, BiVoid AtRho])@@ -98,11 +109,11 @@ where DataObject label bts = ExApplication- (ExDispatch (ExDispatch (ExDispatch ExGlobal (AtLabel "org")) (AtLabel "eolang")) (AtLabel label))+ (BaseObject label) ( BiTau (AtAlpha 0) ( ExApplication- (ExDispatch (ExDispatch (ExDispatch ExGlobal (AtLabel "org")) (AtLabel "eolang")) (AtLabel "bytes"))+ (BaseObject "bytes") ( BiTau (AtAlpha 0) (ExFormation [BiDelta bts, BiVoid AtRho])
src/Must.hs view
@@ -1,10 +1,11 @@ -- SPDX-FileCopyrightText: Copyright (c) 2025 Objectionary.com -- SPDX-License-Identifier: MIT -module Must (Must (..), inRange, exceedsUpperBound) where+module Must (Must (..), inRange, exceedsUpperBound, validateMust) where import Data.List (isInfixOf) import Text.Read (readMaybe)+import Text.Printf (printf) data Must = MtDisabled@@ -63,3 +64,15 @@ exceedsUpperBound (MtExact n) current = current > n exceedsUpperBound (MtRange _ (Just max)) current = current > max exceedsUpperBound (MtRange _ Nothing) _ = False++validateMust :: Must -> Maybe String+validateMust MtDisabled = Nothing+validateMust (MtExact n) | n <= 0 = Just "--must exact value must be positive"+validateMust (MtRange (Just minVal) _) | minVal < 0 = Just "--must minimum must be non-negative"+validateMust (MtRange _ (Just maxVal)) | maxVal < 0 = Just "--must maximum must be non-negative"+validateMust (MtRange Nothing _) = Nothing+validateMust (MtRange _ Nothing) = Nothing+validateMust (MtRange (Just min) (Just max))+ | min > max = Just (printf "--must range invalid: minimum (%d) is greater than maximum (%d)" min max)+ | otherwise = Nothing+validateMust _ = Nothing
src/Parser.hs view
@@ -16,6 +16,8 @@ parseNumberThrows, parseBinding, parseBytes,+ PhiParser (..),+ phiParser, ) where @@ -42,6 +44,16 @@ | CouldNotParseNumber {message :: String} deriving (Exception) +data PhiParser = PhiParser+ { _attribute :: Parser Attribute,+ _binding :: Parser Binding,+ _expression :: Parser Expression,+ _string :: Parser String+ }++phiParser :: PhiParser+phiParser = PhiParser fullAttribute binding expression quotedStr+ instance Show ParserException where show CouldNotParseProgram {..} = printf "Couldn't parse given phi program, cause: %s" message show CouldNotParseExpression {..} = printf "Couldn't parse given phi expression, cause: %s" message@@ -66,55 +78,6 @@ rest <- many (satisfy (`notElem` " \r\n\t,.|':;!?][}{)(⟧⟦") <?> "allowed character") return (first : rest) -escapedChar :: Parser Char-escapedChar = do- _ <- char '\\'- c <- oneOf ['\\', '"', 'n', 'r', 't', 'b', 'f', 'u', 'x']- case c of- '\\' -> return '\\'- '"' -> return '"'- 'n' -> return '\n'- 'r' -> return '\r'- 't' -> return '\t'- 'b' -> return '\b'- 'f' -> return '\f'- 'u' -> unicodeEscape- 'x' -> hexEscape- _ -> fail ("Unknown escape: \\" ++ [c])- where- unicodeEscape :: Parser Char- unicodeEscape = do- hexDigits <- count 4 hexDigitChar- case readHex hexDigits of- [(n, "")] ->- if n >= 0xD800 && n <= 0xDBFF- then -- High surrogate, look for low surrogate- do- _ <- string "\\u"- lowHexDigits <- count 4 hexDigitChar- case readHex lowHexDigits of- [(low, "")] ->- if low >= 0xDC00 && low <= 0xDFFF- then do- -- Valid surrogate pair, combine them- let codePoint = 0x10000 + ((n - 0xD800) * 0x400) + (low - 0xDC00)- return (chr codePoint)- else fail ("Invalid low surrogate: \\u" ++ lowHexDigits)- _ -> fail ("Invalid low surrogate hex: \\u" ++ lowHexDigits)- else- if n >= 0xDC00 && n <= 0xDFFF- then fail ("Unexpected low surrogate: \\u" ++ hexDigits)- else- if n >= 0 && n <= 0x10FFFF- then return (chr n)- else fail ("Invalid Unicode code point: \\u" ++ hexDigits)- hexEscape :: Parser Char- hexEscape = do- digits <- count 2 hexDigitChar- case readHex digits of- [(n, "")] -> return (chr n)- _ -> fail ("Invalid hex escape: \\x" ++ digits)- function :: Parser String function = lexeme $ do first <- oneOf ['A' .. 'Z']@@ -220,6 +183,57 @@ ) ) +quotedStr :: Parser String+quotedStr = char '"' >> manyTill (choice [escapedChar, noneOf ['\\', '"']]) (char '"')+ where+ escapedChar :: Parser Char+ escapedChar = do+ _ <- char '\\'+ c <- oneOf ['\\', '"', 'n', 'r', 't', 'b', 'f', 'u', 'x']+ case c of+ '\\' -> return '\\'+ '"' -> return '"'+ 'n' -> return '\n'+ 'r' -> return '\r'+ 't' -> return '\t'+ 'b' -> return '\b'+ 'f' -> return '\f'+ 'u' -> unicodeEscape+ 'x' -> hexEscape+ _ -> fail ("Unknown escape: \\" ++ [c])+ unicodeEscape :: Parser Char+ unicodeEscape = do+ hexDigits <- count 4 hexDigitChar+ case readHex hexDigits of+ [(n, "")] ->+ if n >= 0xD800 && n <= 0xDBFF+ then -- High surrogate, look for low surrogate+ do+ _ <- string "\\u"+ lowHexDigits <- count 4 hexDigitChar+ case readHex lowHexDigits of+ [(low, "")] ->+ if low >= 0xDC00 && low <= 0xDFFF+ then do+ -- Valid surrogate pair, combine them+ let codePoint = 0x10000 + ((n - 0xD800) * 0x400) + (low - 0xDC00)+ return (chr codePoint)+ else fail ("Invalid low surrogate: \\u" ++ lowHexDigits)+ _ -> fail ("Invalid low surrogate hex: \\u" ++ lowHexDigits)+ else+ if n >= 0xDC00 && n <= 0xDFFF+ then fail ("Unexpected low surrogate: \\u" ++ hexDigits)+ else+ if n >= 0 && n <= 0x10FFFF+ then return (chr n)+ else fail ("Invalid Unicode code point: \\u" ++ hexDigits)+ hexEscape :: Parser Char+ hexEscape = do+ digits <- count 2 hexDigitChar+ case readHex digits of+ [(n, "")] -> return (chr n)+ _ -> fail ("Invalid hex escape: \\x" ++ digits)+ tauBinding :: Parser Attribute -> Parser Binding tauBinding attr = do attr' <- attr@@ -368,10 +382,7 @@ _ <- choice [symbol "T", symbol "⊥"] return ExTermination, number,- lexeme $ do- _ <- char '"'- str <- manyTill (choice [escapedChar, noneOf ['\\', '"']]) (char '"')- return (DataString (strToBts str)),+ lexeme (DataString . strToBts <$> quotedStr), try (ExMeta <$> meta' 'e' "𝑒"), ExDispatch ExThis <$> attribute ]
src/Printer.hs view
@@ -14,6 +14,7 @@ printBytes, printExtraArg, printSubsts,+ printSubsts', PrintConfig (..), logPrintConfig, )@@ -52,7 +53,7 @@ printExpression expr = printExpression' expr defaultPrintConfig printAttribute' :: Attribute -> Encoding -> String-printAttribute' attr encoding = render (withEncoding encoding (toCST attr 0 :: ATTRIBUTE))+printAttribute' attr encoding = render (withEncoding encoding (toCST attr 0 NO_EOL :: ATTRIBUTE)) printAttribute :: Attribute -> String printAttribute attr =@@ -66,7 +67,7 @@ printBinding bd = printBinding' bd defaultPrintConfig printBytes :: Bytes -> String-printBytes bts = render (toCST bts 0 :: BYTES)+printBytes bts = render (toCST bts 0 NO_EOL :: BYTES) printExtraArg' :: ExtraArgument -> PrintConfig -> String printExtraArg' (ArgAttribute attr) (_, encoding, _) = printAttribute' attr encoding@@ -91,13 +92,13 @@ printSubst :: Subst -> PrintConfig -> String printSubst (Subst mp) config =- " "- <> intercalate- "\n "- (map (\(key, value) -> key <> " >> " <> printMetaValue value config) (Map.toList mp))+ intercalate+ "\n"+ (map (\(key, value) -> key <> " >> " <> printMetaValue value config) (Map.toList mp)) printSubsts' :: [Subst] -> PrintConfig -> String-printSubsts' substs config = intercalate "\n---\n" (map (`printSubst` config) substs)+printSubsts' [] _ = "------"+printSubsts' substs config = intercalate "\n------\n" (map (`printSubst` config) substs) printSubsts :: [Subst] -> String printSubsts substs = printSubsts' substs defaultPrintConfig
src/Render.hs view
@@ -1,4 +1,5 @@ {-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE NamedFieldPuns #-} {-# LANGUAGE RecordWildCards #-} -- SPDX-FileCopyrightText: Copyright (c) 2025 Objectionary.com@@ -125,6 +126,8 @@ instance Render PAIR where render PA_TAU {..} = render attr <> render SPACE <> render arrow <> render SPACE <> render expr+ render PA_FORMATION {voids = [], attr, arrow, expr} = render (PA_TAU attr arrow expr)+ render PA_FORMATION {..} = render attr <> "(" <> intercalate ", " (map render voids) <> ")" <> render SPACE <> render arrow <> render SPACE <> render expr render PA_LAMBDA {..} = render LAMBDA <> render SPACE <> render DASHED_ARROW <> render SPACE <> render func render PA_LAMBDA' {..} = "L> " <> func render PA_VOID {..} = render attr <> render SPACE <> render arrow <> render SPACE <> render void@@ -140,6 +143,9 @@ render BDS_PAIR {..} = render COMMA <> render eol <> render tab <> render pair <> render bindings render BDS_META {..} = render COMMA <> render eol <> render tab <> render meta <> render bindings +instance Render APP_BINDING where+ render APP_BINDING {..} = render pair+ instance Render BINDING where render BI_EMPTY {..} = "" render BI_PAIR {..} = render pair <> render bindings@@ -152,10 +158,6 @@ render AAS_EMPTY = "" render AAS_EXPR {..} = render COMMA <> render eol <> render tab <> render expr <> render args --- @todo #163:30min Introduce node for formation with inlined voids.--- We need to be able to print formation with inlined void attributes:--- x(a, b) -> [[ y -> 1 ]] => x -> [[ a -> ?, b -> ?, y -> 1 ]]--- Don't forget to extend toSalty instance so such sugar. instance Render EXPRESSION where render EX_GLOBAL {..} = render global render EX_DEF_PACKAGE {..} = render pckg@@ -164,8 +166,9 @@ render EX_TERMINATION {..} = render termination render EX_FORMATION {..} = render lsb <> render eol <> render tab <> render binding <> render eol' <> render tab' <> render rsb render EX_DISPATCH {..} = render expr <> "." <> render attr- render EX_APPLICATION {..} = render expr <> "(" <> render eol <> render tab <> render bindings <> render eol' <> render tab' <> ")"- render EX_APPLICATION' {..} = render expr <> "(" <> render eol <> render tab <> render args <> render eol' <> render tab' <> ")"+ render EX_APPLICATION {..} = render expr <> "(" <> render eol <> render tab <> render tau <> render eol' <> render tab' <> ")"+ render EX_APPLICATION_TAUS {..} = render expr <> "(" <> render eol <> render tab <> render taus <> render eol' <> render tab' <> ")"+ render EX_APPLICATION_EXPRS {..} = render expr <> "(" <> render eol <> render tab <> render args <> render eol' <> render tab' <> ")" render EX_STRING {..} = '"' : render str <> "\"" render EX_NUMBER {..} = either show show num render EX_META {..} = render meta
src/Rewriter.hs view
@@ -7,11 +7,12 @@ -- SPDX-FileCopyrightText: Copyright (c) 2025 Objectionary.com -- SPDX-License-Identifier: MIT -module Rewriter (rewrite, rewrite', RewriteContext (..), Rewritten (..)) where+module Rewriter (rewrite, rewrite', RewriteContext (..), Rewritten) where import AST import Builder import Control.Exception (Exception, throwIO)+import Data.Char (toLower) import Data.Foldable (foldlM) import Data.Functor ((<&>)) import qualified Data.Map.Strict as M@@ -33,19 +34,12 @@ type RewriteState = ([Rewritten], Set.Set Program) -data Rewritten = Rewritten- { program :: Program,- maybeRule :: Maybe Y.Rule- }--instance Eq Rewritten where- left == right = program left == program right+type Rewritten = (Program, Maybe String) data RewriteContext = RewriteContext { _maxDepth :: Integer, _maxCycles :: Integer, _depthSensitive :: Bool,- _sequence :: Bool, _buildTerm :: BuildTermFunc, _must :: Must, _saveStep :: SaveStepFunc@@ -131,7 +125,7 @@ tryBuildAndReplaceFast ptn res substs ctx = buildAndReplace' ptn res substs replaceProgramThrows ctx -- The function returns tuple (X, Y) where--- - X is sequence of programs; for more details about this sequence see description of rewrite' function+-- - X is sequence of programs; -- - Y is Set of unique programs after each rule application. It allows to stop the rewriting if we're getting -- into loop and get back to program which we've already got before rewrite :: RewriteState -> [Y.Rule] -> Integer -> RewriteContext -> IO RewriteState@@ -145,7 +139,7 @@ let ruleName = fromMaybe "unknown" (Y.name rule) ptn = Y.pattern rule res = Y.result rule- Rewritten {..} = head _rewrittens+ (program, _) = head _rewrittens in if _count - 1 == _maxDepth then do logDebug (printf "Max amount of rewriting cycles (%d) for rule '%s' has been reached, rewriting is stopped" _maxDepth ruleName)@@ -179,24 +173,19 @@ (printProgram prog) ) _saveStep prog (((iteration - 1) * _maxDepth) + _count)- _rewrite (rewriteSequence prog, Set.insert prog _unique) (_count + 1)+ _rewrite (leadsTo prog, Set.insert prog _unique) (_count + 1) where- rewriteSequence :: Program -> [Rewritten]- rewriteSequence _prog- | _sequence =- let Rewritten {..} : rest = _rewrittens- in Rewritten _prog Nothing : Rewritten program (Just rule) : rest- | otherwise = [Rewritten _prog Nothing]+ leadsTo :: Program -> [Rewritten]+ leadsTo _prog =+ let (program, _) : rest = _rewrittens+ in (_prog, Nothing) : (program, Just (map toLower (fromMaybe "unknown" (Y.name rule)))) : rest -- The function accepts single program but returns sequence of programs--- If RewriteContext has _sequence == True - all the intermediate--- programs after each rule application are included into sequence--- Otherwise sequence contains only one program rewrite' :: Program -> [Y.Rule] -> RewriteContext -> IO [Rewritten]-rewrite' prog rules ctx = _rewrite ([Rewritten prog Nothing], Set.empty) 1 ctx <&> reverse+rewrite' prog rules ctx = _rewrite ([(prog, Nothing)], Set.empty) 1 ctx <&> reverse where _rewrite :: RewriteState -> Integer -> RewriteContext -> IO [Rewritten]- _rewrite state@(rewrittens, unique) count ctx@RewriteContext{..} = do+ _rewrite state@(rewrittens, unique) count ctx@RewriteContext {..} = do let cycles = _maxCycles must = _must current = count - 1@@ -212,7 +201,9 @@ else do logDebug (printf "Starting rewriting cycle for all rules: %d out of %d" count cycles) state'@(rewrittens', unique') <- rewrite state rules count ctx- if head rewrittens' == head rewrittens+ let (program', _) = head rewrittens'+ (program, _) = head rewrittens+ if program' == program then do logDebug "Rewriting is stopped since it has no effect" if not (inRange must current)
src/Sugar.hs view
@@ -1,3 +1,4 @@+{-# LANGUAGE InstanceSigs #-} {-# LANGUAGE NamedFieldPuns #-} {-# LANGUAGE RecordWildCards #-} @@ -60,9 +61,23 @@ toSalty EX_DISPATCH {..} = EX_DISPATCH (toSalty expr) attr toSalty EX_FORMATION {lsb, binding = bd@BI_EMPTY {..}, rsb} = EX_FORMATION lsb NO_EOL TAB' (toSalty (bdWithVoidRho bd)) NO_EOL TAB' rsb toSalty EX_FORMATION {..} = EX_FORMATION lsb eol tab (toSalty (bdWithVoidRho binding)) eol' tab' rsb- toSalty EX_APPLICATION {..} = EX_APPLICATION (toSalty expr) eol tab (toSalty bindings) eol' tab'- toSalty EX_APPLICATION' {..} = EX_APPLICATION (toSalty expr) eol tab (toSalty (argToBinding args tab)) eol' tab'+ toSalty EX_APPLICATION {..} = EX_APPLICATION (toSalty expr) EOL (TAB indent) (toSalty tau) EOL (TAB (indent - 1)) indent+ toSalty EX_APPLICATION_TAUS {..} =+ foldl+ toApplication+ expr+ (tauToPairs taus) where+ toApplication :: EXPRESSION -> PAIR -> EXPRESSION+ toApplication exp pair =+ EX_APPLICATION (toSalty exp) EOL (TAB indent) (APP_BINDING (toSalty pair)) EOL (TAB (indent - 1)) indent+ tauToPairs :: BINDING -> [PAIR]+ tauToPairs BI_PAIR {..} = pair : tausToPairs bindings+ tausToPairs :: BINDINGS -> [PAIR]+ tausToPairs BDS_EMPTY {..} = []+ tausToPairs BDS_PAIR {..} = pair : tausToPairs bindings+ toSalty EX_APPLICATION_EXPRS {..} = toSalty (EX_APPLICATION_TAUS expr EOL (TAB indent) (argToBinding args tab) EOL (TAB (indent - 1)) indent)+ where argToBinding :: APP_ARG -> TAB -> BINDING argToBinding APP_ARG {..} = BI_PAIR@@ -71,66 +86,73 @@ argsToBindings :: APP_ARGS -> Integer -> TAB -> BINDINGS argsToBindings AAS_EMPTY _ tab = BDS_EMPTY tab argsToBindings AAS_EXPR {..} idx tb = BDS_PAIR eol tb (PA_TAU (AT_ALPHA ALPHA idx) ARROW expr) (argsToBindings args (idx + 1) tb)- toSalty EX_NUMBER {num, tab = tb@TAB {..}} =- let number = ExDispatch (ExDispatch (ExDispatch ExGlobal (AtLabel "org")) (AtLabel "eolang")) (AtLabel "number")- bytes = ExDispatch (ExDispatch (ExDispatch ExGlobal (AtLabel "org")) (AtLabel "eolang")) (AtLabel "bytes")- number' = toCST number (indent + 1) :: EXPRESSION- bytes' = toCST bytes (indent + 2) :: EXPRESSION- data' = toCST (ExFormation [BiDelta (numToBts (either toDouble id num))]) (indent + 2) :: EXPRESSION- in toSalty- ( EX_APPLICATION'- number'- EOL- (TAB (indent + 1))- ( APP_ARG- ( EX_APPLICATION'- bytes'- EOL- (TAB (indent + 2))- (APP_ARG data' AAS_EMPTY)- EOL- (TAB (indent + 1))- )- AAS_EMPTY- )- EOL- tb- )- toSalty EX_STRING {str, tab = tb@TAB {..}} =- let string = ExDispatch (ExDispatch (ExDispatch ExGlobal (AtLabel "org")) (AtLabel "eolang")) (AtLabel "string")- bytes = ExDispatch (ExDispatch (ExDispatch ExGlobal (AtLabel "org")) (AtLabel "eolang")) (AtLabel "bytes")- string' = toCST string (indent + 1) :: EXPRESSION- bytes' = toCST bytes (indent + 2) :: EXPRESSION- data' = toCST (ExFormation [BiDelta (strToBts str)]) (indent + 2) :: EXPRESSION- in toSalty- ( EX_APPLICATION'- string'- EOL- (TAB (indent + 1))- ( APP_ARG- ( EX_APPLICATION'- bytes'- EOL- (TAB (indent + 2))- (APP_ARG data' AAS_EMPTY)- EOL- (TAB (indent + 1))- )- AAS_EMPTY- )- EOL- tb- )+ toSalty EX_NUMBER {num, tab = tab@TAB {..}, rhos} =+ saltifyPrimitive+ (toCST (BaseObject "number") (indent + 1) EOL)+ (toCST (BaseObject "bytes") (indent + 2) EOL)+ (toCST (ExFormation [BiDelta (numToBts (either toDouble id num))]) (indent + 2) EOL)+ tab+ rhos+ toSalty EX_STRING {str, tab = tab@TAB {..}, rhos} =+ saltifyPrimitive+ (toCST (BaseObject "string") (indent + 1) EOL)+ (toCST (BaseObject "bytes") (indent + 2) EOL)+ (toCST (ExFormation [BiDelta (strToBts str)]) (indent + 2) EOL)+ tab+ rhos toSalty expr = expr +saltifyPrimitive :: EXPRESSION -> EXPRESSION -> EXPRESSION -> TAB -> [Binding] -> EXPRESSION+saltifyPrimitive base bytes data' tb@TAB {..} rhos =+ let next = TAB (indent + 1)+ in toSalty+ ( EX_APPLICATION_TAUS+ base+ EOL+ next+ ( BI_PAIR+ ( PA_TAU+ (AT_ALPHA ALPHA 0)+ ARROW+ ( EX_APPLICATION_EXPRS+ bytes+ EOL+ (TAB (indent + 2))+ (APP_ARG data' AAS_EMPTY)+ EOL+ next+ (indent + 2)+ )+ )+ (toCST rhos (indent + 1) EOL)+ next+ )+ EOL+ tb+ (indent + 1)+ )+ instance ToSalty BINDING where toSalty BI_PAIR {..} = BI_PAIR (toSalty pair) (toSalty bindings) tab toSalty bd = bd +instance ToSalty APP_BINDING where+ toSalty APP_BINDING {..} = APP_BINDING (toSalty pair)+ instance ToSalty BINDINGS where toSalty BDS_PAIR {..} = BDS_PAIR eol tab (toSalty pair) (toSalty bindings) toSalty bds = bds instance ToSalty PAIR where toSalty PA_TAU {..} = PA_TAU attr arrow (toSalty expr)+ toSalty PA_FORMATION {voids, attr, arrow, expr = expr@EX_FORMATION {..}} =+ PA_TAU attr arrow (toSalty (EX_FORMATION lsb eol tab (joinToBinding voids binding) eol' tab' rsb))+ where+ joinToBinding :: [ATTRIBUTE] -> BINDING -> BINDING+ joinToBinding [] bd = bd+ joinToBinding (attr : rest) bd = BI_PAIR (PA_VOID attr arrow EMPTY) (joinToBindings rest bd) tab+ joinToBindings :: [ATTRIBUTE] -> BINDING -> BINDINGS+ joinToBindings [] BI_EMPTY {..} = BDS_EMPTY tab+ joinToBindings [] BI_PAIR {..} = BDS_PAIR eol tab pair bindings+ joinToBindings (attr : rest) bd = BDS_PAIR eol tab (PA_VOID attr arrow EMPTY) (joinToBindings rest bd) toSalty pair = pair
src/Yaml.hs view
@@ -134,13 +134,13 @@ = Ordinal Attribute | Length Binding | Literal Integer- deriving (Generic, Show)+ deriving (Eq, Generic, Show) data Comparable = CmpAttr Attribute | CmpNum Number | CmpExpr Expression- deriving (Generic, Show)+ deriving (Eq, Generic, Show) data Condition = And [Condition]@@ -153,7 +153,7 @@ | XI Expression | Matches String Expression | PartOf Expression Binding- deriving (Generic, Show)+ deriving (Eq, Generic, Show) data ExtraArgument = ArgAttribute Attribute
test/CLISpec.hs view
@@ -10,7 +10,7 @@ import CLI (runCLI) import Control.Exception import Control.Monad (forM_, unless, when)-import Data.List (isInfixOf)+import Data.List (intercalate, isInfixOf) import Data.Version (showVersion) import GHC.IO.Handle import Paths_phino (version)@@ -116,6 +116,12 @@ ["rewrite", "--input=latex"] ["The value 'latex' can't be used for '--input' option"] + it "with negative --log-lines" $+ withStdin "" $+ testCLIFailed+ ["rewrite", "--log-lines=-2"]+ ["--log-lines must be >= -1"]+ it "with negative --max-depth" $ withStdin "" $ testCLIFailed@@ -276,9 +282,7 @@ ["rewrite", "--output=latex", "--sweet"] [ "\\begin{phiquation}", "\\Big\\{[[\n",- " |x\\char95{}o| -> QQ.|z|(\n",- " |y| -> 5\n",- " ),\n",+ " |x\\char95{}o| -> QQ.|z|( |y| -> 5 ),\n", " |q\\char36{}| -> T,\n", " |w| -> $,\n", " ^ -> Q,\n",@@ -303,7 +307,7 @@ testCLISucceeded ["rewrite", "--output=latex", "--sweet", "--flat", "--expression=foo"] [ "\\begin{phiquation}",- "\\phiExpression{foo} \\Big\\{[[ |x| -> 5 ]]\\Big\\}",+ "\\phiExpression{foo} \\Big\\{[[ |x| -> 5 ]]\\Big\\}.\n", "\\end{phiquation}" ] @@ -312,7 +316,7 @@ testCLISucceeded ["rewrite", "--output=latex", "--sweet", "--flat", "--label=foo"] [ "\\begin{phiquation}\n\\label{foo}\n",- "\\Big\\{[[ |x| -> 5 ]]\\Big\\}\n",+ "\\Big\\{[[ |x| -> 5 ]]\\Big\\}.\n", "\\end{phiquation}" ] @@ -377,7 +381,7 @@ [ "\\begin{phiquation}", "\\Big\\{[[ |x| -> \"foo\" ]]\\Big\\} \\leadsto_{\\nameref{r:first}}", " \\leadsto \\Big\\{Q.|x|( |y| -> \"foo\" )\\Big\\} \\leadsto_{\\nameref{r:second}}",- " \\leadsto \\Big\\{[[ |x| -> \"foo\" ]]\\Big\\}",+ " \\leadsto \\Big\\{[[ |x| -> \"foo\" ]]\\Big\\}.", "\\end{phiquation}" ] ]@@ -499,15 +503,98 @@ ["rewrite", "--sweet", "--flat"] ["{⟦ x ↦ 5, y ↦ \"hey\", z ↦ ⟦ w ↦ ⟦⟧ ⟧ ⟧}"] + it "removes unnecessary rho bindings in primitive applications" $+ withStdin+ ( unlines+ [ "{[[",+ " z -> [[ x -> [[ t -> 42 ]].t ]].x,",+ " org -> [[ eolang -> [[ bytes -> [[ data -> ? ]], number -> [[ as-bytes -> ? ]] ]] ]]",+ "]]}"+ ]+ )+ ( testCLISucceeded+ ["rewrite", "--sweet", "--normalize", "--flat"]+ ["{⟦ z ↦ 42, org ↦ ⟦ eolang ↦ ⟦ bytes(data) ↦ ⟦⟧, number(as-bytes) ↦ ⟦⟧ ⟧ ⟧ ⟧}"]+ )++ it "reduces log message" $+ withStdin "{[[ x -> [[ y -> ? ]](y -> 5) ]]}" $+ testCLISucceeded+ ["rewrite", "--log-level=debug", "--log-lines=4", "--normalize"]+ [ intercalate+ "\n"+ [ "[DEBUG]: Applied 'COPY' (28 nodes -> 25 nodes)",+ "{⟦",+ " x ↦ ⟦",+ " y ↦ 5",+ "---| log is limited by --log-lines=4 option |---"+ ]+ ]+ describe "dataize" $ do it "dataizes simple program" $ withStdin "Q -> [[ D> 01- ]]" $ testCLISucceeded ["dataize"] ["01-"] - it "fails to dataize" $+ it "dataizes to dead" $ withStdin "Q -> [[ ]]" $- testCLIFailed ["dataize"] ["[ERROR]: Could not dataize given program"]+ testCLISucceeded ["dataize"] ["⊥"] + it "dataizes with --sequence" $+ withStdin "{[[ @ -> [[ x -> [[ D> 01-, y -> ? ]](y -> [[ ]]) ]].x ]]}" $+ testCLISucceeded+ ["dataize", "--sequence", "--output=latex", "--flat", "--sweet"]+ [ intercalate+ "\n"+ [ "\\begin{phiquation}",+ "\\Big\\{[[ @ -> [[ |x| -> [[ D> 01-, |y| -> ? ]]( |y| -> [[]] ) ]].|x| ]]\\Big\\} \\leadsto_{\\nameref{r:contextualize}}",+ " \\leadsto \\Big\\{[[ |x| -> [[ D> 01-, |y| -> ? ]]( |y| -> [[]] ) ]].|x|\\Big\\} \\leadsto_{\\nameref{r:copy}}",+ " \\leadsto \\Big\\{[[ |x| -> [[ D> 01-, |y| -> [[]] ]] ]].|x|\\Big\\} \\leadsto_{\\nameref{r:dot}}",+ " \\leadsto \\Big\\{[[ D> 01-, |y| -> [[]] ]]( ^ -> [[ |x| -> [[ D> 01-, |y| -> [[]] ]] ]] )\\Big\\} \\leadsto_{\\nameref{r:copy}}",+ " \\leadsto \\Big\\{[[ D> 01-, |y| -> [[]], ^ -> [[ |x| -> [[ D> 01-, |y| -> [[]] ]] ]] ]]\\Big\\} \\leadsto_{\\nameref{r:M(prim)}}",+ " \\leadsto \\Big\\{[[ D> 01-, |y| -> [[]], ^ -> [[ |x| -> [[ D> 01-, |y| -> [[]] ]] ]] ]]\\Big\\}.",+ "\\end{phiquation}",+ "01-"+ ]+ ]++ describe "fails" $ do+ it "with --output != latex and --nonumber" $+ withStdin "{[[]]}" $+ testCLIFailed+ ["dataize", "--nonumber", "--output=xmir"]+ ["The --nonumber option can stay together with --output=latex only"]++ it "with --omit-listing and --output != xmir" $+ withStdin "{[[]]}" $+ testCLIFailed+ ["dataize", "--omit-listing", "--output=phi"]+ ["--omit-listing"]++ it "with --omit-comments and --output != xmir" $+ withStdin "{[[]]}" $+ testCLIFailed+ ["dataize", "--omit-comments", "--output=phi"]+ ["--omit-comments"]++ it "with --expression and --output != latex" $+ withStdin "{[[]]}" $+ testCLIFailed+ ["dataize", "--expression=foo", "--output=phi"]+ ["--expression option can stay together with --output=latex only"]++ it "with --label and --output != latex" $+ withStdin "{[[]]}" $+ testCLIFailed+ ["dataize", "--label=foo", "--output=phi"]+ ["--label option can stay together with --output=latex only"]++ it "with wrong --hide option" $+ withStdin "{[[]]}" $+ testCLIFailed+ ["dataize", "--hide=Q.x(Q.y)"]+ ["[ERROR]: Invalid set of arguments: Only dispatch expression", "but given: Φ.x( Φ.y )"]+ describe "explain" $ do it "explains single rule" $ testCLISucceeded@@ -556,15 +643,9 @@ [ "{⟦", " org ↦ ⟦", " eolang ↦ ⟦",- " number ↦ ⟦",- " φ ↦ ∅",- " ⟧,",- " bytes ↦ ⟦",- " data ↦ ∅",- " ⟧,",- " string ↦ ⟦",- " φ ↦ ∅",- " ⟧,",+ " number(φ) ↦ ⟦⟧,",+ " bytes(data) ↦ ⟦⟧,",+ " string(φ) ↦ ⟦⟧,", " λ ⤍ Package", " ⟧,", " λ ⤍ Package",@@ -587,3 +668,40 @@ testCLIFailed ["merge"] ["At least one input file must be specified for 'merge' command"]+ + describe "match" $ do+ it "takes from stdin" $+ withStdin "{[[]]}" $+ testCLISucceeded ["match"] ["[INFO]"]+ + it "takes from file" $+ testCLISucceeded ["match", "test-resources/cli/foo.phi"] ["[INFO]"]+ + it "does not print substitutions without pattern" $+ withStdin "{[[]]}" $+ testCLISucceeded ["match"] ["[INFO]: The --pattern is not provided, no substitutions are built"]+ + it "prints one substitution" $+ withStdin "{[[ x -> Q.x ]]}" $+ testCLISucceeded ["match", "--pattern=Q.!a"] ["a >> x"]+ + it "prints many substitutions" $+ withStdin "{[[ x -> Q.x, y -> Q.y ]]}" $+ testCLISucceeded ["match", "--pattern=Q.!a"] ["a >> x\n------\na >> y"]++ it "builds substitutions with conditions" $+ withStdin "{[[ x -> Q.y ]].x}" $+ testCLISucceeded+ ["match", "--pattern=[[ !a -> Q.y, !B ]].!a", "--when=and(not(alpha(!a)),eq(length(!B),1))"]+ ["B >> ⟦ ρ ↦ ∅ ⟧\na >> x"]+ + it "builds with condition from file" $+ testCLISucceeded+ ["match", "--pattern=[[ !B ]]", "--when=eq(length(!B),2)", "test-resources/cli/foo.phi"]+ ["B >> ⟦\n foo ↦ Φ.org.eolang.x,\n ρ ↦ ∅\n⟧"]+ + it "fails on parsing --when condition" $+ withStdin "{[[]]}" $+ testCLIFailed+ ["match", "--pattern=[[!B]]", "--when=hello"]+ ["[ERROR]: Couldn't parse given condition"]
+ test/ConditionSpec.hs view
@@ -0,0 +1,51 @@+-- SPDX-FileCopyrightText: Copyright (c) 2025 Objectionary.com+-- SPDX-License-Identifier: MIT++module ConditionSpec where++import AST (Attribute (AtLabel, AtMeta), Binding (BiMeta), Expression (ExDispatch, ExGlobal, ExMeta))+import Condition+import Control.Monad (forM_)+import Data.Either (isLeft, isRight)+import Test.Hspec (Spec, describe, it, shouldBe, shouldSatisfy)+import Yaml qualified as Y++spec :: Spec+spec = do+ describe "just parses" $+ forM_+ [ "in (!a, !B)",+ " not (in (!a1, !B)) ",+ "alpha(x)",+ "eq(1, 1)",+ "or(eq(ordinal(a),1),eq(length(!B),-2),eq(!e1,!e2),eq(!a1,x),eq(Q.org.eolang,[[ x -> 2 ]]))",+ "and(alpha(q),eq(-5,21))",+ "nf([[ x -> !e ]].x)",+ "xi(!e1)",+ "matches(\"hello(\\\"\\u0000)\", !e)",+ "part-of ( [[ x -> 1 ]] , !B ) ",+ "and(not(alpha(!a)),eq(!a,x))"+ ]+ (\expr -> it expr (parseCondition expr `shouldSatisfy` isRight))++ describe "parses correctly" $+ forM_+ [ ("in(!a, !B)", Y.In (AtMeta "a") (BiMeta "B")),+ ("not(in(!a,!B))", Y.Not (Y.In (AtMeta "a") (BiMeta "B"))),+ ("alpha(y)", Y.Alpha (AtLabel "y")),+ ("eq(1,-2)", Y.Eq (Y.CmpNum (Y.Literal 1)) (Y.CmpNum (Y.Literal (-2)))),+ ("eq(ordinal(z),length(!B1))", Y.Eq (Y.CmpNum (Y.Ordinal (AtLabel "z"))) (Y.CmpNum (Y.Length (BiMeta "B1")))),+ ("eq(!a1, !e2)", Y.Eq (Y.CmpAttr (AtMeta "a1")) (Y.CmpExpr (ExMeta "e2"))),+ ("or(xi(!e1), nf(Q.x))", Y.Or [Y.XI (ExMeta "e1"), Y.NF (ExDispatch ExGlobal (AtLabel "x"))]),+ ("and(matches(\"hi\", !e),part-of(!e, !B))", Y.And [Y.Matches "hi" (ExMeta "e"), Y.PartOf (ExMeta "e") (BiMeta "B")])+ ]+ (\(expr, res) -> it expr (parseCondition expr `shouldBe` Right res))++ describe "does not parse" $+ forM_+ [ "some()",+ "in(!a, !a)",+ "alpha(!B)",+ "or(or(), or())"+ ]+ (\expr -> it expr (parseCondition expr `shouldSatisfy` isLeft))
test/DataizeSpec.hs view
@@ -9,39 +9,49 @@ import Deps (dontSaveStep) import Functions (buildTerm) import Parser (parseProgramThrows)+import Printer (printProgram)+import Rewriter (Rewritten) import Test.Hspec defaultDataizeContext :: Program -> DataizeContext defaultDataizeContext prog = DataizeContext prog 25 25 False buildTerm dontSaveStep -test :: (Eq a, Show a) => (Expression -> DataizeContext -> IO (Maybe a)) -> [(String, Expression, Expression, Maybe a)] -> Spec+test :: (Eq a, Show a) => ((Expression, [Rewritten]) -> DataizeContext -> IO (Maybe a, [Rewritten])) -> [(String, Expression, Expression, Maybe a)] -> Spec test func useCases = forM_ useCases $ \(desc, input, prog, output) -> it desc $ do- res <- func input (defaultDataizeContext (Program prog))+ (res, _) <- func (input, []) (defaultDataizeContext (Program prog)) res `shouldBe` output +test' :: (Eq a, Show a) => ((Expression, [Rewritten]) -> DataizeContext -> IO (a, [Rewritten])) -> [(String, Expression, Expression, a)] -> Spec+test' func useCases =+ forM_ useCases $ \(desc, input, prog, output) ->+ it desc $ do+ (res, _) <- func (input, [(Program prog, Nothing)]) (defaultDataizeContext (Program prog))+ res `shouldBe` output+ testDataize :: [(String, String, Bytes)] -> Spec testDataize useCases = forM_ useCases $ \(name, prog, res) -> it name $ do prog' <- parseProgramThrows prog- value <- dataize prog' (defaultDataizeContext prog')+ putStrLn (printProgram prog')+ (value, _) <- dataize prog' (defaultDataizeContext prog') value `shouldBe` Just res spec :: Spec spec = do describe "morph" $- test+ test' morph- [ ("[[ D> 00- ]] => [[ D> 00- ]]", ExFormation [BiDelta (BtOne "00")], ExGlobal, Just (ExFormation [BiDelta (BtOne "00")])),- ("T => T", ExTermination, ExGlobal, Just ExTermination),- ("$ => X", ExThis, ExGlobal, Nothing),- ("Q => X", ExGlobal, ExGlobal, Nothing),+ [ ("[[ D> 00- ]] => [[ D> 00- ]]", ExFormation [BiDelta (BtOne "00")], ExGlobal, ExFormation [BiDelta (BtOne "00")]),+ ("T => T", ExTermination, ExGlobal, ExTermination),+ ("$ => X", ExThis, ExGlobal, ExTermination),+ ("Q => X", ExGlobal, ExGlobal, ExTermination), ( "Q.x (Q -> [[ x -> [[]] ]]) => [[]]", ExDispatch ExGlobal (AtLabel "x"), ExFormation [BiTau (AtLabel "x") (ExFormation [])],- Just (ExFormation [])+ ExFormation [] ) ]
test/HideSpec.hs view
@@ -15,10 +15,8 @@ import Hide (hide) import Misc import Parser (parseExpressionThrows, parseProgramThrows)-import Rewriter import System.FilePath import Test.Hspec-import Yaml (normalizationRules) data YamlPack = YamlPack { program :: String,@@ -34,9 +32,7 @@ spec = describe "hide packs" $ do let resources = "test-resources/hide-packs"- rule = head normalizationRules packs <- runIO (allPathsIn resources)- forM_ packs ( \pth -> it (makeRelative resources pth) $ do@@ -44,6 +40,6 @@ prog <- parseProgramThrows program expr <- parseExpressionThrows hidden res <- parseProgramThrows result- let [Rewritten {program = prog'}] = hide [Rewritten prog (Just rule)] [expr]+ let [(prog', _)] = hide [(prog, Nothing)] [expr] prog' `shouldBe` res )
test/ParserSpec.hs view
@@ -9,7 +9,7 @@ import AST import Control.Monad (forM_) import Data.Either (isLeft, isRight)-import Misc (allPathsIn)+import Misc import Parser import System.FilePath (takeBaseName) import Test.Hspec (Example (Arg), Expectation, Spec, SpecWith, describe, it, runIO, shouldBe, shouldSatisfy)@@ -147,56 +147,20 @@ Just ( ExApplication ( ExDispatch- ( ExApplication- (ExDispatch (ExDispatch (ExDispatch ExGlobal (AtLabel "org")) (AtLabel "eolang")) (AtLabel "number"))- ( BiTau- (AtAlpha 0)- ( ExApplication- (ExDispatch (ExDispatch (ExDispatch ExGlobal (AtLabel "org")) (AtLabel "eolang")) (AtLabel "bytes"))- ( BiTau- (AtAlpha 0)- (ExFormation [BiDelta (BtMany ["40", "14", "00", "00", "00", "00", "00", "00"]), BiVoid AtRho])- )- )- )- )+ (DataNumber (BtMany ["40", "14", "00", "00", "00", "00", "00", "00"])) (AtLabel "plus") ) ( BiTau (AtAlpha 0) ( ExApplication ( ExDispatch- ( ExApplication- (ExDispatch (ExDispatch (ExDispatch ExGlobal (AtLabel "org")) (AtLabel "eolang")) (AtLabel "number"))- ( BiTau- (AtAlpha 0)- ( ExApplication- (ExDispatch (ExDispatch (ExDispatch ExGlobal (AtLabel "org")) (AtLabel "eolang")) (AtLabel "bytes"))- ( BiTau- (AtAlpha 0)- (ExFormation [BiDelta (BtMany ["40", "14", "00", "00", "00", "00", "00", "00"]), BiVoid AtRho])- )- )- )- )+ (DataNumber (BtMany ["40", "14", "00", "00", "00", "00", "00", "00"])) (AtLabel "q") ) ( BiTau (AtAlpha 0) ( ExDispatch- ( ExApplication- (ExDispatch (ExDispatch (ExDispatch ExGlobal (AtLabel "org")) (AtLabel "eolang")) (AtLabel "string"))- ( BiTau- (AtAlpha 0)- ( ExApplication- (ExDispatch (ExDispatch (ExDispatch ExGlobal (AtLabel "org")) (AtLabel "eolang")) (AtLabel "bytes"))- ( BiTau- (AtAlpha 0)- (ExFormation [BiDelta (BtMany ["68", "65", "6C", "6C", "6F"]), BiVoid AtRho])- )- )- )- )+ (DataString (BtMany ["68", "65", "6C", "6C", "6F"])) (AtLabel "length") ) )
test/RewriterSpec.hs view
@@ -1,7 +1,6 @@ {-# LANGUAGE DeriveAnyClass #-} {-# LANGUAGE DeriveGeneric #-} {-# LANGUAGE LambdaCase #-}-{-# LANGUAGE RecordWildCards #-} {-# OPTIONS_GHC -Wno-orphans #-} -- SPDX-FileCopyrightText: Copyright (c) 2025 Objectionary.com@@ -20,7 +19,7 @@ import Must (Must (..)) import Parser (parseProgramThrows) import Printer (printProgram)-import Rewriter (RewriteContext (RewriteContext), Rewritten (..), rewrite')+import Rewriter (RewriteContext (RewriteContext), rewrite') import System.FilePath (makeRelative, replaceExtension, (</>)) import Test.Hspec (Spec, describe, expectationFailure, it, pending, runIO) import Yaml (normalizationRules)@@ -99,7 +98,7 @@ if normalize' then pure normalizationRules else pure []- [Rewritten {..}] <-+ rewrittens <- rewrite' prog rules'@@ -107,11 +106,11 @@ repeat' repeat' False- False buildTerm must' dontSaveStep )+ let (program, _) = last rewrittens result' <- parseProgramThrows (output pack) unless (program == result') $ expectationFailure