繁体   English   中英

JavaCC:如何从令牌中排除字符串? (也要理解令牌的歧义。)

[英]JavaCC: How can one exclude a string from a token? (A.k.a. understanding token ambiguity.)

我已经在理解方面有很多问题,如何在JavaCC中优雅地(或完全以某种方式)处理歧义标记。 让我们来看这个例子:

我想解析XML处理指令。

格式为: "<?" <target> <data> "?>" "<?" <target> <data> "?>"target是一个XML名称, data可以是?>以外的任何东西 ,因为它是结束标记。

因此,让我们在JavaCC中定义它:
(我使用词法状态,在这种情况下为DEFAULTPROC_INST

TOKEN : <#NAME : (very-long-definition-from-xml-1.1-goes-here) >
TOKEN : <WSS : (" " | "\t")+ >   // WSS = whitespaces
<DEFAULT> TOKEN : {<PI_START : "<?" > : PROC_INST}
<PROC_INST> TOKEN : {<PI_TARGET : <NAME> >}
<PROC_INST> TOKEN : {<PI_DATA : ~[] >}   // accept everything
<PROC_INST> TOKEN : {<PI_END : "?>" > : DEFAULT}

现在可以识别处理指令的部分:

void PROC_INSTR() : {} {
(
    <PI_START>
    (t=<PI_TARGET>){System.out.println("target: " + t.image);}
    <WSS>
    (t=<PI_DATA>){System.out.println("data: " + t.image);}
    <PI_END>
) {}
}

让我们用<?mytarget here-goes-some-data?>测试一下:

识别目标: "target: mytarget" 但是现在我得到了我最喜欢的 JavaCC解析错误:

!!  procinstparser.ParseException: Encountered "" at line 1, column 15.
!!  Was expecting one of:
!!      

没遇到什么? 没期待什么? 要不然是啥? 谢谢JavaCC!

我知道,我可以使用JavaCC的MORE关键字,但是这会将整个处理指令作为一个令牌提供给我,因此我不得不自己进一步解析/令牌化。 我为什么要那么做? 我正在编写不解析的解析器吗?

问题是(我猜):因此<PI_DATA>识别“所有”,我的定义是错误的。 我应该告诉JavaCC将“ ?>以外的所有内容”识别为处理指令数据。

但是怎么办呢?

注意:我只能使用~["a"|"b"|"c"]排除单个字符而不能排除诸如~["abc"]~["?>"] 字符串 JavaCC的另一个强大功能。

谢谢。

关于令牌生成器的一句话

分词器(* TokenManager)匹配尽可能多的输入字符。 PI_DATA是“〜[]”(1个字符),因此如果找不到更长的匹配项,它将与任何单个输入字符匹配。 PI_END是“?>”(2个字符),因此它将始终匹配而不是PI_DATA。 语法的这一部分是正确的。

意外的嫌疑人

问题可能实际上来自NAME。 您没有写该令牌的实际定义,所以我只能对此做一些假设。 如果NAME的定义过于贪婪 ,它将在PROC_INST状态下匹配太多输入字符,并且您可能永远不会遇到PI_DATA或PI_END。

注意带有空格的“(...)+”,或邪恶的“(〜[])*”,它吞噬了EOF之前的所有内容。

其他嫌疑犯

我看到一个潜在的问题是,尽管您希望PI_DATA被匹配,但PI_TARGET可能会被匹配多次。 再说一次,我只能猜测,因为我没有NAME的定义。

您可能需要澄清的另一点是:您定义了WSS令牌,但没有在状态PROC_INST中使用它。 它应该是PI_DATA的一部分吗? 如果没有,您可能要跳过它。

不要滥用令牌生成器

如果发现无法使标记生成器服从您,则可能需要将棘手的部分移至解析器。 在您的情况下,可能很难区分PI_TARGET和PI_DATA(如上所述)。

解析器可以期望在PI目标之后获得 PI数据,而令牌生成器不能(或几乎没有)期望从令牌到下一个令牌。

解析器的另一个优点是,您甚至可以编写Java代码来窥视下一个标记并做出相应的反应。 这应该被认为是万不得已的方法,但是当您必须执行诸如将多个令牌连接到一个众所周知的令牌之类的事情时,它可能会很有用。 这可能是您在这里寻找的内容(使用PI_END作为终止符 )。

最后一个窍门

这是一个简化语法的技巧:

  1. 跳过PI_START,但是将状态更改为PROC_INST
  2. 在PROC_INST中,将PI_DATA定义为MORE(并将其重命名为PI_DATA_CHAR,或者根本不命名)。
  3. 在PROC_INST中,从令牌图像中删除最后两个字符,发出PI_DATA并将状态更改为DEFAULT
  4. 在您的解析器产品中,将处理指令简单定义为,其中PI_DATA的令牌映像已准备好使用

JavaCC的(稀疏...)文档中提供了有关在标记化器操作中操作标记图像的详细信息。 就像设置StringBuffer的长度一样简单。

语法的一个问题是WSS仅在默认状态下适用。 改写为

<DEFAULT, PROC_INST> TOKEN : {< WSS: (" " | "\t")+ > \}

错误消息是它正在等待WSS,但是找到了“”。

关于排除整个字符串,常见问题解答中概述了几种方法。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM