JKQTPlotter trunk/v5.0.0
an extensive Qt5+Qt6 Plotter framework (including a feature-richt plotter widget, a speed-optimized, but limited variant and a LaTeX equation renderer!), written fully in C/C++ and without external dependencies
Loading...
Searching...
No Matches
jkqtpcsstools.h
1
2/*
3 Copyright (c) 2008-2024 Jan W. Krieger (<jan@jkrieger.de>)
4
5
6
7 This software is free software: you can redistribute it and/or modify
8 it under the terms of the GNU Lesser General Public License as published by
9 the Free Software Foundation, either version 3 of the License, or
10 (at your option) any later version.
11
12 This program is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU Lesser General Public License for more details.
16
17 You should have received a copy of the GNU Lesser General Public License
18 along with this program. If not, see <http://www.gnu.org/licenses/>.
19*/
20
21#ifndef JKQTPCSSTOOLS_H_INCLUDED
22#define JKQTPCSSTOOLS_H_INCLUDED
23#include "jkqtcommon/jkqtcommon_imexport.h"
24#include <QString>
25#include <QColor>
26#include <QGradient>
27#include <QStack>
28#include "jkqtcommon/jkqtpmathtools.h"
29#include "jkqtcommon/jkqtpexpected.h"
30
31
32/*! \brief A simple parser for certain CSS subsets
33 \ingroup jkqtptools_math_parser
34
35 \section jkqtpcss_ebnf EBNF definition of the parsed expressions
36
37<pre>
38<lingradient> -> <b>linear-gradient(</b> <angle>? <b>,</b> <gradientstops> <b>)</b>
39<angle> -> <numberwithunit>
40 | <b>to</b> (<b>left</b>|<b>right</b>|<b>bottom</b>|<b>top</b>) (<b>left</b>|<b>right</b>|<b>bottom</b>|<b>top</b>)?
41<numberwithunit> -> <number> [<b>A</b>-<b>Z</b><b>a</b>-<b>z</b><b>%</b>]*
42<gradientstops> -> <gradientstop> | <gradientstop> <b>,</b> <gradientstops>
43<gradientstop> -> <color> (<number><b>%</b>)?
44<color> -> NAME
45 | <b>#</b><hexnumber>
46 | (<b>rgb</b> | <b>rgba</b> | <b>hsl</b> | <b>hsv</b> | <b>gray</b> | <b>grey</b> | <b>red</b> | <b>green</b> | <b>blue</b>) <b>(</b> (<numberwithunit> [<b>,/</b>]? ){1-4} <b>)</b>
47
48<number> -> floating-point-number, i.e. "[+-]?\d+\.?\d*"
49<hexnumber> -> RGB | RRGGBB | RGBA | RRGGBBAA
50</pre>
51
52
53 */
55{
56public:
57
58 /** \brief encodes a number with its unit, e.g. \c 100% or \c 45deg or ... */
60 inline NumberWithUnit(double val=0.0, const QString& unit_=QString()): unit(unit_), number(val) {};
61 /** \bref unit as string */
62 QString unit;
63 /** \brief the number value ittself */
64 double number;
65 /** \brief partly evaluates the unit and transforms the number accordingly.
66 *
67 * This will do these transformations:
68 * - unit == \c % --> returns number/100.0;
69 * - unit == angle unit : returns number in degrees:
70 * - unit == \c rad --> returns number/M_PI*180.0
71 * - unit == \c turn --> returns number*360.0
72 * - unit == \c grad --> returns number/400.0*360.0
73 * .
74 * .
75 */
76 inline double normNumber() const {
77 const QString nu=normUnit();
78 if (nu=="%") return number/100.0;
79 if (nu=="rad") return number/JKQTPSTATISTICS_PI*180.0;
80 if (nu=="turn") return number*360.0;
81 if (nu=="grad") return number/400.0*360.0;
82 return number;
83 }
84 /** \brief returns number as a normalized number for RGB, output is in the range 0..1
85 *
86 * This interprets %-values and all other values as from the range 0..255 and noralizes both to 0..1
87 */
88 inline double normRGBNumber() const {
89 const QString nu=normUnit();
90 if (nu=="%") return jkqtp_bounded(0.0,number/100.0, 1.0);
91 return jkqtp_bounded(0.0,number/255.0, 1.0);
92 }
93 /** \brief returns number as a normalized number for Hue, output is in the range 0..1
94 *
95 * This interprets %-values and all other values as from the range 0..360 and noralizes both to 0..1
96 */
97 inline double normHueNumber() const {
98 const QString nu=normUnit();
99 if (nu=="%") return jkqtp_bounded(0.0,number/100.0, 1.0);
100 if (nu=="rad") return fmod(number/(JKQTPSTATISTICS_PI*2.0),1.0);
101 if (nu=="turn") return fmod(number,1.0);
102 if (nu=="grad") return fmod(number/400.0,1.0);
103 return fmod(number/360.0, 1.0);
104 }
105 /** \brief returns a normalized version of the unit, i.e. all lower-case, timmed and simplified */
106 inline QString normUnit() const { return unit.simplified().trimmed().toLower(); }
107
108 inline bool operator==(const NumberWithUnit& other) const {
109 return number==other.number && unit==other.unit;
110 }
111 };
112
113
114
115protected:
116 /** @name Tokenizer */
117 /**@{*/
118
119 struct Token {
120 /** \brief the possible Token that can be recognized by the tokenizer in JKQTPCSSParser::getToken() */
122 END, /*!< \brief end token */
123 NAME, /*!< \brief a name (consisting of characters) of a variable or function */
124 NUMBER, /*!< \brief a number, possibly with a unit(string) */
125 HEXSTRING, /*!< \brief a string in HEX notation (i.e. 0-9a-fA-F) */
126 LBRACKET, /*!< \brief left brackets '(' */
127 RBRACKET, /*!< \brief right brackets ')' */
128 COMMA, /*!< \brief a comma ',' */
129 SLASH, /*!< \brief a slash '/' */
130 };
131
132 static QString toString(TokenType type);
133
136 Token(double num, const QString& unit_=QString());
137 Token(const QString& str, TokenType type=NAME);
139 /** \brief the string value of the current token (when applicable) during the parsing step */
140 QString StringValue;
141
142 /** \brief the string value of the current token (when applicable) during the parsing step */
144
145 /** \brief checks whether the current token is of the given type (does not compare any other field) */
146 inline bool is(TokenType othertype) const {
147 return (type==othertype);
148 }
149 /** \brief checks whether the current token is of type TokenType::NAME and the String value equals \a name (case-insensitive commparison, trimmer, simplified) */
150 inline bool isNormString(const QString& name) const {
151 return (type==TokenType::NAME) && (getNormString()==name.toLower().simplified().trimmed());
152 }
153 /** \brief checks whether the current token is of type TokenType::NAME and the String value equals any of the entries in \a names (case-insensitive commparison, trimmer, simplified) */
154 inline bool isNormStringAnyOf(const QStringList& names) const {
155 if (type==TokenType::NAME) {
156 const QString ns=getNormString();
157 for (const auto&name: names) {
158 if (ns==name.toLower().simplified().trimmed()) return true;
159 }
160 }
161 return false;
162 }
163 /** \brief normlizes the StringValue (i.e. lower-case, trimmer, simmplified) */
164 inline QString getNormString() const {
165 return StringValue.toLower().simplified().trimmed();
166 }
167 /** \brief converts the TOken information to a string */
168 QString toString() const;
169 };
170
171
172
173 /** \brief Tokenizer: look at the next token from the input, but don't set it as CurrentToken and don't move the read pointer pos */
174 Token peekNextToken(int* endPos=nullptr);
175 /** \brief Tokenizer: extract the next token from the input */
177
178 /** \brief the current token while parsing a string */
180
181
182 /** \brief this stream is used to read in the program. An object is created and assigned
183 * (and destroyed) by the parse()-function */
184 QString text;
185 /** \brief current reading position in text */
186 int pos;
187
188 /** \brief indicates whether pos points to the end of text */
189 inline bool textAtEnd() const {
190 return pos<0 || pos>=text.size()-1;
191 }
192
193 inline bool getCh(QChar& ch) {
194 if (pos<0 || pos>=text.size()) return false;
195 ch=text[pos];
196 pos++;
197 return true;
198 }
199
200 inline bool peekCh(QChar& ch) {
201 if (pos<0 || pos>=text.size()) return false;
202 ch=text[pos];
203 return true;
204 }
205
206 inline void putBackCh() {
207 pos--;
208 }
209
210
211 /**@}*/
212
213 /** @name Error Handling */
214 /**@{*/
215
216 struct RawErrorTag_t {};
219 inline GeneralError(const QString& err=QString("unspecified error"), int p=-1): error(QString("%1 at pos.%2").arg(err).arg(p)), pos(p) {}
220 inline GeneralError(RawErrorTag_t, const QString& err, int p=-1): error(err), pos(p) { }
221 inline GeneralError(const QString& context, const QString& err, int p=-1): error(QString("%1 in context %3 at pos.%2").arg(err).arg(p).arg(context)), pos(p) {}
222 QString error;
223 int pos;
224 };
225 /** \brief Exception for unexpected Token */
227 inline UnexpectedTokenError(Token::TokenType expectedToken, Token::TokenType foundToken, int pos):
228 GeneralError(RawErrorTag, QString("unexpected token at pos.%1 (found: %2, expected: %3)").arg(pos).arg(Token::toString(foundToken)).arg(Token::toString(expectedToken)), pos)
229 {}
230 inline UnexpectedTokenError(Token::TokenType expectedToken, const Token& foundToken, int pos):
231 GeneralError(RawErrorTag, QString("unexpected token at pos.%1 (found: %2, expected: %3)").arg(pos).arg(foundToken.toString()).arg(Token::toString(expectedToken)), pos)
232 {}
233 inline UnexpectedTokenError(const QString& context, Token::TokenType expectedToken, const Token& foundToken, int pos):
234 GeneralError(RawErrorTag, QString("unexpected token at pos.%1 in context %4 (found: %2, expected: %3)").arg(pos).arg(foundToken.toString()).arg(Token::toString(expectedToken)).arg(context), pos)
235 {}
236 };
237 /** \brief Exception for unexpected Term */
239 inline UnexpectedTermError(const QString& expectedToken, const Token& foundToken, int pos):
240 GeneralError(RawErrorTag, QString("unexpected term at pos.%1 (found: %2, expected: %3)").arg(pos).arg(foundToken.toString()).arg(expectedToken), pos)
241 {}
242 inline UnexpectedTermError(const QString& context, const QString& expectedToken, const Token& foundToken, int pos):
243 GeneralError(RawErrorTag, QString("unexpected term at pos.%1 in context %4 (found: %2, expected: %3)").arg(pos).arg(foundToken.toString()).arg(expectedToken).arg(context), pos)
244 {}
245 };
246 /** \brief Exception for wrong number of function arguments */
248 inline WrongNumberOfArgumentError(const QString& func, int numArgs, int minArgs, int maxArgs, int pos):
249 GeneralError(RawErrorTag, QString("wrong number of function arguments for %2() found at pos.%1 (found: %3, expected: %4...%5)").arg(pos).arg(func).arg(numArgs).arg(minArgs).arg(maxArgs), pos)
250 {}
251 };
252 /** \brief Exception when a string cannot be converted properly */
254 inline UnconvertobleError(const QString& str, const QString& target, int pos):
255 GeneralError(RawErrorTag, QString("Could not convert '%2' to %3 at pos.%1").arg(pos).arg(str).arg(target), pos)
256 {}
257 };
258 /**@}*/
259
260
261 /** @name Parser */
262 /**@{*/
263
264 /** \brief parses a number with unit */
266 /** \brief parses a color definition <color> */
268 /** \brief parses a color definition <gradient> */
270
271 /**@}*/
272
273
274
275
276 /** \brief class constructor
277 *
278 * \note This also registers all standatd functions and constants by calling addStandardFunctions() and addStandardVariables()
279 */
280 JKQTPCSSParser(const QString& text);
281
282public:
283
284
285
286
287
288
289 /** \brief parses the given expression*/
290 static NumberWithUnit readNumberWithUnit(const QString& prog);
291 /** \brief parses the given expression, which should represent a color (incl. color-functions like \c rgb() ) */
292 static QColor readColor(const QString& prog);
293 /** \brief parses the given expression, which should represent a QGradient */
294 static QGradient readGradient(const QString& prog);
295
296};
297
298
299#endif // JKQTPCSSTOOLS_H_INCLUDED
A simple parser for certain CSS subsets.
Definition jkqtpcsstools.h:55
bool getCh(QChar &ch)
Definition jkqtpcsstools.h:193
JKQTPExpected< QColor, JKQTPCSSParser::GeneralError > parseColor(bool get)
parses a color definition <color>
QString text
this stream is used to read in the program. An object is created and assigned (and destroyed) by the ...
Definition jkqtpcsstools.h:184
JKQTPExpected< JKQTPCSSParser::NumberWithUnit, JKQTPCSSParser::GeneralError > parseNumberWithUnit(bool get)
parses a number with unit
static QGradient readGradient(const QString &prog)
parses the given expression, which should represent a QGradient
static NumberWithUnit readNumberWithUnit(const QString &prog)
parses the given expression
static QColor readColor(const QString &prog)
parses the given expression, which should represent a color (incl. color-functions like rgb() )
JKQTPExpected< QGradient, JKQTPCSSParser::GeneralError > parseGradient(bool get)
parses a color definition <gradient>
int pos
current reading position in text
Definition jkqtpcsstools.h:186
bool textAtEnd() const
indicates whether pos points to the end of text
Definition jkqtpcsstools.h:189
bool peekCh(QChar &ch)
Definition jkqtpcsstools.h:200
static RawErrorTag_t RawErrorTag
Definition jkqtpcsstools.h:217
Token CurrentToken
the current token while parsing a string
Definition jkqtpcsstools.h:179
Token getToken()
Tokenizer: extract the next token from the input.
JKQTPCSSParser(const QString &text)
class constructor
void putBackCh()
Definition jkqtpcsstools.h:206
Token peekNextToken(int *endPos=nullptr)
Tokenizer: look at the next token from the input, but don't set it as CurrentToken and don't move the...
Definition jkqtpcsstools.h:216
#define JKQTCOMMON_LIB_EXPORT
Definition jkqtcommon_imexport.h:87
T jkqtp_bounded(T min, T v, T max)
limits a value v to the given range min ... max
Definition jkqtpmathtools.h:267
#define JKQTPSTATISTICS_PI
Definition jkqtpmathtools.h:52
Definition jkqtpcsstools.h:218
GeneralError(const QString &context, const QString &err, int p=-1)
Definition jkqtpcsstools.h:221
QString error
Definition jkqtpcsstools.h:222
GeneralError(RawErrorTag_t, const QString &err, int p=-1)
Definition jkqtpcsstools.h:220
GeneralError(const QString &err=QString("unspecified error"), int p=-1)
Definition jkqtpcsstools.h:219
int pos
Definition jkqtpcsstools.h:223
encodes a number with its unit, e.g. 100% or 45deg or ...
Definition jkqtpcsstools.h:59
double number
the number value ittself
Definition jkqtpcsstools.h:64
double normNumber() const
partly evaluates the unit and transforms the number accordingly.
Definition jkqtpcsstools.h:76
QString normUnit() const
returns a normalized version of the unit, i.e. all lower-case, timmed and simplified
Definition jkqtpcsstools.h:106
NumberWithUnit(double val=0.0, const QString &unit_=QString())
Definition jkqtpcsstools.h:60
QString unit
Definition jkqtpcsstools.h:62
double normRGBNumber() const
returns number as a normalized number for RGB, output is in the range 0..1
Definition jkqtpcsstools.h:88
double normHueNumber() const
returns number as a normalized number for Hue, output is in the range 0..1
Definition jkqtpcsstools.h:97
bool operator==(const NumberWithUnit &other) const
Definition jkqtpcsstools.h:108
Definition jkqtpcsstools.h:119
QString getNormString() const
normlizes the StringValue (i.e. lower-case, trimmer, simmplified)
Definition jkqtpcsstools.h:164
QString StringValue
the string value of the current token (when applicable) during the parsing step
Definition jkqtpcsstools.h:140
TokenType type
Definition jkqtpcsstools.h:138
QString toString() const
converts the TOken information to a string
Token(TokenType type)
bool isNormStringAnyOf(const QStringList &names) const
checks whether the current token is of type TokenType::NAME and the String value equals any of the en...
Definition jkqtpcsstools.h:154
Token(double num, const QString &unit_=QString())
bool isNormString(const QString &name) const
checks whether the current token is of type TokenType::NAME and the String value equals name (case-in...
Definition jkqtpcsstools.h:150
Token(const QString &str, TokenType type=NAME)
static QString toString(TokenType type)
TokenType
the possible Token that can be recognized by the tokenizer in JKQTPCSSParser::getToken()
Definition jkqtpcsstools.h:121
@ LBRACKET
left brackets '('
Definition jkqtpcsstools.h:126
@ HEXSTRING
a string in HEX notation (i.e. 0-9a-fA-F)
Definition jkqtpcsstools.h:125
@ SLASH
a slash '/'
Definition jkqtpcsstools.h:129
@ NAME
a name (consisting of characters) of a variable or function
Definition jkqtpcsstools.h:123
@ NUMBER
a number, possibly with a unit(string)
Definition jkqtpcsstools.h:124
@ COMMA
a comma ','
Definition jkqtpcsstools.h:128
@ RBRACKET
right brackets ')'
Definition jkqtpcsstools.h:127
@ END
end token
Definition jkqtpcsstools.h:122
double NumberValue
the string value of the current token (when applicable) during the parsing step
Definition jkqtpcsstools.h:143
bool is(TokenType othertype) const
checks whether the current token is of the given type (does not compare any other field)
Definition jkqtpcsstools.h:146
Exception when a string cannot be converted properly.
Definition jkqtpcsstools.h:253
UnconvertobleError(const QString &str, const QString &target, int pos)
Definition jkqtpcsstools.h:254
Exception for unexpected Term.
Definition jkqtpcsstools.h:238
UnexpectedTermError(const QString &expectedToken, const Token &foundToken, int pos)
Definition jkqtpcsstools.h:239
UnexpectedTermError(const QString &context, const QString &expectedToken, const Token &foundToken, int pos)
Definition jkqtpcsstools.h:242
Exception for unexpected Token.
Definition jkqtpcsstools.h:226
UnexpectedTokenError(Token::TokenType expectedToken, const Token &foundToken, int pos)
Definition jkqtpcsstools.h:230
UnexpectedTokenError(const QString &context, Token::TokenType expectedToken, const Token &foundToken, int pos)
Definition jkqtpcsstools.h:233
UnexpectedTokenError(Token::TokenType expectedToken, Token::TokenType foundToken, int pos)
Definition jkqtpcsstools.h:227
Exception for wrong number of function arguments.
Definition jkqtpcsstools.h:247
WrongNumberOfArgumentError(const QString &func, int numArgs, int minArgs, int maxArgs, int pos)
Definition jkqtpcsstools.h:248
an "expected" datatype, which can either represent a function result of type T or an error of type E
Definition jkqtpexpected.h:61