summaryrefslogtreecommitdiffstats
path: root/desktop-widgets/templatelayout.cpp
diff options
context:
space:
mode:
authorGravatar Berthold Stoeger <bstoeger@mail.tuwien.ac.at>2020-12-12 17:20:00 +0100
committerGravatar Dirk Hohndel <dirk@hohndel.org>2020-12-17 13:03:56 -0800
commit0310eb31da6f58741189d43f0397c083752caab3 (patch)
treedcffbaa3112fa2275b5c41f4d67c7089fe40b2e2 /desktop-widgets/templatelayout.cpp
parentf42a70586be15fcbb0f82260bd6774572cd375d2 (diff)
downloadsubsurface-0310eb31da6f58741189d43f0397c083752caab3.tar.gz
printing: refactor if and loop code
The loop code was buggy: the current position was only increased inside when executing the loop once. This would obviously fail for empty lists. Moreover, the whole thing was quite difficult to reason about, since a reference to the current position was passed down in the call hierarchy. Instead, pass from and to values to the parse function and create a generic function that can search for the end of loop and if blocks. This function handles nested if and for loops. The if-code now formats the block only if the condition is true. The old code would format the block and throw it away if not needed. This should now provide better diagnostics for mismatched tags. Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
Diffstat (limited to 'desktop-widgets/templatelayout.cpp')
-rw-r--r--desktop-widgets/templatelayout.cpp72
1 files changed, 48 insertions, 24 deletions
diff --git a/desktop-widgets/templatelayout.cpp b/desktop-widgets/templatelayout.cpp
index 89222b8a8..c69d24b29 100644
--- a/desktop-widgets/templatelayout.cpp
+++ b/desktop-widgets/templatelayout.cpp
@@ -129,8 +129,7 @@ QString TemplateLayout::generate()
QList<token> tokens = lexer(templateContents);
QString buffer;
QTextStream out(&buffer);
- int pos = 0;
- parser(tokens, pos, out, state);
+ parser(tokens, 0, tokens.size(), out, state);
htmlContent = out.readAll();
return htmlContent;
}
@@ -156,8 +155,7 @@ QString TemplateLayout::generateStatistics()
QList<token> tokens = lexer(templateContents);
QString buffer;
QTextStream out(&buffer);
- int pos = 0;
- parser(tokens, pos, out, state);
+ parser(tokens, 0, tokens.size(), out, state);
htmlContent = out.readAll();
return htmlContent;
}
@@ -286,35 +284,47 @@ static QRegularExpression forloop(R"(\s*(\w+)\s+in\s+(\w+))"); // Look for "VAR
static QRegularExpression ifstatement(R"(forloop\.counter\|\s*divisibleby\:\s*(\d+))"); // Look for forloop.counter|divisibleby: NUMBER
template<typename V, typename T>
-void TemplateLayout::parser_for(QList<token> tokenList, int &pos, QTextStream &out, State &state,
+void TemplateLayout::parser_for(QList<token> tokenList, int from, int to, QTextStream &out, State &state,
const V &data, const T *&act)
{
const T *old = act;
int i = 1; // Loop iterators start at one
int olditerator = state.forloopiterator;
- int savepos = pos;
for (const T &item: data) {
act = &item;
- state.forloopiterator = i;
- pos = savepos;
- ++i;
- parser(tokenList, pos, out, state);
+ state.forloopiterator = i++;
+ parser(tokenList, from, to, out, state);
}
act = old;
state.forloopiterator = olditerator;
}
-void TemplateLayout::parser(QList<token> tokenList, int &pos, QTextStream &out, State &state)
+// Find end of for or if block. Keeps track of nested blocks.
+// Pos should point one past the starting tag.
+// Returns -1 if no matching end tag found.
+static int findEnd(const QList<token> &tokenList, int from, int to, token_t start, token_t end)
{
- while (pos < tokenList.length()) {
+ int depth = 1;
+ for (int pos = from; pos < to; ++pos) {
+ if (tokenList[pos].type == start) {
+ ++depth;
+ } else if (tokenList[pos].type == end) {
+ if (--depth <= 0)
+ return pos;
+ }
+ }
+ return -1;
+}
+
+void TemplateLayout::parser(QList<token> tokenList, int from, int to, QTextStream &out, State &state)
+{
+ for (int pos = from; pos < to; ++pos) {
switch (tokenList[pos].type) {
case LITERAL:
out << translate(tokenList[pos].contents, state);
- ++pos;
break;
case BLOCKSTART:
case BLOCKSTOP:
- ++pos;
break;
case FORSTART:
{
@@ -327,23 +337,31 @@ void TemplateLayout::parser(QList<token> tokenList, int &pos, QTextStream &out,
state.types[itemname] = listname;
QString buffer;
QTextStream capture(&buffer);
+ int loop_end = findEnd(tokenList, pos, to, FORSTART, FORSTOP);
+ if (loop_end < 0) {
+ out << "UNMATCHED FOR: '" << argument << "'";
+ break;
+ }
if (listname == "years") {
- parser_for(tokenList, pos, capture, state, state.years, state.currentYear);
+ parser_for(tokenList, pos, loop_end, capture, state, state.years, state.currentYear);
} else if (listname == "dives") {
- parser_for(tokenList, pos, capture, state, state.dives, state.currentDive);
+ parser_for(tokenList, pos, loop_end, capture, state, state.dives, state.currentDive);
} else if (listname == "cylinders") {
if (state.currentDive)
- parser_for(tokenList, pos, capture, state, state.currentDive->cylinders, state.currentCylinder);
+ parser_for(tokenList, pos, loop_end, capture, state, state.currentDive->cylinders, state.currentCylinder);
else
qWarning("cylinders loop outside of dive");
} else if (listname == "cylinderObjects") {
if (state.currentDive)
- parser_for(tokenList, pos, capture, state, state.currentDive->cylinderObjects, state.currentCylinderObject);
+ parser_for(tokenList, pos, loop_end, capture, state, state.currentDive->cylinderObjects, state.currentCylinderObject);
else
qWarning("cylinderObjects loop outside of dive");
+ } else {
+ qWarning("unknown loop: %s", qPrintable(listname));
}
state.types.remove(itemname);
out << capture.readAll();
+ pos = loop_end;
} else {
out << "PARSING ERROR: '" << argument << "'";
}
@@ -355,13 +373,20 @@ void TemplateLayout::parser(QList<token> tokenList, int &pos, QTextStream &out,
++pos;
QRegularExpressionMatch match = ifstatement.match(argument);
if (match.hasMatch()) {
+ int if_end = findEnd(tokenList, pos, to, IFSTART, IFSTOP);
+ if (if_end < 0) {
+ out << "UNMATCHED IF: '" << argument << "'";
+ break;
+ }
int divisor = match.captured(1).toInt();
- QString buffer;
- QTextStream capture(&buffer);
int counter = std::max(0, state.forloopiterator);
- parser(tokenList, pos, capture, state);
- if (!(counter % divisor))
+ if (!(counter % divisor)) {
+ QString buffer;
+ QTextStream capture(&buffer);
+ parser(tokenList, pos, if_end, capture, state);
out << capture.readAll();
+ }
+ pos = if_end;
} else {
out << "PARSING ERROR: '" << argument << "'";
}
@@ -369,11 +394,10 @@ void TemplateLayout::parser(QList<token> tokenList, int &pos, QTextStream &out,
break;
case FORSTOP:
case IFSTOP:
- ++pos;
+ out << "UNEXPECTED END: " << tokenList[pos].contents;
return;
case PARSERERROR:
out << "PARSING ERROR";
- ++pos;
}
}
}