mostly works
This commit is contained in:
parent
45df0c91e7
commit
9a8a45ff3b
|
|
@ -0,0 +1,26 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <algorithm>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
namespace jinja {
|
||||||
|
|
||||||
|
static void string_replace_all(std::string & s, const std::string & search, const std::string & replace) {
|
||||||
|
if (search.empty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
std::string builder;
|
||||||
|
builder.reserve(s.length());
|
||||||
|
size_t pos = 0;
|
||||||
|
size_t last_pos = 0;
|
||||||
|
while ((pos = s.find(search, last_pos)) != std::string::npos) {
|
||||||
|
builder.append(s, last_pos, pos - last_pos);
|
||||||
|
builder.append(replace);
|
||||||
|
last_pos = pos + search.length();
|
||||||
|
}
|
||||||
|
builder.append(s, last_pos, std::string::npos);
|
||||||
|
s = std::move(builder);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace jinja
|
||||||
|
|
@ -65,7 +65,7 @@ struct func_args {
|
||||||
throw std::runtime_error("Expected " + std::to_string(count) + " arguments, got " + std::to_string(args.size()));
|
throw std::runtime_error("Expected " + std::to_string(count) + " arguments, got " + std::to_string(args.size()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// TODO: add support for get kwargs
|
value get_kwarg(const std::string & key) const;
|
||||||
// utility functions
|
// utility functions
|
||||||
template<typename T> void ensure_vals() const {
|
template<typename T> void ensure_vals() const {
|
||||||
ensure_count(1);
|
ensure_count(1);
|
||||||
|
|
|
||||||
|
|
@ -67,12 +67,14 @@ template<typename T>
|
||||||
static value test_type_fn(const func_args & args) {
|
static value test_type_fn(const func_args & args) {
|
||||||
args.ensure_count(1);
|
args.ensure_count(1);
|
||||||
bool is_type = is_val<T>(args.args[0]);
|
bool is_type = is_val<T>(args.args[0]);
|
||||||
|
JJ_DEBUG("test_type_fn: type=%s result=%d", typeid(T).name(), is_type ? 1 : 0);
|
||||||
return mk_val<value_bool>(is_type);
|
return mk_val<value_bool>(is_type);
|
||||||
}
|
}
|
||||||
template<typename T, typename U>
|
template<typename T, typename U>
|
||||||
static value test_type_fn(const func_args & args) {
|
static value test_type_fn(const func_args & args) {
|
||||||
args.ensure_count(1);
|
args.ensure_count(1);
|
||||||
bool is_type = is_val<T>(args.args[0]) || is_val<U>(args.args[0]);
|
bool is_type = is_val<T>(args.args[0]) || is_val<U>(args.args[0]);
|
||||||
|
JJ_DEBUG("test_type_fn: type=%s or %s result=%d", typeid(T).name(), typeid(U).name(), is_type ? 1 : 0);
|
||||||
return mk_val<value_bool>(is_type);
|
return mk_val<value_bool>(is_type);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -95,6 +97,20 @@ const func_builtins & global_builtins() {
|
||||||
}
|
}
|
||||||
return out;
|
return out;
|
||||||
}},
|
}},
|
||||||
|
{"strftime_now", [](const func_args & args) -> value {
|
||||||
|
args.ensure_count(1);
|
||||||
|
args.ensure_vals<value_string>();
|
||||||
|
std::string format = args.args[0]->as_string().str();
|
||||||
|
// get current time
|
||||||
|
// TODO: make sure this is the same behavior as Python's strftime
|
||||||
|
std::time_t t = std::time(nullptr);
|
||||||
|
char buf[100];
|
||||||
|
if (std::strftime(buf, sizeof(buf), format.c_str(), std::localtime(&t))) {
|
||||||
|
return mk_val<value_string>(std::string(buf));
|
||||||
|
} else {
|
||||||
|
throw raised_exception("strftime_now: failed to format time");
|
||||||
|
}
|
||||||
|
}},
|
||||||
|
|
||||||
// tests
|
// tests
|
||||||
{"test_is_boolean", test_type_fn<value_bool>},
|
{"test_is_boolean", test_type_fn<value_bool>},
|
||||||
|
|
@ -296,6 +312,25 @@ const func_builtins & value_string_t::get_builtins() const {
|
||||||
args.ensure_vals<value_string>();
|
args.ensure_vals<value_string>();
|
||||||
return mk_val<value_string>(args.args[0]->as_string());
|
return mk_val<value_string>(args.args[0]->as_string());
|
||||||
}},
|
}},
|
||||||
|
{"default", [](const func_args & args) -> value {
|
||||||
|
value input = args.args[0];
|
||||||
|
if (!is_val<value_string>(input)) {
|
||||||
|
throw raised_exception("default() first argument must be a string");
|
||||||
|
}
|
||||||
|
value default_val = mk_val<value_string>("");
|
||||||
|
if (args.args.size() > 1 && !args.args[1]->is_undefined()) {
|
||||||
|
default_val = args.args[1];
|
||||||
|
}
|
||||||
|
value boolean_val = mk_val<value_bool>(false);
|
||||||
|
if (args.args.size() > 1) {
|
||||||
|
boolean_val = args.args[1];
|
||||||
|
}
|
||||||
|
if (input->is_undefined() || (boolean_val->as_bool() && !input->as_bool())) {
|
||||||
|
return default_val;
|
||||||
|
} else {
|
||||||
|
return input;
|
||||||
|
}
|
||||||
|
}},
|
||||||
{"indent", [](const func_args &) -> value {
|
{"indent", [](const func_args &) -> value {
|
||||||
throw std::runtime_error("indent builtin not implemented");
|
throw std::runtime_error("indent builtin not implemented");
|
||||||
}},
|
}},
|
||||||
|
|
@ -380,6 +415,40 @@ const func_builtins & value_array_t::get_builtins() const {
|
||||||
res->val_arr = std::move(arr);
|
res->val_arr = std::move(arr);
|
||||||
return res;
|
return res;
|
||||||
}},
|
}},
|
||||||
|
{"selectattr", [](const func_args & args) -> value {
|
||||||
|
value input = args.args[0];
|
||||||
|
if (!is_val<value_array>(input)) {
|
||||||
|
throw raised_exception("selectattr() first argument must be an array, got " + input->type());
|
||||||
|
}
|
||||||
|
std::vector<std::string> selected;
|
||||||
|
for (size_t i = 1; i < args.args.size(); ++i) {
|
||||||
|
const auto & v = args.args[i];
|
||||||
|
if (!is_val<value_string>(v)) {
|
||||||
|
throw raised_exception("selectattr() attributes must be strings, got " + v->type());
|
||||||
|
}
|
||||||
|
JJ_DEBUG("selectattr: selecting attribute '%s'", v->as_string().str().c_str());
|
||||||
|
selected.push_back(v->as_string().str());
|
||||||
|
}
|
||||||
|
auto result = mk_val<value_array>();
|
||||||
|
for (const auto & item : input->as_array()) {
|
||||||
|
if (!is_val<value_object>(item)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
const auto & obj = item->as_object();
|
||||||
|
bool match = true;
|
||||||
|
for (const auto & attr : selected) {
|
||||||
|
auto it = obj.find(attr);
|
||||||
|
if (it == obj.end() || it->second->is_undefined() || (is_val<value_bool>(it->second) && !it->second->as_bool())) {
|
||||||
|
match = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (match) {
|
||||||
|
result->push_back(item);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}},
|
||||||
// TODO: reverse, sort, join, string, unique
|
// TODO: reverse, sort, join, string, unique
|
||||||
};
|
};
|
||||||
return builtins;
|
return builtins;
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@
|
||||||
#include "jinja-vm.h"
|
#include "jinja-vm.h"
|
||||||
#include "jinja-parser.h"
|
#include "jinja-parser.h"
|
||||||
#include "jinja-value.h"
|
#include "jinja-value.h"
|
||||||
|
#include "jinja-utils.h"
|
||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
@ -14,6 +15,22 @@ bool g_jinja_debug = true;
|
||||||
|
|
||||||
namespace jinja {
|
namespace jinja {
|
||||||
|
|
||||||
|
// func_args method implementations
|
||||||
|
|
||||||
|
value func_args::get_kwarg(const std::string & key) const {
|
||||||
|
for (const auto & arg : args) {
|
||||||
|
if (is_val<value_kwarg>(arg)) {
|
||||||
|
auto * kwarg = cast_val<value_kwarg>(arg);
|
||||||
|
if (kwarg->key == key) {
|
||||||
|
return kwarg->val;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return mk_val<value_undefined>();
|
||||||
|
}
|
||||||
|
|
||||||
|
// utils
|
||||||
|
|
||||||
static value_array exec_statements(const statements & stmts, context & ctx) {
|
static value_array exec_statements(const statements & stmts, context & ctx) {
|
||||||
auto result = mk_val<value_array>();
|
auto result = mk_val<value_array>();
|
||||||
for (const auto & stmt : stmts) {
|
for (const auto & stmt : stmts) {
|
||||||
|
|
@ -23,23 +40,6 @@ static value_array exec_statements(const statements & stmts, context & ctx) {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void string_replace_all(std::string & s, const std::string & search, const std::string & replace) {
|
|
||||||
if (search.empty()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
std::string builder;
|
|
||||||
builder.reserve(s.length());
|
|
||||||
size_t pos = 0;
|
|
||||||
size_t last_pos = 0;
|
|
||||||
while ((pos = s.find(search, last_pos)) != std::string::npos) {
|
|
||||||
builder.append(s, last_pos, pos - last_pos);
|
|
||||||
builder.append(replace);
|
|
||||||
last_pos = pos + search.length();
|
|
||||||
}
|
|
||||||
builder.append(s, last_pos, std::string::npos);
|
|
||||||
s = std::move(builder);
|
|
||||||
}
|
|
||||||
|
|
||||||
// execute with error handling
|
// execute with error handling
|
||||||
value statement::execute(context & ctx) {
|
value statement::execute(context & ctx) {
|
||||||
try {
|
try {
|
||||||
|
|
@ -138,6 +138,7 @@ value binary_expression::execute_impl(context & ctx) {
|
||||||
return mk_val<value_int>(static_cast<int64_t>(res));
|
return mk_val<value_int>(static_cast<int64_t>(res));
|
||||||
}
|
}
|
||||||
} else if (op.value == "/") {
|
} else if (op.value == "/") {
|
||||||
|
JJ_DEBUG("Division operation: %f / %f", a, b);
|
||||||
return mk_val<value_float>(a / b);
|
return mk_val<value_float>(a / b);
|
||||||
} else if (op.value == "%") {
|
} else if (op.value == "%") {
|
||||||
double rem = std::fmod(a, b);
|
double rem = std::fmod(a, b);
|
||||||
|
|
@ -149,12 +150,16 @@ value binary_expression::execute_impl(context & ctx) {
|
||||||
return mk_val<value_int>(static_cast<int64_t>(rem));
|
return mk_val<value_int>(static_cast<int64_t>(rem));
|
||||||
}
|
}
|
||||||
} else if (op.value == "<") {
|
} else if (op.value == "<") {
|
||||||
|
JJ_DEBUG("Comparison operation: %f < %f is %d", a, b, a < b);
|
||||||
return mk_val<value_bool>(a < b);
|
return mk_val<value_bool>(a < b);
|
||||||
} else if (op.value == ">") {
|
} else if (op.value == ">") {
|
||||||
|
JJ_DEBUG("Comparison operation: %f > %f is %d", a, b, a > b);
|
||||||
return mk_val<value_bool>(a > b);
|
return mk_val<value_bool>(a > b);
|
||||||
} else if (op.value == ">=") {
|
} else if (op.value == ">=") {
|
||||||
|
JJ_DEBUG("Comparison operation: %f >= %f is %d", a, b, a >= b);
|
||||||
return mk_val<value_bool>(a >= b);
|
return mk_val<value_bool>(a >= b);
|
||||||
} else if (op.value == "<=") {
|
} else if (op.value == "<=") {
|
||||||
|
JJ_DEBUG("Comparison operation: %f <= %f is %d", a, b, a <= b);
|
||||||
return mk_val<value_bool>(a <= b);
|
return mk_val<value_bool>(a <= b);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -235,24 +240,33 @@ static value try_builtin_func(const std::string & name, const value & input, boo
|
||||||
value filter_expression::execute_impl(context & ctx) {
|
value filter_expression::execute_impl(context & ctx) {
|
||||||
value input = operand->execute(ctx);
|
value input = operand->execute(ctx);
|
||||||
|
|
||||||
if (is_stmt<identifier>(filter)) {
|
JJ_DEBUG("Applying filter to %s", input->type().c_str());
|
||||||
auto filter_val = cast_stmt<identifier>(filter)->val;
|
|
||||||
|
|
||||||
if (filter_val == "to_json") {
|
if (is_stmt<identifier>(filter)) {
|
||||||
|
auto filter_id = cast_stmt<identifier>(filter)->val;
|
||||||
|
|
||||||
|
if (filter_id == "to_json") {
|
||||||
// TODO: Implement to_json filter
|
// TODO: Implement to_json filter
|
||||||
throw std::runtime_error("to_json filter not implemented");
|
throw std::runtime_error("to_json filter not implemented");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (filter_val == "trim") {
|
if (filter_id == "trim") {
|
||||||
filter_val = "strip"; // alias
|
filter_id = "strip"; // alias
|
||||||
}
|
}
|
||||||
JJ_DEBUG("Applying filter '%s' to %s", filter_val.c_str(), input->type().c_str());
|
JJ_DEBUG("Applying filter '%s' to %s", filter_id.c_str(), input->type().c_str());
|
||||||
return try_builtin_func(filter_val, input)->invoke({});
|
return try_builtin_func(filter_id, input)->invoke({});
|
||||||
|
|
||||||
} else if (is_stmt<call_expression>(filter)) {
|
} else if (is_stmt<call_expression>(filter)) {
|
||||||
// TODO
|
auto call = cast_stmt<call_expression>(filter);
|
||||||
// value filter_func = filter->execute(ctx);
|
auto filter_id = cast_stmt<identifier>(call->callee)->val;
|
||||||
throw std::runtime_error("Filter with arguments not implemented");
|
|
||||||
|
JJ_DEBUG("Applying filter '%s' with arguments to %s", filter_id.c_str(), input->type().c_str());
|
||||||
|
func_args args;
|
||||||
|
for (const auto & arg_expr : call->args) {
|
||||||
|
args.args.push_back(arg_expr->execute(ctx));
|
||||||
|
}
|
||||||
|
|
||||||
|
return try_builtin_func(filter_id, input)->invoke(args);
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
throw std::runtime_error("Invalid filter expression");
|
throw std::runtime_error("Invalid filter expression");
|
||||||
|
|
@ -268,7 +282,7 @@ value test_expression::execute_impl(context & ctx) {
|
||||||
|
|
||||||
auto test_id = cast_stmt<identifier>(test)->val;
|
auto test_id = cast_stmt<identifier>(test)->val;
|
||||||
auto it = builtins.find("test_is_" + test_id);
|
auto it = builtins.find("test_is_" + test_id);
|
||||||
JJ_DEBUG("Test expression %s '%s' %s", operand->type().c_str(), test_id.c_str(), negate ? "(negate)" : "");
|
JJ_DEBUG("Test expression %s '%s' %s (using function 'test_is_%s')", operand->type().c_str(), test_id.c_str(), negate ? "(negate)" : "", test_id.c_str());
|
||||||
if (it == builtins.end()) {
|
if (it == builtins.end()) {
|
||||||
throw std::runtime_error("Unknown test '" + test_id + "'");
|
throw std::runtime_error("Unknown test '" + test_id + "'");
|
||||||
}
|
}
|
||||||
|
|
@ -336,6 +350,12 @@ value for_statement::execute_impl(context & ctx) {
|
||||||
JJ_DEBUG("Executing for statement, iterable type: %s", iter_expr->type().c_str());
|
JJ_DEBUG("Executing for statement, iterable type: %s", iter_expr->type().c_str());
|
||||||
|
|
||||||
value iterable_val = iter_expr->execute(scope);
|
value iterable_val = iter_expr->execute(scope);
|
||||||
|
|
||||||
|
if (iterable_val->is_undefined()) {
|
||||||
|
JJ_DEBUG("%s", "For loop iterable is undefined, skipping loop");
|
||||||
|
iterable_val = mk_val<value_array>();
|
||||||
|
}
|
||||||
|
|
||||||
if (!is_val<value_array>(iterable_val) && !is_val<value_object>(iterable_val)) {
|
if (!is_val<value_array>(iterable_val) && !is_val<value_object>(iterable_val)) {
|
||||||
throw std::runtime_error("Expected iterable or object type in for loop: got " + iterable_val->type());
|
throw std::runtime_error("Expected iterable or object type in for loop: got " + iterable_val->type());
|
||||||
}
|
}
|
||||||
|
|
@ -555,7 +575,10 @@ value member_expression::execute_impl(context & ctx) {
|
||||||
|
|
||||||
value val = mk_val<value_undefined>();
|
value val = mk_val<value_undefined>();
|
||||||
|
|
||||||
if (is_val<value_object>(object)) {
|
if (is_val<value_undefined>(object)) {
|
||||||
|
JJ_DEBUG("%s", "Accessing property on undefined object, returning undefined");
|
||||||
|
return val;
|
||||||
|
} else if (is_val<value_object>(object)) {
|
||||||
if (!is_val<value_string>(property)) {
|
if (!is_val<value_string>(property)) {
|
||||||
throw std::runtime_error("Cannot access object with non-string: got " + property->type());
|
throw std::runtime_error("Cannot access object with non-string: got " + property->type());
|
||||||
}
|
}
|
||||||
|
|
@ -623,35 +646,39 @@ value call_expression::execute_impl(context & ctx) {
|
||||||
|
|
||||||
// compare operator for value_t
|
// compare operator for value_t
|
||||||
bool value_compare(const value & a, const value & b) {
|
bool value_compare(const value & a, const value & b) {
|
||||||
JJ_DEBUG("Comparing types: %s and %s", a->type().c_str(), b->type().c_str());
|
auto cmp = [&]() {
|
||||||
// compare numeric types
|
// compare numeric types
|
||||||
if ((is_val<value_int>(a) || is_val<value_float>(a)) &&
|
if ((is_val<value_int>(a) || is_val<value_float>(a)) &&
|
||||||
(is_val<value_int>(b) || is_val<value_float>(b))){
|
(is_val<value_int>(b) || is_val<value_float>(b))){
|
||||||
try {
|
try {
|
||||||
return a->as_float() == b->as_float();
|
return a->as_float() == b->as_float();
|
||||||
} catch (...) {}
|
} catch (...) {}
|
||||||
}
|
}
|
||||||
// compare string and number
|
// compare string and number
|
||||||
// TODO: not sure if this is the right behavior
|
// TODO: not sure if this is the right behavior
|
||||||
if ((is_val<value_string>(b) && (is_val<value_int>(a) || is_val<value_float>(a))) ||
|
if ((is_val<value_string>(b) && (is_val<value_int>(a) || is_val<value_float>(a))) ||
|
||||||
(is_val<value_string>(a) && (is_val<value_int>(b) || is_val<value_float>(b)))) {
|
(is_val<value_string>(a) && (is_val<value_int>(b) || is_val<value_float>(b)))) {
|
||||||
try {
|
try {
|
||||||
|
return a->as_string().str() == b->as_string().str();
|
||||||
|
} catch (...) {}
|
||||||
|
}
|
||||||
|
// compare boolean simple
|
||||||
|
if (is_val<value_bool>(a) && is_val<value_bool>(b)) {
|
||||||
|
return a->as_bool() == b->as_bool();
|
||||||
|
}
|
||||||
|
// compare string simple
|
||||||
|
if (is_val<value_string>(a) && is_val<value_string>(b)) {
|
||||||
return a->as_string().str() == b->as_string().str();
|
return a->as_string().str() == b->as_string().str();
|
||||||
} catch (...) {}
|
}
|
||||||
}
|
// compare by type
|
||||||
// compare boolean simple
|
if (a->type() != b->type()) {
|
||||||
if (is_val<value_bool>(a) && is_val<value_bool>(b)) {
|
return false;
|
||||||
return a->as_bool() == b->as_bool();
|
}
|
||||||
}
|
|
||||||
// compare string simple
|
|
||||||
if (is_val<value_string>(a) && is_val<value_string>(b)) {
|
|
||||||
return a->as_string().str() == b->as_string().str();
|
|
||||||
}
|
|
||||||
// compare by type
|
|
||||||
if (a->type() != b->type()) {
|
|
||||||
return false;
|
return false;
|
||||||
}
|
};
|
||||||
return false;
|
auto result = cmp();
|
||||||
|
JJ_DEBUG("Comparing types: %s and %s result=%d", a->type().c_str(), b->type().c_str(), result);
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
value keyword_argument_expression::execute_impl(context & ctx) {
|
value keyword_argument_expression::execute_impl(context & ctx) {
|
||||||
|
|
|
||||||
|
|
@ -71,7 +71,7 @@ struct statement {
|
||||||
// execute_impl must be overridden by derived classes
|
// execute_impl must be overridden by derived classes
|
||||||
virtual value execute_impl(context &) { throw std::runtime_error("cannot exec " + type()); }
|
virtual value execute_impl(context &) { throw std::runtime_error("cannot exec " + type()); }
|
||||||
// execute is the public method to execute a statement with error handling
|
// execute is the public method to execute a statement with error handling
|
||||||
virtual value execute(context &);
|
value execute(context &);
|
||||||
};
|
};
|
||||||
|
|
||||||
// Type Checking Utilities
|
// Type Checking Utilities
|
||||||
|
|
@ -288,13 +288,17 @@ struct array_literal : public expression {
|
||||||
for (const auto& item : this->val) chk_type<expression>(item);
|
for (const auto& item : this->val) chk_type<expression>(item);
|
||||||
}
|
}
|
||||||
std::string type() const override { return "ArrayLiteral"; }
|
std::string type() const override { return "ArrayLiteral"; }
|
||||||
|
value execute_impl(context & ctx) override {
|
||||||
|
auto arr = mk_val<value_array>();
|
||||||
|
for (const auto & item_stmt : val) {
|
||||||
|
arr->push_back(item_stmt->execute(ctx));
|
||||||
|
}
|
||||||
|
return arr;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
struct tuple_literal : public expression {
|
struct tuple_literal : public array_literal {
|
||||||
statements val;
|
explicit tuple_literal(statements && val) : array_literal(std::move(val)) {}
|
||||||
explicit tuple_literal(statements && val) : val(std::move(val)) {
|
|
||||||
for (const auto & item : this->val) chk_type<expression>(item);
|
|
||||||
}
|
|
||||||
std::string type() const override { return "TupleLiteral"; }
|
std::string type() const override { return "TupleLiteral"; }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -376,6 +380,13 @@ struct select_expression : public expression {
|
||||||
chk_type<expression>(this->test);
|
chk_type<expression>(this->test);
|
||||||
}
|
}
|
||||||
std::string type() const override { return "SelectExpression"; }
|
std::string type() const override { return "SelectExpression"; }
|
||||||
|
value execute_impl(context & ctx) override {
|
||||||
|
auto predicate = test->execute_impl(ctx);
|
||||||
|
if (!predicate->as_bool()) {
|
||||||
|
return mk_val<value_undefined>();
|
||||||
|
}
|
||||||
|
return lhs->execute_impl(ctx);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -474,6 +485,14 @@ struct ternary_expression : public expression {
|
||||||
chk_type<expression>(this->false_expr);
|
chk_type<expression>(this->false_expr);
|
||||||
}
|
}
|
||||||
std::string type() const override { return "Ternary"; }
|
std::string type() const override { return "Ternary"; }
|
||||||
|
value execute_impl(context & ctx) override {
|
||||||
|
value cond_val = condition->execute(ctx);
|
||||||
|
if (cond_val->as_bool()) {
|
||||||
|
return true_expr->execute(ctx);
|
||||||
|
} else {
|
||||||
|
return false_expr->execute(ctx);
|
||||||
|
}
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
struct raised_exception : public std::exception {
|
struct raised_exception : public std::exception {
|
||||||
|
|
|
||||||
|
|
@ -83,6 +83,8 @@ void run(std::string contents) {
|
||||||
messages->push_back(std::move(msg2));
|
messages->push_back(std::move(msg2));
|
||||||
|
|
||||||
ctx.var["messages"] = std::move(messages);
|
ctx.var["messages"] = std::move(messages);
|
||||||
|
ctx.var["eos_token"] = jinja::mk_val<jinja::value_string>("</s>");
|
||||||
|
// ctx.var["tools"] = jinja::mk_val<jinja::value_null>();
|
||||||
|
|
||||||
jinja::vm vm(ctx);
|
jinja::vm vm(ctx);
|
||||||
const jinja::value results = vm.execute(ast);
|
const jinja::value results = vm.execute(ast);
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue