2017-05-11 5 views
0

람다 - 미적분을 기반으로 한 프로그래밍 언어 용 파서를 만들고 있습니다. 중위 연산자와 우선 순위를 추가하지만 구문 분석기가 부정적인 우선 순위에 대한 오류와 충돌합니다. 나는 수작업으로 파싱을 할 수는 있지만, 우선권을 얻을 수없는 것 같다. 그래서 나는 OperatorPrecedenceParser를 사용하는 법을 배울 것입니다.OperatorPrecedenceParser 내가 가지고 있지 않은 부정적인 우선 순위에 대한 예외를 throw합니다.

부정적인 우선 순위가 없기 때문에 내가 왜 충돌하는지 잘 모르기 때문에 코드를 보여줄 것입니다.

언어 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"; 
     ","; 
     "::" 
    ] 

파서 자체입니다. 파서 결합 자 (그리고 파싱)를 처음 사용하는 것은 너무나 잘못된 것이 있다면 알고 싶습니다. 그렇지 않으면 왜 단지 충돌이 일어 났는지 아는 것만으로도 충분합니다.

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 

답변

0

귀하의 샘플 코드는 expOpbinOp이 포함되지 않기 때문에, 완성 될 것 같지 않습니다. 마지막 두 줄을 사용하지 않고 코드를 실행하면 OPP는 "연산자 우선 순위가 0보다 커야합니다"라는 메시지와 함께 ArgumentOutOfRangeException을 발생시킵니다. 쉼표 연산자가 추가 될 때. 문제는 쉼표 연산자의 우선 순위를 0으로 지정했기 때문입니다.

Visual Studio와 같은 완전히 통합 된 디버거와 함께 IDE를 사용하면 이러한 문제를 쉽게 진단 할 수 있습니다.

+0

expOp와 binOp의 경우 중요하지 않습니다. 나는 그들을 지우는 것을 잊었다. 그것들은 내가 만들려고 노력한 우선권 파서를 굴려 놓은 것이다. 나는 메시지에 대한 예외에 대해 알았다. 나는 0이 유효한 우선 순위라고 생각했다. 의사는 다음과 같이 말합니다 : 값은 항상 더 큰 0입니다. 실종 된 것이 있습니까? – ZelteHonor

+0

예, 문서의 텍스트가 "미만"으로 누락되었습니다. 그 점을 지적 해 주셔서 감사합니다! 나는 제로가 허용 된 가치라고 생각하게 만든 이유가 궁금합니다. "0보다 큰"을 "부정적이지 않은"것으로 해석 했습니까? (0을 허용하고 싶다면 "0보다 크거나 같음"이라고 쓰였습니다.) –

+0

나는 그것을 빨리 읽었습니다. 실제로 그것이 틀린 것처럼 보이기 때문에 내가 어떻게 잘못 읽었는지 정확히 알지 못합니다. 수학자가 종종 쓰는 것에 익숙하기 때문에 그럴 수도 있습니다 : 0보다 엄밀히 말하면. – ZelteHonor