構文木を作る その3

今日は、構文木を評価してECHOだけ動くインタプリタまで作りました。
構文木の使い方は分かったので、他の機能の実装方法もおおよそ見当がつきました。
演算や制御構造は特に問題はなさそうです。


面倒なのは、ruleとincludeです。
今回はruleを何度もパースしたくないので、構文木のまま保存しておく必要があります。
ただし、構文木をコピーするのは無駄なので、ポインタだけ保存しておくことにします。
そうすると、必然的にincludeで生成される構文木はinclude元の評価が終わるまで取っておくことになります。
で、その辺を考慮しつつ、実行コンテキストなどを変更中です。
今日の差分


以下、今日のお試しコード。

#include <hamigaki/bjam2/grammars/bjam_grammar.hpp>
#include <hamigaki/bjam2/util/list_of_list.hpp>
#include <hamigaki/bjam2/util/skip_parser.hpp>
#include <boost/spirit/tree/parse_tree.hpp>
#include <exception>
#include <iostream>

namespace bjam = hamigaki::bjam2;
namespace spirit = boost::spirit;

typedef bjam::bjam_grammar grammar_t;
typedef spirit::tree_match<const char*> parse_tree_match_t;
typedef parse_tree_match_t::node_t node_t;
typedef parse_tree_match_t::const_tree_iterator iter_t;

bjam::string_list eval_non_punct(const node_t& tree)
{
    if (tree.children.size() == 1)
    {
        const node_t& x = tree.children.front();
        return bjam::string_list(std::string(x.value.begin(), x.value.end()));
    }
    else
    {
        // TODO: 関数呼び出しの場合
        return bjam::string_list();
    }
}

bjam::string_list eval_list(const node_t& tree)
{
    iter_t beg = tree.children.begin();
    iter_t end = tree.children.end();

    bjam::string_list list;
    for ( ; beg != end; ++beg)
    {
        if (beg->value.id().to_long() == grammar_t::non_punct_id)
            list += ::eval_non_punct(*beg);
    }
    return list;
}

bjam::list_of_list eval_lol(const node_t& tree)
{
    iter_t beg = tree.children.begin();
    iter_t end = tree.children.end();

    bjam::list_of_list values;
    for ( ; beg != end; ++beg)
        values.push_back(::eval_list(*beg));

    return values;
}

bjam::list_of_list eval_invoke_stmt(const node_t& tree)
{
    iter_t beg = tree.children.begin();
    iter_t end = tree.children.end();

    std::string name(beg->value.begin(), beg->value.end());
    if (name == "ECHO")
    {
        bjam::list_of_list args = ::eval_lol(*++beg);

        if (!args.empty())
            std::cout << args[0] << std::endl;
        else
            std::cout << std::endl;
    }

    return bjam::list_of_list();
}

bjam::list_of_list eval_rule(const node_t& tree)
{
    iter_t beg = tree.children.begin();
    iter_t end = tree.children.end();

    if (beg->value.id() == spirit::parser_id(grammar_t::invoke_stmt_id))
    {
        return ::eval_invoke_stmt(*beg);
    }
    else
    {
        // TODO: invoke_stmt以外の場合
        return bjam::list_of_list();
    }
}

bjam::list_of_list eval_rules(const node_t& tree)
{
    iter_t beg = tree.children.begin();
    iter_t end = tree.children.end();

    if (beg->value.id() == spirit::parser_id(grammar_t::rule_id))
    {
        bjam::list_of_list values = ::eval_rule(*beg);
        while (++beg != end)
            values = ::eval_rules(*beg);
        return values;
    }
    else
    {
        // TODO: local_set_stmtの場合
        return bjam::list_of_list();
    }
}

bjam::list_of_list eval_run(const node_t& tree)
{
    if (!tree.children.empty())
        return ::eval_rules(tree.children.front());
    else
        return bjam::list_of_list();
}

bjam::list_of_list evaluate(const spirit::tree_parse_info<>& info)
{
    return ::eval_run(info.trees[0]);
}

int main()
{
    try
    {
        grammar_t g;
        bjam::skip_parser skip;

        spirit::tree_parse_info<> info =
            spirit::pt_parse("ECHO a b ;", g, skip);

        if (info.full)
            std::cout << ::evaluate(info) << std::endl;
        else
            std::cerr << "failed" << std::endl;
    }
    catch (const std::exception& e)
    {
        std::cerr << e.what() << std::endl;
    }
}

実行結果

a b