I have a document in a *.css (Cascading Style Sheets) like format, but it has its own keywords. Actually it is a personalized css (I call it *.pss), with own tags and properties. here I have an excerpt:
/* CSS like style sheet file *.pss */
@include "otherStyleSheet.pss";
/* comment */
[propertyID="1230000"] {
fillColor : #f3f1ed;
minSize : 5;
lineWidth : 3;
}
/* sphere */
[propertyID="124???|123000"] {
lineType : dotted;
}
/* square */
[propertyID="125???"] {
lineType : thinline;
}
/* ring */
[propertyID="133???"] {
lineType : thickline;
[hasInnerRing=true] {
innerLineType : thinline;
}
}
I would like to parse it very easily, is there already something Ready-To-Use from Qt? What would be the easiest way?
Since *.css has its own keywords, I am NOT interessted in CSS parsers.
My further intention after parsing that *.pss is to store its properties in a Model structure .
There's nothing public within Qt. You're of course free to use the Qt's private CSS parser - you can copy it and modify to fit your needs.
See qtbase/src/gui/text/qcssparser_p.h
, in qtbase/src/gui/text
.
The good news is that for the example you've shown above, the modifications would be very minor. Qt's CSS parser already supports @import
, so we only additional bit of syntax you have is the nested selector syntax. Without that syntax, you can use QCss::Parser
as-is. The parser was written in a flexible fashion, where you don't need to worry about formal CSS keywords: it will still let you access all the declarations, whether they make sense from the formal CSS point of view or not.
Iterating the parse tree is as simple as it gets:
int main() {
QCss::Parser parser(pss);
QCss::StyleSheet styleSheet;
if (!parser.parse(&styleSheet))
return 1;
for (auto rule : styleSheet.styleRules) {
qDebug() << "** Rule **";
for (auto sel : rule.selectors) {
for (auto bSel : sel.basicSelectors)
qDebug() << bSel;
}
for (auto decl : rule.declarations)
qDebug() << decl;
}
}
The output is what we'd expect:
** Rule **
BasicSelector "propertyID"="1230000"
Declaration "fillColor" = '#f3f1ed' % QColor(ARGB 1, 0.952941, 0.945098, 0.929412)
Declaration "minSize" = '5' % 5
Declaration "lineWidth" = '3'
** Rule **
BasicSelector "propertyID"="124???|123000"
Declaration "lineType" = 'dotted'
** Rule **
BasicSelector "propertyID"="125???"
Declaration "lineType" = 'thinline'
** Rule **
BasicSelector "propertyID"="133???"
Declaration "lineType" = 'thickline'
We have to implement the debug stream operators for QCss
classes ourselves:
QDebug operator<<(QDebug dbg, const QCss::AttributeSelector & sel) {
QDebugStateSaver saver(dbg);
dbg.noquote().nospace() << "\"" << sel.name << "\"";
switch (sel.valueMatchCriterium) {
case QCss::AttributeSelector::MatchEqual:
dbg << "="; break;
case QCss::AttributeSelector::MatchContains:
dbg << "~="; break;
case QCss::AttributeSelector::MatchBeginsWith:
dbg << "^="; break;
case QCss::AttributeSelector::NoMatch:
break;
}
if (sel.valueMatchCriterium != QCss::AttributeSelector::NoMatch && !sel.value.isEmpty())
dbg << "\"" << sel.value << "\"";
return dbg;
}
QDebug operator<<(QDebug dbg, const QCss::BasicSelector & sel) {
QDebugStateSaver saver(dbg);
dbg.noquote().nospace() << "BasicSelector";
if (!sel.elementName.isEmpty())
dbg << " #" << sel.elementName;
for (auto & id : sel.ids)
dbg << " id:" << id;
for (auto & aSel : sel.attributeSelectors)
dbg << " " << aSel;
return dbg;
}
When traversing the declaration, the QCss::parser
already interprets some standard values for us, e.g. colors, integers, etc.
QDebug operator<<(QDebug dbg, const QCss::Declaration & decl) {
QDebugStateSaver saver(dbg);
dbg.noquote().nospace() << "Declaration";
dbg << " \"" << decl.d->property << "\" = ";
bool first = true;
for (auto value : decl.d->values) {
if (!first) dbg << ", ";
dbg << "\'" << value.toString() << "\'";
first = false;
}
if (decl.d->property == "fillColor")
dbg << " % " << decl.colorValue();
else if (decl.d->property == "minSize") {
int i;
if (decl.intValue(&i)) dbg << " % " << i;
}
return dbg;
}
Finally, the boilerplate and the stylesheet to be parsed:
// https://github.com/KubaO/stackoverflown/tree/master/questions/css-like-parser-31583622
#include <QtGui>
#include <private/qcssparser_p.h>
const char pss[] =
"/* @include \"otherStyleSheet.pss\"; */ \
[propertyID=\"1230000\"] { \
fillColor : #f3f1ed; \
minSize : 5; \
lineWidth : 3; \
} \
\
/* sphere */ \
[propertyID=\"124???|123000\"] { \
lineType : dotted; \
} \
\
/* square */ \
[propertyID=\"125???\"] { \
lineType : thinline; \
} \
\
/* ring */ \
[propertyID=\"133???\"] { \
lineType : thickline; \
/*[hasInnerRing=true] { \
innerLineType : thinline; \
}*/ \
}";
Support for nested selectors/rules can be implemented by modifying the parser source. The change needed to make Parser::parseRuleset
recursive is very minor. I'll leave this as the exercise for the reader :)
All in all, I'd think that reusing the existing parser is much easier than rolling your own, especially as your users will inevitably wish you to support more and more of the CSS spec.
I know two possibilities:
Due to the fact, that your personalized *.pss is not that complex as a CSS (simple bracketing etc.), I would recommend 2.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With