【问题标题】:Is it possible to describe block comments using EBNF?是否可以使用 EBNF 描述块注释?
【发布时间】:2019-08-27 19:47:56
【问题描述】:

说,我有以下 EBNF:

document    = content , { content } ;
content     = hello world | answer | space ;
hello world = "hello" , space , "world" ;
answer      = "42" ;
space       = " " ;

这让我可以解析如下内容:

hello world 42

现在我想用块注释来扩展这个语法。我怎样才能正确地做到这一点?

如果我开始简单:

document    = content , { content } ;
content     = hello world | answer | space | comment;
hello world = "hello" , space , "world" ;
answer      = "42" ;
space       = " " ;
comment     = "/*" , ?any character? , "*/" ;

我无法解析:

Hello /* I'm the taxman! */ World 42

如果我从上面的特殊情况进一步扩展语法,它会变得丑陋,但会解析。

document    = content , { content } ;
content     = hello world | answer | space | comment;
hello world = "hello" , { comment } , space , { comment } , "world" ;
answer      = "42" ;
space       = " " ;
comment     = "/*" , ?any character? , "*/" ;

但我仍然无法解析如下内容:

Hel/*p! I need somebody. Help! Not just anybody... */lo World 42

我将如何使用 EBNF 语法来做到这一点?还是根本不可能?

【问题讨论】:

  • 你用什么工具来测试你的语法?
  • @whydoubt 除了我的脑袋。

标签: parsing comments context-free-grammar ebnf


【解决方案1】:

假设您将“你好”视为一个标记,您不会想要任何东西来打破它。如果您需要这样做,则有必要分解规则:

hello_world = "h", {comment}, "e", {comment}, "l", {comment}, "l", {comment}, "o" ,
              { comment }, space, { comment },
              "w", {comment}, "o", {comment}, "r", {comment}, "l", {comment}, "d" ;

考虑到更广泛的问题,不将语言 cmets 描述为形式语法的一部分似乎很常见,而是将其作为旁注。但是,通常可以通过将注释视为等同于空格来完成:

space = " " | comment ;

您可能还需要考虑添加一条规则来描述连续的空格:

spaces = { space }- ;

清理您的最终语法,但将“hello”和“world”视为标记(即不允许它们分开),可能会导致如下结果:

document    = { content }- ;
content     = hello world | answer | space ;
hello world = "hello" , spaces , "world" ;
answer      = "42" ;
spaces      = { space }- ;
space       = " " | comment ;
comment     = "/*" , ?any character? , "*/" ;

【讨论】:

  • 在 ISO 14977 中,下划线是其他字符 (7.5),不应在元标识符中使用。在EBNF whitespacing in meta identifiers 上查看我的解释。
  • 在 ISO 14977 中,重复可以使用一个空的 except 来表示,因此一个或多个空格可以显示为 spaces = {" "}-;。在How to denote at least one repetition in EBNF? 上查看我的解释。
  • @RickSmith 我在其他地方看到过下划线,但没有意识到不应该使用它,所以我会相应地进行编辑。虽然一个或多个的符号看起来很奇怪,但也会对此进行编辑。
  • EBNF 名称下有许多变体,但 OP 在元标识符中使用了一个空格,这是 ISO 14977 特有的。您是否考虑过为您的答案使用预处理器?它将删除 cmets,同时保持其余文本不变。似乎它会提供一个更清洁的解决方案。
  • @RickSmith 啊哈!我只是使用了Wikipedia 语法,这似乎是 ISO 14977 好吧:D
【解决方案2】:

我将如何使用 EBNF 语法来做到这一点?还是根本不可能?

有些语言在预处理器中删除 cmets,有些用空格替换 cmets。删除 cmets 似乎是解决此问题的最简单方法。但是,此解决方案会从文字中删除 cmets,通常不会这样做。

document = preprocess, process;

preprocess = {(? any character ? - comment, ? append char to text ?)},
    ? text for input to process ?;

comment = "/*", {? any character ? - "*/"}, "*/", ? discard ?;

process = {content}-;

content = hello world | answer | spaces;

hello world = ("H" | "h"), "ello", spaces, ("W" | "w") , "orld";

answer = "42";

spaces = {" "}-;

给定的预处理器,

Hello /* I'm the taxman! */ World 42

生产

Hello  World 42

注意这两个空格。

而且,对于

Hel/*p! I need somebody. Help! Not just anybody... */lo World 42

生产

Hello World 42

【讨论】:

  • 大多数语言用空格字符替换 cmets(至少理论上是这样),所以 Hel/* ... */lo 是两个标记,而不是一个。
  • @rici 啊,理论与实践。所有(?)语言都定义了 cmets 以及它们可以放置的位置。 EBNF (6.7) 定义了 cmets,使得 cmets 可以被删除(或用空格替换)而不影响语法定义的语言。我熟悉的其他语言使用 end-of-line cmets,删除或替换空格无关紧要。但是,对于 LaTex,需要删除,因为任何替换空间都会成为文档的一部分。
  • EBNF 不允许将 cmets 放置在句法元素的中间,因此您不能在注释周围进行标记粘贴。这与 C、C++、C#、CSS、D、EcmaScript(及其变体)、Java、PHP 以及可能的许多其他语言中使用的块注释语法 (/*...*/) 相同,例如,类似于块 cmets 、Lua 和 Haskell。当我说“理论上”时,我的意思是语言处理器不需要用单个空格字符物理替换注释;它只需要表现得好像发生了那样(它可能仍然会在错误消息中正确计算行号)。
  • @rici 如果 EBNF 中的注释错位,那将是语法错误;因此移除或更换是无关紧要的。问题是:How would I do this with an EBNF grammar? Or is it not even possible at all? 我提供了一个方法并相信它可以做到。这可能不是一个好方法——当然不是唯一的方法——但它是一种方法(解释Nietzsche)。我对你所说的没有异议;它似乎不适用于这个问题。也许 OP 稍后会询问更多。
  • 谢谢!很抱歉没有在问题中明确说明这一点,但我想将我的 cmets 保留在 AST 中。因此,虽然它可能适用于其他情况,但预处理器并不是我要寻找的。尽管如此,我还是给你一个好的答案。
猜你喜欢
  • 2015-04-30
  • 2010-09-27
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多