Add ability to get current script context

closes #277
This commit is contained in:
Jason Turner
2016-10-13 20:42:09 -06:00
parent fac8f3ec90
commit c97a69537d
5 changed files with 114 additions and 28 deletions

View File

@@ -455,6 +455,13 @@ the contained function.
If both a 2 parameter and a 3 parameter signature match, the 3 parameter function always wins. 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 # Built In Functions

View File

@@ -31,7 +31,8 @@ namespace chaiscript
{ {
static const std::set<std::string> m_reserved_words static const std::set<std::string> m_reserved_words
= {"def", "fun", "while", "for", "if", "else", "&&", "||", ",", "auto", = {"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; return m_reserved_words.count(name) > 0;
} }

View File

@@ -1368,30 +1368,27 @@ namespace chaiscript
AST_Node_Impl_Ptr<T> guardnode; AST_Node_Impl_Ptr<T> guardnode;
const auto d = t_ss->get_parent_locals(); const std::string & class_name = this->children[0]->text;
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<std::string>(itr->second)):this->children[0]->text;
//The first param of a method is always the implied this ptr. //The first param of a method is always the implied this ptr.
std::vector<std::string> t_param_names{"this"}; std::vector<std::string> t_param_names{"this"};
dispatch::Param_Types param_types; dispatch::Param_Types param_types;
if ((this->children.size() > static_cast<size_t>(3 + class_offset)) if ((this->children.size() > 3)
&& (this->children[static_cast<size_t>(2 + class_offset)]->identifier == AST_Node_Type::Arg_List)) { && (this->children[2]->identifier == AST_Node_Type::Arg_List)) {
auto args = Arg_List_AST_Node<T>::get_arg_names(this->children[static_cast<size_t>(2 + class_offset)]); auto args = Arg_List_AST_Node<T>::get_arg_names(this->children[2]);
t_param_names.insert(t_param_names.end(), args.begin(), args.end()); t_param_names.insert(t_param_names.end(), args.begin(), args.end());
param_types = Arg_List_AST_Node<T>::get_arg_types(this->children[static_cast<size_t>(2 + class_offset)], t_ss); param_types = Arg_List_AST_Node<T>::get_arg_types(this->children[2], t_ss);
if (this->children.size() > static_cast<size_t>(4 + class_offset)) { if (this->children.size() > 4) {
guardnode = this->children[static_cast<size_t>(3 + class_offset)]; guardnode = this->children[3];
} }
} }
else { else {
//no parameters //no parameters
if (this->children.size() > static_cast<size_t>(3 + class_offset)) { if (this->children.size() > 3) {
guardnode = this->children[static_cast<size_t>(2 + class_offset)]; guardnode = this->children[2];
} }
} }
@@ -1408,7 +1405,7 @@ namespace chaiscript
} }
try { try {
const std::string & function_name = this->children[static_cast<size_t>(1 + class_offset)]->text; const std::string & function_name = this->children[1]->text;
auto node = this->children.back(); auto node = this->children.back();
if (function_name == class_name) { if (function_name == class_name) {
@@ -1454,13 +1451,10 @@ namespace chaiscript
Boxed_Value eval_internal(const chaiscript::detail::Dispatch_State &t_ss) const override Boxed_Value eval_internal(const chaiscript::detail::Dispatch_State &t_ss) const override
{ {
const auto &d = t_ss->get_parent_locals(); std::string class_name = this->children[0]->text;
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<std::string>(itr->second)):this->children[0]->text;
try { try {
std::string attr_name = this->children[static_cast<size_t>(1 + class_offset)]->text; std::string attr_name = this->children[1]->text;
t_ss->add( t_ss->add(
std::make_shared<dispatch::detail::Dynamic_Object_Function>( std::make_shared<dispatch::detail::Dynamic_Object_Function>(
@@ -1470,7 +1464,7 @@ namespace chaiscript
}), }),
true true
), this->children[static_cast<size_t>(1 + class_offset)]->text); ), this->children[1]->text);
} catch (const exception::name_conflict_error &e) { } catch (const exception::name_conflict_error &e) {
throw exception::eval_error("Attribute redefined '" + e.name() + "'"); throw exception::eval_error("Attribute redefined '" + e.name() + "'");
} }

View File

@@ -839,6 +839,41 @@ namespace chaiscript
} else if (text == "NaN") { } else if (text == "NaN") {
m_match_stack.push_back(make_node<eval::Constant_AST_Node<Tracer>>(text, start.line, start.col, m_match_stack.push_back(make_node<eval::Constant_AST_Node<Tracer>>(text, start.line, start.col,
const_var(std::numeric_limits<double>::quiet_NaN()))); const_var(std::numeric_limits<double>::quiet_NaN())));
} else if (text == "__LINE__") {
m_match_stack.push_back(make_node<eval::Constant_AST_Node<Tracer>>(text, start.line, start.col,
const_var(start.line)));
} else if (text == "__FILE__") {
m_match_stack.push_back(make_node<eval::Constant_AST_Node<Tracer>>(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<eval::Constant_AST_Node<Tracer>>(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<eval::Constant_AST_Node<Tracer>>(text, start.line, start.col,
const_var(std::move(fun_name))));
} else if (text == "_") { } else if (text == "_") {
m_match_stack.push_back(make_node<eval::Constant_AST_Node<Tracer>>(text, start.line, start.col, m_match_stack.push_back(make_node<eval::Constant_AST_Node<Tracer>>(text, start.line, start.col,
Boxed_Value(std::make_shared<dispatch::Placeholder_Object>()))); Boxed_Value(std::make_shared<dispatch::Placeholder_Object>())));
@@ -1517,7 +1552,7 @@ namespace chaiscript
} }
/// Reads a function definition from input /// 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; bool retval = false;
const auto prev_stack_top = m_match_stack.size(); const auto prev_stack_top = m_match_stack.size();
@@ -1525,6 +1560,10 @@ namespace chaiscript
if (Keyword("def")) { if (Keyword("def")) {
retval = true; retval = true;
if (t_class_context) {
m_match_stack.push_back(make_node<eval::Id_AST_Node<Tracer>>(t_class_name, m_position.line, m_position.col));
}
if (!Id(true)) { if (!Id(true)) {
throw exception::eval_error("Missing function name in definition", File_Position(m_position.line, m_position.col), *m_filename); 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); 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()) {} 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); 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 /// 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; bool retval = false;
const auto prev_stack_top = m_match_stack.size(); const auto prev_stack_top = m_match_stack.size();
@@ -1916,7 +1956,7 @@ namespace chaiscript
if (Char('{')) { if (Char('{')) {
retval = true; retval = true;
Class_Statements(); Class_Statements(t_class_name);
if (!Char('}')) { if (!Char('}')) {
throw exception::eval_error("Incomplete class block", File_Position(m_position.line, m_position.col), *m_filename); 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 /// 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; bool retval = false;
const auto prev_stack_top = m_match_stack.size(); 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"))) { if (t_class_context && (Keyword("attr") || Keyword("auto") || Keyword("var"))) {
retval = true; retval = true;
m_match_stack.push_back(make_node<eval::Id_AST_Node<Tracer>>(t_class_name, m_position.line, m_position.col));
if (!Id(true)) { if (!Id(true)) {
throw exception::eval_error("Incomplete attribute declaration", File_Position(m_position.line, m_position.col), *m_filename); 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 /// Reads a unary prefixed expression from input
bool Prefix() { bool Prefix() {
const auto prev_stack_top = m_match_stack.size(); const auto prev_stack_top = m_match_stack.size();
constexpr const std::array<const char *, 6> prefix_opers{"++", "--", "-", "+", "!", "~"}; constexpr const std::array<const char *, 6> prefix_opers{{"++", "--", "-", "+", "!", "~"}};
for (const auto &oper : prefix_opers) for (const auto &oper : prefix_opers)
{ {
@@ -2351,7 +2393,7 @@ namespace chaiscript
} }
/// Parses statements allowed inside of a class block /// Parses statements allowed inside of a class block
bool Class_Statements() { bool Class_Statements(const std::string &t_class_name) {
bool retval = false; bool retval = false;
bool has_more = true; bool has_more = true;
@@ -2359,7 +2401,7 @@ namespace chaiscript
while (has_more) { while (has_more) {
const auto start = m_position; 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) { if (!saw_eol) {
throw exception::eval_error("Two function definitions missing line separator", File_Position(start.line, start.col), *m_filename); throw exception::eval_error("Two function definitions missing line separator", File_Position(start.line, start.col), *m_filename);
} }

View File

@@ -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)