RedPanda-CPP/RedPandaIDE/parser/cpptokenizer.cpp

676 lines
16 KiB
C++
Raw Normal View History

2021-08-13 22:53:26 +08:00
#include "cpptokenizer.h"
2021-08-14 22:52:37 +08:00
#include <QFile>
#include <QTextStream>
CppTokenizer::CppTokenizer()
2021-08-13 22:53:26 +08:00
{
}
2021-08-14 22:52:37 +08:00
void CppTokenizer::reset()
{
mTokenList.clear();
mBuffer.clear();
mBufferStr.clear();
}
void CppTokenizer::tokenize(const QStringList &buffer)
2021-08-14 18:55:42 +08:00
{
reset();
2021-08-14 22:52:37 +08:00
2021-08-14 18:55:42 +08:00
mBuffer = buffer;
if (mBuffer.isEmpty())
return;
mBufferStr = mBuffer[0];
for (int i=1;i<mBuffer.size();i++) {
mBufferStr+='\n';
mBufferStr+=mBuffer[i];
}
mStart = mBufferStr.data();
mCurrent = mStart;
mLineCount = mStart;
QString s = "";
bool bSkipBlocks = false;
mCurrentLine = 1;
while (true) {
mLastToken = s;
s = getNextToken(true, true, bSkipBlocks);
simplify(s);
if (s.isEmpty())
break;
else
addToken(s,mCurrentLine);
}
}
2021-08-14 22:52:37 +08:00
void CppTokenizer::dumpTokens(const QString &fileName)
{
QFile file(fileName);
if (file.open(QIODevice::WriteOnly | QIODevice::Truncate)) {
QTextStream stream(&file);
for (PToken token:mTokenList) {
stream<<QString("%1,%2").arg(token->line).arg(token->text)<<Qt::endl;
}
}
}
2021-08-15 16:49:37 +08:00
const CppTokenizer::TokenList &CppTokenizer::tokens()
{
return mTokenList;
}
CppTokenizer::PToken CppTokenizer::operator[](int i)
{
return mTokenList[i];
}
int CppTokenizer::tokenCount()
{
return mTokenList.count();
}
2021-08-14 22:52:37 +08:00
void CppTokenizer::addToken(const QString &sText, int iLine)
2021-08-13 22:53:26 +08:00
{
PToken token = std::make_shared<Token>();
token->text = sText;
token->line = iLine;
mTokenList.append(token);
}
2021-08-14 22:52:37 +08:00
void CppTokenizer::countLines()
2021-08-13 22:53:26 +08:00
{
2021-08-16 23:17:48 +08:00
while ((*mLineCount != 0) && (mLineCount < mCurrent)) {
2021-08-13 22:53:26 +08:00
if (*mLineCount == '\n')
mCurrentLine ++;
mLineCount++;
}
}
2021-08-14 22:52:37 +08:00
QString CppTokenizer::getArguments()
2021-08-13 22:53:26 +08:00
{
QChar* offset = mCurrent;
skipPair('(', ')');
QString result(offset,mCurrent-offset);
simplifyArgs(result);
if ((*mCurrent == '.') || ((*mCurrent == '-') && (*(mCurrent + 1) == '>'))) {
// skip '.' and '->'
2021-08-16 23:17:48 +08:00
while ( !( *mCurrent == 0
2021-08-13 22:53:26 +08:00
|| *mCurrent == '('
|| *mCurrent == ';'
|| *mCurrent == '{'
|| *mCurrent == '}'
|| *mCurrent == ')'
|| isLineChar(*mCurrent)
|| isSpaceChar(*mCurrent)) )
mCurrent++;
}
skipToNextToken();
return result;
}
2021-08-14 22:52:37 +08:00
QString CppTokenizer::getForInit()
2021-08-13 22:53:26 +08:00
{
QChar* startOffset = mCurrent;
// Step into the init statement
mCurrent++;
// Process until ; or end of file
while (true) {
QString s = getNextToken(true, true, false);
simplify(s);
if (!s.isEmpty())
addToken(s,mCurrentLine);
if ( (s == "") || (s == ";") || (s==":"))
break;
// : is used in for-each loop
}
// Skip to end of for loop
mCurrent = startOffset;
skipPair('(', ')');
return "";
}
2021-08-14 22:52:37 +08:00
QString CppTokenizer::getNextToken(bool bSkipParenthesis, bool bSkipArray, bool bSkipBlock)
2021-08-14 12:33:02 +08:00
{
QString result;
bool done = false;
while (true) {
skipToNextToken();
2021-08-16 23:17:48 +08:00
if (*mCurrent == 0)
2021-08-14 12:33:02 +08:00
break;
if (isPreprocessor()) {
countLines();
result = getPreprocessor(); // don't count preprocessor lines
if (result.startsWith("#include")) { // if we find
int delimPos = result.lastIndexOf(':');
if (delimPos >= 0) {
bool ok;
mCurrentLine = result.mid(delimPos+1).toInt(&ok)-1; // fCurrLine is 0 based
}
}
done = (result != "");
} else if (isForInit()) {
countLines();
result = getForInit();
done = (result != "");
} else if (isArguments()) {
countLines();
result = getArguments();
done = (result != "");
} else if (isWord()) {
countLines();
result = getWord(false, bSkipArray, bSkipBlock);
done = (result != "");
} else if (isNumber()) {
countLines();
result = getNumber();
done = (result != "");
} else {
switch((*mCurrent).unicode()) {
2021-08-16 23:17:48 +08:00
case 0:
2021-08-14 12:33:02 +08:00
done = true;
break;
case '/':
advance();
break;
case '{':
case '}':
case ';':
case ',':
case ':': //just return the brace or the ';'
countLines();
result = *mCurrent;
advance();
done = true;
break;
case '>': // keep stream operators
if (*(mCurrent + 1) == '>') {
countLines();
result = ">>";
advance();
done = true;
} else
advance();
break;
case '<':
if (*(mCurrent + 1) == '<') {
countLines();
result = "<<";
advance();
done = true;
} else
advance();
break;
default:
advance();
}
}
if (done)
break;
}
return result;
}
2021-08-14 22:52:37 +08:00
QString CppTokenizer::getNumber()
2021-08-13 22:53:26 +08:00
{
QChar* offset = mCurrent;
if (isDigitChar(*mCurrent)) {
while (isDigitChar(*mCurrent) || isHexChar(*mCurrent)) {
advance();
}
}
QString result;
2021-08-14 22:52:37 +08:00
if (offset != mCurrent) {
2021-08-13 22:53:26 +08:00
result = QString(offset,mCurrent-offset);
if (*mCurrent=='.') // keep '.' for decimal
result += *mCurrent;
}
return result;
}
2021-08-14 22:52:37 +08:00
QString CppTokenizer::getPreprocessor()
2021-08-14 12:33:02 +08:00
{
QChar *offset = mCurrent;
skipToEOL();
return QString(offset, mCurrent-offset);
}
2021-08-14 22:52:37 +08:00
QString CppTokenizer::getWord(bool bSkipParenthesis, bool bSkipArray, bool bSkipBlock)
2021-08-14 12:33:02 +08:00
{
bool bFoundTemplate = false;
// bIsSmartPointer:=False;
// Skip spaces
skipToNextToken();
// Get next word...
QChar* offset = mCurrent;
// Copy the word ahead of us
while (isLetterChar(*mCurrent) || isDigitChar(*mCurrent))
mCurrent++;
QString currentWord;
if (offset != mCurrent) {
currentWord = QString(offset,mCurrent-offset);
}
// Append the operator characters and argument list to the operator word
if ((currentWord == "operator") ||
(currentWord == "operator*") ||
(currentWord == "operator&")) {
// Spaces between 'operator' and the operator itself are allowed
while (isSpaceChar(*mCurrent))
mCurrent++;
// Find end of operator
while (isOperatorChar(*mCurrent))
mCurrent++;
currentWord = QString(offset,mCurrent-offset);
} else if (currentWord == "template") {
bFoundTemplate = true;
}
QString result;
// We found a word...
if (!currentWord.isEmpty()) {
// Skip whitespace
skipToNextToken();
// Skip template contents, but keep template variable types
if (*mCurrent == '<') {
offset = mCurrent; //we don't skip
skipTemplateArgs();
if (!bFoundTemplate) {
result += QString(offset, mCurrent-offset);
skipToNextToken();
}
} else if (bSkipArray && (*mCurrent == '[')) {
// Append array stuff
while(true) {
skipPair('[', ']');
result += QString(offset,mCurrent-offset);
simplifyArgs(result);
skipToNextToken();
if (*mCurrent!='[') //maybe multi-dimension array
break;
}
} else if (bSkipBlock && (*mCurrent == '{')) {
skipPair('{', '}');
skipToNextToken();
}
// Keep parent/child operators
if (*mCurrent == '.') {
result+=*mCurrent;
mCurrent++;
} else if ((*mCurrent == '-') && (*(mCurrent + 1) == '>')) {
result+=QString(mCurrent,2);
mCurrent+=2;
} else if ((*mCurrent == ':') && (*(mCurrent + 1) == ':')) {
result+=QString(mCurrent,2);
mCurrent+=2;
// Append next token to this one
QString s = getWord(bSkipParenthesis, bSkipArray, bSkipBlock);
result += s;
}
}
return result;
}
2021-08-14 22:52:37 +08:00
bool CppTokenizer::isArguments()
2021-08-14 12:33:02 +08:00
{
return *mCurrent == '(';
}
2021-08-14 22:52:37 +08:00
bool CppTokenizer::isForInit()
2021-08-14 12:33:02 +08:00
{
return (*mCurrent == '(') && (mLastToken == "for");
}
2021-08-14 22:52:37 +08:00
bool CppTokenizer::isNumber()
2021-08-14 12:33:02 +08:00
{
return isDigitChar(*mCurrent);
}
2021-08-14 22:52:37 +08:00
bool CppTokenizer::isPreprocessor()
2021-08-14 12:33:02 +08:00
{
return *mCurrent=='#';
}
2021-08-14 22:52:37 +08:00
bool CppTokenizer::isWord()
2021-08-14 12:33:02 +08:00
{
bool result = isLetterChar(*mCurrent);
if (result && (*(mCurrent+1) == '"'))
result = false;
return result;
}
2021-08-14 22:52:37 +08:00
void CppTokenizer::simplify(QString &output)
2021-08-14 12:33:02 +08:00
{
//remove \n \r;
QString temp;
for (QChar ch:output) {
if (!isLineChar(ch))
temp+=ch;
}
output = temp.trimmed();
}
2021-08-14 22:52:37 +08:00
void CppTokenizer::simplifyArgs(QString &output)
2021-08-14 12:33:02 +08:00
{
QString temp;
QString lastSpace = "";
bool parentheseStart = true;
for (QChar ch:output.trimmed()) {
if (isSpaceChar(ch)) {
if (!parentheseStart)
lastSpace+=ch;
} else if (ch==','){
temp+=ch;
lastSpace = "";
parentheseStart = false;
} else if (ch=='(') {
temp+=ch;
lastSpace = "";
parentheseStart=true;
} else if (ch==')') {
temp+=ch;
lastSpace = "";
parentheseStart = false;
} else {
parentheseStart=false;
if (!lastSpace.isEmpty()) {
temp+=" ";
}
lastSpace = "";
temp+=ch;
}
}
output = temp;
}
2021-08-14 22:52:37 +08:00
void CppTokenizer::skipAssignment()
2021-08-14 12:33:02 +08:00
{
while (true) {
switch ((*mCurrent).unicode()) {
case '(': skipPair('(', ')');
break;
case '"': skipDoubleQuotes();
break;
case '\'': skipSingleQuote();
break;
case '{': skipPair('{', '}'); // support struct initializers
break;
case '/':
mCurrent++;
break;
default:
if ((*mCurrent == 'R') && (*(mCurrent+1) == '"'))
skipRawString();
else
mCurrent++;
}
if (*mCurrent == ','
|| *mCurrent ==';'
|| *mCurrent ==')'
|| *mCurrent =='}'
2021-08-16 23:17:48 +08:00
|| *mCurrent ==0)
2021-08-14 12:33:02 +08:00
break;
}
}
2021-08-14 22:52:37 +08:00
void CppTokenizer::skipDoubleQuotes()
2021-08-14 12:33:02 +08:00
{
mCurrent++;
2021-08-16 23:17:48 +08:00
while (!(*mCurrent=='"' || *mCurrent == 0)) {
2021-08-14 12:33:02 +08:00
if (*mCurrent == '\\')
mCurrent+=2; // skip escaped char
else
mCurrent++;
}
2021-08-16 23:17:48 +08:00
if (*mCurrent!=0) {
2021-08-14 12:33:02 +08:00
mCurrent++;
}
}
2021-08-14 22:52:37 +08:00
void CppTokenizer::skipPair(const QChar &cStart, const QChar cEnd, const QSet<QChar>& failChars)
2021-08-14 12:33:02 +08:00
{
mCurrent++;
2021-08-16 23:17:48 +08:00
while (*mCurrent != 0) {
2021-08-14 12:59:54 +08:00
if ((*mCurrent == '(') && !failChars.contains('(')) {
skipPair('(', ')', failChars);
} else if ((*mCurrent == '[') && !failChars.contains('[')) {
skipPair('[', ']', failChars);
} else if ((*mCurrent == '{') && !failChars.contains('{')) {
skipPair('{', '}', failChars);
} else if (*mCurrent == cStart) {
skipPair(cStart, cEnd, failChars);
} else if (*mCurrent == cEnd) {
mCurrent++; // skip over end
break;
} else if ((*mCurrent == 'R') && (*(mCurrent+1) == '"')) {
if (cStart != '\'' && cStart!='\"')
skipRawString(); // don't do it inside AnsiString!
else
mCurrent++;
} else if (*mCurrent == '"') {
if (cStart != '\'' && cStart!='\"')
skipDoubleQuotes(); // don't do it inside AnsiString!
else
mCurrent++;
} else if (*mCurrent == '\'') {
if (cStart != '\'' && cStart!='\"')
skipSingleQuote(); // don't do it inside AnsiString!
else
mCurrent++;
} else if (failChars.contains(*mCurrent)) {
break;
} else {
mCurrent++;
}
}
2021-08-14 12:33:02 +08:00
}
2021-08-14 22:52:37 +08:00
void CppTokenizer::skipRawString()
2021-08-14 18:55:42 +08:00
{
mCurrent++; //skip R
bool noEscape = false;
while(true) {
mCurrent++;
switch(mCurrent->unicode()) {
case '(':
noEscape = true;
break;
case ')':
noEscape = false;
break;
}
2021-08-16 23:17:48 +08:00
if (*mCurrent == 0)
2021-08-14 18:55:42 +08:00
break;
if ((*mCurrent == '"') && !noEscape)
break;
}
2021-08-16 23:17:48 +08:00
if (*mCurrent!=0)
2021-08-14 18:55:42 +08:00
mCurrent++;
}
2021-08-14 22:52:37 +08:00
void CppTokenizer::skipSingleQuote()
2021-08-14 18:55:42 +08:00
{
mCurrent++;
2021-08-16 23:17:48 +08:00
while (!(*mCurrent=='\'' || *mCurrent == 0)) {
2021-08-14 18:55:42 +08:00
if (*mCurrent == '\\')
mCurrent+=2; // skip escaped char
else
mCurrent++;
}
2021-08-16 23:17:48 +08:00
if (*mCurrent!=0) {
2021-08-14 18:55:42 +08:00
mCurrent++;
}
}
2021-08-14 22:52:37 +08:00
void CppTokenizer::skipSplitLine()
2021-08-14 18:55:42 +08:00
{
mCurrent++; // skip '\'
while ( isLineChar(*mCurrent)) // skip newline
mCurrent++;
}
2021-08-14 22:52:37 +08:00
void CppTokenizer::skipTemplateArgs()
2021-08-14 18:55:42 +08:00
{
if (*mCurrent != '<')
return;
QChar* start = mCurrent;
QSet<QChar> failSet;
failSet.insert('{');
failSet.insert('}');
failSet.insert(';');
skipPair('<', '>', failSet);
// if we failed, return to where we came from
if (start!=mCurrent && *(mCurrent - 1) != '>')
mCurrent = start;
}
2021-08-14 22:52:37 +08:00
void CppTokenizer::skipToEOL()
2021-08-14 18:55:42 +08:00
{
while (true) {
2021-08-16 23:17:48 +08:00
while (!isLineChar(*mCurrent) && (*mCurrent!=0)) {
2021-08-14 18:55:42 +08:00
mCurrent++;
}
2021-08-16 23:17:48 +08:00
if (*mCurrent==0)
2021-08-14 18:55:42 +08:00
return;
bool splitLine = (*(mCurrent - 1) == '\\');
while (isLineChar(*mCurrent))
mCurrent++;
2021-08-16 23:17:48 +08:00
if (!splitLine || *mCurrent==0)
2021-08-14 18:55:42 +08:00
break;
}
}
2021-08-14 22:52:37 +08:00
void CppTokenizer::skipToNextToken()
2021-08-14 18:55:42 +08:00
{
while (isSpaceChar(*mCurrent) || isLineChar(*mCurrent))
advance();
}
2021-08-14 22:52:37 +08:00
void CppTokenizer::advance()
2021-08-13 22:53:26 +08:00
{
switch(mCurrent->unicode()) {
case '\"': skipDoubleQuotes();
break;
case '\'': skipSingleQuote();
break;
case '/':
if (*(mCurrent + 1) == '=')
skipAssignment();
else
mCurrent++;
break;
case '=':
skipAssignment();
break;
case '&':
case '*':
case '!':
case '|':
case '+':
case '-':
case '~':
if (*(mCurrent + 1) == '=')
skipAssignment();
else
mCurrent++;
break;
case '\\':
if (isLineChar(*(mCurrent + 1)))
skipSplitLine();
else
mCurrent++;
break;
default:
if ((*mCurrent == 'R') && (*(mCurrent+1) == '"'))
skipRawString();
else
mCurrent++;
}
}
2021-08-14 12:33:02 +08:00
2021-08-14 22:52:37 +08:00
bool CppTokenizer::isLetterChar(const QChar &ch)
2021-08-14 12:33:02 +08:00
{
return (ch>= 'A' && ch<='Z')
|| (ch>='a' && ch<='z')
|| ch == '_'
|| ch == '*'
|| ch == '&'
|| ch == '~';
}
2021-08-14 22:52:37 +08:00
bool CppTokenizer::isHexChar(const QChar &ch)
2021-08-14 12:33:02 +08:00
{
return (ch >= 'A' && ch<='F')
|| (ch>='a' && ch<='f')
|| ch == 'x'
|| ch == 'L';
}
2021-08-14 22:52:37 +08:00
bool CppTokenizer::isDigitChar(const QChar &ch)
2021-08-14 12:33:02 +08:00
{
return (ch>='0' && ch<='9');
}
2021-08-14 22:52:37 +08:00
bool CppTokenizer::isSpaceChar(const QChar &ch)
2021-08-14 12:33:02 +08:00
{
return (ch == ' ' || ch == '\t');
}
2021-08-14 22:52:37 +08:00
bool CppTokenizer::isLineChar(const QChar &ch)
2021-08-14 12:33:02 +08:00
{
return (ch=='\n' || ch=='\r');
}
2021-08-14 22:52:37 +08:00
bool CppTokenizer::isBlankChar(const QChar &ch)
2021-08-14 12:33:02 +08:00
{
return (ch<=32);
}
2021-08-14 22:52:37 +08:00
bool CppTokenizer::isOperatorChar(const QChar &ch)
2021-08-14 12:33:02 +08:00
{
switch (ch.unicode()) {
case '+':
case '-':
case '/':
case '*':
case '[':
case ']':
case '=':
case '%':
case '!':
case '&':
case '|':
case '>':
case '<':
case '^':
return true;
default:
return false;
}
}
2021-08-14 22:52:37 +08:00
bool CppTokenizer::currentWordEquals(QChar *wordStart, QChar *wordEnd, const QString& text)
2021-08-14 12:33:02 +08:00
{
QString currentWord(wordStart, wordEnd-wordStart);
return currentWord == text;
}