qdoc: Bring qdoc's QML parser up to date

qdoc's QML parser has fallen behind the QML: parser in
QtDeclarative. Bring it up to date.

Change-Id: I12a688873564762434852960350c56655004e460
Task-number: QTBUG-44868
Reviewed-by: Martin Smith <martin.smith@digia.com>
This commit is contained in:
Martin Smith 2015-03-09 10:23:50 +01:00
parent fd826c112e
commit ee63462f47
9 changed files with 499 additions and 325 deletions

File diff suppressed because it is too large Load Diff

View File

@ -56,7 +56,7 @@ namespace QQmlJS { namespace AST {
class SourceLocation
{
public:
SourceLocation(quint32 offset = 0, quint32 length = 0, quint32 line = 0, quint32 column = 0)
explicit SourceLocation(quint32 offset = 0, quint32 length = 0, quint32 line = 0, quint32 column = 0)
: offset(offset), length(length),
startLine(line), startColumn(column)
{ }

View File

@ -114,7 +114,7 @@ double integerFromString(const QString &str, int radix)
Engine::Engine()
: _lexer(0)
: _lexer(0), _directives(0)
{ }
Engine::~Engine()
@ -135,6 +135,12 @@ Lexer *Engine::lexer() const
void Engine::setLexer(Lexer *lexer)
{ _lexer = lexer; }
Directives *Engine::directives() const
{ return _directives; }
void Engine::setDirectives(Directives *directives)
{ _directives = directives; }
MemoryPool *Engine::pool()
{ return &_pool; }

View File

@ -57,6 +57,7 @@ QT_QML_BEGIN_NAMESPACE
namespace QQmlJS {
class Lexer;
class Directives;
class MemoryPool;
class QML_PARSER_EXPORT DiagnosticMessage
@ -84,6 +85,7 @@ public:
class QML_PARSER_EXPORT Engine
{
Lexer *_lexer;
Directives *_directives;
MemoryPool _pool;
QList<AST::SourceLocation> _comments;
QString _extraCode;
@ -102,6 +104,9 @@ public:
Lexer *lexer() const;
void setLexer(Lexer *lexer);
Directives *directives() const;
void setDirectives(Directives *directives);
MemoryPool *pool();
inline QStringRef midRef(int position, int size) { return _code.midRef(position, size); }

View File

@ -33,17 +33,6 @@
#ifndef QQMLJSGLOBAL_P_H
#define QQMLJSGLOBAL_P_H
//
// W A R N I N G
// -------------
//
// This file is not part of the Qt API. It exists purely as an
// implementation detail. This header file may change from version to
// version without notice, or even be removed.
//
// We mean it.
//
#include <QtCore/qglobal.h>
#ifdef QT_CREATOR
@ -65,9 +54,9 @@
// QmlDevTools is a static library
# define QML_PARSER_EXPORT
# elif defined(QT_BUILD_QML_LIB)
# define QML_PARSER_EXPORT Q_AUTOTEST_EXPORT
# define QML_PARSER_EXPORT Q_DECL_EXPORT
# else
# define QML_PARSER_EXPORT
# define QML_PARSER_EXPORT Q_DECL_IMPORT
# endif
#endif // QT_CREATOR

View File

@ -887,8 +887,7 @@ again:
int Lexer::scanNumber(QChar ch)
{
if (ch != QLatin1Char('0')) {
QByteArray buf;
buf.reserve(64);
QVarLengthArray<char, 64> buf;
buf += ch.toLatin1();
QChar n = _char;
@ -1225,12 +1224,60 @@ bool Lexer::canInsertAutomaticSemicolon(int token) const
|| _followsClosingBrace;
}
bool Lexer::scanDirectives(Directives *directives)
static const int uriTokens[] = {
QQmlJSGrammar::T_IDENTIFIER,
QQmlJSGrammar::T_PROPERTY,
QQmlJSGrammar::T_SIGNAL,
QQmlJSGrammar::T_READONLY,
QQmlJSGrammar::T_ON,
QQmlJSGrammar::T_BREAK,
QQmlJSGrammar::T_CASE,
QQmlJSGrammar::T_CATCH,
QQmlJSGrammar::T_CONTINUE,
QQmlJSGrammar::T_DEFAULT,
QQmlJSGrammar::T_DELETE,
QQmlJSGrammar::T_DO,
QQmlJSGrammar::T_ELSE,
QQmlJSGrammar::T_FALSE,
QQmlJSGrammar::T_FINALLY,
QQmlJSGrammar::T_FOR,
QQmlJSGrammar::T_FUNCTION,
QQmlJSGrammar::T_IF,
QQmlJSGrammar::T_IN,
QQmlJSGrammar::T_INSTANCEOF,
QQmlJSGrammar::T_NEW,
QQmlJSGrammar::T_NULL,
QQmlJSGrammar::T_RETURN,
QQmlJSGrammar::T_SWITCH,
QQmlJSGrammar::T_THIS,
QQmlJSGrammar::T_THROW,
QQmlJSGrammar::T_TRUE,
QQmlJSGrammar::T_TRY,
QQmlJSGrammar::T_TYPEOF,
QQmlJSGrammar::T_VAR,
QQmlJSGrammar::T_VOID,
QQmlJSGrammar::T_WHILE,
QQmlJSGrammar::T_CONST,
QQmlJSGrammar::T_DEBUGGER,
QQmlJSGrammar::T_RESERVED_WORD,
QQmlJSGrammar::T_WITH,
QQmlJSGrammar::EOF_SYMBOL
};
static inline bool isUriToken(int token)
{
if (_qmlMode) {
// the directives are a Javascript-only extension.
return false;
const int *current = uriTokens;
while (*current != QQmlJSGrammar::EOF_SYMBOL) {
if (*current == token)
return true;
++current;
}
return false;
}
bool Lexer::scanDirectives(Directives *directives, DiagnosticMessage *error)
{
Q_ASSERT(!_qmlMode);
lex(); // fetch the first token
@ -1238,24 +1285,33 @@ bool Lexer::scanDirectives(Directives *directives)
return true;
do {
const int lineNumber = tokenStartLine();
const int column = tokenStartColumn();
lex(); // skip T_DOT
const int lineNumber = tokenStartLine();
if (! (_tokenKind == T_IDENTIFIER || _tokenKind == T_RESERVED_WORD))
return false; // expected a valid QML/JS directive
return true; // expected a valid QML/JS directive
const QString directiveName = tokenText();
if (! (directiveName == QLatin1String("pragma") ||
directiveName == QLatin1String("import")))
directiveName == QLatin1String("import"))) {
error->message = QCoreApplication::translate("QQmlParser", "Syntax error");
error->loc.startLine = tokenStartLine();
error->loc.startColumn = tokenStartColumn();
return false; // not a valid directive name
}
// it must be a pragma or an import directive.
if (directiveName == QLatin1String("pragma")) {
// .pragma library
if (! (lex() == T_IDENTIFIER && tokenText() == QLatin1String("library")))
if (! (lex() == T_IDENTIFIER && tokenText() == QLatin1String("library"))) {
error->message = QCoreApplication::translate("QQmlParser", "Syntax error");
error->loc.startLine = tokenStartLine();
error->loc.startColumn = tokenStartColumn();
return false; // expected `library
}
// we found a .pragma library directive
directives->pragmaLibrary();
@ -1274,22 +1330,53 @@ bool Lexer::scanDirectives(Directives *directives)
fileImport = true;
pathOrUri = tokenText();
if (!pathOrUri.endsWith(QLatin1String("js"))) {
error->message = QCoreApplication::translate("QQmlParser","Imported file must be a script");
error->loc.startLine = tokenStartLine();
error->loc.startColumn = tokenStartColumn();
return false;
}
} else if (_tokenKind == T_IDENTIFIER) {
// .import T_IDENTIFIER (. T_IDENTIFIER)* T_NUMERIC_LITERAL as T_IDENTIFIER
pathOrUri = tokenText();
lex(); // skip the first T_IDENTIFIER
for (; _tokenKind == T_DOT; lex()) {
if (lex() != T_IDENTIFIER)
while (true) {
if (!isUriToken(_tokenKind)) {
error->message = QCoreApplication::translate("QQmlParser","Invalid module URI");
error->loc.startLine = tokenStartLine();
error->loc.startColumn = tokenStartColumn();
return false;
}
pathOrUri += QLatin1Char('.');
pathOrUri += tokenText();
pathOrUri.append(tokenText());
lex();
if (tokenStartLine() != lineNumber) {
error->message = QCoreApplication::translate("QQmlParser","Invalid module URI");
error->loc.startLine = tokenStartLine();
error->loc.startColumn = tokenStartColumn();
return false;
}
if (_tokenKind != QQmlJSGrammar::T_DOT)
break;
pathOrUri.append(QLatin1Char('.'));
lex();
if (tokenStartLine() != lineNumber) {
error->message = QCoreApplication::translate("QQmlParser","Invalid module URI");
error->loc.startLine = tokenStartLine();
error->loc.startColumn = tokenStartColumn();
return false;
}
}
if (_tokenKind != T_NUMERIC_LITERAL)
if (_tokenKind != T_NUMERIC_LITERAL) {
error->message = QCoreApplication::translate("QQmlParser","Module import requires a version");
error->loc.startLine = tokenStartLine();
error->loc.startColumn = tokenStartColumn();
return false; // expected the module version number
}
version = tokenText();
}
@ -1297,22 +1384,51 @@ bool Lexer::scanDirectives(Directives *directives)
//
// recognize the mandatory `as' followed by the module name
//
if (! (lex() == T_IDENTIFIER && tokenText() == QLatin1String("as")))
if (! (lex() == T_IDENTIFIER && tokenText() == QLatin1String("as") && tokenStartLine() == lineNumber)) {
if (fileImport)
error->message = QCoreApplication::translate("QQmlParser", "File import requires a qualifier");
else
error->message = QCoreApplication::translate("QQmlParser", "Module import requires a qualifier");
if (tokenStartLine() != lineNumber) {
error->loc.startLine = lineNumber;
error->loc.startColumn = column;
} else {
error->loc.startLine = tokenStartLine();
error->loc.startColumn = tokenStartColumn();
}
return false; // expected `as'
}
if (lex() != T_IDENTIFIER)
if (lex() != T_IDENTIFIER || tokenStartLine() != lineNumber) {
if (fileImport)
error->message = QCoreApplication::translate("QQmlParser", "File import requires a qualifier");
else
error->message = QCoreApplication::translate("QQmlParser", "Module import requires a qualifier");
error->loc.startLine = tokenStartLine();
error->loc.startColumn = tokenStartColumn();
return false; // expected module name
}
const QString module = tokenText();
if (!module.at(0).isUpper()) {
error->message = QCoreApplication::translate("QQmlParser","Invalid import qualifier");
error->loc.startLine = tokenStartLine();
error->loc.startColumn = tokenStartColumn();
return false;
}
if (fileImport)
directives->importFile(pathOrUri, module);
directives->importFile(pathOrUri, module, lineNumber, column);
else
directives->importModule(pathOrUri, version, module);
directives->importModule(pathOrUri, version, module, lineNumber, column);
}
if (tokenStartLine() != lineNumber)
if (tokenStartLine() != lineNumber) {
error->message = QCoreApplication::translate("QQmlParser", "Syntax error");
error->loc.startLine = tokenStartLine();
error->loc.startColumn = tokenStartColumn();
return false; // the directives cannot span over multiple lines
}
// fetch the first token after the .pragma/.import directive
lex();

View File

@ -55,6 +55,7 @@ QT_QML_BEGIN_NAMESPACE
namespace QQmlJS {
class Engine;
class DiagnosticMessage;
class QML_PARSER_EXPORT Directives {
public:
@ -64,17 +65,21 @@ public:
{
}
virtual void importFile(const QString &jsfile, const QString &module)
virtual void importFile(const QString &jsfile, const QString &module, int line, int column)
{
Q_UNUSED(jsfile);
Q_UNUSED(module);
Q_UNUSED(line);
Q_UNUSED(column);
}
virtual void importModule(const QString &uri, const QString &version, const QString &module)
virtual void importModule(const QString &uri, const QString &version, const QString &module, int line, int column)
{
Q_UNUSED(uri);
Q_UNUSED(version);
Q_UNUSED(module);
Q_UNUSED(line);
Q_UNUSED(column);
}
};
@ -146,7 +151,7 @@ public:
int lex();
bool scanRegExp(RegExpBodyPrefix prefix = NoPrefix);
bool scanDirectives(Directives *directives);
bool scanDirectives(Directives *directives, DiagnosticMessage *error);
int regExpFlags() const { return _patternFlags; }
QString regExpPattern() const { return _tokenText; }

View File

@ -57,6 +57,8 @@ QT_QML_BEGIN_NAMESPACE
namespace QQmlJS {
class Managed;
class QML_PARSER_EXPORT MemoryPool : public QSharedData
{
MemoryPool(const MemoryPool &other);
@ -100,6 +102,30 @@ public:
_ptr = _end = 0;
}
template <typename _Tp> _Tp *New() { return new (this->allocate(sizeof(_Tp))) _Tp(); }
template <typename PoolContentType, typename Visitor>
void visitManagedPool(Visitor &visitor)
{
for (int i = 0; i <= _blockCount; ++i) {
char *p = _blocks[i];
char *end = p + BLOCK_SIZE;
if (i == _blockCount) {
Q_ASSERT(_ptr <= end);
end = _ptr;
}
Q_ASSERT(p <= end);
const qptrdiff increment = (sizeof(PoolContentType) + 7) & ~7;
while (p + increment <= end) {
visitor(reinterpret_cast<PoolContentType*>(p));
p += increment;
}
}
}
private:
void *allocate_helper(size_t size)
{

View File

@ -161,7 +161,24 @@ bool Parser::parse(int startToken)
token_buffer[0].token = startToken;
first_token = &token_buffer[0];
last_token = &token_buffer[1];
if (startToken == T_FEED_JS_PROGRAM && !lexer->qmlMode()) {
Directives ignoreDirectives;
Directives *directives = driver->directives();
if (!directives)
directives = &ignoreDirectives;
DiagnosticMessage error;
if (!lexer->scanDirectives(directives, &error)) {
diagnostic_messages.append(error);
return false;
}
token_buffer[1].token = lexer->tokenKind();
token_buffer[1].dval = lexer->tokenValue();
token_buffer[1].loc = location(lexer);
token_buffer[1].spell = lexer->tokenSpell();
last_token = &token_buffer[2];
} else {
last_token = &token_buffer[1];
}
tos = -1;
program = 0;