Parsec

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"]}