@@ -1385,6 +1385,13 @@ static std::string strip(const std::string & s) {
1385
1385
return s.substr (start, end - start + 1 );
1386
1386
}
1387
1387
1388
+ static std::string capitalize (const std::string & s) {
1389
+ if (s.empty ()) return s;
1390
+ auto result = s;
1391
+ result[0 ] = std::toupper (result[0 ]);
1392
+ return result;
1393
+ }
1394
+
1388
1395
static std::string html_escape (const std::string & s) {
1389
1396
std::string result;
1390
1397
result.reserve (s.size ());
@@ -1462,6 +1469,9 @@ class MethodCallExpr : public Expression {
1462
1469
if (method->get_name () == " strip" ) {
1463
1470
vargs.expectArgs (" strip method" , {0 , 0 }, {0 , 0 });
1464
1471
return Value (strip (str));
1472
+ } else if (method->get_name () == " capitalize" ) {
1473
+ vargs.expectArgs (" capitalize method" , {0 , 0 }, {0 , 0 });
1474
+ return Value (capitalize (str));
1465
1475
} else if (method->get_name () == " endswith" ) {
1466
1476
vargs.expectArgs (" endswith method" , {1 , 1 }, {0 , 0 });
1467
1477
auto suffix = vargs.args [0 ].get <std::string>();
@@ -1792,7 +1802,7 @@ class Parser {
1792
1802
auto left = parseStringConcat ();
1793
1803
if (!left) throw std::runtime_error (" Expected left side of 'logical compare' expression" );
1794
1804
1795
- static std::regex compare_tok (R"( ==|!=|<=?|>=?|in\b|is\b|not[\r\n\s] +in\b)" );
1805
+ static std::regex compare_tok (R"( ==|!=|<=?|>=?|in\b|is\b|not\s +in\b)" );
1796
1806
static std::regex not_tok (R"( not\b)" );
1797
1807
std::string op_str;
1798
1808
while (!(op_str = consumeToken (compare_tok)).empty ()) {
@@ -2171,7 +2181,7 @@ class Parser {
2171
2181
using TemplateTokenIterator = TemplateTokenVector::const_iterator;
2172
2182
2173
2183
std::vector<std::string> parseVarNames () {
2174
- static std::regex varnames_regex (R"( ((?:\w+)(?:[\r\n\s]*,[\r\n\s] *(?:\w+))*)[\r\n\s] *)" );
2184
+ static std::regex varnames_regex (R"( ((?:\w+)(?:\s*,\s *(?:\w+))*)\s *)" );
2175
2185
2176
2186
std::vector<std::string> group;
2177
2187
if ((group = consumeTokenGroups (varnames_regex)).empty ()) throw std::runtime_error (" Expected variable names" );
@@ -2194,13 +2204,13 @@ class Parser {
2194
2204
}
2195
2205
2196
2206
TemplateTokenVector tokenize () {
2197
- static std::regex comment_tok (R"( \{#([-~]?)([\s\S\r\n ]*?)([-~]?)#\})" );
2207
+ static std::regex comment_tok (R"( \{#([-~]?)([\s\S]*?)([-~]?)#\})" );
2198
2208
static std::regex expr_open_regex (R"( \{\{([-~])?)" );
2199
- static std::regex block_open_regex (R"( ^\{%([-~])?[\s\n\r] *)" );
2209
+ static std::regex block_open_regex (R"( ^\{%([-~])?\s *)" );
2200
2210
static std::regex block_keyword_tok (R"( (if|else|elif|endif|for|endfor|generation|endgeneration|set|endset|block|endblock|macro|endmacro|filter|endfilter|break|continue)\b)" );
2201
2211
static std::regex non_text_open_regex (R"( \{\{|\{%|\{#)" );
2202
- static std::regex expr_close_regex (R"( [\s\n\r] *([-~])?\}\})" );
2203
- static std::regex block_close_regex (R"( [\s\n\r] *([-~])?%\})" );
2212
+ static std::regex expr_close_regex (R"( \s *([-~])?\}\})" );
2213
+ static std::regex block_close_regex (R"( \s *([-~])?%\})" );
2204
2214
2205
2215
TemplateTokenVector tokens;
2206
2216
std::vector<std::string> group;
@@ -2284,7 +2294,7 @@ class Parser {
2284
2294
auto post_space = parseBlockClose ();
2285
2295
tokens.push_back (std::make_unique<EndGenerationTemplateToken>(location, pre_space, post_space));
2286
2296
} else if (keyword == " set" ) {
2287
- static std::regex namespaced_var_regex (R"( (\w+)[\s\n\r] *\.[\s\n\r] *(\w+))" );
2297
+ static std::regex namespaced_var_regex (R"( (\w+)\s *\.\s *(\w+))" );
2288
2298
2289
2299
std::string ns;
2290
2300
std::vector<std::string> var_names;
@@ -2336,6 +2346,11 @@ class Parser {
2336
2346
throw std::runtime_error (" Unexpected block: " + keyword);
2337
2347
}
2338
2348
} else if (std::regex_search (it, end, match, non_text_open_regex)) {
2349
+ if (!match.position ()) {
2350
+ if (match[0 ] != " {#" )
2351
+ throw std::runtime_error (" Internal error: Expected a comment" );
2352
+ throw std::runtime_error (" Missing end of comment tag" );
2353
+ }
2339
2354
auto text_end = it + match.position ();
2340
2355
text = std::string (it, text_end);
2341
2356
it = text_end;
@@ -2400,7 +2415,7 @@ class Parser {
2400
2415
2401
2416
auto text = text_token->text ;
2402
2417
if (post_space == SpaceHandling::Strip) {
2403
- static std::regex trailing_space_regex (R"( (\s|\r|\n) +$)" );
2418
+ static std::regex trailing_space_regex (R"( \s +$)" );
2404
2419
text = std::regex_replace (text, trailing_space_regex, " " );
2405
2420
} else if (options.lstrip_blocks && it != end) {
2406
2421
auto i = text.size ();
@@ -2410,7 +2425,7 @@ class Parser {
2410
2425
}
2411
2426
}
2412
2427
if (pre_space == SpaceHandling::Strip) {
2413
- static std::regex leading_space_regex (R"( ^(\s|\r|\n) +)" );
2428
+ static std::regex leading_space_regex (R"( ^\s +)" );
2414
2429
text = std::regex_replace (text, leading_space_regex, " " );
2415
2430
} else if (options.trim_blocks && (it - 1 ) != begin && !dynamic_cast <ExpressionTemplateToken*>((*(it - 2 )).get ())) {
2416
2431
if (text.length () > 0 && text[0 ] == ' \n ' ) {
0 commit comments