diff --git a/include/chaiscript/chaiscript.hpp b/include/chaiscript/chaiscript.hpp index 84ba0f9..eb9fe84 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, Method, Attr_Decl }; }; + Inline_Range, Annotation, Try, Catch, Finally, 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", "Method", "Attr_Decl"}; + "Inline_Range", "Annotation", "Try", "Catch", "Finally", "Method", "Attr_Decl"}; return token_types[tokentype]; } diff --git a/include/chaiscript/language/chaiscript_eval.hpp b/include/chaiscript/language/chaiscript_eval.hpp index 9121e4b..0f1af38 100644 --- a/include/chaiscript/language/chaiscript_eval.hpp +++ b/include/chaiscript/language/chaiscript_eval.hpp @@ -609,12 +609,20 @@ namespace chaiscript retval = eval_token(ss, node->children[0]); } catch (const Eval_Error &) { + if (node->children.back()->identifier == Token_Type::Finally) { + eval_token(ss, node->children.back()->children[0]); + } ss.pop_scope(); throw; } catch (const std::exception &e) { Boxed_Value except = Boxed_Value(boost::ref(e)); - for (unsigned int i = 1; i < node->children.size(); ++i) { + + unsigned int end_point = node->children.size(); + if (node->children.back()->identifier == Token_Type::Finally) { + end_point = node->children.size() - 1; + } + for (unsigned int i = 1; i < end_point; ++i) { TokenPtr catch_block = node->children[i]; if (catch_block->children.size() == 1) { @@ -636,6 +644,9 @@ namespace chaiscript try { guard = boxed_cast(eval_token(ss, catch_block->children[1])); } catch (const bad_boxed_cast &) { + if (node->children.back()->identifier == Token_Type::Finally) { + eval_token(ss, node->children.back()->children[0]); + } ss.pop_scope(); throw Eval_Error("Guard condition not boolean", catch_block->children[1]); } @@ -645,6 +656,9 @@ namespace chaiscript } } else { + if (node->children.back()->identifier == Token_Type::Finally) { + eval_token(ss, node->children.back()->children[0]); + } ss.pop_scope(); throw Eval_Error("Internal error: catch block size unrecognized", catch_block); } @@ -674,6 +688,9 @@ namespace chaiscript try { guard = boxed_cast(eval_token(ss, catch_block->children[1])); } catch (const bad_boxed_cast &) { + if (node->children.back()->identifier == Token_Type::Finally) { + eval_token(ss, node->children.back()->children[0]); + } ss.pop_scope(); throw Eval_Error("Guard condition not boolean", catch_block->children[1]); } @@ -683,15 +700,24 @@ namespace chaiscript } } else { + if (node->children.back()->identifier == Token_Type::Finally) { + eval_token(ss, node->children.back()->children[0]); + } ss.pop_scope(); throw Eval_Error("Internal error: catch block size unrecognized", catch_block); } } } catch (...) { + if (node->children.back()->identifier == Token_Type::Finally) { + eval_token(ss, node->children.back()->children[0]); + } ss.pop_scope(); throw; } + if (node->children.back()->identifier == Token_Type::Finally) { + retval = eval_token(ss, node->children.back()->children[0]); + } ss.pop_scope(); diff --git a/include/chaiscript/language/chaiscript_parser.hpp b/include/chaiscript/language/chaiscript_parser.hpp index 52c60f6..2afeea2 100644 --- a/include/chaiscript/language/chaiscript_parser.hpp +++ b/include/chaiscript/language/chaiscript_parser.hpp @@ -983,6 +983,17 @@ namespace chaiscript has_matches = true; } } + while (Eol()); + if (Keyword("finally", false)) { + int finally_stack_top = match_stack.size(); + + while (Eol()); + + if (!Block()) { + throw Eval_Error("Incomplete 'finally' block", File_Position(line, col), filename); + } + build_match(Token_Type::Finally, finally_stack_top); + } build_match(Token_Type::Try, prev_stack_top); } diff --git a/unittests/exception_finally.chai b/unittests/exception_finally.chai new file mode 100644 index 0000000..85ac3d8 --- /dev/null +++ b/unittests/exception_finally.chai @@ -0,0 +1,19 @@ +try { + throw(3) +} +catch(x) { + print(x) +} +finally { + print("Finally #1") +} + +try { + print("Safe") +} +catch { + print("Caught") +} +finally { + print("Finally #2") +} diff --git a/unittests/exception_finally.txt b/unittests/exception_finally.txt new file mode 100644 index 0000000..8c5edad --- /dev/null +++ b/unittests/exception_finally.txt @@ -0,0 +1,4 @@ +3 +Finally #1 +Safe +Finally #2