pattern matching against string based commands.
An extensible, format-agnostic command parsing library designed to be easy to use and syntactically light weight.
Assuming we write a parser to convert a command such as
calculator add 1 2 -v=yes
Into path and flags such as ["calculator", "add"]
and Map.fromList [("v","yes")]
, This library will then match said path and flags against a nested record type of commands built up using lightweight monadic syntax and tries to execute the associated function if the matching and value converting works, or returns an error if the path/flags fail to match any command.
To get started, see the documentation for the Commander
module below. Additionally, an examples folder is included in the source illustrating usage - see https://github.com/jsdw/hs-commander for more.
Commander: A Haskell Library for parsing commands
This library aims to make it super easy to create and use nested commands with flags in the form of an object that is easy to traverse and run custom actions on, and easy to extend with handling for safe casting to values from their input strings.
As an example of usage (take a look in the examples folder for more) we can define commands as follows:
someCommands :: Command (IO ())
someCommands = commands $ do
command "repeat" $ do
help "Repeat a string n times"
run $
\(Value str :: Value "value to repeat" String)
(Flag n :: Flag '["n"] "times to repeat" Int) ->
sequence_ $ replicate n (putStrLn str)
command "calculate" $ do
help "perform calculations"
command "add" $ do
help "add two numbers"
run $
\(Value n1 :: Value "number 1" Int)
(Value n2 :: Value "number 2" Int)
(Flag verbose :: Flag '["v", "verbose"] "verbose mode" Bool) ->
if verbose
then putStrLn $ (show n1) ++ " + " ++ (show n2) ++ " = " ++ (show $ n1 + n2)
else putStrLn (show $ n1 + n2)
command "multiply" $ do
help "multiply two numbers"
run $
\(Value n1 :: Value "number 1" Int)
(Value n2 :: Value "number 2" Int) ->
putStrLn $ (show n1) ++ " x " ++ (show n2) ++ " = " ++ (show $ n1 * n2)
And this leads to someCommands
being a nested record of type Command (IO ())
(where IO ()
corresponds to the output from the functions you provide). This record can then be traversed and interacted with.
The novelty of this approach (compared to others I have seen, which is a non exhaustive list!) is the use of the functions input parameter types to cast-from-string and inject the appropriate values into the function safely at runtime, as well as to generate documentation. This means that we only declare the flags and values each command requires in one place, and do not execute any of the output unless all parameters are fully satisfied. These functions are then safely hidden away inside an existential type, and so we don't need any other type level magic or handling to work with the output.
Installation
Add this repository to your stack.yaml file under the packages folder, so we end up with something that looks a bit like:
packages: - '.' - location: git: https://github.com/jsdw/hs-commander commit: abcdef123456789abcdef1234
run
stack install
.import
Commander
into your library.
you can run the example code by running stack install :example1 && example1
assuming that the directory stack copies binaries to is present in your PATH
.
Documentation
if you clone this repository locally somewhere, you can use stack haddock
inside its folder in order to generate haddock documentation for it.
Disclaimer
This project is still under heavy development and could well change drastically!