Parsing 如何处理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

我使用LL(k)EBNF语法来解析字符流。我需要三种不同类型的代币:

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> ")"
.