首页 > 解决方案 > 构建 AST 时处理错误和释放内存的策略

问题描述

假设我有一个非常简单的语法:表达式 := number '+' number。我编写了一个递归下降解析器(在 C 或其他一些非垃圾收集语言中,我不想使用 Yacc、ANTLR 等),它为这个语法构造了 mini-AST。没有垃圾收集,所以我必须处理所有内存分配和释放。

ASTNode *parse() {
  operand1 = parseNumber();
  expect (plus);
  operand2 = parseNumber();

  // Create tree with root (plus) and left and right leaves
  return makeNode (operand1, operand2, plus);
}

parseNumber() 将解析一个数字,为​​节点分配空间,并将节点作为指针返回,例如*ASTNumber,用于增长的 AST。makeNode() 会将所有内容组合在一起并返回 AST 的完成部分。

但是,解析数字时可能会出错,比如说第二个数字。此时我必须优雅地展开树,因为它现在只是部分构建,我们不希望内存泄漏。一种策略是在调用 parseNumber 检测到错误时返回特定的错误节点,例如 ASTError。上面带有错误检测的代码将如下所示:

void parse() {
  operand1 = parseNumber();
  if (operand1 == ASTError) {
     return operand1; // Note its returning ASTError to the caller
  }
     
  if (not expect (plus)) {
     free (operand1);  // Free up the partially constructed AST
     return makeASTError(plus);
  }

  operand2 = parseNumber();
  if (operand2 = ASTError) {
     free (operand1);
     return operand2; 
  }

  // No error, return the finished AST
  return makeNode (operand1, operand2, plus);
}

我的问题:这是在 AST 构建过程中处理错误的合理方法吗?我没有在书籍或网上看到任何建议在这里应该做什么。如果一个人正在使用 Java 甚至 Go 进行编程,这不是你会担心的事情,因为你造成的任何混乱都会被垃圾收集器清除。

我想出的另一种策略是拥有两个解析器,一个用于检查语法错误但不执行其他任何操作。如果成功,它将启动第二个解析器,该解析器现在可以安全地构建 AST 而无需担心错误,因此无需担心释放部分构建的树。

我在做什么好吗?还是我错过了一些非常明显的事情?

标签: compiler-constructionabstract-syntax-tree

解决方案


推荐阅读