람다 - 미적분을 기반으로 한 프로그래밍 언어 용 파서를 만들고 있습니다. 중위 연산자와 우선 순위를 추가하지만 구문 분석기가 부정적인 우선 순위에 대한 오류와 충돌합니다. 나는 수작업으로 파싱을 할 수는 있지만, 우선권을 얻을 수없는 것 같다. 그래서 나는 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
expOp와 binOp의 경우 중요하지 않습니다. 나는 그들을 지우는 것을 잊었다. 그것들은 내가 만들려고 노력한 우선권 파서를 굴려 놓은 것이다. 나는 메시지에 대한 예외에 대해 알았다. 나는 0이 유효한 우선 순위라고 생각했다. 의사는 다음과 같이 말합니다 : 값은 항상 더 큰 0입니다. 실종 된 것이 있습니까? – ZelteHonor
예, 문서의 텍스트가 "미만"으로 누락되었습니다. 그 점을 지적 해 주셔서 감사합니다! 나는 제로가 허용 된 가치라고 생각하게 만든 이유가 궁금합니다. "0보다 큰"을 "부정적이지 않은"것으로 해석 했습니까? (0을 허용하고 싶다면 "0보다 크거나 같음"이라고 쓰였습니다.) –
나는 그것을 빨리 읽었습니다. 실제로 그것이 틀린 것처럼 보이기 때문에 내가 어떻게 잘못 읽었는지 정확히 알지 못합니다. 수학자가 종종 쓰는 것에 익숙하기 때문에 그럴 수도 있습니다 : 0보다 엄밀히 말하면. – ZelteHonor