File indexing completed on 2024-12-01 03:32:22
0001 /* 0002 SPDX-FileCopyrightText: 2003-2009 Cies Breijs <cies AT kde DOT nl> 0003 0004 SPDX-License-Identifier: GPL-2.0-or-later 0005 */ 0006 0007 #include "tokenizer.h" 0008 0009 #include <QDebug> 0010 0011 void Tokenizer::initialize(const QString& inString) 0012 { 0013 translator = Translator::instance(); 0014 inputString = inString + QLatin1Char('\n'); // the certainty of a hard break at the end makes parsing much easier 0015 at = 0; 0016 row = 1; 0017 col = 1; 0018 prevCol = 1; 0019 atEnd = false; 0020 } 0021 0022 0023 Token* Tokenizer::getToken() 0024 { 0025 int startRow = row; 0026 int startCol = col; 0027 0028 QChar c = getChar(); // get and store the next character from the string 0029 0030 // catch the end of the input string 0031 if (atEnd) 0032 return new Token(Token::EndOfInput, QStringLiteral("END"), row, col, row, col); 0033 0034 int cType = translator->look2type(c); // since we need to know it often we store it 0035 0036 // catch spaces 0037 if (isSpace(c)) { 0038 QString look; 0039 do { 0040 look += (isTab(c) ? QStringLiteral(" ") : QStringLiteral(" ")); 0041 c = getChar(); 0042 } while (isSpace(c) && !atEnd); 0043 ungetChar(); 0044 return new Token(Token::WhiteSpace, look, startRow, startCol, row, col); 0045 } 0046 0047 // catch EndOfLine's 0048 if (isBreak(c)) { 0049 return new Token(Token::EndOfLine, QStringLiteral("\\n"), startRow, startCol, startRow+1, 1); 0050 } 0051 0052 // catch comments 0053 if (cType == Token::Comment) { 0054 QString look; 0055 do { 0056 look += c; 0057 c = getChar(); 0058 } while (!isBreak(c) && !atEnd); 0059 ungetChar(); 0060 return new Token(Token::Comment, look, startRow, startCol, row, col); 0061 } 0062 0063 // catch strings 0064 if (cType == Token::StringDelimiter) { 0065 QString look = QString(c); 0066 do { 0067 c = getChar(); 0068 look += c; 0069 } while (!(translator->look2type(c) == Token::StringDelimiter && look.right(2) != QLatin1String("\\\"")) && 0070 !isBreak(c) && !atEnd); 0071 return new Token(Token::String, look, startRow, startCol, row, col); 0072 } 0073 0074 // catch variables 0075 if (cType == Token::VariablePrefix) { 0076 QString look; 0077 do { 0078 look += c; 0079 c = getChar(); 0080 } while (isWordChar(c) || c.category() == QChar::Number_DecimalDigit || c == QLatin1Char('_')); 0081 ungetChar(); 0082 return new Token(Token::Variable, look, startRow, startCol, row, col); 0083 } 0084 0085 // catch words (known commands or function calls) 0086 if (isWordChar(c)) { // first char has to be a letter 0087 QString look; 0088 do { 0089 look += c; 0090 c = getChar(); 0091 } while (isWordChar(c) || c.isDigit() || c == QLatin1Char('_')); // next chars 0092 ungetChar(); 0093 int type = translator->look2type(look); 0094 if (type == Token::Unknown) 0095 type = Token::FunctionCall; 0096 return new Token(type, look, startRow, startCol, row, col); 0097 } 0098 0099 // catch numbers 0100 if (c.isDigit() || cType == Token::DecimalSeparator) { 0101 bool hasDot = false; 0102 0103 int localType = cType; 0104 QString look; 0105 do { 0106 if (localType == Token::DecimalSeparator) hasDot = true; 0107 look += c; 0108 c = getChar(); 0109 localType = translator->look2type(c); 0110 } while (c.isDigit() || (localType == Token::DecimalSeparator && !hasDot)); 0111 ungetChar(); 0112 0113 // if all we got is a dot then this is not a number, so return an Error token here 0114 if (translator->look2type(look) == Token::DecimalSeparator) 0115 return new Token(Token::Error, look, startRow, startCol, row, col); 0116 0117 return new Token(Token::Number, look, startRow, startCol, row, col); 0118 } 0119 0120 // catch previously uncaught 'double character tokens' (tokens that ar not in letters, like: == != >= <=) 0121 { 0122 QString look = QString(c).append(getChar()); 0123 int type = translator->look2type(look); 0124 if (type != Token::Unknown) 0125 return new Token(type, look, startRow, startCol, row, col); 0126 ungetChar(); 0127 } 0128 0129 // catch known tokens of a single character (as last...) 0130 if (cType != Token::Unknown) 0131 return new Token(cType, static_cast<QString>(c), startRow, startCol, row, col); 0132 0133 // this does not neglect calls to functions with a name of length one (checked it) 0134 return new Token(Token::Error, static_cast<QString>(c), startRow, startCol, row, col); 0135 } 0136 0137 0138 QChar Tokenizer::getChar() 0139 { 0140 if (at >= inputString.size()) { 0141 atEnd = true; 0142 // //qDebug() << "Tokenizer::getChar() returns: a ZERO CHAR " << " @ " << at - 1; 0143 return QChar(); 0144 } 0145 QChar c(inputString.at(at)); 0146 at++; 0147 if (isBreak(c)) { 0148 row++; 0149 prevCol = col; 0150 col = 1; 0151 } else { 0152 col++; 0153 } 0154 // //qDebug() << "Tokenizer::getChar() returns: " << c << " (" << c.category() << ") " << " @ " << at - 1; 0155 return c; 0156 } 0157 0158 0159 void Tokenizer::ungetChar() 0160 { 0161 if (at <= 0) return; // do nothing when trying to go before the first character 0162 0163 at--; 0164 if (atEnd) atEnd = false; 0165 0166 QChar c(inputString.at(at)); 0167 if (isBreak(c)) { 0168 row--; 0169 col = prevCol; 0170 } else { 0171 col--; 0172 } 0173 } 0174 0175 0176 bool Tokenizer::isWordChar(const QChar& c) 0177 { 0178 // this method exists because some languages have non-letter category characters 0179 // mixed with their letter character to make words (like hindi) 0180 // NOTE: this has to be extended then languages give problems, 0181 // just add a category in the following test 0182 return (c.isLetter() || c.isMark()); 0183 } 0184 0185 bool Tokenizer::isBreak(const QChar& c) 0186 { 0187 return (c == QLatin1Char('\x0a') || c == QLatin1Char('\n')); 0188 // c.category() == QChar::Other_Control // one of these also contains the tab (\t) 0189 // c.category() == QChar::Separator_Line 0190 // c.category() == QChar::Separator_Paragraph 0191 } 0192 0193 0194 bool Tokenizer::isSpace(const QChar& c) 0195 { 0196 return (c.category() == QChar::Separator_Space || c == QLatin1Char(' ') || isTab(c)); 0197 } 0198 0199 bool Tokenizer::isTab(const QChar& c) 0200 { 0201 return (c == QLatin1Char('\x09') || c == QLatin1Char('\t')); 0202 }