[英]error in BNF fparsec parser
I have made the following parser to try to parse BNF: 我做了以下解析器来尝试解析BNF:
type Literal = Literal of string
type RuleName = RuleName of string
type Term = Literal of Literal
| RuleName of RuleName
type List = List of Term list
type Expression = Expression of List list
type Rule = Rule of RuleName * Expression
type BNF = Syntax of Rule list
let pBFN : Parser<BNF, unit> =
let pWS = skipMany (pchar ' ')
let pLineEnd = skipMany1 (pchar ' ' >>. newline)
let pLiteral =
let pL c = between (pchar c) (pchar c) (manySatisfy (isNoneOf ("\n" + string c)))
(pL '"') <|> (pL '\'') |>> Literal.Literal
let pRuleName = between (pchar '<') (pchar '>') (manySatisfy (isNoneOf "\n<>")) |>> RuleName.RuleName
let pTerm = (pLiteral |>> Term.Literal) <|> (pRuleName |>> Term.RuleName)
let pList = sepBy1 pTerm pWS |>> List.List
let pExpression = sepBy1 pList (pWS >>. (pchar '|') .>> pWS) |>> Expression.Expression
let pRule = pWS >>. pRuleName .>> pWS .>> pstring "::=" .>> pWS .>>. pExpression .>> pLineEnd |>> Rule.Rule
many1 pRule |>> BNF.Syntax
For testing, I'm running it on BNF's BNF as per Wikipedia : 为了进行测试,我根据Wikipedia在BNF的BNF上运行它:
<syntax> ::= <rule> | <rule> <syntax>
<rule> ::= <opt-whitespace> "<" <rule-name> ">" <opt-whitespace> "::=" <opt-whitespace> <expression> <line-end>
<opt-whitespace> ::= " " <opt-whitespace> | ""
<expression> ::= <list> | <list> <opt-whitespace> "|" <opt-whitespace> <expression>
<line-end> ::= <opt-whitespace> <EOL> | <line-end> <line-end>
<list> ::= <term> | <term> <opt-whitespace> <list>
<term> ::= <literal> | "<" <rule-name> ">"
<literal> ::= '"' <text> '"' | "'" <text> "'"
But it always fails with this error: 但是它总是会因以下错误而失败:
Error in Ln: 1 Col: 21
<syntax> ::= <rule> | <rule> <syntax>
^
Expecting: ' ', '"', '\'' or '<'
What am I doing wrong? 我究竟做错了什么?
Edit 编辑
The function I'm using to test: 我用来测试的功能:
let test =
let text = "<syntax> ::= <rule> | <rule> <syntax>
<rule> ::= <opt-whitespace> \"<\" <rule-name> \">\" <opt-whitespace> \"::=\" <opt-whitespace> <expression> <line-end>
<opt-whitespace> ::= \" \" <opt-whitespace> | \"\"
<expression> ::= <list> | <list> <opt-whitespace> \"|\" <opt-whitespace> <expression>
<line-end> ::= <opt-whitespace> <EOL> | <line-end> <line-end>
<list> ::= <term> | <term> <opt-whitespace> <list>
<term> ::= <literal> | \"<\" <rule-name> \">\"
<literal> ::= '\"' <text> '\"' | \"'\" <text> \"'\""
run pBNF text
Your first problem is with pList
: sepBy1
is greedily grabbing trailing spaces, but once it does that it then expects an additional term to follow rather than the end of the list. 您的第一个问题是
pList
: sepBy1
贪婪地抓取尾随空格,但是一旦这样做,它就会期望后面跟随一个附加术语,而不是列表的末尾。 The simplest way to fix this is to use sepEndBy1
instead. 解决此问题的最简单方法是改用
sepEndBy1
。
This will expose your next problem: pEndLine
isn't faithfully implemented because you're always looking for exactly one space followed by a newline, when you should be looking for any number of spaces instead (that is, you want pWS >>. newline
in the interior, rather than pchar ' ' >>. newline
). 这将暴露您的下一个问题:
pEndLine
没有如实实现,因为当您应该查找任何数量的空格时(即,您希望pWS >>. newline
在内部,而不是pchar ' ' >>. newline
)。
Finally, note that your definition requires each rule to end with a newline, so you won't be able to parse your string as given (you'll need to append an empty line to the end). 最后,请注意,您的定义要求每个规则都以换行符结尾,因此您将无法解析给定的字符串(您需要在末尾添加一个空行)。 Instead you might want to pull
newline
out of your definition of pRule
and define the main parser as sepBy1 pRule pLineEnd |>> BNF.Syntax
. 相反,你可能想拉
newline
出你定义的pRule
和定义主解析器sepBy1 pRule pLineEnd |>> BNF.Syntax
。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.