Ich erstelle einen Parser für eine Programmiersprache basierend auf dem Lambda-Kalkül. Ich füge Infix-Operator und ihre Priorität hinzu, aber der Parser stürzt mit einem Fehler über negative Priorität ab. Ich bin in der Lage, den Operator manuell zu parsen, aber es scheint, dass ich die Priorität nicht richtig bekommen kann. Also habe ich gelernt, dass ich auch den OperatorPrecedenceParser verwenden kann.OperatorPrecedenceParser werfen Ausnahme über negative Priorität, die ich nicht habe
Ich werde den Code zeigen, weil ich keine Ahnung habe, warum es abstürzt, da ich keine negative Priorität habe.
Die Sprache AST
module MiniML
type Exp =
| C of Cst
| Id of Id
| Lam of Id * Exp
| App of Exp * Exp
| Let of Id * Exp * Exp
| Pair of Exp * Exp
| If of Exp * Exp * Exp
and Cst = I of int | B of bool | Unit | Nil
and Id = string;;
let op = ["+";
"-";
"*";
"/";
"=";
"<";
">";
"@";
"and";
"or";
",";
"::"
]
Hier ist der Parser selbst. Es ist mein erstes Mal mit Parser-Kombinator (und Parsing), also wenn es etwas schrecklich falsch ist, würde ich gerne wissen. Sonst wüsste ich nur, warum es krachen würde.
open MiniML
open FParsec
let ws = spaces
let operator : Parser<MiniML.Id,unit> = op |> List.map pstring |> choice
let keyword : Parser<string,unit> = ["false";"true";"let";"end";"in";"if";"then";"else";"lam"] |> List.map pstring |> choice
let fstId = asciiLetter <|> pchar '_'
let restId = fstId <|> digit <|> pchar '''
let betweenPar p = between (pchar '(' .>> ws) (pchar ')' .>> ws) p
let cstB = (stringReturn "true" (B true)) <|> (stringReturn "false" (B false))
let cstI = puint32 |>> (int >> I)
let cstU = stringReturn "()" Unit
let cstN = stringReturn "[]" Nil
let expC : Parser<Exp,unit> = cstB <|> cstI <|> cstU <|> cstN |>> C
let expIdStr = notFollowedByL keyword "Cannot use keyword as variable" >>.
notFollowedByL operator "Cannot use operator as variable" >>.
many1CharsTill2 fstId restId (notFollowedBy restId)
let expId : Parser<Exp,unit> = expIdStr |>> (MiniML.Exp.Id)
let exp, expRef = createParserForwardedToRef<Exp, unit>()
let expApp, expAppRef = createParserForwardedToRef<Exp, unit>()
let expLam : Parser<Exp,unit> = (pstring "lam" >>. ws >>. expIdStr .>> ws .>> pchar '.') .>> ws .>>. exp |>> Lam
let expLet = tuple3 (pstring "let" >>. ws >>. expIdStr .>> ws .>> pchar '=' .>> ws) (exp .>> ws .>> pstring "in" .>> ws) (exp .>> ws .>> pstring "end") |>> Let
let expIf = tuple3 (pstring "if" >>. ws >>. exp .>> ws) (pstring "then" >>. ws >>. exp .>> ws) (pstring "else" >>. ws >>. exp) |>> If
let closeEXP, closeEXPRef = createParserForwardedToRef<Exp, unit>()
let expBang = (pstring "!" >>% MiniML.Id "!") .>>. closeEXP |>> App
let buildList (el,ef) =
let rec go l = match l with
| (e::es) -> App(MiniML.Id "cons", Pair(e,go es))
| [] -> C Nil
go (el @ [ef])
let expList = between (pchar '[' .>> ws) (pchar ']') (many (exp .>>? (ws .>> pchar ';' .>> ws)) .>>. exp .>> ws
|>> buildList)
do closeEXPRef := choice [expC ; expId ; expBang ; betweenPar exp ; expList] .>> ws
do expAppRef := many1 closeEXP |>> (function (x::xs) -> List.fold (fun x y -> App(x,y)) x xs | [] -> failwith "Impossible")
let opOpp : InfixOperator<Exp,unit,unit> list =
[
InfixOperator("*", ws, 6, Associativity.Left, fun x y -> App(MiniML.Id "*",Pair(x,y)));
InfixOperator("/", ws, 6, Associativity.Left, fun x y -> App(MiniML.Id "/",Pair(x,y)));
InfixOperator("+", ws, 5, Associativity.Left, fun x y -> App(MiniML.Id "+",Pair(x,y)));
InfixOperator("-", ws, 5, Associativity.Left, fun x y -> App(MiniML.Id "-",Pair(x,y)));
InfixOperator("::", ws,4, Associativity.Right, fun x y -> App(MiniML.Id "cons",Pair(x,y)));
InfixOperator("=", ws, 3, Associativity.Left, fun x y -> App(MiniML.Id "=",Pair(x,y)));
InfixOperator("<", ws, 3, Associativity.Left, fun x y -> App(MiniML.Id "<",Pair(x,y)));
InfixOperator(">", ws, 3, Associativity.Left, fun x y -> App(MiniML.Id ">",Pair(x,y)));
InfixOperator("and", ws, 2, Associativity.Right, fun x y -> App(MiniML.Id "and",Pair(x,y)));
InfixOperator("or", ws, 1, Associativity.Right, fun x y -> App(MiniML.Id "or",Pair(x,y)));
InfixOperator(",", ws,0, Associativity.None, fun x y -> Pair(x,y))
]
let opp = new OperatorPrecedenceParser<Exp,unit,unit>()
let expr = opp.ExpressionParser
let term = exp <|> betweenPar expr
opp.TermParser <- term
List.iter (fun x -> opp.AddOperator(x)) opOpp
do expRef := [expLam;expIf;expLet;expApp] |> choice |> (fun p -> p .>>. opt (expOp operator) |>> binOp)
let mainExp = expr .>> eof
Für expOp und binOp sind sie nicht wichtig. Ich habe vergessen, sie zu löschen. Sie sind für den Hand-Rolled-Priority-Parser, den ich versucht habe zu machen. Ich wusste über die Ausnahme mit der Nachricht. Ich dachte, dass 0 eine gültige Priorität ist. Das Dokument sagt: Der Wert ist immer größer Null. Gibt es ein als vermisst? – ZelteHonor
Ja, der Text in der Dokumentation fehlt ein "als". Danke, dass du das unterstrichen hast! Ich bin neugierig, was Sie denken ließ, dass Null ein zulässiger Wert ist. Haben Sie "größer (als) Null" als "nicht negativ" interpretiert? (Wenn ich 0 erlauben wollte, hätte ich "größer als oder gleich null" geschrieben.) –
Ich habe es schnell gelesen. Ich weiß nicht genau, wie ich es falsch verstanden habe, weil es tatsächlich klar scheint. Vielleicht liegt es daran, dass ich mich an Mathematiker gewöhnt habe, die oft schreiben: Streng größer als 0. – ZelteHonor