如何用位置信息注释AST

How to annotate the AST with position information?

本文关键字:注释 AST 信息 位置 何用      更新时间:2023-10-16

我使用Boost Spirit在一个小的编译器项目中解析源文件。

如果在解析过程中出现错误,我可以打印错误的位置,但在稍后的阶段,通常是在执行语义检查时,我该怎么做?

我的源文件使用自动规则解析为抽象语法树。我想将行和列信息添加到AST节点中。有没有一种简单的方法可以在解析过程中实现这一点?

我在Lexer中使用boost::spirit::classic::position_iterator2,然后在语法中使用这个Lexer。

谢谢

编辑:

lexer的定义如下:

typedef std::string::iterator base_iterator_type;
typedef boost::spirit::classic::position_iterator2<base_iterator_type> pos_iterator_type;
typedef boost::spirit::lex::lexertl::token<pos_iterator_type> Tok;
typedef boost::spirit::lex::lexertl::actor_lexer<Tok> lexer_type;
template<typename L>
class SpiritLexer : public lex::lexer<L> {
   //Token definitions
}
typedef lexer_type::iterator_type Iterator;
typedef SpiritLexer<lexer_type> Lexer;

语法是这样定义的:

struct EddiGrammar : qi::grammar<lexer::Iterator, ast::SourceFile()> {
    EddiGrammar(const lexer::Lexer& lexer);
    //Token definitions
};

最后,以下是我如何解析来源:

ast::SourceFile program
std::ifstream in(file.c_str());
in.unsetf(std::ios::skipws);
in.seekg(0, std::istream::end);
std::size_t size(static_cast<size_t>(in.tellg()));
in.seekg(0, std::istream::beg);
std::string contents(size, 0);
in.read(&contents[0], size);
pos_iterator_type position_begin(contents.begin(), contents.end(), file);
pos_iterator_type position_end;
Lexer lexer;
EddiGrammar grammar(lexer);
bool r = spirit::lex::tokenize_and_parse(position_begin, position_end, lexer, grammar, program);

在我的语法中,我通过引用一些lexer标记来使用lexer。例如:

else_ %=
        lexer.else_
    >>  lexer.left_brace
    >>  *(instruction)
    >>  lexer.right_brace;

我的所有AST节点都是使用自动规则构建的。

我找到了解决这个问题的方法。

我编写了一个简单的终端解析器,只需获取当前位置并将其添加到AST节点。

这是我写的解析器:

namespace boost { namespace spirit {
    BOOST_SPIRIT_TERMINAL(position)
    template <>
    struct use_terminal<qi::domain, tag::position> : mpl::true_ {};
}}
namespace boost { namespace spirit { namespace qi
{
    struct position : primitive_parser<position>
    {
        position(const eddic::lexer::pos_iterator_type& position_begin) : position_begin(position_begin) {}
        template <typename Context, typename Iterator>
        struct attribute {
            typedef eddic::ast::Position type;
        };
        template <typename Iterator, typename Context
            , typename Skipper, typename Attribute>
            bool parse(Iterator& first, Iterator const& last
                    , Context& /*context*/, Skipper const& skipper, Attribute& attr) const
        {
            qi::skip_over(first, last, skipper);
            auto& pos = position_begin.get_position();
            attr.theLine = position_begin.get_currentline();
            attr.file = pos.file;
            attr.column = pos.column;
            attr.line = pos.line;
            return true;
        }
        template <typename Context>
        info what(Context& context) const {
            return info("position");
        }
        const eddic::lexer::pos_iterator_type& position_begin;
    };
    template <typename Modifiers>
    struct make_primitive<tag::position, Modifiers> {
        typedef position result_type;
        result_type operator()(unused_type, eddic::lexer::Lexer const& lexer, unused_type) const
        {
            return result_type(lexer);
        }
    };
}}}

以及我用来存储信息的结构:

struct Position {
    std::string file;
    std::string theLine;
    int line;
    int column;
};

它工作得很好,但我必须将位置迭代器传递给解析器。如果有人知道从提供给解析函数的迭代器中获取position_iteror2迭代器的方法,我将不胜感激。