From 7ad6eb39caf2ba75fd585f317a255ebc9ca47080 Mon Sep 17 00:00:00 2001 From: Xuan Son Nguyen Date: Sat, 27 Dec 2025 16:00:07 +0100 Subject: [PATCH] binary_expression::execute --- common/jinja/jinja-parser.cpp | 2 +- common/jinja/jinja-value.h | 98 ++++++++++++++++++++++ common/jinja/jinja-vm.cpp | 151 ++++++++++++++++++++++++++++++++++ common/jinja/jinja-vm.h | 30 ++++--- 4 files changed, 267 insertions(+), 14 deletions(-) create mode 100644 common/jinja/jinja-value.h diff --git a/common/jinja/jinja-parser.cpp b/common/jinja/jinja-parser.cpp index 5b20f010dc..de61023560 100644 --- a/common/jinja/jinja-parser.cpp +++ b/common/jinja/jinja-parser.cpp @@ -474,7 +474,7 @@ private: while (!is(token::close_square_bracket)) { if (is(token::colon)) { // A case where a default is used - // e.g., [:2] will be parsed as [undefined, 2] + // e.g., [:2] will be parsed as [undefined, 2] slices.push_back(nullptr); ++current; // consume colon is_slice = true; diff --git a/common/jinja/jinja-value.h b/common/jinja/jinja-value.h new file mode 100644 index 0000000000..b06f465a1d --- /dev/null +++ b/common/jinja/jinja-value.h @@ -0,0 +1,98 @@ +#pragma once + +#include +#include +#include + + +namespace jinja { + +struct value_t; +using value = std::unique_ptr; + +struct value_t { + int64_t val_int; + double val_flt; + std::string val_str; + bool val_bool; + std::vector val_arr; + std::map val_obj; + + virtual std::string type() const { return ""; } + + virtual ~value_t() = default; + virtual int64_t as_int() const { throw std::runtime_error("Not an int value"); } + virtual double as_float() const { throw std::runtime_error("Not a float value"); } + virtual std::string as_string() const { throw std::runtime_error("Not a string value"); } + virtual bool as_bool() const { throw std::runtime_error("Not a bool value"); } + virtual const std::vector & as_array() const { throw std::runtime_error("Not an array value"); } + virtual const std::map & as_object() const { throw std::runtime_error("Not an object value"); } + virtual bool is_null() const { return false; } + virtual bool is_undefined() const { return false; } + + virtual bool operator==(const value & other) const { + // TODO + return false; + } + virtual bool operator!=(const value & other) const { + return !(*this == other); + } +}; + +struct value_int_t : public value_t { + value_int_t(int64_t v) { val_int = v; } + virtual std::string type() const override { return "Integer"; } + virtual int64_t as_int() const override { return val_int; } + virtual double as_float() const override { return static_cast(val_int); } +}; +using value_int = std::unique_ptr; + +struct value_float_t : public value_t { + value_float_t(double v) { val_flt = v; } + virtual std::string type() const override { return "Float"; } + virtual double as_float() const override { return val_flt; } + virtual int64_t as_int() const override { return static_cast(val_flt); } +}; +using value_float = std::unique_ptr; + +struct value_string_t : public value_t { + value_string_t(const std::string & v) { val_str = v; } + virtual std::string type() const override { return "String"; } + virtual std::string as_string() const override { return val_str; } +}; +using value_string = std::unique_ptr; + +struct value_bool_t : public value_t { + value_bool_t(bool v) { val_bool = v; } + virtual std::string type() const override { return "Boolean"; } + virtual bool as_bool() const override { return val_bool; } +}; +using value_bool = std::unique_ptr; + +struct value_array_t : public value_t { + value_array_t(const std::vector && v) { val_arr = std::move(v); } + virtual std::string type() const override { return "Array"; } + virtual const std::vector & as_array() const override { return val_arr; } +}; +using value_array = std::unique_ptr; + +struct value_object_t : public value_t { + value_object_t(const std::map & v) { val_obj = v; } + virtual std::string type() const override { return "Object"; } + virtual const std::map & as_object() const override { return val_obj; } +}; +using value_object = std::unique_ptr; + +struct value_null_t : public value_t { + virtual std::string type() const override { return "Null"; } + virtual bool is_null() const override { return true; } +}; +using value_null = std::unique_ptr; + +struct value_undefined_t : public value_t { + virtual std::string type() const override { return "Undefined"; } + virtual bool is_undefined() const override { return true; } +}; +using value_undefined = std::unique_ptr; + +} // namespace jinja diff --git a/common/jinja/jinja-vm.cpp b/common/jinja/jinja-vm.cpp index e69de29bb2..1c3ec49013 100644 --- a/common/jinja/jinja-vm.cpp +++ b/common/jinja/jinja-vm.cpp @@ -0,0 +1,151 @@ +#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 diff --git a/common/jinja/jinja-vm.h b/common/jinja/jinja-vm.h index b848ec4d9b..a77f21cdfa 100644 --- a/common/jinja/jinja-vm.h +++ b/common/jinja/jinja-vm.h @@ -1,16 +1,20 @@ #pragma once + #include "jinja-lexer.h" +#include "jinja-value.h" #include #include #include #include +#include namespace jinja { struct context { - // TODO + std::ostringstream out; + std::map var; }; /** @@ -19,7 +23,7 @@ struct context { struct statement { virtual ~statement() = default; virtual std::string type() const { return "Statement"; } - virtual void execute(context & ctx) = 0; + virtual value execute(context & ctx) = 0; }; using statement_ptr = std::unique_ptr; @@ -46,7 +50,6 @@ static void chk_type(const statement_ptr & ptr) { */ struct expression : public statement { std::string type() const override { return "Expression"; } - void execute(context & ctx) override {} }; // Statements @@ -56,7 +59,7 @@ struct program : public statement { explicit program(statements && body) : body(std::move(body)) {} std::string type() const override { return "Program"; } - void execute(context & ctx) override {} + value execute(context & ctx) override {} }; struct if_statement : public statement { @@ -70,7 +73,7 @@ struct if_statement : public statement { } std::string type() const override { return "If"; } - void execute(context & ctx) override {} + value execute(context & ctx) override {} }; struct identifier; @@ -94,17 +97,17 @@ struct for_statement : public statement { } std::string type() const override { return "For"; } - void execute(context & ctx) override {} + value execute(context & ctx) override {} }; struct break_statement : public statement { std::string type() const override { return "Break"; } - void execute(context & ctx) override {} + value execute(context & ctx) override {} }; struct continue_statement : public statement { std::string type() const override { return "Continue"; } - void execute(context & ctx) override {} + value execute(context & ctx) override {} }; struct set_statement : public statement { @@ -119,7 +122,7 @@ struct set_statement : public statement { } std::string type() const override { return "Set"; } - void execute(context & ctx) override {} + value execute(context & ctx) override {} }; struct macro_statement : public statement { @@ -134,14 +137,14 @@ struct macro_statement : public statement { } std::string type() const override { return "Macro"; } - void execute(context & ctx) override {} + value execute(context & ctx) override {} }; struct comment_statement : public statement { std::string value; explicit comment_statement(const std::string & value) : value(value) {} std::string type() const override { return "Comment"; } - void execute(context & ctx) override {} + value execute(context & ctx) override {} }; // Expressions @@ -246,6 +249,7 @@ struct binary_expression : public expression { chk_type(this->right); } std::string type() const override { return "BinaryExpression"; } + value execute(context & ctx) override; }; /** @@ -273,7 +277,7 @@ struct filter_statement : public statement { chk_type(this->filter); } std::string type() const override { return "FilterStatement"; } - void execute(context & ctx) override {} + value execute(context & ctx) override {} }; /** @@ -369,7 +373,7 @@ struct call_statement : public statement { for (const auto& arg : this->caller_args) chk_type(arg); } std::string type() const override { return "CallStatement"; } - void execute(context & ctx) override {} + value execute(context & ctx) override {} }; struct ternary_expression : public expression {