diff --git a/cheatsheet.md b/cheatsheet.md index ab85c16..c220dfd 100644 --- a/cheatsheet.md +++ b/cheatsheet.md @@ -455,6 +455,13 @@ the contained function. If both a 2 parameter and a 3 parameter signature match, the 3 parameter function always wins. +## Context + + * `__LINE__` Current file line number + * `__FILE__` Full path of current file + * `__CLASS__` Name of current class + * `__FUNC__` Mame of current function + # Built In Functions diff --git a/include/chaiscript/language/chaiscript_common.hpp b/include/chaiscript/language/chaiscript_common.hpp index ea42206..2e8e05c 100644 --- a/include/chaiscript/language/chaiscript_common.hpp +++ b/include/chaiscript/language/chaiscript_common.hpp @@ -31,7 +31,8 @@ namespace chaiscript { static const std::set m_reserved_words = {"def", "fun", "while", "for", "if", "else", "&&", "||", ",", "auto", - "return", "break", "true", "false", "class", "attr", "var", "global", "GLOBAL", "_"}; + "return", "break", "true", "false", "class", "attr", "var", "global", "GLOBAL", "_", + "__LINE__", "__FILE__", "__FUNC__", "__CLASS__"}; return m_reserved_words.count(name) > 0; } diff --git a/include/chaiscript/language/chaiscript_eval.hpp b/include/chaiscript/language/chaiscript_eval.hpp index 4ad24bb..58be8a0 100644 --- a/include/chaiscript/language/chaiscript_eval.hpp +++ b/include/chaiscript/language/chaiscript_eval.hpp @@ -1368,30 +1368,27 @@ namespace chaiscript AST_Node_Impl_Ptr guardnode; - const auto d = t_ss->get_parent_locals(); - const auto itr = d.find("_current_class_name"); - const auto class_offset = (itr != d.end())?-1:0; - const std::string & class_name = (itr != d.end())?std::string(boxed_cast(itr->second)):this->children[0]->text; + const std::string & class_name = this->children[0]->text; //The first param of a method is always the implied this ptr. std::vector t_param_names{"this"}; dispatch::Param_Types param_types; - if ((this->children.size() > static_cast(3 + class_offset)) - && (this->children[static_cast(2 + class_offset)]->identifier == AST_Node_Type::Arg_List)) { - auto args = Arg_List_AST_Node::get_arg_names(this->children[static_cast(2 + class_offset)]); + if ((this->children.size() > 3) + && (this->children[2]->identifier == AST_Node_Type::Arg_List)) { + auto args = Arg_List_AST_Node::get_arg_names(this->children[2]); t_param_names.insert(t_param_names.end(), args.begin(), args.end()); - param_types = Arg_List_AST_Node::get_arg_types(this->children[static_cast(2 + class_offset)], t_ss); + param_types = Arg_List_AST_Node::get_arg_types(this->children[2], t_ss); - if (this->children.size() > static_cast(4 + class_offset)) { - guardnode = this->children[static_cast(3 + class_offset)]; + if (this->children.size() > 4) { + guardnode = this->children[3]; } } else { //no parameters - if (this->children.size() > static_cast(3 + class_offset)) { - guardnode = this->children[static_cast(2 + class_offset)]; + if (this->children.size() > 3) { + guardnode = this->children[2]; } } @@ -1408,7 +1405,7 @@ namespace chaiscript } try { - const std::string & function_name = this->children[static_cast(1 + class_offset)]->text; + const std::string & function_name = this->children[1]->text; auto node = this->children.back(); if (function_name == class_name) { @@ -1454,13 +1451,10 @@ namespace chaiscript Boxed_Value eval_internal(const chaiscript::detail::Dispatch_State &t_ss) const override { - const auto &d = t_ss->get_parent_locals(); - const auto itr = d.find("_current_class_name"); - const auto class_offset = (itr != d.end())?-1:0; - std::string class_name = (itr != d.end())?std::string(boxed_cast(itr->second)):this->children[0]->text; + std::string class_name = this->children[0]->text; try { - std::string attr_name = this->children[static_cast(1 + class_offset)]->text; + std::string attr_name = this->children[1]->text; t_ss->add( std::make_shared( @@ -1470,7 +1464,7 @@ namespace chaiscript }), true - ), this->children[static_cast(1 + class_offset)]->text); + ), this->children[1]->text); } catch (const exception::name_conflict_error &e) { throw exception::eval_error("Attribute redefined '" + e.name() + "'"); } diff --git a/include/chaiscript/language/chaiscript_parser.hpp b/include/chaiscript/language/chaiscript_parser.hpp index 2b384c6..7c694ef 100644 --- a/include/chaiscript/language/chaiscript_parser.hpp +++ b/include/chaiscript/language/chaiscript_parser.hpp @@ -839,6 +839,41 @@ namespace chaiscript } else if (text == "NaN") { m_match_stack.push_back(make_node>(text, start.line, start.col, const_var(std::numeric_limits::quiet_NaN()))); + } else if (text == "__LINE__") { + m_match_stack.push_back(make_node>(text, start.line, start.col, + const_var(start.line))); + } else if (text == "__FILE__") { + m_match_stack.push_back(make_node>(text, start.line, start.col, + const_var(m_filename))); + } else if (text == "__FUNC__") { + const std::string fun_name = [&]()->std::string{ + for (size_t idx = m_match_stack.size() - 1; idx > 0; --idx) + { + if (m_match_stack[idx-1]->identifier == AST_Node_Type::Id + && m_match_stack[idx-0]->identifier == AST_Node_Type::Arg_List) { + return m_match_stack[idx-1]->text; + } + } + return "NOT_IN_FUNCTION"; + }(); + + m_match_stack.push_back(make_node>(text, start.line, start.col, + const_var(std::move(fun_name)))); + } else if (text == "__CLASS__") { + const std::string fun_name = [&]()->std::string{ + for (size_t idx = m_match_stack.size() - 1; idx > 1; --idx) + { + if (m_match_stack[idx-2]->identifier == AST_Node_Type::Id + && m_match_stack[idx-1]->identifier == AST_Node_Type::Id + && m_match_stack[idx-0]->identifier == AST_Node_Type::Arg_List) { + return m_match_stack[idx-2]->text; + } + } + return "NOT_IN_CLASS"; + }(); + + m_match_stack.push_back(make_node>(text, start.line, start.col, + const_var(std::move(fun_name)))); } else if (text == "_") { m_match_stack.push_back(make_node>(text, start.line, start.col, Boxed_Value(std::make_shared()))); @@ -1517,7 +1552,7 @@ namespace chaiscript } /// Reads a function definition from input - bool Def(const bool t_class_context = false) { + bool Def(const bool t_class_context = false, const std::string &t_class_name = "") { bool retval = false; const auto prev_stack_top = m_match_stack.size(); @@ -1525,6 +1560,10 @@ namespace chaiscript if (Keyword("def")) { retval = true; + if (t_class_context) { + m_match_stack.push_back(make_node>(t_class_name, m_position.line, m_position.col)); + } + if (!Id(true)) { throw exception::eval_error("Missing function name in definition", File_Position(m_position.line, m_position.col), *m_filename); } @@ -1708,10 +1747,11 @@ namespace chaiscript throw exception::eval_error("Missing class name in definition", File_Position(m_position.line, m_position.col), *m_filename); } + const auto class_name = m_match_stack.back()->text; while (Eol()) {} - if (!Class_Block()) { + if (!Class_Block(class_name)) { throw exception::eval_error("Incomplete 'class' block", File_Position(m_position.line, m_position.col), *m_filename); } @@ -1908,7 +1948,7 @@ namespace chaiscript /// Reads a curly-brace C-style class block from input - bool Class_Block() { + bool Class_Block(const std::string &t_class_name) { bool retval = false; const auto prev_stack_top = m_match_stack.size(); @@ -1916,7 +1956,7 @@ namespace chaiscript if (Char('{')) { retval = true; - Class_Statements(); + Class_Statements(t_class_name); if (!Char('}')) { throw exception::eval_error("Incomplete class block", File_Position(m_position.line, m_position.col), *m_filename); } @@ -2059,7 +2099,7 @@ namespace chaiscript } /// Reads a variable declaration from input - bool Var_Decl(const bool t_class_context = false) { + bool Var_Decl(const bool t_class_context = false, const std::string &t_class_name = "") { bool retval = false; const auto prev_stack_top = m_match_stack.size(); @@ -2067,6 +2107,8 @@ namespace chaiscript if (t_class_context && (Keyword("attr") || Keyword("auto") || Keyword("var"))) { retval = true; + m_match_stack.push_back(make_node>(t_class_name, m_position.line, m_position.col)); + if (!Id(true)) { throw exception::eval_error("Incomplete attribute declaration", File_Position(m_position.line, m_position.col), *m_filename); } @@ -2176,7 +2218,7 @@ namespace chaiscript /// Reads a unary prefixed expression from input bool Prefix() { const auto prev_stack_top = m_match_stack.size(); - constexpr const std::array prefix_opers{"++", "--", "-", "+", "!", "~"}; + constexpr const std::array prefix_opers{{"++", "--", "-", "+", "!", "~"}}; for (const auto &oper : prefix_opers) { @@ -2351,7 +2393,7 @@ namespace chaiscript } /// Parses statements allowed inside of a class block - bool Class_Statements() { + bool Class_Statements(const std::string &t_class_name) { bool retval = false; bool has_more = true; @@ -2359,7 +2401,7 @@ namespace chaiscript while (has_more) { const auto start = m_position; - if (Def(true) || Var_Decl(true)) { + if (Def(true, t_class_name) || Var_Decl(true, t_class_name)) { if (!saw_eol) { throw exception::eval_error("Two function definitions missing line separator", File_Position(start.line, start.col), *m_filename); } diff --git a/unittests/execution_context.chai b/unittests/execution_context.chai new file mode 100644 index 0000000..2944827 --- /dev/null +++ b/unittests/execution_context.chai @@ -0,0 +1,42 @@ + + +assert_equal(__LINE__, 3) + +def f() { + [__LINE__, __CLASS__, __FUNC__] +} + +var res = f() + +assert_equal(res[0], 6) +assert_equal(res[1], "NOT_IN_CLASS") +assert_equal(res[2], "f") + +assert_equal(__CLASS__, "NOT_IN_CLASS") +assert_equal(__FUNC__, "NOT_IN_FUNCTION") + +class C +{ + def C() {} + def member() { [__LINE__, __CLASS__, __FUNC__]; } +} + +var c = C(); + +var res2 = c.member(); + +assert_equal(res2[0], 21) +assert_equal(res2[1], "C") +assert_equal(res2[2], "member") + +def C::member2() { [__LINE__, __CLASS__, __FUNC__]; } + +var res3 = c.member2(); + +assert_equal(res3[0], 32) +assert_equal(res3[1], "C") +assert_equal(res3[2], "member2") + +assert_true(__FILE__.find("execution_context.chai") != -1) + +