Parse: lazy initialization and mutually recursive monads in F #

I wrote a small monadic library of parser-combinators in F # (somewhat similar to FParsec ) and now I tried to implement a small parser for a programming language.

At first I implemented the code in Haskell (with Parsec), which worked perfectly. Parsers for infix expressions are designed mutually recursively.

parseInfixOp :: Parser String -> Parser Expression -> Parser Expression
parseInfixOp operatorParser subParser = ignoreSpaces $ do
                                          x <- ignoreSpaces $ subParser
                                          do
                                            op <- ignoreSpaces $ operatorParser
                                            y  <- parseInfixOp operatorParser subParser
                                            return $ BinaryOp op x y
                                           <|> return x

parseInfix :: [String] -> Parser Expression -> Parser Expression
parseInfix list = parseInfixOp (foldl1 (<|>) $ map string list)

parseExpr :: Parser Expression
parseExpr = parseInfix0

parseInfix0 = parseInfix ["==", "<>", "And", "Or", "Xor", "<", ">", "<=", ">="] parseInfix1
parseInfix1 = parseInfix ["+", "-", "Mod"] parseInfix2
parseInfix2 = parseInfix ["*", "/", "\\"] parseInfix3
parseInfix3 = parseInfix ["^"] parseInfix4
parseInfix4 = parseFactor

parseFactor :: Parser Expression
parseFactor = parseFactor' <|> (betweenChars '(' ')' parseExpr)

parseFactor' :: Parser Expression
parseFactor' = parseString
           <|> parseBool
           <|> parseNumber
           <|> parseVariable
           <|> (try parseFunCall) <|> parseIdentifier  

Since the order of the functions does not matter, and Haskell evaluates in a non-strict manner, this is normal, but F # strictly evaluates.

let rec parseExpr = parseInfix0
and parseFactor = (parseFactor') <|> (betweenChars '(' ')' parseExpr)
and parseInfix2 = parseInfix ["^"] parseFactor BinaryOp
and parseInfix1 = parseInfix ["*"; "/"] parseInfix2 BinaryOp
and parseInfix0 = parseInfix ["+"; "-"] parseInfix1 BinaryOp
and parseFunCall = parser {
        let! first = letter
        let! rest = many (letter <|> digit)
        let  funcName = toStr $ first::rest

        do! ignoreSpace
        let! args = betweenChars '(' ')' $ sepBy (parseExpr) ","

        return FunCall(funcName, args)
    }
and parseFactor' =
    parseNumber
<|> parseString
<|> parseBool
<|> parseFunCall
<|> parseIdentifier

F # now either complains about recursive objects, or just throws StackOverflowExceptionat runtime due to an infinite loop, or doesn't even compile the source, because "the value will be part of its own definition."

. lazy , ?

+3
3

? ; , (, - ) .

- , " " " ".

...
and parseInfix2 = body
...

...
and parseInfix2 x = (body) x
...

'parseInfix2' ... F # ( Haskell) , (do eta-conversion, ).

"", , , - ( , "", , , - "" ).

StackOverflowExceptions, , , , , , . , . , .

+8

η- - , , .

- :

let rec p1 = lazy (...)
and p2 = ... p1.Value ..

, , Delay :

type Builder() =
    member this.Delay(f: unit -> Parser<_>) : Parser<_> = 
        let promise = Lazy.Create f
        Return () |> Bind (fun () -> promise.Value)

let parse = new Builder()


let rec p1 =
    parse {
        ...
    }

and p2 =
    parse {
        ...
    }
+2

eta, - . F #, , . , , , ( , ). .

, :

let rec myParser s = (parseExpr myParser) s

. : " , OCaml, , ". , .

+1

Source: https://habr.com/ru/post/1711880/


All Articles