use shared_ptr for values

This commit is contained in:
Xuan Son Nguyen 2025-12-28 17:46:25 +01:00
parent 4331e9c8e9
commit 7f17608ea4
5 changed files with 137 additions and 147 deletions

View File

@ -12,7 +12,7 @@
namespace jinja {
struct value_t;
using value = std::unique_ptr<value_t>;
using value = std::shared_ptr<value_t>;
// Helper to check the type of a value
@ -21,7 +21,7 @@ struct extract_pointee {
using type = T;
};
template<typename U>
struct extract_pointee<std::unique_ptr<U>> {
struct extract_pointee<std::shared_ptr<U>> {
using type = U;
};
template<typename T>
@ -35,9 +35,19 @@ bool is_val(const value_t * ptr) {
return dynamic_cast<const PointeeType*>(ptr) != nullptr;
}
template<typename T, typename... Args>
std::unique_ptr<typename extract_pointee<T>::type> mk_val(Args&&... args) {
std::shared_ptr<typename extract_pointee<T>::type> mk_val(Args&&... args) {
using PointeeType = typename extract_pointee<T>::type;
return std::make_unique<PointeeType>(std::forward<Args>(args)...);
return std::make_shared<PointeeType>(std::forward<Args>(args)...);
}
template<typename T>
const typename extract_pointee<T>::type * cast_val(const value & ptr) {
using PointeeType = typename extract_pointee<T>::type;
return dynamic_cast<const PointeeType*>(ptr.get());
}
template<typename T>
typename extract_pointee<T>::type * cast_val(value & ptr) {
using PointeeType = typename extract_pointee<T>::type;
return dynamic_cast<PointeeType*>(ptr.get());
}
template<typename T>
void ensure_val(const value & ptr) {
@ -91,8 +101,8 @@ struct value_t {
// my_arr = [my_obj]
// my_obj["a"] = 3
// print(my_arr[0]["a"]) # should print 3
std::shared_ptr<std::vector<value>> val_arr;
std::shared_ptr<std::map<std::string, value>> val_obj;
std::vector<value> val_arr;
std::map<std::string, value> val_obj;
func_handler val_func;
@ -116,10 +126,6 @@ struct value_t {
}
virtual std::string as_repr() const { return as_string().str(); }
virtual value clone() const {
return std::make_unique<value_t>(*this);
}
};
@ -129,10 +135,9 @@ struct value_int_t : public value_t {
virtual int64_t as_int() const override { return val_int; }
virtual double as_float() const override { return static_cast<double>(val_int); }
virtual string as_string() const override { return std::to_string(val_int); }
virtual value clone() const override { return std::make_unique<value_int_t>(*this); }
virtual const func_builtins & get_builtins() const override;
};
using value_int = std::unique_ptr<value_int_t>;
using value_int = std::shared_ptr<value_int_t>;
struct value_float_t : public value_t {
@ -141,10 +146,9 @@ struct value_float_t : public value_t {
virtual double as_float() const override { return val_flt; }
virtual int64_t as_int() const override { return static_cast<int64_t>(val_flt); }
virtual string as_string() const override { return std::to_string(val_flt); }
virtual value clone() const override { return std::make_unique<value_float_t>(*this); }
virtual const func_builtins & get_builtins() const override;
};
using value_float = std::unique_ptr<value_float_t>;
using value_float = std::shared_ptr<value_float_t>;
struct value_string_t : public value_t {
@ -160,13 +164,12 @@ struct value_string_t : public value_t {
}
return ss.str();
}
virtual value clone() const override { return std::make_unique<value_string_t>(*this); }
virtual const func_builtins & get_builtins() const override;
void mark_input() {
val_str.mark_input();
}
};
using value_string = std::unique_ptr<value_string_t>;
using value_string = std::shared_ptr<value_string_t>;
struct value_bool_t : public value_t {
@ -174,92 +177,68 @@ struct value_bool_t : public value_t {
virtual std::string type() const override { return "Boolean"; }
virtual bool as_bool() const override { return val_bool; }
virtual string as_string() const override { return std::string(val_bool ? "True" : "False"); }
virtual value clone() const override { return std::make_unique<value_bool_t>(*this); }
virtual const func_builtins & get_builtins() const override;
};
using value_bool = std::unique_ptr<value_bool_t>;
using value_bool = std::shared_ptr<value_bool_t>;
struct value_array_t : public value_t {
value_array_t() {
val_arr = std::make_shared<std::vector<value>>();
}
value_array_t() = default;
value_array_t(value & v) {
// point to the same underlying data
val_arr = v->val_arr;
}
void push_back(const value & val) {
val_arr->push_back(val->clone());
val_arr.push_back(val);
}
virtual std::string type() const override { return "Array"; }
virtual const std::vector<value> & as_array() const override { return *val_arr; }
// clone will also share the underlying data (point to the same vector)
virtual value clone() const override {
auto tmp = std::make_unique<value_array_t>();
tmp->val_arr = this->val_arr;
return tmp;
}
virtual const std::vector<value> & as_array() const override { return val_arr; }
virtual string as_string() const override {
std::ostringstream ss;
ss << "[";
for (size_t i = 0; i < val_arr->size(); i++) {
for (size_t i = 0; i < val_arr.size(); i++) {
if (i > 0) ss << ", ";
ss << val_arr->at(i)->as_repr();
ss << val_arr.at(i)->as_repr();
}
ss << "]";
return ss.str();
}
virtual bool as_bool() const override {
return !val_arr->empty();
return !val_arr.empty();
}
virtual const func_builtins & get_builtins() const override;
};
using value_array = std::unique_ptr<value_array_t>;
using value_array = std::shared_ptr<value_array_t>;
struct value_object_t : public value_t {
value_object_t() {
val_obj = std::make_shared<std::map<std::string, value>>();
}
value_object_t() = default;
value_object_t(value & v) {
// point to the same underlying data
val_obj = v->val_obj;
}
value_object_t(const std::map<std::string, value> & obj) {
val_obj = std::make_shared<std::map<std::string, value>>();
val_obj = std::map<std::string, value>();
for (const auto & pair : obj) {
(*val_obj)[pair.first] = pair.second->clone();
val_obj[pair.first] = pair.second;
}
}
void insert(const std::string & key, const value & val) {
(*val_obj)[key] = val->clone();
val_obj[key] = val;
}
virtual std::string type() const override { return "Object"; }
virtual const std::map<std::string, value> & as_object() const override { return *val_obj; }
// clone will also share the underlying data (point to the same map)
virtual value clone() const override {
auto tmp = std::make_unique<value_object_t>();
tmp->val_obj = this->val_obj;
return tmp;
}
virtual const std::map<std::string, value> & as_object() const override { return val_obj; }
virtual bool as_bool() const override {
return !val_obj->empty();
return !val_obj.empty();
}
virtual const func_builtins & get_builtins() const override;
};
using value_object = std::unique_ptr<value_object_t>;
using value_object = std::shared_ptr<value_object_t>;
struct value_func_t : public value_t {
std::string name; // for debugging
value arg0; // bound "this" argument, if any
value_func_t(const value_func_t & other) {
val_func = other.val_func;
name = other.name;
if (other.arg0) {
arg0 = other.arg0->clone();
}
}
value_func_t(const func_handler & func, std::string func_name = "") {
val_func = func;
name = func_name;
@ -267,14 +246,14 @@ struct value_func_t : public value_t {
value_func_t(const func_handler & func, const value & arg_this, std::string func_name = "") {
val_func = func;
name = func_name;
arg0 = arg_this->clone();
arg0 = arg_this;
}
virtual value invoke(const func_args & args) const override {
if (arg0) {
func_args new_args;
new_args.args.push_back(arg0->clone());
new_args.args.push_back(arg0);
for (const auto & a : args.args) {
new_args.args.push_back(a->clone());
new_args.args.push_back(a);
}
return val_func(new_args);
} else {
@ -283,9 +262,8 @@ struct value_func_t : public value_t {
}
virtual std::string type() const override { return "Function"; }
virtual std::string as_repr() const override { return type(); }
virtual value clone() const override { return std::make_unique<value_func_t>(*this); }
};
using value_func = std::unique_ptr<value_func_t>;
using value_func = std::shared_ptr<value_func_t>;
struct value_null_t : public value_t {
@ -293,9 +271,8 @@ struct value_null_t : public value_t {
virtual bool is_null() const override { return true; }
virtual bool as_bool() const override { return false; }
virtual std::string as_repr() const override { return type(); }
virtual value clone() const override { return std::make_unique<value_null_t>(*this); }
};
using value_null = std::unique_ptr<value_null_t>;
using value_null = std::shared_ptr<value_null_t>;
struct value_undefined_t : public value_t {
@ -303,24 +280,18 @@ struct value_undefined_t : public value_t {
virtual bool is_undefined() const override { return true; }
virtual bool as_bool() const override { return false; }
virtual std::string as_repr() const override { return type(); }
virtual value clone() const override { return std::make_unique<value_undefined_t>(*this); }
};
using value_undefined = std::unique_ptr<value_undefined_t>;
using value_undefined = std::shared_ptr<value_undefined_t>;
// special value for kwarg
struct value_kwarg_t : public value_t {
std::string key;
value val;
value_kwarg_t(const value_kwarg_t & other) {
key = other.key;
val = other.val->clone();
}
value_kwarg_t(const std::string & k, const value & v) : key(k), val(v->clone()) {}
value_kwarg_t(const std::string & k, const value & v) : key(k), val(v) {}
virtual std::string type() const override { return "KwArg"; }
virtual std::string as_repr() const override { return type(); }
virtual value clone() const override { return std::make_unique<value_kwarg_t>(*this); }
};
using value_kwarg = std::unique_ptr<value_kwarg_t>;
using value_kwarg = std::shared_ptr<value_kwarg_t>;
const func_builtins & global_builtins();

View File

@ -55,7 +55,7 @@ static T slice(const T & array, std::optional<int64_t> start = std::nullopt, std
}
for (int64_t i = start_val; direction * i < direction * stop_val; i += step) {
if (i >= 0 && i < len) {
result.push_back(std::move(array[static_cast<size_t>(i)]->clone()));
result.push_back(array[static_cast<size_t>(i)]);
}
}
return result;
@ -87,7 +87,7 @@ const func_builtins & global_builtins() {
if (!is_val<value_kwarg>(arg)) {
throw raised_exception("namespace() arguments must be kwargs");
}
auto kwarg = dynamic_cast<value_kwarg_t*>(arg.get());
auto kwarg = cast_val<value_kwarg>(arg);
out->insert(kwarg->key, kwarg->val);
}
return out;
@ -265,12 +265,12 @@ const func_builtins & value_string_t::get_builtins() const {
std::string token;
while ((pos = str.find(delim)) != std::string::npos) {
token = str.substr(0, pos);
result->val_arr->push_back(mk_val<value_string>(token));
result->push_back(mk_val<value_string>(token));
str.erase(0, pos + delim.length());
}
auto res = mk_val<value_string>(str);
res->val_str.mark_input_based_on(args.args[0]->val_str);
result->val_arr->push_back(std::move(res));
result->push_back(std::move(res));
return std::move(result);
}},
{"replace", [](const func_args & args) -> value {
@ -353,7 +353,7 @@ const func_builtins & value_array_t::get_builtins() const {
const auto & arr = args.args[0]->as_array();
auto result = mk_val<value_array>();
for (const auto& v : arr) {
result->val_arr->push_back(v->clone());
result->push_back(v);
}
return result;
}},
@ -363,7 +363,7 @@ const func_builtins & value_array_t::get_builtins() const {
if (arr.empty()) {
return mk_val<value_undefined>();
}
return arr[0]->clone();
return arr[0];
}},
{"last", [](const func_args & args) -> value {
args.ensure_vals<value_array>();
@ -371,7 +371,7 @@ const func_builtins & value_array_t::get_builtins() const {
if (arr.empty()) {
return mk_val<value_undefined>();
}
return arr[arr.size() - 1]->clone();
return arr[arr.size() - 1];
}},
{"length", [](const func_args & args) -> value {
args.ensure_vals<value_array>();
@ -391,7 +391,7 @@ const func_builtins & value_array_t::get_builtins() const {
}
auto arr = slice(args.args[0]->as_array(), start, stop, step);
auto res = mk_val<value_array>();
res->val_arr = std::make_shared<std::vector<value>>(std::move(arr));
res->val_arr = std::move(arr);
return res;
}},
// TODO: reverse, sort, join, string, unique
@ -408,7 +408,7 @@ const func_builtins & value_object_t::get_builtins() const {
std::string key = args.args[1]->as_string().str();
auto it = obj.find(key);
if (it != obj.end()) {
return it->second->clone();
return it->second;
} else {
return mk_val<value_undefined>();
}
@ -418,7 +418,7 @@ const func_builtins & value_object_t::get_builtins() const {
const auto & obj = args.args[0]->as_object();
auto result = mk_val<value_array>();
for (const auto & pair : obj) {
result->val_arr->push_back(mk_val<value_string>(pair.first));
result->push_back(mk_val<value_string>(pair.first));
}
return result;
}},
@ -427,7 +427,7 @@ const func_builtins & value_object_t::get_builtins() const {
const auto & obj = args.args[0]->as_object();
auto result = mk_val<value_array>();
for (const auto & pair : obj) {
result->val_arr->push_back(pair.second->clone());
result->push_back(pair.second);
}
return result;
}},
@ -437,9 +437,9 @@ const func_builtins & value_object_t::get_builtins() const {
auto result = mk_val<value_array>();
for (const auto & pair : obj) {
auto item = mk_val<value_array>();
item->val_arr->push_back(mk_val<value_string>(pair.first));
item->val_arr->push_back(pair.second->clone());
result->val_arr->push_back(std::move(item));
item->push_back(mk_val<value_string>(pair.first));
item->push_back(pair.second);
result->push_back(std::move(item));
}
return result;
}},

View File

@ -13,16 +13,11 @@
namespace jinja {
template<typename T>
static bool is_stmt(const statement_ptr & ptr) {
return dynamic_cast<const T*>(ptr.get()) != nullptr;
}
static value_array exec_statements(const statements & stmts, context & ctx) {
auto result = mk_val<value_array>();
for (const auto & stmt : stmts) {
JJ_DEBUG("Executing statement of type %s", stmt->type().c_str());
result->val_arr->push_back(stmt->execute(ctx));
result->push_back(stmt->execute(ctx));
}
return result;
}
@ -32,7 +27,7 @@ value identifier::execute(context & ctx) {
auto builtins = global_builtins();
if (it != ctx.var.end()) {
JJ_DEBUG("Identifier '%s' found", val.c_str());
return it->second->clone();
return it->second;
} else if (builtins.find(val) != builtins.end()) {
JJ_DEBUG("Identifier '%s' found in builtins", val.c_str());
return mk_val<value_func>(builtins.at(val), val);
@ -115,10 +110,10 @@ value binary_expression::execute(context & ctx) {
auto & right_arr = right_val->as_array();
auto result = mk_val<value_array>();
for (const auto & item : left_arr) {
result->val_arr->push_back(item->clone());
result->push_back(item);
}
for (const auto & item : right_arr) {
result->val_arr->push_back(item->clone());
result->push_back(item);
}
return result;
}
@ -185,7 +180,7 @@ value filter_expression::execute(context & ctx) {
value input = operand->execute(ctx);
if (is_stmt<identifier>(filter)) {
auto filter_val = dynamic_cast<identifier*>(filter.get())->val;
auto filter_val = cast_stmt<identifier>(filter)->val;
if (filter_val == "to_json") {
// TODO: Implement to_json filter
@ -215,7 +210,7 @@ value test_expression::execute(context & ctx) {
throw std::runtime_error("Invalid test expression");
}
auto test_id = dynamic_cast<identifier*>(test.get())->val;
auto test_id = cast_stmt<identifier>(test)->val;
auto it = builtins.find("test_is_" + test_id);
JJ_DEBUG("Test expression %s '%s'", operand->type().c_str(), test_id.c_str());
if (it == builtins.end()) {
@ -252,12 +247,12 @@ value if_statement::execute(context & ctx) {
if (test_val->as_bool()) {
for (auto & stmt : body) {
JJ_DEBUG("IF --> Executing THEN body, current block: %s", stmt->type().c_str());
out->val_arr->push_back(stmt->execute(ctx));
out->push_back(stmt->execute(ctx));
}
} else {
for (auto & stmt : alternate) {
JJ_DEBUG("IF --> Executing ELSE body, current block: %s", stmt->type().c_str());
out->val_arr->push_back(stmt->execute(ctx));
out->push_back(stmt->execute(ctx));
}
}
return out;
@ -271,7 +266,7 @@ value for_statement::execute(context & ctx) {
if (is_stmt<select_expression>(iterable)) {
JJ_DEBUG("%s", "For loop has test expression");
auto select = dynamic_cast<select_expression*>(iterable.get());
auto select = cast_stmt<select_expression>(iterable);
iter_expr = std::move(select->lhs);
test_expr = std::move(select->test);
}
@ -292,7 +287,7 @@ value for_statement::execute(context & ctx) {
} else {
auto & arr = iterable_val->as_array();
for (const auto & item : arr) {
items.push_back(item->clone());
items.push_back(item);
}
}
@ -306,12 +301,12 @@ value for_statement::execute(context & ctx) {
std::function<void(context&)> scope_update_fn = [](context &) { /* no-op */};
if (is_stmt<identifier>(loopvar)) {
auto id = dynamic_cast<identifier*>(loopvar.get())->val;
auto id = cast_stmt<identifier>(loopvar)->val;
scope_update_fn = [id, &items, i](context & ctx) {
ctx.var[id] = items[i]->clone();
ctx.var[id] = items[i];
};
} else if (is_stmt<tuple_literal>(loopvar)) {
auto tuple = dynamic_cast<tuple_literal*>(loopvar.get());
auto tuple = cast_stmt<tuple_literal>(loopvar);
if (!is_val<value_array>(current)) {
throw std::runtime_error("Cannot unpack non-iterable type: " + current->type());
}
@ -325,8 +320,8 @@ value for_statement::execute(context & ctx) {
if (!is_stmt<identifier>(tuple->val[j])) {
throw std::runtime_error("Cannot unpack non-identifier type: " + tuple->val[j]->type());
}
auto id = dynamic_cast<identifier*>(tuple->val[j].get())->val;
ctx.var[id] = c_arr[j]->clone();
auto id = cast_stmt<identifier>(tuple->val[j])->val;
ctx.var[id] = c_arr[j];
}
};
} else {
@ -339,7 +334,7 @@ value for_statement::execute(context & ctx) {
continue;
}
}
filtered_items.push_back(current->clone());
filtered_items.push_back(current);
scope_update_fns.push_back(scope_update_fn);
}
@ -356,9 +351,9 @@ value for_statement::execute(context & ctx) {
loop_obj->insert("first", mk_val<value_bool>(i == 0));
loop_obj->insert("last", mk_val<value_bool>(i == filtered_items.size() - 1));
loop_obj->insert("length", mk_val<value_int>(filtered_items.size()));
loop_obj->insert("previtem", i > 0 ? filtered_items[i - 1]->clone() : mk_val<value_undefined>());
loop_obj->insert("nextitem", i < filtered_items.size() - 1 ? filtered_items[i + 1]->clone() : mk_val<value_undefined>());
ctx.var["loop"] = loop_obj->clone();
loop_obj->insert("previtem", i > 0 ? filtered_items[i - 1] : mk_val<value_undefined>());
loop_obj->insert("nextitem", i < filtered_items.size() - 1 ? filtered_items[i + 1] : mk_val<value_undefined>());
ctx.var["loop"] = loop_obj;
scope_update_fns[i](ctx);
try {
for (auto & stmt : body) {
@ -386,12 +381,12 @@ value set_statement::execute(context & ctx) {
auto rhs = val ? val->execute(ctx) : exec_statements(body, ctx);
if (is_stmt<identifier>(assignee)) {
auto var_name = dynamic_cast<identifier*>(assignee.get())->val;
auto var_name = cast_stmt<identifier>(assignee)->val;
JJ_DEBUG("Setting variable '%s' with value type %s", var_name.c_str(), rhs->type().c_str());
ctx.var[var_name] = rhs->clone();
ctx.var[var_name] = rhs;
} else if (is_stmt<tuple_literal>(assignee)) {
auto tuple = dynamic_cast<tuple_literal*>(assignee.get());
auto tuple = cast_stmt<tuple_literal>(assignee);
if (!is_val<value_array>(rhs)) {
throw std::runtime_error("Cannot unpack non-iterable type in set: " + rhs->type());
}
@ -404,27 +399,27 @@ value set_statement::execute(context & ctx) {
if (!is_stmt<identifier>(elem)) {
throw std::runtime_error("Cannot unpack to non-identifier in set: " + elem->type());
}
auto var_name = dynamic_cast<identifier*>(elem.get())->val;
ctx.var[var_name] = arr[i]->clone();
auto var_name = cast_stmt<identifier>(elem)->val;
ctx.var[var_name] = arr[i];
}
} else if (is_stmt<member_expression>(assignee)) {
auto member = dynamic_cast<member_expression*>(assignee.get());
auto member = cast_stmt<member_expression>(assignee);
if (member->computed) {
throw std::runtime_error("Cannot assign to computed member");
}
if (!is_stmt<identifier>(member->property)) {
throw std::runtime_error("Cannot assign to member with non-identifier property");
}
auto prop_name = dynamic_cast<identifier*>(member->property.get())->val;
auto prop_name = cast_stmt<identifier>(member->property)->val;
value object = member->object->execute(ctx);
if (!is_val<value_object>(object)) {
throw std::runtime_error("Cannot assign to member of non-object");
}
auto obj_ptr = dynamic_cast<value_object_t*>(object.get());
auto obj_ptr = cast_val<value_object>(object);
JJ_DEBUG("Setting object property '%s'", prop_name.c_str());
obj_ptr->insert(prop_name, rhs->clone());
obj_ptr->insert(prop_name, rhs);
} else {
throw std::runtime_error("Invalid LHS inside assignment expression: " + assignee->type());
@ -433,7 +428,7 @@ value set_statement::execute(context & ctx) {
}
value macro_statement::execute(context & ctx) {
std::string name = dynamic_cast<identifier*>(this->name.get())->val;
std::string name = cast_stmt<identifier>(this->name)->val;
const func_handler func = [this, &ctx, name](const func_args & args) -> value {
JJ_DEBUG("Invoking macro '%s' with %zu arguments", name.c_str(), args.args.size());
context macro_ctx(ctx); // new scope for macro execution
@ -442,9 +437,9 @@ value macro_statement::execute(context & ctx) {
size_t param_count = this->args.size();
size_t arg_count = args.args.size();
for (size_t i = 0; i < param_count; ++i) {
std::string param_name = dynamic_cast<identifier*>(this->args[i].get())->val;
std::string param_name = cast_stmt<identifier>(this->args[i])->val;
if (i < arg_count) {
macro_ctx.var[param_name] = args.args[i]->clone();
macro_ctx.var[param_name] = args.args[i];
} else {
macro_ctx.var[param_name] = mk_val<value_undefined>();
}
@ -466,7 +461,7 @@ value member_expression::execute(context & ctx) {
if (this->computed) {
JJ_DEBUG("Member expression, computing property type %s", this->property->type().c_str());
if (is_stmt<slice_expression>(this->property)) {
auto s = dynamic_cast<slice_expression*>(this->property.get());
auto s = cast_stmt<slice_expression>(this->property);
value start_val = s->start_expr ? s->start_expr->execute(ctx) : mk_val<value_undefined>();
value stop_val = s->stop_expr ? s->stop_expr->execute(ctx) : mk_val<value_undefined>();
value step_val = s->step_expr ? s->step_expr->execute(ctx) : mk_val<value_undefined>();
@ -478,15 +473,15 @@ value member_expression::execute(context & ctx) {
step_val->as_repr().c_str());
auto slice_func = try_builtin_func("slice", object);
func_args args;
args.args.push_back(start_val->clone());
args.args.push_back(stop_val->clone());
args.args.push_back(step_val->clone());
args.args.push_back(start_val);
args.args.push_back(stop_val);
args.args.push_back(step_val);
return slice_func->invoke(args);
} else {
property = this->property->execute(ctx);
}
} else {
property = mk_val<value_string>(dynamic_cast<identifier*>(this->property.get())->val);
property = mk_val<value_string>(cast_stmt<identifier>(this->property)->val);
}
JJ_DEBUG("Member expression on object type %s, property type %s", object->type().c_str(), property->type().c_str());
@ -502,7 +497,7 @@ value member_expression::execute(context & ctx) {
auto & obj = object->as_object();
auto it = obj.find(key);
if (it != obj.end()) {
val = it->second->clone();
val = it->second;
} else {
val = try_builtin_func(key, object, true);
}
@ -514,7 +509,7 @@ value member_expression::execute(context & ctx) {
if (is_val<value_array>(object)) {
auto & arr = object->as_array();
if (index >= 0 && index < static_cast<int64_t>(arr.size())) {
val = arr[index]->clone();
val = arr[index];
}
} else { // value_string
auto str = object->as_string().str();
@ -554,7 +549,7 @@ value call_expression::execute(context & ctx) {
if (!is_val<value_func>(callee_val)) {
throw std::runtime_error("Callee is not a function: got " + callee_val->type());
}
auto * callee_func = dynamic_cast<value_func_t*>(callee_val.get());
auto * callee_func = cast_val<value_func>(callee_val);
JJ_DEBUG("Calling function '%s' with %zu arguments", callee_func->name.c_str(), args.args.size());
return callee_func->invoke(args);
}
@ -597,7 +592,7 @@ value keyword_argument_expression::execute(context & ctx) {
throw std::runtime_error("Keyword argument key must be identifiers");
}
std::string k = dynamic_cast<identifier*>(key.get())->val;
std::string k = cast_stmt<identifier>(key)->val;
JJ_DEBUG("Keyword argument expression key: %s, value: %s", k.c_str(), val->type().c_str());
value v = val->execute(ctx);

View File

@ -12,6 +12,33 @@
namespace jinja {
struct statement;
using statement_ptr = std::unique_ptr<statement>;
using statements = std::vector<statement_ptr>;
// Helpers for dynamic casting and type checking
template<typename T>
struct extract_pointee_unique {
using type = T;
};
template<typename U>
struct extract_pointee_unique<std::unique_ptr<U>> {
using type = U;
};
template<typename T>
bool is_stmt(const statement_ptr & ptr) {
return dynamic_cast<const T*>(ptr.get()) != nullptr;
}
template<typename T>
T * cast_stmt(statement_ptr & ptr) {
return dynamic_cast<T*>(ptr.get());
}
template<typename T>
const T * cast_stmt(const statement_ptr & ptr) {
return dynamic_cast<const T*>(ptr.get());
}
// End Helpers
struct context {
std::map<std::string, value> var;
@ -25,7 +52,7 @@ struct context {
context(const context & parent) {
// inherit variables (for example, when entering a new scope)
for (const auto & pair : parent.var) {
var[pair.first] = pair.second->clone();
var[pair.first] = pair.second;
}
}
};
@ -39,9 +66,6 @@ struct statement {
virtual value execute(context &) { throw std::runtime_error("cannot exec " + type()); }
};
using statement_ptr = std::unique_ptr<statement>;
using statements = std::vector<statement_ptr>;
// Type Checking Utilities
template<typename T>
@ -461,7 +485,7 @@ struct vm {
value_array results = mk_val<value_array>();
for (auto & stmt : prog.body) {
value res = stmt->execute(ctx);
results->val_arr->push_back(std::move(res));
results->push_back(std::move(res));
}
return results;
}
@ -474,13 +498,13 @@ struct vm {
void gather_string_parts_recursive(const value & val, std::vector<jinja::string_part> & parts) {
if (is_val<value_string>(val)) {
const auto & str_val = dynamic_cast<value_string_t*>(val.get())->val_str;
const auto & str_val = cast_val<value_string>(val)->val_str;
for (const auto & part : str_val.parts) {
parts.push_back(part);
}
} else if (is_val<value_array>(val)) {
auto items = dynamic_cast<value_array_t*>(val.get())->val_arr.get();
for (const auto & item : *items) {
auto items = cast_val<value_array>(val)->as_array();
for (const auto & item : items) {
gather_string_parts_recursive(item, parts);
}
}

View File

@ -47,15 +47,15 @@ int main(void) {
return str_val;
};
jinja::value messages = jinja::mk_val<jinja::value_array>();
jinja::value msg1 = jinja::mk_val<jinja::value_object>();
(*msg1->val_obj)["role"] = make_non_special_string("user");
(*msg1->val_obj)["content"] = make_non_special_string("Hello, how are you?");
messages->val_arr->push_back(std::move(msg1));
jinja::value msg2 = jinja::mk_val<jinja::value_object>();
(*msg2->val_obj)["role"] = make_non_special_string("assistant");
(*msg2->val_obj)["content"] = make_non_special_string("I am fine, thank you!");
messages->val_arr->push_back(std::move(msg2));
jinja::value_array messages = jinja::mk_val<jinja::value_array>();
jinja::value_object msg1 = jinja::mk_val<jinja::value_object>();
msg1->insert("role", make_non_special_string("user"));
msg1->insert("content", make_non_special_string("Hello, how are you?"));
messages->push_back(std::move(msg1));
jinja::value_object msg2 = jinja::mk_val<jinja::value_object>();
msg2->insert("role", make_non_special_string("assistant"));
msg2->insert("content", make_non_special_string("I am fine, thank you!"));
messages->push_back(std::move(msg2));
ctx.var["messages"] = std::move(messages);