2017-05-11 4 views
0

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 

Antwort

0

Ihr Beispielcode scheint nicht vollständig zu sein, da expOp und binOp nicht enthalten sind. Wenn ich Ihren Code ohne die letzten zwei Zeilen ausführen, gibt das OPP ArgumentOutOfRangeException mit der Nachricht "die Operator-Priorität muss größer als 0 sein" aus. wenn der Komma-Operator hinzugefügt wird. Das Problem ist, dass Sie 0 als Priorität für den Kommaoperator angegeben haben.

Solche Probleme sind einfacher zu diagnostizieren, wenn Sie eine IDE mit einem vollständig integrierten Debugger wie Visual Studio verwenden.

+0

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

+0

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.) –

+0

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