1818#include < unordered_set>
1919#include < json.hpp>
2020
21+ #ifdef _WIN32
22+ #define ENDL " \r\n "
23+ #else
24+ #define ENDL " \n "
25+ #endif
26+
2127using json = nlohmann::ordered_json;
2228
2329namespace minja {
@@ -32,6 +38,15 @@ struct Options {
3238
3339struct ArgumentsValue ;
3440
41+ static std::string normalize_newlines (const std::string & s) {
42+ #ifdef _WIN32
43+ static const std::regex nl_regex (" \r\n " );
44+ return std::regex_replace (s, nl_regex, " \n " );
45+ #else
46+ return s;
47+ #endif
48+ }
49+
3550/* Values that behave roughly like in Python. */
3651class Value : public std ::enable_shared_from_this<Value> {
3752public:
@@ -76,7 +91,7 @@ class Value : public std::enable_shared_from_this<Value> {
7691 void dump (std::ostringstream & out, int indent = -1 , int level = 0 , bool to_json = false ) const {
7792 auto print_indent = [&](int level) {
7893 if (indent > 0 ) {
79- out << " \n " ;
94+ out << ENDL ;
8095 for (int i = 0 , n = level * indent; i < n; ++i) out << ' ' ;
8196 }
8297 };
@@ -547,11 +562,11 @@ static std::string error_location_suffix(const std::string & source, size_t pos)
547562 auto max_line = std::count (start, end, ' \n ' ) + 1 ;
548563 auto col = pos - std::string (start, it).rfind (' \n ' );
549564 std::ostringstream out;
550- out << " at row " << line << " , column " << col << " :\n " ;
551- if (line > 1 ) out << get_line (line - 1 ) << " \n " ;
552- out << get_line (line) << " \n " ;
553- out << std::string (col - 1 , ' ' ) << " ^" << " \n " ;
554- if (line < max_line) out << get_line (line + 1 ) << " \n " ;
565+ out << " at row " << line << " , column " << col << " :" ENDL ;
566+ if (line > 1 ) out << get_line (line - 1 ) << ENDL ;
567+ out << get_line (line) << ENDL ;
568+ out << std::string (col - 1 , ' ' ) << " ^" << ENDL ;
569+ if (line < max_line) out << get_line (line + 1 ) << ENDL ;
555570
556571 return out.str ();
557572}
@@ -786,7 +801,7 @@ class TemplateNode {
786801 std::string render (const std::shared_ptr<Context> & context) const {
787802 std::ostringstream out;
788803 render (out, context);
789- return out.str ();
804+ return normalize_newlines ( out.str () );
790805 }
791806};
792807
@@ -1214,8 +1229,8 @@ class BinaryOpExpr : public Expression {
12141229 if (!l.to_bool ()) return Value (false );
12151230 return right->evaluate (context).to_bool ();
12161231 } else if (op == Op::Or) {
1217- if (l.to_bool ()) return Value ( true ) ;
1218- return right->evaluate (context). to_bool () ;
1232+ if (l.to_bool ()) return l ;
1233+ return right->evaluate (context);
12191234 }
12201235
12211236 auto r = right->evaluate (context);
@@ -1292,6 +1307,10 @@ struct ArgumentsExpression {
12921307static std::string strip (const std::string & s) {
12931308 static std::regex trailing_spaces_regex (" ^\\ s+|\\ s+$" );
12941309 return std::regex_replace (s, trailing_spaces_regex, " " );
1310+ // auto start = s.find_first_not_of(" \t\n\r");
1311+ // if (start == std::string::npos) return "";
1312+ // auto end = s.find_last_not_of(" \t\n\r");
1313+ // return s.substr(start, end - start + 1);
12951314}
12961315
12971316static std::string html_escape (const std::string & s) {
@@ -1302,7 +1321,7 @@ static std::string html_escape(const std::string & s) {
13021321 case ' &' : result += " &" ; break ;
13031322 case ' <' : result += " <" ; break ;
13041323 case ' >' : result += " >" ; break ;
1305- case ' "' : result += " " ;" ; break ;
1324+ case ' "' : result += " " ;" ; break ;
13061325 case ' \' ' : result += " '" ; break ;
13071326 default : result += c; break ;
13081327 }
@@ -2101,13 +2120,14 @@ class Parser {
21012120 static std::regex expr_open_regex (R"( \{\{([-~])?)" );
21022121 static std::regex block_open_regex (R"( ^\{%([-~])?[\s\n\r]*)" );
21032122 static std::regex block_keyword_tok (R"( (if|else|elif|endif|for|endfor|set|endset|block|endblock|macro|endmacro|filter|endfilter)\b)" );
2104- static std::regex text_regex (R"( [\s\S\n\r]*?($|(?=\ {\{|\{%|\{#)) )" );
2123+ static std::regex non_text_open_regex (R"( \ {\{|\{%|\{#)" );
21052124 static std::regex expr_close_regex (R"( [\s\n\r]*([-~])?\}\})" );
21062125 static std::regex block_close_regex (R"( [\s\n\r]*([-~])?%\})" );
21072126
21082127 TemplateTokenVector tokens;
21092128 std::vector<std::string> group;
21102129 std::string text;
2130+ std::smatch match;
21112131
21122132 try {
21132133 while (it != end) {
@@ -2228,10 +2248,15 @@ class Parser {
22282248 } else {
22292249 throw std::runtime_error (" Unexpected block: " + keyword);
22302250 }
2231- } else if (!(text = consumeToken (text_regex, SpaceHandling::Keep)).empty ()) {
2251+ } else if (std::regex_search (it, end, match, non_text_open_regex)) {
2252+ auto text_end = it + match.position ();
2253+ text = std::string (it, text_end);
2254+ it = text_end;
22322255 tokens.push_back (std::make_unique<TextTemplateToken>(location, SpaceHandling::Keep, SpaceHandling::Keep, text));
22332256 } else {
2234- if (it != end) throw std::runtime_error (" Unexpected character" );
2257+ text = std::string (it, end);
2258+ it = end;
2259+ tokens.push_back (std::make_unique<TextTemplateToken>(location, SpaceHandling::Keep, SpaceHandling::Keep, text));
22352260 }
22362261 }
22372262 return tokens;
@@ -2280,24 +2305,31 @@ class Parser {
22802305 SpaceHandling post_space = it != end ? (*it)->pre_space : SpaceHandling::Keep;
22812306
22822307 auto text = text_token->text ;
2283- if (pre_space == SpaceHandling::Strip) {
2284- static std::regex leading_space_regex (R"( ^(\s|\r|\n)+)" );
2285- text = std::regex_replace (text, leading_space_regex, " " );
2286- } else if (options.trim_blocks && (it - 1 ) != begin && !dynamic_cast <ExpressionTemplateToken*>((*(it - 2 )).get ())) {
2287- static std::regex leading_line (R"( ^[ \t]*\r?\n)" );
2288- text = std::regex_replace (text, leading_line, " " );
2289- }
22902308 if (post_space == SpaceHandling::Strip) {
22912309 static std::regex trailing_space_regex (R"( (\s|\r|\n)+$)" );
22922310 text = std::regex_replace (text, trailing_space_regex, " " );
22932311 } else if (options.lstrip_blocks && it != end) {
2294- static std::regex trailing_last_line_space_regex (R"( (\r?\n)[ \t]*$)" );
2295- text = std::regex_replace (text, trailing_last_line_space_regex, " $1" );
2312+ auto i = text.size ();
2313+ while (i > 0 && (text[i - 1 ] == ' ' || text[i - 1 ] == ' \t ' )) i--;
2314+ if ((i == 0 && (it - 1 ) == begin) || (i > 0 && text[i - 1 ] == ' \n ' )) {
2315+ text.resize (i);
2316+ }
2317+ }
2318+ if (pre_space == SpaceHandling::Strip) {
2319+ static std::regex leading_space_regex (R"( ^(\s|\r|\n)+)" );
2320+ text = std::regex_replace (text, leading_space_regex, " " );
2321+ } else if (options.trim_blocks && (it - 1 ) != begin && !dynamic_cast <ExpressionTemplateToken*>((*(it - 2 )).get ())) {
2322+ if (text.length () > 0 && text[0 ] == ' \n ' ) {
2323+ text.erase (0 , 1 );
2324+ }
22962325 }
2297-
22982326 if (it == end && !options.keep_trailing_newline ) {
2299- static std::regex r (R"( \r?\n$)" );
2300- text = std::regex_replace (text, r, " " ); // Strip one trailing newline
2327+ auto i = text.size ();
2328+ if (i > 0 && text[i - 1 ] == ' \n ' ) {
2329+ i--;
2330+ if (i > 0 && text[i - 1 ] == ' \r ' ) i--;
2331+ text.resize (i);
2332+ }
23012333 }
23022334 children.emplace_back (std::make_shared<TextNode>(token->location , text));
23032335 } else if (auto expr_token = dynamic_cast <ExpressionTemplateToken*>(token.get ())) {
@@ -2357,7 +2389,7 @@ class Parser {
23572389public:
23582390
23592391 static std::shared_ptr<TemplateNode> parse (const std::string& template_str, const Options & options) {
2360- Parser parser (std::make_shared<std::string>(template_str), options);
2392+ Parser parser (std::make_shared<std::string>(normalize_newlines ( template_str) ), options);
23612393 auto tokens = parser.tokenize ();
23622394 TemplateTokenIterator begin = tokens.begin ();
23632395 auto it = begin;
@@ -2627,11 +2659,11 @@ inline std::shared_ptr<Context> Context::builtins() {
26272659 while (std::getline (iss, line, ' \n ' )) {
26282660 auto needs_indent = !is_first || first;
26292661 if (is_first) is_first = false ;
2630- else out += " \n " ;
2662+ else out += ENDL ;
26312663 if (needs_indent) out += indent;
26322664 out += line;
26332665 }
2634- if (!text.empty () && text.back () == ' \n ' ) out += " \n " ;
2666+ if (!text.empty () && text.back () == ' \n ' ) out += ENDL ;
26352667 return out;
26362668 }));
26372669 globals.set (" selectattr" , Value::callable ([=](const std::shared_ptr<Context> & context, ArgumentsValue & args) {
0 commit comments