gpt4 book ai didi

f# - 为什么组合器 "between"不能与 "choice"作为应用解析器一起使用?

转载 作者:行者123 更新时间:2023-12-04 04:48:00 24 4
gpt4 key购买 nike

据我了解选择 组合器隐式附加 pzero parser 到我的解析器列表,当 fparsec 无法解析输入流的下一部分时,它应该搜索括号。

这是最小的完整代码:

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'

最佳答案

看来这里的问题是stmtList解析器是用 sepBy1 实现的组合器。 sepBy1 stmt sep解析 p 的一次或多次出现由 sep 分隔(但未结束) ,即在 EBNF 中:p (sep p)*。当解析器在 importlib(""msxml6.dll"") 之后看到分号时,它期望在空格之后有另一个语句。

如果你想让语句列表末尾的分号是可选的,你可以简单地使用 sepEndBy1而不是 sepBy1 ,或者如果你总是想要一个分号,你可以使用

let stmtList = many1 stmt

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

关于f# - 为什么组合器 "between"不能与 "choice"作为应用解析器一起使用?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/17890659/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com