/**************************************************************************** ** ** Copyright (C) 2016 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of Qt for Python. ** ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3 as published by the Free Software ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include "typeparser.h" #include #include #include #include class Scanner { public: enum Token { StarToken, AmpersandToken, LessThanToken, ColonToken, CommaToken, OpenParenToken, CloseParenToken, SquareBegin, SquareEnd, GreaterThanToken, ConstToken, VolatileToken, Identifier, NoToken, InvalidToken }; Scanner(const QString &s) : m_pos(0), m_length(s.length()), m_tokenStart(-1), m_chars(s.constData()) { } Token nextToken(QString *errorMessage = Q_NULLPTR); QString identifier() const; QString msgParseError(const QString &why) const; private: int m_pos; int m_length; int m_tokenStart; const QChar *m_chars; }; QString Scanner::identifier() const { return QString(m_chars + m_tokenStart, m_pos - m_tokenStart); } Scanner::Token Scanner::nextToken(QString *errorMessage) { Token tok = NoToken; // remove whitespace while (m_pos < m_length && m_chars[m_pos] == u' ') ++m_pos; m_tokenStart = m_pos; while (m_pos < m_length) { const QChar &c = m_chars[m_pos]; if (tok == NoToken) { switch (c.toLatin1()) { case '*': tok = StarToken; break; case '&': tok = AmpersandToken; break; case '<': tok = LessThanToken; break; case '>': tok = GreaterThanToken; break; case ',': tok = CommaToken; break; case '(': tok = OpenParenToken; break; case ')': tok = CloseParenToken; break; case '[': tok = SquareBegin; break; case ']' : tok = SquareEnd; break; case ':': tok = ColonToken; Q_ASSERT(m_pos + 1 < m_length); ++m_pos; break; default: if (c.isLetterOrNumber() || c == u'_') { tok = Identifier; } else { QString message; QTextStream (&message) << ": Unrecognized character in lexer at " << m_pos << " : '" << c << '\''; message = msgParseError(message); if (errorMessage) *errorMessage = message; else qWarning().noquote().nospace() << message; return InvalidToken; } break; } } if (tok <= GreaterThanToken) { ++m_pos; break; } if (tok == Identifier) { if (c.isLetterOrNumber() || c == u'_') ++m_pos; else break; } } if (tok == Identifier) { switch (m_pos - m_tokenStart) { case 5: if (m_chars[m_tokenStart] == u'c' && m_chars[m_tokenStart + 1] == u'o' && m_chars[m_tokenStart + 2] == u'n' && m_chars[m_tokenStart + 3] == u's' && m_chars[m_tokenStart + 4] == u't') { tok = ConstToken; } break; case 8: if (m_chars[m_tokenStart] == u'v' && m_chars[m_tokenStart + 1] == u'o' && m_chars[m_tokenStart + 2] == u'l' && m_chars[m_tokenStart + 3] == u'a' && m_chars[m_tokenStart + 4] == u't' && m_chars[m_tokenStart + 5] == u'i' && m_chars[m_tokenStart + 6] == u'l' && m_chars[m_tokenStart + 7] == u'e') { tok = VolatileToken; } break; } } return tok; } QString Scanner::msgParseError(const QString &why) const { return QStringLiteral("TypeParser: Unable to parse \"") + QString(m_chars, m_length) + QStringLiteral("\": ") + why; } TypeInfo TypeParser::parse(const QString &str, QString *errorMessage) { Scanner scanner(str); QStack stack; stack.push(TypeInfo()); bool colon_prefix = false; bool in_array = false; QString array; bool seenStar = false; Scanner::Token tok = scanner.nextToken(errorMessage); while (tok != Scanner::NoToken) { if (tok == Scanner::InvalidToken) return TypeInfo(); // switch (tok) { // case Scanner::StarToken: printf(" - *\n"); break; // case Scanner::AmpersandToken: printf(" - &\n"); break; // case Scanner::LessThanToken: printf(" - <\n"); break; // case Scanner::GreaterThanToken: printf(" - >\n"); break; // case Scanner::ColonToken: printf(" - ::\n"); break; // case Scanner::CommaToken: printf(" - ,\n"); break; // case Scanner::ConstToken: printf(" - const\n"); break; // case Scanner::SquareBegin: printf(" - [\n"); break; // case Scanner::SquareEnd: printf(" - ]\n"); break; // case Scanner::Identifier: printf(" - '%s'\n", qPrintable(scanner.identifier())); break; // default: // break; // } switch (tok) { case Scanner::StarToken: seenStar = true; stack.top().addIndirection(Indirection::Pointer); break; case Scanner::AmpersandToken: switch (stack.top().referenceType()) { case NoReference: stack.top().setReferenceType(LValueReference); break; case LValueReference: stack.top().setReferenceType(RValueReference); break; case RValueReference: const QString message = scanner.msgParseError(QStringLiteral("Too many '&' qualifiers")); if (errorMessage) *errorMessage = message; else qWarning().noquote().nospace() << message; return TypeInfo(); } break; case Scanner::LessThanToken: stack.push(TypeInfo()); break; case Scanner::CommaToken: { auto i = stack.pop(); stack.top().addInstantiation(i); // Add after populating to prevent detach stack.push(TypeInfo()); } break; case Scanner::GreaterThanToken: { auto i = stack.pop(); stack.top().addInstantiation(i); // Add after populating to prevent detach } break; case Scanner::ColonToken: colon_prefix = true; break; case Scanner::ConstToken: if (seenStar) { // "int *const": Last indirection is const. auto indirections = stack.top().indirectionsV(); Q_ASSERT(!indirections.isEmpty()); indirections[0] = Indirection::ConstPointer; stack.top().setIndirectionsV(indirections); } else { stack.top().setConstant(true); } break; case Scanner::VolatileToken: stack.top().setVolatile(true); break; case Scanner::OpenParenToken: // function pointers not supported case Scanner::CloseParenToken: { const QString message = scanner.msgParseError(QStringLiteral("Function pointers are not supported")); if (errorMessage) *errorMessage = message; else qWarning().noquote().nospace() << message; return TypeInfo(); } case Scanner::Identifier: if (in_array) { array = scanner.identifier(); } else if (colon_prefix || stack.top().qualifiedName().isEmpty()) { stack.top().addName(scanner.identifier()); colon_prefix = false; } else { QStringList qualifiedName = stack.top().qualifiedName(); qualifiedName.last().append(u' ' + scanner.identifier()); stack.top().setQualifiedName(qualifiedName); } break; case Scanner::SquareBegin: in_array = true; break; case Scanner::SquareEnd: in_array = false; stack.top().addArrayElement(array); break; default: break; } tok = scanner.nextToken(); } if (stack.isEmpty() || stack.constFirst().qualifiedName().isEmpty()) { *errorMessage = u"Unable to parse type \""_qs + str + u"\"."_qs; return {}; } return stack.constFirst(); }