module parsec
open prelude
open prelude.option
open prelude.string
open prelude.html
open prelude.array
Parser a = {parser: String -> {rest: String, parsed: Option a}}
Runs a parser over a text argument and throws away the leftover text.
inline run_parser:
Parser p -> String -> Option p
| {parser = p} x = p x |> λ {parsed = {some = y}, _} = {some = y}
| _ = {none}
Applies a function to the parse result, only if the parse was successful.
(<$>):
(a -> b) -> Parser a -> Parser b
f <$> {parser = z} =
{ parser s =
z s |> λ {parsed = {some: x}, rest = ss} =
{parsed = {some = f x}, rest = ss}
| {parsed = {none}, rest = x} =
{parsed = {none}, rest = x} }
Allows functions that take multiple arguments to be applied sequentially to a sequence of parsed values.
inline (<*>):
Parser (a -> b) -> Parser a -> Parser b
{parser = fp} <*> {parser = p} =
{ parser s =
fp s |> λ {parsed = {some: f}, rest = rr} =
p rr |> λ {parsed = {some = x}, rest = z} =
{parsed = {some = f x}, rest = z}
| {parsed = {none}, rest = x} =
{parsed = {none}, rest = x}
| {parsed: {none}, rest: x} =
{parsed: {none}, rest: x} }
Applies two parsers in sequence, throwing out the result of the first.
inline (*>):
Parser a -> Parser b -> Parser b
{parser = f} *> {parser = g} =
{ parser s =
f s |> λ {parsed = {some = x}, rest = zz} = g zz
| {parsed = {none}, rest = x} =
{parsed = {none}, rest = x} }
private inline lift(g, {parser: f}) =
{ parser text =
let f' = f text
in g { text: text
f: f
parsed: f'.parsed
rest: f'.rest
f': f' }}
Alternative will try the first parser, and applies the second only if the first fails and consumes no input from the text.
inline (<|>):
Parser a -> Parser a -> Parser a
{parser = f} <|> {parser = g} =
let h text {parsed = {none}, rest = textt} when text == textt =
g text
| _ x = x
in {parser text = h text (f text)}
Tries to apply a parser, but restores the parser state in the case of a failure. This is useful with combinators that check the state of the consumed text, like <|>
inline try':
Parser a -> Parser a
= lift λ (x & {_, parsed: {none}}) =
{parsed: {none}, rest: x.text}
| x = x.f'
A parser that matches a string. Unlike Haskell's Parsec library, parsec.forml will not consume any input if the entire string doesn't match. This is for efficiency reasons currently, but may change in the future.
inline string:
String -> Parser String
| x = { parser y =
let sub = do! `y.substring(0, x.length)`
if (sub == x)
{ rest = do! `y.substring(x.length)`
parsed = {some = x} }
else {rest = y, parsed = {none}} }
Applies a parser repeatedly, until parsing fails.
many:
Parser a -> Parser (Array a)
= lift λ (x & {_, parsed: {none}}) =
{parsed: {some: []}, rest = x.rest}
| (y & {_, parsed: {some: x}}) =
(push! x <$> many {parser: y.f}).parser y.rest
module "Testing"
{some = "ten"} == run_parser (string "ten") "tens"
{some = "tenten"} == run_parser ((λx = x +++ x) <$> string "ten") "tenfingers"
{parsed = {some = "tenten"}, rest = "fingers"}
== ((λx = x +++ x) <$> string "ten").parser "tenfingers"
var result = run_parser ((λx y = y +++ x) <$> string "ten" <*> string "fingers") "tenfingers"
result == {some: "fingersten"}
var result = run_parser (string "ten" <|> string "eleven") "eleven"
result == {some: "eleven"}
var result = run_parser (string "ten" *> string "fingers") "tenfingers"
result == {some: "fingers"}
var parser = (string "ten" *> string "fingers") <|> string "tentoes"
result = run_parser parser "tentoes"
result == {none}
var parser = try' (string "ten" *> string "fingers") <|> string "tentoes"
result = run_parser parser "tentoes"
result == {some: "tentoes"}
var parser = many (string "ten")
result = run_parser parser "tententententen"
result == {some: ["ten", "ten", "ten", "ten", "ten"]}