render gemma tmpl ok
This commit is contained in:
parent
10835f2720
commit
81310d29c1
|
|
@ -142,11 +142,11 @@ private:
|
|||
|
||||
} else if (name == "call") {
|
||||
statements caller_args;
|
||||
bool has_caller_args = false;
|
||||
// bool has_caller_args = false;
|
||||
if (is(token::open_paren)) {
|
||||
// Optional caller arguments, e.g. {% call(user) dump_users(...) %}
|
||||
caller_args = parse_args();
|
||||
has_caller_args = true;
|
||||
// has_caller_args = true;
|
||||
}
|
||||
auto callee = parse_primary_expression();
|
||||
if (!is_type<identifier>(callee)) throw std::runtime_error("Expected identifier");
|
||||
|
|
|
|||
|
|
@ -28,8 +28,13 @@ bool is_val(const value & ptr) {
|
|||
using PointeeType = typename extract_pointee<T>::type;
|
||||
return dynamic_cast<const PointeeType*>(ptr.get()) != nullptr;
|
||||
}
|
||||
template<typename T>
|
||||
bool is_val(const value_t * ptr) {
|
||||
using PointeeType = typename extract_pointee<T>::type;
|
||||
return dynamic_cast<const PointeeType*>(ptr) != nullptr;
|
||||
}
|
||||
template<typename T, typename... Args>
|
||||
value mk_val(Args&&... args) {
|
||||
std::unique_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)...);
|
||||
}
|
||||
|
|
@ -70,6 +75,8 @@ struct func_args {
|
|||
using func_handler = std::function<value(const func_args &)>;
|
||||
using func_builtins = std::map<std::string, func_handler>;
|
||||
|
||||
bool value_compare(const value & a, const value & b);
|
||||
|
||||
struct value_t {
|
||||
int64_t val_int;
|
||||
double val_flt;
|
||||
|
|
@ -93,12 +100,12 @@ struct value_t {
|
|||
|
||||
virtual std::string type() const { return ""; }
|
||||
|
||||
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 int64_t as_int() const { throw std::runtime_error(type() + " is not an int value"); }
|
||||
virtual double as_float() const { throw std::runtime_error(type() + " is not a float value"); }
|
||||
virtual std::string as_string() const { throw std::runtime_error(type() + " is not a string value"); }
|
||||
virtual bool as_bool() const { throw std::runtime_error(type() + " is not a bool value"); }
|
||||
virtual const std::vector<value> & as_array() const { throw std::runtime_error(type() + " is not an array value"); }
|
||||
virtual const std::map<std::string, value> & as_object() const { throw std::runtime_error(type() + " is not an object value"); }
|
||||
virtual value invoke(const func_args &) const { throw std::runtime_error("Not a function value"); }
|
||||
virtual bool is_null() const { return false; }
|
||||
virtual bool is_undefined() const { return false; }
|
||||
|
|
@ -106,17 +113,11 @@ struct value_t {
|
|||
throw std::runtime_error("No builtins available for type " + type());
|
||||
}
|
||||
|
||||
virtual std::string as_repr() const { return as_string(); }
|
||||
|
||||
virtual value clone() const {
|
||||
return std::make_unique<value_t>(*this);
|
||||
}
|
||||
|
||||
virtual bool operator==(const value & other) const {
|
||||
// TODO
|
||||
return false;
|
||||
}
|
||||
virtual bool operator!=(const value & other) const {
|
||||
return !(*this == other);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
|
@ -188,8 +189,12 @@ struct value_array_t : public value_t {
|
|||
val_arr->push_back(other.val_arr->at(i)->clone());
|
||||
}
|
||||
}
|
||||
void push_back(const value & val) {
|
||||
val_arr->push_back(val->clone());
|
||||
}
|
||||
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;
|
||||
|
|
@ -200,7 +205,7 @@ struct value_array_t : public value_t {
|
|||
ss << "[";
|
||||
for (size_t i = 0; i < val_arr->size(); i++) {
|
||||
if (i > 0) ss << ", ";
|
||||
ss << val_arr->at(i)->as_string();
|
||||
ss << val_arr->at(i)->as_repr();
|
||||
}
|
||||
ss << "]";
|
||||
return ss.str();
|
||||
|
|
@ -224,8 +229,12 @@ struct value_object_t : public value_t {
|
|||
(*val_obj)[pair.first] = pair.second->clone();
|
||||
}
|
||||
}
|
||||
void insert(const std::string & key, const value & val) {
|
||||
(*val_obj)[key] = val->clone();
|
||||
}
|
||||
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;
|
||||
|
|
@ -244,6 +253,7 @@ struct value_func_t : public value_t {
|
|||
return val_func(args);
|
||||
}
|
||||
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>;
|
||||
|
|
@ -252,6 +262,8 @@ using value_func = std::unique_ptr<value_func_t>;
|
|||
struct value_null_t : public value_t {
|
||||
virtual std::string type() const override { return "Null"; }
|
||||
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>;
|
||||
|
|
@ -260,8 +272,13 @@ 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; }
|
||||
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>;
|
||||
|
||||
|
||||
const func_builtins & global_builtins();
|
||||
|
||||
} // namespace jinja
|
||||
|
|
|
|||
|
|
@ -8,6 +8,18 @@
|
|||
|
||||
namespace jinja {
|
||||
|
||||
const func_builtins & global_builtins() {
|
||||
static const func_builtins builtins = {
|
||||
{"raise_exception", [](const func_args & args) -> value {
|
||||
args.ensure_count(1);
|
||||
std::string msg = args.args[0]->as_string();
|
||||
throw raised_exception("Jinja Exception: " + msg);
|
||||
}},
|
||||
};
|
||||
return builtins;
|
||||
}
|
||||
|
||||
|
||||
const func_builtins & value_int_t::get_builtins() const {
|
||||
static const func_builtins builtins = {
|
||||
{"abs", [](const func_args & args) -> value {
|
||||
|
|
@ -189,10 +201,10 @@ const func_builtins & value_string_t::get_builtins() const {
|
|||
args.ensure_vals<value_string>();
|
||||
return mk_val<value_string>(args.args[0]->as_string());
|
||||
}},
|
||||
{"indent", [](const func_args & args) -> value {
|
||||
{"indent", [](const func_args &) -> value {
|
||||
throw std::runtime_error("indent builtin not implemented");
|
||||
}},
|
||||
{"join", [](const func_args & args) -> value {
|
||||
{"join", [](const func_args &) -> value {
|
||||
throw std::runtime_error("join builtin not implemented");
|
||||
}},
|
||||
};
|
||||
|
|
@ -307,5 +319,4 @@ const func_builtins & value_object_t::get_builtins() const {
|
|||
return builtins;
|
||||
}
|
||||
|
||||
|
||||
} // namespace jinja
|
||||
|
|
|
|||
|
|
@ -18,11 +18,24 @@ 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));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
value identifier::execute(context & ctx) {
|
||||
auto it = ctx.var.find(val);
|
||||
auto builtins = global_builtins();
|
||||
if (it != ctx.var.end()) {
|
||||
JJ_DEBUG("Identifier '%s' found", val.c_str());
|
||||
return it->second->clone();
|
||||
} 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));
|
||||
} else {
|
||||
JJ_DEBUG("Identifier '%s' not found, returning undefined", val.c_str());
|
||||
return mk_val<value_undefined>();
|
||||
|
|
@ -31,6 +44,7 @@ value identifier::execute(context & ctx) {
|
|||
|
||||
value binary_expression::execute(context & ctx) {
|
||||
value left_val = left->execute(ctx);
|
||||
JJ_DEBUG("Executing binary expression with operator '%s'", op.value.c_str());
|
||||
|
||||
// Logical operators
|
||||
if (op.value == "and") {
|
||||
|
|
@ -42,9 +56,9 @@ value binary_expression::execute(context & ctx) {
|
|||
// Equality operators
|
||||
value right_val = right->execute(ctx);
|
||||
if (op.value == "==") {
|
||||
return mk_val<value_bool>(left_val == right_val);
|
||||
return mk_val<value_bool>(value_compare(left_val, right_val));
|
||||
} else if (op.value == "!=") {
|
||||
return mk_val<value_bool>(left_val != right_val);
|
||||
return mk_val<value_bool>(!value_compare(left_val, right_val));
|
||||
}
|
||||
|
||||
// Handle undefined and null values
|
||||
|
|
@ -70,6 +84,7 @@ value binary_expression::execute(context & ctx) {
|
|||
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;
|
||||
JJ_DEBUG("Arithmetic operation: %f %s %f = %f", a, op.value.c_str(), b, res);
|
||||
bool is_float = is_val<value_float>(left_val) || is_val<value_float>(right_val);
|
||||
if (is_float) {
|
||||
return mk_val<value_float>(res);
|
||||
|
|
@ -80,6 +95,7 @@ value binary_expression::execute(context & ctx) {
|
|||
return mk_val<value_float>(a / b);
|
||||
} else if (op.value == "%") {
|
||||
double rem = std::fmod(a, b);
|
||||
JJ_DEBUG("Modulo operation: %f %% %f = %f", a, b, rem);
|
||||
bool is_float = is_val<value_float>(left_val) || is_val<value_float>(right_val);
|
||||
if (is_float) {
|
||||
return mk_val<value_float>(rem);
|
||||
|
|
@ -123,6 +139,7 @@ value binary_expression::execute(context & ctx) {
|
|||
|
||||
// String concatenation
|
||||
if (is_val<value_string>(left_val) || is_val<value_string>(right_val)) {
|
||||
JJ_DEBUG("%s", "String concatenation with + operator");
|
||||
if (op.value == "+") {
|
||||
return mk_val<value_string>(left_val->as_string() + right_val->as_string());
|
||||
}
|
||||
|
|
@ -177,7 +194,6 @@ value filter_expression::execute(context & ctx) {
|
|||
}
|
||||
|
||||
if (is_val<value_array>(input)) {
|
||||
auto & arr = input->as_array();
|
||||
auto res = try_builtin(filter_val);
|
||||
if (res) {
|
||||
return res;
|
||||
|
|
@ -222,7 +238,12 @@ value if_statement::execute(context & ctx) {
|
|||
auto out = mk_val<value_array>();
|
||||
if (test_val->as_bool()) {
|
||||
for (auto & stmt : body) {
|
||||
JJ_DEBUG("Executing if body statement of type %s", stmt->type().c_str());
|
||||
JJ_DEBUG("IF --> Executing THEN body, current block: %s", stmt->type().c_str());
|
||||
out->val_arr->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));
|
||||
}
|
||||
}
|
||||
|
|
@ -230,19 +251,171 @@ value if_statement::execute(context & ctx) {
|
|||
}
|
||||
|
||||
value for_statement::execute(context & ctx) {
|
||||
throw std::runtime_error("for_statement::execute not implemented");
|
||||
}
|
||||
context scope(ctx); // new scope for loop variables
|
||||
|
||||
value break_statement::execute(context & ctx) {
|
||||
throw std::runtime_error("break_statement::execute not implemented");
|
||||
}
|
||||
statement_ptr iter_expr = std::move(iterable);
|
||||
statement_ptr test_expr = nullptr;
|
||||
|
||||
value continue_statement::execute(context & ctx) {
|
||||
throw std::runtime_error("continue_statement::execute not implemented");
|
||||
if (is_stmt<select_expression>(iterable)) {
|
||||
JJ_DEBUG("%s", "For loop has test expression");
|
||||
auto select = dynamic_cast<select_expression*>(iterable.get());
|
||||
iter_expr = std::move(select->lhs);
|
||||
test_expr = std::move(select->test);
|
||||
}
|
||||
|
||||
JJ_DEBUG("Executing for statement, iterable type: %s", iter_expr->type().c_str());
|
||||
|
||||
value iterable_val = iter_expr->execute(scope);
|
||||
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());
|
||||
}
|
||||
|
||||
std::vector<value> items;
|
||||
if (is_val<value_object>(iterable_val)) {
|
||||
auto & obj = iterable_val->as_object();
|
||||
for (auto & p : obj) {
|
||||
items.push_back(mk_val<value_string>(p.first));
|
||||
}
|
||||
} else {
|
||||
auto & arr = iterable_val->as_array();
|
||||
for (const auto & item : arr) {
|
||||
items.push_back(item->clone());
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<std::function<void(context &)>> scope_update_fns;
|
||||
|
||||
std::vector<value> filtered_items;
|
||||
for (size_t i = 0; i < items.size(); ++i) {
|
||||
context loop_scope(scope);
|
||||
|
||||
const value & current = items[i];
|
||||
|
||||
std::function<void(context&)> scope_update_fn = [](context &) { /* no-op */};
|
||||
if (is_stmt<identifier>(loopvar)) {
|
||||
auto id = dynamic_cast<identifier*>(loopvar.get())->val;
|
||||
scope_update_fn = [id, &items, i](context & ctx) {
|
||||
ctx.var[id] = items[i]->clone();
|
||||
};
|
||||
} else if (is_stmt<tuple_literal>(loopvar)) {
|
||||
auto tuple = dynamic_cast<tuple_literal*>(loopvar.get());
|
||||
if (!is_val<value_array>(current)) {
|
||||
throw std::runtime_error("Cannot unpack non-iterable type: " + current->type());
|
||||
}
|
||||
auto & c_arr = current->as_array();
|
||||
if (tuple->val.size() != c_arr.size()) {
|
||||
throw std::runtime_error(std::string("Too ") + (tuple->val.size() > c_arr.size() ? "few" : "many") + " items to unpack");
|
||||
}
|
||||
scope_update_fn = [tuple, &items, i](context & ctx) {
|
||||
auto & c_arr = items[i]->as_array();
|
||||
for (size_t j = 0; j < tuple->val.size(); ++j) {
|
||||
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();
|
||||
}
|
||||
};
|
||||
} else {
|
||||
throw std::runtime_error("Invalid loop variable(s): " + loopvar->type());
|
||||
}
|
||||
if (test_expr) {
|
||||
scope_update_fn(loop_scope);
|
||||
value test_val = test_expr->execute(loop_scope);
|
||||
if (!test_val->as_bool()) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
filtered_items.push_back(current->clone());
|
||||
scope_update_fns.push_back(scope_update_fn);
|
||||
}
|
||||
|
||||
auto result = mk_val<value_array>();
|
||||
|
||||
bool noIteration = true;
|
||||
for (size_t i = 0; i < filtered_items.size(); ++i) {
|
||||
JJ_DEBUG("For loop iteration %zu/%zu", i + 1, filtered_items.size());
|
||||
value_object loop_obj = mk_val<value_object>();
|
||||
loop_obj->insert("index", mk_val<value_int>(i + 1));
|
||||
loop_obj->insert("index0", mk_val<value_int>(i));
|
||||
loop_obj->insert("revindex", mk_val<value_int>(filtered_items.size() - i));
|
||||
loop_obj->insert("revindex0", mk_val<value_int>(filtered_items.size() - i - 1));
|
||||
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();
|
||||
scope_update_fns[i](ctx);
|
||||
try {
|
||||
for (auto & stmt : body) {
|
||||
value val = stmt->execute(ctx);
|
||||
result->push_back(val);
|
||||
}
|
||||
} catch (const continue_statement::exception &) {
|
||||
continue;
|
||||
} catch (const break_statement::exception &) {
|
||||
break;
|
||||
}
|
||||
noIteration = false;
|
||||
}
|
||||
if (noIteration) {
|
||||
for (auto & stmt : default_block) {
|
||||
value val = stmt->execute(ctx);
|
||||
result->push_back(val);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
value set_statement::execute(context & ctx) {
|
||||
throw std::runtime_error("set_statement::execute not implemented");
|
||||
auto rhs = val ? val->execute(ctx) : exec_statements(body, ctx);
|
||||
|
||||
if (is_stmt<identifier>(assignee)) {
|
||||
auto var_name = dynamic_cast<identifier*>(assignee.get())->val;
|
||||
JJ_DEBUG("Setting variable '%s'", var_name.c_str());
|
||||
ctx.var[var_name] = rhs->clone();
|
||||
|
||||
} else if (is_stmt<tuple_literal>(assignee)) {
|
||||
auto tuple = dynamic_cast<tuple_literal*>(assignee.get());
|
||||
if (!is_val<value_array>(rhs)) {
|
||||
throw std::runtime_error("Cannot unpack non-iterable type in set: " + rhs->type());
|
||||
}
|
||||
auto & arr = rhs->as_array();
|
||||
if (arr.size() != tuple->val.size()) {
|
||||
throw std::runtime_error(std::string("Too ") + (tuple->val.size() > arr.size() ? "few" : "many") + " items to unpack in set");
|
||||
}
|
||||
for (size_t i = 0; i < tuple->val.size(); ++i) {
|
||||
auto & elem = tuple->val[i];
|
||||
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();
|
||||
}
|
||||
|
||||
} else if (is_stmt<member_expression>(assignee)) {
|
||||
auto member = dynamic_cast<member_expression*>(assignee.get());
|
||||
value object = member->object->execute(ctx);
|
||||
if (!is_val<value_object>(object)) {
|
||||
throw std::runtime_error("Cannot assign to member of non-object");
|
||||
}
|
||||
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 obj_ptr = dynamic_cast<value_object*>(object.get());
|
||||
JJ_DEBUG("Setting object property '%s'", prop_name.c_str());
|
||||
obj_ptr->get()->insert(prop_name, rhs->clone());
|
||||
|
||||
} else {
|
||||
throw std::runtime_error("Invalid LHS inside assignment expression: " + assignee->type());
|
||||
}
|
||||
return mk_val<value_null>();
|
||||
}
|
||||
|
||||
value member_expression::execute(context & ctx) {
|
||||
|
|
@ -279,6 +452,7 @@ value member_expression::execute(context & ctx) {
|
|||
} else if (is_val<value_array>(object) || is_val<value_string>(object)) {
|
||||
if (is_val<value_int>(property)) {
|
||||
int64_t index = property->as_int();
|
||||
JJ_DEBUG("Accessing %s index %lld", is_val<value_array>(object) ? "array" : "string", index);
|
||||
if (is_val<value_array>(object)) {
|
||||
auto & arr = object->as_array();
|
||||
if (index >= 0 && index < static_cast<int64_t>(arr.size())) {
|
||||
|
|
@ -292,6 +466,7 @@ value member_expression::execute(context & ctx) {
|
|||
}
|
||||
} else if (is_val<value_string>(property)) {
|
||||
auto key = property->as_string();
|
||||
JJ_DEBUG("Accessing %s built-in '%s'", is_val<value_array>(object) ? "array" : "string", key.c_str());
|
||||
auto builtins = object->get_builtins();
|
||||
auto bit = builtins.find(key);
|
||||
if (bit != builtins.end()) {
|
||||
|
|
@ -320,4 +495,55 @@ value member_expression::execute(context & ctx) {
|
|||
return val;
|
||||
}
|
||||
|
||||
static func_args gather_call_args(const statements & arg_stmts, context & ctx) {
|
||||
func_args args;
|
||||
for (auto & arg_stmt : arg_stmts) {
|
||||
args.args.push_back(arg_stmt->execute(ctx));
|
||||
}
|
||||
return args;
|
||||
}
|
||||
|
||||
value call_expression::execute(context & ctx) {
|
||||
auto args = gather_call_args(this->args, ctx);
|
||||
value callee_val = callee->execute(ctx);
|
||||
JJ_DEBUG("Calling function of type %s with %zu arguments", callee_val->type().c_str(), args.args.size());
|
||||
if (!is_val<value_t>(callee_val)) {
|
||||
throw std::runtime_error("Callee is not a function: got " + callee_val->type());
|
||||
}
|
||||
return callee_val->invoke(args);
|
||||
}
|
||||
|
||||
// compare operator for value_t
|
||||
bool value_compare(const value & a, const value & b) {
|
||||
JJ_DEBUG("Comparing types: %s and %s", a->type().c_str(), b->type().c_str());
|
||||
// compare numeric types
|
||||
if ((is_val<value_int>(a) || is_val<value_float>(a)) &&
|
||||
(is_val<value_int>(b) || is_val<value_float>(b))){
|
||||
try {
|
||||
return a->as_float() == b->as_float();
|
||||
} catch (...) {}
|
||||
}
|
||||
// compare string and number
|
||||
// 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))) ||
|
||||
(is_val<value_string>(a) && (is_val<value_int>(b) || is_val<value_float>(b)))) {
|
||||
try {
|
||||
return a->as_string() == b->as_string();
|
||||
} 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() == b->as_string();
|
||||
}
|
||||
// compare by type
|
||||
if (a->type() != b->type()) {
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
} // namespace jinja
|
||||
|
|
|
|||
|
|
@ -32,7 +32,7 @@ struct context {
|
|||
struct statement {
|
||||
virtual ~statement() = default;
|
||||
virtual std::string type() const { return "Statement"; }
|
||||
virtual value execute(context & ctx) { throw std::runtime_error("cannot exec " + type()); }
|
||||
virtual value execute(context &) { throw std::runtime_error("cannot exec " + type()); }
|
||||
};
|
||||
|
||||
using statement_ptr = std::unique_ptr<statement>;
|
||||
|
|
@ -68,7 +68,7 @@ struct program : public statement {
|
|||
|
||||
explicit program(statements && body) : body(std::move(body)) {}
|
||||
std::string type() const override { return "Program"; }
|
||||
value execute(context & ctx) override {
|
||||
value execute(context &) override {
|
||||
throw std::runtime_error("Cannot execute program directly, use jinja::vm instead");
|
||||
}
|
||||
};
|
||||
|
|
@ -113,12 +113,30 @@ struct for_statement : public statement {
|
|||
|
||||
struct break_statement : public statement {
|
||||
std::string type() const override { return "Break"; }
|
||||
value execute(context & ctx) override;
|
||||
|
||||
struct exception : public std::exception {
|
||||
const char* what() const noexcept override {
|
||||
return "Break statement executed";
|
||||
}
|
||||
};
|
||||
|
||||
value execute(context &) override {
|
||||
throw break_statement::exception();
|
||||
}
|
||||
};
|
||||
|
||||
struct continue_statement : public statement {
|
||||
std::string type() const override { return "Continue"; }
|
||||
value execute(context & ctx) override;
|
||||
|
||||
struct exception : public std::exception {
|
||||
const char* what() const noexcept override {
|
||||
return "Continue statement executed";
|
||||
}
|
||||
};
|
||||
|
||||
value execute(context &) override {
|
||||
throw continue_statement::exception();
|
||||
}
|
||||
};
|
||||
|
||||
struct set_statement : public statement {
|
||||
|
|
@ -148,14 +166,12 @@ struct macro_statement : public statement {
|
|||
}
|
||||
|
||||
std::string type() const override { return "Macro"; }
|
||||
value execute(context & ctx) override {}
|
||||
};
|
||||
|
||||
struct comment_statement : public statement {
|
||||
std::string val;
|
||||
explicit comment_statement(const std::string & v) : val(v) {}
|
||||
std::string type() const override { return "Comment"; }
|
||||
value execute(context & ctx) override {}
|
||||
};
|
||||
|
||||
// Expressions
|
||||
|
|
@ -184,6 +200,7 @@ struct call_expression : public expression {
|
|||
for (const auto& arg : this->args) chk_type<expression>(arg);
|
||||
}
|
||||
std::string type() const override { return "CallExpression"; }
|
||||
value execute(context & ctx) override;
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
@ -202,7 +219,7 @@ struct integer_literal : public expression {
|
|||
int64_t val;
|
||||
explicit integer_literal(int64_t val) : val(val) {}
|
||||
std::string type() const override { return "IntegerLiteral"; }
|
||||
value execute(context & ctx) override {
|
||||
value execute(context &) override {
|
||||
return std::make_unique<value_int_t>(val);
|
||||
}
|
||||
};
|
||||
|
|
@ -211,7 +228,7 @@ struct float_literal : public expression {
|
|||
double val;
|
||||
explicit float_literal(double val) : val(val) {}
|
||||
std::string type() const override { return "FloatLiteral"; }
|
||||
value execute(context & ctx) override {
|
||||
value execute(context &) override {
|
||||
return std::make_unique<value_float_t>(val);
|
||||
}
|
||||
};
|
||||
|
|
@ -220,7 +237,7 @@ struct string_literal : public expression {
|
|||
std::string val;
|
||||
explicit string_literal(const std::string & val) : val(val) {}
|
||||
std::string type() const override { return "StringLiteral"; }
|
||||
value execute(context & ctx) override {
|
||||
value execute(context &) override {
|
||||
return std::make_unique<value_string_t>(val);
|
||||
}
|
||||
};
|
||||
|
|
@ -300,7 +317,6 @@ struct filter_statement : public statement {
|
|||
chk_type<identifier, call_expression>(this->filter);
|
||||
}
|
||||
std::string type() const override { return "FilterStatement"; }
|
||||
value execute(context & ctx) override {}
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
@ -396,7 +412,6 @@ struct call_statement : public statement {
|
|||
for (const auto& arg : this->caller_args) chk_type<expression>(arg);
|
||||
}
|
||||
std::string type() const override { return "CallStatement"; }
|
||||
value execute(context & ctx) override {}
|
||||
};
|
||||
|
||||
struct ternary_expression : public expression {
|
||||
|
|
@ -413,6 +428,14 @@ struct ternary_expression : public expression {
|
|||
std::string type() const override { return "Ternary"; }
|
||||
};
|
||||
|
||||
struct raised_exception : public std::exception {
|
||||
std::string message;
|
||||
raised_exception(const std::string & msg) : message(msg) {}
|
||||
const char* what() const noexcept override {
|
||||
return message.c_str();
|
||||
}
|
||||
};
|
||||
|
||||
//////////////////////
|
||||
|
||||
struct vm {
|
||||
|
|
|
|||
|
|
@ -11,11 +11,11 @@
|
|||
#include "jinja/jinja-lexer.h"
|
||||
|
||||
int main(void) {
|
||||
//std::string contents = "{% if messages[0]['role'] == 'system' %}{{ raise_exception('System role not supported') }}{% endif %}{% for message in messages %}{% if (message['role'] == 'user') != (loop.index0 % 2 == 0) %}{{ raise_exception('Conversation roles must alternate user/assistant/user/assistant/...') }}{% endif %}{% if (message['role'] == 'assistant') %}{% set role = 'model' %}{% else %}{% set role = message['role'] %}{% endif %}{{ '<start_of_turn>' + role + '\\n' + message['content'] | trim + '<end_of_turn>\\n' }}{% endfor %}{% if add_generation_prompt %}{{'<start_of_turn>model\\n'}}{% endif %}";
|
||||
std::string contents = "{% if messages[0]['role'] == 'system' %}{{ raise_exception('System role not supported') }}{% endif %}{% for message in messages %}{% if (message['role'] == 'user') != (loop.index0 % 2 == 0) %}{{ raise_exception('Conversation roles must alternate user/assistant/user/assistant/...') }}{% endif %}{% if (message['role'] == 'assistant') %}{% set role = 'model' %}{% else %}{% set role = message['role'] %}{% endif %}{{ '<start_of_turn>' + role + '\\n' + message['content'] | trim + '<end_of_turn>\\n' }}{% endfor %}{% if add_generation_prompt %}{{'<start_of_turn>model\\n'}}{% endif %}";
|
||||
|
||||
//std::string contents = "{% if messages[0]['role'] != 'system' %}nice {{ messages[0]['content'] }}{% endif %}";
|
||||
|
||||
std::string contents = "<some_tokens> {{ messages[0]['content'] }} <another_token>";
|
||||
//std::string contents = "<some_tokens> {{ messages[0]['content'] }} <another_token>";
|
||||
|
||||
std::cout << "=== INPUT ===\n" << contents << "\n\n";
|
||||
|
||||
|
|
@ -34,11 +34,11 @@ int main(void) {
|
|||
std::cout << "stmt type: " << stmt->type() << "\n";
|
||||
}
|
||||
|
||||
std::cout << "\n=== OUTPUT ===\n";
|
||||
std::cout << "\n=== RUN ===\n";
|
||||
jinja::context ctx;
|
||||
|
||||
auto make_non_special_string = [](const std::string & s) {
|
||||
jinja::value_string str_val = std::make_unique<jinja::value_string_t>(s);
|
||||
jinja::value_string str_val = jinja::mk_val<jinja::value_string>(s);
|
||||
str_val->is_user_input = true;
|
||||
return str_val;
|
||||
};
|
||||
|
|
@ -57,7 +57,12 @@ int main(void) {
|
|||
|
||||
jinja::vm vm(ctx);
|
||||
auto results = vm.execute(ast);
|
||||
|
||||
std::cout << "\n=== RESULTS ===\n";
|
||||
for (const auto & res : results) {
|
||||
if (res->is_null()) {
|
||||
continue;
|
||||
}
|
||||
auto str_ptr = dynamic_cast<jinja::value_string_t*>(res.get());
|
||||
std::string is_user_input = "false";
|
||||
if (str_ptr) {
|
||||
|
|
|
|||
Loading…
Reference in New Issue