diff --git a/include/minja/minja.hpp b/include/minja/minja.hpp index 5ed0556..72098d0 100644 --- a/include/minja/minja.hpp +++ b/include/minja/minja.hpp @@ -499,13 +499,17 @@ class Value : public std::enable_shared_from_this { return get() * rhs.get(); } Value operator/(const Value& rhs) const { - if (is_number_integer() && rhs.is_number_integer()) - return get() / rhs.get(); - else + if (is_number_integer() && rhs.is_number_integer()) { + auto d = rhs.get(); + if (d == 0) throw std::runtime_error("integer division by zero"); + return get() / d; + } else return get() / rhs.get(); } Value operator%(const Value& rhs) const { - return get() % rhs.get(); + auto d = rhs.get(); + if (d == 0) throw std::runtime_error("modulo by zero"); + return get() % d; } }; @@ -1371,8 +1375,8 @@ class BinaryOpExpr : public Expression { case Op::Mul: return l * r; case Op::Div: return l / r; case Op::MulMul: return std::pow(l.get(), r.get()); - case Op::DivDiv: return l.get() / r.get(); - case Op::Mod: return l.get() % r.get(); + case Op::DivDiv: { auto d = r.get(); if (d == 0) throw std::runtime_error("integer division by zero"); return l.get() / d; } + case Op::Mod: { auto d = r.get(); if (d == 0) throw std::runtime_error("modulo by zero"); return l.get() % d; } case Op::Eq: return l == r; case Op::Ne: return l != r; case Op::Lt: return l < r; @@ -1705,6 +1709,7 @@ class Parser { std::shared_ptr template_str; CharIterator start, end, it; Options options; + int expr_depth = 0; Parser(const std::shared_ptr& template_str, const Options & options) : template_str(template_str), options(options) { if (!template_str) throw std::runtime_error("Template string is null"); @@ -1882,6 +1887,13 @@ class Parser { } std::shared_ptr parseExpression(bool allow_if_expr = true) { + struct DepthGuard { + int & d; + explicit DepthGuard(int & d) : d(d) { + if (++d > 1000) throw std::runtime_error("expression nesting too deep"); + } + ~DepthGuard() { --d; } + } depth_guard(expr_depth); auto left = parseLogicalOr(); if (it == end) return left;