Skip to content

Commit f645887

Browse files
author
ochafik
committed
Update minja.hpp google/minja@202aa2f
1 parent f0bd693 commit f645887

File tree

1 file changed

+60
-28
lines changed

1 file changed

+60
-28
lines changed

common/minja.hpp

Lines changed: 60 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,12 @@
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+
2127
using json = nlohmann::ordered_json;
2228

2329
namespace minja {
@@ -32,6 +38,15 @@ struct Options {
3238

3339
struct 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. */
3651
class Value : public std::enable_shared_from_this<Value> {
3752
public:
@@ -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 {
12921307
static 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

12971316
static std::string html_escape(const std::string & s) {
@@ -1302,7 +1321,7 @@ static std::string html_escape(const std::string & s) {
13021321
case '&': result += "&amp;"; break;
13031322
case '<': result += "&lt;"; break;
13041323
case '>': result += "&gt;"; break;
1305-
case '"': result += "&quot;"; break;
1324+
case '"': result += "&#34;"; break;
13061325
case '\'': result += "&apos;"; 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 {
23572389
public:
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

Comments
 (0)