0 | module Text.YAML.Node
3 | import Derive.Prelude
4 | import Text.YAML.Types
7 | %language ElabReflection
19 | data STag : Type where
27 | TOther : String -> STag
29 | %runElab derive "STag" [Show,Eq]
33 | tagURI : STag -> String
34 | tagURI TNull = "tag:yaml.org,2002:null"
35 | tagURI TBool = "tag:yaml.org,2002:bool"
36 | tagURI TInt = "tag:yaml.org,2002:int"
37 | tagURI TFloat = "tag:yaml.org,2002:float"
38 | tagURI TStr = "tag:yaml.org,2002:str"
39 | tagURI TSeq = "tag:yaml.org,2002:seq"
40 | tagURI TMap = "tag:yaml.org,2002:map"
41 | tagURI (TOther s) = s
46 | fromURI : String -> STag
47 | fromURI "tag:yaml.org,2002:null" = TNull
48 | fromURI "tag:yaml.org,2002:bool" = TBool
49 | fromURI "tag:yaml.org,2002:int" = TInt
50 | fromURI "tag:yaml.org,2002:float" = TFloat
51 | fromURI "tag:yaml.org,2002:str" = TStr
52 | fromURI "tag:yaml.org,2002:seq" = TSeq
53 | fromURI "tag:yaml.org,2002:map" = TMap
54 | fromURI s = TOther s
66 | data Node : Type where
67 | NScalar : (tag : STag) -> (style : Style) -> (text : String) -> Node
68 | NSeq : (tag : STag) -> List Node -> Node
69 | NMap : (tag : STag) -> List (Node, Node) -> Node
71 | scText : SnocList String -> String -> SnocList String
72 | scText ss s = ss :< show s
75 | showNode : SnocList String -> Node -> SnocList String
76 | showNode ss (NScalar _ _ s) = scText ss s
77 | showNode ss (NSeq _ []) = ss :< "[]"
78 | showNode ss (NSeq _ (n :: ns)) = showNodes (showNode (ss :< "[") n) ns
79 | showNode ss (NMap _ []) = ss :< "{}"
80 | showNode ss (NMap _ (p :: ps)) = showPairs (showPair (ss :< "{") p) ps
82 | showPair : SnocList String -> (Node, Node) -> SnocList String
83 | showPair ss (k, v) = showNode (showNode ss k :< ": ") v
85 | showNodes : SnocList String -> List Node -> SnocList String
86 | showNodes ss [] = ss :< "]"
87 | showNodes ss (n :: ns) = showNodes (showNode (ss :< ", ") n) ns
89 | showPairs : SnocList String -> List (Node, Node) -> SnocList String
90 | showPairs ss [] = ss :< "}"
91 | showPairs ss (p :: ps) = showPairs (showPair (ss :< ", ") p) ps
95 | canon : Node -> String
96 | canon n = fastConcat (showNode [<] n <>> [])
109 | data Schema : Type where
117 | %runElab derive "Schema" [Show,Eq]
120 | all1 : (Char -> Bool) -> List Char -> Bool
122 | all1 f cs = all f cs
125 | isIntCs : List Char -> Bool
126 | isIntCs ('0' :: 'x' :: cs) = all1 isHexDigit cs
127 | isIntCs ('0' :: 'o' :: cs) = all1 isOctDigit cs
128 | isIntCs ('+' :: cs) = all1 isDigit cs
129 | isIntCs ('-' :: cs) = all1 isDigit cs
130 | isIntCs cs = all1 isDigit cs
133 | expPart : List Char -> Bool
135 | expPart (e :: '+' :: cs) = (e == 'e' || e == 'E') && all1 isDigit cs
136 | expPart (e :: '-' :: cs) = (e == 'e' || e == 'E') && all1 isDigit cs
137 | expPart (e :: cs) = (e == 'e' || e == 'E') && all1 isDigit cs
140 | mantissa : List Char -> Maybe (List Char)
141 | mantissa ('.' :: cs) = case span isDigit cs of
144 | mantissa cs = case span isDigit cs of
146 | (_, '.' :: r) => Just (snd $
span isDigit r)
150 | isFloatCs : List Char -> Bool
151 | isFloatCs ('+' :: cs) = maybe False expPart (mantissa cs)
152 | isFloatCs ('-' :: cs) = maybe False expPart (mantissa cs)
153 | isFloatCs cs = maybe False expPart (mantissa cs)
155 | nullWords, boolWords, infWords, nanWords : List String
156 | trueWords, falseWords, posInfWords, negInfWords : List String
157 | trueWords = ["true", "True", "TRUE"]
158 | falseWords = ["false", "False", "FALSE"]
159 | posInfWords = ["+.inf", ".inf", "+.Inf", ".Inf", "+.INF", ".INF"]
160 | negInfWords = ["-.inf", "-.Inf", "-.INF"]
161 | nullWords = ["", "~", "null", "Null", "NULL"]
162 | boolWords = ["true", "True", "TRUE", "false", "False", "FALSE"]
164 | [ ".inf", "+.inf", "-.inf", ".Inf", "+.Inf", "-.Inf"
165 | , ".INF", "+.INF", "-.INF"
167 | nanWords = [".nan", ".NaN", ".NAN"]
172 | resolvePlain : String -> STag
174 | if s `elem` nullWords then TNull
175 | else if s `elem` boolWords then TBool
176 | else if (s `elem` infWords) || (s `elem` nanWords) then TFloat
179 | in if isIntCs cs then TInt
180 | else if isFloatCs cs then TFloat
187 | scalarTag : Schema -> Tag -> Style -> String -> STag
188 | scalarTag _ (Verbatim t) _ _ = fromURI t
189 | scalarTag _ NonSpec _ _ = TStr
190 | scalarTag Core NoTag Plain s = resolvePlain s
191 | scalarTag _ NoTag _ _ = TStr
196 | collTag : Schema -> Tag -> (dflt : STag) -> STag
197 | collTag _ (Verbatim t) _ = fromURI t
198 | collTag _ _ dflt = dflt
204 | public export %inline
205 | nodeTag : Node -> STag
206 | nodeTag (NScalar t _ _) = t
207 | nodeTag (NSeq t _) = t
208 | nodeTag (NMap t _) = t
210 | public export %inline
211 | isNull : Node -> Bool
212 | isNull n = nodeTag n == TNull
216 | asText : Node -> Maybe String
217 | asText (NScalar _ _ s) = Just s
222 | asString : Node -> Maybe String
223 | asString (NScalar TStr _ s) = Just s
224 | asString _ = Nothing
228 | asBool : Node -> Maybe Bool
229 | asBool (NScalar TBool _ s) =
230 | if s `elem` trueWords then Just True
231 | else if s `elem` falseWords then Just False
235 | digits : (base : Integer) -> (Char -> Maybe Integer) -> List Char -> Maybe Integer
236 | digits base f [] = Nothing
237 | digits base f cs = go 0 cs
239 | go : Integer -> List Char -> Maybe Integer
240 | go acc [] = Just acc
241 | go acc (c :: t) = case f c of
242 | Just d => go (acc * base + d) t
245 | decDigit, octDigit', hexDigit' : Char -> Maybe Integer
246 | decDigit c = if isDigit c then Just (cast (ord c - 0x30)) else Nothing
247 | octDigit' c = if isOctDigit c then Just (cast (ord c - 0x30)) else Nothing
249 | if isDigit c then Just (cast (ord c - 0x30))
250 | else if c >= 'a' && c <= 'f' then Just (cast (ord c - 0x57))
251 | else if c >= 'A' && c <= 'F' then Just (cast (ord c - 0x37))
254 | intVal : List Char -> Maybe Integer
255 | intVal ('0' :: 'x' :: cs) = digits 16 hexDigit' cs
256 | intVal ('0' :: 'o' :: cs) = digits 8 octDigit' cs
257 | intVal ('+' :: cs) = digits 10 decDigit cs
258 | intVal ('-' :: cs) = negate <$> digits 10 decDigit cs
259 | intVal cs = digits 10 decDigit cs
264 | asInteger : Node -> Maybe Integer
265 | asInteger (NScalar TInt _ s) = intVal (unpack s)
266 | asInteger _ = Nothing
271 | floatParts : List Char -> Maybe (List Char, List Char, Integer)
273 | let (ip, r1) := span isDigit cs
274 | (fp, r2) := case r1 of
275 | '.' :: t => span isDigit t
277 | in case (ip, fp) of
278 | ([], []) => Nothing
279 | _ => (\e => (ip, fp, e)) <$> expVal r2
282 | expVal : List Char -> Maybe Integer
285 | if e == 'e' || e == 'E'
287 | '+' :: ds => digits 10 decDigit ds
288 | '-' :: ds => negate <$> digits 10 decDigit ds
289 | ds => digits 10 decDigit ds
295 | dblVal : String -> Maybe Double
297 | if s `elem` posInfWords
298 | then Just (1.0 / 0.0)
299 | else if s `elem` negInfWords
300 | then Just (-1.0 / 0.0)
301 | else if s `elem` nanWords
302 | then Just (0.0 / 0.0)
303 | else case unpack s of
304 | '+' :: cs => build "" cs
305 | '-' :: cs => build "-" cs
309 | orZero : List Char -> String
311 | orZero cs = pack cs
313 | build : String -> List Char -> Maybe Double
315 | (\(ip, fp, e) => cast "\{sign}\{orZero ip}.\{orZero fp}e\{show e}")
320 | asDouble : Node -> Maybe Double
321 | asDouble (NScalar TFloat _ s) = dblVal s
322 | asDouble n@(NScalar TInt _ _) = cast <$> asInteger n
323 | asDouble _ = Nothing
326 | asSeq : Node -> Maybe (List Node)
327 | asSeq (NSeq _ ns) = Just ns
331 | asMap : Node -> Maybe (List (Node, Node))
332 | asMap (NMap _ ps) = Just ps
338 | lookupKey : String -> Node -> Maybe Node
339 | lookupKey s (NMap _ ps) = go ps
341 | go : List (Node, Node) -> Maybe Node
343 | go ((NScalar _ _ t, v) :: rest) = if t == s then Just v else go rest
344 | go (_ :: rest) = go rest
345 | lookupKey _ _ = Nothing
355 | data CScalar : Type where
357 | CBool : Bool -> CScalar
358 | CInt : Integer -> CScalar
360 | CInf : (neg : Bool) -> CScalar
364 | CSci : (neg : Bool) -> (digits : String) -> (exp : Integer) -> CScalar
367 | CNull == CNull = True
368 | CBool x == CBool y = x == y
369 | CInt x == CInt y = x == y
370 | CZero == CZero = True
371 | CInf x == CInf y = x == y
372 | CNaN == CNaN = True
373 | CSci n d e == CSci m f g = n == m && d == f && e == g
376 | canonFloat : String -> Maybe CScalar
378 | if s `elem` posInfWords then Just (CInf False)
379 | else if s `elem` negInfWords then Just (CInf True)
380 | else if s `elem` nanWords then Just CNaN
381 | else case unpack s of
382 | '+' :: cs => norm False cs
383 | '-' :: cs => norm True cs
384 | cs => norm False cs
387 | norm : Bool -> List Char -> Maybe CScalar
389 | (ip, fp, e) <- floatParts cs
390 | let (zs, ds) := span ('0' ==) (ip ++ fp)
391 | case reverse (dropWhile ('0' ==) (reverse ds)) of
394 | Just (CSci neg (pack ds') (e + cast (length ip) - cast (length zs) - 1))
398 | canonScalar : STag -> String -> Maybe CScalar
399 | canonScalar TNull s = if s `elem` nullWords then Just CNull else Nothing
400 | canonScalar TBool s =
401 | if s `elem` trueWords then Just (CBool True)
402 | else if s `elem` falseWords then Just (CBool False)
404 | canonScalar TInt s = CInt <$> intVal (unpack s)
405 | canonScalar TFloat s = canonFloat s
406 | canonScalar _ _ = Nothing
412 | eqScalar : STag -> String -> String -> Bool
413 | eqScalar t a b = case (canonScalar t a, canonScalar t b) of
414 | (Just x, Just y) => x == y
418 | eqNode : Node -> Node -> Bool
419 | eqNode (NScalar t1 _ s1) (NScalar t2 _ s2) = t1 == t2 && eqScalar t1 s1 s2
420 | eqNode (NSeq t1 ns1) (NSeq t2 ns2) = t1 == t2 && eqNodes ns1 ns2
421 | eqNode (NMap t1 ps1) (NMap t2 ps2) = t1 == t2 && eqPairs ps1 ps2
424 | eqNodes : List Node -> List Node -> Bool
425 | eqNodes [] [] = True
426 | eqNodes (x :: xs) (y :: ys) = eqNode x y && eqNodes xs ys
427 | eqNodes _ _ = False
429 | eqPairs : List (Node, Node) -> List (Node, Node) -> Bool
430 | eqPairs [] [] = True
431 | eqPairs ((k1,v1) :: xs) ((k2,v2) :: ys) =
432 | eqNode k1 k2 && eqNode v1 v2 && eqPairs xs ys
433 | eqPairs _ _ = False