4

我正在使用 C/C++ 在 Bison & Flex 中编写一个简单的计算器(逻辑在 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 ...这是野牛/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

4

2 回答 2

2

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

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

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

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

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

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

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

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

“代码 1”将检查$1并安排停止评估(设置全局变量或类似的东西)。“代码 2”将有条件地评估$4(4,因为“代码 1”现在是 3 美元)。所有评估代码都必须遵守“代码 1”的规定——如果“代码 1”说“不评估”,它就不能评估。或者你可以按照我的建议和aselle 的 建议去做;分别解析和评估。

我赞同 aselle 关于The UNIX Programming Environment的建议。里面有一整章是关于开发一个计算器(他们称之为hoc高阶计算器),值得一读。但是请注意,这本书是在 1984 年出版的,并且比 C 标准早了很多。C 代码中没有原型,并且(按照现代标准)它需要一些自由。我确实有hoc6hoc他们描述的最后一个版本;还有版本 1-3)在现代 C 中——如果你想要的话,请联系我(见我的个人资料)。

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

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

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

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

现在是一个答案。

于 2012-12-01T18:15:08.663 回答
1

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

您还可以查看 SeExpr http://www.disneyanimation.com/technology/seexpr.html ,这是一个用于标量和 3 个向量的简单表达式语言计算器。如果您查看第 313 行的https://github.com/wdas/SeExpr/blob/master/src/SeExpr/SeExprNode.cpp ,您将看到 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 中的操作)。希望这可以帮助。

于 2012-12-01T16:07:37.210 回答