mirror of
https://github.com/unisonweb/unison.git
synced 2024-11-15 14:35:01 +03:00
parser more efficient
This commit is contained in:
parent
6cd5b99c3f
commit
3b88a5842a
@ -6,46 +6,92 @@ import Dict as M
|
|||||||
|
|
||||||
type Msg = [String]
|
type Msg = [String]
|
||||||
|
|
||||||
-- type P a
|
type P a = J.Value -> Either Msg a
|
||||||
-- type P a = J.Value -> Either Msg a
|
|
||||||
-- type Parser a = { focus : Path, parser : P a }
|
|
||||||
-- combinators like lift2 can merge common paths
|
|
||||||
|
|
||||||
type Parser a = J.Value -> Either Msg a
|
data E = Key String | Index Int
|
||||||
|
type Path = [E]
|
||||||
|
|
||||||
|
type Parser a = { focus : Path, parse : P a }
|
||||||
|
|
||||||
run : Parser a -> J.Value -> Either String a
|
safeIndex : Int -> [a] -> Maybe a
|
||||||
run p v = case p v of
|
safeIndex i xs = case drop i xs of
|
||||||
Left stack -> Left (join "\n" stack)
|
[] -> Nothing
|
||||||
Right a -> Right a
|
h :: t -> Just h
|
||||||
|
|
||||||
|
getPath : Path -> J.Value -> Either Msg J.Value
|
||||||
|
getPath p v =
|
||||||
|
let
|
||||||
|
failure loc v = Left [
|
||||||
|
"invalid path: " ++ show p,
|
||||||
|
"remainder: " ++ show loc,
|
||||||
|
"focus: " ++ J.toString "" v
|
||||||
|
]
|
||||||
|
go loc v = case loc of
|
||||||
|
[] -> Right v
|
||||||
|
Key k :: t -> case v of
|
||||||
|
J.Object obj -> case M.get k obj of
|
||||||
|
Nothing -> failure loc v
|
||||||
|
Just v -> go t v
|
||||||
|
_ -> failure loc v
|
||||||
|
Index i :: t -> case v of
|
||||||
|
J.Array vs -> case safeIndex i vs of
|
||||||
|
Nothing -> failure loc v
|
||||||
|
Just v -> go t v
|
||||||
|
_ -> failure loc v
|
||||||
|
in go p v
|
||||||
|
|
||||||
|
run : Parser a -> J.Value -> Either Msg a
|
||||||
|
run p v = either Left p.parse (getPath p.focus v)
|
||||||
|
|
||||||
unit : a -> Parser a
|
unit : a -> Parser a
|
||||||
unit a _ = Right a
|
unit a = { focus = [], parse = \v -> Right a }
|
||||||
|
|
||||||
|
value : Parser J.Value
|
||||||
|
value = { focus = [], parse = Right }
|
||||||
|
|
||||||
fail : String -> Parser a
|
fail : String -> Parser a
|
||||||
fail msg _ = Left [msg]
|
fail msg = { focus = [], parse = \v -> Left [msg] }
|
||||||
|
|
||||||
scope : String -> Parser a -> Parser a
|
scope : String -> Parser a -> Parser a
|
||||||
scope msg p v = case p v of
|
scope msg p =
|
||||||
Left stack -> Left (msg :: stack)
|
let focus = p.focus
|
||||||
a -> a
|
parse v = case p.parse v of
|
||||||
|
Left stack -> Left (msg :: stack)
|
||||||
|
a -> a
|
||||||
|
in { focus = focus, parse = parse }
|
||||||
|
|
||||||
bind : (a -> Parser b) -> Parser a -> Parser b
|
bind : (a -> Parser b) -> Parser a -> Parser b
|
||||||
bind f p v = case p v of
|
bind f p =
|
||||||
Right a -> f a v
|
let focus = p.focus
|
||||||
Left e -> Left e
|
parse v = case p.parse v of
|
||||||
|
Right a -> run (f a) v
|
||||||
|
Left e -> Left e
|
||||||
|
in { focus = focus, parse = parse }
|
||||||
|
|
||||||
infixl 3 >>=
|
infixl 3 >>=
|
||||||
(>>=) : Parser a -> (a -> Parser b) -> Parser b
|
(>>=) : Parser a -> (a -> Parser b) -> Parser b
|
||||||
a >>= f = bind f a
|
a >>= f = bind f a
|
||||||
|
|
||||||
map : (a -> b) -> Parser a -> Parser b
|
map : (a -> b) -> Parser a -> Parser b
|
||||||
map f = bind (unit . f)
|
map f p =
|
||||||
|
let parse v = case p.parse v of
|
||||||
|
Left e -> Left e
|
||||||
|
Right a -> Right (f a)
|
||||||
|
in { p | parse <- parse }
|
||||||
|
|
||||||
lift2 : (a -> b -> c) -> Parser a -> Parser b -> Parser c
|
lift2 : (a -> b -> c) -> Parser a -> Parser b -> Parser c
|
||||||
lift2 f a b v = case (a v, b v) of
|
lift2 f a b =
|
||||||
(Left e, _) -> Left e
|
let common = a.focus `zip` b.focus
|
||||||
(_, Left e) -> Left e
|
|> concatMap (\p -> if fst p == snd p then [fst p] else [])
|
||||||
(Right a, Right b) -> Right (f a b)
|
restL = drop (length common) a.focus
|
||||||
|
restR = drop (length common) b.focus
|
||||||
|
parseL v = either Left a.parse (getPath restL v)
|
||||||
|
parseR v = either Left b.parse (getPath restR v)
|
||||||
|
parse v = case (parseL v, parseR v) of
|
||||||
|
(Left e, _) -> Left e
|
||||||
|
(_, Left e) -> Left e
|
||||||
|
(Right a, Right b) -> Right (f a b)
|
||||||
|
in { focus = common, parse = parse }
|
||||||
|
|
||||||
apply : Parser (a -> b) -> Parser a -> Parser b
|
apply : Parser (a -> b) -> Parser a -> Parser b
|
||||||
apply f a = lift2 (<|) f a
|
apply f a = lift2 (<|) f a
|
||||||
@ -58,54 +104,30 @@ infixl 4 #|
|
|||||||
(#|) : (a -> b) -> Parser a -> Parser b
|
(#|) : (a -> b) -> Parser a -> Parser b
|
||||||
f #| a = map f a
|
f #| a = map f a
|
||||||
|
|
||||||
key : String -> Parser J.Value
|
|
||||||
key k v = case v of
|
|
||||||
J.Object obj -> M.get k obj
|
|
||||||
|> maybe (Left ["not found"]) Right
|
|
||||||
e -> Left ["not found: " ++ J.toString "" v]
|
|
||||||
|
|
||||||
string : Parser String
|
string : Parser String
|
||||||
string v = case v of
|
string = value >>= \v -> case v of
|
||||||
J.String s -> Right s
|
J.String s -> unit s
|
||||||
_ -> Left ["not a string: " ++ J.toString "" v]
|
_ -> fail ("not a string: " ++ J.toString "" v)
|
||||||
|
|
||||||
int : Parser Int
|
int : Parser Int
|
||||||
int v = case v of
|
int = value >>= \v -> case v of
|
||||||
J.Number v -> Right (floor v)
|
J.Number v -> unit (floor v)
|
||||||
_ -> Left ["not a number: " ++ J.toString "" v]
|
_ -> fail ("not a number: " ++ J.toString "" v)
|
||||||
|
|
||||||
number : Parser Float
|
|
||||||
number v = case v of
|
|
||||||
J.Number v -> Right v
|
|
||||||
_ -> Left ["not a number: " ++ J.toString "" v]
|
|
||||||
|
|
||||||
asString : Parser J.Value -> Parser String
|
|
||||||
asString p v = case v of
|
|
||||||
J.String s -> Right s
|
|
||||||
_ -> Left ["not a string: " ++ J.toString "" v]
|
|
||||||
|
|
||||||
atKey : String -> Parser a -> Parser a
|
number : Parser Float
|
||||||
atKey fld p =
|
number = value >>= \v -> case v of
|
||||||
let p2 v = case v of
|
J.Number v -> unit v
|
||||||
J.Object obj -> M.get fld obj
|
_ -> fail ("not a number: " ++ J.toString "" v)
|
||||||
|> maybe (Left ["not found"]) p
|
|
||||||
e -> Left ["not found: " ++ J.toString "" v]
|
atKey : String -> Parser a -> Parser a
|
||||||
in scope fld p2
|
atKey k p = { p | focus <- Key k :: p.focus }
|
||||||
|
|
||||||
atIndex : Int -> Parser a -> Parser a
|
atIndex : Int -> Parser a -> Parser a
|
||||||
atIndex ind p =
|
atIndex ind p = { p | focus <- Index ind :: p.focus }
|
||||||
let p2 v = case v of
|
|
||||||
J.Array vs ->
|
|
||||||
let sub = drop ind vs
|
|
||||||
in if isEmpty sub
|
|
||||||
then Left ["out of bounds: " ++ show ind ++ " " ++ J.toString "" v]
|
|
||||||
else p (head sub)
|
|
||||||
e -> Left ["not found: " ++ J.toString "" v]
|
|
||||||
in scope ("index: " ++ show ind) p2
|
|
||||||
|
|
||||||
union : String -> String -> (String -> Parser a) -> Parser a
|
union : String -> String -> (String -> Parser a) -> Parser a
|
||||||
union tag contents f =
|
union tag contents f =
|
||||||
asString (key tag) >>= \t -> atKey contents (f t)
|
atKey tag string >>= \t -> atKey contents (f t)
|
||||||
|
|
||||||
union' : (String -> Parser a) -> Parser a
|
union' : (String -> Parser a) -> Parser a
|
||||||
union' = union "tag" "contents"
|
union' = union "tag" "contents"
|
||||||
|
Loading…
Reference in New Issue
Block a user