【问题标题】:Using Flex to treat XML Schema xs:annotation elements as comments使用 Flex 将 XML Schema xs:annotation 元素视为注释
【发布时间】:2021-09-15 02:53:54
【问题描述】:

我正在尝试使用 Flex 来标记 XML Schema 文件。我想将 元素视为注释。以下是 XML Schema 中 元素的示例:

<xs:annotation>
    <xs:documentation>This is a comment for humans</xs:documentation>
    <xs:appinfo>This is a comment for machines</xs:appinfo>
</xs:annotation>

我正在遵循 Flex&Bison 书籍第 38 页上的示例,并且正在使用 COMMENT 状态。这是我正在采取的方法:在遇到

时开始评论
"<xs:annotation>"    { BEGIN(COMMENT) ; }

遇到结束标签时切换状态

<COMMENT>"</xs:annotation>"  { BEGIN(INITIAL); }

xs:annotation 开始标记和结束标记之间的注释是除&lt;&lt; 之外的任何字符,后跟除/&lt;/ 之外的任何字符,后跟除x 之外的任何字符, 或&lt;/x 后接除s 以外的任何字符,或&lt;/xs 后接除: 以外的任何字符,或&lt;/xs: 后接除a 以外的任何字符,或&lt;/xs:a 后接除@ 以外的任何字符987654336@

<COMMENT>([^<]|<[^/]|</[^x]|</x[^s]|</xs[^:]|</xs:[^a]|</xs:a[^n])+

不幸的是,Flex 给出了以下错误消息:

unrecognized rule

请问我做错了什么?

【问题讨论】:

    标签: regex flex-lexer lex


    【解决方案1】:

    / 是 (f)lex (“尾随上下文”)中的正则表达式运算符。要将其用作文字字符,它必须被引用、转义或包含在字符类中。所以其中任何一个都可以用来表示固定字符串&lt;/xs:

    <"/"xs:
    "</xs:"
    <\/xs:
    <[/]xs:
    

    如果您是 (f)lex 的新手,您可能不熟悉尾随上下文运算符或带引号的字符串等功能,这些功能在大多数正则表达式库中都没有实现。您可能需要花几分钟时间阅读flex manual section on pattern syntax。时间不长。


    识别开始和结束分隔符之间的文本的最简单模板如下:

    %x COMMENT
      // ...
    %%
    "<xs:annotation>"            { BEGIN(COMMENT) ; }
    <COMMENT>"</xs:annotation>"  { BEGIN(INITIAL); }
    <COMMENT>.|\n                ;
    <COMMENT><<EOF>>             { fputs("Unterminated comment\n", stderr): }
    
    

    (除了倒数第二行之外的所有内容都与您已有的相同。)

    值得注意的是它是如何工作的。模式.|\n 绝对匹配任何单个字符。但它不会干扰结束分隔符的检测,因为所谓的“最大咀嚼”规则规定词法扫描器总是在每个点接受最长的匹配。这是 (f)lex 生成的扫描器用来决定两个匹配模式中的哪一个适用的规则。 (如果多个规则产生相同的最长匹配,扫描器会选择文件中第一个出现的那个。)

    因此,当输入以 &lt;/xs:annotation&gt; 开头时,.|\n 模式将不匹配,因为存在(很多)更长的匹配项。

    你可以停在那里。但是,正如 John Levine 在 Flex 和 Bison 中指出的那样,这并不是最有效的解决方案,因为每个单独的字符都被视为单个标记。即使没有与令牌关联的操作,它仍然会产生与令牌匹配相关的所有开销。所以添加一个匹配更长序列的附加规则是有意义的;但是,这种较长的模式不得干扰对结束分隔符的识别。

    例如,我们可以添加规则:

    <COMMENT>[^<]+                ;
    

    这将匹配不包括&lt; 的任何字符序列,从而在文本上移动更少的标记。 (在 Flex 和 Bison 中,等效规则写为 ([^*]|\n)+,但不需要显式换行匹配,因为在 (f)lex 中,否定字符类确实匹配换行,除非在设置。)

    但请注意,我们仍然需要一个匹配单个字符的规则,因为上面的规则不会匹配 &lt;。由于这两个规则具有相同的作用(即什么都不做),我们可以将它们结合起来:

    <COMMENT>[^<]+|.              ;
    

    这几乎肯定是您应该停止阅读的地方 :-) 但是您似乎一直在寻求扩展此优化以匹配以 &lt; 开头的其他较长序列,这些序列与结束分隔符不匹配,所以我会注意到这种优化可以扩展,但这需要小心谨慎。例如,如果我们要写:

    <COMMENT>([^<]+|<[^/])|.      ;
    

    我们会发现(很快,如果我们编写了足够的单元测试:-))扫描仪无法识别

    <xs:annotation>This is not valid XML: <</xs:annotation>
    

    这对您的项目来说可能不是一个大问题,但在尝试手写否定正则表达式时需要考虑到这一点。正确的扩展名是:

    <COMMENT>([^<]+|<[^/<])|.     ;
    

    实际上,额外的令牌匹配所产生的开销确实很小,不值得英勇努力避免它。简单的版本几乎肯定足以满足所有实际用途。但是,它确实会带来不同的词汇开销,因为它会强制扫描器在每次遇到关闭分隔符以外的关闭标记时回退到先前的位置。回退的问题与其说是回退的成本——在大多数情况下,这种情况并不常见——不如说是词法规则中任何地方都存在回退阻止 flex 对词法分析器应用优化. (在没有回退的扫描仪中,没有必要在每个输入位置检查规则是否可以在该点匹配以保存回退信息。)

    为了消除回退,我们可以添加一个匹配结束分隔符的任何前缀的规则。 (由于&lt;在开始后没有出现在结束分隔符中,我们不必担心可能的重叠,如上所述。并非所有可能的结束分隔符都是这种情况。)

    <COMMENT><("/"(x(s(:(a(n(n(o(t(a(t(i(on?)?)?)?)?)?)?)?)?)?)?)?)?)?   ;
    

    但请注意,只有在词汇语法的其他地方没有后备时才值得这样做。因此,如果您不想仔细计算括号,请不要担心。

    【讨论】:

    • @Roger:谢谢。顺便说一句,虽然这应该有效(病理情况除外,但确实会出现),但我认为这不是完成任务的最佳方式。稍后我会尝试添加一些内容。
    • 你好@rici 哦!我渴望听到更好的方法来完成这项任务。我是 Flex 的新手,因此非常感谢您提供的任何提示!
    • 很好的解释!再次感谢@rici
    猜你喜欢
    • 1970-01-01
    • 2016-12-05
    • 1970-01-01
    • 1970-01-01
    • 2016-04-04
    • 1970-01-01
    • 1970-01-01
    • 2016-09-02
    • 1970-01-01
    相关资源
    最近更新 更多