Line data Source code
1 : /**
2 : Copyright (c) 2024 Stappler LLC <admin@stappler.dev>
3 :
4 : Permission is hereby granted, free of charge, to any person obtaining a copy
5 : of this software and associated documentation files (the "Software"), to deal
6 : in the Software without restriction, including without limitation the rights
7 : to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8 : copies of the Software, and to permit persons to whom the Software is
9 : furnished to do so, subject to the following conditions:
10 :
11 : The above copyright notice and this permission notice shall be included in
12 : all copies or substantial portions of the Software.
13 :
14 : THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 : IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 : FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 : AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 : LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19 : OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20 : THE SOFTWARE.
21 : **/
22 :
23 : #include "SPPugContext.h"
24 : #include "SPPugVariable.h"
25 :
26 : namespace STAPPLER_VERSIONIZED stappler::pug {
27 :
28 : struct ContextFn {
29 : using OutStream = Callback<void(StringView)>;
30 : using VarScope = Context::VarScope;
31 :
32 : template <typename Callback>
33 : bool extractValue(Var &var, const Callback &cb);
34 :
35 : template <typename Callback>
36 : bool extractName(Var &var, const Callback &cb);
37 :
38 : Var execOperator(const Expression &expr, Expression::Op op);
39 : Var execComposition(const Expression &expr, Expression::Op op);
40 :
41 : size_t prepareArgsList(Context::VarList &, const Expression &expr, Expression::Block block);
42 :
43 : Var performVarExpr(const Expression &expr, Expression::Op op);
44 : Var performUnaryOp(Var &v, Expression::Op op);
45 : Var performBinaryOp(Var &l, Var &r, Expression::Op op);
46 :
47 : Var execExpr(const Expression &expr, Expression::Op op, bool assignable);
48 : bool printExpr(const Expression &expr, Expression::Op op, const OutStream &out);
49 :
50 : Value *getMutableValue(Var &val) const;
51 : void onError(const StringView &) const;
52 :
53 : Var getVar(const StringView &key) const;
54 : Var getVar(VarScope *, const StringView &key) const;
55 :
56 : const VarStorage *getVarStorage(VarScope *, const StringView &key) const;
57 :
58 : VarScope *currentScope = nullptr;
59 : const Callback<void(StringView)> *outStream = nullptr;
60 : bool escapeOutput = false;
61 : bool allowUndefined = false;
62 : };
63 :
64 : template <typename Callback>
65 800 : bool ContextFn::extractValue(Var &var, const Callback &cb) {
66 800 : switch (var.type) {
67 0 : case Var::Temporary:
68 0 : switch (var.temporaryStorage.type) {
69 0 : case VarStorage::ValueReference:
70 0 : if (auto mut = var.temporaryStorage.getMutable()) {
71 0 : return cb(move(*mut));
72 : } else {
73 0 : return cb(Value(var.temporaryStorage.readValue()));
74 : }
75 : break;
76 0 : default:
77 0 : onError("Dereference of non-value type is forbidden");
78 0 : break;
79 : }
80 0 : break;
81 775 : case Var::Static:
82 775 : if (auto mut = var.staticStorage.getMutable()) {
83 50 : return cb(move(*mut));
84 : } else {
85 725 : return cb(Value(var.staticStorage.readValue()));
86 : }
87 : break;
88 25 : case Var::Variable:
89 25 : switch (var.variableStorage->type) {
90 25 : case VarStorage::ValueReference:
91 25 : return cb(Value(var.variableStorage->readValue()));
92 : break;
93 0 : default:
94 0 : onError("Dereference of non-value type is forbidden");
95 0 : break;
96 : }
97 0 : break;
98 0 : default:
99 0 : onError("Dereference of undefined is forbidden");
100 0 : break;
101 : }
102 0 : return false;
103 : }
104 :
105 : template <typename Callback>
106 425 : bool ContextFn::extractName(Var &var, const Callback &cb) {
107 425 : switch (var.type) {
108 0 : case Var::Temporary:
109 : case Var::Variable:
110 0 : switch (var.temporaryStorage.type) {
111 0 : case VarStorage::ValueReference:
112 0 : return cb(var.temporaryStorage.readValue().asString());
113 : break;
114 0 : default:
115 0 : onError("Dereference of non-value type is forbidden");
116 0 : break;
117 : }
118 0 : break;
119 425 : case Var::Static:
120 425 : return cb(var.staticStorage.readValue().asString());
121 : break;
122 0 : default:
123 0 : onError("Dereference of undefined is forbidden");
124 0 : break;
125 : }
126 0 : return false;
127 : }
128 :
129 :
130 200 : Var ContextFn::execOperator(const Expression &expr, Expression::Op op) {
131 200 : Context::VarList list;
132 200 : if (prepareArgsList(list, expr, Expression::Operator) != maxOf<size_t>()) {
133 200 : auto val = new Value(Value::Type::DICTIONARY);
134 200 : auto &dict = val->asDict();
135 200 : auto d = list.data();
136 200 : auto s = list.size();
137 200 : dict.reserve(s / 2);
138 625 : for (size_t i = 0; i < s / 2; ++ i) {
139 425 : if (!extractName(d[i * 2], [&, this] (const StringView &name) {
140 425 : if (!extractValue(d[i * 2 + 1], [&] (Value &&val) {
141 850 : dict.emplace(name.str<memory::PoolInterface>(), move(val));
142 425 : return true;
143 : })) {
144 0 : return false;
145 : }
146 425 : return true;
147 : })) {
148 0 : return Var();
149 : }
150 : }
151 200 : return Var(false, val);
152 : } else {
153 0 : onError("Fail to read argument list for { } expression");
154 : }
155 0 : return Var();
156 200 : }
157 :
158 125 : Var ContextFn::execComposition(const Expression &expr, Expression::Op op) {
159 125 : Context::VarList list;
160 125 : if (prepareArgsList(list, expr, Expression::Composition) != maxOf<size_t>()) {
161 125 : auto val = new Value(Value::Type::ARRAY);
162 125 : auto &arr = val->asArray();
163 125 : auto d = list.data();
164 125 : auto s = list.size();
165 125 : arr.reserve(s);
166 500 : for (size_t i = 0; i < s; ++ i) {
167 375 : if (!extractValue(d[i], [&] (Value &&val) {
168 375 : arr.emplace_back(move(val));
169 375 : return true;
170 : })) {
171 0 : return Var();
172 : }
173 : }
174 125 : return Var(false, val);
175 : } else {
176 0 : onError("Fail to read argument list for [ ] expression");
177 : }
178 0 : return Var();
179 125 : }
180 :
181 2125 : size_t ContextFn::prepareArgsList(Context::VarList &list, const Expression &expr, Expression::Block block) {
182 2125 : if (block == Expression::Parentesis || block == Expression::Composition) {
183 1425 : if (expr.op == Expression::Comma && expr.left && expr.right) {
184 425 : auto r1 = prepareArgsList(list, *expr.left, block);
185 425 : auto r2 = prepareArgsList(list, *expr.right, block);
186 425 : if (r1 != maxOf<size_t>() && r2 != maxOf<size_t>()) {
187 425 : return r1 + r2;
188 : }
189 1000 : } else if (!expr.left && !expr.right && expr.op != Expression::NoOp) {
190 25 : return 0;
191 : } else {
192 975 : if (expr.isToken && expr.value.empty()) {
193 75 : return 0;
194 : }
195 900 : if (auto var = execExpr(expr, (block == Expression::Parentesis) ? Expression::Call : Expression::Subscript, false)) {
196 900 : list.emplace(move(var));
197 900 : return 1;
198 900 : }
199 : }
200 700 : } else if (block == Expression::Operator) {
201 700 : if ((expr.op == Expression::Comma || expr.op == Expression::Sequence) && expr.left && expr.right) {
202 250 : auto r1 = prepareArgsList(list, *expr.left, block);
203 250 : auto r2 = prepareArgsList(list, *expr.right, block);
204 250 : if (r1 != maxOf<size_t>() && r2 != maxOf<size_t>()) {
205 250 : return r1 + r2;
206 : }
207 0 : } else {
208 450 : if (expr.op == Expression::Colon && expr.left && expr.right) {
209 425 : if (auto key = execExpr(*expr.left, Expression::Colon, false)) {
210 425 : if (auto value = execExpr(*expr.right, Expression::Construct, false)) {
211 425 : list.emplace(move(key));
212 425 : list.emplace(move(value));
213 425 : return 1;
214 425 : }
215 425 : }
216 25 : } else if (!expr.left && !expr.right && expr.op != Expression::NoOp) {
217 25 : return 0;
218 : }
219 : }
220 : }
221 0 : return maxOf<size_t>();
222 : }
223 :
224 1225 : static bool ContextFn_asBool(const Value &v) {
225 1225 : return v.isBasicType() ? v.asBool() : !v.empty();
226 : }
227 :
228 53475 : Var ContextFn::execExpr(const Expression &expr, Expression::Op op, bool assignable) {
229 53475 : if (expr.left || expr.right || expr.op != Expression::NoOp) {
230 21150 : if (expr.block == Expression::Operator && op != Expression::Construct) {
231 200 : return execOperator(expr, op);
232 20950 : } else if (expr.block == Expression::Composition && op != Expression::Subscript) {
233 125 : return execComposition(expr, op);
234 : }
235 : }
236 :
237 53150 : if (!expr.left && !expr.right) {
238 32325 : if (expr.isToken && op != Expression::Colon) {
239 19500 : return getVar(expr.value.getString());
240 : } else {
241 12825 : return Var(true, &expr.value);
242 : }
243 20825 : } else if (expr.left && expr.right) {
244 19575 : switch (expr.op) {
245 350 : case Expression::Call:
246 350 : if (auto var = execExpr(*expr.left, expr.op, assignable)) {
247 350 : if (auto cb = var.getCallable()) {
248 350 : Context::VarList list;
249 350 : if (prepareArgsList(list, *expr.right, Expression::Parentesis) != maxOf<size_t>()) {
250 350 : if (auto ret = (*cb)(*var.getStorage(), list.data(), list.size())) {
251 350 : return ret;
252 : } else {
253 0 : onError("Fail to make call");
254 0 : return Var();
255 350 : }
256 : } else {
257 0 : onError("Invalid argument list for call");
258 0 : return Var();
259 : }
260 350 : } else {
261 0 : onError("Expression is not callable");
262 0 : return Var();
263 : }
264 350 : }
265 0 : break;
266 125 : case Expression::Subscript:
267 125 : if (auto var = execExpr(*expr.left, expr.op, assignable)) {
268 100 : if (var.getType() == Var::SoftUndefined) {
269 0 : onError("Invalid subscription [] from <undefined>");
270 0 : return Var();
271 : }
272 100 : Context::VarList list;
273 100 : if (prepareArgsList(list, *expr.right, Expression::Composition) == 1) {
274 100 : if (auto &val = list.data()->readValue()) {
275 100 : if (val.isInteger()) {
276 25 : if (auto ret = var.subscript(val.asInteger(), assignable)) {
277 25 : return ret;
278 : } else {
279 0 : return Var(nullptr);
280 25 : }
281 75 : } else if (!val.isDictionary() && !val.isArray()) {
282 75 : auto str = val.asString();
283 75 : if (auto ret = var.subscript(str, assignable)) {
284 75 : return ret;
285 : } else {
286 0 : return Var(nullptr);
287 75 : }
288 75 : }
289 : }
290 : }
291 0 : onError("Invalid subscription [] arguments");
292 0 : return Var();
293 225 : }
294 25 : break;
295 0 : case Expression::Construct:
296 0 : onError("Construct not implemented");
297 0 : return Var();
298 : break;
299 900 : case Expression::And:
300 900 : if (auto var = execExpr(*expr.left, expr.op, assignable)) {
301 900 : if (!ContextFn_asBool(var.readValue())) {
302 125 : return var;
303 : }
304 : } else {
305 0 : return Var();
306 900 : }
307 775 : return execExpr(*expr.right, expr.op, assignable);
308 : break;
309 75 : case Expression::Or:
310 75 : if (auto var = execExpr(*expr.left, expr.op, assignable)) {
311 75 : if (ContextFn_asBool(var.readValue())) {
312 75 : return var;
313 : }
314 : } else {
315 0 : return Var();
316 75 : }
317 0 : return execExpr(*expr.right, expr.op, assignable);
318 : break;
319 250 : case Expression::ConditionalSwitch:
320 250 : if (expr.left && expr.left->op == Expression::Conditional && expr.left->left) {
321 250 : auto tmpUndef = allowUndefined;
322 250 : allowUndefined = true;
323 250 : if (auto var = execExpr(*expr.left->left, expr.left->op, assignable)) {
324 250 : allowUndefined = tmpUndef;
325 250 : if (ContextFn_asBool(var.readValue())) {
326 100 : return execExpr(*expr.left->right, expr.op, assignable);
327 : } else {
328 150 : return execExpr(*expr.right, expr.op, assignable);
329 : }
330 : } else {
331 0 : allowUndefined = tmpUndef;
332 0 : return execExpr(*expr.right, expr.op, assignable);
333 250 : }
334 : }
335 0 : onError("Invalid conditional expression");
336 0 : return Var();
337 : break;
338 1875 : case Expression::Assignment:
339 : case Expression::SumAssignment:
340 : case Expression::DiffAssignment:
341 : case Expression::MultAssignment:
342 : case Expression::DivAssignment:
343 : case Expression::RemAssignment:
344 : case Expression::LShiftAssignment:
345 : case Expression::RShiftAssignment:
346 : case Expression::AndAssignment:
347 : case Expression::XorAssignment:
348 : case Expression::OrAssignment: { // left-to-right associativity
349 1875 : auto r = execExpr(*expr.right, expr.op, assignable);
350 1875 : auto l = execExpr(*expr.left, expr.op, true);
351 1875 : return performBinaryOp(l, r, expr.op);
352 : break;
353 1875 : }
354 9300 : case Expression::Dot: {
355 9300 : auto l = execExpr(*expr.left, expr.op, assignable);
356 9300 : if (l && l.getType() != Var::SoftUndefined) {
357 9300 : if (expr.right && expr.right->isToken && expr.right->value.isString()) {
358 9300 : if (auto v = l.subscript(expr.right->value.getString(), assignable)) {
359 7575 : return v;
360 : } else {
361 1725 : return Var(nullptr);
362 9300 : }
363 : } else {
364 0 : onError(string::toString<memory::PoolInterface>("Invalid argument for <dot> operator"));
365 0 : return Var();
366 : }
367 : } else {
368 0 : onError(string::toString<memory::PoolInterface>("Fail to read <dot>: <undefined>.", expr.right->value.getString()));
369 0 : return Var();
370 : }
371 : break;
372 9300 : }
373 6700 : default: { // right-to-left associativity
374 6700 : auto l = execExpr(*expr.left, expr.op, assignable);
375 6700 : auto r = execExpr(*expr.right, expr.op, assignable);
376 6700 : return performBinaryOp(l, r, expr.op);
377 : break;
378 6700 : }
379 : }
380 1275 : } else if (expr.left) {
381 1250 : if (expr.op == Expression::Neg) {
382 150 : auto var = execExpr(*expr.left, expr.op, assignable);
383 150 : if (var) {
384 150 : return performUnaryOp(var, expr.op);
385 : } else {
386 0 : onError(string::toString<memory::PoolInterface>("Invalid call <neg> <undefined>"));
387 0 : return Var(Value(true));
388 : }
389 1250 : } else if (expr.op != Expression::Var) {
390 325 : if (auto var = execExpr(*expr.left, expr.op, assignable)) {
391 325 : return performUnaryOp(var, expr.op);
392 325 : }
393 : } else {
394 775 : return performVarExpr(*expr.left, expr.op);
395 : }
396 : }
397 :
398 25 : return Var();
399 : }
400 :
401 12075 : static void ContextFn_printEscapedString(const StringView &str, const ContextFn::OutStream &out, bool escapeOutput) {
402 12075 : if (escapeOutput) {
403 11800 : StringView r(str);
404 61800 : while (!r.empty()) {
405 50000 : out << r.readUntil<StringView::Chars<'&', '<', '>', '"', '\''>>();
406 50000 : switch (r[0]) {
407 950 : case '&': out << "&"; break;
408 1475 : case '<': out << "<"; break;
409 1475 : case '>': out << ">"; break;
410 33950 : case '"': out << """; break;
411 475 : case '\'': out << "'"; break;
412 : }
413 50000 : ++ r;
414 : }
415 : } else {
416 275 : out << str;
417 : }
418 12075 : }
419 :
420 12450 : static void ContextFn_printVar(const Var &var, const ContextFn::OutStream &out, bool escapeOutput) {
421 12450 : auto &val = var.readValue();
422 12450 : switch (val.getType()) {
423 11875 : case Value::Type::CHARSTRING:
424 11875 : ContextFn_printEscapedString(val.getString(), out, escapeOutput);
425 11875 : break;
426 200 : case Value::Type::INTEGER: out << val.asInteger(); break;
427 150 : case Value::Type::DOUBLE: out << val.asString(); break; // use internal converter
428 50 : case Value::Type::BOOLEAN: out << (val.asBool() ? "true" : "false"); break;
429 75 : case Value::Type::EMPTY: out << "null"; break;
430 100 : default: data::json::write(out, val, false, false); break;
431 : }
432 12450 : }
433 :
434 7825 : bool ContextFn::printExpr(const Expression &expr, Expression::Op op, const OutStream &out) {
435 : // optimize Comma operator to output results sequentially
436 7825 : if (expr.op != Expression::Comma || (expr.block != Expression::None && expr.block != Expression::Parentesis)) {
437 7600 : if (auto var = execExpr(expr, op, false)) {
438 7575 : ContextFn_printVar(var, out, escapeOutput);
439 7575 : return true;
440 7600 : }
441 25 : } else {
442 225 : if (expr.left) {
443 225 : if (!printExpr(*expr.left, Expression::Comma, out)) {
444 0 : return false;
445 : }
446 : }
447 225 : if (expr.right) {
448 225 : if (!printExpr(*expr.right, Expression::Comma, out)) {
449 0 : return false;
450 : }
451 : }
452 225 : return true;
453 : }
454 25 : return false;
455 : }
456 :
457 775 : Var ContextFn::performVarExpr(const Expression &expr, Expression::Op op) {
458 775 : if (expr.isToken && expr.value.isString()) {
459 775 : auto &n = expr.value.getString();
460 775 : auto it = currentScope->namedVars.find(n);
461 775 : if (it == currentScope->namedVars.end()) {
462 775 : auto p_it = currentScope->namedVars.emplace(n, VarStorage());
463 775 : return Var(&p_it.first->second);
464 : } else {
465 0 : onError(string::toString<memory::PoolInterface>("Variable name conflict for ", n));
466 : }
467 : }
468 0 : return Var();
469 : }
470 :
471 475 : Var ContextFn::performUnaryOp(Var &v, Expression::Op op) {
472 475 : if (auto &val = v.readValue()) {
473 450 : switch (op) {
474 150 : case Expression::SuffixIncr:
475 150 : if (auto mut = getMutableValue(v)) {
476 150 : auto i = val.asInteger();
477 150 : *mut = Value(i + 1);
478 300 : return Var(Value(i));
479 : } else {
480 0 : return Var();
481 : }
482 : break;
483 :
484 50 : case Expression::SuffixDecr:
485 50 : if (auto mut = getMutableValue(v)) {
486 50 : auto i = val.asInteger();
487 50 : *mut = Value(i - 1);
488 100 : return Var(Value(i));
489 : } else {
490 0 : onError(string::toString<memory::PoolInterface>("Fail to write into constant value"));
491 0 : return Var();
492 : }
493 : break;
494 :
495 25 : case Expression::PrefixIncr:
496 25 : if (auto mut = getMutableValue(v)) {
497 25 : *mut = Value(val.asInteger() + 1);
498 25 : return v;
499 : } else {
500 0 : onError(string::toString<memory::PoolInterface>("Fail to write into constant value"));
501 0 : return Var();
502 : }
503 : break;
504 :
505 0 : case Expression::PrefixDecr:
506 0 : if (auto mut = getMutableValue(v)) {
507 0 : *mut = Value(val.asInteger() - 1);
508 0 : return v;
509 : } else {
510 0 : onError(string::toString<memory::PoolInterface>("Fail to write into constant value"));
511 0 : return Var();
512 : }
513 : break;
514 :
515 75 : case Expression::Minus:
516 150 : return Var(val.isInteger() ? Value(- val.asInteger()) : Value(- val.asDouble()));
517 : break;
518 :
519 125 : case Expression::Neg:
520 250 : return Var(val.isBasicType() ? Value(!val.asBool()) : Value(!val.empty()));
521 : break;
522 :
523 25 : case Expression::BitNot:
524 50 : return Var(Value(~ val.asInteger()));
525 : break;
526 :
527 0 : default:
528 0 : onError(string::toString<memory::PoolInterface>("Invalid unary operator: ", int(toInt(op))));
529 0 : return Var();
530 : break;
531 : }
532 25 : } else if (allowUndefined) {
533 25 : switch (op) {
534 25 : case Expression::Neg:
535 50 : return Var(Value(true));
536 : break;
537 0 : default: break;
538 : }
539 : }
540 :
541 0 : onError("Unexpected null value on unary op");
542 0 : return Var();
543 : }
544 :
545 : template <typename Callback>
546 300 : static bool Variable_assign(Var &target, const Var &var, Callback &&cb) {
547 300 : if (auto mut = target.getMutable()) {
548 300 : if (cb(*mut, var.readValue())) {
549 300 : return true;
550 : }
551 : }
552 0 : return false;
553 : }
554 :
555 8575 : Var ContextFn::performBinaryOp(Var &l, Var &r, Expression::Op op) {
556 8575 : auto &lV = l.readValue();
557 8575 : auto &rV = r.readValue();
558 :
559 425 : auto numOp = [&, this] (const Value &l, const Value &r, auto intOp, auto doubleOp) {
560 425 : if (l.isInteger() && r.isInteger()) {
561 250 : return Var(Value(intOp(l.asInteger(), r.asInteger())));
562 300 : } else if (l.isBasicType() && r.isBasicType()) {
563 600 : return Var(Value(doubleOp(l.asDouble(), r.asDouble())));
564 : } else {
565 0 : onError("Invalid type for numeric operation");
566 0 : return Var();
567 : }
568 8575 : };
569 :
570 150 : auto intOp = [&, this] (const Value &l, const Value &r, auto intOp) {
571 150 : if (l.isInteger() && r.isInteger()) {
572 300 : return Var(Value(intOp(l.asInteger(), r.asInteger())));
573 : } else {
574 0 : onError("Invalid type for integer operation");
575 0 : return Var();
576 : }
577 8575 : };
578 :
579 75 : auto numberAssignment = [&, this] (auto intOp, auto doubleOp) -> Var {
580 225 : if (Variable_assign(l, r, [&] (Value &mut, const Value &r) -> bool {
581 75 : if ((mut.isInteger() || mut.isDouble()) && (r.isInteger() || r.isDouble())) {
582 75 : if (mut.isInteger() && r.isInteger()) {
583 75 : mut.setInteger(intOp(mut.getInteger(), r.asInteger()));
584 : } else {
585 0 : mut.setDouble(doubleOp(mut.asDouble(), r.asDouble()));
586 : }
587 75 : return true;
588 : }
589 0 : return false;
590 : })) {
591 75 : return Var(l);
592 : } else {
593 0 : onError("Invalid assignment operator");
594 0 : return Var();
595 : }
596 8575 : };
597 :
598 150 : auto intAssignment = [&, this] (auto intOp) -> Var {
599 450 : if (Variable_assign(l, r, [&] (Value &mut, const Value &r) -> bool {
600 150 : if (mut.isInteger() && r.isInteger()) {
601 150 : mut.setInteger(intOp(mut.getInteger(), r.asInteger()));
602 150 : return true;
603 : }
604 0 : return false;
605 : })) {
606 150 : return Var(l);
607 : } else {
608 0 : onError("Invalid assignment operator");
609 0 : return Var();
610 : }
611 8575 : };
612 :
613 1575 : auto variableAssign = [&] (Var &target, const Var &var) -> bool {
614 1575 : return target.assign(var);
615 : };
616 :
617 : // try assignment
618 8575 : if (r.getType() != Var::SoftUndefined) {
619 8575 : switch (op) {
620 1575 : case Expression::Assignment:
621 1575 : if (variableAssign(l, r)) {
622 1550 : return Var(l);
623 : } else {
624 25 : onError("Invalid assignment operator");
625 25 : return Var();
626 : }
627 : break;
628 75 : case Expression::SumAssignment:
629 75 : if (Variable_assign(l, r, [this] (Value &mut, const Value &r) -> bool {
630 75 : if (!mut) { mut = r; return true; }
631 75 : else if (mut.isString()) { mut.getString() += r.asString(); return true; }
632 75 : else if (mut.isInteger() && !r.isDouble()) { mut.setInteger(mut.getInteger() + r.asInteger()); return true; }
633 50 : else if ((mut.isInteger() && r.isDouble()) || mut.isDouble()) { mut.setDouble(mut.asDouble() + r.asDouble()); return true; }
634 0 : else if (mut.isBool()) { mut.setBool(mut.asBool() + r.asBool()); return true; }
635 0 : else { onError("Invalid SumAssignment operator"); return false; }
636 : })) {
637 75 : return Var(l);
638 : } else {
639 0 : onError("Invalid assignment operator");
640 0 : return Var();
641 : }
642 : break;
643 50 : case Expression::DiffAssignment: return numberAssignment([] (int64_t l, int64_t r) { return l - r; }, [] (double l, double r) { return l - r; }); break;
644 50 : case Expression::MultAssignment: return numberAssignment([] (int64_t l, int64_t r) { return l * r; }, [] (double l, double r) { return l * r; }); break;
645 50 : case Expression::DivAssignment: return numberAssignment([] (int64_t l, int64_t r) { return l / r; }, [] (double l, double r) { return l / r; }); break;
646 50 : case Expression::RemAssignment: return intAssignment([] (int64_t l, int64_t r) { return l % r; }); break;
647 50 : case Expression::LShiftAssignment: return intAssignment([] (int64_t l, int64_t r) { return l << r; }); break;
648 50 : case Expression::RShiftAssignment: return intAssignment([] (int64_t l, int64_t r) { return l >> r; }); break;
649 50 : case Expression::AndAssignment: return intAssignment([] (int64_t l, int64_t r) { return l & r; }); break;
650 50 : case Expression::XorAssignment: return intAssignment([] (int64_t l, int64_t r) { return l ^ r; }); break;
651 50 : case Expression::OrAssignment: return intAssignment([] (int64_t l, int64_t r) { return l | r; }); break;
652 6700 : default: break;
653 : }
654 : }
655 :
656 6700 : if (lV && rV) {
657 6425 : switch (op) {
658 50 : case Expression::Mult: return numOp(lV, rV, [] (int64_t l, int64_t r) { return l * r; }, [] (double l, double r) { return l * r; }); break;
659 50 : case Expression::Div: return numOp(lV, rV, [] (int64_t l, int64_t r) { return l / r; }, [] (double l, double r) { return l / r; }); break;
660 50 : case Expression::Rem: return intOp(lV, rV, [] (int64_t l, int64_t r) { return l % r; }); break;
661 :
662 5800 : case Expression::Sum:
663 5800 : if (lV.isString() || rV.isString()) {
664 10750 : return Var(Value(lV.asString() + rV.asString()));
665 425 : } else if (lV.isInteger() && rV.isInteger()) {
666 400 : return Var(Value(lV.asInteger() + rV.asInteger()));
667 225 : } else if (lV.isDictionary() && rV.isDictionary()) {
668 25 : auto d = lV.asDict();
669 75 : for (auto &it : rV.asDict()) {
670 50 : d.emplace(it.first, it.second);
671 : }
672 25 : return Var(false, new Value(std::move(d)));
673 225 : } else if (lV.isArray() && rV.isArray()) {
674 25 : auto d = lV.asArray();
675 100 : for (auto &it : rV.asArray()) {
676 75 : d.emplace_back(it);
677 : }
678 25 : return Var(false, new Value(std::move(d)));
679 25 : } else {
680 350 : return Var(Value(lV.asDouble() + rV.asDouble()));
681 : }
682 : break;
683 :
684 100 : case Expression::Sub: return numOp(lV, rV, [] (int64_t l, int64_t r) { return l - r; }, [] (double l, double r) { return l - r; }); break;
685 :
686 350 : case Expression::Lt: return numOp(lV, rV, [] (int64_t l, int64_t r) { return l < r; }, [] (double l, double r) { return l < r; }); break;
687 100 : case Expression::LtEq: return numOp(lV, rV, [] (int64_t l, int64_t r) { return l <= r; }, [] (double l, double r) { return l <= r; }); break;
688 100 : case Expression::Gt: return numOp(lV, rV, [] (int64_t l, int64_t r) { return l > r; }, [] (double l, double r) { return l > r; }); break;
689 100 : case Expression::GtEq: return numOp(lV, rV, [] (int64_t l, int64_t r) { return l >= r; }, [] (double l, double r) { return l >= r; }); break;
690 :
691 50 : case Expression::Eq: return Var(Value(lV == rV)); break;
692 50 : case Expression::NotEq: return Var(Value(lV != rV)); break;
693 :
694 0 : case Expression::And: return Var(Value(ContextFn_asBool(lV) && ContextFn_asBool(rV))); break;
695 0 : case Expression::Or: return Var(Value(ContextFn_asBool(lV) || ContextFn_asBool(rV))); break;
696 0 : case Expression::Comma: return Var(Value(lV.asString() + rV.asString())); break;
697 :
698 50 : case Expression::ShiftLeft: return intOp(lV, rV, [] (int64_t l, int64_t r) { return l << r; }); break;
699 50 : case Expression::ShiftRight: return intOp(lV, rV, [] (int64_t l, int64_t r) { return l >> r; }); break;
700 50 : case Expression::BitAnd: return intOp(lV, rV, [] (int64_t l, int64_t r) { return l & r; }); break;
701 50 : case Expression::BitOr: return intOp(lV, rV, [] (int64_t l, int64_t r) { return l | r; }); break;
702 50 : case Expression::BitXor: return intOp(lV, rV, [] (int64_t l, int64_t r) { return l ^ r; }); break;
703 :
704 0 : case Expression::Dot:
705 : case Expression::Var:
706 : case Expression::Sharp:
707 : case Expression::Scope:
708 :
709 : case Expression::Call:
710 : case Expression::Subscript:
711 : case Expression::Construct:
712 :
713 : case Expression::Colon:
714 : case Expression::Sequence:
715 :
716 : case Expression::Conditional:
717 : case Expression::ConditionalSwitch:
718 : case Expression::Assignment:
719 : case Expression::SumAssignment:
720 : case Expression::DiffAssignment:
721 : case Expression::MultAssignment:
722 : case Expression::DivAssignment:
723 : case Expression::RemAssignment:
724 : case Expression::LShiftAssignment:
725 : case Expression::RShiftAssignment:
726 : case Expression::AndAssignment:
727 : case Expression::XorAssignment:
728 : case Expression::OrAssignment:
729 0 : onError(string::toString<memory::PoolInterface>("Operator not implemented: ", int(toInt(op))));
730 0 : return Var();
731 : break;
732 :
733 0 : case Expression::NoOp:
734 0 : onError(string::toString<memory::PoolInterface>("Call of NoOp"));
735 0 : return Var();
736 : break;
737 :
738 0 : case Expression::SuffixIncr:
739 : case Expression::SuffixDecr:
740 : case Expression::PrefixIncr:
741 : case Expression::PrefixDecr:
742 : case Expression::Minus:
743 : case Expression::Neg:
744 : case Expression::BitNot:
745 0 : onError(string::toString<memory::PoolInterface>("Unary operator called as binary: ", int(toInt(op))));
746 0 : return Var();
747 : break;
748 : }
749 :
750 0 : return Var();
751 275 : } else if (lV || rV) {
752 200 : auto valToInt = [&] (const Value &v, const Var &var) {
753 200 : return (v) ? v.asInteger() : int64_t(0);
754 : };
755 :
756 200 : auto valToDouble = [&] (const Value &v, const Var &var) {
757 200 : return (v) ? v.asInteger() : ((var.getType() == Var::SoftUndefined) ? std::numeric_limits<double>::quiet_NaN() : double(0.0));
758 : };
759 :
760 200 : auto numNullOp = [&, this] (const Value &lVal, const Value &rVal, auto intOp, auto doubleOp) {
761 200 : if (lVal.isInteger() || rVal.isInteger()) {
762 200 : return Var(Value(intOp(valToInt(lVal, l), valToInt(rVal, r))));
763 100 : } else if (lVal.isBasicType() || rVal.isBasicType()) {
764 200 : return Var(Value(doubleOp(valToDouble(lVal, l), valToDouble(rVal, r))));
765 : } else {
766 0 : onError("Invalid type for numeric operation");
767 0 : return Var();
768 : }
769 275 : };
770 :
771 275 : switch (op) {
772 100 : case Expression::Lt: return numNullOp(lV, rV, [] (int64_t l, int64_t r) { return l < r; }, [] (double l, double r) { return l < r; }); break;
773 100 : case Expression::LtEq: return numNullOp(lV, rV, [] (int64_t l, int64_t r) { return l <= r; }, [] (double l, double r) { return l <= r; }); break;
774 100 : case Expression::Gt: return numNullOp(lV, rV, [] (int64_t l, int64_t r) { return l > r; }, [] (double l, double r) { return l > r; }); break;
775 100 : case Expression::GtEq: return numNullOp(lV, rV, [] (int64_t l, int64_t r) { return l >= r; }, [] (double l, double r) { return l >= r; }); break;
776 :
777 50 : case Expression::Eq: return Var(Value(lV.isNull() && rV.isNull())); break;
778 50 : case Expression::NotEq: return Var(Value(!lV.isNull() || !rV.isNull())); break;
779 :
780 0 : case Expression::And: return Var(Value(false)); break;
781 0 : case Expression::Or: return Var(Value(ContextFn_asBool(lV) || ContextFn_asBool(rV))); break;
782 25 : default:
783 25 : onError(string::toString<memory::PoolInterface>("Invalid operation with undefined"));
784 25 : return Var();
785 : break;
786 : }
787 : }
788 0 : return Var();
789 : }
790 :
791 225 : Value *ContextFn::getMutableValue(Var &val) const {
792 225 : if (auto v = val.getMutable()) {
793 225 : return v;
794 : }
795 0 : onError("Fail to acquire mutable value from constant");
796 0 : return nullptr;
797 : }
798 :
799 100 : void ContextFn::onError(const StringView &err) const {
800 100 : if (!outStream) {
801 0 : std::cout << "Context error: " << err << "\n";
802 : } else {
803 100 : *outStream << "<!-- " << "Context error: " << err << " -->";
804 : }
805 100 : }
806 :
807 19500 : Var ContextFn::getVar(const StringView &key) const {
808 19500 : Var ret;
809 19500 : if (currentScope) {
810 19500 : ret = getVar(currentScope, key);
811 : }
812 19500 : if (!ret && !allowUndefined) {
813 50 : onError(string::toString<memory::PoolInterface>("Access to undefined variable: ", key));
814 : }
815 :
816 19500 : if (!ret && allowUndefined) {
817 350 : return Var(nullptr);
818 : }
819 :
820 19150 : return ret;
821 19500 : }
822 :
823 38200 : Var ContextFn::getVar(VarScope *scope, const StringView &key) const {
824 38200 : auto it = scope->namedVars.find(key);
825 38200 : if (it != scope->namedVars.end()) {
826 19100 : return Var(&it->second);
827 19100 : } else if (scope->prev) {
828 18700 : return getVar(scope->prev, key);
829 : } else {
830 400 : return Var();
831 : }
832 : }
833 :
834 0 : const VarStorage *ContextFn::getVarStorage(VarScope *scope, const StringView &key) const {
835 0 : auto it = scope->namedVars.find(key);
836 0 : if (it != scope->namedVars.end()) {
837 0 : return &it->second;
838 0 : } else if (scope->prev) {
839 0 : return getVarStorage(scope->prev, key);
840 : }
841 0 : return nullptr;
842 : }
843 :
844 1750 : void Context::VarList::emplace(Var &&var) {
845 1750 : if (staticCount < MinStaticVars) {
846 1700 : staticList[staticCount] = move(var);
847 1700 : ++ staticCount;
848 50 : } else if (staticCount == MinStaticVars) {
849 25 : dynamicList.reserve(MinStaticVars * 2);
850 225 : for (auto &it : staticList) {
851 200 : dynamicList.emplace_back(move(it));
852 200 : it.clear();
853 : }
854 25 : dynamicList.emplace_back(move(var));
855 25 : staticCount = MinStaticVars + 1;
856 : } else {
857 25 : dynamicList.emplace_back(move(var));
858 : }
859 1750 : }
860 :
861 675 : size_t Context::VarList::size() const {
862 675 : return (staticCount <= MinStaticVars) ? staticCount : dynamicList.size();
863 : }
864 :
865 775 : Var * Context::VarList::data() {
866 775 : return (staticCount <= MinStaticVars) ? staticList.data() : dynamicList.data();
867 : }
868 :
869 :
870 0 : bool Context::isConstExpression(const Expression &expr) {
871 0 : return expr.isConst();
872 : }
873 :
874 175 : bool Context::printConstExpr(const Expression &expr, const OutStream &out, bool escapeOutput) {
875 175 : ContextFn fn;
876 175 : fn.outStream = &out;
877 175 : fn.escapeOutput = escapeOutput;
878 350 : return fn.printExpr(expr, expr.op, out);
879 : }
880 :
881 5125 : static void Context_printAttrVar(const StringView &name, const Var &var, const Context::OutStream &out, bool escapeOutput) {
882 5125 : auto &val = var.readValue();
883 5125 : if (val.isBool()) {
884 50 : if (val.getBool()) {
885 25 : out << " " << name;
886 : }
887 5075 : } else if (val.isArray()) {
888 50 : out << " " << name << "=\"";
889 50 : bool empty = true;
890 200 : for (auto &it : val.asArray()) {
891 150 : if (empty) { empty = false; } else { out << " "; }
892 150 : ContextFn_printEscapedString(it.asString(), out, escapeOutput);
893 : }
894 50 : out << "\"";
895 5025 : } else if (val.isDictionary() && name == "style") {
896 25 : out << " " << name << "=\"";
897 75 : for (auto &it : val.asDict()) {
898 50 : out << it.first << ":";
899 50 : ContextFn_printEscapedString(it.second.asString(), out, escapeOutput);
900 50 : out << ";";
901 : }
902 25 : out << "\"";
903 5000 : } else if (val.isNull()) {
904 : // do nothing
905 : } else {
906 4875 : out << " " << name << "=\"";
907 4875 : ContextFn_printVar(var, out, escapeOutput);
908 4875 : out << "\"";
909 : }
910 5125 : }
911 :
912 50 : static void Context_printAttrList(const Var &var, const Context::OutStream &out) {
913 50 : auto &val = var.readValue();
914 100 : for (auto &it : val.asDict()) {
915 50 : out << " " << it.first << "=\"" << it.second.asString() << "\"";
916 : }
917 50 : }
918 :
919 3575 : bool Context::printAttrVar(const StringView &name, const Expression &expr, const OutStream &out, bool escapeOutput) {
920 3575 : ContextFn fn;
921 3575 : fn.outStream = &out;
922 3575 : fn.escapeOutput = escapeOutput;
923 3575 : if (auto var = fn.execExpr(expr, expr.op, false)) {
924 3575 : Context_printAttrVar(name, var, out, escapeOutput);
925 3575 : return true;
926 3575 : }
927 0 : return false;
928 : }
929 :
930 25 : bool Context::printAttrExpr(const Expression &expr, const OutStream &out) {
931 25 : ContextFn fn;
932 25 : fn.outStream = &out;
933 25 : fn.escapeOutput = false;
934 25 : if (auto var = fn.execExpr(expr, expr.op, false)) {
935 25 : Context_printAttrList(var, out);
936 25 : return true;
937 25 : }
938 0 : return false;
939 : }
940 :
941 500 : Context::Context() : currentScope(&globalScope) { }
942 :
943 7200 : bool Context::print(const Expression &expr, const OutStream &out, bool escapeOutput) {
944 7200 : ContextFn fn{currentScope};
945 7200 : fn.outStream = &out;
946 7200 : fn.escapeOutput = escapeOutput;
947 14400 : return fn.printExpr(expr, expr.op, out);
948 : }
949 :
950 1550 : bool Context::printAttr(const StringView &name, const Expression &expr, const OutStream &out, bool escapeOutput) {
951 1550 : ContextFn fn{currentScope};
952 1550 : fn.outStream = &out;
953 1550 : fn.escapeOutput = escapeOutput;
954 1550 : if (auto var = fn.execExpr(expr, expr.op, false)) {
955 1550 : Context_printAttrVar(name, var, out, escapeOutput);
956 1550 : return true;
957 1550 : }
958 0 : return false;
959 : }
960 :
961 25 : bool Context::printAttrExprList(const Expression &expr, const OutStream &out) {
962 25 : ContextFn fn{currentScope};
963 25 : fn.outStream = &out;
964 25 : fn.escapeOutput = false;
965 25 : if (auto var = fn.execExpr(expr, expr.op, false)) {
966 25 : Context_printAttrList(var, out);
967 25 : return true;
968 25 : }
969 0 : return false;
970 : }
971 :
972 9300 : Var Context::exec(const Expression &expr, const OutStream &out, bool allowUndefined) {
973 9300 : ContextFn fn{currentScope};
974 9300 : fn.outStream = &out;
975 9300 : fn.allowUndefined = allowUndefined;
976 18600 : return fn.execExpr(expr, expr.op, false);
977 : }
978 :
979 125 : void Context::set(const StringView &name, const Value &val, VarClass *cl) {
980 125 : auto it = currentScope->namedVars.emplace(name.str<memory::PoolInterface>()).first;
981 125 : it->second.set(val, cl);
982 125 : }
983 :
984 3300 : void Context::set(const StringView &name, Value &&val, VarClass *cl) {
985 3300 : auto it = currentScope->namedVars.emplace(name.str<memory::PoolInterface>()).first;
986 3300 : it->second.set(move(val), cl);
987 3300 : }
988 :
989 2475 : void Context::set(const StringView &name, bool isConst, const Value *val, VarClass *cl) {
990 2475 : auto it = currentScope->namedVars.emplace(name.str<memory::PoolInterface>()).first;
991 2475 : it->second.set(isConst, val, cl);
992 2475 : }
993 :
994 25 : void Context::set(const StringView &name, VarClass *cl) {
995 25 : auto it = currentScope->namedVars.emplace(name.str<memory::PoolInterface>()).first;
996 25 : it->second.set(cl);
997 25 : }
998 :
999 975 : void Context::set(const StringView &name, Callback &&cb) {
1000 975 : auto it = currentScope->namedVars.emplace(name.str<memory::PoolInterface>()).first;
1001 975 : it->second.set(new Callback(move(cb)));
1002 975 : }
1003 :
1004 350 : VarClass * Context::set(const StringView &name, VarClass &&cl) {
1005 350 : auto c_it = classes.emplace(name.str<memory::PoolInterface>(), move(cl)).first;
1006 350 : auto it = currentScope->namedVars.emplace(name.str<memory::PoolInterface>()).first;
1007 350 : it->second.set(&c_it->second);
1008 350 : return &c_it->second;
1009 : }
1010 :
1011 550 : static bool Context_pushMixinVar(Context::Mixin &mixin, Expression *expr) {
1012 550 : bool requireDefault = (!mixin.args.empty() && mixin.args.back().second);
1013 :
1014 550 : if (expr->isToken) {
1015 450 : if (!requireDefault) {
1016 450 : ++ mixin.required;
1017 450 : mixin.args.emplace_back(pair(StringView(expr->value.getString()), nullptr));
1018 450 : return true;
1019 : }
1020 100 : } else if (expr->op == Expression::Assignment) {
1021 100 : if (expr->left && expr->left->isToken && expr->right) {
1022 100 : mixin.args.emplace_back(pair(StringView(expr->left->value.getString()), expr->right));
1023 100 : return true;
1024 : }
1025 : }
1026 0 : return false;
1027 : };
1028 :
1029 550 : static bool Context_processMixinArgs(Context::Mixin &mixin, Expression *expr) {
1030 550 : if (expr) {
1031 550 : if (expr->isToken || expr->op == Expression::Assignment) {
1032 500 : return Context_pushMixinVar(mixin, expr);
1033 50 : } else if (expr->op == Expression::Comma) {
1034 50 : if (!Context_processMixinArgs(mixin, expr->left)) {
1035 0 : return false;
1036 : }
1037 50 : return Context_pushMixinVar(mixin, expr->right);
1038 : } else {
1039 0 : return false;
1040 : }
1041 : }
1042 0 : return true;
1043 : }
1044 :
1045 775 : bool Context::setMixin(const StringView &name, const Template::Chunk *chunk) {
1046 775 : auto it = currentScope->mixins.find(name);
1047 775 : if (it != currentScope->mixins.end()) {
1048 0 : return false;
1049 : }
1050 :
1051 775 : Mixin mixin{chunk};
1052 775 : if (chunk->expr && chunk->expr->op == Expression::Call) {
1053 500 : if (!Context_processMixinArgs(mixin, chunk->expr->right)) {
1054 0 : return false;
1055 : }
1056 : }
1057 :
1058 775 : currentScope->mixins.emplace(name.str<memory::PoolInterface>(), move(mixin));
1059 775 : return true;
1060 775 : }
1061 :
1062 800 : const Context::Mixin *Context::getMixin(const StringView &name) const {
1063 800 : auto scope = currentScope;
1064 900 : while (scope) {
1065 900 : auto it = scope->mixins.find(name);
1066 900 : if (it != scope->mixins.end()) {
1067 800 : return &it->second;
1068 : }
1069 :
1070 100 : scope = scope->prev;
1071 : }
1072 0 : return nullptr;
1073 : }
1074 :
1075 0 : const VarStorage *Context::getVar(const StringView &name) const {
1076 0 : ContextFn fn;
1077 0 : return fn.getVarStorage(currentScope, name);
1078 : }
1079 :
1080 725 : bool Context::runInclude(const StringView &name, const OutStream &out, Template::RunContext &rctx) {
1081 725 : bool ret = false;
1082 725 : if (_includeCallback) {
1083 725 : ret = _includeCallback(name, *this, out, rctx);
1084 : }
1085 725 : if (!ret) {
1086 0 : out << "<!-- fail to include " << name << " -->";
1087 : }
1088 725 : return ret;
1089 : }
1090 :
1091 475 : void Context::setIncludeCallback(IncludeCallback &&cb) {
1092 475 : _includeCallback = move(cb);
1093 475 : }
1094 :
1095 7125 : void Context::pushVarScope(VarScope &scope) {
1096 7125 : scope.prev = currentScope;
1097 7125 : currentScope = &scope;
1098 7125 : }
1099 :
1100 7125 : void Context::popVarScope() {
1101 7125 : currentScope = currentScope->prev;
1102 7125 : }
1103 :
1104 325 : static inline bool Context_default_encodeURIChar(char c) {
1105 325 : if (('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z') || ('0' <= c && c <= '9')
1106 150 : || c == '-' || c == '_' || c == '.' || c == '!' || c == '~' || c == '*' || c == '\'' || c == '#'
1107 125 : || c == ';' || c == ',' || c == '/' || c == '?' || c == ':' || c == '@' || c == '&' || c == '='
1108 100 : || c == '+' || c == '$' || c == '(' || c == ')') {
1109 275 : return false;
1110 : } else {
1111 50 : return true;
1112 : }
1113 : }
1114 :
1115 25 : static const String Context_default_encodeURI(const String &data) {
1116 25 : String ret; ret.reserve(data.size() * 2);
1117 350 : for (auto &c : data) {
1118 325 : if (Context_default_encodeURIChar(c)) {
1119 50 : ret.push_back('%');
1120 50 : ret.append(base16::charToHex(c), 2);
1121 : } else {
1122 275 : ret.push_back(c);
1123 : }
1124 : }
1125 25 : return ret;
1126 0 : }
1127 :
1128 200 : static inline bool Context_default_encodeURIComponentChar(char c) {
1129 200 : if (('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z') || ('0' <= c && c <= '9')
1130 100 : || c == '-' || c == '_' || c == '.' || c == '!' || c == '~' || c == '*' || c == '\'' || c == '(' || c == ')') {
1131 150 : return false;
1132 : } else {
1133 50 : return true;
1134 : }
1135 : }
1136 :
1137 25 : static const String Context_default_encodeURIComponent(const String &data) {
1138 25 : String ret; ret.reserve(data.size() * 2);
1139 225 : for (auto &c : data) {
1140 200 : if (Context_default_encodeURIComponentChar(c)) {
1141 50 : ret.push_back('%');
1142 50 : ret.append(base16::charToHex(c), 2);
1143 : } else {
1144 150 : ret.push_back(c);
1145 : }
1146 : }
1147 25 : return ret;
1148 0 : }
1149 :
1150 475 : void Context::loadDefaults() {
1151 475 : set("encodeURI", [] (pug::VarStorage &, pug::Var *var, size_t argc) -> pug::Var {
1152 25 : if (var && argc == 1 && var->readValue().isString()) {
1153 50 : return pug::Var(Value(Context_default_encodeURI(var->readValue().getString())));
1154 : }
1155 0 : return pug::Var();
1156 : });
1157 475 : set("encodeURIComponent", [] (pug::VarStorage &, pug::Var *var, size_t argc) -> pug::Var {
1158 25 : if (var && argc == 1 && var->readValue().isString()) {
1159 50 : return pug::Var(Value(Context_default_encodeURIComponent(var->readValue().getString())));
1160 : }
1161 0 : return pug::Var();
1162 : });
1163 475 : }
1164 :
1165 : }
|