Parsing 如何处理EBNF语法中不同标记中的重叠字符组?
我使用LL(k)EBNF语法来解析字符流。我需要三种不同类型的代币:Parsing 如何处理EBNF语法中不同标记中的重叠字符组?,parsing,language-agnostic,token,grammar,ebnf,Parsing,Language Agnostic,Token,Grammar,Ebnf,我使用LL(k)EBNF语法来解析字符流。我需要三种不同类型的代币: CHARACTERS letter = 'A'..'Z' + 'a'..'z' . digit = "0123456789" . messageChar = '\u0020'..'\u007e' - ' ' - '(' - ')' . TOKENS num = ['-'] digit { digit } [ '.' digit { digit } ] . ident = letter { letter
CHARACTERS
letter = 'A'..'Z' + 'a'..'z' .
digit = "0123456789" .
messageChar = '\u0020'..'\u007e' - ' ' - '(' - ')' .
TOKENS
num = ['-'] digit { digit } [ '.' digit { digit } ] .
ident = letter { letter | digit | '_' } .
message = messageChar { messageChar } .
前两个令牌声明很好,因为它们不共享任何公共字符
但是,第三个字符串,message
,是无效的,因为有些字符串可能是num
和message
(例如“123”
),而其他字符串可能是标识和message
(例如“Hello”
)。因此,标记器无法正确区分
另一个例子是区分整数和实数。除非您要求所有实数至少有一位小数(意味着1需要编码为1.0,这对我来说不是一个选项),否则我无法从语法中获得这两种数字类型之间差异的支持。我必须将所有值都表示为real,并在点之后进行检查。这很好,但不是最优的。我真正的问题是消息
令牌。我找不到解决办法
所以问题是,我可以用LL(k)EBNF语法来做这件事吗?我正在使用生成解析器和扫描程序
如果我不能使用LL(k)EBNF,那么我还可以考虑哪些其他选项
编辑这是我从CoCo/R获得的输出:
Coco/R (Apr 23, 2010)
Tokens double and message cannot be distinguished
Tokens ident and message cannot be distinguished
...
9 errors detected
Coco/R(2010年4月23日)
无法区分标记和消息
无法区分标识和消息
...
检测到9个错误
尽管有标题,这一切似乎都与扫描器有关,而不是与解析器有关。我没有使用CoCo/R,所以我不能直接对其进行评论,但在典型的(例如,lex/Flex)扫描程序中,规则是按顺序考虑的,因此选择的规则/模式是第一个匹配的规则/模式。我写过的大多数扫描器都包含一个“.”(即,匹配任何内容)作为最后一个模式,如果有一些输入与任何其他规则不匹配,则会显示一条错误消息。您可能需要研究一个具有上下文敏感标记化的PEG生成器
我想不出用COCO/R或类似的方法来解决这个问题,因为每个标记都需要明确
如果消息被引号包围,或者其他一些消除歧义的方法,那么您就不会有问题。我真的认为PEG可能是你的答案,因为它也有顺序选择(第一场比赛)
另请看:
试试这个:
CHARACTERS
letter = 'A'..'Z' + 'a'..'z' .
digit = "0123456789" .
messageChar = '\u0020'..'\u007e' - ' ' - '(' - ')' .
TOKENS
double = ['-'] digit { digit } [ '.' digit { digit } ] .
ident = letter { letter | digit | '_' } .
message = messageChar { messageChar } CONTEXT (")") .
哦,我必须指出,'\u0020'
是unicode空间,您随后将使用“-”
”删除它。哦,如果您不需要多个字符的前瞻,那么可以使用CONTEXT('))
。这在您的情况下不起作用,因为上面的所有标记都可能出现在')
之前
FWIW:CONTEXT
不使用所包含的序列,您仍必须在生产中使用它
编辑:
好的,这似乎有效。真的,这次我是认真的:)
编辑(2011年4月9日)
因子
(.value=0.0;)
=
(
整数
(.value=Convert.ToDouble(t.val);)
|
实数
(.value=Convert.ToDouble(t.val);)
)
|“(“表达式”)”
.
或
因子
(.value=0.0;)
=
(intNumber | realNumber)
(.value=Convert.ToDouble(t.val);)
|“(“表达式”)”
.
在CoCo/R中,您可以在一个文件中指定标记和语法。CoCo/R似乎正在检查这种模糊性。我试着重新排序我的声明,但没有看到任何区别。我会再试几次。太棒了。这听起来正是我需要的。我设法把这件事推迟到现在,所以你的回答正是时候。我曾考虑将所有标记合并到一个通用的“符号”定义中,但这听起来更好。我会让你知道我的进展。您是否可以对任何潜在的性能影响进行评论?您可能会发现它稍微慢一点,这取决于解析器生成器。如果考虑到速度的话,手工制作东西应该很容易。如果你能告诉我你打算使用哪种语言/平台(例如Java/JVM、C#/.NET、C++),那么我也许可以提出一些建议。@Drew:如果你能提供一个你想要处理的输入的经过净化的示例,那也会有帮助。当我设计DSL时,我倾向于先编写几个示例,然后再从那里开始工作(这些示例也作为一些单元测试的输入)。@Andre:实际上我在解析其他人的格式。这是一系列的性表达。每个SExpression都应转换为不同的对象类型。关于显式解析SExpressions,我问了另一个问题(),因为对于这种简单的结构化数据格式,可能不需要完整的语法。您可以在这里看到数据示例:有几个重复模式。例如,(pol)
应该映射到我的PolarCoordinate
类型。我有一个字符流的性表达:(…)(…)(…)(…)
。理想情况下,我希望直接处理流,并为序列中的每个表达式吐出一个对象。嘿,Andre,谢谢。我只是想重温一下这个问题并测试一下你的代码。我尝试了很多方法,但你的答案是唯一有效的。似乎CoCo/R扫描仪相当有限。例如,int和float类型不可能有标记,因为它们以相同的方式重叠。无论如何,再次谢谢你!Coco/R的局限性在于它是LL(1),但您可以为int和float使用令牌,如我添加的示例所示。你只需要一种方法来区分。啊,好吧,我知道我遗漏了什么。我想我希望的是,realNumber
token可能有一个可选的小数位和实数部分。在您的示例中,1234
不是一个实数,即使在数学上我
CHARACTERS
letter = 'A'..'Z' + 'a'..'z' .
digit = "0123456789" .
// messageChar = '\u0020'..'\u007e' - ' ' - '(' - ')' .
TOKENS
double = ['-'] digit { digit } [ '.' digit { digit } ] .
ident = letter { letter | digit | '_' } .
// message = letter { messageChar } CONTEXT (')') .
// MessageText<out string m> = message (. m = t.val; .)
// .
HearExpr<out HeardMessage message> =
(.
TimeSpan time;
Angle direction = Angle.NaN;
string messageText = "";
.)
"(hear"
TimeSpan<out time>
( "self" | AngleInDegrees<out direction> )
// MessageText<out messageText>
{
ANY (. messageText += t.val; .)
}
')'
(.
message = new HeardMessage(time, direction, new Message(messageText));
.)
.
COMPILER Calculator
CHARACTERS
digit = "0123456789".
TOKENS
intNumber = ['-'] digit { digit } .
realNumber = ['-'] { digit } "." digit { digit }
[("e" | "E") ["+" | "-"] digit {digit}] .
PRODUCTIONS
Calculator = { Expression "=" } .
Expression = Term { "+" Term | "-" Term }.
Term = Factor { "*" Factor | "/" Factor }.
Factor = intNumber | realNumber .
END Calculator.
Factor<out double value>
(. value = 0.0; .)
=
(
intNumber
(. value = Convert.ToDouble(t.val); .)
|
realNumber
(. value = Convert.ToDouble(t.val); .)
)
| "(" Expression<out value> ")"
.
Factor<out double value>
(. value = 0.0; .)
=
( intNumber | realNumber )
(. value = Convert.ToDouble(t.val); .)
| "(" Expression<out value> ")"
.