在Bison中启动短路规则&&和||操作

时间:2012-12-01 14:16:11

标签: c++ parsing bison lex flex-lexer

我在Bison& amp;编程中编写一个简单的计算器Flex,使用C / C ++(逻辑在Bison中完成,C / C ++部分负责数据结构,例如STL等)。

我有以下问题:

在我的计算器中,美元符号$表示i ++和++ i(前缀和后缀),例如:

int y = 3;
-> $y = 4
-> y$ = 4

当用户点击:int_expression1 && int_expression2时,如果int_expression1被评估为0(即为假),那么我不会想要评估int_expression2

例如:

int a = 0 ; 
int x = 2 ;

用户点击:int z = a&&x$ ...

因此,变量a被评估为0,因此,我不想评估x,但它仍然增长1 ...这里是代码bison / c ++:

%union
{
    int int_value;
    double double_value;
    char* string_value;
}

%type <int_value> int_expr
%type <double_value> double_expr
%type <double_value> cmp_expr

int_expr:

    | int_expr '&&' int_expr    { /* And operation between two integers */
                      if ($1 == 0) 
                        $$ = 0;
                      else  // calc
                        $$ = $1 && $3;          
                    }

如果第一个表达式已被评估为假(即0),我如何告诉bison不评估第二个表达式?

2 个答案:

答案 0 :(得分:2)

将广泛的评论转化为答案:

  

如果第一个表达式已被评估为假,我如何告诉Bison不评估第二个表达式?

这是你的代码进行评估,而不是Bison;把'责备'放在它所属的地方。

在评估RHS之前,您需要检测到您正在处理&&规则。如果第一个&&评估为0,则需要在int_expr之后和暂停评估的第二个int_expr之前插入一些代码。您还需要修改所有代码其他评估代码,用于检查并遵守“不评估”标志。

或者,您可以让Bison进行解析并创建一个在解析完成时执行的程序,而不是在解析时进行评估。这是一个更大的变化。

  

您确定在第二个int_expr之前放置一些代码吗?我似乎无法找到合理的方法来做到这一点。这是一个很好的技巧,但我找不到一种方法来实际告诉Bison不要评估第二个int_expr,而不会破坏整个评估。

您必须编写代码,以便在不应评估时不进行评估。 Bison的语法是:

| int_expr '&&' {...code 1...} int_expr {...code 2...}

'代码1'将检查$1并安排停止评估(设置全局变量或类似的东西)。 “代码2”将有条件地评估$4(4因为“代码1”现在为3美元)。所有评估代码都必须遵守“代码1”的要求 - 它不能评估“代码1”是否表示“不评估”。或者你可以按我的建议行事aselle suggested;分别解析和评估。

我是第二个关于The UNIX Programming Environment的建议。有一整章关于开发一个计算器(他们称之为高阶计算器的hoc)值得一读。但是要注意,这本书是在1984年出版的,并且在C标准之前有很大的优势。 C代码中没有原型,并且(按现代标准)它需要一些自由。我确实有hoc6(他们描述的hoc的最后一个版本;还有现代C版本1-3) - 如果你需要,请与我联系(参见我的个人资料)。

  

这就是问题所在:我无法停止在规则中间进行评估,因为我无法使用return(我可以,但没有用;它会导致程序退出)。 | intExpr '&&' { if ($1 == 0) {/* turn off a flag */ } } intExpr { /* code */}退出$3后,$4会自动评估。

您可以在规则中间停止评估,但必须对表达式评估代码块进行编码以考虑可能性。当我说“停止评估”时,我的意思是“停止计算”,而不是“停止解析器”。解析必须继续;计算值的代码必须仅在需要评估时评估,而不是在不需要评估时评估。这可能是一个(呃!)全局标志,或者你可能有其他一些机制。

最好将解析器转换为代码生成器,并在解析后执行代码。这种复杂性是为什么这是一个好策略。

  @JonathanLeffler:你确实是国王!这应该是一个答案!!!

现在这是一个答案。

答案 1 :(得分:1)

在计算器中进行评估之前,您几乎肯定想要生成一些其他表示形式。解析树或ast是经典方法,但简单的堆栈机器也很受欢迎。有很多很好的例子说明如何做到这一点,但我最喜欢的是 http://www.amazon.com/Unix-Programming-Environment-Prentice-Hall-Software/dp/013937681X 这显示了如何使用像yacc(old bison)中所做的简单的直接评估工具,并将其一直带到一种几乎与BASIC一样强大的编程语言。所有这一切都在很少的页面这是一本非常古老的书,但非常值得一读。

您还可以查看SeExpr http://www.disneyanimation.com/technology/seexpr.html 这是一个简单的表达语言计算器,用于标量和3个向量。如果你看https://github.com/wdas/SeExpr/blob/master/src/SeExpr/SeExprNode.cpp 在313行,你会看到&amp;&amp;他执行eval()函数:

void
SeExprAndNode::eval(SeVec3d& result) const
{
    // operands and result must be scalar
    SeVec3d a, b;
    child(0)->eval(a);
    if (!a[0]) {
    result[0] = 0;
    } else { 
    child(1)->eval(b);
    result[0] = (b[0] != 0.0); 
    }
}

该文件包含表示解析树中的操作的所有对象。在解析代码时生成这些对象(这些是yacc中的操作)。希望这会有所帮助。