File indexing completed on 2024-04-14 14:53:13

0001 #!/bin/bash
0002 #
0003 #   Copyright (C) 2006-2015 Jarosław Staniek <staniek@kde.org>
0004 #
0005 #   This program is free software; you can redistribute it and/or
0006 #   modify it under the terms of the GNU General Public
0007 #   License as published by the Free Software Foundation; either
0008 #   version 2 of the License, or (at your option) any later version.
0009 #
0010 #   This program is distributed in the hope that it will be useful,
0011 #   but WITHOUT ANY WARRANTY; without even the implied warranty of
0012 #   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
0013 #    General Public License for more details.
0014 #
0015 #   You should have received a copy of the GNU General Public License
0016 #   along with this program; see the file COPYING.  If not, write to
0017 #   the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
0018 #   Boston, MA 02110-1301, USA.
0019 #
0020 # Generates parser and lexer code using bison and flex
0021 #
0022 
0023 me=generate_parser_code.sh
0024 
0025 BISON_MIN=3.0.4      # keep updated for best results
0026 BISON_MIN_NUM=30004  # keep updated for best results
0027 FLEX_MIN=2.5.37      # keep updated for best results
0028 FLEX_MIN_NUM=20537   # keep updated for best results
0029 
0030 # Check minimum version of bison
0031 bisonv=`bison --version | head -n 1| cut -f4 -d" "`
0032 bisonv1=`echo $bisonv | cut -f1 -d.`
0033 bisonv2=`echo $bisonv | cut -f2 -d.`
0034 bisonv3=`echo $bisonv | cut -f3 -d.`
0035 if [ -z $bisonv3 ] ; then bisonv3=0; fi
0036 bisonvnum=`expr $bisonv1 \* 10000 + $bisonv2 \* 100 + $bisonv3`
0037 if [ $bisonvnum -lt $BISON_MIN_NUM ] ; then
0038     echo "$bisonv is too old bison version, the minimum is $BISON_MIN."
0039     exit 1
0040 fi
0041 
0042 # Check minimum version of flex
0043 flexv=`flex --version | head -n 1| cut -f2 -d" "`
0044 flexv1=`echo $flexv | cut -f1 -d.`
0045 flexv2=`echo $flexv | cut -f2 -d.`
0046 flexv3=`echo $flexv | cut -f3 -d.`
0047 flexvnum=`expr $flexv1 \* 10000 + $flexv2 \* 100 + $flexv3`
0048 if [ $flexvnum -lt $FLEX_MIN_NUM ] ; then
0049     echo "$flexv is too old flex version, the minimum is $FLEX_MIN."
0050     exit 1
0051 fi
0052 
0053 # Generate lexer and parser
0054 builddir=$PWD
0055 srcdir=`dirname $0`
0056 cd $srcdir
0057 
0058 flex -ogenerated/sqlscanner.cpp KDbSqlScanner.l
0059 # Correct a few yy_size_t vs size_t vs int differences that some bisons cause
0060 sed --in-place 's/int yyleng/yy_size_t yyleng/g;s/int yyget_leng/yy_size_t yyget_leng/g;s/yyleng = (int)/yyleng = (size_t)/g;' generated/sqlscanner.cpp
0061 bison -d KDbSqlParser.y -Wall -fall -rall --report-file=$builddir/KDbSqlParser.output
0062 
0063 # postprocess
0064 cat << EOF > generated/sqlparser.h
0065 /****************************************************************************
0066  * Created by $me
0067  * WARNING! All changes made in this file will be lost!
0068  ****************************************************************************/
0069 #ifndef KDBSQLPARSER_H
0070 #define KDBSQLPARSER_H
0071 
0072 #include "KDbDateTime.h"
0073 #include "KDbExpression.h"
0074 #include "KDbField.h"
0075 #include "KDbOrderByColumn.h"
0076 
0077 struct OrderByColumnInternal;
0078 struct SelectOptionsInternal;
0079 
0080 EOF
0081 
0082 # Fine-tune the code: extra functions and remove trailing white space
0083 cat KDbSqlParser.tab.h >> generated/sqlparser.h
0084 echo '#endif' >> generated/sqlparser.h
0085 sed --in-place 's/[[:space:]]\+$//;s/\t/        /g' generated/sqlparser.h
0086 
0087 cat KDbSqlParser.tab.c | sed -e "s/KDbSqlParser\.tab\.c/sqlparser.cpp/g" > generated/sqlparser.cpp
0088 cat << EOF >> generated/sqlparser.cpp
0089 KDB_TESTING_EXPORT const char* g_tokenName(unsigned int offset) {
0090     const int t = YYTRANSLATE(offset);
0091     if (t >= YYTRANSLATE(::SQL_TYPE)) {
0092         return yytname[t];
0093     }
0094     return nullptr;
0095 }
0096 
0097 //static
0098 const int KDbToken::maxCharTokenValue = 253;
0099 
0100 //static
0101 const int KDbToken::maxTokenValue = YYMAXUTOK;
0102 EOF
0103 sed --in-place 's/[[:space:]]\+$//;s/\t/        /g' generated/sqlparser.cpp
0104 
0105 # Extract table of SQL tokens
0106 # unused ./extract_tokens.sh > generated/tokens.cpp
0107 rm -f KDbSqlParser.tab.h KDbSqlParser.tab.c
0108 
0109 # Create KDbToken.h
0110 cat << EOF > generated/KDbToken.h
0111 /****************************************************************************
0112  * Created by $me
0113  * WARNING! All changes made in this file will be lost!
0114  ****************************************************************************/
0115 /* This file is part of the KDE project
0116    Copyright (C) 2015-2018 Jarosław Staniek <staniek@kde.org>
0117 
0118    This library is free software; you can redistribute it and/or
0119    modify it under the terms of the GNU Library General Public
0120    License as published by the Free Software Foundation; either
0121    version 2 of the License, or (at your option) any later version.
0122 
0123    This library is distributed in the hope that it will be useful,
0124    but WITHOUT ANY WARRANTY; without even the implied warranty of
0125    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
0126    Library General Public License for more details.
0127 
0128    You should have received a copy of the GNU Library General Public License
0129    along with this library; see the file COPYING.LIB.  If not, write to
0130    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
0131  * Boston, MA 02110-1301, USA.
0132 */
0133 
0134 #ifndef KDB_TOKEN_H
0135 #define KDB_TOKEN_H
0136 
0137 #include "kdb_export.h"
0138 
0139 #include <QDebug>
0140 
0141 class KDbDriver;
0142 
0143 /*! @brief A type-safe KDbSQL token
0144  It can be used in KDb expressions
0145  @see KDbExpression */
0146 class KDB_EXPORT KDbToken
0147 {
0148 public:
0149     //! @todo add KDbToken(const QByteArray &name)
0150 
0151     //! Creates an invalid token
0152     inline KDbToken() : v(0) {}
0153 
0154     KDbToken(const KDbToken &other) : v(other.v) {}
0155 
0156     //! Creates a single-character token
0157     //! Only characters that belong to the grammar are accepted:
0158 EOF
0159 (echo -n "    //! "; grep "\"'.'\","  generated/sqlparser.cpp \
0160     | sed -e "s/\"\('.'\)\",/\1/g;s/\"[0-9A-Za-z_$]*\",[ ]*//g;" | tr --delete '\n' \
0161     | sed -e "s/ $//g;") >> generated/KDbToken.h
0162 
0163 cat << EOF >> generated/KDbToken.h
0164 
0165     //! Invalid KDbToken is created for character that is not accepted.
0166     KDbToken(char charToken);
0167 
0168     //! @return true if this token is valid
0169     inline bool isValid() const { return v != 0; }
0170 
0171     //! @return name of this token
0172     //! Useful for debugging.
0173     //! For example "NOT_EQUAL" string is returned for the NOT_EQUAL token.
0174     //! A single character is returned for printable single-character tokens.
0175     //! A number is returned for non-printable single-character.
0176     //! "<INVALID_TOKEN>" is returned for an invalid string.
0177     QString name() const;
0178 
0179     //! @return string interpretation of this token (as visibe to the user)
0180     //! For example "<>" is returned for the NOT_EQUAL token.
0181     //! Empty string is returned for an invalid string
0182     //! The result may depend on the optional @a driver parameter.
0183     //! If @a driver is @c nullptr, representation for portable KDbSQL dialect is returned.
0184     QString toString(const KDbDriver *driver = nullptr) const;
0185 
0186     //! Like toString(const KDbDriver *driver)
0187     static QString toString(KDbToken token, const KDbDriver *driver = nullptr);
0188 
0189     //! Maximum character token value (253)
0190     static const int maxCharTokenValue;
0191 
0192     //! Maximum character token value
0193     static const int maxTokenValue;
0194 
0195     //! @return character equivalent of this token
0196     //! Only character-based tokens are supported this way (toInt() <= maxCharTokenValue).
0197     //! For unsupported tokens @c nullptr is returned.
0198     inline char toChar() const { return v <= maxCharTokenValue ? v : 0; }
0199 
0200     //! @return numeric value of this token
0201     inline int value() const { return v; }
0202 
0203     //! @return true if this token is equal to @a other token
0204     inline bool operator==(KDbToken other) const { return v == other.v; }
0205 
0206     //! @return true if this token is not equal to @a other token
0207     inline bool operator!=(KDbToken other) const { return v != other.v; }
0208 
0209     //! @return true if this token is equal to @a other token
0210     inline bool operator==(char charToken) const { return v == charToken; }
0211 
0212     //! @return true if this token is not equal to @a other token
0213     inline bool operator!=(char charToken) const { return v != charToken; }
0214 
0215     static QList<KDbToken> allTokens();
0216 
0217     // -- constants go here --
0218 EOF
0219 
0220 function extractTokens()
0221 {
0222     grep -E  "    [A-Z0-9_]+ = [[:digit:]]+" generated/sqlparser.h \
0223         | sed -e "s/^    //g;s/ = / /g;s/,//g"
0224 }
0225 
0226 extractTokens | while read token value; do
0227     echo "    static const KDbToken $token;"
0228 done >> generated/KDbToken.h
0229 
0230 function customTokens()
0231 {
0232 cat << EOF
0233 BETWEEN_AND 0x1001
0234 NOT_BETWEEN_AND 0x1002
0235 EOF
0236 }
0237 
0238 echo "    //! Custom tokens are not used in parser but used as an extension in expression classes." >> generated/KDbToken.h
0239 
0240 customTokens | while read token value; do
0241     echo "    static const KDbToken $token;"
0242 done >> generated/KDbToken.h
0243 
0244 cat << EOF >> generated/KDbToken.h
0245     // -- end of constants --
0246 
0247     class List;
0248 private:
0249     inline KDbToken(int value) : v(value) {}
0250     int v;
0251 };
0252 
0253 //! Sends information about token @a token to debug output @a dbg.
0254 KDB_EXPORT QDebug operator<<(QDebug dbg, KDbToken token);
0255 
0256 #endif
0257 EOF
0258 
0259 cat << EOF > generated/KDbToken.cpp
0260 /****************************************************************************
0261  * Created by $me
0262  * WARNING! All changes made in this file will be lost!
0263  ****************************************************************************/
0264 /* This file is part of the KDE project
0265    Copyright (C) 2015-2018 Jarosław Staniek <staniek@kde.org>
0266 
0267    This library is free software; you can redistribute it and/or
0268    modify it under the terms of the GNU Library General Public
0269    License as published by the Free Software Foundation; either
0270    version 2 of the License, or (at your option) any later version.
0271 
0272    This library is distributed in the hope that it will be useful,
0273    but WITHOUT ANY WARRANTY; without even the implied warranty of
0274    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
0275    Library General Public License for more details.
0276 
0277    You should have received a copy of the GNU Library General Public License
0278    along with this library; see the file COPYING.LIB.  If not, write to
0279    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
0280  * Boston, MA 02110-1301, USA.
0281 */
0282 
0283 #include "KDbToken.h"
0284 #include "KDbDriver.h"
0285 #include "KDbDriver_p.h"
0286 #include "KDbDriverBehavior.h"
0287 #include "sqlparser.h"
0288 #include "parser/KDbParser_p.h"
0289 
0290 #include <QGlobalStatic>
0291 
0292 KDbToken::KDbToken(char charToken)
0293     : v(g_tokenName(charToken) == nullptr ? 0 : charToken)
0294 {
0295 }
0296 
0297 QString KDbToken::name() const
0298 {
0299     if (!isValid()) {
0300         return QLatin1String("<INVALID_TOKEN>");
0301     }
0302     if (v > maxCharTokenValue) {
0303         return QLatin1String(g_tokenName(v));
0304     }
0305     if (isprint(v)) {
0306         return QString(QLatin1Char(char(v)));
0307     }
0308     else {
0309         return QLatin1String(QByteArray::number(v));
0310     }
0311 }
0312 
0313 QString KDbToken::toString(const KDbDriver *driver) const
0314 {
0315     if (toChar() > 0) {
0316         return name();
0317     }
0318     // other arithmetic operations: << >>
0319     // NOTE: only include cases that have toString() != name() or are dependent on driver
0320     switch (v) {
0321     case ::BITWISE_SHIFT_RIGHT: return QLatin1String(">>");
0322     case ::BITWISE_SHIFT_LEFT: return QLatin1String("<<");
0323         // other relational operations: <= >= <> (or !=) LIKE IN
0324     case ::NOT_EQUAL: return QLatin1String("<>");
0325     case ::NOT_EQUAL2: return QLatin1String("!=");
0326     case ::LESS_OR_EQUAL: return QLatin1String("<=");
0327     case ::GREATER_OR_EQUAL: return QLatin1String(">=");
0328     case ::LIKE: return driver ? KDbDriverPrivate::behavior(driver)->LIKE_OPERATOR : QLatin1String("LIKE");
0329     case ::NOT_LIKE:
0330         return driver
0331             ? (QString::fromLatin1("NOT ") + KDbDriverPrivate::behavior(driver)->LIKE_OPERATOR)
0332             : QString::fromLatin1("NOT LIKE");
0333     case ::SQL_IN: return QLatin1String("IN");
0334         // other logical operations: OR (or ||) AND (or &&) XOR
0335     case ::SIMILAR_TO: return QLatin1String("SIMILAR TO");
0336     case ::NOT_SIMILAR_TO: return QLatin1String("NOT SIMILAR TO");
0337         // other string operations: || (as CONCATENATION)
0338     case ::CONCATENATION: return QLatin1String("||");
0339         // SpecialBinary "pseudo operators":
0340         /* not handled here */
0341     default:;
0342     }
0343     const QString s = name();
0344     if (!s.isEmpty()) {
0345         return s;
0346     }
0347     return QString::fromLatin1("<INVALID_TOKEN#%1> ").arg(v);
0348 }
0349 
0350 //static
0351 QString KDbToken::toString(KDbToken token, const KDbDriver *driver)
0352 {
0353     return token.toString(driver);
0354 }
0355 
0356 KDB_EXPORT QDebug operator<<(QDebug dbg, KDbToken token)
0357 {
0358     QDebugStateSaver saver(dbg);
0359     dbg.nospace().noquote() << token.name();
0360     return dbg.maybeSpace();
0361 }
0362 
0363 //! @internal
0364 class KDbToken::List
0365 {
0366 public:
0367     List()
0368     {
0369         for (int i = 0; i < KDbToken::maxTokenValue; ++i) {
0370             if (g_tokenName(i)) {
0371                 data.append(KDbToken(i));
0372             }
0373         }
0374     }
0375     QList<KDbToken> data;
0376 };
0377 
0378 Q_GLOBAL_STATIC(KDbToken::List, g_allTokens)
0379 
0380 //static
0381 QList<KDbToken> KDbToken::allTokens()
0382 {
0383     return g_allTokens->data;
0384 }
0385 
0386 EOF
0387 
0388 extractTokens | while read token value; do
0389     echo "const KDbToken KDbToken::$token(::$token);"
0390 done >> generated/KDbToken.cpp
0391 
0392 customTokens | while read token value; do
0393     echo "const KDbToken KDbToken::$token($value);"
0394 done >> generated/KDbToken.cpp