2013-07-26 3 views
2

필자는 선택 사항 인 조합자가 내 파서 목록에 pzero 파서를 암시 적으로 추가하고 fparsec이 입력 스트림의 다음 부분을 구문 분석하지 못하면 대괄호를 찾아야합니다. 여기 왜 "between"이 "choice"를 적용한 파서로 사용할 수 없습니까?

최소한의 전체 코드입니다 :

open System 
open System.Collections.Generic 
open FParsec 

type IDL = 
    |Library of string * IDL list 
    |ImportLib of string 
    |ImportAlias of string 

let comment : Parser<unit,unit> = pstring "//" >>. skipRestOfLine true >>. spaces 
let ws = spaces >>. (opt comment) 
let str s = pstring s >>. ws 
let identifierString = ws >>. many1Satisfy isLetter .>> ws // [A-z]+ 
let identifierPath = ws >>. many1Satisfy (fun c -> isLetter c || isDigit c || c = '.' || c = '\\' || c = '/') .>> ws // valid path letters 
let keywords = ["importlib"; "IMPORTLIB"; "interface"; "typedef"; "coclass"] 
let keywordsSet = new HashSet<string>(keywords) 
let isKeyword (set : HashSet<string>) str = set.Contains(str) 

let pidentifier set (f : Parser<string, unit>) : Parser<string, unit> = 
    let expectedIdentifier = expected "identifier" 
    fun stream -> 
     let state = stream.State 
     let reply = f stream 
     if reply.Status <> Ok || not (isKeyword set reply.Result) then 
      printf "got id %s\n" reply.Result 
      ws stream |> ignore 
      reply 
     else // result is keyword, so backtrack to before the string 
      stream.BacktrackTo(state) 
      Reply(Error, expectedIdentifier) 

let identifier = pidentifier keywordsSet 

let stmt, stmtRef = createParserForwardedToRef() 

let stmtList = sepBy1 stmt (str ";") 

let importlib = 
    str "importlib" >>. 
     between (str "(" .>> str "\"") (str "\"" >>. str ")") 
      (identifier identifierPath) |>> ImportLib 

let importalias = 
    str "IMPORTLIB" >>. 
     between (str "(") (str ")") 
      (identifier identifierString) |>> ImportAlias 

let library = 
    pipe2 
     (str "library" >>. identifier identifierString) 
     (between (str "{") (str "}") stmtList) 
     (fun lib slist -> Library(lib, slist)) 

do stmtRef:= choice [importlib; importalias] 

let prog = 
    ws >>. library .>> ws .>> eof 

let s = @" 
library ModExpress 
{ 
    importlib(""stdole2.tlb""); 
    importlib(""msxml6.dll""); 
}" 

let test p str = 
    match run p str with 
    | Success(result, _, _) -> printfn "Success: %A" result 
    | Failure(errorMsg, _, _) -> printfn "Failure: %s" errorMsg 

test prog s 

System.Console.Read() |> ignore 

하지만 입력 문자열

library ModExpress 
{ 
     importlib(""stdole2.tlb""); 
     importlib(""msxml6.dll""); 
} 

위해 나는 다음과 같은 오류가 발생했습니다 :

Failure: Error in Ln: 6 Col: 1 
} 
^ 
Expecting: '//', 'IMPORTLIB' or 'importlib' 

답변

3

이 문제가 여기에 있다는 것을 보인다 stmtList 파서는 sepBy1 연결자로 구현됩니다. sepBy1 stmt sep은 의 하나 이상의 발생을 sep (즉 EBNF : p (sep p) *)으로 구분 (종료하지 않음)하여 파싱합니다. 파서가 importlib (""msxml6.dll "") 다음에 세미콜론을 볼 때 공백 다음에 다른 명령문을 기다립니다.

당신이 선택 성명 목록의 마지막에 세미콜론을 확인하려면, 당신은 단순히 sepBy1 대신 sepEndBy1를 사용할 수 있습니다, 또는 당신은 항상 세미콜론을 요구하려는 경우, 당신은

let stmtList = many1 stmt 

do stmtRef:= choice [importlib; importalias] .>> str ";" 
+0

감사합니다 사용할 수 있습니다 , 스테판 - sepEndBy1과 완벽하게 작동합니다. – ssp