0 | module IotaTime.Pattern
3 | import Data.String.Parser
11 | = UnexpectedEnd Integer String
12 | | UnexpectedCharacter Integer String Char
13 | | InvalidNumber Integer String
14 | | ValueOutOfRange String Integer Integer Integer
15 | | TrailingInput Integer String
16 | | InvalidValue String
19 | PatternParser : Type -> Type
20 | PatternParser = Parser.Parser
27 | record PatternRep state value where
28 | constructor MkPattern
29 | initialState : state
30 | finish : state -> Either PatternError value
31 | parsePart : PatternParser (Either PatternError (state -> state))
32 | formatPart : value -> String
36 | Pattern : Type -> Type -> Type
37 | Pattern = PatternRep
40 | patternInitialState : Pattern state value -> state
41 | patternInitialState = initialState
44 | patternFinish : Pattern state value -> state -> Either PatternError value
45 | patternFinish = finish
48 | patternParsePart : Pattern state value ->
49 | PatternParser (Either PatternError (state -> state))
50 | patternParsePart = parsePart
53 | patternFormatPart : Pattern state value -> value -> String
54 | patternFormatPart = formatPart
58 | record LiteralPatternRep where
59 | constructor MkLiteralPattern
60 | literalText : String
64 | LiteralPattern : Type
65 | LiteralPattern = LiteralPatternRep
68 | string : String -> LiteralPattern
69 | string = MkLiteralPattern
72 | char : Char -> LiteralPattern
73 | char value = MkLiteralPattern (pack [value])
76 | literalField : Pattern state value -> String -> Pattern state value
77 | literalField template text = MkPattern
78 | template.initialState
81 | ignore (Parser.string text)
85 | appendLiteral : Pattern state value -> LiteralPattern -> Pattern state value
86 | appendLiteral pattern literal = MkPattern
87 | pattern.initialState
90 | result <- pattern.parsePart
92 | Left error => pure (Left error)
94 | ignore (Parser.string literal.literalText)
95 | pure (Right update))
96 | (\value => pattern.formatPart value ++ literal.literalText)
101 | (<%) : Pattern state value -> LiteralPattern -> Pattern state value
102 | (<%) = appendLiteral
105 | Semigroup (Pattern state value) where
106 | left <+> right = MkPattern
110 | resultLeft <- left.parsePart
112 | Left error => pure (Left error)
113 | Right updateLeft => do
114 | resultRight <- right.parsePart
115 | pure (map (\updateRight => updateRight . updateLeft) resultRight))
116 | (\value => left.formatPart value ++ right.formatPart value)
118 | pairUpdate : (leftState -> leftState) -> (rightState -> rightState) ->
119 | (leftState, rightState) -> (leftState, rightState)
120 | pairUpdate updateLeft updateRight (leftState, rightState) =
121 | (updateLeft leftState, updateRight rightState)
124 | pairPattern : (combined -> left) -> (combined -> right) ->
125 | (left -> right -> combined) ->
126 | Pattern leftState left -> Pattern rightState right ->
127 | Pattern (leftState, rightState) combined
128 | pairPattern leftOf rightOf combine left right = MkPattern
129 | (left.initialState, right.initialState)
130 | (\(leftState, rightState) => do
131 | leftValue <- left.finish leftState
132 | rightValue <- right.finish rightState
133 | Right (combine leftValue rightValue))
135 | parsedLeft <- left.parsePart
137 | Left error => pure (Left error)
138 | Right updateLeft => do
139 | parsedRight <- right.parsePart
140 | pure (map (pairUpdate updateLeft) parsedRight))
141 | (\value => left.formatPart (leftOf value) ++ right.formatPart (rightOf value))
145 | format : Pattern state value -> value -> String
146 | format pattern = pattern.formatPart
148 | structuralError : String -> Int -> String -> Either PatternError value
149 | structuralError source position expected =
150 | if position >= strLength source
151 | then Left (UnexpectedEnd (cast position) expected)
152 | else case unpack (strSubstr position 1 source) of
153 | actual :: _ => Left (UnexpectedCharacter (cast position) expected actual)
154 | [] => Left (UnexpectedEnd (cast position) expected)
161 | parseWith : Pattern state value -> state -> String -> Either PatternError value
162 | parseWith pattern start source =
163 | let initial = Parser.S source 0 (strLength source)
164 | in case runIdentity (pattern.parsePart.runParser initial) of
165 | Parser.Fail position expected => structuralError source position expected
166 | Parser.OK result final => case result of
167 | Left error => Left error
168 | Right update => if final.pos == final.maxPos
169 | then pattern.finish (update start)
170 | else Left (TrailingInput (cast final.pos)
171 | (strSubstr final.pos (final.maxPos - final.pos) source))
175 | parse : Pattern state value -> String -> Either PatternError value
176 | parse pattern = parseWith pattern pattern.initialState
178 | patternDigitValue : Char -> Integer
179 | patternDigitValue value = cast value - cast '0'
181 | readUnsignedInteger : Integer -> Nat -> List Char ->
182 | Maybe (Integer, Nat)
183 | readUnsignedInteger found count (value :: remaining) =
185 | then readUnsignedInteger
186 | (found * 10 + patternDigitValue value) (S count) remaining
187 | else if count == 0 then Nothing else Just (found, count)
188 | readUnsignedInteger found count [] =
189 | if count == 0 then Nothing else Just (found, count)
191 | signedIntegerParser : PatternParser (Either PatternError (Integer -> Integer))
192 | signedIntegerParser = Parser.P (\state =>
193 | let remaining = unpack
194 | (strSubstr state.pos (state.maxPos - state.pos) state.input)
195 | (negative, signWidth, digits) = case remaining of
196 | '-' :: rest => (True, 1, rest)
197 | rest => (False, 0, rest)
198 | in case readUnsignedInteger 0 0 digits of
199 | Nothing => pure (Parser.Fail state.pos "signed integer")
200 | Just (magnitude, digitWidth) =>
201 | let consumed = signWidth + digitWidth
202 | value = if negative then negate magnitude else magnitude
203 | in pure (Parser.OK (Right (const value))
204 | ({ pos := state.pos + cast consumed } state)))
209 | pSignedInteger : Pattern Integer Integer
210 | pSignedInteger = MkPattern 0 Right signedIntegerParser show
212 | decimalDigit : PatternParser Char
213 | decimalDigit = Parser.satisfy (\value => value >= '0' && value <= '9')
216 | fixedDigits : Nat -> PatternParser (List Char)
217 | fixedDigits Z = pure []
218 | fixedDigits (S width) = [| decimalDigit :: fixedDigits width |]
220 | upToDigits : Nat -> PatternParser (List Char)
221 | upToDigits Z = pure []
222 | upToDigits (S maximum) = do
223 | next <- Parser.optional decimalDigit
226 | Just digit => map (digit ::) (upToDigits maximum)
228 | variableDigits : Nat -> PatternParser (List Char)
229 | variableDigits Z = Parser.fail "digit"
230 | variableDigits (S maximum) = [| decimalDigit :: upToDigits maximum |]
232 | currentPosition : PatternParser Int
233 | currentPosition = Parser.P (\state => pure (Parser.OK state.pos state))
235 | readDigits : List Char -> Integer
236 | readDigits = foldl (\value, digit => value * 10 + cast digit - cast '0') 0
238 | numberPart : (width : Nat) -> (maximumWidth : Nat) ->
239 | (minimum : Integer) -> (maximum : Integer) ->
240 | PatternParser (Either PatternError Integer)
241 | numberPart width maximumWidth minimum maximum = do
242 | position <- currentPosition
243 | digits <- if width <= 1
244 | then variableDigits maximumWidth
245 | else fixedDigits width
246 | let value = readDigits digits
247 | pure (if value >= minimum && value <= maximum
249 | else Left (ValueOutOfRange (pack digits) minimum maximum (cast position)))
252 | numberUpdatePart : (Integer -> state -> state) ->
253 | (width : Nat) -> (maximumWidth : Nat) ->
254 | (minimum : Integer) -> (maximum : Integer) ->
255 | PatternParser (Either PatternError (state -> state))
256 | numberUpdatePart setter width maximumWidth minimum maximum =
257 | map (map setter) (numberPart width maximumWidth minimum maximum)
259 | caseInsensitive : String -> PatternParser ()
260 | caseInsensitive value = consume (unpack value)
262 | consume : List Char -> PatternParser ()
263 | consume [] = pure ()
264 | consume (expected :: rest) = do
265 | ignore (Parser.satisfy
266 | (\actual => Prelude.toLower actual == Prelude.toLower expected))
269 | namedChoice : List (String, field) -> PatternParser field
270 | namedChoice choices = choose (sortBy longerFirst (filter nonEmpty choices))
272 | nonEmpty : (String, field) -> Bool
273 | nonEmpty (name, _) = name /= ""
275 | longerFirst : (String, field) -> (String, field) -> Ordering
276 | longerFirst (left, _) (right, _) =
277 | compare (length (unpack right)) (length (unpack left))
279 | choose : List (String, field) -> PatternParser field
280 | choose [] = Parser.fail "named field"
281 | choose ((name, value) :: rest) =
282 | (caseInsensitive name *> pure value) <|> choose rest
285 | namedUpdatePart : List (String, field) -> (field -> state -> state) ->
286 | PatternParser (Either PatternError (state -> state))
287 | namedUpdatePart choices setter = map (Right . setter) (namedChoice choices)
290 | namedConsumePart : List String ->
291 | PatternParser (Either PatternError (state -> state))
292 | namedConsumePart names = map (const (Right id))
293 | (namedChoice (map (\name => (name, ())) names))
296 | spaceNumberUpdatePart : (Integer -> state -> state) ->
297 | (maximumWidth : Nat) ->
298 | (minimum : Integer) -> (maximum : Integer) ->
299 | PatternParser (Either PatternError (state -> state))
300 | spaceNumberUpdatePart setter maximumWidth minimum maximum = do
301 | ignore (Parser.optional (Parser.char ' '))
302 | map (map setter) (numberPart 1 maximumWidth minimum maximum)