@@ -1385,6 +1385,13 @@ static std::string strip(const std::string & s) {
13851385 return s.substr (start, end - start + 1 );
13861386}
13871387
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+
13881395static std::string html_escape (const std::string & s) {
13891396 std::string result;
13901397 result.reserve (s.size ());
@@ -1462,6 +1469,9 @@ class MethodCallExpr : public Expression {
14621469 if (method->get_name () == " strip" ) {
14631470 vargs.expectArgs (" strip method" , {0 , 0 }, {0 , 0 });
14641471 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));
14651475 } else if (method->get_name () == " endswith" ) {
14661476 vargs.expectArgs (" endswith method" , {1 , 1 }, {0 , 0 });
14671477 auto suffix = vargs.args [0 ].get <std::string>();
@@ -1792,7 +1802,7 @@ class Parser {
17921802 auto left = parseStringConcat ();
17931803 if (!left) throw std::runtime_error (" Expected left side of 'logical compare' expression" );
17941804
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)" );
17961806 static std::regex not_tok (R"( not\b)" );
17971807 std::string op_str;
17981808 while (!(op_str = consumeToken (compare_tok)).empty ()) {
@@ -2171,7 +2181,7 @@ class Parser {
21712181 using TemplateTokenIterator = TemplateTokenVector::const_iterator;
21722182
21732183 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 *)" );
21752185
21762186 std::vector<std::string> group;
21772187 if ((group = consumeTokenGroups (varnames_regex)).empty ()) throw std::runtime_error (" Expected variable names" );
@@ -2194,13 +2204,13 @@ class Parser {
21942204 }
21952205
21962206 TemplateTokenVector tokenize () {
2197- static std::regex comment_tok (R"( \{#([-~]?)([\s\S\r\n ]*?)([-~]?)#\})" );
2207+ static std::regex comment_tok (R"( \{#([-~]?)([\s\S]*?)([-~]?)#\})" );
21982208 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 *)" );
22002210 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)" );
22012211 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 *([-~])?%\})" );
22042214
22052215 TemplateTokenVector tokens;
22062216 std::vector<std::string> group;
@@ -2284,7 +2294,7 @@ class Parser {
22842294 auto post_space = parseBlockClose ();
22852295 tokens.push_back (std::make_unique<EndGenerationTemplateToken>(location, pre_space, post_space));
22862296 } 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+))" );
22882298
22892299 std::string ns;
22902300 std::vector<std::string> var_names;
@@ -2336,6 +2346,11 @@ class Parser {
23362346 throw std::runtime_error (" Unexpected block: " + keyword);
23372347 }
23382348 } 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+ }
23392354 auto text_end = it + match.position ();
23402355 text = std::string (it, text_end);
23412356 it = text_end;
@@ -2400,7 +2415,7 @@ class Parser {
24002415
24012416 auto text = text_token->text ;
24022417 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 +$)" );
24042419 text = std::regex_replace (text, trailing_space_regex, " " );
24052420 } else if (options.lstrip_blocks && it != end) {
24062421 auto i = text.size ();
@@ -2410,7 +2425,7 @@ class Parser {
24102425 }
24112426 }
24122427 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 +)" );
24142429 text = std::regex_replace (text, leading_space_regex, " " );
24152430 } else if (options.trim_blocks && (it - 1 ) != begin && !dynamic_cast <ExpressionTemplateToken*>((*(it - 2 )).get ())) {
24162431 if (text.length () > 0 && text[0 ] == ' \n ' ) {
0 commit comments