diff --git a/doc/19-language-reference.md b/doc/19-language-reference.md index c0d843b82..78379fe9a 100644 --- a/doc/19-language-reference.md +++ b/doc/19-language-reference.md @@ -760,6 +760,17 @@ The `continue` and `break` keywords can be used to control how the loop is execu skips over the remaining expressions for the loop body and begins the next loop evaluation. The `break` keyword breaks out of the loop. +## Exceptions + +Built-in commands may throw exceptions to signal errors such as invalid arguments. User scripts can throw exceptions +using the `throw` keyword. + +Example: + + throw "An error occurred." + +There is currently no way for scripts to catch exceptions. + ## Types All values have a static type. The `typeof` function can be used to determine the type of a value: diff --git a/lib/config/config_lexer.ll b/lib/config/config_lexer.ll index 25310d650..abe0a34c6 100644 --- a/lib/config/config_lexer.ll +++ b/lib/config/config_lexer.ll @@ -208,6 +208,7 @@ for return T_FOR; if return T_IF; else return T_ELSE; while return T_WHILE; +throw return T_THROW; =\> return T_FOLLOWS; \<\< return T_SHIFT_LEFT; \>\> return T_SHIFT_RIGHT; diff --git a/lib/config/config_parser.yy b/lib/config/config_parser.yy index 80e06ec8e..fd4e1995a 100644 --- a/lib/config/config_parser.yy +++ b/lib/config/config_parser.yy @@ -181,6 +181,7 @@ static void MakeRBinaryOp(Expression** result, Expression *left, Expression *rig %token T_IF "if (T_IF)" %token T_ELSE "else (T_ELSE)" %token T_WHILE "while (T_WHILE)" +%token T_THROW "throw (T_THROW)" %token T_FOLLOWS "=> (T_FOLLOWS)" %token T_NULLARY_LAMBDA_BEGIN "{{ (T_NULLARY_LAMBDA_BEGIN)" %token T_NULLARY_LAMBDA_END "}} (T_NULLARY_LAMBDA_END)" @@ -680,6 +681,10 @@ lterm: type $$ = new WhileExpression($3, $5, @$); } + | T_THROW rterm + { + $$ = new ThrowExpression($2, @$); + } | rterm_side_effect ; diff --git a/lib/config/configcompiler.cpp b/lib/config/configcompiler.cpp index 4eb5766ed..6d7edccf2 100644 --- a/lib/config/configcompiler.cpp +++ b/lib/config/configcompiler.cpp @@ -50,7 +50,6 @@ ConfigCompiler::ConfigCompiler(const String& path, std::istream *input, const St ConfigCompiler::~ConfigCompiler(void) { DestroyScanner(); - delete m_Input; } /** @@ -174,17 +173,6 @@ void ConfigCompiler::HandleLibrary(const String& library) Utility::LoadExtensionLibrary(library); } -void ConfigCompiler::CompileHelper(void) -{ - try { - m_Promise.set_value(boost::shared_ptr(Compile())); - } catch (...) { - m_Promise.set_exception(boost::current_exception()); - } - - delete this; -} - /** * Compiles a stream. * @@ -198,12 +186,15 @@ Expression *ConfigCompiler::CompileStream(const String& path, std::istream *stre stream->exceptions(std::istream::badbit); - ConfigCompiler* ctx = new ConfigCompiler(path, stream, zone); + ConfigCompiler ctx(path, stream, zone); - boost::shared_future > ftr = boost::shared_future >(ctx->m_Promise.get_future()); - - Utility::QueueAsyncCallback(boost::bind(&ConfigCompiler::CompileHelper, ctx)); - return new FutureExpression(ftr); + try { + return ctx.Compile(); + } catch (const ScriptError& ex) { + return new ThrowExpression(MakeLiteral(ex.what()), ex.GetDebugInfo()); + } catch (const std::exception& ex) { + return new ThrowExpression(MakeLiteral(DiagnosticInformation(ex))); + } } /** @@ -216,10 +207,9 @@ Expression *ConfigCompiler::CompileFile(const String& path, const String& zone) { CONTEXT("Compiling configuration file '" + path + "'"); - std::ifstream *stream = new std::ifstream(); - stream->open(path.CStr(), std::ifstream::in); + std::ifstream stream(path.CStr(), std::ifstream::in); - if (!*stream) + if (!stream) BOOST_THROW_EXCEPTION(posix_error() << boost::errinfo_api_function("std::ifstream::open") << boost::errinfo_errno(errno) @@ -228,7 +218,7 @@ Expression *ConfigCompiler::CompileFile(const String& path, const String& zone) Log(LogInformation, "ConfigCompiler") << "Compiling config file: " << path; - return CompileStream(path, stream, zone); + return CompileStream(path, &stream, zone); } /** @@ -240,8 +230,8 @@ Expression *ConfigCompiler::CompileFile(const String& path, const String& zone) */ Expression *ConfigCompiler::CompileText(const String& path, const String& text, const String& zone) { - std::stringstream *stream = new std::stringstream(text); - return CompileStream(path, stream, zone); + std::stringstream stream(text); + return CompileStream(path, &stream, zone); } /** diff --git a/lib/config/configcompiler.hpp b/lib/config/configcompiler.hpp index e3b692c23..aa438473e 100644 --- a/lib/config/configcompiler.hpp +++ b/lib/config/configcompiler.hpp @@ -114,7 +114,7 @@ private: void InitializeScanner(void); void DestroyScanner(void); - void CompileHelper(void); + void HandleIncludeZone(const String& tag, const String& path, const String& pattern, std::vector& expressions); public: bool m_Eof; diff --git a/lib/config/expression.cpp b/lib/config/expression.cpp index 3b758dc49..171078289 100644 --- a/lib/config/expression.cpp +++ b/lib/config/expression.cpp @@ -674,6 +674,14 @@ void icinga::BindToScope(Expression *& expr, ScopeSpecifier scopeSpec) } } +ExpressionResult ThrowExpression::DoEvaluate(ScriptFrame& frame, DebugHint *dhint) const +{ + ExpressionResult messageres = m_Message->Evaluate(frame); + CHECK_RESULT(messageres); + Value message = messageres.GetValue(); + BOOST_THROW_EXCEPTION(ScriptError(message, m_DebugInfo)); +} + ExpressionResult ImportExpression::DoEvaluate(ScriptFrame& frame, DebugHint *dhint) const { String type = VMOps::GetField(frame.Self, "type", m_DebugInfo); diff --git a/lib/config/expression.hpp b/lib/config/expression.hpp index 6f7dbfe53..ff0cbb586 100644 --- a/lib/config/expression.hpp +++ b/lib/config/expression.hpp @@ -225,28 +225,6 @@ private: boost::shared_ptr m_Expression; }; -class I2_CONFIG_API FutureExpression : public Expression -{ -public: - FutureExpression(const boost::shared_future >& future) - : m_Future(future) - { } - -protected: - virtual ExpressionResult DoEvaluate(ScriptFrame& frame, DebugHint *dhint) const - { - return m_Future.get()->DoEvaluate(frame, dhint); - } - - virtual const DebugInfo& GetDebugInfo(void) const - { - return m_Future.get()->GetDebugInfo(); - } - -private: - mutable boost::shared_future > m_Future; -}; - class I2_CONFIG_API LiteralExpression : public Expression { public: @@ -768,6 +746,25 @@ protected: I2_CONFIG_API void BindToScope(Expression *& expr, ScopeSpecifier scopeSpec); +class I2_CONFIG_API ThrowExpression : public DebuggableExpression +{ +public: + ThrowExpression(Expression *message, const DebugInfo& debugInfo = DebugInfo()) + : DebuggableExpression(debugInfo), m_Message(message) + { } + + ~ThrowExpression(void) + { + delete m_Message; + } + +protected: + virtual ExpressionResult DoEvaluate(ScriptFrame& frame, DebugHint *dhint) const; + +private: + Expression *m_Message; +}; + class I2_CONFIG_API ImportExpression : public DebuggableExpression { public: