runtime.cpp 35 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865
  1. #include "lexer.h"
  2. #include "runtime.h"
  3. #include "value.h"
  4. #include "utils.h"
  5. #include <string>
  6. #include <vector>
  7. #include <memory>
  8. #include <cmath>
  9. #define FILENAME "jinja-runtime"
  10. bool g_jinja_debug = false;
  11. namespace jinja {
  12. void enable_debug(bool enable) {
  13. g_jinja_debug = enable;
  14. }
  15. static value_string exec_statements(const statements & stmts, context & ctx) {
  16. auto result = mk_val<value_array>();
  17. for (const auto & stmt : stmts) {
  18. JJ_DEBUG("Executing statement of type %s", stmt->type().c_str());
  19. result->push_back(stmt->execute(ctx));
  20. }
  21. // convert to string parts
  22. value_string str = mk_val<value_string>();
  23. gather_string_parts_recursive(result, str);
  24. return str;
  25. }
  26. static std::string get_line_col(const std::string & source, size_t pos) {
  27. size_t line = 1;
  28. size_t col = 1;
  29. for (size_t i = 0; i < pos && i < source.size(); i++) {
  30. if (source[i] == '\n') {
  31. line++;
  32. col = 1;
  33. } else {
  34. col++;
  35. }
  36. }
  37. return "line " + std::to_string(line) + ", column " + std::to_string(col);
  38. }
  39. // execute with error handling
  40. value statement::execute(context & ctx) {
  41. try {
  42. return execute_impl(ctx);
  43. } catch (const continue_statement::signal & /* ex */) {
  44. throw;
  45. } catch (const break_statement::signal & /* ex */) {
  46. throw;
  47. } catch (const rethrown_exception & /* ex */) {
  48. throw;
  49. } catch (const not_implemented_exception & /* ex */) {
  50. throw;
  51. } catch (const std::exception & e) {
  52. const std::string & source = *ctx.src;
  53. if (source.empty()) {
  54. std::ostringstream oss;
  55. oss << "\nError executing " << type() << " at position " << pos << ": " << e.what();
  56. throw rethrown_exception(oss.str());
  57. } else {
  58. std::ostringstream oss;
  59. oss << "\n------------\n";
  60. oss << "While executing " << type() << " at " << get_line_col(source, pos) << " in source:\n";
  61. oss << peak_source(source, pos) << "\n";
  62. oss << "Error: " << e.what();
  63. // throw as another exception to avoid repeated formatting
  64. throw rethrown_exception(oss.str());
  65. }
  66. }
  67. }
  68. value identifier::execute_impl(context & ctx) {
  69. auto it = ctx.get_val(val);
  70. auto builtins = global_builtins();
  71. if (!it->is_undefined()) {
  72. if (ctx.is_get_stats) {
  73. it->stats.used = true;
  74. }
  75. JJ_DEBUG("Identifier '%s' found, type = %s", val.c_str(), it->type().c_str());
  76. return it;
  77. } else if (builtins.find(val) != builtins.end()) {
  78. JJ_DEBUG("Identifier '%s' found in builtins", val.c_str());
  79. return mk_val<value_func>(val, builtins.at(val));
  80. } else {
  81. JJ_DEBUG("Identifier '%s' not found, returning undefined", val.c_str());
  82. return mk_val<value_undefined>(val);
  83. }
  84. }
  85. value object_literal::execute_impl(context & ctx) {
  86. auto obj = mk_val<value_object>();
  87. for (const auto & pair : val) {
  88. value key_val = pair.first->execute(ctx);
  89. if (!is_val<value_string>(key_val) && !is_val<value_int>(key_val)) {
  90. throw std::runtime_error("Object literal: keys must be string or int values, got " + key_val->type());
  91. }
  92. std::string key = key_val->as_string().str();
  93. value val = pair.second->execute(ctx);
  94. JJ_DEBUG("Object literal: setting key '%s' with value type %s", key.c_str(), val->type().c_str());
  95. obj->insert(key, val);
  96. if (is_val<value_int>(key_val)) {
  97. obj->val_obj.is_key_numeric = true;
  98. } else if (obj->val_obj.is_key_numeric) {
  99. throw std::runtime_error("Object literal: cannot mix numeric and non-numeric keys");
  100. }
  101. }
  102. return obj;
  103. }
  104. value binary_expression::execute_impl(context & ctx) {
  105. value left_val = left->execute(ctx);
  106. // Logical operators
  107. if (op.value == "and") {
  108. return left_val->as_bool() ? right->execute(ctx) : std::move(left_val);
  109. } else if (op.value == "or") {
  110. return left_val->as_bool() ? std::move(left_val) : right->execute(ctx);
  111. }
  112. // Equality operators
  113. value right_val = right->execute(ctx);
  114. JJ_DEBUG("Executing binary expression %s '%s' %s", left_val->type().c_str(), op.value.c_str(), right_val->type().c_str());
  115. if (op.value == "==") {
  116. return mk_val<value_bool>(value_compare(left_val, right_val, value_compare_op::eq));
  117. } else if (op.value == "!=") {
  118. return mk_val<value_bool>(!value_compare(left_val, right_val, value_compare_op::eq));
  119. }
  120. auto workaround_concat_null_with_str = [&](value & res) -> bool {
  121. bool is_left_null = left_val->is_none() || left_val->is_undefined();
  122. bool is_right_null = right_val->is_none() || right_val->is_undefined();
  123. bool is_left_str = is_val<value_string>(left_val);
  124. bool is_right_str = is_val<value_string>(right_val);
  125. if ((is_left_null && is_right_str) || (is_right_null && is_left_str)) {
  126. JJ_DEBUG("%s", "Workaround: treating null/undefined as empty string for string concatenation");
  127. string left_str = is_left_null ? string() : left_val->as_string();
  128. string right_str = is_right_null ? string() : right_val->as_string();
  129. auto output = left_str.append(right_str);
  130. res = mk_val<value_string>(std::move(output));
  131. return true;
  132. }
  133. return false;
  134. };
  135. // Handle undefined and null values
  136. if (is_val<value_undefined>(left_val) || is_val<value_undefined>(right_val)) {
  137. if (is_val<value_undefined>(right_val) && (op.value == "in" || op.value == "not in")) {
  138. // Special case: `anything in undefined` is `false` and `anything not in undefined` is `true`
  139. return mk_val<value_bool>(op.value == "not in");
  140. }
  141. if (op.value == "+" || op.value == "~") {
  142. value res = mk_val<value_undefined>();
  143. if (workaround_concat_null_with_str(res)) {
  144. return res;
  145. }
  146. }
  147. throw std::runtime_error("Cannot perform operation " + op.value + " on undefined values");
  148. } else if (is_val<value_none>(left_val) || is_val<value_none>(right_val)) {
  149. if (op.value == "+" || op.value == "~") {
  150. value res = mk_val<value_undefined>();
  151. if (workaround_concat_null_with_str(res)) {
  152. return res;
  153. }
  154. }
  155. throw std::runtime_error("Cannot perform operation on null values");
  156. }
  157. // Float operations
  158. if ((is_val<value_int>(left_val) || is_val<value_float>(left_val)) &&
  159. (is_val<value_int>(right_val) || is_val<value_float>(right_val))) {
  160. double a = left_val->as_float();
  161. double b = right_val->as_float();
  162. if (op.value == "+" || op.value == "-" || op.value == "*") {
  163. double res = (op.value == "+") ? a + b : (op.value == "-") ? a - b : a * b;
  164. JJ_DEBUG("Arithmetic operation: %f %s %f = %f", a, op.value.c_str(), b, res);
  165. bool is_float = is_val<value_float>(left_val) || is_val<value_float>(right_val);
  166. if (is_float) {
  167. return mk_val<value_float>(res);
  168. } else {
  169. return mk_val<value_int>(static_cast<int64_t>(res));
  170. }
  171. } else if (op.value == "/") {
  172. JJ_DEBUG("Division operation: %f / %f", a, b);
  173. return mk_val<value_float>(a / b);
  174. } else if (op.value == "%") {
  175. double rem = std::fmod(a, b);
  176. JJ_DEBUG("Modulo operation: %f %% %f = %f", a, b, rem);
  177. bool is_float = is_val<value_float>(left_val) || is_val<value_float>(right_val);
  178. if (is_float) {
  179. return mk_val<value_float>(rem);
  180. } else {
  181. return mk_val<value_int>(static_cast<int64_t>(rem));
  182. }
  183. } else if (op.value == "<") {
  184. JJ_DEBUG("Comparison operation: %f < %f is %d", a, b, a < b);
  185. return mk_val<value_bool>(a < b);
  186. } else if (op.value == ">") {
  187. JJ_DEBUG("Comparison operation: %f > %f is %d", a, b, a > b);
  188. return mk_val<value_bool>(a > b);
  189. } else if (op.value == ">=") {
  190. JJ_DEBUG("Comparison operation: %f >= %f is %d", a, b, a >= b);
  191. return mk_val<value_bool>(a >= b);
  192. } else if (op.value == "<=") {
  193. JJ_DEBUG("Comparison operation: %f <= %f is %d", a, b, a <= b);
  194. return mk_val<value_bool>(a <= b);
  195. }
  196. }
  197. // Array operations
  198. if (is_val<value_array>(left_val) && is_val<value_array>(right_val)) {
  199. if (op.value == "+") {
  200. auto & left_arr = left_val->as_array();
  201. auto & right_arr = right_val->as_array();
  202. auto result = mk_val<value_array>();
  203. for (const auto & item : left_arr) {
  204. result->push_back(item);
  205. }
  206. for (const auto & item : right_arr) {
  207. result->push_back(item);
  208. }
  209. return result;
  210. }
  211. } else if (is_val<value_array>(right_val)) {
  212. auto & arr = right_val->as_array();
  213. bool member = false;
  214. for (const auto & item : arr) {
  215. if (value_compare(left_val, item, value_compare_op::eq)) {
  216. member = true;
  217. break;
  218. }
  219. }
  220. if (op.value == "in") {
  221. JJ_DEBUG("Checking membership: %s in Array is %d", left_val->type().c_str(), member);
  222. return mk_val<value_bool>(member);
  223. } else if (op.value == "not in") {
  224. JJ_DEBUG("Checking non-membership: %s not in Array is %d", left_val->type().c_str(), !member);
  225. return mk_val<value_bool>(!member);
  226. }
  227. }
  228. // String concatenation with ~ and +
  229. if ((is_val<value_string>(left_val) || is_val<value_string>(right_val)) &&
  230. (op.value == "~" || op.value == "+")) {
  231. JJ_DEBUG("String concatenation with %s operator", op.value.c_str());
  232. auto output = left_val->as_string().append(right_val->as_string());
  233. auto res = mk_val<value_string>();
  234. res->val_str = std::move(output);
  235. return res;
  236. }
  237. // String membership
  238. if (is_val<value_string>(left_val) && is_val<value_string>(right_val)) {
  239. auto left_str = left_val->as_string().str();
  240. auto right_str = right_val->as_string().str();
  241. if (op.value == "in") {
  242. return mk_val<value_bool>(right_str.find(left_str) != std::string::npos);
  243. } else if (op.value == "not in") {
  244. return mk_val<value_bool>(right_str.find(left_str) == std::string::npos);
  245. }
  246. }
  247. // String in object
  248. if (is_val<value_string>(left_val) && is_val<value_object>(right_val)) {
  249. auto key = left_val->as_string().str();
  250. bool has_key = right_val->has_key(key);
  251. if (op.value == "in") {
  252. return mk_val<value_bool>(has_key);
  253. } else if (op.value == "not in") {
  254. return mk_val<value_bool>(!has_key);
  255. }
  256. }
  257. throw std::runtime_error("Unknown operator \"" + op.value + "\" between " + left_val->type() + " and " + right_val->type());
  258. }
  259. static value try_builtin_func(context & ctx, const std::string & name, value & input, bool undef_on_missing = false) {
  260. JJ_DEBUG("Trying built-in function '%s' for type %s", name.c_str(), input->type().c_str());
  261. if (ctx.is_get_stats) {
  262. input->stats.used = true;
  263. input->stats.ops.insert(name);
  264. }
  265. auto builtins = input->get_builtins();
  266. auto it = builtins.find(name);
  267. if (it != builtins.end()) {
  268. JJ_DEBUG("Binding built-in '%s'", name.c_str());
  269. return mk_val<value_func>(name, it->second, input);
  270. }
  271. if (undef_on_missing) {
  272. return mk_val<value_undefined>(name);
  273. }
  274. throw std::runtime_error("Unknown (built-in) filter '" + name + "' for type " + input->type());
  275. }
  276. value filter_expression::execute_impl(context & ctx) {
  277. value input = operand ? operand->execute(ctx) : val;
  278. JJ_DEBUG("Applying filter to %s", input->type().c_str());
  279. if (is_stmt<identifier>(filter)) {
  280. auto filter_id = cast_stmt<identifier>(filter)->val;
  281. if (filter_id == "trim") {
  282. filter_id = "strip"; // alias
  283. }
  284. JJ_DEBUG("Applying filter '%s' to %s", filter_id.c_str(), input->type().c_str());
  285. return try_builtin_func(ctx, filter_id, input)->invoke(func_args(ctx));
  286. } else if (is_stmt<call_expression>(filter)) {
  287. auto call = cast_stmt<call_expression>(filter);
  288. if (!is_stmt<identifier>(call->callee)) {
  289. throw std::runtime_error("Filter callee must be an identifier");
  290. }
  291. auto filter_id = cast_stmt<identifier>(call->callee)->val;
  292. if (filter_id == "trim") {
  293. filter_id = "strip"; // alias
  294. }
  295. JJ_DEBUG("Applying filter '%s' with arguments to %s", filter_id.c_str(), input->type().c_str());
  296. func_args args(ctx);
  297. for (const auto & arg_expr : call->args) {
  298. args.push_back(arg_expr->execute(ctx));
  299. }
  300. return try_builtin_func(ctx, filter_id, input)->invoke(args);
  301. } else {
  302. throw std::runtime_error("Invalid filter expression");
  303. }
  304. }
  305. value filter_statement::execute_impl(context & ctx) {
  306. // eval body as string, then apply filter
  307. auto body_val = exec_statements(body, ctx);
  308. value_string parts = mk_val<value_string>();
  309. gather_string_parts_recursive(body_val, parts);
  310. JJ_DEBUG("FilterStatement: applying filter to body string of length %zu", parts->val_str.length());
  311. filter_expression filter_expr(std::move(parts), std::move(filter));
  312. value out = filter_expr.execute(ctx);
  313. // this node can be reused later, make sure filter is preserved
  314. this->filter = std::move(filter_expr.filter);
  315. return out;
  316. }
  317. value test_expression::execute_impl(context & ctx) {
  318. // NOTE: "value is something" translates to function call "test_is_something(value)"
  319. const auto & builtins = global_builtins();
  320. std::string test_id;
  321. value input = operand->execute(ctx);
  322. func_args args(ctx);
  323. args.push_back(input);
  324. if (is_stmt<identifier>(test)) {
  325. test_id = cast_stmt<identifier>(test)->val;
  326. } else if (is_stmt<call_expression>(test)) {
  327. auto call = cast_stmt<call_expression>(test);
  328. if (!is_stmt<identifier>(call->callee)) {
  329. throw std::runtime_error("Test callee must be an identifier");
  330. }
  331. test_id = cast_stmt<identifier>(call->callee)->val;
  332. JJ_DEBUG("Applying test '%s' with arguments to %s", test_id.c_str(), input->type().c_str());
  333. for (const auto & arg_expr : call->args) {
  334. args.push_back(arg_expr->execute(ctx));
  335. }
  336. } else {
  337. throw std::runtime_error("Invalid test expression");
  338. }
  339. auto it = builtins.find("test_is_" + test_id);
  340. 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());
  341. if (it == builtins.end()) {
  342. throw std::runtime_error("Unknown test '" + test_id + "'");
  343. }
  344. auto res = it->second(args);
  345. if (negate) {
  346. return mk_val<value_bool>(!res->as_bool());
  347. } else {
  348. return res;
  349. }
  350. }
  351. value unary_expression::execute_impl(context & ctx) {
  352. value operand_val = argument->execute(ctx);
  353. JJ_DEBUG("Executing unary expression with operator '%s'", op.value.c_str());
  354. if (op.value == "not") {
  355. return mk_val<value_bool>(!operand_val->as_bool());
  356. } else if (op.value == "-") {
  357. if (is_val<value_int>(operand_val)) {
  358. return mk_val<value_int>(-operand_val->as_int());
  359. } else if (is_val<value_float>(operand_val)) {
  360. return mk_val<value_float>(-operand_val->as_float());
  361. } else {
  362. throw std::runtime_error("Unary - operator requires numeric operand");
  363. }
  364. }
  365. throw std::runtime_error("Unknown unary operator '" + op.value + "'");
  366. }
  367. value if_statement::execute_impl(context & ctx) {
  368. value test_val = test->execute(ctx);
  369. auto out = mk_val<value_array>();
  370. if (test_val->as_bool()) {
  371. for (auto & stmt : body) {
  372. JJ_DEBUG("IF --> Executing THEN body, current block: %s", stmt->type().c_str());
  373. out->push_back(stmt->execute(ctx));
  374. }
  375. } else {
  376. for (auto & stmt : alternate) {
  377. JJ_DEBUG("IF --> Executing ELSE body, current block: %s", stmt->type().c_str());
  378. out->push_back(stmt->execute(ctx));
  379. }
  380. }
  381. // convert to string parts
  382. value_string str = mk_val<value_string>();
  383. gather_string_parts_recursive(out, str);
  384. return str;
  385. }
  386. value for_statement::execute_impl(context & ctx) {
  387. context scope(ctx); // new scope for loop variables
  388. jinja::select_expression * select_expr = cast_stmt<select_expression>(iterable);
  389. statement_ptr test_expr_nullptr;
  390. statement_ptr & iter_expr = [&]() -> statement_ptr & {
  391. auto tmp = cast_stmt<select_expression>(iterable);
  392. return tmp ? tmp->lhs : iterable;
  393. }();
  394. statement_ptr & test_expr = [&]() -> statement_ptr & {
  395. auto tmp = cast_stmt<select_expression>(iterable);
  396. return tmp ? tmp->test : test_expr_nullptr;
  397. }();
  398. JJ_DEBUG("Executing for statement, iterable type: %s", iter_expr->type().c_str());
  399. value iterable_val = iter_expr->execute(scope);
  400. if (iterable_val->is_undefined()) {
  401. JJ_DEBUG("%s", "For loop iterable is undefined, skipping loop");
  402. iterable_val = mk_val<value_array>();
  403. }
  404. if (!is_val<value_array>(iterable_val) && !is_val<value_object>(iterable_val)) {
  405. throw std::runtime_error("Expected iterable or object type in for loop: got " + iterable_val->type());
  406. }
  407. std::vector<value> items;
  408. if (is_val<value_object>(iterable_val)) {
  409. JJ_DEBUG("%s", "For loop over object keys");
  410. auto & obj = iterable_val->as_ordered_object();
  411. for (auto & p : obj) {
  412. auto tuple = mk_val<value_array>();
  413. if (iterable_val->val_obj.is_key_numeric) {
  414. tuple->push_back(mk_val<value_int>(std::stoll(p.first)));
  415. } else {
  416. tuple->push_back(mk_val<value_string>(p.first));
  417. }
  418. tuple->push_back(p.second);
  419. items.push_back(tuple);
  420. }
  421. if (ctx.is_get_stats) {
  422. iterable_val->stats.used = true;
  423. iterable_val->stats.ops.insert("object_access");
  424. }
  425. } else {
  426. JJ_DEBUG("%s", "For loop over array items");
  427. auto & arr = iterable_val->as_array();
  428. for (const auto & item : arr) {
  429. items.push_back(item);
  430. }
  431. if (ctx.is_get_stats) {
  432. iterable_val->stats.used = true;
  433. iterable_val->stats.ops.insert("array_access");
  434. }
  435. }
  436. std::vector<std::function<void(context &)>> scope_update_fns;
  437. std::vector<value> filtered_items;
  438. for (size_t i = 0; i < items.size(); ++i) {
  439. context loop_scope(scope);
  440. value current = items[i];
  441. std::function<void(context&)> scope_update_fn = [](context &) { /* no-op */};
  442. if (is_stmt<identifier>(loopvar)) {
  443. auto id = cast_stmt<identifier>(loopvar)->val;
  444. if (is_val<value_object>(iterable_val)) {
  445. // case example: {% for key in dict %}
  446. current = items[i]->as_array()[0];
  447. scope_update_fn = [id, &items, i](context & ctx) {
  448. ctx.set_val(id, items[i]->as_array()[0]);
  449. };
  450. } else {
  451. // case example: {% for item in list %}
  452. scope_update_fn = [id, &items, i](context & ctx) {
  453. ctx.set_val(id, items[i]);
  454. };
  455. }
  456. } else if (is_stmt<tuple_literal>(loopvar)) {
  457. // case example: {% for key, value in dict %}
  458. auto tuple = cast_stmt<tuple_literal>(loopvar);
  459. if (!is_val<value_array>(current)) {
  460. throw std::runtime_error("Cannot unpack non-iterable type: " + current->type());
  461. }
  462. auto & c_arr = current->as_array();
  463. if (tuple->val.size() != c_arr.size()) {
  464. throw std::runtime_error(std::string("Too ") + (tuple->val.size() > c_arr.size() ? "few" : "many") + " items to unpack");
  465. }
  466. scope_update_fn = [tuple, &items, i](context & ctx) {
  467. auto & c_arr = items[i]->as_array();
  468. for (size_t j = 0; j < tuple->val.size(); ++j) {
  469. if (!is_stmt<identifier>(tuple->val[j])) {
  470. throw std::runtime_error("Cannot unpack non-identifier type: " + tuple->val[j]->type());
  471. }
  472. auto id = cast_stmt<identifier>(tuple->val[j])->val;
  473. ctx.set_val(id, c_arr[j]);
  474. }
  475. };
  476. } else {
  477. throw std::runtime_error("Invalid loop variable(s): " + loopvar->type());
  478. }
  479. if (select_expr && test_expr) {
  480. scope_update_fn(loop_scope);
  481. value test_val = test_expr->execute(loop_scope);
  482. if (!test_val->as_bool()) {
  483. continue;
  484. }
  485. }
  486. JJ_DEBUG("For loop: adding item type %s at index %zu", current->type().c_str(), i);
  487. filtered_items.push_back(current);
  488. scope_update_fns.push_back(scope_update_fn);
  489. }
  490. JJ_DEBUG("For loop: %zu items after filtering", filtered_items.size());
  491. auto result = mk_val<value_array>();
  492. bool noIteration = true;
  493. for (size_t i = 0; i < filtered_items.size(); i++) {
  494. JJ_DEBUG("For loop iteration %zu/%zu", i + 1, filtered_items.size());
  495. value_object loop_obj = mk_val<value_object>();
  496. loop_obj->has_builtins = false; // loop object has no builtins
  497. loop_obj->insert("index", mk_val<value_int>(i + 1));
  498. loop_obj->insert("index0", mk_val<value_int>(i));
  499. loop_obj->insert("revindex", mk_val<value_int>(filtered_items.size() - i));
  500. loop_obj->insert("revindex0", mk_val<value_int>(filtered_items.size() - i - 1));
  501. loop_obj->insert("first", mk_val<value_bool>(i == 0));
  502. loop_obj->insert("last", mk_val<value_bool>(i == filtered_items.size() - 1));
  503. loop_obj->insert("length", mk_val<value_int>(filtered_items.size()));
  504. loop_obj->insert("previtem", i > 0 ? filtered_items[i - 1] : mk_val<value_undefined>("previtem"));
  505. loop_obj->insert("nextitem", i < filtered_items.size() - 1 ? filtered_items[i + 1] : mk_val<value_undefined>("nextitem"));
  506. scope.set_val("loop", loop_obj);
  507. scope_update_fns[i](scope);
  508. try {
  509. for (auto & stmt : body) {
  510. value val = stmt->execute(scope);
  511. result->push_back(val);
  512. }
  513. } catch (const continue_statement::signal &) {
  514. continue;
  515. } catch (const break_statement::signal &) {
  516. break;
  517. }
  518. noIteration = false;
  519. }
  520. JJ_DEBUG("For loop complete, total iterations: %zu", filtered_items.size());
  521. if (noIteration) {
  522. for (auto & stmt : default_block) {
  523. value val = stmt->execute(ctx);
  524. result->push_back(val);
  525. }
  526. }
  527. // convert to string parts
  528. value_string str = mk_val<value_string>();
  529. gather_string_parts_recursive(result, str);
  530. return str;
  531. }
  532. value set_statement::execute_impl(context & ctx) {
  533. auto rhs = val ? val->execute(ctx) : exec_statements(body, ctx);
  534. if (is_stmt<identifier>(assignee)) {
  535. auto var_name = cast_stmt<identifier>(assignee)->val;
  536. JJ_DEBUG("Setting global variable '%s' with value type %s", var_name.c_str(), rhs->type().c_str());
  537. ctx.set_val(var_name, rhs);
  538. } else if (is_stmt<tuple_literal>(assignee)) {
  539. auto tuple = cast_stmt<tuple_literal>(assignee);
  540. if (!is_val<value_array>(rhs)) {
  541. throw std::runtime_error("Cannot unpack non-iterable type in set: " + rhs->type());
  542. }
  543. auto & arr = rhs->as_array();
  544. if (arr.size() != tuple->val.size()) {
  545. throw std::runtime_error(std::string("Too ") + (tuple->val.size() > arr.size() ? "few" : "many") + " items to unpack in set");
  546. }
  547. for (size_t i = 0; i < tuple->val.size(); ++i) {
  548. auto & elem = tuple->val[i];
  549. if (!is_stmt<identifier>(elem)) {
  550. throw std::runtime_error("Cannot unpack to non-identifier in set: " + elem->type());
  551. }
  552. auto var_name = cast_stmt<identifier>(elem)->val;
  553. ctx.set_val(var_name, arr[i]);
  554. }
  555. } else if (is_stmt<member_expression>(assignee)) {
  556. auto member = cast_stmt<member_expression>(assignee);
  557. if (member->computed) {
  558. throw std::runtime_error("Cannot assign to computed member");
  559. }
  560. if (!is_stmt<identifier>(member->property)) {
  561. throw std::runtime_error("Cannot assign to member with non-identifier property");
  562. }
  563. auto prop_name = cast_stmt<identifier>(member->property)->val;
  564. value object = member->object->execute(ctx);
  565. if (!is_val<value_object>(object)) {
  566. throw std::runtime_error("Cannot assign to member of non-object");
  567. }
  568. auto obj_ptr = cast_val<value_object>(object);
  569. JJ_DEBUG("Setting object property '%s' with value type %s", prop_name.c_str(), rhs->type().c_str());
  570. obj_ptr->insert(prop_name, rhs);
  571. } else {
  572. throw std::runtime_error("Invalid LHS inside assignment expression: " + assignee->type());
  573. }
  574. return mk_val<value_undefined>();
  575. }
  576. value macro_statement::execute_impl(context & ctx) {
  577. if (!is_stmt<identifier>(this->name)) {
  578. throw std::runtime_error("Macro name must be an identifier");
  579. }
  580. std::string name = cast_stmt<identifier>(this->name)->val;
  581. const func_handler func = [this, name, &ctx](const func_args & args) -> value {
  582. size_t expected_count = this->args.size();
  583. size_t input_count = args.count();
  584. JJ_DEBUG("Invoking macro '%s' with %zu input arguments (expected %zu)", name.c_str(), input_count, expected_count);
  585. context macro_ctx(ctx); // new scope for macro execution
  586. // bind parameters
  587. for (size_t i = 0; i < expected_count; ++i) {
  588. if (i < input_count) {
  589. if (is_stmt<identifier>(this->args[i])) {
  590. // normal parameter
  591. std::string param_name = cast_stmt<identifier>(this->args[i])->val;
  592. JJ_DEBUG(" Binding parameter '%s' to argument of type %s", param_name.c_str(), args.get_pos(i)->type().c_str());
  593. macro_ctx.set_val(param_name, args.get_pos(i));
  594. } else if (is_stmt<keyword_argument_expression>(this->args[i])) {
  595. // default argument used as normal parameter
  596. auto kwarg = cast_stmt<keyword_argument_expression>(this->args[i]);
  597. if (!is_stmt<identifier>(kwarg->key)) {
  598. throw std::runtime_error("Keyword argument key must be an identifier in macro '" + name + "'");
  599. }
  600. std::string param_name = cast_stmt<identifier>(kwarg->key)->val;
  601. JJ_DEBUG(" Binding parameter '%s' to argument of type %s", param_name.c_str(), args.get_pos(i)->type().c_str());
  602. macro_ctx.set_val(param_name, args.get_pos(i));
  603. } else {
  604. throw std::runtime_error("Invalid parameter type in macro '" + name + "'");
  605. }
  606. } else {
  607. auto & default_arg = this->args[i];
  608. if (is_stmt<keyword_argument_expression>(default_arg)) {
  609. auto kwarg = cast_stmt<keyword_argument_expression>(default_arg);
  610. if (!is_stmt<identifier>(kwarg->key)) {
  611. throw std::runtime_error("Keyword argument key must be an identifier in macro '" + name + "'");
  612. }
  613. std::string param_name = cast_stmt<identifier>(kwarg->key)->val;
  614. JJ_DEBUG(" Binding parameter '%s' to default argument of type %s", param_name.c_str(), kwarg->val->type().c_str());
  615. macro_ctx.set_val(param_name, kwarg->val->execute(ctx));
  616. } else {
  617. throw std::runtime_error("Not enough arguments provided to macro '" + name + "'");
  618. }
  619. //std::string param_name = cast_stmt<identifier>(default_args[i])->val;
  620. //JJ_DEBUG(" Binding parameter '%s' to default", param_name.c_str());
  621. //macro_ctx.var[param_name] = default_args[i]->execute(ctx);
  622. }
  623. }
  624. // execute macro body
  625. JJ_DEBUG("Executing macro '%s' body with %zu statements", name.c_str(), this->body.size());
  626. auto res = exec_statements(this->body, macro_ctx);
  627. JJ_DEBUG("Macro '%s' execution complete, result: %s", name.c_str(), res->val_str.str().c_str());
  628. return res;
  629. };
  630. JJ_DEBUG("Defining macro '%s' with %zu parameters", name.c_str(), args.size());
  631. ctx.set_val(name, mk_val<value_func>(name, func));
  632. return mk_val<value_undefined>();
  633. }
  634. value member_expression::execute_impl(context & ctx) {
  635. value object = this->object->execute(ctx);
  636. value property;
  637. if (this->computed) {
  638. // syntax: obj[expr]
  639. JJ_DEBUG("Member expression, computing property type %s", this->property->type().c_str());
  640. int64_t arr_size = 0;
  641. if (is_val<value_array>(object)) {
  642. arr_size = object->as_array().size();
  643. }
  644. if (is_stmt<slice_expression>(this->property)) {
  645. auto s = cast_stmt<slice_expression>(this->property);
  646. value start_val = s->start_expr ? s->start_expr->execute(ctx) : mk_val<value_int>(0);
  647. value stop_val = s->stop_expr ? s->stop_expr->execute(ctx) : mk_val<value_int>(arr_size);
  648. value step_val = s->step_expr ? s->step_expr->execute(ctx) : mk_val<value_int>(1);
  649. // translate to function call: obj.slice(start, stop, step)
  650. JJ_DEBUG("Member expression is a slice: start %s, stop %s, step %s",
  651. start_val->as_repr().c_str(),
  652. stop_val->as_repr().c_str(),
  653. step_val->as_repr().c_str());
  654. auto slice_func = try_builtin_func(ctx, "slice", object);
  655. func_args args(ctx);
  656. args.push_back(start_val);
  657. args.push_back(stop_val);
  658. args.push_back(step_val);
  659. return slice_func->invoke(args);
  660. } else {
  661. property = this->property->execute(ctx);
  662. }
  663. } else {
  664. // syntax: obj.prop
  665. if (!is_stmt<identifier>(this->property)) {
  666. throw std::runtime_error("Static member property must be an identifier");
  667. }
  668. property = mk_val<value_string>(cast_stmt<identifier>(this->property)->val);
  669. std::string prop = property->as_string().str();
  670. JJ_DEBUG("Member expression, object type %s, static property '%s'", object->type().c_str(), prop.c_str());
  671. // behavior of jinja2: obj having prop as a built-in function AND 'prop', as an object key,
  672. // then obj.prop returns the built-in function, not the property value.
  673. // while obj['prop'] returns the property value.
  674. // example: {"obj": {"items": 123}} -> obj.items is the built-in function, obj['items'] is 123
  675. value val = try_builtin_func(ctx, prop, object, true);
  676. if (!is_val<value_undefined>(val)) {
  677. return val;
  678. }
  679. // else, fallthrough to normal property access below
  680. }
  681. JJ_DEBUG("Member expression on object type %s, property type %s", object->type().c_str(), property->type().c_str());
  682. value val = mk_val<value_undefined>("object_property");
  683. if (is_val<value_undefined>(object)) {
  684. JJ_DEBUG("%s", "Accessing property on undefined object, returning undefined");
  685. return val;
  686. } else if (is_val<value_object>(object)) {
  687. if (!is_val<value_string>(property)) {
  688. throw std::runtime_error("Cannot access object with non-string: got " + property->type());
  689. }
  690. auto key = property->as_string().str();
  691. val = object->at(key, val);
  692. if (is_val<value_undefined>(val)) {
  693. val = try_builtin_func(ctx, key, object, true);
  694. }
  695. JJ_DEBUG("Accessed property '%s' value, got type: %s", key.c_str(), val->type().c_str());
  696. } else if (is_val<value_array>(object) || is_val<value_string>(object)) {
  697. if (is_val<value_int>(property)) {
  698. int64_t index = property->as_int();
  699. JJ_DEBUG("Accessing %s index %d", object->type().c_str(), (int)index);
  700. if (is_val<value_array>(object)) {
  701. auto & arr = object->as_array();
  702. if (index < 0) {
  703. index += static_cast<int64_t>(arr.size());
  704. }
  705. if (index >= 0 && index < static_cast<int64_t>(arr.size())) {
  706. val = arr[index];
  707. }
  708. } else { // value_string
  709. auto str = object->as_string().str();
  710. if (index >= 0 && index < static_cast<int64_t>(str.size())) {
  711. val = mk_val<value_string>(std::string(1, str[index]));
  712. }
  713. }
  714. } else if (is_val<value_string>(property)) {
  715. auto key = property->as_string().str();
  716. JJ_DEBUG("Accessing %s built-in '%s'", is_val<value_array>(object) ? "array" : "string", key.c_str());
  717. val = try_builtin_func(ctx, key, object, true);
  718. } else {
  719. throw std::runtime_error("Cannot access property with non-string/non-number: got " + property->type());
  720. }
  721. } else {
  722. if (!is_val<value_string>(property)) {
  723. throw std::runtime_error("Cannot access property with non-string: got " + property->type());
  724. }
  725. auto key = property->as_string().str();
  726. val = try_builtin_func(ctx, key, object, true);
  727. }
  728. if (ctx.is_get_stats && val && object && property) {
  729. val->stats.used = true;
  730. object->stats.used = true;
  731. if (is_val<value_int>(property)) {
  732. object->stats.ops.insert("array_access");
  733. } else if (is_val<value_string>(property)) {
  734. object->stats.ops.insert("object_access");
  735. }
  736. }
  737. return val;
  738. }
  739. value call_expression::execute_impl(context & ctx) {
  740. // gather arguments
  741. func_args args(ctx);
  742. for (auto & arg_stmt : this->args) {
  743. auto arg_val = arg_stmt->execute(ctx);
  744. JJ_DEBUG(" Argument type: %s", arg_val->type().c_str());
  745. args.push_back(std::move(arg_val));
  746. }
  747. // execute callee
  748. value callee_val = callee->execute(ctx);
  749. if (!is_val<value_func>(callee_val)) {
  750. throw std::runtime_error("Callee is not a function: got " + callee_val->type());
  751. }
  752. auto * callee_func = cast_val<value_func>(callee_val);
  753. JJ_DEBUG("Calling function '%s' with %zu arguments", callee_func->name.c_str(), args.count());
  754. return callee_func->invoke(args);
  755. }
  756. value keyword_argument_expression::execute_impl(context & ctx) {
  757. if (!is_stmt<identifier>(key)) {
  758. throw std::runtime_error("Keyword argument key must be identifiers");
  759. }
  760. std::string k = cast_stmt<identifier>(key)->val;
  761. JJ_DEBUG("Keyword argument expression key: %s, value: %s", k.c_str(), val->type().c_str());
  762. value v = val->execute(ctx);
  763. JJ_DEBUG("Keyword argument value executed, type: %s", v->type().c_str());
  764. return mk_val<value_kwarg>(k, v);
  765. }
  766. } // namespace jinja