From f7b8b50e0b624b55b88a8c89ffb2b86a66b5aa14 Mon Sep 17 00:00:00 2001 From: AlephAlpha Date: Fri, 7 Jun 2024 16:15:49 +0800 Subject: [PATCH] :beaver: --- .gitignore | 1 + CHANGELOG.md | 6 +++ Nekomata.cabal | 17 ++++---- app/Main.hs | 40 +++++++++--------- app/Repl.hs | 3 +- src/Nekomata/Builtin.hs | 10 ++++- src/Nekomata/CodePage.hs | 22 ++++++++-- src/Nekomata/Data.hs | 9 ++-- src/Nekomata/Eval.hs | 88 ++++++++++++++++++++++------------------ src/Nekomata/Function.hs | 4 ++ src/Nekomata/Particle.hs | 15 ++++++- src/Nekomata/Result.hs | 37 +++++++++++++++++ 12 files changed, 173 insertions(+), 79 deletions(-) create mode 100644 src/Nekomata/Result.hs diff --git a/.gitignore b/.gitignore index 4adfbf7..7d5d63e 100644 --- a/.gitignore +++ b/.gitignore @@ -66,3 +66,4 @@ cabal.project.local~ # Custom rules (everything added below won't be overriden by 'Generate .gitignore File' if you use 'Update' option) .vscode/settings.json +haddocks/ diff --git a/CHANGELOG.md b/CHANGELOG.md index 3b899d1..61dc81f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,11 @@ # Revision history for Nekomata +## 0.7.0.0 -- Unreleased + +### Breaking changes + +* Some modules in the library are no longer exposed. + ## 0.6.1.0 -- 2024-06-07 * Now the REPL saves the history to a file named `.history` in the current directory. diff --git a/Nekomata.cabal b/Nekomata.cabal index ebb066e..3c2dc2d 100644 --- a/Nekomata.cabal +++ b/Nekomata.cabal @@ -21,7 +21,7 @@ name: Nekomata -- PVP summary: +-+------- breaking API changes -- | | +----- non-breaking API additions -- | | | +--- code changes with no API change -version: 0.6.1.0 +version: 0.7.0.0 -- A short (one-line) description of the package. synopsis: @@ -64,13 +64,8 @@ library exposed-modules: Nekomata.Builtin Nekomata.CodePage - Nekomata.Data Nekomata.Eval - Nekomata.Function - Nekomata.NonDet - Nekomata.Parser Nekomata.Particle - Nekomata.Program -- Modules included in this library but not exported. other-modules: @@ -78,8 +73,14 @@ library Nekomata.Builtin.List Nekomata.Builtin.Math Nekomata.Builtin.String + Nekomata.Data + Nekomata.Function + Nekomata.NonDet + Nekomata.Parser Nekomata.Parser.Data Nekomata.Parser.Program + Nekomata.Program + Nekomata.Result -- LANGUAGE extensions used by modules in this package. -- other-extensions: @@ -88,7 +89,7 @@ library build-depends: , arithmoi ^>=0.13 , base >=4.15 && <5 - , containers ^>=0.6 + , containers ^>=0.7 , integer-roots ^>=1.0 , parsec ^>=3.1 @@ -118,7 +119,7 @@ executable Nekomata build-depends: , base >=4.15 && <5 , bytestring ^>=0.12 - , containers ^>=0.6 + , containers ^>=0.7 , haskeline ^>=0.8 , Nekomata , optparse-applicative ^>=0.18 diff --git a/app/Main.hs b/app/Main.hs index cb0a7aa..6c861fd 100644 --- a/app/Main.hs +++ b/app/Main.hs @@ -24,14 +24,14 @@ optCode = <> metavar "CODE" <> help "Code to run" ) - <|> CodeFile + <|> CodeFile <$> strOption ( long "file" <> short 'f' <> metavar "FILE" <> help "File to run (custom encoding)" ) - <|> CodeFileUtf8 + <|> CodeFileUtf8 <$> strOption ( long "utf8" <> short 'u' @@ -48,13 +48,13 @@ optInput = <> metavar "INPUT" <> help "Input to pass to the program" ) - <|> flag' - InputStdin - ( long "stdin" - <> short 's' - <> help "Read input from stdin" - ) - <|> pure InputNone + <|> flag' + InputStdin + ( long "stdin" + <> short 's' + <> help "Read input from stdin" + ) + <|> pure InputNone optMode :: Parser Mode optMode = @@ -117,17 +117,17 @@ opts :: Parser Opts opts = Opts <$> optRunOnce - <|> flag' Repl (long "repl" <> short 'r' <> help "Run the REPL") - <|> flag' - DocBuiltin - (long "doc" <> help "Generate documentation for builtins") - <|> flag' - DocCodePage - (long "codepage" <> help "Generate documentation for code page") - <|> flag' - Version - (long "version" <> short 'v' <> help "Show version") - <|> pure Repl + <|> flag' Repl (long "repl" <> short 'r' <> help "Run the REPL") + <|> flag' + DocBuiltin + (long "doc" <> help "Generate documentation for builtins") + <|> flag' + DocCodePage + (long "codepage" <> help "Generate documentation for code page") + <|> flag' + Version + (long "version" <> short 'v' <> help "Show version") + <|> pure Repl optsInfo :: ParserInfo Opts optsInfo = diff --git a/app/Repl.hs b/app/Repl.hs index 62249a0..d66f078 100644 --- a/app/Repl.hs +++ b/app/Repl.hs @@ -7,7 +7,6 @@ import qualified Data.Map.Strict as Map import Data.Version (showVersion) import Nekomata.Builtin (Builtin (short), builtinMap, infoByName) import Nekomata.Eval -import Nekomata.Function (Function (arity)) import qualified Nekomata.Particle as Particle import Paths_Nekomata (version) import System.Console.Haskeline @@ -152,7 +151,7 @@ repl state = handleInterrupt (outputStrLn "Cancelled." >> repl state) $ do outputStrLn $ "Invalid code: " ++ show e repl state Right function' -> do - outputStrLn $ code ++ " : " ++ show (arity function') + outputStrLn $ code ++ " : " ++ show (getArity function') repl state replCommandCompletion :: String -> [Completion] diff --git a/src/Nekomata/Builtin.hs b/src/Nekomata/Builtin.hs index dfc191b..dac28d6 100644 --- a/src/Nekomata/Builtin.hs +++ b/src/Nekomata/Builtin.hs @@ -20,9 +20,13 @@ import Nekomata.Function -- | A builtin function in Nekomata data Builtin = Builtin { name :: String + -- ^ The name of the builtin function , short :: Char + -- ^ The short name of the builtin function , func :: Function + -- ^ The function itself , help :: String + -- ^ The help message for the builtin function } instance Show Builtin where @@ -1372,7 +1376,11 @@ builtinShortMap :: Map Char Builtin builtinShortMap = Map.fromList [(short b, b) | b <- builtins] -- | An error that occurs when a builtin function is not found -data BuiltinNotFoundError = BuiltinNotFound String | BuiltinShortNotFound Char +data BuiltinNotFoundError + = -- | Cannot find a builtin function with the given full name + BuiltinNotFound String + | -- | Cannot find a builtin function with the given short name + BuiltinShortNotFound Char deriving (Eq) instance Show BuiltinNotFoundError where diff --git a/src/Nekomata/CodePage.hs b/src/Nekomata/CodePage.hs index 500e6b0..5bebcac 100644 --- a/src/Nekomata/CodePage.hs +++ b/src/Nekomata/CodePage.hs @@ -1,4 +1,14 @@ -module Nekomata.CodePage where +module Nekomata.CodePage ( + CodePageError (..), + byteToChar, + charToInt, + checkCodePage, + codePage, + codePageMarkdown, + fromBytes, + intToChar, + toBytes, +) where import Control.Monad (zipWithM) import Data.List (elemIndex) @@ -40,7 +50,7 @@ intToChar x byteToChar :: Word8 -> Char byteToChar = (codePage !!) . fromIntegral --- A Markdown table of the code page +-- | A Markdown table of the code page codePageMarkdown :: String codePageMarkdown = "| |" @@ -67,7 +77,13 @@ codePageMarkdown = escape c = [c] -- | An error that occurs when a character is not in Nekomata's code page. -data CodePageError = CodePageError {char :: Char, pos :: Int} deriving (Eq) +data CodePageError = CodePageError + { char :: Char + -- ^ The character that is not in the code page + , pos :: Int + -- ^ The position of the character in the string + } + deriving (Eq) instance Show CodePageError where show (CodePageError c i) = diff --git a/src/Nekomata/Data.hs b/src/Nekomata/Data.hs index d13d3e5..131e7b1 100644 --- a/src/Nekomata/Data.hs +++ b/src/Nekomata/Data.hs @@ -200,9 +200,12 @@ instance (NonDet a) => NonDet (ListTry a) where -- | Nekomata's data type (deterministic) data Data - = DNum Rational - | DChar Word8 - | DList [Data] + = -- | A rational number + DNum Rational + | -- | A character + DChar Word8 + | -- | A list of data + DList [Data] deriving (Eq, Ord) -- | Check if a @Data@ is a nonempty String, and return the String if it is diff --git a/src/Nekomata/Eval.hs b/src/Nekomata/Eval.hs index 77e6602..8593c6e 100644 --- a/src/Nekomata/Eval.hs +++ b/src/Nekomata/Eval.hs @@ -1,4 +1,25 @@ -module Nekomata.Eval where +module Nekomata.Eval ( + Arity (..), + Data (..), + Function, + NekomataData, + NekomataError, + Mode (..), + Result (..), + Runtime, + allResults, + checkResult, + compile, + countResults, + eval, + firstResult, + getArity, + initRuntime, + readInput, + runFunction, + showResult, + toResult, +) where import Control.Arrow (left) import Control.Monad ((>=>)) @@ -10,6 +31,7 @@ import Nekomata.NonDet import Nekomata.Parser import Nekomata.Particle (ParticleArityError) import Nekomata.Program +import Nekomata.Result import Text.Parsec (ParseError, parse) -- | An error that can occur during parsing and evaluation @@ -35,12 +57,13 @@ compile = . compileProgram -- | Nekomata's runtime state -data Runtime = Runtime {choiceId :: Id, stack :: Stack} +data Runtime = Runtime Id Stack -- | Initialize Nekomata's runtime state with a list of input values initRuntime :: [Data] -> Runtime initRuntime = Runtime initId . initStack . map fromValue +-- | Read a Nekomata input string into a list of values readInput :: String -> Either NekomataError [Data] readInput = left CodePageError @@ -48,14 +71,25 @@ readInput = >=> left ParseError . parse parseInput "" +-- | Nekomata's non-deterministic evaluation result +newtype NekomataData = NekomataData {fromNekomataData :: TryData} + -- | Run a Nekomata function with the given runtime state -runFunction :: Function -> Runtime -> (Runtime, TryData) +runFunction :: Function -> Runtime -> (Runtime, NekomataData) runFunction f (Runtime id' s) = let s' = apply f (leftId id') s - in (Runtime (rightId id') s', top s') + in (Runtime (rightId id') s', NekomataData $ top s') -- | Nekomata's evaluation mode -data Mode = AllValues | FirstValue | CountValues | CheckExistence +data Mode + = -- | Show all results + AllValues + | -- | Show the first result + FirstValue + | -- | Count the number of results + CountValues + | -- | Check if there are any results + CheckExistence deriving (Eq, Ord, Show) -- | Show a Nekomata value @@ -63,49 +97,23 @@ showData :: Data -> String showData x = fromMaybe (show x) $ asString x -- | Get all results of a Nekomata evaluation as a list of strings -allResults :: TryData -> [String] -allResults = map showData . values initDecisions +allResults :: NekomataData -> [String] +allResults = map showData . values initDecisions . fromNekomataData -- | Get the first result of a Nekomata evaluation as a string -firstResult :: TryData -> Maybe String -firstResult = fmap showData . values initDecisions +firstResult :: NekomataData -> Maybe String +firstResult = fmap showData . values initDecisions . fromNekomataData -- | Count the number of results of a Nekomata evaluation -countResults :: TryData -> Integer -countResults = countValues initDecisions +countResults :: NekomataData -> Integer +countResults = countValues initDecisions . fromNekomataData -- | Check if a Nekomata evaluation has any results -checkResult :: TryData -> Bool -checkResult = hasValue initDecisions - --- | The result of a Nekomata evaluation, to be shown to the user -data Result - = All Bool [String] - | First (Maybe String) - | Count Integer - | Check Bool - deriving (Eq) - -instance Show Result where - show (All truncated xs) = unwords xs ++ if truncated then " ..." else "" - show (First x) = fromMaybe "" x - show (Count n) = show n - show (Check b) = show b - --- | Show a Nekomata result separated by newlines -showResult :: Result -> [String] -showResult (All truncated xs) = xs ++ ["..." | truncated] -showResult (First x) = [fromMaybe "" x] -showResult (Count n) = [show n] -showResult (Check b) = [show b] - --- | Truncate a list of strings to a given length -truncate' :: Int -> [String] -> Result -truncate' n xs = - let (ys, zs) = splitAt n xs in All (not $ null zs) ys +checkResult :: NekomataData -> Bool +checkResult = hasValue initDecisions . fromNekomataData -- | Get the result of a Nekomata evaluation according to the mode -toResult :: Mode -> Maybe Int -> TryData -> Result +toResult :: Mode -> Maybe Int -> NekomataData -> Result toResult AllValues Nothing = All False . allResults toResult AllValues (Just n) = truncate' n . allResults toResult FirstValue _ = First . firstResult diff --git a/src/Nekomata/Function.hs b/src/Nekomata/Function.hs index 49f1541..af49ac3 100644 --- a/src/Nekomata/Function.hs +++ b/src/Nekomata/Function.hs @@ -68,6 +68,10 @@ data Function = Function , apply :: Id -> Stack -> Stack } +-- | Get the arity of a function +getArity :: Function -> Arity +getArity = arity + -- | The identity function identity :: Function identity = Function (Arity 0 0) $ \_ s -> s diff --git a/src/Nekomata/Particle.hs b/src/Nekomata/Particle.hs index 59fb7a9..be70075 100644 --- a/src/Nekomata/Particle.hs +++ b/src/Nekomata/Particle.hs @@ -30,10 +30,15 @@ newtype Particle = Particle {runParticle :: Function -> Maybe Function} -- | A builtin particle in Nekomata data BuiltinParticle = BuiltinParticle { name :: String + -- ^ The name of the particle , short :: Char + -- ^ The short name of the particle , particle :: Particle + -- ^ The particle itself , arity :: String + -- ^ The arity of the particle , help :: String + -- ^ The help message for the particle } instance Show BuiltinParticle where @@ -251,8 +256,10 @@ builtinParticleShortMap = -- | An error that occurs when a builtin particle is not found data ParticleNotFoundError - = ParticleNotFound String - | ParticleShortNotFound Char + = -- | Cannot find a particle with the given full name + ParticleNotFound String + | -- | Cannot find a particle with the given short name + ParticleShortNotFound Char deriving (Eq) instance Show ParticleNotFoundError where @@ -266,9 +273,13 @@ with the wrong arity -} data ParticleArityError = ParticleArityError { particleName :: String + -- ^ The name of the particle , particleShort :: Char + -- ^ The short name of the particle , particleArity :: String + -- ^ The arity of the particle , functionArity :: String + -- ^ The arity of the function applied to } deriving (Eq) diff --git a/src/Nekomata/Result.hs b/src/Nekomata/Result.hs new file mode 100644 index 0000000..2f8051f --- /dev/null +++ b/src/Nekomata/Result.hs @@ -0,0 +1,37 @@ +module Nekomata.Result ( + Result (..), + showResult, + truncate', +) where + +import Data.Maybe (fromMaybe) + +-- | The result of a Nekomata evaluation, to be shown to the user +data Result + = -- | All results, possibly truncated + All Bool [String] + | -- | The first result + First (Maybe String) + | -- | The number of results + Count Integer + | -- | Whether there are any results + Check Bool + deriving (Eq) + +instance Show Result where + show (All truncated xs) = unwords xs ++ if truncated then " ..." else "" + show (First x) = fromMaybe "" x + show (Count n) = show n + show (Check b) = show b + +-- | Show a Nekomata result separated by newlines +showResult :: Result -> [String] +showResult (All truncated xs) = xs ++ ["..." | truncated] +showResult (First x) = [fromMaybe "" x] +showResult (Count n) = [show n] +showResult (Check b) = [show b] + +-- | Truncate a list of strings to a given length +truncate' :: Int -> [String] -> Result +truncate' n xs = + let (ys, zs) = splitAt n xs in All (not $ null zs) ys \ No newline at end of file