File indexing completed on 2024-04-14 05:44:24

0001 /*
0002  *  SPDX-FileCopyrightText: 2002-2004 Jesper K. Pedersen <blackie@kde.org>
0003  *
0004  *  SPDX-License-Identifier: LGPL-2.0-only
0005  **/
0006 
0007 #include "emacsregexpconverter.h"
0008 
0009 #include <KLocalizedString>
0010 #include <KMessageBox>
0011 
0012 #include "altnregexp.h"
0013 #include "compoundregexp.h"
0014 #include "concregexp.h"
0015 #include "positionregexp.h"
0016 #include "regexp.h"
0017 #include "repeatregexp.h"
0018 #include "textrangeregexp.h"
0019 #include "textregexp.h"
0020 
0021 bool EmacsRegExpConverter::canParse()
0022 {
0023     return false;
0024 }
0025 
0026 QString EmacsRegExpConverter::toString(AltnRegExp *regexp, bool markSelection)
0027 {
0028     QString res;
0029 
0030     bool first = true;
0031     const RegExpList children = regexp->children();
0032     for (RegExp *child : children) {
0033         if (!first) {
0034             res += QLatin1String("\\|");
0035         }
0036         first = false;
0037         res += toStr(child, markSelection);
0038     }
0039 
0040     return res;
0041 }
0042 
0043 QString EmacsRegExpConverter::toString(ConcRegExp *regexp, bool markSelection)
0044 {
0045     QString res;
0046 
0047     const RegExpList children = regexp->children();
0048     for (RegExp *child : children) {
0049         QString startPar;
0050         QString endPar;
0051         if (child->precedence() < regexp->precedence()) {
0052             startPar = QStringLiteral("\\(");
0053             endPar = QStringLiteral("\\)");
0054         }
0055 
0056         res += startPar + toStr(child, markSelection) + endPar;
0057     }
0058 
0059     return res;
0060 }
0061 
0062 QString EmacsRegExpConverter::toString(LookAheadRegExp * /*regexp*/, bool /*markSelection*/)
0063 {
0064     static bool haveWarned = false;
0065     if (!haveWarned) {
0066         KMessageBox::error(nullptr, i18n("Look ahead regular expressions not supported in Emacs style"));
0067         haveWarned = true;
0068     }
0069 
0070     return QString();
0071 }
0072 
0073 QString EmacsRegExpConverter::toString(TextRangeRegExp *regexp, bool /*markSelection*/)
0074 {
0075     QString txt;
0076 
0077     bool foundCarrot = false;
0078     bool foundDash = false;
0079     bool foundParenthesis = false;
0080 
0081     // print the single characters, but keep "^" as the very
0082     // last element of the characters.
0083     QStringList chars = regexp->chars();
0084     for (int i = 0; i < chars.count(); i++) {
0085         if (chars.at(i).at(0) == QLatin1Char(']')) {
0086             foundParenthesis = true;
0087         } else if (chars.at(i).at(0) == QLatin1Char('-')) {
0088             foundDash = true;
0089         } else if (chars.at(i).at(0) == QLatin1Char('^')) {
0090             foundCarrot = true;
0091         } else {
0092             txt.append(chars.at(i).at(0));
0093         }
0094     }
0095 
0096     // Now insert the ranges.
0097     const auto range = regexp->range();
0098     for (const StringPair &elm : range) {
0099         txt.append(elm.first + QLatin1Char('-') + elm.second);
0100     }
0101 
0102     // Ok, its time to build each part of the regexp, here comes the rule:
0103     // if a ']' is one of the characters, then it must be the first one in the
0104     // list (after then opening '[' and eventually negation '^')
0105     // Next if a '-' is one of the characters, then it must come
0106     // finally if '^' is one of the characters, then it must not be the first
0107     // one!
0108 
0109     QString res = QStringLiteral("[");
0110 
0111     if (regexp->negate()) {
0112         res.append(QLatin1String("^"));
0113     }
0114 
0115     // a ']' must be the first character in teh range.
0116     if (foundParenthesis) {
0117         res.append(QLatin1String("]"));
0118     }
0119 
0120     // a '-' must be the first character ( only coming after a ']')
0121     if (foundDash) {
0122         res.append(QLatin1String("-"));
0123     }
0124 
0125     res += txt;
0126 
0127     // Insert equivalents to \s,\S,\d,\D,\w, and \W
0128     // non-digit, non-space, and non-word is not supported in Emacs style
0129     if (regexp->digit()) {
0130         res += QStringLiteral("0-9");
0131     }
0132 
0133     if (regexp->space()) {
0134         res += QLatin1Char(' ') + QLatin1Char((char)9); // Tab char
0135     }
0136 
0137     if (regexp->wordChar()) {
0138         res += QStringLiteral("a-zA-Z");
0139     }
0140 
0141     if (foundCarrot) {
0142         res.append(QLatin1Char('^'));
0143     }
0144 
0145     res.append(QLatin1Char(']'));
0146 
0147     return res;
0148 }
0149 
0150 QString EmacsRegExpConverter::toString(CompoundRegExp *regexp, bool markSelection)
0151 {
0152     return toStr(regexp->child(), markSelection);
0153 }
0154 
0155 QString EmacsRegExpConverter::toString(DotRegExp * /*regexp*/, bool /*markSelection*/)
0156 {
0157     return QStringLiteral(".");
0158 }
0159 
0160 QString EmacsRegExpConverter::toString(PositionRegExp *regexp, bool /*markSelection*/)
0161 {
0162     static bool haveWarned = false;
0163     switch (regexp->position()) {
0164     case PositionRegExp::BEGLINE:
0165         return QStringLiteral("^");
0166     case PositionRegExp::ENDLINE:
0167         return QStringLiteral("$");
0168     case PositionRegExp::WORDBOUNDARY:
0169     case PositionRegExp::NONWORDBOUNDARY:
0170         if (!haveWarned) {
0171             KMessageBox::error(nullptr, i18n("Word boundary and non word boundary is not supported in Emacs syntax"));
0172             haveWarned = true;
0173             return QString();
0174         }
0175     }
0176     return QString();
0177 }
0178 
0179 QString EmacsRegExpConverter::toString(RepeatRegExp *regexp, bool markSelection)
0180 {
0181     RegExp *child = regexp->child();
0182     QString cText = toStr(child, markSelection);
0183     QString startPar;
0184     QString endPar;
0185 
0186     if (child->precedence() < regexp->precedence()) {
0187         startPar = QStringLiteral("\\(");
0188         endPar = QStringLiteral("\\)");
0189     }
0190 
0191     if (regexp->min() == 0 && regexp->max() == -1) {
0192         return startPar + cText + endPar + QStringLiteral("*");
0193     } else if (regexp->min() == 0 && regexp->max() == 1) {
0194         return startPar + cText + endPar + QStringLiteral("?");
0195     } else if (regexp->min() == 1 && regexp->max() == -1) {
0196         return startPar + cText + endPar + QStringLiteral("+");
0197     } else {
0198         QString res = QString();
0199         for (int i = 0; i < regexp->min(); ++i) {
0200             res += QStringLiteral("\\(") + cText + QStringLiteral("\\)");
0201         }
0202         if (regexp->max() != -1) {
0203             for (int i = regexp->min(); i < regexp->max(); ++i) {
0204                 res += QStringLiteral("\\(") + cText + QStringLiteral("\\)?");
0205             }
0206         } else {
0207             res += QLatin1String("+");
0208         }
0209 
0210         return res;
0211     }
0212 }
0213 
0214 QString EmacsRegExpConverter::toString(TextRegExp *regexp, bool /*markSelection*/)
0215 {
0216     QList<QChar> list;
0217     list << QLatin1Char('$') << QLatin1Char('^') << QLatin1Char('.') << QLatin1Char('*') << QLatin1Char('+') << QLatin1Char('?') << QLatin1Char('[')
0218          << QLatin1Char(']') << QLatin1Char('\\');
0219 
0220     QString res = escape(regexp->text(), list, QLatin1Char('\\'));
0221     return res;
0222 }
0223 
0224 QString EmacsRegExpConverter::name()
0225 {
0226     return QStringLiteral("Emacs");
0227 }
0228 
0229 int EmacsRegExpConverter::features()
0230 {
0231     return WordStart | WordEnd;
0232 }