I'm trying to replicate the structure of a simple if statement:
if (paren) { block } [else ({ block } | rec if (paren)) ]
for if (paren) block, I create a IfBlock AST node. Otherwise, i开发者_如何学Got recursively fills a IfElseBlock node.
I've tried quite a few alternate constructions
let parse_if =
suffixparen .>>. suffixblock |>> IfBlock
//>>? attempt (str "else" >>. ifParser) |>> IfElseBlock
//<|> preturn IfBlock
// .>>? attempt (str "else" >>. ifParser) |>> IfElseBlock
// suffixparen .>>. suffixblock |>> IfBlock
// <|> ifParser |>> IfElseBlock
let inlineIf = str_ws "if" >>. parse_if
do ifParserR := inlineIf
Suggestions?
Have you had a look at my GLSL parser (it's a C-like language)? http://laurent.le-brun.eu/fsharp/glsl_parse.fs
From the code example, here is the relevant part for the if
statement:
let statement, stmtRef = createParserForwardedToRef()
let ifStatement =
pipe3 (keyword "if" >>. parenExp) statement (opt (keyword "else" >>. statement))
(fun cond stmt1 stmt2 -> Ast.If(cond, stmt1, stmt2))
stmtRef := choice [
simpleStatement
block
ifStatement
forLoop
//...
]
I think your problem is that you're using attempt
instead of opt
. opt
means the else
part is optional (if it's not there, you get None
). attempt
is quite different:
The parser
attempt p
applies the parserp
. Ifp
fails after changing the parser state or with a fatal error,attempt p
will backtrack to the original parser state and report a non‐fatal error.
When the parser in attempt
fails, there's still an error but the input is not consumed (it's useful when combined with the <|>
or choice
operators).
精彩评论