diff --git a/std/parser.glu b/std/parser.glu index fd9efa2260..71096ac842 100644 --- a/std/parser.glu +++ b/std/parser.glu @@ -1,295 +1,160 @@ -//! A simple parser combinator library. +//! A simple parser combinator library let prelude = import! std.prelude -let { Functor, Applicative, Alternative, Monad } = prelude -let { id, flip } = import! std.function +let { Functor, Applicative, Alternative, Monad, (<>) } = prelude +let { id, flip, (>>), (<<), (|>), (<|) } = import! std.function + +let { Option } = import! std.option +let { Result, map_err } = import! std.result +let { StateT, eval_state_t , ? } = import! std.statet let { Bool } = import! std.bool -let char @ { ? } = import! std.char +let char @ { Char, ? } = import! std.char let int = import! std.int -let { Result } = import! std.result -let string = import! std.string -let { (<>) } = import! std.prelude let list @ { List } = import! std.list -let { Option } = import! std.option +let string @ { String, ? } = import! std.string -type OffsetString = { start : Int, end : Int, buffer : String } -type Position = Int -type Error = { position : Position, message : String } -type ParseResult a = Result Error { value : a, buffer : OffsetString } +// type Parser s e a = StateT s (Result e) a -/// `Parser` is a monad which parses a `String` into structured values -type Parser a = - OffsetString -> ParseResult a +type ParseError = + | EndOfStream + | PredicateFailed + | TokenMismatch String + | MsgErr String + | Label String (List ParseErr) -let parser : Parser a -> Parser a = id +// TODO How handle error position generically? Zip stream with enumeration? +type Parser s a = StateT s (Result (List ParseErr)) a -let functor : Functor Parser = { - map = \f m -> parser (\buffer -> - let result = parser m buffer - match result with - | Ok a -> Ok { value = f a.value, buffer = a.buffer } - | Err err -> Err err) +#[implicit] +type Parsable a s = { + any_atom : Parser s a } -let { map } = functor +let any_atom ?pb : [Parsable atm srm] -> Parser srm atm = pb.any_atom -let applicative : Applicative Parser = { - functor, +let fail err : ParseErr -> Parser srm _ = \_ -> + wrap_monad <| Err <| Cons err Nil - apply = \f m -> parser (\buffer -> - let result1 = parser f buffer - match result1 with - | Ok g -> - let result2 = parser m g.buffer - match result2 with - | Ok a -> Ok { value = g.value a.value, buffer = a.buffer } - | Err err -> Err err - | Err err -> Err err), +let fail_msg msg : String -> Parser srm _ = \_ -> + fail (MsgErr msg) - wrap = \value -> parser (\buffer -> Ok { value, buffer }), -} +let label_parser lbl psr : String -> Parser srm a -> Parser srm a = \s -> + psr s |> map_err (\errs -> Cons (Label lbl errs) Nil) -let { (*>), (<*), wrap } = import! std.applicative +#[infix(left, 0)] +let () = flip label_parser -let alternative : Alternative Parser = { - applicative, - or = \l r -> parser (\stream -> - match parser l stream with - | Ok a -> Ok a - | Err _ -> parser r stream), - empty = parser (\stream -> Err { position = stream.start, message = "empty" }), -} +// succeeds iff the stream is empty +let eos : Parser s () = \s -> + match any_atom s with + | Err EndOfStream -> wrap () + | Ok _ -> wrap_monad (Err PredicateFailed) + | e -> e -let { (<|>) } = import! std.alternative +let parse_foldl f acc psr : (b -> a -> b) -> b -> Parser s a -> Parser s b = + map (\x -> parse_foldl f (f acc x) psr) psr <|> wrap acc -let monad : Monad Parser = { - applicative, +let parse_foldr f acc psr : (a -> b -> b) -> b -> Parser s a -> Parser s b = + (wrap f <*> psr <*> parse_foldr f acc psr) <|> wrap acc - flat_map = \f m -> parser (\buffer -> - match parser m buffer with - | Ok a -> parser (f a.value) a.buffer - | Err err -> Err err), -} +let many psr : [Parsable atm srm] -> Parser srm a -> Parser srm (List a) = + parse_foldl (flip Cons) Nil psr + +let many1 psr : [Parsable atm srm] -> Parser srm a -> Parser srm (List a) = + wrap Cons <*> prs <*> many psr + +let chain vpsr oppsr acc : Parser s a -> Parser (b -> a -> b) -> b -> Parser s b = + let rhs = map flip oppsr <*> + parse_foldl (|>) acc rhs -let { flat_map } = import! std.monad +let chain1 vpsr oppsr = vpsr |> map (chain vpsr oppsr) -let uncons stream : OffsetString -> Option { char : Char, rest : OffsetString } = - if stream.start == stream.end then - None +let skip n : [Parsable atm srm] -> Int -> Parser srm () = + any_atom >>= \_ -> if n == 0 then + wrap () else - let c = string.char_at stream.buffer stream.start - let char_len = char.len_utf8 c - Some { - char = c, - rest = { - start = stream.start + char_len, - end = stream.end, - buffer = stream.buffer, - }, - } - -let update_position c position : Char -> Position -> Position = - position + char.len_utf8 c - -/// Returns `message` as what was expected by `p` -#[infix(left, 0)] -let () p message : Parser a -> String -> Parser a = - parser (\stream -> - match p stream with - | Ok x -> Ok x - | Err _ -> Err { position = stream.start, message }) - -/// Parses any character. Only errors if the stream is out of input -let any : Parser Char = - parser (\stream -> - match uncons stream with - | Some record -> - let { char, rest } = record - Ok { value = char, buffer = rest } - | None -> Err { position = stream.start, message = "End of stream" }) - -/// Fails the parser with `message` as the cause -let fail message : String -> Parser a = - parser (\stream -> Err { position = stream.start, message }) - -/// Succeeds if `predicate` returns `Some`, fails if `None` is returned -let satisfy_map predicate : (Char -> Option a) -> Parser a = - let f c = - match predicate c with - | Some x -> wrap x - | None -> fail ("Unexpected character " <> char.show.show c) - flat_map f any - -/// Succeeds if `predicate` returns True, fails if `False` is returned -let satisfy predicate : (Char -> Bool) -> Parser Char = - satisfy_map (\c -> if predicate c then Some c else None) - -/// Succeeds if the next token is `expected` -let token expected : Char -> Parser Char = - satisfy (\c -> expected == c) - -/// Succeds if the next token is a letter -let letter : Parser Char = satisfy char.is_alphabetic "letter" - -/// Succeds if the next token is a digit -let digit : Parser Char = satisfy (flip char.is_digit 10) "digit" - -/// Succeds if the next token is alphanumeric -let alpha_num : Parser Char = satisfy char.is_alphanumeric "letter or digit" - -/// Succeds if the next token is a space -let space : Parser Char = token ' ' - -/// Succeds if the next token is a tab -let tab : Parser Char = token '\t' - -/// Parses one or more tokens passing `predicate` and returns the `String` between the start and -/// end of those tokens -let take1 predicate : (Char -> Bool) -> Parser String = - parser (\stream -> - let take_ stream2 = - match uncons stream2 with - | Some record -> - if predicate record.char then take_ record.rest - else if stream.start == stream2.start then - Err { position = stream.start, message = "Unexpected token" } - else Ok { - value = string.slice stream.buffer stream.start stream2.start, - buffer = stream2, - } - | None -> Ok { - value = string.slice stream.buffer stream.start stream.end, - buffer = stream2, - } - take_ stream) - -/// Parses zero or more tokens passing `predicate` and returns the `String` between the start and -/// end of those tokens -let take predicate : (Char -> Bool) -> Parser String = - take1 predicate <|> wrap "" - -/// Parses using `p` and returns the `String` between the start and of what `p` parsed -let recognize p : Parser a -> Parser String = - parser (\stream -> - match parser p stream with - | Ok a -> - Ok { - value = string.slice stream.buffer stream.start a.buffer.start, - buffer = a.buffer, - } - | Err err -> Err err) - -/// Skips over whitespace characters -let spaces = take char.is_whitespace - -/// Creates a parser from a factory function. Useful to prevent mutually recursive parser from looping forever -let lazy_parser f : (() -> Parser a) -> Parser a = - parser (\stream -> f () stream) - -/// Parses `x` between `l` and `r`, returning the result of `x` -let between l r x : Parser a -> Parser b -> Parser c -> Parser c = - l *> x <* r - -rec -/// Parses with `p` zero or more times -let many p : Parser a -> Parser (List a) = - many1 p <|> wrap Nil - -/// Parses with `p` one or more times -let many1 p : Parser a -> Parser (List a) = - do h = p - map (\t -> Cons h t) (many p) -in -rec -/// Parses with `p` zero or more times, ignoring the result of the parser -let skip_many p : Parser a -> Parser () = - skip_many1 p <|> wrap () -/// Parses with `p` one or more times, ignoring the result of the parser -let skip_many1 p : Parser a -> Parser () = - seq p - skip_many p -in -/// Parses one of the characters of `s` -let one_of s : String -> Parser Char = - satisfy (\first -> - let len = string.len s - let one_of_ i = - if i == len then - False + skip (n - 1) + +let take_with n psr : [Parsable atm srm] -> Int -> Parser srm a -> Parser srm (List a) = + if n > 0 then + wrap Cons <*> psr <*> take_with (n-1) psr + else + wrap Nil + +let take = flip take_with any_atom + +let satisfy_map pred : [Parsable atm srm] -> (atm -> Option a) -> Parser srm a = + any_atom >>= \atm -> + match pred atm with + | Some a -> wrap a + | None -> wrap_monad <| Err PredicateFailed + +let satisfy pred : [Parsable atm srm] -> (atm -> Bool) -> Parser srm atm = + satisfy_map (\c -> if pred c then Some c else None) + +let atom x : [Parsable atm srm] -> [Eq atm] -> atm -> Parser srm atm = + satisfy <| (==) x + +let one_of atms : [Parsable atm srm] -> [Foldable (m atm)] -> [Eq atm] -> m atm -> Parser srm atm = + satisfy (flip elem atms) + +// FIXME Fails if end of token is at end of stream? +rec let token ts : [Parsable atm srm] -> [Streamlike atm tksrm] -> [Eq atm] -> tksrm -> Parser srm tksrm = + match uncons ts with + | Some {value = t, state = ts'} -> + s |> (any_atom >>= \x -> + if x == t then + map (\_ -> ts) (token ts') else - let c = string.char_at s i - if first == c then True - else one_of_ (i + char.len_utf8 c) - one_of_ 0) - <|> fail ("Expected one of `" <> s <> "`") - - -/// Parses at least one element of `parser` separated by `sep` -let sep_by1 parser sep : Parser a -> Parser b -> Parser (List a) = - do x = parser - do xs = many (sep *> parser) - wrap (Cons x xs) - -/// Parses `parser` separated by `sep` -let sep_by parser sep : Parser a -> Parser b -> Parser (List a) = - sep_by1 parser sep <|> wrap Nil - -/// Like `sep_by1` but applies the function returned by `op` on the left fold of successive parses -let chainl1 p op : Parser a -> Parser (a -> a -> a) -> Parser a = - do l = p - let rest x = - ( - do f = op - do r = p - rest (f x r)) <|> wrap x - rest l - -/// Like `sep_by` but applies the function returned by `op` on the left fold of successive parses -let chainl p op v : Parser a -> Parser (a -> a -> a) -> a -> Parser a = - chainl1 p op <|> wrap v - - -/// Parses `input` using `p` -let parse p input : Parser a -> String -> Result String a = - match p { start = 0, end = string.len input, buffer = input } with - | Ok ok -> Ok ok.value - | Err err -> Err (int.show.show err.position <> ":" <> err.message) - -{ - Position, Error, ParseResult, Parser, - - functor, applicative, alternative, monad, - - parser, - - any, - between, - token, - many, - many1, - satisfy, - satisfy_map, - spaces, - take1, - take, - lazy_parser, - fail, - recognize, - skip_many, - skip_many1, - one_of, - sep_by, - sep_by1, - chainl1, - chainl, - (), - - alpha_num, - letter, - digit, - space, - tab, - - parse, -} + wrap_monad (Err TokenMismatch)) + | None -> wrap ts + +let between fst x snd : Parser s a -> Parser s b -> Parser s c = + fst *> x <* snd + +// sep_by +// sep_by1 +// chain +// chain1 +// parse +// lazy_parser? + +type Position = Int +type OffsetString = { start : Position, end : Position, buffer : String } +type StringParser a = Parser OffsetString a + +let eolsym c : Char -> Bool = + elem c ['\r', '\n'] + +let eol : [Parsable Char srm] -> Parser srm Char = (atom '\r' *> atom '\n') <|> atom '\n' <|> atom '\r' + +let space : [Parsable Char srm] -> Parser srm Char = + atom ' ' + +let spaces = many space + +let whitespace : [Parsable Char srm] -> Parser srm Char = + satisfy char.is_whitespace + +let whitespaces = many whitespace + +let letter : [Parsable Char srm] -> Parser srm Char = + satisfy char.is_alphabetic + +let digit : [Parsable Char srm] -> Parser srm Char = + satisfy <| flip char.is_digit 10 + +let alphanum : [Parsable Char srm] -> Parser srm Char = + letter <|> digit + +let rest_of_line : Parser s a = + many // TODO not eol + + +type ByteParser a = // TODO + + +// practice parser implementation + +type Parser a = Parser [Byte] a diff --git a/std/result.glu b/std/result.glu index 4e75aed0d4..1b6d7d3b4a 100644 --- a/std/result.glu +++ b/std/result.glu @@ -1,4 +1,4 @@ -//@NO-IMPLICIT-PRELUDEJ +//@NO-IMPLICIT-PRELUDE //! Error handling type. let { Eq, Ord, Ordering, (==) } = import! std.cmp @@ -6,6 +6,7 @@ let { Show } = import! std.show let { Functor } = import! std.functor let { Applicative } = import! std.applicative let { Monad } = import! std.monad +let { Alternative, (<|>) } = import! std.alternative let { Result } = import! std.types let { Foldable } = import! std.foldable let { Traversable } = import! std.traversable @@ -73,6 +74,18 @@ let monad : Monad (Result e) = { | Err err -> Err err, } +let alternative ?alt : [Alternative m] -> Alternative (Result (m e)) = + let or a b = + match a with + | Ok a -> Ok a + | Err ea -> + match b with + | Ok b -> Ok b + | Err eb -> Err (ea <|> eb) + let empty = Err alt.empty + + { applicative, or, empty } + let foldable : Foldable (Result e) = { foldr = \f z r -> match r with @@ -111,6 +124,7 @@ let show ?e ?t : [Show e] -> [Show t] -> Show (Result e t) = functor, applicative, monad, + alternative, foldable, traversable, show, diff --git a/std/statet.glu b/std/statet.glu index 8f8a0f4be8..a862e9dc2d 100644 --- a/std/statet.glu +++ b/std/statet.glu @@ -21,17 +21,16 @@ let functor : [Functor m] -> Functor (StateT s m) = { map = stmap } -// the typechecker can't find map and Functor m without help -let applicative ?mo : [Monad m] -> Applicative (StateT s m) = +let applicative : [Monad m] -> Applicative (StateT s m) = let apply srf sr : StateT s m (a -> b) -> StateT s m a -> StateT s m b = \state -> srf state >>= \fout -> let {value = f, state = state'} = fout - mo.applicative.functor.map (map_sout f) (sr state') + map (map_sout f) (sr state') let stwrap value : a -> StateT s m a = \state -> wrap { value, state } - { functor = functor ?mo.applicative.functor, apply, wrap = stwrap } + { functor, apply, wrap = stwrap } let monad : [Monad m] -> Monad (StateT s m) = let flat_map f sr : (a -> StateT s m b) -> StateT s m a -> StateT s m b = \state -> @@ -45,7 +44,7 @@ let transformer : Transformer (StateT s) = let wrap_monad ma : [Monad m] -> m a -> StateT s m a = \state -> ma >>= \value -> wrap {value, state} - { /* monad, */ wrap_monad } + { wrap_monad } let alternative : [Monad m] -> [Alternative m] -> Alternative (StateT s m) = let stor sra srb = or << sra <*> srb