diff --git a/qt/org.eclipse.cdt.qt.core.acorn/.classpath b/qt/org.eclipse.cdt.qt.core.acorn/.classpath new file mode 100644 index 00000000000..b862a296d38 --- /dev/null +++ b/qt/org.eclipse.cdt.qt.core.acorn/.classpath @@ -0,0 +1,7 @@ + + + + + + + diff --git a/qt/org.eclipse.cdt.qt.core.acorn/.project b/qt/org.eclipse.cdt.qt.core.acorn/.project new file mode 100644 index 00000000000..e8454d75162 --- /dev/null +++ b/qt/org.eclipse.cdt.qt.core.acorn/.project @@ -0,0 +1,28 @@ + + + org.eclipse.cdt.qt.core.acorn + + + + + + org.eclipse.jdt.core.javabuilder + + + + + org.eclipse.pde.ManifestBuilder + + + + + org.eclipse.pde.SchemaBuilder + + + + + + org.eclipse.pde.PluginNature + org.eclipse.jdt.core.javanature + + diff --git a/qt/org.eclipse.cdt.qt.core.acorn/.settings/org.eclipse.jdt.core.prefs b/qt/org.eclipse.cdt.qt.core.acorn/.settings/org.eclipse.jdt.core.prefs new file mode 100644 index 00000000000..295926d9641 --- /dev/null +++ b/qt/org.eclipse.cdt.qt.core.acorn/.settings/org.eclipse.jdt.core.prefs @@ -0,0 +1,7 @@ +eclipse.preferences.version=1 +org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled +org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.8 +org.eclipse.jdt.core.compiler.compliance=1.8 +org.eclipse.jdt.core.compiler.problem.assertIdentifier=error +org.eclipse.jdt.core.compiler.problem.enumIdentifier=error +org.eclipse.jdt.core.compiler.source=1.8 diff --git a/qt/org.eclipse.cdt.qt.core.acorn/META-INF/MANIFEST.MF b/qt/org.eclipse.cdt.qt.core.acorn/META-INF/MANIFEST.MF new file mode 100644 index 00000000000..0cb06697864 --- /dev/null +++ b/qt/org.eclipse.cdt.qt.core.acorn/META-INF/MANIFEST.MF @@ -0,0 +1,10 @@ +Manifest-Version: 1.0 +Bundle-ManifestVersion: 2 +Bundle-Name: %pluginName +Bundle-SymbolicName: org.eclipse.cdt.qt.core.acorn +Bundle-Version: 2.0.0.qualifier +Bundle-Activator: org.eclipse.cdt.qt.core.acorn.Activator +Bundle-Vendor: %providerName +Require-Bundle: org.eclipse.core.runtime +Bundle-RequiredExecutionEnvironment: JavaSE-1.8 +Bundle-ActivationPolicy: lazy diff --git a/qt/org.eclipse.cdt.qt.core.acorn/acorn-qml/.gitignore b/qt/org.eclipse.cdt.qt.core.acorn/acorn-qml/.gitignore new file mode 100644 index 00000000000..30bc1627986 --- /dev/null +++ b/qt/org.eclipse.cdt.qt.core.acorn/acorn-qml/.gitignore @@ -0,0 +1 @@ +/node_modules \ No newline at end of file diff --git a/qt/org.eclipse.cdt.qt.core.acorn/acorn-qml/bin/acorn-qml.js b/qt/org.eclipse.cdt.qt.core.acorn/acorn-qml/bin/acorn-qml.js new file mode 100644 index 00000000000..340dedd3269 --- /dev/null +++ b/qt/org.eclipse.cdt.qt.core.acorn/acorn-qml/bin/acorn-qml.js @@ -0,0 +1,81 @@ +#!/usr/bin/env node +/******************************************************************************* + * Copyright (c) 2015 QNX Software Systems and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * QNX Software Systems - Initial API and implementation + *******************************************************************************/ + +var path = require("path"); +var fs = require("fs"); +var acorn = require(".."); + +var infile, parsed, tokens, options = {}, silent = false, compact = false, tokenize = false; + +function help(status) { + var print = (status == 0) ? console.log : console.error; + print("usage: " + path.basename(process.argv[1]) + "[--ecma3|--ecma5|--ecma6]"); + print(" [--tokenize] [--locations] [---allow-hash-bang] [--compact] [--silent] [--module] [--help] [--] infile"); + process.exit(status); +} + +for (var i = 2; i < process.argv.length; ++i) { + var arg = process.argv[i]; + if (arg[0] != "-" && !infile) infile = arg; + else if (arg == "--" && !infile && i + 2 == process.argv.length) + infile = process.argv[++i]; + else if (arg == "--ecma3") + options.ecmaVersion = 3; + else if (arg == "--ecma5") + options.ecmaVersion = 5; + else if (arg == "--ecma6") + options.ecmaVersion = 6; + else if (arg == "--ecma7") + options.ecmaVersion = 7; + else if (arg == "--locations") + options.locations = true; + else if (arg == "--allow-hash-bang") + options.allowHashBang = true; + else if (arg == "--silent") + silent = true; + else if (arg == "--compact") + compact = true; + else if (arg == "--help") + help(0); + else if (arg == "--tokenize") + tokenize = true; + else if (arg == "--module") + options.sourceType = 'module'; + else + help(1); +} + +// Enable qml parser +options.plugins = { qml: true } + +try { + var code = fs.readFileSync(infile, "utf8"); + + if (!tokenize) + parsed = acorn.parse(code, options); + else { + var get = acorn.tokenize(code, options); + tokens = []; + while (true) { + var token = get(); + tokens.push(token); + if (token.type.type == "eof") + break; + } + } +} catch(e) { + console.log(e.message); + process.exit(1); +} + +if (!silent) + console.log(JSON.stringify(tokenize ? tokens : parsed, null, compact ? null : 2)); diff --git a/qt/org.eclipse.cdt.qt.core.acorn/acorn-qml/index.js b/qt/org.eclipse.cdt.qt.core.acorn/acorn-qml/index.js new file mode 100644 index 00000000000..282e1f47250 --- /dev/null +++ b/qt/org.eclipse.cdt.qt.core.acorn/acorn-qml/index.js @@ -0,0 +1,13 @@ +/******************************************************************************* + * Copyright (c) 2015 QNX Software Systems and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * QNX Software Systems - Initial API and implementation + *******************************************************************************/ +'use strict'; + +module.exports = require('./inject')(require('acorn')); \ No newline at end of file diff --git a/qt/org.eclipse.cdt.qt.core.acorn/acorn-qml/inject.js b/qt/org.eclipse.cdt.qt.core.acorn/acorn-qml/inject.js new file mode 100644 index 00000000000..5583131462e --- /dev/null +++ b/qt/org.eclipse.cdt.qt.core.acorn/acorn-qml/inject.js @@ -0,0 +1,185 @@ +/******************************************************************************* + * Copyright (c) 2015 QNX Software Systems and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * QNX Software Systems - Initial API and implementation + *******************************************************************************/ +'use strict'; + +module.exports = function(acorn) { + + // Define QML token types + var tt = acorn.tokTypes; + + /* + * Shorthand for defining keywords in tt (acorn.tokTypes). Creates a new key in + * tt with the label _. + */ + function kw(name, options) { + if (options === undefined) + options = {}; + options.keyword = name; + tt["_" + name] = new acorn.TokenType(name, options); + } + + // Define QML keywords + kw("property"); + kw("readonly"); + kw("color"); + kw("pragma"); + kw("as"); + + // Define QML token contexts + var tc = acorn.tokContexts; + + // TODO: Add QML contexts (one such example is so we can parse keywords as identifiers) + + // QML parser methods + var pp = acorn.Parser.prototype; + + pp.qml_parseHeaderStatements = function() { + var node = this.startNode() + node.statements = []; + + loop: { + switch (this.type) { + case tt._import: + var qmlImport = this.qml_parseImportStatement(); + node.statements.push(qmlImport); + break loop; + case tt._pragma: + // TODO: parse QML pragma statement + } + } + + return this.finishNode(node, "QMLHeaderStatements") + } + + pp.qml_parseImportStatement = function() { + var node = this.startNode(); + + // Advance to the next token since this method should only be called + // when the current token is 'import' + this.next(); + node.module = this.qml_parseModuleIdentifier(); + // TODO: parse the 'as Identifier' portion of an import statement + this.semicolon(); + return this.finishNode(node, "QMLImportStatement"); + }; + + pp.qml_parseModuleIdentifier = function() { + var node = this.startNode(); + + // Parse the qualified id/string + if (this.type == tt.name) + node.qualifiedId = this.qml_parseQualifiedId(); + else if (this.type == tt.string) { + node.file = this.value; + this.next(); + } else + this.unexpected(); + + // Parse the version number + if (this.type == tt.num) { + node.version = this.parseLiteral(this.value); + // TODO: check that version number has major and minor + } else + this.unexpected(); + return this.finishNode(node, "QMLModuleIdentifier"); + }; + + pp.qml_parseQualifiedId = function() { + var id = this.value; + this.next(); + while(this.type == tt.dot) { + id += '.'; + this.next(); + if (this.type == tt.name) + id += this.value; + else + this.unexpected(); + this.next(); + } + return id; + } + + /* + * Returns a TokenType that matches the given word or undefined if + * no such TokenType could be found. + * + * Uses contextual information to determine whether or not a keyword + * such as 'color' is being used as an identifier. If this is found + * to be the case, tt.name is returned. + */ + pp.qml_getTokenType = function(word) { + // TODO: use context to determine if this is an identifier or + // a keyword (color, real, etc. can be used as identifiers) + switch(word) { + case "property": + return tt._property; + case "readonly": + return tt._readonly; + case "import": + // Make sure that 'import' is recognized as a keyword + // regardless of ecma version set in acorn. + return tt._import; + case "color": + return tt._color; + case "pragma": + return tt._pragma; + case "as": + return tt._as; + } + return undefined; + } + + acorn.plugins.qml = function(instance) { + + // Extend acorn's 'parseTopLevel' method + instance.extend("parseTopLevel", function(nextMethod) { + return function(node) { + // Most of QML's constructs sit at the top-level of the parse tree, + // replacing JavaScripts top-level. Here we are parsing such things + // as the root object literal and header statements of QML. Eventually, + // these rules will delegate down to JavaScript expressions. + if (!node.body) + node.body = []; + + var headerStmts = this.qml_parseHeaderStatements(); + node.body.push(headerStmts) + + // TODO: Parse QML object root + + // TODO: don't call acorn's parseTopLevel method once the above are working + return nextMethod.call(this, node); + }; + }); + + // Extend acorn's 'readWord' method + instance.extend("readWord", function(nextMethod) { + return function() { + // Parse a word and attempt to match it to a QML keyword + var word = this.readWord1(); + var type = this.qml_getTokenType(word); + + if (type !== undefined) + return this.finishToken(type, word); + + // If we were unable to find a QML keyword, call acorn's implementation + // of the readWord method. Since we don't have access to _tokentype, and + // subsequently _tokentype.keywords, we can't look for JavaScript keyword + // matches ourselves. This is unfortunate because we have to move the parser + // backwards and let readWord call readWord1 a second time for every word + // that is not a QML keyword. + this.pos -= word.length; + return nextMethod.call(this); + }; + }); + } + + return acorn +}; \ No newline at end of file diff --git a/qt/org.eclipse.cdt.qt.core.acorn/acorn-qml/package.json b/qt/org.eclipse.cdt.qt.core.acorn/acorn-qml/package.json new file mode 100644 index 00000000000..b1472b23fc3 --- /dev/null +++ b/qt/org.eclipse.cdt.qt.core.acorn/acorn-qml/package.json @@ -0,0 +1,11 @@ +{ + "name": "acorn-qml", + "description": "QML Parser", + "version": "2.4.0", + "scripts": { + "test": "node test/run.js" + }, + "dependencies": { + "acorn": "^2.4.0" + } +} \ No newline at end of file diff --git a/qt/org.eclipse.cdt.qt.core.acorn/acorn-qml/test/driver.js b/qt/org.eclipse.cdt.qt.core.acorn/acorn-qml/test/driver.js new file mode 100644 index 00000000000..cb253cc0096 --- /dev/null +++ b/qt/org.eclipse.cdt.qt.core.acorn/acorn-qml/test/driver.js @@ -0,0 +1,118 @@ +/******************************************************************************* + * Copyright (c) 2015 QNX Software Systems and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * QNX Software Systems - Initial API and implementation + *******************************************************************************/ + +var tests = []; + +exports.test = function(code, ast, options) { + tests.push({code: code, ast: ast, options: options}); +}; +exports.testFail = function(code, message, options) { + tests.push({code: code, error: message, options: options}); +}; +exports.testAssert = function(code, assert, options) { + tests.push({code: code, assert: assert, options: options}); +}; + +exports.runTests = function(config, callback) { + var parse = config.parse; + + for (var i = 0; i < tests.length; ++i) { + var test = tests[i]; + if (config.filter && !config.filter(test)) continue; + try { + var testOpts = test.options || {locations: true}; + var expected = {}; + if (expected.onComment = testOpts.onComment) { + testOpts.onComment = [] + } + if (expected.onToken = testOpts.onToken) { + testOpts.onToken = []; + } + testOpts.plugins = { qml: true }; + var ast = parse(test.code, testOpts); + if (test.error) { + if (config.loose) { + callback("ok", test.code); + } else { + callback("fail", test.code, "Expected error message: " + test.error + "\nBut parsing succeeded."); + } + } + else if (test.assert) { + var error = test.assert(ast); + if (error) callback("fail", test.code, + "\n Assertion failed:\n " + error); + else callback("ok", test.code); + } else { + var mis = misMatch(test.ast, ast); + for (var name in expected) { + if (mis) break; + if (expected[name]) { + mis = misMatch(expected[name], testOpts[name]); + testOpts[name] = expected[name]; + } + } + if (mis) callback("fail", test.code, mis); + else callback("ok", test.code); + } + } catch(e) { + if (!(e instanceof SyntaxError)) { + throw e; + } + if (test.error) { + if (e.message == test.error) callback("ok", test.code); + else callback("fail", test.code, + "Expected error message: " + test.error + "\nGot error message: " + e.message); + } else { + callback("error", test.code, e.stack || e.toString()); + } + } + } +}; + +function ppJSON(v) { return v instanceof RegExp ? v.toString() : JSON.stringify(v, null, 2); } +function addPath(str, pt) { + if (str.charAt(str.length-1) == ")") + return str.slice(0, str.length-1) + "/" + pt + ")"; + return str + " (" + pt + ")"; +} + +var misMatch = exports.misMatch = function(exp, act) { + if (!exp || !act || (typeof exp != "object") || (typeof act != "object")) { + if (exp !== act) return ppJSON(exp) + " !== " + ppJSON(act); + } else if (exp instanceof RegExp || act instanceof RegExp) { + var left = ppJSON(exp), right = ppJSON(act); + if (left !== right) return left + " !== " + right; + } else if (exp.splice) { + if (!act.slice) return ppJSON(exp) + " != " + ppJSON(act); + if (act.length != exp.length) return "array length mismatch " + exp.length + " != " + act.length; + for (var i = 0; i < act.length; ++i) { + var mis = misMatch(exp[i], act[i]); + if (mis) return addPath(mis, i); + } + } else { + for (var prop in exp) { + var mis = misMatch(exp[prop], act[prop]); + if (mis) return addPath(mis, prop); + } + } +}; + +function mangle(ast) { + if (typeof ast != "object" || !ast) return; + if (ast.slice) { + for (var i = 0; i < ast.length; ++i) mangle(ast[i]); + } else { + var loc = ast.start && ast.end && {start: ast.start, end: ast.end}; + if (loc) { delete ast.start; delete ast.end; } + for (var name in ast) if (ast.hasOwnProperty(name)) mangle(ast[name]); + if (loc) ast.loc = loc; + } +} \ No newline at end of file diff --git a/qt/org.eclipse.cdt.qt.core.acorn/acorn-qml/test/run.js b/qt/org.eclipse.cdt.qt.core.acorn/acorn-qml/test/run.js new file mode 100644 index 00000000000..faa6b00112c --- /dev/null +++ b/qt/org.eclipse.cdt.qt.core.acorn/acorn-qml/test/run.js @@ -0,0 +1,81 @@ +/******************************************************************************* + * Copyright (c) 2015 QNX Software Systems and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * QNX Software Systems - Initial API and implementation + *******************************************************************************/ + +var driver = require("./driver.js"); +require("./tests-qml.js"); + +function group(name) { + if (typeof console === "object" && console.group) { + console.group(name); + } +} + +function groupEnd() { + if (typeof console === "object" && console.groupEnd) { + console.groupEnd(name); + } +} + +function log(title, message) { + if (typeof console === "object") console.log(title, message); +} + +var stats, modes = { + Normal: { + config: { + parse: require("..").parse + } + } +}; + +function report(state, code, message) { + if (state != "ok") {++stats.failed; log(code, message);} + ++stats.testsRun; +} + +group("Errors"); + +for (var name in modes) { + group(name); + var mode = modes[name]; + stats = mode.stats = {testsRun: 0, failed: 0}; + var t0 = +new Date; + driver.runTests(mode.config, report); + mode.stats.duration = +new Date - t0; + groupEnd(); +} + +groupEnd(); + +function outputStats(name, stats) { + log(name + ":", stats.testsRun + " tests run in " + stats.duration + "ms; " + + (stats.failed ? stats.failed + " failures." : "all passed.")); +} + +var total = {testsRun: 0, failed: 0, duration: 0}; + +group("Stats"); + +for (var name in modes) { + var stats = modes[name].stats; + outputStats(name + " parser", stats); + for (var key in stats) total[key] += stats[key]; +} + +outputStats("Total", total); + +groupEnd(); + +if (total.failed && typeof process === "object") { + process.stdout.write("", function() { + process.exit(1); + }); +} \ No newline at end of file diff --git a/qt/org.eclipse.cdt.qt.core.acorn/acorn-qml/test/tests-qml.js b/qt/org.eclipse.cdt.qt.core.acorn/acorn-qml/test/tests-qml.js new file mode 100644 index 00000000000..6cd9aa6fa52 --- /dev/null +++ b/qt/org.eclipse.cdt.qt.core.acorn/acorn-qml/test/tests-qml.js @@ -0,0 +1,72 @@ +/******************************************************************************* + * Copyright (c) 2015 QNX Software Systems and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * QNX Software Systems - Initial API and implementation + *******************************************************************************/ + +var testFixture = { + 'QML': { + 'import QtQuick 2.2': { + type: "QMLHeaderStatements", + range: [0, 18], + loc: { + start: { line: 1, column: 0 }, + end: { line: 1, column: 18 } + }, + statements: [ + { + type: "QMLImportStatement", + range: [0, 18], + loc: { + start: { line: 1, column: 0 }, + end: { line: 1, column: 18 } + }, + module: { + type: "QMLModuleIdentifier", + qualifiedId: "QtQuick", + range: [7, 18], + loc: { + start: { line: 1, column: 7 }, + end: { line: 1, column: 18 } + }, + version: { + type: "Literal", + range: [15, 18], + loc: { + start: { line: 1, column: 15 }, + end: { line: 1, column: 18 } + }, + value: 2.2, + raw: "2.2" + } + } + } + ] + } + } +}; + +if (typeof exports !== "undefined") { + var test = require("./driver.js").test; + var testFail = require("./driver.js").testFail; + var tokTypes = require("../").tokTypes; +} + +for (var ns in testFixture) { + ns = testFixture[ns]; + for (var code in ns) { + test(code, { + type: 'Program', + body: [ns[code]] + }, { + ecmaVersion: 6, + locations: true, + ranges: true + }); + } +} \ No newline at end of file diff --git a/qt/org.eclipse.cdt.qt.core.acorn/build.properties b/qt/org.eclipse.cdt.qt.core.acorn/build.properties new file mode 100644 index 00000000000..41eb6ade2b4 --- /dev/null +++ b/qt/org.eclipse.cdt.qt.core.acorn/build.properties @@ -0,0 +1,4 @@ +source.. = src/ +output.. = bin/ +bin.includes = META-INF/,\ + . diff --git a/qt/org.eclipse.cdt.qt.core.acorn/plugin.properties b/qt/org.eclipse.cdt.qt.core.acorn/plugin.properties new file mode 100644 index 00000000000..7ed20d4606f --- /dev/null +++ b/qt/org.eclipse.cdt.qt.core.acorn/plugin.properties @@ -0,0 +1,8 @@ +# Copyright (c) 2013 QNX Software Systems and others. +# All rights reserved. This program and the accompanying materials +# are made available under the terms of the Eclipse Public License v1.0 +# which accompanies this distribution, and is available at +# http://www.eclipse.org/legal/epl-v10.html + +pluginName=C/C++ Qt Acorn QML Parser +providerName=Eclipse CDT diff --git a/qt/org.eclipse.cdt.qt.core.acorn/pom.xml b/qt/org.eclipse.cdt.qt.core.acorn/pom.xml new file mode 100644 index 00000000000..8bdb21a8da6 --- /dev/null +++ b/qt/org.eclipse.cdt.qt.core.acorn/pom.xml @@ -0,0 +1,51 @@ + + + 4.0.0 + + + org.eclipse.cdt + cdt-parent + 8.8.0-SNAPSHOT + ../../pom.xml + + + 2.0.0-SNAPSHOT + org.eclipse.cdt.qt.core.acorn + eclipse-plugin + + + + + maven-antrun-plugin + + + generate-parsers + generate-sources + + run + + + + + + + + + clean-parsers + clean + + run + + + + + + + + + + + + diff --git a/qt/org.eclipse.cdt.qt.core.acorn/src/org/eclipse/cdt/qt/core/acorn/Activator.java b/qt/org.eclipse.cdt.qt.core.acorn/src/org/eclipse/cdt/qt/core/acorn/Activator.java new file mode 100644 index 00000000000..e4f60a0bc89 --- /dev/null +++ b/qt/org.eclipse.cdt.qt.core.acorn/src/org/eclipse/cdt/qt/core/acorn/Activator.java @@ -0,0 +1,40 @@ +/******************************************************************************* + * Copyright (c) 2015 QNX Software Systems and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * QNX Software Systems - Initial API and implementation + *******************************************************************************/ +package org.eclipse.cdt.qt.core.acorn; + +import org.osgi.framework.BundleActivator; +import org.osgi.framework.BundleContext; + +public class Activator implements BundleActivator { + + private static BundleContext context; + + static BundleContext getContext() { + return context; + } + + /* + * (non-Javadoc) + * @see org.osgi.framework.BundleActivator#start(org.osgi.framework.BundleContext) + */ + public void start(BundleContext bundleContext) throws Exception { + Activator.context = bundleContext; + } + + /* + * (non-Javadoc) + * @see org.osgi.framework.BundleActivator#stop(org.osgi.framework.BundleContext) + */ + public void stop(BundleContext bundleContext) throws Exception { + Activator.context = null; + } + +}