Domanda
Sto cercando di replicare la struttura di un'istruzione semplice if:
if (paren) { block } [else ({ block } | rec if (paren)) ]
Per il blocco if (paren), creo un nodo AST ifblock. Altrimenti, riempie ricorsivamente un nodo IFELSEBLOCK.
Ho provato parecchi costruzioni alternative
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
Suggerimenti?
Soluzione
Hai dato un'occhiata al mio parser GLSL (è un linguaggio simile a C)?http://laurent.le-brun.eu/fsharp/glsl_parse.fs
Dall'esempio del codice, ecco la parte pertinente per if
dichiarazione:
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
//...
]
Penso che il tuo problema sia che stai usando attempt
invece di opt
. opt
significa il else
la parte è facoltativa (se non è lì, ottieni None
). attempt
è abbastanza diverso:
Il parser
attempt p
applica il parserp
. Sep
fallisce dopo aver cambiato lo stato del parser o con un errore fatale,attempt p
tornerà a fare un backtrack allo stato di parser originale e segnalerà un errore non fatale.
Quando il parser dentro attempt
fallisce, c'è ancora un errore ma l'input non è consumato (è utile se combinato con il <|>
o choice
operatori).