mirror of
https://github.com/eclipse-cdt/cdt
synced 2025-06-07 17:56:01 +02:00
Bug 480238 - Clean Up Acorn QML Parser
Fixed the AST elements so that they more closely match the format used by acorn (i.e. using 'id' instead of 'identifier' and 'name' instead of 'raw' for the identifier name). Resolved one of the ambiguities dealing with using 'signal', 'property', and 'readonly' as identifiers for properties and QML Objects. Added a bunch of new tests and fixed the old ones to match the new AST. Change-Id: I5a8bbd14b3e6f8531268740dd9e57cb48a0e22b3 Signed-off-by: Matthew Bastien <mbastien@blackberry.com>
This commit is contained in:
parent
5eaf1129a5
commit
19cd53ec10
6 changed files with 2528 additions and 1917 deletions
|
@ -13,12 +13,12 @@
|
||||||
// This will only be visible globally if we are in a browser environment
|
// This will only be visible globally if we are in a browser environment
|
||||||
var acornQML;
|
var acornQML;
|
||||||
|
|
||||||
(function(mod) {
|
(function (mod) {
|
||||||
if (typeof exports == "object" && typeof module == "object") // CommonJS
|
if (typeof exports == "object" && typeof module == "object") // CommonJS
|
||||||
return module.exports = mod(require("./inject.js"), require("acorn"));
|
return module.exports = mod(require("./inject.js"), require("acorn"));
|
||||||
if (typeof define == "function" && define.amd) // AMD
|
if (typeof define == "function" && define.amd) // AMD
|
||||||
return define(["./inject.js", "acorn/dist/acorn"], mod);
|
return define(["./inject.js", "acorn/dist/acorn"], mod);
|
||||||
acornQML = mod(injectQML, acorn); // Plain browser env
|
acornQML = mod(injectQML, acorn); // Plain browser env
|
||||||
})(function(injectQML, acorn) {
|
})(function (injectQML, acorn) {
|
||||||
return injectQML(acorn);
|
return injectQML(acorn);
|
||||||
})
|
})
|
|
@ -13,14 +13,14 @@
|
||||||
// This will only be visible globally if we are in a browser environment
|
// This will only be visible globally if we are in a browser environment
|
||||||
var injectQML;
|
var injectQML;
|
||||||
|
|
||||||
(function(mod) {
|
(function (mod) {
|
||||||
if (typeof exports == "object" && typeof module == "object") // CommonJS
|
if (typeof exports === "object" && typeof module === "object") // CommonJS
|
||||||
return module.exports = mod();
|
return module.exports = mod();
|
||||||
if (typeof define == "function" && define.amd) // AMD
|
if (typeof define === "function" && define.amd) // AMD
|
||||||
return define([], mod);
|
return define([], mod);
|
||||||
injectQML = mod(); // Plain browser env
|
injectQML = mod(); // Plain browser env
|
||||||
})(function() {
|
})(function () {
|
||||||
return function(acorn) {
|
return function (acorn) {
|
||||||
// Acorn token types
|
// Acorn token types
|
||||||
var tt = acorn.tokTypes;
|
var tt = acorn.tokTypes;
|
||||||
|
|
||||||
|
@ -52,9 +52,9 @@ var injectQML;
|
||||||
// QML keywords
|
// QML keywords
|
||||||
kw("import");
|
kw("import");
|
||||||
kw("pragma");
|
kw("pragma");
|
||||||
kw("property", { isQMLContextual : true });
|
kw("property", { isQMLContextual: true });
|
||||||
kw("readonly", { isQMLContextual : true });
|
kw("readonly", { isQMLContextual: true });
|
||||||
kw("signal", { isQMLContextual : true });
|
kw("signal", { isQMLContextual: true });
|
||||||
kw("as");
|
kw("as");
|
||||||
kw("boolean", { isPrimitive: true });
|
kw("boolean", { isPrimitive: true });
|
||||||
kw("double", { isPrimitive: true });
|
kw("double", { isPrimitive: true });
|
||||||
|
@ -86,7 +86,7 @@ var injectQML;
|
||||||
* Parses a set of QML Header Statements which can either be of
|
* Parses a set of QML Header Statements which can either be of
|
||||||
* the type import or pragma
|
* the type import or pragma
|
||||||
*/
|
*/
|
||||||
pp.qml_parseHeaderStatements = function() {
|
pp.qml_parseHeaderStatements = function () {
|
||||||
var node = this.startNode()
|
var node = this.startNode()
|
||||||
node.statements = [];
|
node.statements = [];
|
||||||
|
|
||||||
|
@ -103,20 +103,17 @@ var injectQML;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (node.statements.length > 0) {
|
|
||||||
return this.finishNode(node, "QMLHeaderStatements");
|
return this.finishNode(node, "QMLHeaderStatements");
|
||||||
}
|
}
|
||||||
return undefined;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Parses a QML Pragma statement of the form:
|
* Parses a QML Pragma statement of the form:
|
||||||
* 'pragma' <Identifier>
|
* 'pragma' <Identifier>
|
||||||
*/
|
*/
|
||||||
pp.qml_parsePragmaStatement = function() {
|
pp.qml_parsePragmaStatement = function () {
|
||||||
var node = this.startNode();
|
var node = this.startNode();
|
||||||
this.next();
|
this.expectContextual(qtt._pragma);
|
||||||
node.identifier = this.parseIdent(false);
|
node.id = this.parseIdent(false);
|
||||||
this.semicolon();
|
this.semicolon();
|
||||||
return this.finishNode(node, "QMLPragmaStatement");
|
return this.finishNode(node, "QMLPragmaStatement");
|
||||||
}
|
}
|
||||||
|
@ -128,40 +125,43 @@ var injectQML;
|
||||||
*
|
*
|
||||||
* as specified by http://doc.qt.io/qt-5/qtqml-syntax-imports.html
|
* as specified by http://doc.qt.io/qt-5/qtqml-syntax-imports.html
|
||||||
*/
|
*/
|
||||||
pp.qml_parseImportStatement = function() {
|
pp.qml_parseImportStatement = function () {
|
||||||
var node = this.startNode();
|
var node = this.startNode();
|
||||||
this.next();
|
|
||||||
|
|
||||||
// The type of import varies solely on the next token
|
if (!this.eat(tt._import) && !this.eatContextual(qtt._import)) {
|
||||||
switch(this.type) {
|
this.unexpected();
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (this.type) {
|
||||||
case tt.name:
|
case tt.name:
|
||||||
node.module = this.qml_parseModule();
|
node.module = this.qml_parseModule();
|
||||||
|
node.directory = null;
|
||||||
break;
|
break;
|
||||||
case tt.string:
|
case tt.string:
|
||||||
node.directoryPath = this.parseLiteral(this.value);
|
node.module = null;
|
||||||
|
node.directory = this.parseLiteral(this.value);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
this.unexpected();
|
this.unexpected();
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Parse the qualifier, if any
|
// Parse the qualifier, if any
|
||||||
if (this.isContextual(qtt._as)) {
|
if (this.isContextual(qtt._as)) {
|
||||||
node.qualifier = this.qml_parseQualifier();
|
node.qualifier = this.qml_parseQualifier();
|
||||||
}
|
}
|
||||||
|
|
||||||
this.semicolon();
|
this.semicolon();
|
||||||
|
|
||||||
return this.finishNode(node, "QMLImportStatement");
|
return this.finishNode(node, "QMLImportStatement");
|
||||||
};
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Parses a QML Module of the form:
|
* Parses a QML Module of the form:
|
||||||
* <QualifiedId> <VersionLiteral>
|
* <QMLQualifiedId> <QMLVersionLiteral> ['as' <QMLQualifier>]?
|
||||||
*/
|
*/
|
||||||
pp.qml_parseModule = function() {
|
pp.qml_parseModule = function () {
|
||||||
var node = this.startNode();
|
var node = this.startNode();
|
||||||
|
|
||||||
node.qualifiedId = this.qml_parseQualifiedId();
|
node.id = this.qml_parseQualifiedId(false);
|
||||||
if (this.type === tt.num) {
|
if (this.type === tt.num) {
|
||||||
node.version = this.qml_parseVersionLiteral();
|
node.version = this.qml_parseVersionLiteral();
|
||||||
} else {
|
} else {
|
||||||
|
@ -175,7 +175,7 @@ var injectQML;
|
||||||
* Parses a QML Version Literal which consists of a major and minor
|
* Parses a QML Version Literal which consists of a major and minor
|
||||||
* version separated by a '.'
|
* version separated by a '.'
|
||||||
*/
|
*/
|
||||||
pp.qml_parseVersionLiteral = function() {
|
pp.qml_parseVersionLiteral = function () {
|
||||||
var node = this.startNode();
|
var node = this.startNode();
|
||||||
|
|
||||||
node.raw = this.input.slice(this.start, this.end);
|
node.raw = this.input.slice(this.start, this.end);
|
||||||
|
@ -184,11 +184,11 @@ var injectQML;
|
||||||
if (matches = /(\d+)\.(\d+)/.exec(node.raw)) {
|
if (matches = /(\d+)\.(\d+)/.exec(node.raw)) {
|
||||||
node.major = parseInt(matches[1]);
|
node.major = parseInt(matches[1]);
|
||||||
node.minor = parseInt(matches[2]);
|
node.minor = parseInt(matches[2]);
|
||||||
|
this.next();
|
||||||
} else {
|
} else {
|
||||||
this.raise(this.start, "QML module must specify major and minor version");
|
this.raise(this.start, "QML module must specify major and minor version");
|
||||||
}
|
}
|
||||||
|
|
||||||
this.next();
|
|
||||||
return this.finishNode(node, "QMLVersionLiteral");
|
return this.finishNode(node, "QMLVersionLiteral");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -196,10 +196,10 @@ var injectQML;
|
||||||
* Parses a QML Qualifier of the form:
|
* Parses a QML Qualifier of the form:
|
||||||
* 'as' <Identifier>
|
* 'as' <Identifier>
|
||||||
*/
|
*/
|
||||||
pp.qml_parseQualifier = function() {
|
pp.qml_parseQualifier = function () {
|
||||||
var node = this.startNode();
|
var node = this.startNode();
|
||||||
this.next();
|
this.expectContextual(qtt._as);
|
||||||
node.identifier = this.qml_parseIdent(false);
|
node.id = this.qml_parseIdent(false);
|
||||||
return this.finishNode(node, "QMLQualifier");
|
return this.finishNode(node, "QMLQualifier");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -209,12 +209,12 @@ var injectQML;
|
||||||
*
|
*
|
||||||
* http://doc.qt.io/qt-5/qtqml-syntax-basics.html#object-declarations
|
* http://doc.qt.io/qt-5/qtqml-syntax-basics.html#object-declarations
|
||||||
*/
|
*/
|
||||||
pp.qml_parseObjectLiteral = function(node) {
|
pp.qml_parseObjectLiteral = function (node) {
|
||||||
if (!node) {
|
if (!node) {
|
||||||
node = this.startNode();
|
node = this.startNode();
|
||||||
}
|
}
|
||||||
if (!node.qualifiedId) {
|
if (!node.id) {
|
||||||
node.qualifiedId = this.qml_parseQualifiedId();
|
node.id = this.qml_parseQualifiedId(false);
|
||||||
}
|
}
|
||||||
node.block = this.qml_parseMemberBlock();
|
node.block = this.qml_parseMemberBlock();
|
||||||
return this.finishNode(node, "QMLObjectLiteral");
|
return this.finishNode(node, "QMLObjectLiteral");
|
||||||
|
@ -224,7 +224,7 @@ var injectQML;
|
||||||
* Parses a QML Member Block of the form:
|
* Parses a QML Member Block of the form:
|
||||||
* { <QMLMember>* }
|
* { <QMLMember>* }
|
||||||
*/
|
*/
|
||||||
pp.qml_parseMemberBlock = function() {
|
pp.qml_parseMemberBlock = function () {
|
||||||
var node = this.startNode();
|
var node = this.startNode();
|
||||||
this.expect(tt.braceL);
|
this.expect(tt.braceL);
|
||||||
node.members = [];
|
node.members = [];
|
||||||
|
@ -242,28 +242,33 @@ var injectQML;
|
||||||
* - a JavaScript Function Declaration
|
* - a JavaScript Function Declaration
|
||||||
* - a Signal Definition
|
* - a Signal Definition
|
||||||
*/
|
*/
|
||||||
pp.qml_parseMember = function() {
|
pp.qml_parseMember = function () {
|
||||||
var node = this.startNode();
|
if (this.type === tt._default || this.isContextual(qtt._default) || this.isContextual(qtt._readonly) || this.isContextual(qtt._property)) {
|
||||||
|
return this.qml_parsePropertyDeclaration();
|
||||||
if (this.type === tt._default
|
|
||||||
|| this.isContextual(qtt._default)
|
|
||||||
|| this.isContextual(qtt._readonly)
|
|
||||||
|| this.isContextual(qtt._property)) {
|
|
||||||
return this.qml_parsePropertyDeclaration(node);
|
|
||||||
} else if (this.isContextual(qtt._signal)) {
|
} else if (this.isContextual(qtt._signal)) {
|
||||||
return this.qml_parseSignalDefinition(node);
|
return this.qml_parseSignalDefinition();
|
||||||
} else if (this.type === tt._function) {
|
} else if (this.type === tt._function) {
|
||||||
return this.parseFunctionStatement(node);
|
return this.parseFunctionStatement();
|
||||||
|
}
|
||||||
|
return this.qml_parseObjectLiteralOrPropertyBinding();
|
||||||
}
|
}
|
||||||
|
|
||||||
node.qualifiedId = this.qml_parseQualifiedId();
|
/*
|
||||||
switch(this.type) {
|
* Parses a QML Object Literal or Property Binding depending on the tokens found.
|
||||||
|
*/
|
||||||
|
pp.qml_parseObjectLiteralOrPropertyBinding = function (node) {
|
||||||
|
if (!node) {
|
||||||
|
node = this.startNode();
|
||||||
|
}
|
||||||
|
if (!node.id) {
|
||||||
|
node.id = this.qml_parseQualifiedId(false);
|
||||||
|
}
|
||||||
|
switch (this.type) {
|
||||||
case tt.braceL:
|
case tt.braceL:
|
||||||
return this.qml_parseObjectLiteral(node);
|
return this.qml_parseObjectLiteral(node);
|
||||||
case tt.colon:
|
case tt.colon:
|
||||||
return this.qml_parseProperty(node);
|
return this.qml_parsePropertyBinding(node);
|
||||||
}
|
}
|
||||||
|
|
||||||
this.unexpected();
|
this.unexpected();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -271,84 +276,138 @@ var injectQML;
|
||||||
* Parses a QML Property of the form:
|
* Parses a QML Property of the form:
|
||||||
* <QMLQualifiedID> <QMLBinding>
|
* <QMLQualifiedID> <QMLBinding>
|
||||||
*/
|
*/
|
||||||
pp.qml_parseProperty = function(node) {
|
pp.qml_parsePropertyBinding = function (node) {
|
||||||
if (!node) {
|
if (!node) {
|
||||||
node = this.startNode();
|
node = this.startNode();
|
||||||
}
|
}
|
||||||
if (!node.qualifiedId) {
|
if (!node.id) {
|
||||||
node.qualifiedId = this.qml_parseQualifiedId();
|
node.id = this.qml_parseQualifiedId(false);
|
||||||
}
|
}
|
||||||
node.binding = this.qml_parseBinding();
|
this.expect(tt.colon);
|
||||||
return this.finishNode(node, "QMLProperty");
|
node.expr = this.qml_parsePropertyAssignment();
|
||||||
|
return this.finishNode(node, "QMLPropertyBinding");
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Parses a QML Signal Definition of the form:
|
* Parses a QML Signal Definition of the form:
|
||||||
* 'signal' <Identifier> [(<Type> <Identifier> [',' <Type> <Identifier>]* )]?
|
* 'signal' <Identifier> [(<Type> <Identifier> [',' <Type> <Identifier>]* )]?
|
||||||
*/
|
*/
|
||||||
pp.qml_parseSignalDefinition = function(node) {
|
pp.qml_parseSignalDefinition = function () {
|
||||||
if (!node) {
|
var node = this.startNode();
|
||||||
node = this.startNode();
|
|
||||||
}
|
// Parse as a qualified id in case this is not a signal definition
|
||||||
this.next();
|
var signal = this.qml_parseQualifiedId(true);
|
||||||
node.identifier = this.qml_parseIdent(false);
|
if (signal.parts.length === 1) {
|
||||||
node.parameters = [];
|
if (signal.name !== qtt._signal) {
|
||||||
if (this.type === tt.parenL) {
|
|
||||||
this.next();
|
|
||||||
if (this.type !== tt.parenR) {
|
|
||||||
do {
|
|
||||||
var paramNode = this.startNode();
|
|
||||||
paramNode.type = this.qml_parseIdent(false);
|
|
||||||
paramNode.identifier = this.qml_parseIdent(false);
|
|
||||||
node.parameters.push(paramNode);
|
|
||||||
} while(this.eat(tt.comma));
|
|
||||||
}
|
|
||||||
if (!this.eat(tt.parenR)) {
|
|
||||||
this.unexpected();
|
this.unexpected();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (this.type === tt.colon || this.type === tt.braceL) {
|
||||||
|
// This is a property binding or object literal
|
||||||
|
node.id = signal;
|
||||||
|
return this.qml_parseObjectLiteralOrPropertyBinding(node);
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
// Signal keyword is a qualified ID. This is not a signal definition
|
||||||
|
node.id = signal;
|
||||||
|
return this.qml_parseObjectLiteralOrPropertyBinding(node);
|
||||||
|
}
|
||||||
|
|
||||||
|
node.id = this.qml_parseIdent(false);
|
||||||
|
this.qml_parseSignalParams(node);
|
||||||
this.semicolon();
|
this.semicolon();
|
||||||
return this.finishNode(node, "QMLSignalDefinition");
|
return this.finishNode(node, "QMLSignalDefinition");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Parses QML Signal Parameters of the form:
|
||||||
|
* [(<Type> <Identifier> [',' <Type> <Identifier>]* )]?
|
||||||
|
*/
|
||||||
|
pp.qml_parseSignalParams = function (node) {
|
||||||
|
node.params = [];
|
||||||
|
if (this.eat(tt.parenL)) {
|
||||||
|
if (!this.eat(tt.parenR)) {
|
||||||
|
do {
|
||||||
|
var param = this.startNode();
|
||||||
|
param.kind = this.qml_parseIdent(false);
|
||||||
|
param.id = this.qml_parseIdent(false);
|
||||||
|
node.params.push(this.finishNode(param, "QMLParameter"));
|
||||||
|
} while (this.eat(tt.comma));
|
||||||
|
this.expect(tt.parenR);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Parses a QML Property Declaration (or Alias) of the form:
|
* Parses a QML Property Declaration (or Alias) of the form:
|
||||||
* ['default'|'readonly'] 'property' <QMLType> <Identifier> [<QMLBinding>]
|
* ['default'|'readonly'] 'property' <QMLType> <Identifier> [<QMLBinding>]
|
||||||
*/
|
*/
|
||||||
pp.qml_parsePropertyDeclaration = function(node) {
|
pp.qml_parsePropertyDeclaration = function () {
|
||||||
|
var node = this.startNode();
|
||||||
|
|
||||||
|
// Parse 'default' or 'readonly'
|
||||||
node["default"] = false;
|
node["default"] = false;
|
||||||
node["readonly"] = false;
|
node["readonly"] = false;
|
||||||
|
if (this.eat(tt._default) || this.eatContextual(qtt._default)) {
|
||||||
if (this.type === tt._default || this.isContextual(qtt._default)) {
|
|
||||||
node["default"] = true;
|
node["default"] = true;
|
||||||
this.next();
|
} else if (this.isContextual(qtt._readonly)) {
|
||||||
} else if (this.eatContextual(qtt._readonly)) {
|
// Parse as a qualified id in case this is not a property declaration
|
||||||
node["readonly"] = true;
|
var readonly = this.qml_parseQualifiedId(true);
|
||||||
|
if (readonly.parts.length === 1) {
|
||||||
|
if (this.type === tt.colon || this.type === tt.braceL) {
|
||||||
|
// This is a property binding or object literal.
|
||||||
|
node.id = readonly;
|
||||||
|
return this.qml_parseObjectLiteralOrPropertyBinding(node);
|
||||||
}
|
}
|
||||||
this.expectContextual(qtt._property);
|
node["readonly"] = true;
|
||||||
node.typeInfo = this.qml_parseType();
|
} else {
|
||||||
node.identifier = this.qml_parseIdent(false);
|
// Readonly keyword is a qualified ID. This is not a property declaration.
|
||||||
if (this.type !== tt.colon) {
|
node.id = readonly;
|
||||||
|
return this.qml_parseObjectLiteralOrPropertyBinding(node);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse as a qualified id in case this is not a property declaration
|
||||||
|
var property = this.qml_parseQualifiedId(true);
|
||||||
|
if (property.parts.length === 1 || node["default"] || node["readonly"]) {
|
||||||
|
if (property.name !== qtt._property) {
|
||||||
|
this.unexpected();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.type === tt.colon || this.type === tt.braceL) {
|
||||||
|
// This is a property binding or object literal.
|
||||||
|
node["default"] = undefined;
|
||||||
|
node["readonly"] = undefined;
|
||||||
|
node.id = property;
|
||||||
|
return this.qml_parseObjectLiteralOrPropertyBinding(node);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Property keyword is a qualified ID. This is not a property declaration.
|
||||||
|
node["default"] = undefined;
|
||||||
|
node["readonly"] = undefined;
|
||||||
|
node.id = property;
|
||||||
|
return this.qml_parseObjectLiteralOrPropertyBinding(node);
|
||||||
|
}
|
||||||
|
|
||||||
|
node.kind = this.qml_parseKind();
|
||||||
|
node.id = this.qml_parseIdent(false);
|
||||||
|
if (!this.eat(tt.colon)) {
|
||||||
|
node.init = null;
|
||||||
this.semicolon();
|
this.semicolon();
|
||||||
} else {
|
} else {
|
||||||
node.binding = this.qml_parseBinding();
|
node.init = this.qml_parsePropertyAssignment();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (node.typeInfo.type === qtt._alias) {
|
|
||||||
node.typeInfo = undefined;
|
|
||||||
return this.finishNode(node, "QMLPropertyAlias");
|
|
||||||
}
|
|
||||||
return this.finishNode(node, "QMLPropertyDeclaration");
|
return this.finishNode(node, "QMLPropertyDeclaration");
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Parses a QML Binding of the form:
|
* Parses one of the following possibilities for a QML Property assignment:
|
||||||
* ':' (<Expression>|<QMLStatementBlock>)
|
* - JavaScript Expression
|
||||||
|
* - QML JavaScript Statement Block
|
||||||
|
* - QML Object Literal
|
||||||
*/
|
*/
|
||||||
pp.qml_parseBinding = function() {
|
pp.qml_parsePropertyAssignment = function () {
|
||||||
var node = this.startNode();
|
|
||||||
this.expect(tt.colon);
|
|
||||||
|
|
||||||
// TODO: solve ambiguity where a QML Object Literal starts with a
|
// TODO: solve ambiguity where a QML Object Literal starts with a
|
||||||
// Qualified Id that looks very similar to a MemberExpression in
|
// Qualified Id that looks very similar to a MemberExpression in
|
||||||
// JavaScript. For now, we just won't parse statements like:
|
// JavaScript. For now, we just won't parse statements like:
|
||||||
|
@ -356,23 +415,23 @@ var injectQML;
|
||||||
// test: QMLObject.QualifiedId { }
|
// test: QMLObject.QualifiedId { }
|
||||||
|
|
||||||
if (this.type === tt.braceL) {
|
if (this.type === tt.braceL) {
|
||||||
node.block = this.qml_parseStatementBlock();
|
return this.qml_parseStatementBlock();
|
||||||
return this.finishNode(node, "QMLBinding");
|
} else {
|
||||||
}
|
var node = this.parseExpression(false);
|
||||||
node.expr = this.parseExpression(false);
|
|
||||||
this.semicolon();
|
this.semicolon();
|
||||||
return this.finishNode(node, "QMLBinding");
|
return node;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Parses a QML Statement Block of the form:
|
* Parses a QML Statement Block of the form:
|
||||||
* { <JavaScript Statement>* }
|
* { <JavaScript Statement>* }
|
||||||
*/
|
*/
|
||||||
pp.qml_parseStatementBlock = function() {
|
pp.qml_parseStatementBlock = function () {
|
||||||
var node = this.startNode();
|
var node = this.startNode();
|
||||||
this.expect(tt.braceL);
|
this.expect(tt.braceL);
|
||||||
node.statements = [];
|
node.statements = [];
|
||||||
while(!this.eat(tt.braceR)) {
|
while (!this.eat(tt.braceR)) {
|
||||||
node.statements.push(this.parseStatement(true, false));
|
node.statements.push(this.parseStatement(true, false));
|
||||||
}
|
}
|
||||||
return this.finishNode(node, "QMLStatementBlock");
|
return this.finishNode(node, "QMLStatementBlock");
|
||||||
|
@ -382,52 +441,42 @@ var injectQML;
|
||||||
* Parses a QML Type which can be either a Qualified ID or a primitive type keyword.
|
* Parses a QML Type which can be either a Qualified ID or a primitive type keyword.
|
||||||
* Returns a node of type qtt._alias if the type keyword parsed was "alias".
|
* Returns a node of type qtt._alias if the type keyword parsed was "alias".
|
||||||
*/
|
*/
|
||||||
pp.qml_parseType = function() {
|
pp.qml_parseKind = function () {
|
||||||
var node = this.startNode();
|
|
||||||
|
|
||||||
if (this.type === tt.name || this.type === tt._var) {
|
if (this.type === tt.name || this.type === tt._var) {
|
||||||
var value = this.value;
|
var value = this.value;
|
||||||
if (this.qml_eatPrimitiveType(value)) {
|
if (this.qml_eatPrimitiveType(value)) {
|
||||||
node.isPrimitive = true;
|
return value;
|
||||||
node.primitive = value;
|
|
||||||
} else if (this.eatContextual(qtt._alias)) {
|
} else if (this.eatContextual(qtt._alias)) {
|
||||||
return this.finishNode(node, qtt._alias);
|
return qtt._alias;
|
||||||
} else {
|
} else {
|
||||||
node.isPrimitive = false;
|
return this.qml_parseQualifiedId(false);
|
||||||
node.qualifiedId = this.qml_parseQualifiedId();
|
}
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
this.unexpected();
|
this.unexpected();
|
||||||
}
|
}
|
||||||
|
|
||||||
return this.finishNode(node, "QMLType");
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Parses a Qualified ID of the form:
|
* Parses a Qualified ID of the form:
|
||||||
* <Identifier> ('.' <Identifier>)*
|
* <Identifier> ('.' <Identifier>)*
|
||||||
|
*
|
||||||
|
* If 'liberal' is true then this method will allow non-contextual QML keywords as
|
||||||
|
* identifiers.
|
||||||
*/
|
*/
|
||||||
pp.qml_parseQualifiedId = function() {
|
pp.qml_parseQualifiedId = function (liberal) {
|
||||||
var node = this.startNode();
|
var node = this.startNode();
|
||||||
|
|
||||||
node.parts = [];
|
node.parts = [];
|
||||||
if (!this.qml_isIdent(this.type, this.value)) {
|
node.parts.push(this.qml_parseIdent(liberal));
|
||||||
this.unexpected();
|
while (this.eat(tt.dot)) {
|
||||||
|
node.parts.push(this.qml_parseIdent(liberal));
|
||||||
}
|
}
|
||||||
var id = this.value;
|
|
||||||
this.next();
|
node.name = "";
|
||||||
node.parts.push(id);
|
for (var i = 0; i < node.parts.length; i++) {
|
||||||
while(this.type === tt.dot) {
|
node.name += node.parts[i].name;
|
||||||
id += '.';
|
if (i < node.parts.length - 1) {
|
||||||
this.next();
|
node.name += ".";
|
||||||
if (!this.qml_isIdent(this.type, this.value)) {
|
|
||||||
this.unexpected();
|
|
||||||
}
|
}
|
||||||
id += this.value;
|
|
||||||
node.parts.push(this.value);
|
|
||||||
this.next();
|
|
||||||
}
|
}
|
||||||
node.raw = id;
|
|
||||||
|
|
||||||
return this.finishNode(node, "QMLQualifiedID");
|
return this.finishNode(node, "QMLQualifiedID");
|
||||||
}
|
}
|
||||||
|
@ -435,16 +484,21 @@ var injectQML;
|
||||||
/*
|
/*
|
||||||
* Parses an Identifier in a QML Context. That is, this method uses 'isQMLContextual'
|
* Parses an Identifier in a QML Context. That is, this method uses 'isQMLContextual'
|
||||||
* to throw an error if a non-contextual QML keyword is found.
|
* to throw an error if a non-contextual QML keyword is found.
|
||||||
|
*
|
||||||
|
* If 'liberal' is true then this method will allow non-contextual QML keywords as
|
||||||
|
* identifiers.
|
||||||
*/
|
*/
|
||||||
pp.qml_parseIdent = function(liberal) {
|
pp.qml_parseIdent = function (liberal) {
|
||||||
// Check for non-contextual QML keywords
|
// Check for non-contextual QML keywords
|
||||||
if (this.type === tt.name) {
|
if (this.type === tt.name) {
|
||||||
|
if (!liberal) {
|
||||||
for (var key in keywords) {
|
for (var key in keywords) {
|
||||||
if (!keywords[key].isQMLContextual && this.isContextual(key)) {
|
if (!keywords[key].isQMLContextual && this.isContextual(key)) {
|
||||||
this.unexpected();
|
this.unexpected();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
return this.parseIdent(liberal);
|
return this.parseIdent(liberal);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -452,7 +506,7 @@ var injectQML;
|
||||||
* Returns whether or not a given token type and name can be a QML Identifier.
|
* Returns whether or not a given token type and name can be a QML Identifier.
|
||||||
* Uses the 'isQMLContextual' boolean of 'keywords' to determine this.
|
* Uses the 'isQMLContextual' boolean of 'keywords' to determine this.
|
||||||
*/
|
*/
|
||||||
pp.qml_isIdent = function(type, name) {
|
pp.qml_isIdent = function (type, name) {
|
||||||
if (type === tt.name) {
|
if (type === tt.name) {
|
||||||
var key;
|
var key;
|
||||||
if (key = keywords[name]) {
|
if (key = keywords[name]) {
|
||||||
|
@ -467,7 +521,7 @@ var injectQML;
|
||||||
* Returns whether or not the current token is a QML primitive type and consumes
|
* Returns whether or not the current token is a QML primitive type and consumes
|
||||||
* it as a side effect if it is.
|
* it as a side effect if it is.
|
||||||
*/
|
*/
|
||||||
pp.qml_eatPrimitiveType = function(name) {
|
pp.qml_eatPrimitiveType = function (name) {
|
||||||
if (this.qml_isPrimitiveType(name)) {
|
if (this.qml_isPrimitiveType(name)) {
|
||||||
this.next();
|
this.next();
|
||||||
return true;
|
return true;
|
||||||
|
@ -478,7 +532,7 @@ var injectQML;
|
||||||
/*
|
/*
|
||||||
* Returns whether or not the current token is a QML primitive type.
|
* Returns whether or not the current token is a QML primitive type.
|
||||||
*/
|
*/
|
||||||
pp.qml_isPrimitiveType = function(name) {
|
pp.qml_isPrimitiveType = function (name) {
|
||||||
if (name === "var") {
|
if (name === "var") {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -490,29 +544,19 @@ var injectQML;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
acorn.plugins.qml = function(instance) {
|
acorn.plugins.qml = function (instance) {
|
||||||
|
|
||||||
// Extend acorn's 'parseTopLevel' method
|
// Extend acorn's 'parseTopLevel' method
|
||||||
instance.extend("parseTopLevel", function(nextMethod) {
|
instance.extend("parseTopLevel", function (nextMethod) {
|
||||||
return function(node) {
|
return function (node) {
|
||||||
// Most of QML's constructs sit at the top-level of the parse tree,
|
// Most of QML's constructs sit at the top-level of the parse tree,
|
||||||
// replacing JavaScripts top-level. Here we are parsing such things
|
// replacing JavaScripts top-level. Here we are parsing such things
|
||||||
// as the root object literal and header statements of QML. Eventually,
|
// as the root object literal and header statements of QML. Eventually,
|
||||||
// these rules will delegate down to JavaScript expressions.
|
// these rules will delegate down to JavaScript expressions.
|
||||||
if (!node.body) {
|
node.headerStatements = this.qml_parseHeaderStatements();
|
||||||
node.body = [];
|
node.rootObject = null;
|
||||||
}
|
|
||||||
|
|
||||||
var headerStmts = this.qml_parseHeaderStatements();
|
|
||||||
if (headerStmts !== undefined) {
|
|
||||||
node.body.push(headerStmts);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.type !== tt.eof) {
|
if (this.type !== tt.eof) {
|
||||||
var objRoot = this.qml_parseObjectLiteral();
|
node.rootObject = this.qml_parseObjectLiteral();
|
||||||
if (objRoot !== undefined) {
|
|
||||||
node.body.push(objRoot);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!this.eat(tt.eof)) {
|
if (!this.eat(tt.eof)) {
|
||||||
|
|
|
@ -6,6 +6,6 @@
|
||||||
"test": "node test/run.js"
|
"test": "node test/run.js"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"acorn": "^2.4.0"
|
"acorn": "^2.5.2"
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -75,7 +75,7 @@ outputStats("Total", total);
|
||||||
groupEnd();
|
groupEnd();
|
||||||
|
|
||||||
if (total.failed && typeof process === "object") {
|
if (total.failed && typeof process === "object") {
|
||||||
process.stdout.write("", function() {
|
process.stdout.write("", function () {
|
||||||
process.exit(1);
|
process.exit(1);
|
||||||
});
|
});
|
||||||
}
|
}
|
File diff suppressed because it is too large
Load diff
|
@ -10,67 +10,52 @@
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
(function(mod) {
|
(function (mod) {
|
||||||
if (typeof exports == "object" && typeof module == "object") // CommonJS
|
if (typeof exports == "object" && typeof module == "object") // CommonJS
|
||||||
return mod(require("acorn/walk"));
|
return mod(require("acorn/walk"));
|
||||||
if (typeof define == "function" && define.amd) // AMD
|
if (typeof define == "function" && define.amd) // AMD
|
||||||
return define([ "acorn/dist/walk" ], mod);
|
return define(["acorn/dist/walk"], mod);
|
||||||
mod(acorn.walk); // Plain browser env
|
mod(acorn.walk); // Plain browser env
|
||||||
})(function(walk) {
|
})(function (walk) {
|
||||||
function skipThrough(node, st, c) { c(node, st) }
|
function skipThrough(node, st, c) {
|
||||||
|
c(node, st)
|
||||||
|
}
|
||||||
|
|
||||||
function ignore(node, st, c) {}
|
function ignore(node, st, c) {}
|
||||||
|
|
||||||
var base = walk.base;
|
var base = walk.base;
|
||||||
base["Program"] = function(node, st, c) {
|
base["Program"] = function (node, st, c) {
|
||||||
for (var i = 0; i < node.body.length; i++) {
|
c(node.headerStatements, st);
|
||||||
var nodeBody = node.body[i];
|
c(node.rootObject, st, "QMLRootObject");
|
||||||
if (node.body[i].type === "QMLObjectLiteral") {
|
};
|
||||||
c(node.body[i], st, "QMLRootObject");
|
base["QMLHeaderStatements"] = function (node, st, c) {
|
||||||
} else {
|
|
||||||
c(node.body[i], st);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
base["QMLHeaderStatements"] = function(node, st, c) {
|
|
||||||
for (var i = 0; i < node.statements.length; i++) {
|
for (var i = 0; i < node.statements.length; i++) {
|
||||||
c(node.statements[i], st, "QMLHeaderStatement");
|
c(node.statements[i], st, "QMLHeaderStatement");
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
base["QMLHeaderStatement"] = skipThrough;
|
base["QMLHeaderStatement"] = skipThrough;
|
||||||
base["QMLImportStatement"] = function(node, st, c) {
|
base["QMLImportStatement"] = ignore;
|
||||||
c(node.module, st);
|
|
||||||
}
|
|
||||||
base["QMLModule"] = ignore;
|
|
||||||
base["QMLPragmaStatement"] = ignore;
|
base["QMLPragmaStatement"] = ignore;
|
||||||
base["QMLRootObject"] = skipThrough;
|
base["QMLRootObject"] = skipThrough;
|
||||||
base["QMLObjectLiteral"] = function(node, st, c) {
|
base["QMLObjectLiteral"] = function (node, st, c) {
|
||||||
c(node.block, st);
|
c(node.block, st);
|
||||||
}
|
};
|
||||||
base["QMLMemberBlock"] = function(node, st, c) {
|
base["QMLMemberBlock"] = function (node, st, c) {
|
||||||
for (var i = 0; i < node.members.length; i++) {
|
for (var i = 0; i < node.members.length; i++) {
|
||||||
c(node.members[i], st, "QMLMember");
|
c(node.members[i], st, "QMLMember");
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
base["QMLMember"] = skipThrough;
|
base["QMLMember"] = skipThrough;
|
||||||
base["QMLPropertyDeclaration"] = function(node, st, c) {
|
base["QMLPropertyDeclaration"] = function (node, st, c) {
|
||||||
c(node.identifier, st, "Pattern");
|
|
||||||
c(node.binding, st);
|
c(node.binding, st);
|
||||||
}
|
};
|
||||||
base["QMLSignalDefinition"] = ignore;
|
base["QMLSignalDefinition"] = ignore;
|
||||||
base["QMLProperty"] = function(node, st, c) {
|
base["QMLPropertyBinding"] = function (node, st, c) {
|
||||||
// c(node.qualifiedId, st)
|
|
||||||
c(node.binding, st);
|
c(node.binding, st);
|
||||||
}
|
};
|
||||||
base["QMLBinding"] = function(node, st, c) {
|
base["QMLStatementBlock"] = function (node, st, c) {
|
||||||
if (node.block) {
|
|
||||||
c(node.block, st);
|
|
||||||
} else {
|
|
||||||
c(node.expr, st, "Expression");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
base["QMLStatementBlock"] = function(node, st, c) {
|
|
||||||
for (var i = 0; i < node.statements.length; i++) {
|
for (var i = 0; i < node.statements.length; i++) {
|
||||||
c(node.statements[i], st, "Statement");
|
c(node.statements[i], st, "Statement");
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
})
|
})
|
Loading…
Add table
Reference in a new issue