From 5d6755ff64f4021f06dc8a9c15521e31fc128eee Mon Sep 17 00:00:00 2001 From: yhirose Date: Tue, 28 Jul 2015 06:47:18 -0400 Subject: [PATCH] Added command-line debugger support. --- CMakeLists.txt | 4 +- language/culebra.h | 113 +++++++++++++++++++++++++++------------------ language/main.cc | 83 +++++++++++++++++++++++++++++---- peglib.h | 91 +++++++++++++++++++----------------- 4 files changed, 192 insertions(+), 99 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 791cab0..a6fd919 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,6 +1,6 @@ cmake_minimum_required(VERSION 3.0) include_directories(.) -add_definitions("-std=c++1y") +add_definitions("-std=c++1y" -DPEGLIB_HAS_CONSTEXPR_SUPPORT) add_executable(peglint lint/peglint.cc) target_link_libraries(peglint pthread) @@ -20,5 +20,5 @@ target_link_libraries(calc2 pthread) add_executable(calc3 example/calc3.cc) target_link_libraries(calc3 pthread) -add_executable(culebra language/main.cc language/repl.cc language/interpreter.cc language/parser.cc) +add_executable(culebra language/main.cc) target_link_libraries(culebra pthread) diff --git a/language/culebra.h b/language/culebra.h index 1dbd2be..d9196df 100644 --- a/language/culebra.h +++ b/language/culebra.h @@ -8,7 +8,11 @@ namespace culebra { const auto grammar_ = R"( PROGRAM <- _ STATEMENTS - STATEMENTS <- (EXPRESSION (';' _)?)* + STATEMENTS <- (STATEMENT (';' _)?)* + STATEMENT <- DEBUGGER / EXPRESSION + + DEBUGGER <- 'debugger' _ + EXPRESSION <- ASSIGNMENT / LOGICAL_OR ASSIGNMENT <- MUTABLE PRIMARY (ARGUMENTS / INDEX / DOT)* '=' _ EXPRESSION @@ -336,7 +340,10 @@ inline std::ostream& operator<<(std::ostream& os, const Value& val) struct Environment { - Environment() = default; + Environment(std::shared_ptr parent = nullptr) + : parent(parent) + , level(parent ? parent->level + 1 : 0) { + } void append_outer(std::shared_ptr outer) { if (this->outer) { @@ -385,12 +392,17 @@ struct Environment dic_[s] = Symbol{ val, mut }; } - std::shared_ptr outer; + std::shared_ptr outer; + + std::shared_ptr parent; + size_t level; private: std::map dic_; }; +typedef std::function env, bool force_to_break)> Debugger; + inline bool ObjectValue::has(const std::string& name) const { if (properties->find(name) == properties->end()) { const auto& props = const_cast(this)->builtins(); @@ -521,9 +533,20 @@ inline void setup_built_in_functions(Environment& env) { struct Eval { - static Value eval(const peglib::Ast& ast, std::shared_ptr env) { + Eval(Debugger debugger = nullptr) + : debugger_(debugger) { + } + + Value eval(const peglib::Ast& ast, std::shared_ptr env) { using peglib::operator"" _; + if (debugger_) { + if (ast.original_tag == "STATEMENT"_) { + auto force_to_break = ast.tag == "DEBUGGER"_; + debugger_(ast, env, force_to_break); + } + } + switch (ast.tag) { case "STATEMENTS"_: return eval_statements(ast, env); case "WHILE"_: return eval_while(ast, env); @@ -547,6 +570,7 @@ struct Eval case "BOOLEAN"_: return eval_bool(ast, env); case "NUMBER"_: return eval_number(ast, env); case "INTERPOLATED_STRING"_: return eval_interpolated_string(ast, env); + case "DEBUGGER"_: return Value(); } if (ast.is_token) { @@ -558,7 +582,7 @@ struct Eval } private: - static Value eval_statements(const peglib::Ast& ast, std::shared_ptr env) { + Value eval_statements(const peglib::Ast& ast, std::shared_ptr env) { if (ast.is_token) { return eval(ast, env); } else if (ast.nodes.empty()) { @@ -572,7 +596,7 @@ private: return eval(**it, env); } - static Value eval_while(const peglib::Ast& ast, std::shared_ptr env) { + Value eval_while(const peglib::Ast& ast, std::shared_ptr env) { for (;;) { auto cond = eval(*ast.nodes[0], env); if (!cond.to_bool()) { @@ -583,7 +607,7 @@ private: return Value(); } - static Value eval_if(const peglib::Ast& ast, std::shared_ptr env) { + Value eval_if(const peglib::Ast& ast, std::shared_ptr env) { const auto& nodes = ast.nodes; for (auto i = 0u; i < nodes.size(); i += 2) { @@ -600,7 +624,7 @@ private: return Value(); } - static Value eval_function(const peglib::Ast& ast, std::shared_ptr env) { + Value eval_function(const peglib::Ast& ast, std::shared_ptr env) { std::vector params; for (auto node: ast.nodes[0]->nodes) { auto mut = node->nodes[0]->token == "mut"; @@ -619,13 +643,13 @@ private: )); }; - static Value eval_function_call(const peglib::Ast& ast, std::shared_ptr env, const Value& val) { + Value eval_function_call(const peglib::Ast& ast, std::shared_ptr env, const Value& val) { const auto& f = val.to_function(); const auto& params = *f.params; const auto& args = ast.nodes; if (params.size() <= args.size()) { - auto callEnv = std::make_shared(); + auto callEnv = std::make_shared(env); callEnv->initialize("self", val, false); for (auto iprm = 0u; iprm < params.size(); iprm++) { auto param = params[iprm]; @@ -642,7 +666,7 @@ private: throw std::runtime_error(msg); } - static Value eval_array_reference(const peglib::Ast& ast, std::shared_ptr env, const Value& val) { + Value eval_array_reference(const peglib::Ast& ast, std::shared_ptr env, const Value& val) { const auto& arr = val.to_array(); auto idx = eval(ast, env).to_long(); if (0 <= idx && idx < static_cast(arr.values->size())) { @@ -653,7 +677,7 @@ private: return val; } - static Value eval_property(const peglib::Ast& ast, std::shared_ptr env, const Value& val) { + Value eval_property(const peglib::Ast& ast, std::shared_ptr env, const Value& val) { const auto& obj = val.to_object(); auto name = ast.token; if (!obj.has(name)) { @@ -673,7 +697,7 @@ private: return prop; } - static Value eval_call(const peglib::Ast& ast, std::shared_ptr env) { + Value eval_call(const peglib::Ast& ast, std::shared_ptr env) { using peglib::operator"" _; Value val = eval(*ast.nodes[0], env); @@ -692,11 +716,11 @@ private: return std::move(val); } - static Value eval_block(const peglib::Ast& ast, std::shared_ptr env) { + Value eval_block(const peglib::Ast& ast, std::shared_ptr env) { return Value(); } - static Value eval_logical_or(const peglib::Ast& ast, std::shared_ptr env) { + Value eval_logical_or(const peglib::Ast& ast, std::shared_ptr env) { assert(ast.nodes.size() > 1); // if the size is 1, thes node will be hoisted. Value val; for (auto node: ast.nodes) { @@ -708,7 +732,7 @@ private: return std::move(val); } - static Value eval_logical_and(const peglib::Ast& ast, std::shared_ptr env) { + Value eval_logical_and(const peglib::Ast& ast, std::shared_ptr env) { Value val; for (auto node: ast.nodes) { val = eval(*node, env); @@ -719,7 +743,7 @@ private: return std::move(val); } - static Value eval_condition(const peglib::Ast& ast, std::shared_ptr env) { + Value eval_condition(const peglib::Ast& ast, std::shared_ptr env) { assert(ast.nodes.size() == 3); // if the size is 1, thes node will be hoisted. auto lhs = eval(*ast.nodes[0], env); @@ -735,22 +759,22 @@ private: else { throw std::logic_error("invalid internal condition."); } } - static Value eval_unary_plus(const peglib::Ast& ast, std::shared_ptr env) { + Value eval_unary_plus(const peglib::Ast& ast, std::shared_ptr env) { assert(ast.nodes.size() == 2); // if the size is 1, thes node will be hoisted. return eval(*ast.nodes[1], env); } - static Value eval_unary_minus(const peglib::Ast& ast, std::shared_ptr env) { + Value eval_unary_minus(const peglib::Ast& ast, std::shared_ptr env) { assert(ast.nodes.size() == 2); // if the size is 1, thes node will be hoisted. return Value(eval(*ast.nodes[1], env).to_long() * -1); } - static Value eval_unary_not(const peglib::Ast& ast, std::shared_ptr env) { + Value eval_unary_not(const peglib::Ast& ast, std::shared_ptr env) { assert(ast.nodes.size() == 2); // if the size is 1, thes node will be hoisted. return Value(!eval(*ast.nodes[1], env).to_bool()); } - static Value eval_bin_expression(const peglib::Ast& ast, std::shared_ptr env) { + Value eval_bin_expression(const peglib::Ast& ast, std::shared_ptr env) { auto ret = eval(*ast.nodes[0], env).to_long(); for (auto i = 1u; i < ast.nodes.size(); i += 2) { auto val = eval(*ast.nodes[i + 1], env).to_long(); @@ -766,7 +790,7 @@ private: return Value(ret); } - static Value eval_assignment(const peglib::Ast& ast, std::shared_ptr env) { + Value eval_assignment(const peglib::Ast& ast, std::shared_ptr env) { auto end = ast.nodes.size() - 1; auto mut = ast.nodes[0]->token == "mut"; @@ -826,11 +850,11 @@ private: } }; - static Value eval_identifier(const peglib::Ast& ast, std::shared_ptr env) { + Value eval_identifier(const peglib::Ast& ast, std::shared_ptr env) { return env->get(ast.token); }; - static Value eval_object(const peglib::Ast& ast, std::shared_ptr env) { + Value eval_object(const peglib::Ast& ast, std::shared_ptr env) { ObjectValue obj; for (auto i = 0u; i < ast.nodes.size(); i++) { const auto& prop = *ast.nodes[i]; @@ -842,7 +866,7 @@ private: return Value(std::move(obj)); } - static Value eval_array(const peglib::Ast& ast, std::shared_ptr env) { + Value eval_array(const peglib::Ast& ast, std::shared_ptr env) { ArrayValue arr; for (auto i = 0u; i < ast.nodes.size(); i++) { auto expr = ast.nodes[i]; @@ -852,19 +876,19 @@ private: return Value(std::move(arr)); } - static Value eval_undefined(const peglib::Ast& ast, std::shared_ptr env) { + Value eval_undefined(const peglib::Ast& ast, std::shared_ptr env) { return Value(); }; - static Value eval_bool(const peglib::Ast& ast, std::shared_ptr env) { + Value eval_bool(const peglib::Ast& ast, std::shared_ptr env) { return Value(ast.token == "true"); }; - static Value eval_number(const peglib::Ast& ast, std::shared_ptr env) { + Value eval_number(const peglib::Ast& ast, std::shared_ptr env) { return Value(stol(ast.token)); }; - static Value eval_interpolated_string(const peglib::Ast& ast, std::shared_ptr env) { + Value eval_interpolated_string(const peglib::Ast& ast, std::shared_ptr env) { std::string s; for (auto node: ast.nodes) { const auto& val = eval(*node, env); @@ -876,16 +900,19 @@ private: } return Value(std::move(s)); }; + + Debugger debugger_; }; inline bool run( - const std::string& path, - std::shared_ptr env, - const char* expr, - size_t len, - Value& val, - std::string& msg, - bool print_ast) + const std::string& path, + std::shared_ptr env, + const char* expr, + size_t len, + Value& val, + std::vector& msgs, + std::shared_ptr& ast, + Debugger debugger = nullptr) { try { auto& parser = get_parser(); @@ -893,21 +920,15 @@ inline bool run( parser.log = [&](size_t ln, size_t col, const std::string& err_msg) { std::stringstream ss; ss << path << ":" << ln << ":" << col << ": " << err_msg << std::endl; - msg = ss.str(); + msgs.push_back(ss.str()); }; - std::shared_ptr ast; - - if (parser.parse_n(expr, len, ast)) { - if (print_ast) { - ast->print(); - } - - val = Eval::eval(*ast, env); + if (parser.parse_n(expr, len, ast, path.c_str())) { + val = Eval(debugger).eval(*ast, env); return true; } } catch (std::runtime_error& e) { - msg = e.what(); + msgs.push_back(e.what()); } return false; diff --git a/language/main.cc b/language/main.cc index 43aa441..aea003c 100644 --- a/language/main.cc +++ b/language/main.cc @@ -24,6 +24,49 @@ bool read_file(const char* path, vector& buff) return true; } +struct CommandLineDebugger +{ + void operator()(const peglib::Ast& ast, std::shared_ptr env, bool force_to_break) { + if (quit) { + force_to_break = false; + } if (line == "n" && env->level <= level) { + force_to_break = true; + } else if (line == "s") { + force_to_break = true; + } else if (line == "o" && env->level < level) { + force_to_break = true; + } + + if (force_to_break) { + for (;;) { + line = linenoise::Readline("debug> "); + + if (line == "bt") { + std::cout << "level: " << env->level << std::endl; + } else if (line == "l") { // show source file + std::cout << "line: " << ast.line << " in " << ast.path << std::endl; + } else if (line == "c") { // continue + break; + } else if (line == "n") { // step over + break; + } else if (line == "s") { // step into + break; + } else if (line == "o") { // step out + break; + } else if (line == "q") { // quit + quit = true; + break; + } + } + level = env->level;; + } + } + + std::string line; + size_t level = 0; + bool quit = false; +}; + int repl(shared_ptr env, bool print_ast) { for (;;) { @@ -35,12 +78,19 @@ int repl(shared_ptr env, bool print_ast) if (!line.empty()) { Value val; - string msg; - if (run("(repl)", env, line.c_str(), line.size(), val, msg, print_ast)) { + vector msgs; + std::shared_ptr ast; + auto ret = run("(repl)", env, line.c_str(), line.size(), val, msgs, ast); + if (ret) { + if (print_ast) { + ast->print(); + } cout << val << endl; linenoise::AddHistory(line.c_str()); - } else if (!msg.empty()) { - cout << msg << endl;; + } else if (!msgs.empty()) { + for (const auto& msg: msgs) { + cout << msg << endl;; + } } } } @@ -52,6 +102,7 @@ int main(int argc, const char** argv) { auto print_ast = false; auto shell = false; + auto debug = false; vector path_list; int argi = 1; @@ -61,6 +112,8 @@ int main(int argc, const char** argv) shell = true; } else if (string("--ast") == arg) { print_ast = true; + } else if (string("--debug") == arg) { + debug = true; } else { path_list.push_back(arg); } @@ -81,11 +134,25 @@ int main(int argc, const char** argv) return -1; } - Value val; - string msg; - if (!run(path, env, buff.data(), buff.size(), val, msg, print_ast)) { - cerr << msg << endl; + Value val; + vector msgs; + std::shared_ptr ast; + Debugger dbg; + + CommandLineDebugger debugger; + if (debug) { + dbg = debugger; + } + + auto ret = run(path, env, buff.data(), buff.size(), val, msgs, ast, dbg); + + if (!ret) { + for (const auto& msg: msgs) { + cerr << msg << endl; + } return -1; + } else if (print_ast) { + ast->print(); } } diff --git a/peglib.h b/peglib.h index 959311d..4addbbb 100644 --- a/peglib.h +++ b/peglib.h @@ -180,6 +180,7 @@ struct SemanticValue struct SemanticValues : protected std::vector { + const char* path; const char* ss; const char* s; size_t n; @@ -407,6 +408,7 @@ inline bool fail(size_t len) { */ struct Context { + const char* path; const char* s; size_t l; @@ -423,8 +425,9 @@ struct Context std::map, std::tuple> cache_result; - Context(const char* s, size_t l, size_t def_count, bool enablePackratParsing) - : s(s) + Context(const char* path, const char* s, size_t l, size_t def_count, bool enablePackratParsing) + : path(path) + , s(s) , l(l) , error_pos(nullptr) , message_pos(nullptr) @@ -475,6 +478,7 @@ struct Context if (!sv.empty()) { sv.clear(); } + sv.path = path; sv.ss = s; sv.s = nullptr; sv.n = 0; @@ -1085,32 +1089,32 @@ public: return *this; } - Result parse(const char* s, size_t n) const { + Result parse(const char* s, size_t n, const char* path = nullptr) const { SemanticValues sv; any dt; - return parse_core(s, n, sv, dt); + return parse_core(s, n, sv, dt, path); } - Result parse(const char* s) const { + Result parse(const char* s, const char* path = nullptr) const { auto n = strlen(s); - return parse(s, n); + return parse(s, n, path); } - Result parse(const char* s, size_t n, any& dt) const { + Result parse(const char* s, size_t n, any& dt, const char* path = nullptr) const { SemanticValues sv; - return parse_core(s, n, sv, dt); + return parse_core(s, n, sv, dt, path); } - Result parse(const char* s, any& dt) const { + Result parse(const char* s, any& dt, const char* path = nullptr) const { auto n = strlen(s); - return parse(s, n, dt); + return parse(s, n, dt, path); } template - Result parse_and_get_value(const char* s, size_t n, T& val) const { + Result parse_and_get_value(const char* s, size_t n, T& val, const char* path = nullptr) const { SemanticValues sv; any dt; - auto r = parse_core(s, n, sv, dt); + auto r = parse_core(s, n, sv, dt, path); if (r.ret && !sv.empty() && !sv.front().val.is_undefined()) { val = sv[0].val.get(); } @@ -1118,15 +1122,15 @@ public: } template - Result parse_and_get_value(const char* s, T& val) const { + Result parse_and_get_value(const char* s, T& val, const char* path = nullptr) const { auto n = strlen(s); - return parse_and_get_value(s, n, val); + return parse_and_get_value(s, n, val, path); } template - Result parse_and_get_value(const char* s, size_t n, any& dt, T& val) const { + Result parse_and_get_value(const char* s, size_t n, any& dt, T& val, const char* path = nullptr) const { SemanticValues sv; - auto r = parse_core(s, n, sv, dt); + auto r = parse_core(s, n, sv, dt, path); if (r.ret && !sv.empty() && !sv.front().val.is_undefined()) { val = sv[0].val.get(); } @@ -1134,9 +1138,9 @@ public: } template - Result parse_and_get_value(const char* s, any& dt, T& val) const { + Result parse_and_get_value(const char* s, any& dt, T& val, const char* path = nullptr) const { auto n = strlen(s); - return parse_and_get_value(s, n, dt, val); + return parse_and_get_value(s, n, dt, val, path); } Definition& operator=(Action a) { @@ -1173,11 +1177,11 @@ private: Definition& operator=(const Definition& rhs); Definition& operator=(Definition&& rhs); - Result parse_core(const char* s, size_t n, SemanticValues& sv, any& dt) const { + Result parse_core(const char* s, size_t n, SemanticValues& sv, any& dt, const char* path) const { AssignIDToDefinition assignId; holder_->accept(assignId); - Context cxt(s, n, assignId.ids.size(), enablePackratParsing); + Context cxt(path, s, n, assignId.ids.size(), enablePackratParsing); auto len = holder_->parse(s, n, sv, cxt, dt); return Result{ success(len), len, cxt.error_pos, cxt.message_pos, cxt.message }; } @@ -1953,22 +1957,22 @@ inline constexpr unsigned int operator "" _(const char* s, size_t) { struct Ast { - Ast(size_t line, size_t column, const char* name, const std::vector>& nodes) - : line(line), column(column), name(name), original_name(name), is_token(false), nodes(nodes) + Ast(const char* path, size_t line, size_t column, const char* name, const std::vector>& nodes) + : path(path), line(line), column(column), name(name), original_name(name), is_token(false), nodes(nodes) #ifdef PEGLIB_HAS_CONSTEXPR_SUPPORT , tag(str2tag(name)), original_tag(tag) #endif {} - Ast(size_t line, size_t column, const char* name, const std::string& token) - : line(line), column(column), name(name), original_name(name), is_token(true), token(token) + Ast(const char* path, size_t line, size_t column, const char* name, const std::string& token) + : path(path), line(line), column(column), name(name), original_name(name), is_token(true), token(token) #ifdef PEGLIB_HAS_CONSTEXPR_SUPPORT , tag(str2tag(name)), original_tag(tag) #endif {} Ast(const Ast& ast, const char* original_name) - : line(ast.line), column(ast.column), name(ast.name), original_name(original_name) + : path(ast.path), line(ast.line), column(ast.column), name(ast.name), original_name(original_name) , is_token(ast.is_token), token(ast.token), nodes(ast.nodes) #ifdef PEGLIB_HAS_CONSTEXPR_SUPPORT , tag(ast.tag), original_tag(str2tag(original_name)) @@ -1979,6 +1983,7 @@ struct Ast void print() const; + const std::string path; const size_t line; const size_t column; const std::string name; @@ -2079,41 +2084,41 @@ public: return load_grammar(s, n); } - bool parse_n(const char* s, size_t n) const { + bool parse_n(const char* s, size_t n, const char* path = nullptr) const { if (grammar_ != nullptr) { const auto& rule = (*grammar_)[start_]; - auto r = rule.parse(s, n); + auto r = rule.parse(s, n, path); output_log(s, n, log, r); return r.ret && r.len == n; } return false; } - bool parse(const char* s) const { + bool parse(const char* s, const char* path = nullptr) const { auto n = strlen(s); - return parse_n(s, n); + return parse_n(s, n, path); } - bool parse_n(const char* s, size_t n, any& dt) const { + bool parse_n(const char* s, size_t n, any& dt, const char* path = nullptr) const { if (grammar_ != nullptr) { const auto& rule = (*grammar_)[start_]; - auto r = rule.parse(s, n, dt); + auto r = rule.parse(s, n, dt, path); output_log(s, n, log, r); return r.ret && r.len == n; } return false; } - bool parse(const char* s, any& dt) const { + bool parse(const char* s, any& dt, const char* path = nullptr) const { auto n = strlen(s); - return parse_n(s, n, dt); + return parse_n(s, n, dt, path); } template - bool parse_n(const char* s, size_t n, T& val) const { + bool parse_n(const char* s, size_t n, T& val, const char* path = nullptr) const { if (grammar_ != nullptr) { const auto& rule = (*grammar_)[start_]; - auto r = rule.parse_and_get_value(s, n, val); + auto r = rule.parse_and_get_value(s, n, val, path); output_log(s, n, log, r); return r.ret && r.len == n; } @@ -2121,16 +2126,16 @@ public: } template - bool parse(const char* s, T& val) const { + bool parse(const char* s, T& val, const char* path = nullptr) const { auto n = strlen(s); - return parse_n(s, n, val); + return parse_n(s, n, val, path); } template - bool parse_n(const char* s, size_t n, any& dt, T& val) const { + bool parse_n(const char* s, size_t n, any& dt, T& val, const char* path = nullptr) const { if (grammar_ != nullptr) { const auto& rule = (*grammar_)[start_]; - auto r = rule.parse_and_get_value(s, n, dt, val); + auto r = rule.parse_and_get_value(s, n, dt, val, path); output_log(s, n, log, r); return r.ret && r.len == n; } @@ -2138,7 +2143,7 @@ public: } template - bool parse(const char* s, any& dt, T& val) const { + bool parse(const char* s, any& dt, T& val, const char* path = nullptr) const { auto n = strlen(s); return parse_n(s, n, dt, val); } @@ -2179,7 +2184,7 @@ public: } } - peg& enable_ast(bool optimize_nodes, const std::initializer_list& filters) { + peg& enable_ast(bool optimize_nodes, const std::initializer_list& filters = {}) { for (auto& x: *grammar_) { const auto& name = x.first; auto& rule = x.second; @@ -2192,14 +2197,14 @@ public: rule.action = [=](const SemanticValues& sv) { if (is_token) { auto line = line_info(sv.ss, sv.s); - return std::make_shared(line.first, line.second, name.c_str(), std::string(sv.s, sv.n)); + return std::make_shared(sv.path, line.first, line.second, name.c_str(), std::string(sv.s, sv.n)); } if (opt && sv.size() == 1) { auto ast = std::make_shared(*sv[0].get>(), name.c_str()); return ast; } auto line = line_info(sv.ss, sv.s); - return std::make_shared(line.first, line.second, name.c_str(), sv.transform>()); + return std::make_shared(sv.path, line.first, line.second, name.c_str(), sv.transform>()); }; } }