Skip to content

Commit bd14b9f

Browse files
authored
Initial version from hackage
1 parent f0ada00 commit bd14b9f

File tree

5 files changed

+358
-0
lines changed

5 files changed

+358
-0
lines changed

LICENSE

+22
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
Copyright (c) 2010, SD Swierstra
2+
All rights reserved.
3+
4+
The MIT License
5+
6+
Permission is hereby granted, free of charge, to any person obtaining a copy
7+
of this software and associated documentation files (the "Software"), to deal
8+
in the Software without restriction, including without limitation the rights
9+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10+
copies of the Software, and to permit persons to whom the Software is
11+
furnished to do so, subject to the following conditions:
12+
13+
The above copyright notice and this permission notice shall be included in
14+
all copies or substantial portions of the Software.
15+
16+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22+
THE SOFTWARE.

Setup.hs

+4
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
#!/usr/bin/env runhaskell
2+
import Distribution.Simple
3+
main :: IO ()
4+
main = defaultMain

src/Options/UU/Demo.hs

+86
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
{-# LANGUAGE TemplateHaskell, FlexibleContexts, NoMonomorphismRestriction, TypeSynonymInstances, FlexibleInstances #-}
2+
module Options.UU.Demo where
3+
import Data.Lenses.Template
4+
import Text.ParserCombinators.UU
5+
import Text.ParserCombinators.UU.BasicInstances
6+
import Text.ParserCombinators.UU.Utils
7+
import Text.ParserCombinators.UU.Interleaved
8+
import Options.UU.Interleaved
9+
import Data.Monoid
10+
import System.Environment
11+
12+
-- We assume that we store our options in a data type for which we generate lenses
13+
14+
data Prefers = Agda | Haskell deriving Show
15+
data Address = Address { city_ :: String
16+
, street_ :: String}
17+
deriving Show
18+
data Name = Name { name_:: String
19+
, prefers_:: Prefers
20+
, ints_ :: [Int]
21+
, address_ :: Address}
22+
deriving Show
23+
24+
$(deriveLenses ''Name)
25+
$(deriveLenses ''Address)
26+
27+
instance ShowParserType Prefers where
28+
showType p = " <Agda | Haskell> "
29+
30+
-- The next thing to do is to specify a initial record containing the default values:
31+
defaults = Name "Atze" Haskell []
32+
(Address "Utrecht"
33+
"Princetonplein")
34+
35+
-- Next we define the parser for the options, by specifying for each field what may be specified:
36+
37+
oName =
38+
name `option` ("name", pString, "Name")
39+
<> ints `options` ("ints", pNaturalRaw, "A couple of numbers")
40+
<> prefers `choose` [("agda", Agda, "in case you prefer Agda")
41+
,("haskell", Haskell, "in case you prefer Haskell")
42+
]
43+
<> address `field`
44+
( city `option` ("city", pString, "Home city")
45+
<> street `option` ("street" ,pString, "Home Street" )
46+
)
47+
{-
48+
-- | The function `main` may serve as a template for your own option handling. You can also use this module to see what the effectis of the various ways of passing options
49+
-- >>> ./Demo -i1 --ints 2 --street=Zandlust -a -nDoaitse -i3 --ints=4 --city=Tynaarlo
50+
-- Name {name_ = "Doaitse", prefers_ = Agda, ints_ = [1,2,3,4], address_ = Address {city_ = "Tynaarlo", street_ = "Zandlust"}}
51+
--
52+
-- >>> ./Demo -i1 --ints 2 --street=Zandlust --name Doaitse -i3 --ints=4 --city=Tynaarlo
53+
-- --name [Char] optional Name
54+
-- --ints Int recurring A couple of numbers
55+
-- Choose at least one from(
56+
-- --agda required In case you prefer Agda
57+
-- --haskell required In case you prefer Haskell
58+
-- )
59+
-- --city [Char] optional Home city
60+
-- --street [Char] optional Home Street
61+
-- --
62+
-- -- Correcting steps:
63+
-- -- Inserted "-a" at position 70 expecting one of ["--agda", "--agda=", "--haskell", "--haskell=", "--ints=", "--ints", "-i", "-h", "-a"]
64+
-- -- Inserted "\EOT" at position 70 expecting "\EOT"
65+
66+
67+
main ::IO ()
68+
main = do args <- getArgs
69+
case run defaults oName (concat (map (++ "\EOT") args)) of
70+
Left a -> case a of
71+
Succes v -> print v
72+
Help t -> putStrLn t
73+
Right errors -> putStrLn errors
74+
75+
-- | The function `demo` can be used from within ghci:
76+
-}
77+
78+
-- >>> demo ["-i2", "--street=Zandlust", "--ints=5", "-nAtze", "--city=Houten", "--agda", "-i3"]
79+
-- Name {name_ = "Atze", prefers_ = Agda, ints_ = [2,5,3], address_ = Address {city_ = "Houten", street_ = "Zandlust"}}
80+
81+
demo :: [[Char]] -> IO ()
82+
demo args = case run defaults oName (concat (map (++ "\EOT") args)) of
83+
Left a -> case a of
84+
Succes v -> print v
85+
Help t -> putStrLn t
86+
Right errors -> putStr errors

src/Options/UU/Interleaved.hs

+213
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,213 @@
1+
{-# LANGUAGE NoMonomorphismRestriction,
2+
FlexibleInstances,
3+
ScopedTypeVariables,
4+
RankNTypes,
5+
FlexibleContexts,
6+
CPP,
7+
TemplateHaskell #-}
8+
9+
module Options.UU.Interleaved (
10+
required,
11+
option,
12+
options,
13+
optionsl,
14+
optionsf,
15+
flag,
16+
flags,
17+
field,
18+
choose,
19+
change,
20+
ShowParserType (..),
21+
pString,
22+
pBool,
23+
run,
24+
OptionResult (..)
25+
26+
) where
27+
import Data.Functor.Identity
28+
import Control.Applicative.Interleaved
29+
import Control.Monad.State.Class
30+
import Control.Monad.Trans.State.Lazy
31+
import Text.ParserCombinators.UU -- hiding (pSymbol)
32+
import Text.ParserCombinators.UU.BasicInstances
33+
import Text.ParserCombinators.UU.Utils hiding (lexeme, pSymbol)
34+
import Data.Lenses
35+
import Data.Lenses.Template
36+
37+
-- For a description of how to use these combinators see the accompanying Demo module.
38+
-- Further information can be found in a Technical report at http://www.cs.uu.nl/research/techreps/UU-CS-2013-005.html
39+
40+
instance Splittable (P (Str Char String Int)) where
41+
getPure = getZeroP
42+
getNonPure = getOneP
43+
44+
{-
45+
pSymbol :: String -> p (Str Char String Int) String
46+
pSymbol [] = pure []
47+
pSymbol (s:ss) = (:) <$> pSym s <*> pSymbol ss
48+
-}
49+
50+
type OptionParser a = P (Str Char String Int) a
51+
52+
type Option a = Gram (P (Str Char String Int)) a
53+
54+
type BaseEntry s d = forall m r b. MonadState s m =>
55+
(m () -> StateT r Identity b)
56+
-> d
57+
-> (Gram (P (Str Char String Int)) (r -> r), [Char])
58+
59+
type Entry s a = ShowParserType a => BaseEntry s ([Char], P (Str Char String Int) a, String)
60+
61+
type EntryVal s a = ShowParserType a => BaseEntry s ([Char], a, String)
62+
63+
type EntryVals s a = ShowParserType a => BaseEntry s [([Char], a, String)]
64+
65+
class ShowParserType a where
66+
showType :: OptionParser a -> String
67+
68+
instance ShowParserType a => ShowParserType [a] where
69+
showType (p :: OptionParser [a]) = let q :: OptionParser a = undefined
70+
in "[" ++ showType q ++ "]"
71+
72+
instance ShowParserType Int where
73+
showType p = "Int"
74+
75+
instance ShowParserType Char where
76+
showType p = "Char"
77+
78+
--instance ShowParserType String where
79+
-- showType p = "String"
80+
81+
instance ShowParserType Bool where
82+
showType p = "Bool"
83+
84+
data OptionResult a = Succes a
85+
| Help String
86+
87+
88+
lexeme p = p <* pToken "\EOT"
89+
90+
pString = pMunch (/='\EOT')
91+
pBool = True <$ pToken "True" <|> False <$ pToken "False"
92+
93+
oG p a = mkG ((a `alter`) <$> p)
94+
95+
96+
required_ :: (MonadState a m)
97+
=> (m () -> StateT r Identity b)
98+
-> ( [Char]
99+
, OptionParser (a -> a)
100+
, String
101+
, String
102+
, String
103+
)
104+
-> (Gram (P (Str Char String Int)) (r -> r), [Char])
105+
106+
{-
107+
required_ a (string, p, tp, kind, info)
108+
= let align n t = take n (t++repeat ' ')
109+
(p', tp') = case ( getNonPure p, getPure p) of
110+
(Nothing, Just pe) -> (const pe <$> pToken "\EOT", "")
111+
(Just pne, Nothing) -> ((pToken "\EOT" <|> pure "") *> lexeme pne, tp)
112+
(Just pne, Just pe) -> error "An option can not be both empty and non-empty"
113+
(Nothing, Nothing) -> error "An option should return a value"
114+
in ( oG ( pToken ("-" ++ [head string]) *> p') a
115+
<|> oG ( pToken ("--" ++ string) *> p') a
116+
<|> oG ( pToken ("--" ++ string ++ "=") *> p') a
117+
, "--"++ align 15 string ++ align 15 tp' ++ align 10 kind ++ info ++"\n"
118+
)
119+
-}
120+
121+
required_ a (string, p, tp, kind, info)
122+
= let align n t = take n (t++repeat ' ')
123+
p' = case ( getNonPure p, getPure p) of
124+
(Nothing, Just pe) -> const pe <$> pToken "\EOT"
125+
(Just pne, Nothing) -> (pToken "\EOT" <|> pure "") *> lexeme pne
126+
(Just pne, Just pe) -> error "An option can not be both empty and non-empty"
127+
(Nothing, Nothing) -> error "An option should return a value"
128+
in ( oG (( pToken ("-" ++ [head string])
129+
<|> pToken ("--" ++ string) ) *> (pToken "=" `opt` "") *> noDash *> p') a
130+
, "--"++ align 15 string ++ align 15 tp ++ align 10 kind ++ info ++"\n"
131+
)
132+
133+
noDash = pure "" -- needs further work
134+
135+
-- | a `required` entry specied an entry which has to be provided; in the record containing the default values one may put `undefined`
136+
required :: Entry a a
137+
138+
required a (string, p, info) = required_ a (string, const <$> p, showType p, "required", info)
139+
140+
-- | an `option` entry specied an entry which may be provided; if absent the default value is taken
141+
142+
option :: Entry a a
143+
option a (string, p, i) = let (r, t) = required_ a (string, const <$> p, showType p, "optional", i)
144+
in (r <|> pure id, t)
145+
146+
-- | An `options` entry specifies an element which may occur more than once. The final value contains the list of all the values encountered.
147+
options :: Entry [a] a
148+
options a (string, p, i) = let (pars, text) = required_ a ( string
149+
, (:) <$> p
150+
, showType p
151+
, "recurring"
152+
, i)
153+
in (let pm = (.) <$> pars <*> pm <|> pure id in pm, text)
154+
155+
-- | An `optionl` entry specifies an element which may occur more than once. The last one encountered is taken
156+
-- optionsl :: Entry a a
157+
optionsl a (string, p, i) = let (pars, t) = options a (string, p, i ++"last one is taken") in ( (const. last .($[])) <$> pars, t)
158+
159+
160+
-- | An `optionf` entry specifies an element which may occur more than once. The first one encountered is taken
161+
-- optionsf :: Entry a a
162+
optionsf a (string, p, i) = let (pars, t) = options a (string, p, i ++"first one is taken") in ( (head .) <$> pars, t)
163+
164+
-- | A `flag` entry sets a field to a specific value when encountered
165+
flag :: EntryVal a a
166+
flag a (string, v,i) = option a (string, pure v, i)
167+
168+
-- | A `flags` entry introduces a list of possible parameters, each with a value to which the field should be set
169+
flags :: EntryVals a a
170+
flags a table = foldr (<>) (pure id, "") (map (flag a) table)
171+
172+
-- | A `set` entry introduces a required entry, which sets a spcific value; it is used in `choose` and probably not very useful by itself.
173+
set :: EntryVal a a
174+
set a (string, v,i) = required_ a (string, pure (const v), "", "required", i)
175+
176+
-- | A `choose` entry introduces a list of choices for the specific entry; precisely one should be given
177+
choose :: EntryVals a a
178+
choose a table = let (ps, ts) = unzip (map (set a) table)
179+
in (foldr (<|>) empty ps, "-- choose at least one from \n" ++ concat (map (" "++) ts))
180+
181+
-- | A `change` entry is an optional `choose` entry
182+
change :: EntryVals a a
183+
change a table = let (ps, ts) = unzip (map (set a) table)
184+
in (foldr (<|>) (pure id) ps, "You may choose one from(\n" ++ concat ts ++ ")\n")
185+
186+
187+
188+
-- | A `field` entry introduces a collection of options which are used to set fields in a sub-record of the main record
189+
field
190+
:: (Functor f, Control.Monad.State.Class.MonadState a m) =>
191+
(m ()
192+
-> Control.Monad.Trans.State.Lazy.StateT
193+
r Data.Functor.Identity.Identity b)
194+
-> (f (a -> a), t) -> (f (r -> r), t)
195+
field s opts = let (p, t) = opts in ((s `alter`) <$> p, t)
196+
197+
-- | The function `run` equips the given option specification with an option to ask for @--help@. It concatenates the files coming from the command line and terminates them with an EOT.
198+
-- Make sure your command line arguments do not contain an EOT. It parses the command line arguments and updates the `default` record passed to it
199+
run ::
200+
a -- ^ the record containing the default values
201+
-> (Gram (P (Str Char String Int)) (a -> a), String) -- ^ the specification of the various options
202+
-> String -- ^ The string containing the given options, separated by EOT
203+
-> Either (OptionResult a) [Char] -- ^ The result is either an updated record (`Succes`) with options or a request for `Help`. In case of erroneous input an error message is returned.
204+
205+
run defaults (p, t) inp = do let r@(a, errors) = parse ((,) <$> ( Succes <$> (mkP p <*> pure defaults)
206+
<|> Help t <$ pToken "--help\EOT"
207+
)
208+
<*> pEnd
209+
) (createStr 0 inp)
210+
if null errors then Left a
211+
else Right (t ++ concat (map (++"\n") ("\n-- Correcting steps:": map show errors)))
212+
213+

uu-options.cabal

+33
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
Name: uu-options
2+
Version: 0.2.0.0
3+
Build-Type: Simple
4+
License: MIT
5+
Copyright: S Doaitse Swierstra
6+
License-file: LICENSE
7+
Author: Doaitse Swierstra, Utrecht University
8+
Maintainer: Doaitse Swierstra
9+
Stability: experimental
10+
Homepage: http://www.cs.uu.nl/wiki/bin/view/HUT/ParserCombinators
11+
Bug-reports: mailto:[email protected]
12+
Synopsis: Parse command line options using uu-interleave and uu-parsinglib
13+
Description: Using the new Control.Applicative.Interleaved module we use the uu-parsinglib library to construct extremely concise command line processors, which provide
14+
helpful information when called incorrectly.
15+
.
16+
The module contains a module `Options.UU.Demo` which serves as an example of how to use the various options of the module; you may take a look at the source code.
17+
.
18+
It also contains a function demo which may be called from within ghci to experiment with.
19+
.
20+
Background information can be found in a Technical Report at <http://www.cs.uu.nl/research/techreps/UU-CS-2013-005.html>
21+
Category: Options
22+
23+
cabal-version: >= 1.6
24+
25+
source-repository head
26+
type: svn
27+
location: https://svn.science.uu.nl/repos/project.STEC.uu-parsinglib/uu-options
28+
29+
Library
30+
hs-source-dirs: src
31+
Build-Depends: base >= 4.2 && <5, uu-parsinglib >=2.8 && < 3.0, uu-interleaved >=0.1.0 && < 0.3, transformers >= 0.3.0.0, mtl, template-haskell, lenses >= 0.1.7
32+
Exposed-modules: Options.UU.Interleaved, Options.UU.Demo
33+

0 commit comments

Comments
 (0)