mirror of
https://github.com/subsurface/subsurface.git
synced 2025-02-19 22:16:15 +00:00
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>
This commit is contained in:
parent
f42a70586b
commit
0310eb31da
2 changed files with 50 additions and 26 deletions
|
@ -129,8 +129,7 @@ QString TemplateLayout::generate()
|
||||||
QList<token> tokens = lexer(templateContents);
|
QList<token> tokens = lexer(templateContents);
|
||||||
QString buffer;
|
QString buffer;
|
||||||
QTextStream out(&buffer);
|
QTextStream out(&buffer);
|
||||||
int pos = 0;
|
parser(tokens, 0, tokens.size(), out, state);
|
||||||
parser(tokens, pos, out, state);
|
|
||||||
htmlContent = out.readAll();
|
htmlContent = out.readAll();
|
||||||
return htmlContent;
|
return htmlContent;
|
||||||
}
|
}
|
||||||
|
@ -156,8 +155,7 @@ QString TemplateLayout::generateStatistics()
|
||||||
QList<token> tokens = lexer(templateContents);
|
QList<token> tokens = lexer(templateContents);
|
||||||
QString buffer;
|
QString buffer;
|
||||||
QTextStream out(&buffer);
|
QTextStream out(&buffer);
|
||||||
int pos = 0;
|
parser(tokens, 0, tokens.size(), out, state);
|
||||||
parser(tokens, pos, out, state);
|
|
||||||
htmlContent = out.readAll();
|
htmlContent = out.readAll();
|
||||||
return htmlContent;
|
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
|
static QRegularExpression ifstatement(R"(forloop\.counter\|\s*divisibleby\:\s*(\d+))"); // Look for forloop.counter|divisibleby: NUMBER
|
||||||
|
|
||||||
template<typename V, typename T>
|
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 V &data, const T *&act)
|
||||||
{
|
{
|
||||||
const T *old = act;
|
const T *old = act;
|
||||||
int i = 1; // Loop iterators start at one
|
int i = 1; // Loop iterators start at one
|
||||||
int olditerator = state.forloopiterator;
|
int olditerator = state.forloopiterator;
|
||||||
int savepos = pos;
|
|
||||||
for (const T &item: data) {
|
for (const T &item: data) {
|
||||||
act = &item;
|
act = &item;
|
||||||
state.forloopiterator = i;
|
state.forloopiterator = i++;
|
||||||
pos = savepos;
|
parser(tokenList, from, to, out, state);
|
||||||
++i;
|
|
||||||
parser(tokenList, pos, out, state);
|
|
||||||
}
|
}
|
||||||
act = old;
|
act = old;
|
||||||
state.forloopiterator = olditerator;
|
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) {
|
switch (tokenList[pos].type) {
|
||||||
case LITERAL:
|
case LITERAL:
|
||||||
out << translate(tokenList[pos].contents, state);
|
out << translate(tokenList[pos].contents, state);
|
||||||
++pos;
|
|
||||||
break;
|
break;
|
||||||
case BLOCKSTART:
|
case BLOCKSTART:
|
||||||
case BLOCKSTOP:
|
case BLOCKSTOP:
|
||||||
++pos;
|
|
||||||
break;
|
break;
|
||||||
case FORSTART:
|
case FORSTART:
|
||||||
{
|
{
|
||||||
|
@ -327,23 +337,31 @@ void TemplateLayout::parser(QList<token> tokenList, int &pos, QTextStream &out,
|
||||||
state.types[itemname] = listname;
|
state.types[itemname] = listname;
|
||||||
QString buffer;
|
QString buffer;
|
||||||
QTextStream capture(&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") {
|
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") {
|
} 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") {
|
} else if (listname == "cylinders") {
|
||||||
if (state.currentDive)
|
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
|
else
|
||||||
qWarning("cylinders loop outside of dive");
|
qWarning("cylinders loop outside of dive");
|
||||||
} else if (listname == "cylinderObjects") {
|
} else if (listname == "cylinderObjects") {
|
||||||
if (state.currentDive)
|
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
|
else
|
||||||
qWarning("cylinderObjects loop outside of dive");
|
qWarning("cylinderObjects loop outside of dive");
|
||||||
|
} else {
|
||||||
|
qWarning("unknown loop: %s", qPrintable(listname));
|
||||||
}
|
}
|
||||||
state.types.remove(itemname);
|
state.types.remove(itemname);
|
||||||
out << capture.readAll();
|
out << capture.readAll();
|
||||||
|
pos = loop_end;
|
||||||
} else {
|
} else {
|
||||||
out << "PARSING ERROR: '" << argument << "'";
|
out << "PARSING ERROR: '" << argument << "'";
|
||||||
}
|
}
|
||||||
|
@ -355,13 +373,20 @@ void TemplateLayout::parser(QList<token> tokenList, int &pos, QTextStream &out,
|
||||||
++pos;
|
++pos;
|
||||||
QRegularExpressionMatch match = ifstatement.match(argument);
|
QRegularExpressionMatch match = ifstatement.match(argument);
|
||||||
if (match.hasMatch()) {
|
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();
|
int divisor = match.captured(1).toInt();
|
||||||
QString buffer;
|
|
||||||
QTextStream capture(&buffer);
|
|
||||||
int counter = std::max(0, state.forloopiterator);
|
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();
|
out << capture.readAll();
|
||||||
|
}
|
||||||
|
pos = if_end;
|
||||||
} else {
|
} else {
|
||||||
out << "PARSING ERROR: '" << argument << "'";
|
out << "PARSING ERROR: '" << argument << "'";
|
||||||
}
|
}
|
||||||
|
@ -369,11 +394,10 @@ void TemplateLayout::parser(QList<token> tokenList, int &pos, QTextStream &out,
|
||||||
break;
|
break;
|
||||||
case FORSTOP:
|
case FORSTOP:
|
||||||
case IFSTOP:
|
case IFSTOP:
|
||||||
++pos;
|
out << "UNEXPECTED END: " << tokenList[pos].contents;
|
||||||
return;
|
return;
|
||||||
case PARSERERROR:
|
case PARSERERROR:
|
||||||
out << "PARSING ERROR";
|
out << "PARSING ERROR";
|
||||||
++pos;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -51,9 +51,9 @@ private:
|
||||||
const print_options &printOptions;
|
const print_options &printOptions;
|
||||||
const template_options &templateOptions;
|
const template_options &templateOptions;
|
||||||
QList<token> lexer(QString input);
|
QList<token> lexer(QString input);
|
||||||
void parser(QList<token> tokenList, int &pos, QTextStream &out, State &state);
|
void parser(QList<token> tokenList, int from, int to, QTextStream &out, State &state);
|
||||||
template<typename V, typename T>
|
template<typename V, typename T>
|
||||||
void parser_for(QList<token> tokenList, int &pos, QTextStream &out, State &state, const V &data, const T *&act);
|
void parser_for(QList<token> tokenList, int from, int to, QTextStream &out, State &state, const V &data, const T *&act);
|
||||||
QVariant getValue(QString list, QString property, const State &state);
|
QVariant getValue(QString list, QString property, const State &state);
|
||||||
QString translate(QString s, State &state);
|
QString translate(QString s, State &state);
|
||||||
|
|
||||||
|
|
Loading…
Add table
Reference in a new issue