diff --git a/include/chaiscript/chaiscript.hpp b/include/chaiscript/chaiscript.hpp index 4ddedd9..84ba0f9 100644 --- a/include/chaiscript/chaiscript.hpp +++ b/include/chaiscript/chaiscript.hpp @@ -41,7 +41,7 @@ namespace chaiscript class Token_Type { public: enum Type { Error, Int, Float, Id, Char, Str, Eol, Fun_Call, Inplace_Fun_Call, Arg_List, Variable, Equation, Var_Decl, Expression, Comparison, Additive, Multiplicative, Negate, Not, Array_Call, Dot_Access, Quoted_String, Single_Quoted_String, Lambda, Block, Def, While, If, For, Inline_Array, Inline_Map, Return, File, Prefix, Break, Map_Pair, Value_Range, - Inline_Range, Annotation, Try, Catch }; }; + Inline_Range, Annotation, Try, Catch, Method, Attr_Decl }; }; /** * Helper lookup to get the name of each node type @@ -50,7 +50,7 @@ namespace chaiscript const char *token_types[] = { "Internal Parser Error", "Int", "Float", "Id", "Char", "Str", "Eol", "Fun_Call", "Inplace_Fun_Call", "Arg_List", "Variable", "Equation", "Var_Decl", "Expression", "Comparison", "Additive", "Multiplicative", "Negate", "Not", "Array_Call", "Dot_Access", "Quoted_String", "Single_Quoted_String", "Lambda", "Block", "Def", "While", "If", "For", "Inline_Array", "Inline_Map", "Return", "File", "Prefix", "Break", "Map_Pair", "Value_Range", - "Inline_Range", "Annotation", "Try", "Catch"}; + "Inline_Range", "Annotation", "Try", "Catch", "Method", "Attr_Decl"}; return token_types[tokentype]; } diff --git a/include/chaiscript/language/chaiscript_eval.hpp b/include/chaiscript/language/chaiscript_eval.hpp index ffac5a9..9121e4b 100644 --- a/include/chaiscript/language/chaiscript_eval.hpp +++ b/include/chaiscript/language/chaiscript_eval.hpp @@ -167,6 +167,7 @@ namespace chaiscript for (i = node->children.size()-3; ((int)i) >= 0; i -= 2) { if (node->children[i+1]->text == "=") { Boxed_Value lhs = eval_token(ss, node->children[i]); + try { if (lhs.is_unknown()) { @@ -225,6 +226,22 @@ namespace chaiscript return ss.get_object(node->children[0]->text); } + /** + * Evaluates an attribute declaration + */ + template + Boxed_Value eval_attr_decl(Eval_System &ss, const TokenPtr &node) { + try { + ss.add(fun(boost::function(boost::bind(&dynamic_object_attribute, node->children[0]->text, + node->children[1]->text, _1))), node->children[1]->text); + + } + catch (reserved_word_error &) { + throw Eval_Error("Reserved word used as attribute '" + node->children[1]->text + "'", node); + } + return Boxed_Value(); + } + /** * Evaluates binary boolean operators. Respects short-circuiting rules. */ @@ -586,7 +603,6 @@ namespace chaiscript Boxed_Value eval_try(Eval_System &ss, const TokenPtr &node) { Boxed_Value retval; retval = Boxed_Value(); - ss.new_scope(); try { @@ -863,6 +879,75 @@ namespace chaiscript return Boxed_Value(); } + /** + * Evaluates a function definition + */ + template + Boxed_Value eval_def_method(Eval_System &ss, const TokenPtr &node) { + //TODO: Merge with eval_def cleanly someday? + unsigned int i; + + std::vector param_names; + std::string annotation = node->annotation?node->annotation->text:""; + boost::shared_ptr guard; + size_t numparams; + std::string class_name = node->children[0]->text; + std::string function_name = node->children[1]->text; + TokenPtr guardnode; + + //The first param of a method is always the implied this ptr. + param_names.push_back("this"); + + if ((node->children.size() > 3) && (node->children[2]->identifier == Token_Type::Arg_List)) { + for (i = 0; i < node->children[2]->children.size(); ++i) { + param_names.push_back(node->children[2]->children[i]->text); + } + + if (node->children.size() > 4) { + guardnode = node->children[3]; + } + } + else { + //no parameters + + if (node->children.size() > 3) { + guardnode = node->children[2]; + } + } + + numparams = param_names.size(); + + if (guardnode) { + guard = boost::shared_ptr + (new Dynamic_Proxy_Function(boost::bind(&eval_function, + boost::ref(ss), guardnode, + param_names, _1), numparams)); + } + + try { + if (function_name == class_name) { + ss.add(Proxy_Function + (new Dynamic_Object_Constructor(class_name, Proxy_Function(new Dynamic_Proxy_Function(boost::bind(&eval_function, + boost::ref(ss), node->children.back(), + param_names, _1), numparams, + annotation, guard)))), function_name); + + } + else { + ss.add(Proxy_Function + (new Dynamic_Object_Function(class_name, Proxy_Function(new Dynamic_Proxy_Function(boost::bind(&eval_function, + boost::ref(ss), node->children.back(), + param_names, _1), numparams, + annotation, guard)))), function_name); + + } + } + catch (reserved_word_error &) { + throw Eval_Error("Reserved word used as method name '" + function_name + "'", node); + } + return Boxed_Value(); + } + /** * Evaluates a lambda (anonymous function) */ @@ -1047,6 +1132,14 @@ namespace chaiscript return eval_def(ss, node); break; + case (Token_Type::Method) : + return eval_def_method(ss, node); + break; + + case (Token_Type::Attr_Decl) : + return eval_attr_decl(ss, node); + break; + case (Token_Type::Lambda) : return eval_lambda(ss, node); break; diff --git a/include/chaiscript/language/chaiscript_parser.hpp b/include/chaiscript/language/chaiscript_parser.hpp index bf842ef..52c60f6 100644 --- a/include/chaiscript/language/chaiscript_parser.hpp +++ b/include/chaiscript/language/chaiscript_parser.hpp @@ -867,7 +867,7 @@ namespace chaiscript bool Def() { bool retval = false; bool is_annotated = false; - + bool is_method = false; TokenPtr annotation; if (Annotation()) { @@ -886,6 +886,15 @@ namespace chaiscript throw Eval_Error("Missing function name in definition", File_Position(line, col), filename); } + if (Symbol("::", false)) { + //We're now a method + is_method = true; + + if (!Id(true)) { + throw Eval_Error("Missing method name in definition", File_Position(line, col), filename); + } + } + if (Char('(')) { Arg_List(); if (!Char(')')) { @@ -906,7 +915,12 @@ namespace chaiscript throw Eval_Error("Incomplete function definition", File_Position(line, col), filename); } - build_match(Token_Type::Def, prev_stack_top); + if (is_method) { + build_match(Token_Type::Method, prev_stack_top); + } + else { + build_match(Token_Type::Def, prev_stack_top); + } if (is_annotated) { match_stack.back()->annotation = annotation; @@ -1230,6 +1244,22 @@ namespace chaiscript build_match(Token_Type::Var_Decl, prev_stack_top); } + else if (Keyword("attr")) { + retval = true; + + if (!Id(true)) { + throw Eval_Error("Incomplete attribute declaration", File_Position(line, col), filename); + } + if (!Symbol("::", false)) { + throw Eval_Error("Incomplete attribute declaration", File_Position(line, col), filename); + } + if (!Id(true)) { + throw Eval_Error("Missing attribute name in definition", File_Position(line, col), filename); + } + + + build_match(Token_Type::Attr_Decl, prev_stack_top); + } return retval; } diff --git a/unittests/object_attr.chai b/unittests/object_attr.chai new file mode 100644 index 0000000..e0215b6 --- /dev/null +++ b/unittests/object_attr.chai @@ -0,0 +1,5 @@ +attr bob::z +def bob::bob() { this.z = 10 } +var x = bob() +def bob::fred(x) { this.z - x } +print(x.fred(3)) diff --git a/unittests/object_attr.txt b/unittests/object_attr.txt new file mode 100644 index 0000000..7f8f011 --- /dev/null +++ b/unittests/object_attr.txt @@ -0,0 +1 @@ +7 diff --git a/unittests/object_constructor_guards.chai b/unittests/object_constructor_guards.chai new file mode 100644 index 0000000..2e2f55f --- /dev/null +++ b/unittests/object_constructor_guards.chai @@ -0,0 +1,5 @@ +def bob::bob(x) : x < 10 { print("constructed less than 10: " + x.to_string()) } +def bob::bob(x) { print("constructed any old: " + x.to_string()) } + +var b = bob(12) +var c = bob(3) diff --git a/unittests/object_constructor_guards.txt b/unittests/object_constructor_guards.txt new file mode 100644 index 0000000..130ba0c --- /dev/null +++ b/unittests/object_constructor_guards.txt @@ -0,0 +1,2 @@ +constructed any old: 12 +constructed less than 10: 3 diff --git a/unittests/object_method_guards.chai b/unittests/object_method_guards.chai new file mode 100644 index 0000000..206d8ce --- /dev/null +++ b/unittests/object_method_guards.chai @@ -0,0 +1,7 @@ +def bob::bob() { } +def bob::fred(e) : e < 10 { print("e less than 10") } +def bob::fred(e) { print("e is some value") } + +var b = bob() +b.fred(3) +b.fred(12) diff --git a/unittests/object_method_guards.txt b/unittests/object_method_guards.txt new file mode 100644 index 0000000..442c0be --- /dev/null +++ b/unittests/object_method_guards.txt @@ -0,0 +1,2 @@ +e less than 10 +e is some value