1 -- This file is part of Diohsc
2 -- Copyright (C) 2020 Martin Bays <mbays@sdf.org>
4 -- This program is free software: you can redistribute it and/or modify
5 -- it under the terms of version 3 of the GNU General Public License as
6 -- published by the Free Software Foundation, or any later version.
8 -- You should have received a copy of the GNU General Public License
9 -- along with this program. If not, see http://www.gnu.org/licenses/.
11 {-# LANGUAGE LambdaCase #-}
24 import Control
.Monad
(guard, liftM2, mzero
)
25 import Data
.Bifunctor
(first
)
26 import Data
.Char (isAlphaNum)
27 import Data
.List
(foldl')
28 import Data
.Maybe (maybeToList)
29 import Safe
(atMay
, headMay
)
31 import Text
.Parsec
.String (Parser
)
36 deriving (Eq
,Ord
,Show)
39 = EsSElemSpec ElemSpec
40 | EsSRange
(Maybe ElemSpec
) (Maybe ElemSpec
)
42 deriving (Eq
,Ord
,Show)
44 type ElemsSpecs
= [ElemsSpec
]
46 elemsSpecsNum
:: Int -> ElemsSpecs
47 elemsSpecsNum
= (:[]) . EsSElemSpec
. ESNum
49 resolveElemsSpecs
:: String -> (a
-> String -> Bool) -> [a
] -> ElemsSpecs
-> Either String [a
]
50 resolveElemsSpecs typeStr match
as ess
=
51 concat <$> mapM resolveElemsSpec ess
>>= \case
52 [] -> Left
$ "No such " <> typeStr
55 resolveElemsSpec
(EsSElemSpec es
) =
56 maybeToList . atMay
as <$> resolveES es
57 resolveElemsSpec
(EsSRange mes1 mes2
) = do
58 mns1
<- mapM resolveES mes1
59 mns2
<- mapM resolveES mes2
60 return $ case liftM2 (>) mns1 mns2
of
61 Just
True -> reverse . maybe id drop mns2
$ maybe id (take . (+ 1)) mns1
as
62 _
-> maybe id drop mns1
$ maybe id (take . (+ 1)) mns2
as
63 resolveElemsSpec
(EsSSearch s
) = return $ filter (`match` s
) as
65 resolveES
:: ElemSpec
-> Either String Int
66 resolveES
(ESNum n
) = return $ n
- 1
67 resolveES
(ESSearch s
) =
68 maybe (Left
$ "No " <> typeStr
<> " matches pattern: " ++ s
) Right
$
69 headMay
. map fst . filter snd . zip [0..] $ (`match` s
) <$> as
76 | PTargetLog ElemsSpecs
77 | PTargetQueue ElemsSpecs
78 | PTargetAncestors PTarget ElemsSpecs
79 | PTargetDescendants PTarget ElemsSpecs
81 { ptcIncreasing
:: Bool, ptcNoVisited
:: Bool
82 , ptcTarget
:: PTarget
, ptcSpecs
:: ElemsSpecs
}
84 { ptlNoVisited
:: Bool
85 , ptlTarget
:: PTarget
, ptlSpecs
:: ElemsSpecs
}
86 | PTargetRef PTarget
String
87 deriving (Eq
,Ord
,Show)
89 data CommandArg
= CommandArg
90 { commandArgArg
:: String
91 , commandArgLiteralTail
:: String
92 } deriving (Eq
,Ord
,Show)
95 = CommandLine
(Maybe PTarget
) (Maybe (String,[CommandArg
]))
96 deriving (Eq
,Ord
,Show)
98 parseCommandLine
:: String -> Either String CommandLine
99 parseCommandLine
= first
show . parse
(spaces
>> commandLine
<* eof
) ""
101 parseCommand
:: String -> Either String String
102 parseCommand
= first
show . parse command
""
104 commandLine
:: Parser CommandLine
106 [ char
'#' >> many anyChar
>> return (CommandLine Nothing Nothing
)
107 , liftM2 CommandLine
(optionMaybe target
) . optionMaybe
$ spaces
>> commandAndArgs
110 commandAndArgs
:: Parser
(String, [CommandArg
])
112 liftM2 (,) command
$ spaces
>> commandArgs
114 commandArgs
:: Parser
[CommandArg
]
115 commandArgs
= liftM2 (flip CommandArg
)
116 (lookAhead
$ many1 anyChar
) commandArg `sepEndBy` spaces
118 command
:: Parser
String
123 commandArg
:: Parser
String
124 commandArg
= escapedArg
126 escapedArg
:: Parser
String
127 escapedArg
= escapedWhile
$ noneOf
" "
129 escapedWhile
:: Parser
Char -> Parser
String
130 escapedWhile c
= many1
$ (char
'\\' >> anyChar
) <|
> c
133 nat
= read <$> many1 digit
135 countMany
:: Parser
String -> Parser
Int
136 countMany s
= (s
>> (+ 1) <$> countMany s
) <|
> return 0
138 patt
:: Parser
String
139 patt
= escapedWhile
(noneOf
"^ ") <* optional
(char
'^
')
141 elemSpec
:: Parser ElemSpec
144 , char
'^
' >> ESSearch
<$> patt
]
146 elemsSpec
:: Parser ElemsSpec
148 [ try $ string "^^" >> EsSSearch
<$> patt
150 mess1
<- optionMaybe elemSpec
152 [ char
'-' >> EsSRange mess1
<$> optionMaybe elemSpec
153 , maybe mzero
(return . EsSElemSpec
) mess1
]
156 elemsSpecs
:: Parser ElemsSpecs
157 elemsSpecs
= sepBy1 elemsSpec
(char
',')
159 elemsSpecsBy
:: Parser
String -> Parser ElemsSpecs
160 elemsSpecsBy s
= s
>> choice
162 , elemsSpecsNum
. (+ 1) <$> countMany s
]
164 escapedArgStartingWith
:: Parser
Char -> Parser
String
165 escapedArgStartingWith p
= liftM2 (:) p
(escapedArg
<|
> return "")
167 ref
:: String -> Parser
String
168 ref
= escapedArgStartingWith
. oneOf
170 baseTarget
:: Parser PTarget
172 [ PTargetLinks
False PTargetCurr
<$> elemsSpecs
173 , PTargetRef PTargetCurr
<$> ref
"./?"
174 , PTargetLog
<$> elemsSpecsBy
(string "$")
175 , PTargetQueue
<$> elemsSpecsBy
(string "~")
176 , char
'\'' >> choice
177 [ char
'\'' >> return PTargetJumpBack
178 , PTargetMark
<$> many1 alphaNum
]
180 s
<- escapedArgStartingWith alphaNum
-- scheme required for other uris
181 guard . not $ all (\c
-> c `
elem`
"!|" ||
isAlphaNum c
) s
182 return $ PTargetAbs s
183 , return PTargetCurr
]
185 targetMod
:: Parser
(PTarget
-> PTarget
)
187 [ flip PTargetAncestors
<$> elemsSpecsBy
(string "<")
188 , flip PTargetDescendants
<$> elemsSpecsBy
(string ">")
189 , flip (PTargetChild
True False) <$> elemsSpecsBy
(string "]")
190 , flip (PTargetChild
True True) <$> elemsSpecsBy
(string "}")
191 , flip (PTargetChild
False False) <$> elemsSpecsBy
(string "[")
192 , flip (PTargetChild
False True) <$> elemsSpecsBy
(string "{")
193 , optional
(char
'_
') >> flip (PTargetLinks
False) <$> elemsSpecs
194 , char
'*' >> flip (PTargetLinks
True) <$> elemsSpecs
195 , flip PTargetRef
<$> ref
"/?"
198 target
:: Parser PTarget
201 mods
<- many targetMod
202 guard . not $ base
== PTargetCurr
&& null mods
203 return $ foldl' (flip ($)) base mods