interface Parser.Core exposes [ Parser, ParseResult, parse, parsePartial, fail, const, alt, apply, andThen, # oneOf, map, map2, map3, lazy, maybe, oneOrMore, many, between, sepBy, sepBy1, ignore, buildPrimitiveParser, flatten, ] imports [] ## Opaque type for a parser that will try to parse an `a` from an `input`. ## ## As a simple example, you might consider a parser that tries to parse a `U32` from a `Str`. ## Such a process might succeed or fail, depending on the current value of `input`. ## ## As such, a parser can be considered a recipe ## for a function of the type `input -> Result {val: a, input: input} [ParsingFailure Str]`. ## ## How a parser is _actually_ implemented internally is not important ## and this might change between versions; ## for instance to improve efficiency or error messages on parsing failures. Parser input a := (input -> ParseResult input a) ParseResult input a : Result {val: a, input: input} [ParsingFailure Str] buildPrimitiveParser : (input -> ParseResult input a) -> Parser input a buildPrimitiveParser = \fun -> @Parser fun # -- Generic parsers: ## Most general way of running a parser. ## ## Can be tought of turning the recipe of a parser into its actual parsing function ## and running this function on the given input. ## ## Many (but not all!) parsers consume part of `input` when they succeed. ## This allows you to string parsers together that run one after the other: ## The part of the input that the first parser did not consume, is used by the next parser. ## This is why a parser returns on success both the resulting value and the leftover part of the input. ## ## Of course, this is mostly useful when creating your own internal parsing building blocks. ## `run` or `Parser.Str.runStr` etc. are more useful in daily usage. parsePartial : Parser input a, input -> ParseResult input a parsePartial = \@Parser parser, input -> (parser input) ## Runs a parser on the given input, expecting it to fully consume the input ## ## The `input -> Bool` parameter is used to check whether parsing has 'completed', ## (in other words: Whether all of the input has been consumed.) ## ## For most (but not all!) input types, a parsing run that leaves some unparsed input behind ## should be considered an error. parse : Parser input a, input, (input -> Bool) -> Result a [ParsingFailure Str, ParsingIncomplete input] parse = \parser, input, isParsingCompleted -> when (parsePartial parser input) is Ok {val: val, input: leftover} -> if isParsingCompleted leftover then Ok val else Err (ParsingIncomplete leftover) Err (ParsingFailure msg) -> Err (ParsingFailure msg) ## Parser that can never succeed, regardless of the given input. ## It will always fail with the given error message. ## ## This is mostly useful as 'base case' if all other parsers ## in a `oneOf` or `alt` have failed, to provide some more descriptive error message. fail : Str -> Parser * * fail = \msg -> buildPrimitiveParser \_input -> Err (ParsingFailure msg) ## Parser that will always produce the given `val`, without looking at the actual input. ## This is useful as basic building block, especially in combination with ## `map` and `apply`. const : a -> Parser * a const = \val -> buildPrimitiveParser \input -> Ok { val: val, input: input } ## Try the `left` parser and (only) if it fails, try the `right` parser as fallback. alt : Parser input a, Parser input a -> Parser input a alt = \left, right -> fun = \input -> when (parsePartial left input) is Ok {val: val, input: rest} -> Ok {val: val, input: rest} Err (ParsingFailure leftErr) -> when (parsePartial right input) is Ok {val: val, input: rest} -> Ok {val: val, input: rest} Err (ParsingFailure rightErr) -> Err (ParsingFailure ("\(leftErr) or \(rightErr)")) buildPrimitiveParser fun # applyOld : Parser input a, Parser input (a -> b) -> Parser input b # applyOld = \valParser, funParser -> # combined = \input -> # {val: val, input: rest} <- Result.after (parsePartial valParser input) # (parsePartial funParser rest) # |> Result.map \{val: funVal, input: rest2} -> # {val: funVal val, input: rest2} # @Parser combined ## Runs a parser building a function, then a parser building a value, ## and finally returns the result of calling the function with the value. ## ## This is useful if you are building up a structure that requires more parameters ## than there are variants of `map`, `map2`, `map3` etc. for. ## ## For instance, the following two are the same: ## ## >>> const (\x, y, z -> Triple x y z) ## >>> |> map3 Parser.Str.nat Parser.Str.nat Parser.Str.nat ## ## >>> const (\x -> \y -> \z -> Triple x y z) ## >>> |> apply Parser.Str.nat ## >>> |> apply Parser.Str.nat ## >>> |> apply Parser.Str.nat ## ## (And indeed, this is how `map`, `map2`, `map3` etc. are implemented under the hood.) ## ## # Currying ## Be aware that when using `apply`, you need to explicitly 'curry' the parameters to the construction function. ## This means that instead of writing `\x, y, z -> ...` ## you'll need to write `\x -> \y -> \z -> ...`. ## This is because the parameters to the function will be applied one-by-one as parsing continues. apply : Parser input (a -> b), Parser input a -> Parser input b apply = \funParser, valParser -> combined = \input -> {val: funVal, input: rest} <- Result.after (parsePartial funParser input) (parsePartial valParser rest) |> Result.map \{val: val, input: rest2} -> {val: funVal val, input: rest2} buildPrimitiveParser combined ## Runs `firstParser` and (only) if it succeeds, ## runs the function `buildNextParser` on its result value. ## This function returns a new parser, which is finally run. ## ## `andThen` is usually more flexible than necessary, and less efficient ## than using `const` with `map` and/or `apply`. ## Consider using those functions first. # TODO I am considering leaving this function out alltogether # As using it is an anti-pattern. andThen : Parser input a, (a -> Parser input b) -> Parser input b andThen = \firstParser, buildNextParser -> fun = \input -> {val: firstVal, input: rest} <- Result.after (parsePartial firstParser input) nextParser = (buildNextParser firstVal) parsePartial nextParser rest buildPrimitiveParser fun # NOTE: Using this implementation in an actual program, # currently causes a compile-time StackOverflow (c.f. https://github.com/rtfeldman/roc/issues/3444 ) # oneOfBroken : List (Parser input a) -> Parser input a # oneOfBroken = \parsers -> # List.walkBackwards parsers (fail "Always fail") (\laterParser, earlierParser -> alt earlierParser laterParser) # And this one as well # oneOfBroken2 : List (Parser input a) -> Parser input a # oneOfBroken2 = \parsers -> # if List.isEmpty parsers then # fail "(always fail)" # else # firstParser = List.get parsers (List.len parsers - 1) |> Result.withDefault (fail "this should never happen!!") # alt firstParser (oneOfBroken2 (List.dropLast parsers)) ## Transforms the result of parsing into something else, ## using the given transformation function. map : Parser input a, (a -> b) -> Parser input b map = \simpleParser, transform -> const transform |> apply simpleParser ## Transforms the result of parsing into something else, ## using the given two-parameter transformation function. map2 : Parser input a, Parser input b, (a, b -> c) -> Parser input c map2 = \parserA, parserB, transform -> const (\a -> \b -> transform a b) |> apply parserA |> apply parserB ## Transforms the result of parsing into something else, ## using the given three-parameter transformation function. ## ## If you need transformations with more inputs, ## take a look at `apply`. map3 : Parser input a, Parser input b, Parser input c, (a, b, c-> d) -> Parser input d map3 = \parserA, parserB, parserC, transform -> const (\a -> \b -> \c -> transform a b c) |> apply parserA |> apply parserB |> apply parserC # ^ And this could be repeated for as high as we want, of course. # Removes a layer of 'result' from running the parser. # # This allows for instance to map functions that return a result over the parser, # where errors are turned into `ParsingFailure` s. flatten : Parser input (Result a Str) -> Parser input a flatten = \parser -> buildPrimitiveParser \input -> result = parsePartial parser input when result is Err problem -> Err problem Ok {val: (Ok val), input: inputRest} -> Ok {val: val, input: inputRest} Ok {val: (Err problem), input: _inputRest} -> Err (ParsingFailure problem) ## Runs a parser lazily ## ## This is (only) useful when dealing with a recursive structure. ## For instance, consider a type `Comment : { message: String, responses: List Comment }`. ## Without `lazy`, you would ask the compiler to build an infinitely deep parser. ## (Resulting in a compiler error.) ## lazy : ({} -> Parser input a) -> Parser input a lazy = \thunk -> andThen (const {}) thunk maybe : Parser input a -> Parser input (Result a [Nothing]) maybe = \parser -> alt (parser |> map (\val -> Ok val)) (const (Err Nothing)) manyImpl : Parser input a, List a, input -> ParseResult input (List a) manyImpl = \parser, vals, input -> result = parsePartial parser input when result is Err _ -> Ok {val: vals, input: input} Ok {val: val, input: inputRest} -> manyImpl parser (List.append vals val) inputRest ## A parser which runs the element parser *zero* or more times on the input, ## returning a list containing all the parsed elements. ## ## Also see `oneOrMore`. many : Parser input a -> Parser input (List a) many = \parser -> buildPrimitiveParser \input -> manyImpl parser [] input ## A parser which runs the element parser *one* or more times on the input, ## returning a list containing all the parsed elements. ## ## Also see `many`. oneOrMore : Parser input a -> Parser input (List a) oneOrMore = \parser -> const (\val -> \vals -> List.prepend vals val) |> apply parser |> apply (many parser) # moreParser : Parser (a -> (List a)) # moreParser = # many parser # |> map (\vals -> (\val -> List.prepend vals val)) # apply parser moreParser # val <- andThen parser # parser # |> many # |> map (\vals -> List.prepend vals val) # betweenBraces : Parser input a -> Parser input a # betweenBraces = \parser -> # string "[" # |> applyOld (parser |> map (\res -> \_ -> res)) # |> applyOld (string "]" |> map (\_ -> \res -> res)) ## Runs a parser for an 'opening' delimiter, then your main parser, then the 'closing' delimiter, ## and only returns the result of your main parser. ## ## Useful to recognize structures surrounded by delimiters (like braces, parentheses, quotes, etc.) ## ## >>> betweenBraces = \parser -> parser |> between (scalar '[') (scalar ']') between : Parser input a, Parser input open, Parser input close -> Parser input a between = \parser, open, close-> const (\_ -> \val -> \_ -> val) |> apply open |> apply parser |> apply close sepBy1 : Parser input a, Parser input sep -> Parser input (List a) sepBy1 = \parser, separator -> parserFollowedBySep = const (\_ -> \val -> val) |> apply separator |> apply parser const (\val -> \vals -> List.prepend vals val) |> apply parser |> apply (many parserFollowedBySep) sepBy : Parser input a, Parser input sep -> Parser input (List a) sepBy = \parser, separator -> alt (sepBy1 parser separator) (const []) ignore : Parser input a -> Parser input {} ignore = \parser -> map parser (\_ -> {})