首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >野牛风格:使用我自己的堆栈不好吗?全球变坏了吗?

野牛风格:使用我自己的堆栈不好吗?全球变坏了吗?
EN

Stack Overflow用户
提问于 2012-02-17 01:46:13
回答 3查看 280关注 0票数 2

我的问题基本上是“什么是YACC /Bison的良好风格?”与此相关的是,不管我是否让野牛做它擅长的事情。

例如,我发现我的Bison程序比我的原始代码更依赖全局。请考虑以下几点:

代码语言:javascript
复制
 prog : 
      vents{ /*handle semantics...*/ } 
      unity{ /*handle semantics...*/ } 
      defs;

如果我想在“通风口”和“统一”之后的两个以大括号分隔的块之间传递信息,我认为从信息隐藏的角度来看,使用全局变量(从技术上讲,是一个具有文件级作用域和内部链接的变量)是我所能做的最好的事情。我在这些块中声明的任何变量都是其块的本地变量(我认为.),而我可以放置C++声明的其他指定位置会导致文件级别的作用域。

如果我能够将一个变量声明注入到"yyparse()“函数中,这将更符合我的需要。这类代码是否有钩子,或者注入这样一个变量的其他方法?或者全球只是使用Bison的一个公认的部分?

我也意识到,也许我甚至不想以这种方式在这些部分之间传递信息。但是仅仅使用$$、$1、$2等就可以把所有的东西都传递出去,这对我来说似乎很困难。我只是没有“得到”吗?

我发现我的一个全局变量特别值得怀疑,即使我接受其中的其他变量。它是std::堆栈类型,与输入语言对条件词的支持有关。

当我在编译器输入中遇到一个条件(“if/ text”)时,最终会产生三个汇编语言标签,其中包含一个文本字符串,后面跟着从序列中提取的一个数字。

因此,当我第一次遇到"if“时,我会获得一个序列号,将它推到堆栈上(因为"if”结构可以嵌套),然后使用它(通过“窥视”或"pops")来构造必要的标签和跳转,例如在我的条件、我的"if“块和我的"else”块之后。

我试着使用$-2这样的方法来完成这项工作,但是发现这个标识符不是与我的条件开头有关,而是与刚刚编译的任何块的末尾有关。由$抽象的系统似乎与从左到右读取的代码有关,没有任何概念说明内部的结构是如何嵌套的。

我不指望你们都能帮我.但是,我是否至少在正确的道路上尝试使用$$,$1,$-1,等等?很有可能我放弃得太早了,而且/或我会受益于采取一个干净的工作表方法,即扔掉我的旧的临时代码。

是这样吗?或者我用它的std:堆栈和它的全局集合的方法还好吗?

EN

回答 3

Stack Overflow用户

发布于 2012-02-17 01:58:02

我看不出避免使用全局变量有什么困难,我几乎没有用它们来表示错误或类似的事情。

想想解析器,它应该产生什么?抽象语法树..。

是怎么做的?它是一个n进制树,每个节点都包含一些信息,并且只包含它的子节点,因此不需要全局变量。

我给你看一看我正在写的一种语言,只是想给你一个想法:

代码语言:javascript
复制
bexp:
  bexp T_PLUS bexp { $$ = new ASTBExp($2,$1,$3); }
  | bexp T_MINUS bexp { $$ = new ASTBExp($2,$1,$3); }
  | bexp T_TIMES bexp { $$ = new ASTBExp($2,$1,$3); }
  | bexp T_DIV bexp { $$ = new ASTBExp($2,$1,$3); }

uexp:
  raw_value { $$ = $1; }
  | UOP_NOT uexp { $$ = new ASTUExp($1,$2); }
  | T_LPAREN bexp T_LPAREN { $$ = $2; }
  | var_ref { $$ = new ASTVarRef((ASTIdentifier*)$1); }
  | call { $$ = $1; }

如您所见,解析的每个节点都使用语法的子节点实例化,这也是抽象语法树的语义子节点,并在$$中返回。

根元素类似于

代码语言:javascript
复制
start: root { Compiler::instance()->setAST((ASTRoot*)$1); }
;

root:
  function_list { $$ = new ASTRoot($1); }
;

在其中,我只需获取整个树并将其传递给我的Compiler类的一个实例。

现在,如果您查看调用yyparse()的函数

代码语言:javascript
复制
bool parseSource()
{
  //yydebug = 1;
  freopen(fileName, "r", stdin);
  yyparse();

  return !failed;
}

我只是打开一个文件并调用解析例程。这个函数在这里由Compiler类调用:

代码语言:javascript
复制
  bool compile()
  {
    if (!parseSource())
      return false;

    if (!populateFunctionsTable())
      return false;

    ast->recursivePrint(0);
    Utils::switchStdout(binaryFile);
    ast->generateASM();
    Utils::revertStdout();

    assemble();

    return true;
  }

正如您在这里看到的,调用解析例程,该例程创建整个树,然后在Compiler类中设置它。对树的递归访问(函数generateASM)执行脏工作。

我希望这能澄清一点你应该如何使用你的解析器,如果你需要更多的信息,请告诉我。您不需要在解析器中完成所有的工作。只要在那里进行解析,其他的一切都可以通过对抽象语法树的一些递归调用来解决。

另一个实际示例是您正在讨论的if/ are语句,在语法中它被定义为

代码语言:javascript
复制
if_stat:
  KW_IF T_LPAREN exp T_RPAREN block %prec LOWER_THAN_ELSE { $$ = new ASTIfStat($3, $5); }
  | KW_IF T_LPAREN exp T_RPAREN block KW_ELSE block { $$ = new ASTIfStat($3, $5, $7); }
;

创建了一个特殊的节点来管理if/else构造,然后通过拥有这个generateASM函数来工作:

代码语言:javascript
复制
 void generateASM()
  { 
    if (m_fbody == NULL)
    {
      m_condition->generateASM();
      printf("NOT\n");
      printf("JUMPC iflabel%u\n", labelCounter);
      m_tbody->generateASM();
      printf("iflabel%u:\r\n", labelCounter);

      ++labelCounter;
    }
    else
    {
      u32 c = labelCounter++;
      u32 d = labelCounter++;

      m_condition->generateASM();
      printf("JUMPC iflabel%u\n", c);
      m_fbody->generateASM();
      printf("JUMP iflabel%u\n", d);
      printf("iflabel%u:\n", c);
      m_tbody->generateASM();
      printf("iflabel%u:\n", d);
    }
  }
票数 2
EN

Stack Overflow用户

发布于 2012-02-17 02:11:56

中级规则可以在堆栈上推送一个值。

如果你有

代码语言:javascript
复制
rule
    : A B { ... } C

野牛会自动把它转换成

代码语言:javascript
复制
some_identifier
    : /* empty */ { ... }

rule
    : A B some_identifier C

而且它的值也同样可以被访问。在这种情况下,中间规则语义操作在Bison堆栈上存储了一个值,然后在同一条规则中再次访问。

通常,这些函数是递归的。考虑下面的简单片段

代码语言:javascript
复制
// C++
class Statement { public: virtual ~Statement() {} };
class Expression : public Statement {};
class IfStatement : public Statement { Statement* if_true; Expression* condition; }

// Bison
%type if_statement if_stmt
%type statement stmt
%union {
    IfStatement* if_stmt;
    Statement* stmt;
}

if_statement
    : if { $$ = new IfStatement(); } 
      '(' expression { $2->condition = $4; } 
      ')' statement { $2->if_true = $7; $$ = $2; }

statement
    : if_statement { $$ = $1; }
    | ...

不需要外部堆栈来执行这样的递归功能。

票数 0
EN

Stack Overflow用户

发布于 2012-02-20 11:00:13

您可以使用%parse-param directive声明要传递的其他数据。这使您能够更好地隐藏额外的数据,尽管您还必须将其传递到解析函数中。

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/9321815

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档