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.
## 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

View File

@ -31,7 +31,8 @@ namespace chaiscript
{
static const std::set<std::string> 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;
}

View File

@ -1368,30 +1368,27 @@ namespace chaiscript
AST_Node_Impl_Ptr<T> 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<std::string>(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<std::string> t_param_names{"this"};
dispatch::Param_Types param_types;
if ((this->children.size() > static_cast<size_t>(3 + class_offset))
&& (this->children[static_cast<size_t>(2 + class_offset)]->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)]);
if ((this->children.size() > 3)
&& (this->children[2]->identifier == AST_Node_Type::Arg_List)) {
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());
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)) {
guardnode = this->children[static_cast<size_t>(3 + class_offset)];
if (this->children.size() > 4) {
guardnode = this->children[3];
}
}
else {
//no parameters
if (this->children.size() > static_cast<size_t>(3 + class_offset)) {
guardnode = this->children[static_cast<size_t>(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<size_t>(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<std::string>(itr->second)):this->children[0]->text;
std::string class_name = this->children[0]->text;
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(
std::make_shared<dispatch::detail::Dynamic_Object_Function>(
@ -1470,7 +1464,7 @@ namespace chaiscript
}),
true
), this->children[static_cast<size_t>(1 + class_offset)]->text);
), this->children[1]->text);
} catch (const exception::name_conflict_error &e) {
throw exception::eval_error("Attribute redefined '" + e.name() + "'");
}

View File

@ -839,6 +839,41 @@ namespace chaiscript
} else if (text == "NaN") {
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())));
} 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 == "_") {
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>())));
@ -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<eval::Id_AST_Node<Tracer>>(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<eval::Id_AST_Node<Tracer>>(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<const char *, 6> prefix_opers{"++", "--", "-", "+", "!", "~"};
constexpr const std::array<const char *, 6> 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);
}

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)