LCOV - code coverage report
Current view: top level - extra/webserver/pug - SPPugContext.cc (source / functions) Hit Total Coverage
Test: coverage.info Lines: 583 737 79.1 %
Date: 2024-05-12 00:16:13 Functions: 135 147 91.8 %

          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 << "&amp;"; break;
     408        1475 :                         case '<': out << "&lt;"; break;
     409        1475 :                         case '>': out << "&gt;"; break;
     410       33950 :                         case '"': out << "&quot;"; break;
     411         475 :                         case '\'': out << "&#39;"; 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             : }

Generated by: LCOV version 1.14