#include "jinja-lexer.h" #include "jinja-vm.h" #include "jinja-parser.h" #include #include #include #include namespace jinja { // Helper to check type without asserting (useful for logic) template static bool is_type(const value & ptr) { return dynamic_cast(ptr.get()) != nullptr; } struct vm { context & ctx; explicit vm(context & ctx) : ctx(ctx) {} void execute(program & prog) { for (auto & stmt : prog.body) { stmt->execute(ctx); } } }; value binary_expression::execute(context & ctx) { value left_val = left->execute(ctx); // Logical operators if (op.value == "and") { return left_val->as_bool() ? right->execute(ctx) : std::move(left_val); } else if (op.value == "or") { return left_val->as_bool() ? std::move(left_val) : right->execute(ctx); } // Equality operators value right_val = right->execute(ctx); if (op.value == "==") { return std::make_unique(left_val == right_val); } else if (op.value == "!=") { return std::make_unique(left_val != right_val); } // Handle undefined and null values if (is_type(left_val) || is_type(right_val)) { if (is_type(right_val) && (op.value == "in" || op.value == "not in")) { // Special case: `anything in undefined` is `false` and `anything not in undefined` is `true` return std::make_unique(op.value == "not in"); } throw std::runtime_error("Cannot perform operation " + op.value + " on undefined values"); } else if (is_type(left_val) || is_type(right_val)) { throw std::runtime_error("Cannot perform operation on null values"); } // String concatenation with ~ if (op.value == "~") { return std::make_unique(left_val->as_string() + right_val->as_string()); } // Float operations if ((is_type(left_val) || is_type(left_val)) && (is_type(right_val) || is_type(right_val))) { double a = left_val->as_float(); double b = right_val->as_float(); if (op.value == "+" || op.value == "-" || op.value == "*") { double res = (op.value == "+") ? a + b : (op.value == "-") ? a - b : a * b; bool is_float = is_type(left_val) || is_type(right_val); if (is_float) { return std::make_unique(res); } else { return std::make_unique(static_cast(res)); } } else if (op.value == "/") { return std::make_unique(a / b); } else if (op.value == "%") { double rem = std::fmod(a, b); bool is_float = is_type(left_val) || is_type(right_val); if (is_float) { return std::make_unique(rem); } else { return std::make_unique(static_cast(rem)); } } else if (op.value == "<") { return std::make_unique(a < b); } else if (op.value == ">") { return std::make_unique(a > b); } else if (op.value == ">=") { return std::make_unique(a >= b); } else if (op.value == "<=") { return std::make_unique(a <= b); } } // Array operations if (is_type(left_val) && is_type(right_val)) { if (op.value == "+") { auto& left_arr = left_val->as_array(); auto& right_arr = right_val->as_array(); std::vector result = left_arr; for (auto & v : right_arr) { result.push_back(std::move(v)); } return std::make_unique(result); } } else if (is_type(right_val)) { auto & arr = right_val->as_array(); bool member = std::find_if(arr.begin(), arr.end(), [&](const value& v) { return v == left_val; }) != arr.end(); if (op.value == "in") { return std::make_unique(member); } else if (op.value == "not in") { return std::make_unique(!member); } } // String concatenation if (is_type(left_val) || is_type(right_val)) { if (op.value == "+") { return std::make_unique(left_val->as_string() + right_val->as_string()); } } // String membership if (is_type(left_val) && is_type(right_val)) { auto left_str = left_val->as_string(); auto right_str = right_val->as_string(); if (op.value == "in") { return std::make_unique(right_str.find(left_str) != std::string::npos); } else if (op.value == "not in") { return std::make_unique(right_str.find(left_str) == std::string::npos); } } // String in object if (is_type(left_val) && is_type(right_val)) { auto key = left_val->as_string(); auto & obj = right_val->as_object(); bool has_key = obj.find(key) != obj.end(); if (op.value == "in") { return std::make_unique(has_key); } else if (op.value == "not in") { return std::make_unique(!has_key); } } throw std::runtime_error("Unknown operator \"" + op.value + "\" between " + left_val->type() + " and " + right_val->type()); } } // namespace jinja