boost之spirit学习-mini_c(3)

本文详细介绍了mini_c编译器中抽象语法树AST的设计,包括如何处理循环包含的对象,如unary、function_call和expression。通过boost::recursive_wrapper解决循环依赖,并探讨了expression的定义方式。此外,还提到了使用tagged标记return_statement,以及如何利用boost::optional表示可选语法部分。最后,解释了为何要使用BOOST_FUSION_ADAPT_STRUCT将AST转换为满足fusion的sequence concept,以便于spirit::qi解析器处理嵌套规则。

前一章分析完了main.cpp,了解了mini_c的主流程。现在来看看抽象语法树的定义:ast.hpp


首先,为一些对象打上id,方便编译错误时由对象的id查找到出错的位置(这个是由annotation记录的,后边会讲)

    struct tagged
    {   
        int id; // Used to annotate the AST with the iterator position.
                // This id is used as a key to a map<int, Iterator>
                // (not really part of the AST.)
    };  

带id的对象包括:identifier、function_call、function、return_statement。


    struct nil {}; 
    struct unary;
    struct function_call;
    struct expression;

    struct identifier : tagged
    {   
        identifier(std::string const& name = "") : name(name) {}
        std::string name;
    };  

    typedef boost::variant<
            nil 
          , bool
          , unsigned int 
          , identifier
          , boost::recursive_wrapper<unary>
          , boost::recursive_wrapper<function_call>
          , boost::recursive_wrapper<expression>
        >   
    operand;

后边是对nil的定义,unary(一元表达式)、function_call、expression进行提前声明。

identifier的定义没啥好说的。

operand(操作数)的定义有点意思。操作数可以是空,也可以是bool、unsigned int 或 变量 或 一元表达式 或 函数调用 或 更general的表达式。它用variant来表达这种概念。

但unary、function_call、expression为什么要用boost::recursive_wrapper来定义呢?

因为它们有循环包含关系

  • unary(一元表达式)里有两个成员,一个是optoken,另一个就是operand。
  • expression包含了operation的列表,operation又包含了operand,所以是循环的。
  • function_call里有一个expression的list,所以也是循环包含的

对于这种循环包含的对象,我们一般都用指针相互指向,对象动态分配获得。但这样的话我们就要负责对象的动态分配和释放了,费事又容易出错。因此boost有了recursive_wrapper。

recursive_wrapper定义在<boost/variant/recursive_wrapper.hpp>里。看看代码就知道recursive_wrapper只是对类型T的简单封装,内部维护一个T的指针,内存自动从堆上分配释放,但对外的表现装作和类型T的引用一样。


下面是运算符的定义,没什么好说的:

    enum optoken
    {   
        op_plus,
        op_minus,
        op_times,
        op_divide,
        op_positive,
        op_negative,
        op_not,
        op_equal,
        op_not_equal,
        op_less,
        op_less_equal,
        op_greater,
        op_greater_equal,
        op_and,
        op_or
    };  


下面有一系列语法元素的定义:

    struct unary
    {   
        optoken operator_;
        operand operand_;
    };  

    struct operation
    {   
        optoken operator_;
        operand operand_;
    };  

    struct function_call
    {
        identifier function_name;
        std::list<expression> args;
    };

    struct expression
    {
        operand first;
        std::list<operation> rest;
    };

    struct assignment
    {
        identifier lhs;
        expression rhs;
    };

    struct variable_declaration
    {
        identifier lhs;
        boost::optional<expression> rhs;
    };

unary、function_call、assignment、variable_declaration的定义都很直接明了。

expression的定义复杂点:单个操作数是表达式,单个操作数与其它操作数通过二元运算符连接起来也是表达式。

但总感觉这样的expression定义不符合直观。难道不应该类似这样定义吗:

expr: const | variable | expr + expr | expr - expr | expr * expr | expr / expr | ....

此处存疑,以后再研究为什么


后面是语句级的定义:

    struct if_statement;
    struct while_statement;
    struct statement_list;
    struct return_statement;

    typedef boost::variant<
            variable_declaration
          , assignment
          , boost::recursive_wrapper<if_statement>
          , boost::recursive_wrapper<while_statement>
          , boost::recursive_wrapper<return_statement>
          , boost::recursive_wrapper<statement_list>
        >
    statement;

    struct statement_list : std::list<statement> {};

    struct if_statement
    {
        expression condition;
        statement then;
        boost::optional<statement> else_;
    };

    struct while_statement
    {
        expression condition;
        statement body;
    };

    struct return_statement : tagged
    {
        boost::optional<expression> expr;
    };

和之前一样的原因,statement被定义为variant,而且if/while/return/list statement用boost::recursive_wrapper定义

return_statement被用tagged标记。这是为了检查void函数返回非void值时确定return语句的位置。

另一个值得一提的是对于可选的语法部分使用了boost::optional来表达。boost::optional<T>在被赋值时可以当T的引用使,当访问成员时,可以当指针使用(重载了->和*运算符),可以隐式转换成bool类型,用于判断是否有值。


最后是函数及函数列表的定义和两个调试用的辅助函数:

    struct function
    {
        std::string return_type;
        identifier function_name;
        std::list<identifier> args;
        statement_list body;
    };

    typedef std::list<function> function_list;

    // print functions for debugging
    inline std::ostream& operator<<(std::ostream& out, nil)
    {
        out << "nil"; return out;
    }

    inline std::ostream& operator<<(std::ostream& out, identifier const& id)
    {
        out << id.name; return out;
    }

最后一段是最彰显boost之奇技淫巧的地方,通过BOOST_FUSION_ADAPT_STRUCT把上面定义的AST类转换成满足fusion的sequence concept的类:

BOOST_FUSION_ADAPT_STRUCT(
    client::ast::unary,
    (client::ast::optoken, operator_)
    (client::ast::operand, operand_)
)

BOOST_FUSION_ADAPT_STRUCT(
    client::ast::operation,
    (client::ast::optoken, operator_)
    (client::ast::operand, operand_)
)

BOOST_FUSION_ADAPT_STRUCT(
    client::ast::function_call,
    (client::ast::identifier, function_name)
    (std::list<client::ast::expression>, args)
)

BOOST_FUSION_ADAPT_STRUCT(
    client::ast::expression,
    (client::ast::operand, first)
    (std::list<client::ast::operation>, rest)
)

BOOST_FUSION_ADAPT_STRUCT(
    client::ast::variable_declaration,
    (client::ast::identifier, lhs)
    (boost::optional<client::ast::expression>, rhs)
)

BOOST_FUSION_ADAPT_STRUCT(
    client::ast::assignment,
    (client::ast::identifier, lhs)
    (client::ast::expression, rhs)
)

BOOST_FUSION_ADAPT_STRUCT(
    client::ast::if_statement,
    (client::ast::expression, condition)
    (client::ast::statement, then)
    (boost::optional<client::ast::statement>, else_)
)

BOOST_FUSION_ADAPT_STRUCT(
    client::ast::while_statement,
    (client::ast::expression, condition)
    (client::ast::statement, body)
)

BOOST_FUSION_ADAPT_STRUCT(
    client::ast::return_statement,
    (boost::optional<client::ast::expression>, expr)
)

BOOST_FUSION_ADAPT_STRUCT(
    client::ast::function,
    (std::string, return_type)
    (client::ast::identifier, function_name)
    (std::list<client::ast::identifier>, args)
    (client::ast::statement_list, body)
)

boost::fusion可以参考:http://www.boost.org/doc/libs/1_49_0/libs/fusion/doc/html/fusion。又是一个很难理解的东西。

为什么要把语法树的类用BOOST_FUSION_ADAPT_STRUCT处理一下呢?

这是因为spirit::qi里的parser规则都有一个与之相关联的attribute类型,存放该规则解析出的数据。在mini_c里,每个语法树对象就是规则解析出的数据。但规则是像搭积木一样嵌套堆起来的。例如:

rule_x = rule_a >> rule_b >> rule_c >> rule_d;

表示rule_x是由rule_a、rule_b、rule_c、rule_d依次连接组成。rule_a/b/c/d各自有自己的attribute_type,attr_a/b/c/d。(rule_a >> rule_b >> rule_c >> rule_d)也是一个规则,但它的attribute_type就是boost::fusion::tuple<attr_a, attr_b, attr_c, attr_d>。假设rule_x对应的attribute_type是我们自己定义的attr_x类型。那么怎么把该tuple的数据复制到attr_x里呢?首先把attr_x用BOOST_FUSION_ADAPT_STRUCT处理一下,使它也变成一个和tuple兼容的东西。然后再用boost::fusion::copy把tuple里的东西拷贝到attr_x里去。

为了简单演示这个过程,写了一段小代码:

struct employee
{
    std::string name;
    int         age;
    int         salary;
};

BOOST_FUSION_ADAPT_STRUCT(
        employee,
        (std::string, name)
        (int, age)
        (int, salary)
        )   

std::ostream& operator << (std::ostream& os, const employee& e)
{
    os << e.name << " " << e.age << " " << " " << e.salary;
    return os; 
}

int main(int argc, char* argv[])
{
    boost::fusion::vector<std::string, int, int> fv("JJP", 28, 10000);
    employee e;
    boost::fusion::copy(fv, e); 

    std::cout << e << std::endl;

    return 0;
}











<think>好的,我现在需要回答用户关于CMake选项中-DDOWNLOAD_BOOST-DENABLE_DO​​WNLOADS的区别的问题。首先,我得回忆一下这两个选项的作用,可能查阅相关资料确保准确性。 记得CMake项目中,有些选项用于控制依赖的下载。像Boost这样的库,有时项目会提供自动下载并构建的选项。用户提到的-DDOWNLOAD_BOOST可能是特定于项目的选项,用于允许在配置时下载Boost库。而-DENABLE_DO​​WNLOADS可能是一个更通用的选项,控制是否允许CMake在构建过程中下载任何外部依赖。 需要确认这两个选项的具体定义。通常在CMake中,选项是通过option()命令定义的。例如,项目可能有类似option(DOWNLOAD_BOOST "Download Boost instead of using system" ON),这样用户可以通过-D来设置。而ENABLE_DO​​WNLOADS可能是一个全局开关,影响所有依赖的下载行为。 可能存在的情况是,DOWNLOAD_BOOST是项目特定的,仅控制Boost的下载,而ENABLE_DO​​WNLOADS是控制整个项目是否允许下载任何文件。如果ENABLE_DO​​NLOADS设为OFF,即使DOWNLOAD_BOOST设为ON,也可能无法下载,因为全局开关关闭了下载功能。 需要进一步验证这个理解是否正确。例如,CMake的ExternalProject模块通常会有选项来控制下载,但不同项目可能有不同的实现。用户可能在编译某些项目时遇到需要同时设置这两个选项的情况,或者需要了解它们的优先级。 另外,需要说明这两个选项通常是在CMake配置阶段通过命令行传递的,例如cmake -DDOWNLOAD_BOOST=ON -DENABLE_DO​​WNLOADS=ON ..。可能还要提到,如果系统已经安装了Boost,可以关闭DOWNLOAD_BOOST来使用系统库,而ENABLE_DO​​WNLOADS关闭的话,所有依赖需要预先准备好。 最后,需要建议用户查阅项目的CMakeLists.txt或文档,因为不同项目可能有不同的实现方式,这些选项的具体行为可能因项目而异。总结时强调两者之间的区别和可能的依赖关系,帮助用户正确配置构建选项。</think>在CMake配置中,`-DDOWNLOAD_BOOST` 和 `-DENABLE_DOWNLOADS` 是与依赖管理相关的选项,具体区别如下: --- ### 1. **-DDOWNLOAD_BOOST** - **作用**: 这是一个**项目级选项**,通常用于控制是否在构建过程中自动下载并编译 **Boost 库**。 - 若设置为 `ON`(如 `-DDOWNLOAD_BOOST=ON`),CMake 会从网络下载指定版本的 Boost 源码并编译。 - 若设置为 `OFF`,则要求系统已预装 Boost 库,且CMake会尝试通过 `find_package(Boost)` 查找它。 - **适用场景**: 当项目依赖 Boost,但用户不想手动安装或系统缺少 Boost 时启用。 - **示例**: ```bash cmake -DDOWNLOAD_BOOST=ON -DBOOST_VERSION=1.80.0 .. ``` --- ### 2. **-DENABLE_DOWNLOADS** - **作用**: 这是一个**全局开关**,控制整个项目中所有通过 `ExternalProject` 或 `FetchContent` 定义的**外部依赖是否允许下载**。 - 若设置为 `ON`(默认可能为 `OFF`),允许CMake自动下载所有声明的外部依赖(如GitHub仓库、压缩包等)。 - 若设置为 `OFF`,则禁止任何下载行为,要求所有依赖已预先本地存在。 - **适用场景**: - 在离线环境中构建项目时需设为 `OFF`。 - 需要严格管控依赖来源时(如企业安全策略)。 - **示例**: ```bash cmake -DENABLE_DOWNLOADS=OFF .. ``` --- ### 3. **两者的关系** - **依赖优先级**: `-DDOWNLOAD_BOOST=ON` **必须** 配合 `-DENABLE_DOWNLOADS=ON` 使用。 若全局禁止下载(`ENABLE_DOWNLOADS=OFF`),即使指定下载Boost也会失败。 - **典型配置组合**: ```bash # 允许下载Boost,同时开启全局下载权限 cmake -DDOWNLOAD_BOOST=ON -DENABLE_DOWNLOADS=ON .. # 使用系统Boost,禁止所有下载 cmake -DDOWNLOAD_BOOST=OFF -DENABLE_DOWNLOADS=OFF .. ``` --- ### 4. **验证与调试建议** - 检查项目的 `CMakeLists.txt`,确认选项定义(如 `option(DOWNLOAD_BOOST "...")`)。 - 若下载失败,检查网络代理或尝试手动下载依赖并指定本地路径。 - 通过 `--trace` 或 `--debug-output` 参数查看CMake详细日志。 --- 总结:`-DDOWNLOAD_BOOST` 是 **针对Boost库的局部控制**,而 `-DENABLE_DOWNLOADS` 是 **全局下载开关**。两者需根据项目需求和环境配合使用。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值