bin ops works!
This commit is contained in:
parent
8d1e9a0d12
commit
d8ef00e610
|
|
@ -15,12 +15,22 @@ struct value_t {
|
|||
double val_flt;
|
||||
std::string val_str;
|
||||
bool val_bool;
|
||||
std::vector<value> val_arr;
|
||||
std::map<std::string, value> val_obj;
|
||||
|
||||
// array and object are stored as shared_ptr to allow reference access
|
||||
// example:
|
||||
// my_obj = {"a": 1, "b": 2}
|
||||
// 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;
|
||||
|
||||
value_t() = default;
|
||||
value_t(const value_t &) = default;
|
||||
virtual ~value_t() = default;
|
||||
|
||||
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"); }
|
||||
|
|
@ -30,6 +40,10 @@ struct value_t {
|
|||
virtual bool is_null() const { return false; }
|
||||
virtual bool is_undefined() const { return false; }
|
||||
|
||||
virtual value clone() const {
|
||||
return std::make_unique<value_t>(*this);
|
||||
}
|
||||
|
||||
virtual bool operator==(const value & other) const {
|
||||
// TODO
|
||||
return false;
|
||||
|
|
@ -44,6 +58,8 @@ struct value_int_t : public value_t {
|
|||
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); }
|
||||
virtual std::string as_string() const override { return std::to_string(val_int); }
|
||||
virtual value clone() const override { return std::make_unique<value_int_t>(*this); }
|
||||
};
|
||||
using value_int = std::unique_ptr<value_int_t>;
|
||||
|
||||
|
|
@ -52,6 +68,8 @@ struct value_float_t : public value_t {
|
|||
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); }
|
||||
virtual std::string as_string() const override { return std::to_string(val_flt); }
|
||||
virtual value clone() const override { return std::make_unique<value_float_t>(*this); }
|
||||
};
|
||||
using value_float = std::unique_ptr<value_float_t>;
|
||||
|
||||
|
|
@ -59,6 +77,7 @@ 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; }
|
||||
virtual value clone() const override { return std::make_unique<value_string_t>(*this); }
|
||||
};
|
||||
using value_string = std::unique_ptr<value_string_t>;
|
||||
|
||||
|
|
@ -66,32 +85,81 @@ 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; }
|
||||
virtual std::string as_string() const override { return val_bool ? "True" : "False"; }
|
||||
virtual value clone() const override { return std::make_unique<value_bool_t>(*this); }
|
||||
};
|
||||
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); }
|
||||
value_array_t() {
|
||||
val_arr = std::make_shared<std::vector<value>>();
|
||||
}
|
||||
value_array_t(value & v) {
|
||||
// point to the same underlying data
|
||||
val_arr = v->val_arr;
|
||||
}
|
||||
value_array_t(value_array_t & other, size_t start = 0, size_t end = -1) {
|
||||
val_arr = std::make_shared<std::vector<value>>();
|
||||
size_t sz = other.val_arr->size();
|
||||
if (end == static_cast<size_t>(-1) || end > sz) {
|
||||
end = sz;
|
||||
}
|
||||
if (start > end || start >= sz) {
|
||||
return;
|
||||
}
|
||||
for (size_t i = start; i < end; i++) {
|
||||
val_arr->push_back(other.val_arr->at(i)->clone());
|
||||
}
|
||||
}
|
||||
virtual std::string type() const override { return "Array"; }
|
||||
virtual const std::vector<value> & as_array() const override { return val_arr; }
|
||||
virtual const std::vector<value> & as_array() const override { return *val_arr; }
|
||||
virtual value clone() const override {
|
||||
auto tmp = std::make_unique<value_array_t>();
|
||||
tmp->val_arr = this->val_arr;
|
||||
return tmp;
|
||||
}
|
||||
};
|
||||
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; }
|
||||
/*struct value_object_t : public value_t {
|
||||
value_object_t() {
|
||||
val_obj = std::make_shared<std::map<std::string, value>>();
|
||||
}
|
||||
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>>(obj);
|
||||
}
|
||||
virtual std::string type() const override { return "Object"; }
|
||||
virtual const std::map<std::string, value> & as_object() const override { return val_obj; }
|
||||
virtual const std::map<std::string, value> & as_object() const override { return *val_obj; }
|
||||
virtual value clone() const override {
|
||||
auto tmp = std::make_unique<value_object_t>();
|
||||
tmp->val_obj = this->val_obj;
|
||||
return tmp;
|
||||
}
|
||||
};
|
||||
using value_object = std::unique_ptr<value_object_t>;*/
|
||||
|
||||
struct value_object_t : public value_t {
|
||||
virtual std::string type() const override { return "TEST"; }
|
||||
virtual bool is_null() const override { return true; }
|
||||
virtual value clone() const override { return std::make_unique<value_object_t>(*this); }
|
||||
};
|
||||
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; }
|
||||
virtual value clone() const override { return std::make_unique<value_null_t>(*this); }
|
||||
};
|
||||
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 value clone() const override { return std::make_unique<value_undefined_t>(*this); }
|
||||
};
|
||||
using value_undefined = std::unique_ptr<value_undefined_t>;
|
||||
|
||||
|
|
|
|||
|
|
@ -9,22 +9,27 @@
|
|||
|
||||
namespace jinja {
|
||||
|
||||
// Helper to check type without asserting (useful for logic)
|
||||
// Helper to extract the inner type if T is unique_ptr<U>, else T itself
|
||||
template<typename T>
|
||||
static bool is_type(const value & ptr) {
|
||||
return dynamic_cast<const T*>(ptr.get()) != nullptr;
|
||||
struct extract_pointee {
|
||||
using type = T;
|
||||
};
|
||||
|
||||
template<typename U>
|
||||
struct extract_pointee<std::unique_ptr<U>> {
|
||||
using type = U;
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
static bool is_type(const value& ptr) {
|
||||
using PointeeType = typename extract_pointee<T>::type;
|
||||
return dynamic_cast<const PointeeType*>(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);
|
||||
}
|
||||
}
|
||||
};
|
||||
template<typename T>
|
||||
static bool is_stmt(const statement_ptr & ptr) {
|
||||
return dynamic_cast<const T*>(ptr.get()) != nullptr;
|
||||
}
|
||||
|
||||
value binary_expression::execute(context & ctx) {
|
||||
value left_val = left->execute(ctx);
|
||||
|
|
@ -97,13 +102,16 @@ value binary_expression::execute(context & ctx) {
|
|||
// 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));
|
||||
auto & left_arr = left_val->as_array();
|
||||
auto & right_arr = right_val->as_array();
|
||||
auto result = std::make_unique<value_array_t>();
|
||||
for (const auto & item : left_arr) {
|
||||
result->val_arr->push_back(item->clone());
|
||||
}
|
||||
return std::make_unique<value_array_t>(result);
|
||||
for (const auto & item : right_arr) {
|
||||
result->val_arr->push_back(item->clone());
|
||||
}
|
||||
return result;
|
||||
}
|
||||
} else if (is_type<value_array>(right_val)) {
|
||||
auto & arr = right_val->as_array();
|
||||
|
|
@ -148,4 +156,52 @@ value binary_expression::execute(context & ctx) {
|
|||
throw std::runtime_error("Unknown operator \"" + op.value + "\" between " + left_val->type() + " and " + right_val->type());
|
||||
}
|
||||
|
||||
value filter_expression::execute(context & ctx) {
|
||||
value input = operand->execute(ctx);
|
||||
value filter_func = filter->execute(ctx);
|
||||
|
||||
if (is_stmt<identifier>(filter)) {
|
||||
auto filter_val = dynamic_cast<identifier*>(filter.get())->value;
|
||||
|
||||
if (filter_val == "to_json") {
|
||||
// TODO: Implement to_json filter
|
||||
throw std::runtime_error("to_json filter not implemented");
|
||||
}
|
||||
|
||||
if (is_type<value_array>(input)) {
|
||||
auto & arr = input->as_array();
|
||||
if (filter_val == "list") {
|
||||
return std::make_unique<value_array_t>(input);
|
||||
} else if (filter_val == "first") {
|
||||
if (arr.empty()) {
|
||||
return std::make_unique<value_undefined_t>();
|
||||
}
|
||||
return arr[0]->clone();
|
||||
} else if (filter_val == "last") {
|
||||
if (arr.empty()) {
|
||||
return std::make_unique<value_undefined_t>();
|
||||
}
|
||||
return arr[arr.size() - 1]->clone();
|
||||
} else if (filter_val == "length") {
|
||||
return std::make_unique<value_int_t>(static_cast<int64_t>(arr.size()));
|
||||
} else {
|
||||
// TODO: reverse, sort, join, string, unique
|
||||
throw std::runtime_error("Unknown filter '" + filter_val + "' for array");
|
||||
}
|
||||
|
||||
} else if (is_type<value_string>(input)) {
|
||||
auto str = input->as_string();
|
||||
// TODO
|
||||
throw std::runtime_error("Unknown filter '" + filter_val + "' for string");
|
||||
|
||||
} else if (is_type<value_int>(input) || is_type<value_float>(input)) {
|
||||
// TODO
|
||||
throw std::runtime_error("Unknown filter '" + filter_val + "' for number");
|
||||
|
||||
} else {
|
||||
throw std::runtime_error("Filters not supported for type " + input->type());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace jinja
|
||||
|
|
|
|||
|
|
@ -23,7 +23,7 @@ struct context {
|
|||
struct statement {
|
||||
virtual ~statement() = default;
|
||||
virtual std::string type() const { return "Statement"; }
|
||||
virtual value execute(context & ctx) = 0;
|
||||
virtual value execute(context & ctx) { throw std::runtime_error("cannot exec " + type()); };
|
||||
};
|
||||
|
||||
using statement_ptr = std::unique_ptr<statement>;
|
||||
|
|
@ -186,44 +186,53 @@ struct identifier : public expression {
|
|||
// Literals
|
||||
|
||||
struct integer_literal : public expression {
|
||||
int64_t value;
|
||||
explicit integer_literal(int64_t value) : value(value) {}
|
||||
int64_t val;
|
||||
explicit integer_literal(int64_t val) : val(val) {}
|
||||
std::string type() const override { return "IntegerLiteral"; }
|
||||
value execute(context & ctx) override {
|
||||
return std::make_unique<value_int_t>(val);
|
||||
}
|
||||
};
|
||||
|
||||
struct float_literal : public expression {
|
||||
double value;
|
||||
explicit float_literal(double value) : value(value) {}
|
||||
double val;
|
||||
explicit float_literal(double val) : val(val) {}
|
||||
std::string type() const override { return "FloatLiteral"; }
|
||||
value execute(context & ctx) override {
|
||||
return std::make_unique<value_float_t>(val);
|
||||
}
|
||||
};
|
||||
|
||||
struct string_literal : public expression {
|
||||
std::string value;
|
||||
explicit string_literal(const std::string & value) : value(value) {}
|
||||
std::string val;
|
||||
explicit string_literal(const std::string & val) : val(val) {}
|
||||
std::string type() const override { return "StringLiteral"; }
|
||||
value execute(context & ctx) override {
|
||||
return std::make_unique<value_string_t>(val);
|
||||
}
|
||||
};
|
||||
|
||||
struct array_literal : public expression {
|
||||
statements value;
|
||||
explicit array_literal(statements && value) : value(std::move(value)) {
|
||||
for (const auto& item : this->value) chk_type<expression>(item);
|
||||
statements val;
|
||||
explicit array_literal(statements && val) : val(std::move(val)) {
|
||||
for (const auto& item : this->val) chk_type<expression>(item);
|
||||
}
|
||||
std::string type() const override { return "ArrayLiteral"; }
|
||||
};
|
||||
|
||||
struct tuple_literal : public expression {
|
||||
statements value;
|
||||
explicit tuple_literal(statements && value) : value(std::move(value)) {
|
||||
for (const auto& item : this->value) chk_type<expression>(item);
|
||||
statements 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"; }
|
||||
};
|
||||
|
||||
struct object_literal : public expression {
|
||||
std::vector<std::pair<statement_ptr, statement_ptr>> value;
|
||||
explicit object_literal(std::vector<std::pair<statement_ptr, statement_ptr>> && value)
|
||||
: value(std::move(value)) {
|
||||
for (const auto & pair : this->value) {
|
||||
std::vector<std::pair<statement_ptr, statement_ptr>> val;
|
||||
explicit object_literal(std::vector<std::pair<statement_ptr, statement_ptr>> && val)
|
||||
: val(std::move(val)) {
|
||||
for (const auto & pair : this->val) {
|
||||
chk_type<expression>(pair.first);
|
||||
chk_type<expression>(pair.second);
|
||||
}
|
||||
|
|
@ -391,4 +400,20 @@ struct ternary_expression : public expression {
|
|||
std::string type() const override { return "Ternary"; }
|
||||
};
|
||||
|
||||
//////////////////////
|
||||
|
||||
struct vm {
|
||||
context & ctx;
|
||||
explicit vm(context & ctx) : ctx(ctx) {}
|
||||
|
||||
std::vector<value> execute(program & prog) {
|
||||
std::vector<value> results;
|
||||
for (auto & stmt : prog.body) {
|
||||
value res = stmt->execute(ctx);
|
||||
results.push_back(std::move(res));
|
||||
}
|
||||
return results;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace jinja
|
||||
|
|
|
|||
|
|
@ -11,7 +11,9 @@
|
|||
#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 = "{{ 'hi' + 'fi' }}";
|
||||
|
||||
std::cout << "=== INPUT ===\n" << contents << "\n\n";
|
||||
|
||||
|
|
@ -24,11 +26,20 @@ int main(void) {
|
|||
std::cout << "token: type=" << static_cast<int>(tok.t) << " text='" << tok.value << "'\n";
|
||||
}
|
||||
|
||||
jinja::program ast = jinja::parse_from_tokens(tokens);
|
||||
std::cout << "\n=== AST ===\n";
|
||||
jinja::program ast = jinja::parse_from_tokens(tokens);
|
||||
for (const auto & stmt : ast.body) {
|
||||
std::cout << "stmt type: " << stmt->type() << "\n";
|
||||
}
|
||||
|
||||
std::cout << "\n=== OUTPUT ===\n";
|
||||
jinja::context ctx;
|
||||
jinja::vm vm(ctx);
|
||||
auto results = vm.execute(ast);
|
||||
for (const auto & res : results) {
|
||||
std::cout << "result type: " << res->type() << "\n";
|
||||
std::cout << "result value: " << res->as_string() << "\n";
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue