parent
cb1c7730cf
commit
fa1f4b795b
@ -736,6 +736,19 @@ namespace chaiscript
|
||||
return functions.find(name) != functions.end();
|
||||
}
|
||||
|
||||
/// \returns All values in the local thread state in the parent scope, or if it doesn't exist,
|
||||
/// the current scope.
|
||||
std::map<std::string, Boxed_Value> get_parent_locals() const
|
||||
{
|
||||
StackData &stack = get_stack_data();
|
||||
if (stack.size() > 1)
|
||||
{
|
||||
return stack[1];
|
||||
} else {
|
||||
return stack[0];
|
||||
}
|
||||
}
|
||||
|
||||
/// \returns All values in the local thread state, added through the add() function
|
||||
std::map<std::string, Boxed_Value> get_locals() const
|
||||
{
|
||||
|
@ -37,7 +37,7 @@ namespace chaiscript
|
||||
Comparison, Addition, Subtraction, Multiplication, Division, Modulus, Array_Call, Dot_Access, Quoted_String, Single_Quoted_String,
|
||||
Lambda, Block, Def, While, If, For, Inline_Array, Inline_Map, Return, File, Prefix, Break, Continue, Map_Pair, Value_Range,
|
||||
Inline_Range, Annotation, Try, Catch, Finally, Method, Attr_Decl, Shift, Equality, Bitwise_And, Bitwise_Xor, Bitwise_Or,
|
||||
Logical_And, Logical_Or, Reference, Switch, Case, Default, Ternary_Cond, Noop
|
||||
Logical_And, Logical_Or, Reference, Switch, Case, Default, Ternary_Cond, Noop, Class
|
||||
};
|
||||
};
|
||||
|
||||
@ -510,6 +510,9 @@ namespace chaiscript
|
||||
/// Creates a new scope then pops it on destruction
|
||||
struct Scope_Push_Pop
|
||||
{
|
||||
Scope_Push_Pop(const Scope_Push_Pop &) = delete;
|
||||
Scope_Push_Pop& operator=(const Scope_Push_Pop &) = delete;
|
||||
|
||||
Scope_Push_Pop(chaiscript::detail::Dispatch_Engine &t_de)
|
||||
: m_de(t_de)
|
||||
{
|
||||
@ -523,9 +526,6 @@ namespace chaiscript
|
||||
|
||||
|
||||
private:
|
||||
// explicitly unimplemented copy and assignment
|
||||
Scope_Push_Pop(const Scope_Push_Pop &);
|
||||
Scope_Push_Pop& operator=(const Scope_Push_Pop &);
|
||||
|
||||
chaiscript::detail::Dispatch_Engine &m_de;
|
||||
};
|
||||
@ -533,6 +533,9 @@ namespace chaiscript
|
||||
/// Creates a new function call and pops it on destruction
|
||||
struct Function_Push_Pop
|
||||
{
|
||||
Function_Push_Pop(const Function_Push_Pop &) = delete;
|
||||
Function_Push_Pop& operator=(const Function_Push_Pop &) = delete;
|
||||
|
||||
Function_Push_Pop(chaiscript::detail::Dispatch_Engine &t_de)
|
||||
: m_de(t_de)
|
||||
{
|
||||
@ -551,9 +554,6 @@ namespace chaiscript
|
||||
|
||||
|
||||
private:
|
||||
// explicitly unimplemented copy and assignment
|
||||
Function_Push_Pop(const Function_Push_Pop &);
|
||||
Function_Push_Pop& operator=(const Function_Push_Pop &);
|
||||
|
||||
chaiscript::detail::Dispatch_Engine &m_de;
|
||||
};
|
||||
@ -561,6 +561,9 @@ namespace chaiscript
|
||||
/// Creates a new scope then pops it on destruction
|
||||
struct Stack_Push_Pop
|
||||
{
|
||||
Stack_Push_Pop(const Stack_Push_Pop &) = delete;
|
||||
Stack_Push_Pop& operator=(const Stack_Push_Pop &) = delete;
|
||||
|
||||
Stack_Push_Pop(chaiscript::detail::Dispatch_Engine &t_de)
|
||||
: m_de(t_de)
|
||||
{
|
||||
@ -574,9 +577,6 @@ namespace chaiscript
|
||||
|
||||
|
||||
private:
|
||||
// explicitly unimplemented copy and assignment
|
||||
Stack_Push_Pop(const Stack_Push_Pop &);
|
||||
Stack_Push_Pop& operator=(const Stack_Push_Pop &);
|
||||
|
||||
chaiscript::detail::Dispatch_Engine &m_de;
|
||||
};
|
||||
|
@ -330,6 +330,7 @@ namespace chaiscript
|
||||
m_engine.add_reserved_word("break");
|
||||
m_engine.add_reserved_word("true");
|
||||
m_engine.add_reserved_word("false");
|
||||
m_engine.add_reserved_word("class");
|
||||
m_engine.add_reserved_word("_");
|
||||
|
||||
if (t_lib)
|
||||
|
@ -856,7 +856,23 @@ namespace chaiscript
|
||||
|
||||
return Boxed_Value();
|
||||
}
|
||||
};
|
||||
|
||||
struct Class_AST_Node : public AST_Node {
|
||||
public:
|
||||
Class_AST_Node(const std::string &t_ast_node_text = "", const std::shared_ptr<std::string> &t_fname=std::shared_ptr<std::string>(), int t_start_line = 0, int t_start_col = 0, int t_end_line = 0, int t_end_col = 0) :
|
||||
AST_Node(t_ast_node_text, AST_Node_Type::Class, t_fname, t_start_line, t_start_col, t_end_line, t_end_col) { }
|
||||
virtual ~Class_AST_Node() {}
|
||||
virtual Boxed_Value eval_internal(chaiscript::detail::Dispatch_Engine &t_ss) CHAISCRIPT_OVERRIDE {
|
||||
chaiscript::eval::detail::Scope_Push_Pop spp(t_ss);
|
||||
|
||||
// put class name in current scope so it can be looked up by the attrs and methods
|
||||
t_ss.add_object("_current_class_name", const_var(this->children[0]->text));
|
||||
|
||||
this->children[1]->eval(t_ss);
|
||||
|
||||
return Boxed_Value();
|
||||
}
|
||||
};
|
||||
|
||||
struct Ternary_Cond_AST_Node : public AST_Node {
|
||||
@ -1400,23 +1416,29 @@ namespace chaiscript
|
||||
std::vector<std::string> t_param_names;
|
||||
AST_NodePtr guardnode;
|
||||
|
||||
auto d = t_ss.get_parent_locals();
|
||||
auto itr = d.find("_current_class_name");
|
||||
int class_offset = 0;
|
||||
if (itr != d.end()) class_offset = -1;
|
||||
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.
|
||||
t_param_names.push_back("this");
|
||||
|
||||
if ((this->children.size() > 3) && (this->children[2]->identifier == AST_Node_Type::Arg_List)) {
|
||||
for (size_t i = 0; i < this->children[2]->children.size(); ++i) {
|
||||
t_param_names.push_back(this->children[2]->children[i]->text);
|
||||
if ((this->children.size() > (3 + class_offset)) && (this->children[(2 + class_offset)]->identifier == AST_Node_Type::Arg_List)) {
|
||||
for (size_t i = 0; i < this->children[(2 + class_offset)]->children.size(); ++i) {
|
||||
t_param_names.push_back(this->children[(2 + class_offset)]->children[i]->text);
|
||||
}
|
||||
|
||||
if (this->children.size() > 4) {
|
||||
guardnode = this->children[3];
|
||||
if (this->children.size() > (4 + class_offset)) {
|
||||
guardnode = this->children[(3 + class_offset)];
|
||||
}
|
||||
}
|
||||
else {
|
||||
//no parameters
|
||||
|
||||
if (this->children.size() > 3) {
|
||||
guardnode = this->children[2];
|
||||
if (this->children.size() > (3 + class_offset)) {
|
||||
guardnode = this->children[(2 + class_offset)];
|
||||
}
|
||||
}
|
||||
|
||||
@ -1432,8 +1454,9 @@ namespace chaiscript
|
||||
|
||||
try {
|
||||
const std::string & l_annotation = this->annotation?this->annotation->text:"";
|
||||
const std::string & class_name = this->children[0]->text;
|
||||
const std::string & function_name = this->children[1]->text;
|
||||
|
||||
const std::string & function_name = this->children[(1 + class_offset)]->text;
|
||||
|
||||
if (function_name == class_name) {
|
||||
t_ss.add(Proxy_Function
|
||||
(new dispatch::detail::Dynamic_Object_Constructor(class_name, Proxy_Function
|
||||
@ -1478,22 +1501,28 @@ namespace chaiscript
|
||||
Attr_Decl_AST_Node(const std::string &t_ast_node_text = "", const std::shared_ptr<std::string> &t_fname=std::shared_ptr<std::string>(), int t_start_line = 0, int t_start_col = 0, int t_end_line = 0, int t_end_col = 0) :
|
||||
AST_Node(t_ast_node_text, AST_Node_Type::Attr_Decl, t_fname, t_start_line, t_start_col, t_end_line, t_end_col) { }
|
||||
virtual ~Attr_Decl_AST_Node() {}
|
||||
virtual Boxed_Value eval_internal(chaiscript::detail::Dispatch_Engine &t_ss) CHAISCRIPT_OVERRIDE{
|
||||
try {
|
||||
virtual Boxed_Value eval_internal(chaiscript::detail::Dispatch_Engine &t_ss) CHAISCRIPT_OVERRIDE
|
||||
{
|
||||
const auto &d = t_ss.get_parent_locals();
|
||||
const auto itr = d.find("_current_class_name");
|
||||
int class_offset = 0;
|
||||
if (itr != d.end()) class_offset = -1;
|
||||
std::string class_name = (itr != d.end())?std::string(boxed_cast<std::string>(itr->second)):this->children[0]->text;
|
||||
|
||||
try {
|
||||
t_ss.add(Proxy_Function
|
||||
(new dispatch::detail::Dynamic_Object_Function(
|
||||
this->children[0]->text,
|
||||
class_name,
|
||||
fun(std::function<Boxed_Value (dispatch::Dynamic_Object &)>(std::bind(&dispatch::Dynamic_Object::get_attr,
|
||||
std::placeholders::_1,
|
||||
this->children[1]->text
|
||||
this->children[(1 + class_offset)]->text
|
||||
)))
|
||||
)
|
||||
), this->children[1]->text);
|
||||
), this->children[(1 + class_offset)]->text);
|
||||
|
||||
}
|
||||
catch (const exception::reserved_word_error &) {
|
||||
throw exception::eval_error("Reserved word used as attribute '" + this->children[1]->text + "'");
|
||||
throw exception::eval_error("Reserved word used as attribute '" + this->children[(1 + class_offset)]->text + "'");
|
||||
} catch (const exception::name_conflict_error &e) {
|
||||
throw exception::eval_error("Attribute redefined '" + e.name() + "'");
|
||||
}
|
||||
|
@ -1358,7 +1358,7 @@ namespace chaiscript
|
||||
/**
|
||||
* Reads a function definition from input
|
||||
*/
|
||||
bool Def() {
|
||||
bool Def(bool t_class_context = false) {
|
||||
bool retval = false;
|
||||
bool is_annotated = false;
|
||||
AST_NodePtr annotation;
|
||||
@ -1410,7 +1410,7 @@ namespace chaiscript
|
||||
throw exception::eval_error("Incomplete function definition", File_Position(m_line, m_col), *m_filename);
|
||||
}
|
||||
|
||||
if (is_method) {
|
||||
if (is_method || t_class_context) {
|
||||
build_match(AST_NodePtr(new eval::Method_AST_Node()), prev_stack_top);
|
||||
}
|
||||
else {
|
||||
@ -1555,6 +1555,35 @@ namespace chaiscript
|
||||
return retval;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads a class block from input
|
||||
*/
|
||||
bool Class() {
|
||||
bool retval = false;
|
||||
|
||||
size_t prev_stack_top = m_match_stack.size();
|
||||
|
||||
if (Keyword("class")) {
|
||||
retval = true;
|
||||
|
||||
if (!Id(true)) {
|
||||
throw exception::eval_error("Missing class name in definition", File_Position(m_line, m_col), *m_filename);
|
||||
}
|
||||
|
||||
|
||||
while (Eol()) {}
|
||||
|
||||
if (!Class_Block()) {
|
||||
throw exception::eval_error("Incomplete 'class' block", File_Position(m_line, m_col), *m_filename);
|
||||
}
|
||||
|
||||
build_match(AST_NodePtr(new eval::Class_AST_Node()), prev_stack_top);
|
||||
}
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Reads a while block from input
|
||||
*/
|
||||
@ -1737,6 +1766,28 @@ namespace chaiscript
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads a curly-brace C-style class block from input
|
||||
*/
|
||||
bool Class_Block() {
|
||||
bool retval = false;
|
||||
|
||||
size_t prev_stack_top = m_match_stack.size();
|
||||
|
||||
if (Char('{')) {
|
||||
retval = true;
|
||||
|
||||
Class_Statements();
|
||||
if (!Char('}')) {
|
||||
throw exception::eval_error("Incomplete class block", File_Position(m_line, m_col), *m_filename);
|
||||
}
|
||||
|
||||
build_match(AST_NodePtr(new eval::Block_AST_Node()), prev_stack_top);
|
||||
}
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads a curly-brace C-style block from input
|
||||
*/
|
||||
@ -1875,12 +1926,20 @@ namespace chaiscript
|
||||
/**
|
||||
* Reads a variable declaration from input
|
||||
*/
|
||||
bool Var_Decl() {
|
||||
bool Var_Decl(bool t_class_context = false) {
|
||||
bool retval = false;
|
||||
|
||||
size_t prev_stack_top = m_match_stack.size();
|
||||
|
||||
if (Keyword("auto") || Keyword("var")) {
|
||||
if (t_class_context && (Keyword("attr") || Keyword("auto") || Keyword("var"))) {
|
||||
retval = true;
|
||||
|
||||
if (!Id(true)) {
|
||||
throw exception::eval_error("Incomplete attribute declaration", File_Position(m_line, m_col), *m_filename);
|
||||
}
|
||||
|
||||
build_match(AST_NodePtr(new eval::Attr_Decl_AST_Node()), prev_stack_top);
|
||||
} else if (Keyword("auto") || Keyword("var")) {
|
||||
retval = true;
|
||||
|
||||
if (!(Reference() || Id(true))) {
|
||||
@ -1888,8 +1947,7 @@ namespace chaiscript
|
||||
}
|
||||
|
||||
build_match(AST_NodePtr(new eval::Var_Decl_AST_Node()), prev_stack_top);
|
||||
}
|
||||
else if (Keyword("attr")) {
|
||||
} else if (Keyword("attr")) {
|
||||
retval = true;
|
||||
|
||||
if (!Id(true)) {
|
||||
@ -2262,6 +2320,44 @@ namespace chaiscript
|
||||
return retval;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses statements allowed inside of a class block
|
||||
*/
|
||||
bool Class_Statements() {
|
||||
bool retval = false;
|
||||
|
||||
bool has_more = true;
|
||||
bool saw_eol = true;
|
||||
|
||||
while (has_more) {
|
||||
int prev_line = m_line;
|
||||
int prev_col = m_col;
|
||||
if (Def(true)) {
|
||||
if (!saw_eol) {
|
||||
throw exception::eval_error("Two function definitions missing line separator", File_Position(prev_line, prev_col), *m_filename);
|
||||
}
|
||||
has_more = true;
|
||||
retval = true;
|
||||
saw_eol = true;
|
||||
} else if (Var_Decl(true)) {
|
||||
if (!saw_eol) {
|
||||
throw exception::eval_error("Two function definitions missing line separator", File_Position(prev_line, prev_col), *m_filename);
|
||||
}
|
||||
has_more = true;
|
||||
retval = true;
|
||||
saw_eol = true;
|
||||
} else if (Eol()) {
|
||||
has_more = true;
|
||||
retval = true;
|
||||
saw_eol = true;
|
||||
} else {
|
||||
has_more = false;
|
||||
}
|
||||
}
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
/**
|
||||
* Top level parser, starts parsing of all known parses
|
||||
*/
|
||||
@ -2306,6 +2402,14 @@ namespace chaiscript
|
||||
retval = true;
|
||||
saw_eol = true;
|
||||
}
|
||||
else if (Class()) {
|
||||
if (!saw_eol) {
|
||||
throw exception::eval_error("Two function definitions missing line separator", File_Position(prev_line, prev_col), *m_filename);
|
||||
}
|
||||
has_more = true;
|
||||
retval = true;
|
||||
saw_eol = true;
|
||||
}
|
||||
else if (For()) {
|
||||
if (!saw_eol) {
|
||||
throw exception::eval_error("Two function definitions missing line separator", File_Position(prev_line, prev_col), *m_filename);
|
||||
|
27
unittests/class.chai
Normal file
27
unittests/class.chai
Normal file
@ -0,0 +1,27 @@
|
||||
|
||||
class Vector3
|
||||
{
|
||||
// you can use attr, auto or var in this context
|
||||
attr x
|
||||
auto y
|
||||
var z
|
||||
|
||||
def Vector3(x,y,z)
|
||||
{
|
||||
this.x = x
|
||||
this.y = y
|
||||
this.z = z
|
||||
}
|
||||
|
||||
def doSomething(mult)
|
||||
{
|
||||
return this.x * this.y * this.z * mult
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
auto v = Vector3(1,2,3)
|
||||
assert_equal(1, v.x)
|
||||
assert_equal(v.doSomething(2), 12)
|
||||
|
Loading…
x
Reference in New Issue
Block a user