BNF fparsec 解析器中的错误
error in BNF fparsec parser
我制作了以下解析器来尝试解析 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
为了测试,我 运行 根据 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> "'"
但它总是失败并出现以下错误:
Error in Ln: 1 Col: 21
<syntax> ::= <rule> | <rule> <syntax>
^
Expecting: ' ', '"', '\'' or '<'
我做错了什么?
编辑
我用来测试的函数:
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
你的第一个问题是 pList
:sepBy1
贪婪地抓住尾随的 spaces,但是一旦它这样做了,它就会期待一个额外的术语跟随而不是结束名单。解决此问题的最简单方法是改用 sepEndBy1
。
这将暴露你的下一个问题:pEndLine
没有得到忠实的实现,因为你总是在寻找一个 space 后跟一个换行符,而你应该寻找任意数量的spaces 而不是(也就是说,您希望在内部使用 pWS >>. newline
,而不是 pchar ' ' >>. newline
)。
最后,请注意您的定义要求每个规则都以换行符结尾,因此您将无法按给定的方式解析字符串(您需要在末尾附加一个空行)。相反,您可能希望从 pRule
的定义中提取 newline
并将主解析器定义为 sepBy1 pRule pLineEnd |>> BNF.Syntax
.
我制作了以下解析器来尝试解析 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
为了测试,我 运行 根据 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> "'"
但它总是失败并出现以下错误:
Error in Ln: 1 Col: 21
<syntax> ::= <rule> | <rule> <syntax>
^
Expecting: ' ', '"', '\'' or '<'
我做错了什么?
编辑
我用来测试的函数:
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
你的第一个问题是 pList
:sepBy1
贪婪地抓住尾随的 spaces,但是一旦它这样做了,它就会期待一个额外的术语跟随而不是结束名单。解决此问题的最简单方法是改用 sepEndBy1
。
这将暴露你的下一个问题:pEndLine
没有得到忠实的实现,因为你总是在寻找一个 space 后跟一个换行符,而你应该寻找任意数量的spaces 而不是(也就是说,您希望在内部使用 pWS >>. newline
,而不是 pchar ' ' >>. newline
)。
最后,请注意您的定义要求每个规则都以换行符结尾,因此您将无法按给定的方式解析字符串(您需要在末尾附加一个空行)。相反,您可能希望从 pRule
的定义中提取 newline
并将主解析器定义为 sepBy1 pRule pLineEnd |>> BNF.Syntax
.