对于我实际实现的解析器,我在解析器中部分拥有这些私有函数:
Parser私有方法:
Token const* current_token() const;
Token const* next_token();
Token const* peek_token();
std::unique_ptr<ast::Expression> parse_expression();
std::unique_ptr<ast::TypeSpecifier> parse_type_specifier();
std::unique_ptr<ast::VariableDeclarationStatement> parse_variable_declaration();
std::unique_ptr<ast::Statement> parse_function_definition();
std::unique_ptr<ast::Statement> parse_top_level_statement();
parse_variable_declaration方法的实现是这样的:
parse_variable_declaration():
std::unique_ptr<ast::VariableDeclarationStatement> Parser::parse_variable_declaration() {
next_token(); // consume 'var'
if (current_token()->get_type() != TokenTypes::identifier) {
throw parser_error(current_token(), "", "expected identifier\n");
}
const auto id = current_token(); // store identifier
next_token(); // consume identifier
std::unique_ptr<ast::TypeSpecifier> type;
std::unique_ptr<ast::Expression> expr;
auto assignment_required = true;
if (current_token()->get_type() == TokenTypes::op_colon) { // optional type specifier
next_token(); // consume ':'
type = parse_type_specifier();
assignment_required = false;
}
if (assignment_required && current_token()->get_type() != TokenTypes::op_equals) {
throw parser_error(current_token(), "", "expected equals operator\n");
}
if (current_token()->get_type() == TokenTypes::op_equals) {
next_token(); // consume '='
expr = parse_expression();
}
if (current_token()->get_type() != TokenTypes::op_semi_colon) {
throw parser_error(current_token(), "", "expected semi-colon\n");
}
next_token(); // consume ';'
DEBUG_STDERR("parsed: variable_declaration_statement\n");
return std::make_unique<ast::VariableDeclarationStatement>(
id->get_string(), std::move(type), std::move(expr));
}
最后一行(返回)以分段错误结束。 它基本上调用VariableDeclarationStatement的构造函数:
VariableDeclarationStatement ctor:
VariableDeclarationStatement::VariableDeclarationStatement(
std::string const& name,
std::unique_ptr<TypeSpecifier> type_specifier,
std::unique_ptr<Expression> expr
):
m_name{name},
m_type_specifier{std::move(type_specifier)},
m_expr{std::move(expr)}
{}
我从昨天开始调试这个东西,似乎无法找出为什么这不能按预期工作。我想构建抽象语法树(解析器输出),其中包含指向其子节点的唯一指针(因为它们是他们孩子唯一有意义的所有者) - 这就是我尝试使用它们的原因。
控制台输出:DEBUG_STDERR
parsed: primitive_type_int // from parse_type_specifier()
parsed: integral_expression // from parse_expression()
parsed: variable_declaration_statement
[1] 12638 segmentation fault (core dumped) ./cion_compiler
答案 0 :(得分:2)
对唯一指针的移动操作基本上归结为简单的指针副本。没有理由为什么unique_ptr
的任何实现会在移动它们的过程中取消引用指针。因此,此操作导致seg-fault的可能性几乎为零。
在return-statement / constructor-call中,作为id->get_string()
调用的一部分,您确实有一个(或更多)非常明显的指针取消引用。
首先,id
指针创建如下:
const Token* const id = current_token(); // store identifier
next_token(); // consume identifier
除非保证current_token()
返回的任何指针在时间结束之前(或在当前解析操作的生命周期内)有效,否则很有可能在调用{之后{1}},next_token()
指针无效,即指向不存在或已失效的id
对象。
即使Token
指针仍然指向现有的id
对象,它可能处于“僵尸”状态,并且通过{{1}从中获取字符串},是一个无效的操作。
如果我是你,那就是我要寻找seg-fault的来源。您可能还希望在(内存)调试器中运行它以获取它的源,它可能会指向Token
函数作为它的源,在{{1的解除引用期间指针(get_string()
指针)或在构造字符串本身时。如果get_string
是this
类中的虚函数,它还可以指向虚拟表查找。无论哪种方式,我都非常怀疑这是seg-fault的原因,因为它是你发布的唯一明显危险的代码。
答案 1 :(得分:0)
正如你们正确建议的那样,错误隐藏在可疑的id指针中。 我的程序中的解析器通过词法分析器中的unique_ptr接收令牌,并将它们作为当前令牌存储。 因此,方法current_token()返回一个指向unique_ptr的指针,该指针在下次调用next_token()时立即被删除。 将无效指针存储到id中已经删除的令牌会导致问题。
我用几种不同的方式修改了代码。
首先,我将上面的帮助方法中的返回类型从“Token const *”更改为“Token const&amp;”并且id变量现在只复制get_string值,并且不执行其他与指针相关的操作。
通过这些更改,成功解决了分段故障问题! =)