binary_expression::execute
This commit is contained in:
parent
8cea1ed6b0
commit
7ad6eb39ca
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -0,0 +1,98 @@
|
|||
#pragma once
|
||||
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <map>
|
||||
|
||||
|
||||
namespace jinja {
|
||||
|
||||
struct value_t;
|
||||
using value = std::unique_ptr<value_t>;
|
||||
|
||||
struct value_t {
|
||||
int64_t val_int;
|
||||
double val_flt;
|
||||
std::string val_str;
|
||||
bool val_bool;
|
||||
std::vector<value> val_arr;
|
||||
std::map<std::string, value> 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<value> & as_array() const { throw std::runtime_error("Not an array value"); }
|
||||
virtual const std::map<std::string, value> & 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<double>(val_int); }
|
||||
};
|
||||
using value_int = std::unique_ptr<value_int_t>;
|
||||
|
||||
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<int64_t>(val_flt); }
|
||||
};
|
||||
using value_float = std::unique_ptr<value_float_t>;
|
||||
|
||||
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<value_string_t>;
|
||||
|
||||
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<value_bool_t>;
|
||||
|
||||
struct value_array_t : public value_t {
|
||||
value_array_t(const std::vector<value> && v) { val_arr = std::move(v); }
|
||||
virtual std::string type() const override { return "Array"; }
|
||||
virtual const std::vector<value> & as_array() const override { return val_arr; }
|
||||
};
|
||||
using value_array = std::unique_ptr<value_array_t>;
|
||||
|
||||
struct value_object_t : public value_t {
|
||||
value_object_t(const std::map<std::string, value> & v) { val_obj = v; }
|
||||
virtual std::string type() const override { return "Object"; }
|
||||
virtual const std::map<std::string, value> & as_object() const override { return val_obj; }
|
||||
};
|
||||
using value_object = std::unique_ptr<value_object_t>;
|
||||
|
||||
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<value_null_t>;
|
||||
|
||||
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<value_undefined_t>;
|
||||
|
||||
} // namespace jinja
|
||||
|
|
@ -0,0 +1,151 @@
|
|||
#include "jinja-lexer.h"
|
||||
#include "jinja-vm.h"
|
||||
#include "jinja-parser.h"
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <memory>
|
||||
#include <algorithm>
|
||||
|
||||
namespace jinja {
|
||||
|
||||
// Helper to check type without asserting (useful for logic)
|
||||
template<typename T>
|
||||
static bool is_type(const value & ptr) {
|
||||
return dynamic_cast<const T*>(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<value_bool_t>(left_val == right_val);
|
||||
} else if (op.value == "!=") {
|
||||
return std::make_unique<value_bool_t>(left_val != right_val);
|
||||
}
|
||||
|
||||
// Handle undefined and null values
|
||||
if (is_type<value_undefined>(left_val) || is_type<value_undefined>(right_val)) {
|
||||
if (is_type<value_undefined>(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<value_bool_t>(op.value == "not in");
|
||||
}
|
||||
throw std::runtime_error("Cannot perform operation " + op.value + " on undefined values");
|
||||
} else if (is_type<value_null>(left_val) || is_type<value_null>(right_val)) {
|
||||
throw std::runtime_error("Cannot perform operation on null values");
|
||||
}
|
||||
|
||||
// String concatenation with ~
|
||||
if (op.value == "~") {
|
||||
return std::make_unique<value_string_t>(left_val->as_string() + right_val->as_string());
|
||||
}
|
||||
|
||||
// Float operations
|
||||
if ((is_type<value_int>(left_val) || is_type<value_float>(left_val)) &&
|
||||
(is_type<value_int>(right_val) || is_type<value_float>(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<value_float>(left_val) || is_type<value_float>(right_val);
|
||||
if (is_float) {
|
||||
return std::make_unique<value_float_t>(res);
|
||||
} else {
|
||||
return std::make_unique<value_int_t>(static_cast<int64_t>(res));
|
||||
}
|
||||
} else if (op.value == "/") {
|
||||
return std::make_unique<value_float_t>(a / b);
|
||||
} else if (op.value == "%") {
|
||||
double rem = std::fmod(a, b);
|
||||
bool is_float = is_type<value_float>(left_val) || is_type<value_float>(right_val);
|
||||
if (is_float) {
|
||||
return std::make_unique<value_float_t>(rem);
|
||||
} else {
|
||||
return std::make_unique<value_int_t>(static_cast<int64_t>(rem));
|
||||
}
|
||||
} else if (op.value == "<") {
|
||||
return std::make_unique<value_bool_t>(a < b);
|
||||
} else if (op.value == ">") {
|
||||
return std::make_unique<value_bool_t>(a > b);
|
||||
} else if (op.value == ">=") {
|
||||
return std::make_unique<value_bool_t>(a >= b);
|
||||
} else if (op.value == "<=") {
|
||||
return std::make_unique<value_bool_t>(a <= b);
|
||||
}
|
||||
}
|
||||
|
||||
// Array operations
|
||||
if (is_type<value_array>(left_val) && is_type<value_array>(right_val)) {
|
||||
if (op.value == "+") {
|
||||
auto& left_arr = left_val->as_array();
|
||||
auto& right_arr = right_val->as_array();
|
||||
std::vector<value> result = left_arr;
|
||||
for (auto & v : right_arr) {
|
||||
result.push_back(std::move(v));
|
||||
}
|
||||
return std::make_unique<value_array_t>(result);
|
||||
}
|
||||
} else if (is_type<value_array>(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<value_bool_t>(member);
|
||||
} else if (op.value == "not in") {
|
||||
return std::make_unique<value_bool_t>(!member);
|
||||
}
|
||||
}
|
||||
|
||||
// String concatenation
|
||||
if (is_type<value_string>(left_val) || is_type<value_string>(right_val)) {
|
||||
if (op.value == "+") {
|
||||
return std::make_unique<value_string_t>(left_val->as_string() + right_val->as_string());
|
||||
}
|
||||
}
|
||||
|
||||
// String membership
|
||||
if (is_type<value_string>(left_val) && is_type<value_string>(right_val)) {
|
||||
auto left_str = left_val->as_string();
|
||||
auto right_str = right_val->as_string();
|
||||
if (op.value == "in") {
|
||||
return std::make_unique<value_bool_t>(right_str.find(left_str) != std::string::npos);
|
||||
} else if (op.value == "not in") {
|
||||
return std::make_unique<value_bool_t>(right_str.find(left_str) == std::string::npos);
|
||||
}
|
||||
}
|
||||
|
||||
// String in object
|
||||
if (is_type<value_string>(left_val) && is_type<value_object>(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<value_bool_t>(has_key);
|
||||
} else if (op.value == "not in") {
|
||||
return std::make_unique<value_bool_t>(!has_key);
|
||||
}
|
||||
}
|
||||
|
||||
throw std::runtime_error("Unknown operator \"" + op.value + "\" between " + left_val->type() + " and " + right_val->type());
|
||||
}
|
||||
|
||||
} // namespace jinja
|
||||
|
|
@ -1,16 +1,20 @@
|
|||
#pragma once
|
||||
|
||||
#include "jinja-lexer.h"
|
||||
#include "jinja-value.h"
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <cassert>
|
||||
#include <memory>
|
||||
#include <sstream>
|
||||
|
||||
|
||||
namespace jinja {
|
||||
|
||||
struct context {
|
||||
// TODO
|
||||
std::ostringstream out;
|
||||
std::map<std::string, value> 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<statement>;
|
||||
|
|
@ -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<expression>(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<identifier, call_expression>(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<expression>(arg);
|
||||
}
|
||||
std::string type() const override { return "CallStatement"; }
|
||||
void execute(context & ctx) override {}
|
||||
value execute(context & ctx) override {}
|
||||
};
|
||||
|
||||
struct ternary_expression : public expression {
|
||||
|
|
|
|||
Loading…
Reference in New Issue