diff --git a/include/chaiscript/chaiscript.hpp b/include/chaiscript/chaiscript.hpp index 28e4a93..4ddedd9 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 }; }; + Inline_Range, Annotation, Try, Catch }; }; /** * 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"}; + "Inline_Range", "Annotation", "Try", "Catch"}; return token_types[tokentype]; } diff --git a/include/chaiscript/language/chaiscript_eval.hpp b/include/chaiscript/language/chaiscript_eval.hpp index 9cdc29e..c7fbbda 100644 --- a/include/chaiscript/language/chaiscript_eval.hpp +++ b/include/chaiscript/language/chaiscript_eval.hpp @@ -586,19 +586,25 @@ namespace chaiscript Boxed_Value eval_try(Eval_System &ss, const TokenPtr &node) { Boxed_Value retval; retval = Boxed_Value(); + Boxed_Value except; + bool succeeded = false; ss.new_scope(); try { retval = eval_token(ss, node->children[0]); + succeeded = true; } catch (const Eval_Error &) { + ss.pop_scope(); throw; } catch (const std::exception &e) { + except = Boxed_Value(boost::ref(e)); + /* if (node->children.size() > 2) { if (node->children[1]->text == "catch") { if (node->children.size() > 3) { - ss.add_object(node->children[2]->text, Boxed_Value(boost::ref(e))); + ss.add_object(node->children[2]->text, ); retval = eval_token(ss, node->children[3]); } else { @@ -606,8 +612,11 @@ namespace chaiscript } } } + */ } catch (Boxed_Value &bv) { + except = bv; + /* if (node->children.size() > 2) { if (node->children[1]->text == "catch") { if (node->children.size() > 3) { @@ -619,7 +628,52 @@ namespace chaiscript } } } + */ } + catch (...) { + ss.pop_scope(); + throw; + } + + if (!succeeded) { + + for (unsigned int i = 1; i < node->children.size(); ++i) { + TokenPtr catch_block = node->children[i]; + + if (catch_block->children.size() == 1) { + //No variable capture, no guards + retval = eval_token(ss, catch_block->children[0]); + break; + } + else if (catch_block->children.size() == 2) { + //Variable capture, no guards + ss.add_object(catch_block->children[0]->text, except); + retval = eval_token(ss, catch_block->children[1]); + break; + } + else if (catch_block->children.size() == 3) { + //Variable capture, no guards + ss.add_object(catch_block->children[0]->text, except); + + bool guard; + try { + guard = boxed_cast(eval_token(ss, catch_block->children[1])); + } catch (const bad_boxed_cast &) { + ss.pop_scope(); + throw Eval_Error("Guard condition not boolean", catch_block->children[1]); + } + if (guard) { + retval = eval_token(ss, catch_block->children[2]); + break; + } + } + else { + ss.pop_scope(); + throw Eval_Error("Internal error: catch block size unrecognized", catch_block); + } + } + } + ss.pop_scope(); return retval; diff --git a/include/chaiscript/language/chaiscript_parser.hpp b/include/chaiscript/language/chaiscript_parser.hpp index dcbb5f0..bf842ef 100644 --- a/include/chaiscript/language/chaiscript_parser.hpp +++ b/include/chaiscript/language/chaiscript_parser.hpp @@ -947,11 +947,17 @@ namespace chaiscript while (has_matches) { while (Eol()); has_matches = false; - if (Keyword("catch", true)) { + if (Keyword("catch", false)) { + int catch_stack_top = match_stack.size(); if (Char('(')) { if (!(Id(true) && Char(')'))) { throw Eval_Error("Incomplete 'catch' expression", File_Position(line, col), filename); } + if (Char(':')) { + if (!Expression()) { + throw Eval_Error("Missing guard expression for catch", File_Position(line, col), filename); + } + } } while (Eol()); @@ -959,7 +965,7 @@ namespace chaiscript if (!Block()) { throw Eval_Error("Incomplete 'catch' block", File_Position(line, col), filename); } - + build_match(Token_Type::Catch, catch_stack_top); has_matches = true; } } diff --git a/unittests/exception_guards.chai b/unittests/exception_guards.chai new file mode 100644 index 0000000..640dbdf --- /dev/null +++ b/unittests/exception_guards.chai @@ -0,0 +1,28 @@ +for (var i = 2; i < 6; ++i) { + try { + throw(i) + } + catch(e) : e < 2 { + print("Catch 1: " + e.to_string()) + } + catch(e) : e < 4 { + print("Catch 2: " + e.to_string()) + } + catch(e) { + print("Catch 3: " + e.to_string()) + } + catch { + print("This is never called") + } +} + +try { + throw(3) +} +catch(e) : e < 3 +{ + print("Caught less than 3") +} +catch { + print("Backup catch") +} diff --git a/unittests/exception_guards.txt b/unittests/exception_guards.txt new file mode 100644 index 0000000..fcf6673 --- /dev/null +++ b/unittests/exception_guards.txt @@ -0,0 +1,5 @@ +Catch 2: 2 +Catch 2: 3 +Catch 3: 4 +Catch 3: 5 +Backup catch