構文木を作る その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