提升精神:“语义行为是邪恶的”?

时间:2022-10-14 20:20:42

Reading and watching this presentation: http://boost-spirit.com/home/2011/06/12/ast-construction-with-the-universal-tree/
I've discovered this statement -- basically we are suggested not to use semantic actions.

阅读和观看此演示文稿:http://boost-spirit.com/home/2011/06/12/ast-construction-with-the-universal-tree/我发现了这个陈述 - 基本上我们建议不要使用语义动作。

I must admit, that I've already felt something like that: grammars with semantic actions actually look kinda ugly. and, when I needed to extend/change them, it took a lot of "micromanagement" exactly with semantic actions. The approach with attribute grammar, demonstrated in the presentation, seems to be much more elegant and promising.

我必须承认,我已经感受到了类似的东西:具有语义行为的语法实际上看起来有点难看。而且,当我需要扩展/改变它们时,它完全采用语义动作进行了大量的“微观管理”。在演示文稿中演示的具有属性语法的方法似乎更加优雅和有前途。

So I'd like to ask: is this is an "official" point? Should I learn how to work with attribute grammar and avoid semantic actions in more detail? If so -- I'd like to = ask for some basic (maybe even trivial) examples, demonstrating such an approach -- the LISP interpreter is too complex for me to chew...

所以我想问:这是一个“官方”的观点吗?我应该学习如何使用属性语法并更详细地避免语义动作吗?如果是这样 - 我想=要求一些基本的(甚至是微不足道的)例子,证明这样的方法 - LISP解释器太复杂了我不能咀嚼......

1 个解决方案

#1


28  

I'm sure Hartmut will answer in a second. Till then, this is my take:

我相信哈特穆特会在一秒钟内回答。直到那时,这是我的看法:

No that is not an official point.

不,这不是官方观点。

Semantic actions have some drawbacks

语义动作有一些缺点

  • The simplest disadvantage of semantic actions is the stylistic notion of separation of concerns. You want to express syntax in one place, and semantics in another. This helps maintainability (especially with regards to the lengthy compile times for compiling Spirit Grammars)

    语义行为最简单的缺点是关注点分离的风格概念。您希望在一个地方表达语法,在另一个地方表达语义。这有助于维护(特别是关于编译Spirit Grammars的冗长编译时间)

  • More complicated implications if they have side-effects (which is frequently the case). Imagine backtracking from a parsed node when the semantic action had a side-effect: the parser state will be reverted, but the external effects aren't.

    如果它们有副作用(通常是这种情况),会产生更复杂的影响。想象一下,当语义动作产生副作用时,从解析的节点回溯:解析器状态将被还原,但外部效果则不会。

    In a way, using attributes only is like using deterministic, pure functions in a functional program, it is easier to reason about the correctness of a program (or, in this case the grammar state machine) when it is composed of pure functions only.

    在某种程度上,仅使用属性就像在功能程序中使用确定性的纯函数一样,当它仅由纯函数组成时,更容易推断程序(或者在本例中为语法状态机)的正确性。

  • Semantic actions have a tendency (but not necessarily so) to introduce more copying around by value; this, in combination with heavy backtracking, could reduce performance. Of course, if the semantic action is 'heavy' this, in itself, is going to hinder performance of parsing.

    语义动作倾向于(但不一定如此)引入更多的值复制;与重型回溯相结合,可能会降低性能。当然,如果语义动作“重”,这本身就会妨碍解析的性能。


Semantic actions are good for various purposes. In fact, if you need to parse non-trivial grammars with context sensitivity you cannot escape them.

语义动作有利于各种目的。实际上,如果你需要解析具有上下文敏感性的非平凡语法,你就无法逃脱它们。

  1. Consider the use of qi::locals<> and inherited attributes (code from the Mini XML - ASTs! sample) - they involve semantic actions:

    考虑使用qi :: locals <>和继承属性(来自Mini XML - ASTs!示例的代码) - 它们涉及语义操作:

    xml =
            start_tag                   [at_c<0>(_val) = _1]
        >>  *node                      
        >>  end_tag(at_c<0>(_val)) // passing the name from the 
                                   // ... start_tag as inherited attribute
    ;
    

    Or one using qi::locals:

    或者使用qi :: locals:

    rule<char const*, locals<char> > rl;
    rl = alpha[_a = _1] >> char_(_a); // get two identical characters
    test_parser("aa", rl); // pass
    test_parser("ax", rl); // fail
    

    IMO, these semantic action pose less of a problem usually, because when they get backtracked, the next time execution passes (the same) semantic action, the local will just get overwritten by the new, correct, value.

    IMO,这些语义动作通常不会造成问题,因为当它们被回溯时,下一次执行传递(相同)语义动作时,本地将被新的,正确的值覆盖。

  2. Also, some jobs are really 'quick-and-dirty' and don't warrant the use of utree or a hand-rolled AST type:

    此外,有些工作真的“快而脏”,并且不保证使用utree或手动AST类型:

     qi::phrase_parse(first, last, // imagine qi::istream_iterator... 
         intesting_string_pattern  // we want to match certain patterns on the fly
                [ log_interesting_strings ], // and pass them to our logger
         noise_skipper             // but we skip all noise
     );
    

    Here, the semantic action is the core of the parsers function. It works, because no backtracking is involved at the level of nodes with semantic actions.

    这里,语义动作是解析器功能的核心。它起作用,因为在具有语义动作的节点级别上不涉及回溯。

  3. The semantic actions are a mirror-image of semantic actions in Spirit Karma, where they usually pose less of the problems than in Qi; so even if only for interface/API consistency, semantic actions are 'a good thing' and enhance the usability of Boost Spirit as a whole.

    语义动作是Spirit Karma中语义动作的镜像,在这些动作中,它们通常比Qi中的问题少。所以,即使只是为了接口/ API的一致性,语义动作也是“好事”,并提升整个Boost Spirit的可用性。

#1


28  

I'm sure Hartmut will answer in a second. Till then, this is my take:

我相信哈特穆特会在一秒钟内回答。直到那时,这是我的看法:

No that is not an official point.

不,这不是官方观点。

Semantic actions have some drawbacks

语义动作有一些缺点

  • The simplest disadvantage of semantic actions is the stylistic notion of separation of concerns. You want to express syntax in one place, and semantics in another. This helps maintainability (especially with regards to the lengthy compile times for compiling Spirit Grammars)

    语义行为最简单的缺点是关注点分离的风格概念。您希望在一个地方表达语法,在另一个地方表达语义。这有助于维护(特别是关于编译Spirit Grammars的冗长编译时间)

  • More complicated implications if they have side-effects (which is frequently the case). Imagine backtracking from a parsed node when the semantic action had a side-effect: the parser state will be reverted, but the external effects aren't.

    如果它们有副作用(通常是这种情况),会产生更复杂的影响。想象一下,当语义动作产生副作用时,从解析的节点回溯:解析器状态将被还原,但外部效果则不会。

    In a way, using attributes only is like using deterministic, pure functions in a functional program, it is easier to reason about the correctness of a program (or, in this case the grammar state machine) when it is composed of pure functions only.

    在某种程度上,仅使用属性就像在功能程序中使用确定性的纯函数一样,当它仅由纯函数组成时,更容易推断程序(或者在本例中为语法状态机)的正确性。

  • Semantic actions have a tendency (but not necessarily so) to introduce more copying around by value; this, in combination with heavy backtracking, could reduce performance. Of course, if the semantic action is 'heavy' this, in itself, is going to hinder performance of parsing.

    语义动作倾向于(但不一定如此)引入更多的值复制;与重型回溯相结合,可能会降低性能。当然,如果语义动作“重”,这本身就会妨碍解析的性能。


Semantic actions are good for various purposes. In fact, if you need to parse non-trivial grammars with context sensitivity you cannot escape them.

语义动作有利于各种目的。实际上,如果你需要解析具有上下文敏感性的非平凡语法,你就无法逃脱它们。

  1. Consider the use of qi::locals<> and inherited attributes (code from the Mini XML - ASTs! sample) - they involve semantic actions:

    考虑使用qi :: locals <>和继承属性(来自Mini XML - ASTs!示例的代码) - 它们涉及语义操作:

    xml =
            start_tag                   [at_c<0>(_val) = _1]
        >>  *node                      
        >>  end_tag(at_c<0>(_val)) // passing the name from the 
                                   // ... start_tag as inherited attribute
    ;
    

    Or one using qi::locals:

    或者使用qi :: locals:

    rule<char const*, locals<char> > rl;
    rl = alpha[_a = _1] >> char_(_a); // get two identical characters
    test_parser("aa", rl); // pass
    test_parser("ax", rl); // fail
    

    IMO, these semantic action pose less of a problem usually, because when they get backtracked, the next time execution passes (the same) semantic action, the local will just get overwritten by the new, correct, value.

    IMO,这些语义动作通常不会造成问题,因为当它们被回溯时,下一次执行传递(相同)语义动作时,本地将被新的,正确的值覆盖。

  2. Also, some jobs are really 'quick-and-dirty' and don't warrant the use of utree or a hand-rolled AST type:

    此外,有些工作真的“快而脏”,并且不保证使用utree或手动AST类型:

     qi::phrase_parse(first, last, // imagine qi::istream_iterator... 
         intesting_string_pattern  // we want to match certain patterns on the fly
                [ log_interesting_strings ], // and pass them to our logger
         noise_skipper             // but we skip all noise
     );
    

    Here, the semantic action is the core of the parsers function. It works, because no backtracking is involved at the level of nodes with semantic actions.

    这里,语义动作是解析器功能的核心。它起作用,因为在具有语义动作的节点级别上不涉及回溯。

  3. The semantic actions are a mirror-image of semantic actions in Spirit Karma, where they usually pose less of the problems than in Qi; so even if only for interface/API consistency, semantic actions are 'a good thing' and enhance the usability of Boost Spirit as a whole.

    语义动作是Spirit Karma中语义动作的镜像,在这些动作中,它们通常比Qi中的问题少。所以,即使只是为了接口/ API的一致性,语义动作也是“好事”,并提升整个Boost Spirit的可用性。