1
0
Fork 0
mirror of https://github.com/eclipse-cdt/cdt synced 2025-07-19 15:05:36 +02:00

Bug 481126 - QML Directory File Parsing

Added a new parser that is able to construct an AST for a given qmldir
file.  Added tests to ensure the parser works for standard qmldir files.

Change-Id: I292aace3cdec8b4a544033f80812df965fef50b8
Signed-off-by: Matthew Bastien <mbastien@blackberry.com>
This commit is contained in:
Matthew Bastien 2015-12-09 14:57:25 -05:00
parent c5d03dddb6
commit 1d36f36ef8
40 changed files with 2434 additions and 1 deletions

View file

@ -0,0 +1,287 @@
/*******************************************************************************
* 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.tests;
import static org.junit.Assert.*;
import java.io.ByteArrayInputStream;
import java.io.InputStream;
import org.eclipse.cdt.internal.qt.core.location.Position;
import org.eclipse.cdt.qt.core.qmldir.QMLDirectoryLexer;
import org.eclipse.cdt.qt.core.qmldir.QMLDirectoryLexer.Token;
import org.eclipse.cdt.qt.core.qmldir.QMLDirectoryLexer.TokenType;
import org.junit.Test;
@SuppressWarnings("nls")
public class QMLDirectoryLexerTests {
private void assertToken(TokenType type, String text, int start, int end, Position locStart, Position locEnd, Token actual) {
// Check token type and text
assertEquals("Unexpected token type", type, actual.getType());
assertEquals("Unexpected token text", text, actual.getText());
// Check position offsets
assertEquals("Unexpected start position", start, actual.getStart());
assertEquals("Unexpected end position", end, actual.getEnd());
// Check SourceLocation start
assertEquals("Unexpected location start line", locStart.getLine(), actual.getLocation().getStart().getLine());
assertEquals("Unexpected location start column", locStart.getColumn(), actual.getLocation().getStart().getColumn());
// Check SourceLocation end
assertEquals("Unexpected location end line", locEnd.getLine(), actual.getLocation().getEnd().getLine());
assertEquals("Unexpected location end column", locEnd.getColumn(), actual.getLocation().getEnd().getColumn());
}
private InputStream createInputStream(String s) {
return new ByteArrayInputStream(s.getBytes());
}
@Test
public void testCommentToken() {
QMLDirectoryLexer lexer = new QMLDirectoryLexer();
lexer.setInput(new ByteArrayInputStream("# This is a comment".getBytes()));
assertToken(TokenType.COMMENT,
"# This is a comment",
0, 19,
new Position(1, 0), new Position(1, 19),
lexer.nextToken(false));
}
@Test
public void testMultipleCommentTokens() {
QMLDirectoryLexer lexer = new QMLDirectoryLexer();
lexer.setInput(createInputStream("# This is a comment\n# This is another comment\n"));
assertToken(TokenType.COMMENT,
"# This is a comment",
0, 19,
new Position(1, 0), new Position(1, 19),
lexer.nextToken(false));
assertEquals(TokenType.COMMAND_END, lexer.nextToken(false).getType());
assertToken(TokenType.COMMENT,
"# This is another comment",
20, 45,
new Position(2, 0), new Position(2, 25),
lexer.nextToken(false));
assertEquals(TokenType.COMMAND_END, lexer.nextToken(false).getType());
assertEquals(TokenType.EOF, lexer.nextToken(false).getType());
}
@Test
public void testModuleToken() {
QMLDirectoryLexer lexer = new QMLDirectoryLexer();
lexer.setInput(createInputStream("module"));
assertToken(TokenType.MODULE,
"module",
0, 6,
new Position(1, 0), new Position(1, 6),
lexer.nextToken());
}
@Test
public void testTypeInfoToken() {
QMLDirectoryLexer lexer = new QMLDirectoryLexer();
lexer.setInput(createInputStream("typeinfo"));
assertToken(TokenType.TYPEINFO,
"typeinfo",
0, 8,
new Position(1, 0), new Position(1, 8),
lexer.nextToken());
}
@Test
public void testSingletonToken() {
QMLDirectoryLexer lexer = new QMLDirectoryLexer();
lexer.setInput(createInputStream("singleton"));
assertToken(TokenType.SINGLETON,
"singleton",
0, 9,
new Position(1, 0), new Position(1, 9),
lexer.nextToken());
}
@Test
public void testInternalToken() {
QMLDirectoryLexer lexer = new QMLDirectoryLexer();
lexer.setInput(createInputStream("internal"));
assertToken(TokenType.INTERNAL,
"internal",
0, 8,
new Position(1, 0), new Position(1, 8),
lexer.nextToken());
}
@Test
public void testPluginToken() {
QMLDirectoryLexer lexer = new QMLDirectoryLexer();
lexer.setInput(createInputStream("plugin"));
assertToken(TokenType.PLUGIN,
"plugin",
0, 6,
new Position(1, 0), new Position(1, 6),
lexer.nextToken());
}
@Test
public void testClassnameToken() {
QMLDirectoryLexer lexer = new QMLDirectoryLexer();
lexer.setInput(createInputStream("classname"));
assertToken(TokenType.CLASSNAME,
"classname",
0, 9,
new Position(1, 0), new Position(1, 9),
lexer.nextToken());
}
@Test
public void testDependsToken() {
QMLDirectoryLexer lexer = new QMLDirectoryLexer();
lexer.setInput(createInputStream("depends"));
assertToken(TokenType.DEPENDS,
"depends",
0, 7,
new Position(1, 0), new Position(1, 7),
lexer.nextToken());
}
@Test
public void testDesignerSupportedToken() {
QMLDirectoryLexer lexer = new QMLDirectoryLexer();
lexer.setInput(createInputStream("designersupported"));
assertToken(TokenType.DESIGNERSUPPORTED,
"designersupported",
0, 17,
new Position(1, 0), new Position(1, 17),
lexer.nextToken());
}
@Test
public void testWordToken() {
QMLDirectoryLexer lexer = new QMLDirectoryLexer();
lexer.setInput(createInputStream("QtQuick.Control"));
assertToken(TokenType.WORD,
"QtQuick.Control",
0, 15,
new Position(1, 0), new Position(1, 15),
lexer.nextToken());
}
@Test
public void testWordTokenContainsKeyword() {
QMLDirectoryLexer lexer = new QMLDirectoryLexer();
lexer.setInput(createInputStream("plugins.test"));
assertToken(TokenType.WORD,
"plugins.test",
0, 12,
new Position(1, 0), new Position(1, 12),
lexer.nextToken());
}
@Test
public void testWordTokenAsRelativePath() {
QMLDirectoryLexer lexer = new QMLDirectoryLexer();
lexer.setInput(createInputStream("./test/something/"));
assertToken(TokenType.WORD,
"./test/something/",
0, 17,
new Position(1, 0), new Position(1, 17),
lexer.nextToken());
}
@Test
public void testWordTokenAsAbsoluteWindowsPath() {
QMLDirectoryLexer lexer = new QMLDirectoryLexer();
lexer.setInput(createInputStream("C:\\Users\\someone\\test\\something\\"));
assertToken(TokenType.WORD,
"C:\\Users\\someone\\test\\something\\",
0, 32,
new Position(1, 0), new Position(1, 32),
lexer.nextToken());
}
@Test
public void testWordTokenAsAbsoluteUnixPath() {
QMLDirectoryLexer lexer = new QMLDirectoryLexer();
lexer.setInput(createInputStream("/usr/local/test/something/"));
assertToken(TokenType.WORD,
"/usr/local/test/something/",
0, 26,
new Position(1, 0), new Position(1, 26),
lexer.nextToken());
}
@Test
public void testDecimalToken() {
QMLDirectoryLexer lexer = new QMLDirectoryLexer();
lexer.setInput(createInputStream("2.3"));
assertToken(TokenType.DECIMAL,
"2.3",
0, 3,
new Position(1, 0), new Position(1, 3),
lexer.nextToken());
}
@Test
public void testIntegerToken() {
QMLDirectoryLexer lexer = new QMLDirectoryLexer();
lexer.setInput(createInputStream("3"));
assertToken(TokenType.INTEGER,
"3",
0, 1,
new Position(1, 0), new Position(1, 1),
lexer.nextToken());
}
@Test
public void testWhitespaceToken() {
QMLDirectoryLexer lexer = new QMLDirectoryLexer();
lexer.setInput(createInputStream(" \t\n"));
assertToken(TokenType.WHITESPACE,
" \t",
0, 2,
new Position(1, 0), new Position(1, 2),
lexer.nextToken(false));
}
@Test
public void testCommandEndToken() {
QMLDirectoryLexer lexer = new QMLDirectoryLexer();
lexer.setInput(createInputStream("\n"));
assertToken(TokenType.COMMAND_END,
"\\n",
0, 1,
new Position(1, 0), new Position(1, 1),
lexer.nextToken());
}
@Test
public void testEOFToken() {
QMLDirectoryLexer lexer = new QMLDirectoryLexer();
lexer.setInput(createInputStream(""));
assertToken(TokenType.EOF,
"",
0, 0,
new Position(1, 0), new Position(1, 0),
lexer.nextToken());
}
@Test
public void testEOFTokenAfterCommand() {
QMLDirectoryLexer lexer = new QMLDirectoryLexer();
lexer.setInput(createInputStream("\n"));
lexer.nextToken();
assertToken(TokenType.EOF,
"",
1, 1,
new Position(2, 0), new Position(2, 0),
lexer.nextToken());
}
}

View file

@ -0,0 +1,308 @@
/*******************************************************************************
* 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.tests;
import static org.hamcrest.CoreMatchers.*;
import static org.junit.Assert.*;
import java.io.ByteArrayInputStream;
import java.io.InputStream;
import org.eclipse.cdt.internal.qt.core.location.Position;
import org.eclipse.cdt.qt.core.location.IPosition;
import org.eclipse.cdt.qt.core.qmldir.IQDirAST;
import org.eclipse.cdt.qt.core.qmldir.IQDirASTNode;
import org.eclipse.cdt.qt.core.qmldir.IQDirClassnameCommand;
import org.eclipse.cdt.qt.core.qmldir.IQDirCommentCommand;
import org.eclipse.cdt.qt.core.qmldir.IQDirDependsCommand;
import org.eclipse.cdt.qt.core.qmldir.IQDirDesignerSupportedCommand;
import org.eclipse.cdt.qt.core.qmldir.IQDirInternalCommand;
import org.eclipse.cdt.qt.core.qmldir.IQDirModuleCommand;
import org.eclipse.cdt.qt.core.qmldir.IQDirPluginCommand;
import org.eclipse.cdt.qt.core.qmldir.IQDirResourceCommand;
import org.eclipse.cdt.qt.core.qmldir.IQDirSingletonCommand;
import org.eclipse.cdt.qt.core.qmldir.IQDirSyntaxError;
import org.eclipse.cdt.qt.core.qmldir.IQDirTypeInfoCommand;
import org.eclipse.cdt.qt.core.qmldir.QMLDirectoryParser;
import org.eclipse.cdt.qt.core.qmldir.QMLDirectoryParser.SyntaxError;
import org.junit.Test;
@SuppressWarnings("nls")
public class QMLDirectoryParserTests {
public void assertLocation(int start, int end, IPosition locStart, IPosition locEnd, IQDirASTNode node) {
// Check position offsets
assertEquals("Unexpected start position", start, node.getStart());
assertEquals("Unexpected end position", end, node.getEnd());
// Check SourceLocation start
assertEquals("Unexpected location start line", locStart.getLine(), node.getLocation().getStart().getLine());
assertEquals("Unexpected location start column", locStart.getColumn(), node.getLocation().getStart().getColumn());
}
private InputStream createInputStream(String s) {
return new ByteArrayInputStream(s.getBytes());
}
@Test
public void testModuleCommand() {
QMLDirectoryParser parser = new QMLDirectoryParser();
IQDirAST ast = parser.parse(createInputStream("module QtQuick.Controls\n"), false);
assertEquals("Unexpected command list size", 1, ast.getCommands().size());
assertThat("Unexpected command", ast.getCommands().get(0), instanceOf(IQDirModuleCommand.class));
IQDirModuleCommand mod = (IQDirModuleCommand) ast.getCommands().get(0);
assertEquals("Unexpected qualified ID", "QtQuick.Controls", mod.getModuleIdentifier().getText());
assertLocation(0, 24, new Position(1, 0), new Position(1, 24), mod);
}
@Test
public void testModuleNoIdentifier() {
try {
QMLDirectoryParser parser = new QMLDirectoryParser();
parser.parse(createInputStream("module\n"), false);
fail("Parser did not throw SyntaxError");
} catch (SyntaxError e) {
assertEquals("Unexpected token '\\n' (1:6)", e.getMessage());
}
}
@Test
public void testSingletonCommand() {
QMLDirectoryParser parser = new QMLDirectoryParser();
IQDirAST ast = parser.parse(createInputStream("singleton Singleton 2.3 Singleton.qml\n"), false);
assertEquals("Unexpected command list size", 1, ast.getCommands().size());
assertThat("Unexpected command", ast.getCommands().get(0), instanceOf(IQDirSingletonCommand.class));
IQDirSingletonCommand singleton = (IQDirSingletonCommand) ast.getCommands().get(0);
assertEquals("Unexpected type name", "Singleton", singleton.getTypeName().getText());
assertEquals("Unexpected initial version", "2.3", singleton.getInitialVersion().getVersionString());
assertEquals("Unexpected file name", "Singleton.qml", singleton.getFile().getText());
assertLocation(0, 38, new Position(1, 0), new Position(1, 38), singleton);
}
@Test
public void testInvalidVersionNumber() {
try {
QMLDirectoryParser parser = new QMLDirectoryParser();
parser.parse(createInputStream("singleton Singleton 2 Singleton.qml\n"), false);
fail("Parser did not throw SyntaxError");
} catch (SyntaxError e) {
assertEquals("Unexpected token '2' (1:20)", e.getMessage());
}
}
@Test
public void testInternalCommand() {
QMLDirectoryParser parser = new QMLDirectoryParser();
IQDirAST ast = parser.parse(createInputStream("internal MyPrivateType MyPrivateType.qml\n"), false);
assertEquals("Unexpected command list size", 1, ast.getCommands().size());
assertThat("Unexpected command", ast.getCommands().get(0), instanceOf(IQDirInternalCommand.class));
IQDirInternalCommand internal = (IQDirInternalCommand) ast.getCommands().get(0);
assertEquals("Unexpected type name", "MyPrivateType", internal.getTypeName().getText());
assertEquals("Unexpected file name", "MyPrivateType.qml", internal.getFile().getText());
assertLocation(0, 41, new Position(1, 0), new Position(1, 41), internal);
}
@Test
public void testResourceCommand() {
QMLDirectoryParser parser = new QMLDirectoryParser();
IQDirAST ast = parser.parse(createInputStream("MyScript 1.0 MyScript.qml\n"), false);
assertEquals("Unexpected command list size", 1, ast.getCommands().size());
assertThat("Unexpected command", ast.getCommands().get(0), instanceOf(IQDirResourceCommand.class));
IQDirResourceCommand resource = (IQDirResourceCommand) ast.getCommands().get(0);
assertEquals("Unexpected type name", "MyScript", resource.getResourceIdentifier().getText());
assertEquals("Unexpected initial version", "1.0", resource.getInitialVersion().getVersionString());
assertEquals("Unexpected file name", "MyScript.qml", resource.getFile().getText());
assertLocation(0, 26, new Position(1, 0), new Position(1, 26), resource);
}
@Test
public void testPluginCommand() {
QMLDirectoryParser parser = new QMLDirectoryParser();
IQDirAST ast = parser.parse(createInputStream("plugin MyPluginLibrary\n"), false);
assertEquals("Unexpected command list size", 1, ast.getCommands().size());
assertThat("Unexpected command", ast.getCommands().get(0), instanceOf(IQDirPluginCommand.class));
IQDirPluginCommand plugin = (IQDirPluginCommand) ast.getCommands().get(0);
assertEquals("Unexpected identifier", "MyPluginLibrary", plugin.getName().getText());
assertEquals("Unexpected path", null, plugin.getPath());
assertLocation(0, 23, new Position(1, 0), new Position(1, 23), plugin);
}
@Test
public void testPluginCommandWithPath() {
QMLDirectoryParser parser = new QMLDirectoryParser();
IQDirAST ast = parser.parse(createInputStream("plugin MyPluginLibrary ./lib/\n"), false);
assertEquals("Unexpected command list size", 1, ast.getCommands().size());
assertThat("Unexpected command", ast.getCommands().get(0), instanceOf(IQDirPluginCommand.class));
IQDirPluginCommand plugin = (IQDirPluginCommand) ast.getCommands().get(0);
assertEquals("Unexpected identifier", "MyPluginLibrary", plugin.getName().getText());
assertEquals("Unexpected path", "./lib/", plugin.getPath().getText());
assertLocation(0, 30, new Position(1, 0), new Position(1, 30), plugin);
}
@Test
public void testClassnameCommand() {
QMLDirectoryParser parser = new QMLDirectoryParser();
IQDirAST ast = parser.parse(createInputStream("classname MyClass\n"), false);
assertEquals("Unexpected command list size", 1, ast.getCommands().size());
assertThat("Unexpected command", ast.getCommands().get(0), instanceOf(IQDirClassnameCommand.class));
IQDirClassnameCommand classname = (IQDirClassnameCommand) ast.getCommands().get(0);
assertEquals("Unexpected class name", "MyClass", classname.getIdentifier().getText());
assertLocation(0, 18, new Position(1, 0), new Position(1, 18), classname);
}
@Test
public void testTypeInfoCommand() {
QMLDirectoryParser parser = new QMLDirectoryParser();
IQDirAST ast = parser.parse(createInputStream("typeinfo mymodule.qmltypes\n"), false);
assertEquals("Unexpected command list size", 1, ast.getCommands().size());
assertThat("Unexpected command", ast.getCommands().get(0), instanceOf(IQDirTypeInfoCommand.class));
IQDirTypeInfoCommand typeinfo = (IQDirTypeInfoCommand) ast.getCommands().get(0);
assertEquals("Unexpected file name", "mymodule.qmltypes", typeinfo.getFile().getText());
assertLocation(0, 27, new Position(1, 0), new Position(1, 27), typeinfo);
}
@Test
public void testDependsCommand() {
QMLDirectoryParser parser = new QMLDirectoryParser();
IQDirAST ast = parser.parse(createInputStream("depends MyOtherModule 1.0\n"), false);
assertEquals("Unexpected command list size", 1, ast.getCommands().size());
assertThat("Unexpected command", ast.getCommands().get(0), instanceOf(IQDirDependsCommand.class));
IQDirDependsCommand depends = (IQDirDependsCommand) ast.getCommands().get(0);
assertEquals("Unexpected module identifier", "MyOtherModule", depends.getModuleIdentifier().getText());
assertEquals("Unexpected initial version", "1.0", depends.getInitialVersion().getVersionString());
assertLocation(0, 26, new Position(1, 0), new Position(1, 26), depends);
}
@Test
public void testDesignerSupportedCommand() {
QMLDirectoryParser parser = new QMLDirectoryParser();
IQDirAST ast = parser.parse(createInputStream("designersupported\n"), false);
assertEquals("Unexpected command list size", 1, ast.getCommands().size());
assertThat("Unexpected command", ast.getCommands().get(0), instanceOf(IQDirDesignerSupportedCommand.class));
assertLocation(0, 18, new Position(1, 0), new Position(1, 18), ast.getCommands().get(0));
}
@Test
public void testCommentCommand() {
QMLDirectoryParser parser = new QMLDirectoryParser();
IQDirAST ast = parser.parse(createInputStream("# This is a comment command\n"), false);
assertEquals("Unexpected command list size", 1, ast.getCommands().size());
assertThat("Unexpected command", ast.getCommands().get(0), instanceOf(IQDirCommentCommand.class));
IQDirCommentCommand comment = (IQDirCommentCommand) ast.getCommands().get(0);
assertEquals("Unexpected text", "# This is a comment command", comment.getText());
}
@Test
public void testSyntaxErrorCommand() {
QMLDirectoryParser parser = new QMLDirectoryParser();
IQDirAST ast = parser.parse(createInputStream("classname"));
assertEquals("Unexpected command list size", 1, ast.getCommands().size());
assertThat("Unexpected command", ast.getCommands().get(0), instanceOf(IQDirSyntaxError.class));
IQDirSyntaxError err = (IQDirSyntaxError) ast.getCommands().get(0);
assertEquals("Unexpected message", "Unexpected token 'EOF' (1:9)", err.getSyntaxError().getMessage());
assertLocation(0, 9, new Position(1, 0), new Position(1, 9), err);
}
@Test
public void testSyntaxErrorCommandIncludesWholeLine() {
QMLDirectoryParser parser = new QMLDirectoryParser();
IQDirAST ast = parser.parse(createInputStream("classname class extra\n"));
assertEquals("Unexpected command list size", 1, ast.getCommands().size());
assertThat("Unexpected command", ast.getCommands().get(0), instanceOf(IQDirSyntaxError.class));
IQDirSyntaxError err = (IQDirSyntaxError) ast.getCommands().get(0);
assertEquals("Unexpected message", "Expected token '\\n' or 'EOF', but saw 'extra' (1:16)",
err.getSyntaxError().getMessage());
assertLocation(0, 22, new Position(1, 0), new Position(1, 22), err);
}
@Test
public void testExampleQMLDirFile() {
QMLDirectoryParser parser = new QMLDirectoryParser();
IQDirAST ast = parser.parse(createInputStream("module QtQuick\n" +
"plugin qtquick2plugin\n" +
"classname QtQuick2Plugin\n" +
"typeinfo plugins.qmltypes\n" +
"designersupported\n"));
assertEquals("Unexpected command list size", 5, ast.getCommands().size());
// Module Command (index 0)
assertThat("Unexpected command", ast.getCommands().get(0), instanceOf(IQDirModuleCommand.class));
IQDirModuleCommand mod = (IQDirModuleCommand) ast.getCommands().get(0);
assertEquals("Unexpected module qualified ID", "QtQuick", mod.getModuleIdentifier().getText());
// Plugin Command (index 1)
assertThat("Unexpected command", ast.getCommands().get(1), instanceOf(IQDirPluginCommand.class));
IQDirPluginCommand plugin = (IQDirPluginCommand) ast.getCommands().get(1);
assertEquals("Unexpected plugin identifier", "qtquick2plugin", plugin.getName().getText());
assertEquals("Unexpected plugin path", null, plugin.getPath());
// Classname Command (index 2)
assertThat("Unexpected command", ast.getCommands().get(2), instanceOf(IQDirClassnameCommand.class));
IQDirClassnameCommand classname = (IQDirClassnameCommand) ast.getCommands().get(2);
assertEquals("Unexpected class name", "QtQuick2Plugin", classname.getIdentifier().getText());
// Type Info Command (index 3)
assertThat("Unexpected command", ast.getCommands().get(3), instanceOf(IQDirTypeInfoCommand.class));
IQDirTypeInfoCommand typeinfo = (IQDirTypeInfoCommand) ast.getCommands().get(3);
assertEquals("Unexpected type info file name", "plugins.qmltypes", typeinfo.getFile().getText());
// Designer Supported Command (index 4)
assertThat("Unexpected command", ast.getCommands().get(4), instanceOf(IQDirDesignerSupportedCommand.class));
}
@Test
public void testExampleQMLDirFileWithError() {
QMLDirectoryParser parser = new QMLDirectoryParser();
IQDirAST ast = parser.parse(createInputStream("module QtQuick\n" +
"plugin qtquick2plugin\n" +
"classnames QtQuick2Plugin\n" +
"typeinfo plugins.qmltypes\n" +
"designersupported\n"));
assertEquals("Unexpected command list size", 5, ast.getCommands().size());
// Module Command (index 0)
assertThat("Unexpected command", ast.getCommands().get(0), instanceOf(IQDirModuleCommand.class));
IQDirModuleCommand mod = (IQDirModuleCommand) ast.getCommands().get(0);
assertEquals("Unexpected module qualified ID", "QtQuick", mod.getModuleIdentifier().getText());
// Plugin Command (index 1)
assertThat("Unexpected command", ast.getCommands().get(1), instanceOf(IQDirPluginCommand.class));
IQDirPluginCommand plugin = (IQDirPluginCommand) ast.getCommands().get(1);
assertEquals("Unexpected plugin identifier", "qtquick2plugin", plugin.getName().getText());
assertEquals("Unexpected plugin path", null, plugin.getPath());
// Syntax Error Command (index 2)
assertThat("Unexpected command", ast.getCommands().get(2), instanceOf(IQDirSyntaxError.class));
IQDirSyntaxError err = (IQDirSyntaxError) ast.getCommands().get(2);
assertEquals("Unexpected error message", "Unexpected token 'QtQuick2Plugin' (3:11)", err.getSyntaxError().getMessage());
// Type Info Command (index 3)
assertThat("Unexpected command", ast.getCommands().get(3), instanceOf(IQDirTypeInfoCommand.class));
IQDirTypeInfoCommand typeinfo = (IQDirTypeInfoCommand) ast.getCommands().get(3);
assertEquals("Unexpected type info file name", "plugins.qmltypes", typeinfo.getFile().getText());
// Designer Supported Command (index 4)
assertThat("Unexpected command", ast.getCommands().get(4), instanceOf(IQDirDesignerSupportedCommand.class));
}
@Test
public void testParseTwoDifferentStreams() {
QMLDirectoryParser parser = new QMLDirectoryParser();
// Parse module QtQuick.Controls
IQDirAST ast = parser.parse(createInputStream("module QtQuick.Controls\n"), false);
assertEquals("Unexpected command list size", 1, ast.getCommands().size());
assertThat("Unexpected command", ast.getCommands().get(0), instanceOf(IQDirModuleCommand.class));
IQDirModuleCommand mod = (IQDirModuleCommand) ast.getCommands().get(0);
assertEquals("Unexpected qualified ID", "QtQuick.Controls", mod.getModuleIdentifier().getText());
assertLocation(0, 24, new Position(1, 0), new Position(1, 24), mod);
// Parse a second module MyModule
ast = parser.parse(createInputStream("module MyModule\n"), false);
assertEquals("Unexpected command list size", 1, ast.getCommands().size());
assertThat("Unexpected command", ast.getCommands().get(0), instanceOf(IQDirModuleCommand.class));
mod = (IQDirModuleCommand) ast.getCommands().get(0);
assertEquals("Unexpected qualified ID", "MyModule", mod.getModuleIdentifier().getText());
assertLocation(0, 16, new Position(1, 0), new Position(1, 16), mod);
}
}

View file

@ -25,6 +25,10 @@ Bundle-Localization: plugin
Export-Package: org.eclipse.cdt.internal.qt.core;x-friends:="org.eclipse.cdt.qt.ui,org.eclipse.cdt.qt.ui.tests",
org.eclipse.cdt.internal.qt.core.build;x-friends:="org.eclipse.cdt.qt.ui",
org.eclipse.cdt.internal.qt.core.index;x-friends:="org.eclipse.cdt.qt.ui.tests",
org.eclipse.cdt.internal.qt.core.location;x-friends:="org.eclipse.cdt.qt.core.tests",
org.eclipse.cdt.internal.qt.core.parser;x-friends:="org.eclipse.cdt.qt.ui",
org.eclipse.cdt.internal.qt.core.project;x-friends:="org.eclipse.cdt.qt.ui",
org.eclipse.cdt.qt.core
org.eclipse.cdt.internal.qt.core.qmldir;x-friends:="org.eclipse.cdt.qt.core.tests",
org.eclipse.cdt.qt.core;x-friends:="org.eclipse.cdt.qt.core.tests",
org.eclipse.cdt.qt.core.location,
org.eclipse.cdt.qt.core.qmldir

View file

@ -0,0 +1,38 @@
/*******************************************************************************
* 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.internal.qt.core.location;
import org.eclipse.cdt.qt.core.location.IPosition;
public class Position implements IPosition {
private final int line;
private final int column;
public Position(int line, int column) {
this.line = line;
this.column = column;
}
@Override
public int getLine() {
return line;
}
@Override
public int getColumn() {
return column;
}
@Override
public String toString() {
return "(" + line + ":" + column + ")"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
}
}

View file

@ -0,0 +1,58 @@
/*******************************************************************************
* 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.internal.qt.core.location;
import org.eclipse.cdt.qt.core.location.IPosition;
import org.eclipse.cdt.qt.core.location.ISourceLocation;
public class SourceLocation implements ISourceLocation {
private String source;
private IPosition start;
private IPosition end;
public SourceLocation() {
this(null, null, null);
}
public SourceLocation(String source, IPosition start, IPosition end) {
this.source = source;
this.start = start;
this.end = end;
}
public void setSource(String value) {
this.source = value;
}
@Override
public String getSource() {
return source;
}
public void setStart(IPosition value) {
this.start = value;
}
@Override
public IPosition getStart() {
return start;
}
public void setEnd(IPosition value) {
this.end = value;
}
@Override
public IPosition getEnd() {
return end;
}
}

View file

@ -0,0 +1,36 @@
/*******************************************************************************
* 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.internal.qt.core.qmldir;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import org.eclipse.cdt.qt.core.qmldir.IQDirAST;
import org.eclipse.cdt.qt.core.qmldir.IQDirCommand;
public class QDirAST extends QDirASTNode implements IQDirAST {
private final List<IQDirCommand> commands;
public QDirAST() {
commands = new ArrayList<>();
}
public void addCommand(IQDirCommand command) {
commands.add(command);
}
@Override
public List<IQDirCommand> getCommands() {
return Collections.unmodifiableList(commands);
}
}

View file

@ -0,0 +1,55 @@
/*******************************************************************************
* 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.internal.qt.core.qmldir;
import org.eclipse.cdt.internal.qt.core.location.SourceLocation;
import org.eclipse.cdt.qt.core.qmldir.IQDirASTNode;
public class QDirASTNode implements IQDirASTNode {
private SourceLocation location;
private int start;
private int end;
public QDirASTNode() {
this.location = new SourceLocation();
this.start = -1;
this.end = -1;
}
public void setLocation(SourceLocation value) {
this.location = value;
}
@Override
public SourceLocation getLocation() {
return location;
}
public void setStart(int value) {
this.start = value;
}
@Override
public int getStart() {
return start;
}
public void setEnd(int value) {
this.end = value;
}
@Override
public int getEnd() {
return end;
}
}

View file

@ -0,0 +1,28 @@
/*******************************************************************************
* 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.internal.qt.core.qmldir;
import org.eclipse.cdt.qt.core.qmldir.IQDirClassnameCommand;
public class QDirClassnameCommand extends QDirASTNode implements IQDirClassnameCommand {
private QDirWord ident;
public void setIdentifier(QDirWord value) {
this.ident = value;
}
@Override
public QDirWord getIdentifier() {
return ident;
}
}

View file

@ -0,0 +1,28 @@
/*******************************************************************************
* 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.internal.qt.core.qmldir;
import org.eclipse.cdt.qt.core.qmldir.IQDirCommentCommand;
public class QDirCommentCommand extends QDirASTNode implements IQDirCommentCommand {
private String text;
public void setText(String value) {
this.text = value;
}
@Override
public String getText() {
return text;
}
}

View file

@ -0,0 +1,38 @@
/*******************************************************************************
* 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.internal.qt.core.qmldir;
import org.eclipse.cdt.qt.core.qmldir.IQDirDependsCommand;
public class QDirDependsCommand extends QDirASTNode implements IQDirDependsCommand {
private QDirWord moduleName;
private QDirVersion version;
public void setModuleIdentifier(QDirWord value) {
this.moduleName = value;
}
@Override
public QDirWord getModuleIdentifier() {
return moduleName;
}
public void setInitialVersion(QDirVersion value) {
this.version = value;
}
@Override
public QDirVersion getInitialVersion() {
return version;
}
}

View file

@ -0,0 +1,16 @@
/*******************************************************************************
* 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.internal.qt.core.qmldir;
import org.eclipse.cdt.qt.core.qmldir.IQDirDesignerSupportedCommand;
public class QDirDesignerSupportedCommand extends QDirASTNode implements IQDirDesignerSupportedCommand {
}

View file

@ -0,0 +1,38 @@
/*******************************************************************************
* 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.internal.qt.core.qmldir;
import org.eclipse.cdt.qt.core.qmldir.IQDirInternalCommand;
public class QDirInternalCommand extends QDirASTNode implements IQDirInternalCommand {
private QDirWord typeName;
private QDirWord file;
public void setTypeName(QDirWord value) {
this.typeName = value;
}
@Override
public QDirWord getTypeName() {
return typeName;
}
public void setFile(QDirWord value) {
this.file = value;
}
@Override
public QDirWord getFile() {
return file;
}
}

View file

@ -0,0 +1,27 @@
/*******************************************************************************
* 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.internal.qt.core.qmldir;
import org.eclipse.cdt.qt.core.qmldir.IQDirModuleCommand;
public class QDirModuleCommand extends QDirASTNode implements IQDirModuleCommand {
private QDirWord identifier;
public void setModuleIdentifier(QDirWord value) {
this.identifier = value;
}
@Override
public QDirWord getModuleIdentifier() {
return identifier;
}
}

View file

@ -0,0 +1,38 @@
/*******************************************************************************
* 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.internal.qt.core.qmldir;
import org.eclipse.cdt.qt.core.qmldir.IQDirPluginCommand;
public class QDirPluginCommand extends QDirASTNode implements IQDirPluginCommand {
private QDirWord qid;
private QDirWord path;
public void setName(QDirWord value) {
this.qid = value;
}
@Override
public QDirWord getName() {
return qid;
}
public void setPath(QDirWord value) {
this.path = value;
}
@Override
public QDirWord getPath() {
return path;
}
}

View file

@ -0,0 +1,47 @@
/*******************************************************************************
* 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.internal.qt.core.qmldir;
import org.eclipse.cdt.qt.core.qmldir.IQDirResourceCommand;
public class QDirResourceCommand extends QDirASTNode implements IQDirResourceCommand {
private QDirWord typeName;
private QDirVersion version;
private QDirWord file;
public void setResourceIdentifier(QDirWord value) {
this.typeName = value;
}
@Override
public QDirWord getResourceIdentifier() {
return typeName;
}
public void setInitialVersion(QDirVersion value) {
this.version = value;
}
@Override
public QDirVersion getInitialVersion() {
return version;
}
public void setFile(QDirWord value) {
this.file = value;
}
@Override
public QDirWord getFile() {
return file;
}
}

View file

@ -0,0 +1,48 @@
/*******************************************************************************
* 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.internal.qt.core.qmldir;
import org.eclipse.cdt.qt.core.qmldir.IQDirSingletonCommand;
public class QDirSingletonCommand extends QDirASTNode implements IQDirSingletonCommand {
private QDirWord typeName;
private QDirVersion version;
private QDirWord file;
public void setTypeName(QDirWord value) {
this.typeName = value;
}
@Override
public QDirWord getTypeName() {
return typeName;
}
public void setInitialVersion(QDirVersion value) {
this.version = value;
}
@Override
public QDirVersion getInitialVersion() {
return version;
}
public void setFile(QDirWord value) {
this.file = value;
}
@Override
public QDirWord getFile() {
return file;
}
}

View file

@ -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.internal.qt.core.qmldir;
import org.eclipse.cdt.qt.core.qmldir.IQDirASTNode;
import org.eclipse.cdt.qt.core.qmldir.IQDirSyntaxError;
import org.eclipse.cdt.qt.core.qmldir.QMLDirectoryLexer.Token;
import org.eclipse.cdt.qt.core.qmldir.QMLDirectoryParser.SyntaxError;
public class QDirSyntaxError extends QDirASTNode implements IQDirSyntaxError {
private SyntaxError exception;
public QDirSyntaxError(SyntaxError exception) {
this.exception = exception;
}
@Override
public Token getOffendingToken() {
return this.exception.getOffendingToken();
}
@Override
public IQDirASTNode getIncompleteNode() {
return this.exception.getIncompleteNode();
}
@Override
public SyntaxError getSyntaxError() {
return this.exception;
}
}

View file

@ -0,0 +1,28 @@
/*******************************************************************************
* 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.internal.qt.core.qmldir;
import org.eclipse.cdt.qt.core.qmldir.IQDirTypeInfoCommand;
public class QDirTypeInfoCommand extends QDirASTNode implements IQDirTypeInfoCommand {
private QDirWord file;
public void setFile(QDirWord value) {
this.file = value;
}
@Override
public QDirWord getFile() {
return file;
}
}

View file

@ -0,0 +1,28 @@
/*******************************************************************************
* 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.internal.qt.core.qmldir;
import org.eclipse.cdt.qt.core.qmldir.IQDirVersion;
public class QDirVersion extends QDirASTNode implements IQDirVersion {
private String version;
public void setVersionString(String value) {
this.version = value;
}
@Override
public String getVersionString() {
return version;
}
}

View file

@ -0,0 +1,28 @@
/*******************************************************************************
* 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.internal.qt.core.qmldir;
import org.eclipse.cdt.qt.core.qmldir.IQDirWord;
public class QDirWord extends QDirASTNode implements IQDirWord {
private String text;
public void setText(String value) {
this.text = value;
}
@Override
public String getText() {
return text;
}
}

View file

@ -0,0 +1,30 @@
/*******************************************************************************
* 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.location;
/**
* Stores a line/offset pair as integers.
*/
public interface IPosition {
/**
* Gets the one-indexed line number indicated by this <code>IPosition</code>
*
* @return the line number
*/
public int getLine();
/**
* Gets the zero-indexed column indicated by this <code>IPosition</code>
*
* @return the column
*/
public int getColumn();
}

View file

@ -0,0 +1,38 @@
/*******************************************************************************
* 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.location;
/**
* Represents a location in a source file. Uses the {@link IPosition} interface to store the start and end locations as a
* line/offset pair.
*/
public interface ISourceLocation {
/**
* Gets the String representing the source of this <code>ISourceLocation</code>
*
* @return the source or <code>null</code> if not available
*/
public String getSource();
/**
* Gets the zero-indexed offset indicating the start of this <code>ISourceLocation</code>
*
* @return the start offset
*/
public IPosition getStart();
/**
* Gets the zero-indexed offset indicating the end of this <code>ISourceLocation</code>
*
* @return the end offset
*/
public IPosition getEnd();
}

View file

@ -0,0 +1,26 @@
/*******************************************************************************
* 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.qmldir;
import java.util.List;
/**
* The main entry point into the qmldir AST. This interface contains a list of Commands specified within the qmldir file that it
* represents.
*/
public interface IQDirAST extends IQDirASTNode {
/**
* Gets the list of commands in the qmldir file that this <code>IQDirAST</code> represents.
*
* @return the list of all commands in the qmldir file
*/
public List<IQDirCommand> getCommands();
}

View file

@ -0,0 +1,41 @@
/*******************************************************************************
* 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.qmldir;
import org.eclipse.cdt.qt.core.location.ISourceLocation;
/**
* The base type for all qmldir AST nodes. Contains methods for retrieving a node's positional information.
*/
public interface IQDirASTNode {
/**
* Gets a more detailed description of this node's location than {@link IQDirASTNode#getStart()} and
* {@link IQDirASTNode#getStart()}. This method allows the retrieval of line and column information in order to make output for
* syntax errors and the like more human-readable.
*
* @return the {@link ISourceLocation} representing this node's location in the source
*/
public ISourceLocation getLocation();
/**
* Gets the zero-indexed offset indicating the start of this node in the source.
*
* @return the node's start offset
*/
public int getStart();
/**
* Gets the zero-indexed offset indicating the end of this node in the source.
*
* @return the node's end offset
*/
public int getEnd();
}

View file

@ -0,0 +1,25 @@
/*******************************************************************************
* 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.qmldir;
/**
* An AST Node representing a Classname Command in a qmldir file.
*
* @see <a href="http://doc.qt.io/qt-5/qtqml-modules-qmldir.html">Module Definition qmldir Files</a>
*/
public interface IQDirClassnameCommand extends IQDirCommand {
/**
* Gets the <code>IQDirWord</code> representing the identifier for the classname.
*
* @return the identifier for the classname
*/
public IQDirWord getIdentifier();
}

View file

@ -0,0 +1,19 @@
/*******************************************************************************
* 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.qmldir;
/**
* The base interface for all qmldir AST nodes that function as commands.
*
* @see <a href="http://doc.qt.io/qt-5/qtqml-modules-qmldir.html">Module Definition qmldir Files</a>
*/
public interface IQDirCommand extends IQDirASTNode {
}

View file

@ -0,0 +1,25 @@
/*******************************************************************************
* 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.qmldir;
/**
* An AST Node representing a Comment Command in a qmldir file.
*
* @see <a href="http://doc.qt.io/qt-5/qtqml-modules-qmldir.html">Module Definition qmldir Files</a>
*/
public interface IQDirCommentCommand extends IQDirCommand {
/**
* Gets the String representation of this comment as it appears in the qmldir file.
*
* @return the String representation of this comment
*/
public String getText();
}

View file

@ -0,0 +1,32 @@
/*******************************************************************************
* 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.qmldir;
/**
* An AST Node representing a Depends Command in a qmldir file.
*
* @see <a href="http://doc.qt.io/qt-5/qtqml-modules-qmldir.html">Module Definition qmldir Files</a>
*/
public interface IQDirDependsCommand extends IQDirCommand {
/**
* Gets the <code>IQDirWord</code> representing the module identifier that this qmldir module depends on.
*
* @return the module identifier
*/
public IQDirWord getModuleIdentifier();
/**
* Gets the <code>IQDirVersion</code> representing the initial version of the module that this qmldir module depends on.
*
* @return the initial version
*/
public IQDirVersion getInitialVersion();
}

View file

@ -0,0 +1,19 @@
/*******************************************************************************
* 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.qmldir;
/**
* An AST Node representing a Designer Supported Command in a qmldir file.
*
* @see <a href="http://doc.qt.io/qt-5/qtqml-modules-qmldir.html">Module Definition qmldir Files</a>
*/
public interface IQDirDesignerSupportedCommand extends IQDirCommand {
}

View file

@ -0,0 +1,32 @@
/*******************************************************************************
* 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.qmldir;
/**
* An AST Node representing an Internal Command in a qmldir file.
*
* @see <a href="http://doc.qt.io/qt-5/qtqml-modules-qmldir.html">Module Definition qmldir Files</a>
*/
public interface IQDirInternalCommand extends IQDirCommand {
/**
* Gets the <code>IQDirWord</code> representing the type name of the internal type.
*
* @return the type names
*/
public IQDirWord getTypeName();
/**
* Gets the <code>IQDirWord</code> representing the filename of the internal type.
*
* @return the filename
*/
public IQDirWord getFile();
}

View file

@ -0,0 +1,25 @@
/*******************************************************************************
* 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.qmldir;
/**
* An AST Node representing a Module Command in a qmldir file.
*
* @see <a href="http://doc.qt.io/qt-5/qtqml-modules-qmldir.html">Module Definition qmldir Files</a>
*/
public interface IQDirModuleCommand extends IQDirCommand {
/**
* Gets the <code>IQDirWord</code> representing the identifier for the module.
*
* @return the identifier for the module
*/
public IQDirWord getModuleIdentifier();
}

View file

@ -0,0 +1,32 @@
/*******************************************************************************
* 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.qmldir;
/**
* An AST Node representing a Plugin Command in a qmldir file.
*
* @see <a href="http://doc.qt.io/qt-5/qtqml-modules-qmldir.html">Module Definition qmldir Files</a>
*/
public interface IQDirPluginCommand extends IQDirCommand {
/**
* Gets the <code>IQDirWord</code> representing the name of the plugin.
*
* @return the plugin name
*/
public IQDirWord getName();
/**
* Gets the <code>IQDirWord</code> representing the path to the plugin if it was given.
*
* @return the path to the plugin or <code>null</code> if not available
*/
public IQDirWord getPath();
}

View file

@ -0,0 +1,39 @@
/*******************************************************************************
* 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.qmldir;
/**
* An AST Node representing a Resource Command in a qmldir file.
*
* @see <a href="http://doc.qt.io/qt-5/qtqml-modules-qmldir.html">Module Definition qmldir Files</a>
*/
public interface IQDirResourceCommand extends IQDirCommand {
/**
* Gets the <code>IQDirWord</code> representing the identifier of the resource.
*
* @return the identifier of the resource
*/
public IQDirWord getResourceIdentifier();
/**
* Gets the <code>IQDirVersion</code> representing the initial version of the resource.
*
* @return the initial version
*/
public IQDirVersion getInitialVersion();
/**
* Gets the <code>IQDirWord</code> representing the filename of the resource.
*
* @return the filename
*/
public IQDirWord getFile();
}

View file

@ -0,0 +1,39 @@
/*******************************************************************************
* 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.qmldir;
/**
* An AST Node representing a Singleton Command in a qmldir file.
*
* @see <a href="http://doc.qt.io/qt-5/qtqml-modules-qmldir.html">Module Definition qmldir Files</a>
*/
public interface IQDirSingletonCommand extends IQDirCommand {
/**
* Gets the <code>IQDirWord</code> representing the type name of the singleton type.
*
* @return the type name
*/
public IQDirWord getTypeName();
/**
* Gets the <code>IQDirVersion</code> representing the initial version of the singleton type.
*
* @return the initial version
*/
public IQDirVersion getInitialVersion();
/**
* Gets the <code>IQDirWord</code> representing the filename of the singleton type.
*
* @return the filename
*/
public IQDirWord getFile();
}

View file

@ -0,0 +1,43 @@
/*******************************************************************************
* 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.qmldir;
import org.eclipse.cdt.qt.core.qmldir.QMLDirectoryLexer.Token;
import org.eclipse.cdt.qt.core.qmldir.QMLDirectoryParser.SyntaxError;
/**
* An AST Node representing a syntax error in a qmldir file. Due to the fact that the qmldir file is so simple, a syntax error will
* only occur at the command level while the parser jumps to the next line to recover.
*/
public interface IQDirSyntaxError extends IQDirCommand {
/**
* Gets the token that caused the parser to fail. This is a helper method equivalent to
* <code>getSyntaxError.getOffendingToken()</code>.
*
* @return the offending token.
*/
public Token getOffendingToken();
/**
* Gets the node that the parser was working on before it failed (if available). This is a helper method equivalent to
* <code>getSyntaxError.getIncompleteNode()</code>.
*
* @return the incomplete node or <code>null</code> if not available
*/
public IQDirASTNode getIncompleteNode();
/**
* Gets the syntax error that occurred.
*
* @return the syntax error
*/
public SyntaxError getSyntaxError();
}

View file

@ -0,0 +1,25 @@
/*******************************************************************************
* 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.qmldir;
/**
* An AST Node representing a Type Info Command in a qmldir file.
*
* @see <a href="http://doc.qt.io/qt-5/qtqml-modules-qmldir.html">Module Definition qmldir Files</a>
*/
public interface IQDirTypeInfoCommand extends IQDirCommand {
/**
* Gets the <code>IQDirWord</code> representing the filename of the type info file.
*
* @return the filename of the type info file
*/
public IQDirWord getFile();
}

View file

@ -0,0 +1,23 @@
/*******************************************************************************
* 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.qmldir;
/**
* An AST Node representing a version String of the form &ltMajorVersion&gt.&ltMinorVersion&gt
*/
public interface IQDirVersion extends IQDirASTNode {
/**
* Gets the String value of this version. The result will always be of the form "&ltMajorVersion&gt.&ltMinorVersion&gt"
*
* @return a string value of this version
*/
public String getVersionString();
}

View file

@ -0,0 +1,27 @@
/*******************************************************************************
* 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.qmldir;
/**
* An AST Node representing a set of characters that does not contain whitespace and does not start with a digit. This encompasses
* the syntax for Identifiers, Qualified IDs, Paths, and File Names all in one parser rule.
*/
public interface IQDirWord extends IQDirASTNode {
/**
* Gets the String representing this word as it appears in the qmldir file.<br>
* <br>
* <b>Note:</b> The text is not modified or validated in any way when it is parsed. It is necessary for the caller to perform
* semantic validation of the returned value to ensure it represents a valid identifier, filename, or path.
*
* @return a string representing this word
*/
public String getText();
}

View file

@ -0,0 +1,252 @@
/*******************************************************************************
* 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.qmldir;
import java.io.InputStream;
import java.util.Scanner;
import java.util.regex.MatchResult;
import java.util.regex.Pattern;
import org.eclipse.cdt.internal.qt.core.location.Position;
import org.eclipse.cdt.internal.qt.core.location.SourceLocation;
import org.eclipse.cdt.qt.core.location.ISourceLocation;
/**
* Converts an <code>InputStream</code> representing a qmldir file into a stream of tokens through successive calls to
* <code>nextToken</code>. This lexer uses regular expressions to match its 16 valid token types:
* <ul>
* <li><b>COMMENT</b>: A single line comment that begins with '#'
* <li><b>MODULE</b>: Keyword 'module'
* <li><b>TYPEINFO</b>: The keyword 'typeinfo'
* <li><b>SINGLETON</b>: The keyword 'singleton'
* <li><b>INTERNAL</b>: The keyword 'internal'
* <li><b>PLUGIN</b>: The keyword 'plugin'
* <li><b>CLASSNAME</b>: The keyword 'classname'
* <li><b>DEPENDS</b>: The keyword 'depends'
* <li><b>DESIGNERSUPPORTED</b>: The keyword 'designersupported'
* <li><b>WORD</b>: A group of characters that form an identifier, filename, or path
* <li><b>DECIMAL</b>: A number of the form [0-9]+ '.' [0-9]+
* <li><b>INTEGER</b>: An integer of the form [0-9]+
* <li><b>WHITESPACE</b>: A group of whitespace characters (not including newlines)
* <li><b>COMMAND_END</b>: A newline character
* <li><b>UNKNOWN</b>: A group of characters that does not match any of the preceding tokens
* <li><b>EOF</b>: End of File
* </ul>
*/
public class QMLDirectoryLexer {
/**
* A single matched token returned by a <code>QMLDirectoryLexer</code>. A <code>Token</code> stores information on how it was
* matched including the type of token, the exact text that was matched, and its position in the <code>InputStream</code> .
*/
public static class Token {
private final TokenType tokType;
private final String raw;
private final ISourceLocation location;
private final int start;
private final int end;
private Token(TokenType type, MatchResult match, int line, int lineStart) {
this(type, match.group(), match.start(), match.end(), line, lineStart);
}
private Token(TokenType type, String raw, int start, int end, int line, int lineStart) {
this.tokType = type;
raw = raw.replaceAll("\n", "\\\\n"); //$NON-NLS-1$ //$NON-NLS-2$
raw = raw.replaceAll("\r", "\\\\r"); //$NON-NLS-1$ //$NON-NLS-2$
this.raw = raw;
this.start = start;
this.end = end;
this.location = new SourceLocation(null,
new Position(line, start - lineStart),
new Position(line, end - lineStart));
}
/**
* Get the type of token that was matched.
*
* @return the type of token
*/
public TokenType getType() {
return tokType;
}
/**
* Gets the raw text that this token was matched with.
*
* @return a String representing the matched text
*/
public String getText() {
return raw;
}
/**
* Gets a more detailed description of this token's location in the <code>InputStream</code> than {@link Token#getStart()}
* and {@link Token#getEnd()}. This method allows the retrieval of line and column information in order to make output for
* syntax errors and the like more human-readable.
*
* @return the {@link ISourceLocation} representing this token's location in the <code>InputStream</code>
*/
public ISourceLocation getLocation() {
return location;
}
/**
* Gets the zero-indexed offset indicating the start of this token in the <code>InputStream</code>.
*
* @return the token's start offset
*/
public int getStart() {
return start;
}
/**
* Gets the zero-indexed offset indicating the end of this token in the <code>InputStream</code>.
*
* @return the token's end offset
*/
public int getEnd() {
return end;
}
}
/**
* An Enumeration encompassing the 16 possible types of tokens returned by a <code>QMLDirectoryLexer</code>.
*
* @see org.eclipse.cdt.qt.core.qmldir.QMLDirectoryLexer
*/
public static enum TokenType {
COMMENT("#.*$"), //$NON-NLS-1$
MODULE("module(?=\\s|$)"), //$NON-NLS-1$
TYPEINFO("typeinfo(?=\\s|$)"), //$NON-NLS-1$
SINGLETON("singleton(?=\\s|$)"), //$NON-NLS-1$
INTERNAL("internal(?=\\s|$)"), //$NON-NLS-1$
PLUGIN("plugin(?=\\s|$)"), //$NON-NLS-1$
CLASSNAME("classname(?=\\s|$)"), //$NON-NLS-1$
DEPENDS("depends(?=\\s|$)"), //$NON-NLS-1$
DESIGNERSUPPORTED("designersupported(?=\\s|$)"), //$NON-NLS-1$
WORD("[^0-9\\s][^\\s]*"), //$NON-NLS-1$
DECIMAL("[0-9]+\\.[0-9]+"), //$NON-NLS-1$
INTEGER("[0-9]+"), //$NON-NLS-1$
WHITESPACE("\\h+"), //$NON-NLS-1$
COMMAND_END("(?:\r\n)|\n"), //$NON-NLS-1$
UNKNOWN(".+"), //$NON-NLS-1$
EOF(null);
private static Pattern pattern;
private static Pattern patternForAllTerminals() {
if (pattern == null) {
String regex = ""; //$NON-NLS-1$
TokenType[] tokens = TokenType.values();
for (int i = 0; i < TokenType.values().length; i++) {
TokenType tok = tokens[i];
if (tok.regex != null) {
if (i != 0) {
regex += "|"; //$NON-NLS-1$
}
regex += "(" + tok.regex + ")"; //$NON-NLS-1$ //$NON-NLS-2$
}
}
pattern = Pattern.compile(regex, Pattern.MULTILINE);
}
return pattern;
}
private final String regex;
private TokenType(String regex) {
this.regex = regex;
}
}
private Scanner input;
private MatchResult lastMatch;
private int currentLine;
private int currentLineStart;
/**
* Creates a new <code>QMLDirectoryLexer</code> without initializing any of the its internal state. A call to
* <code>setInput</code> is necessary to fully initialize the lexer before any calls to <code>nextToken</code>.
*/
public QMLDirectoryLexer() {
}
/**
* Prepares for lexical analysis by giving the lexer an <code>InputStream</code> to retrieve text from.
*
* @param input
* the input to perform lexical analysis on
*/
public void setInput(InputStream input) {
this.input = new Scanner(input);
this.lastMatch = null;
this.currentLine = 1;
this.currentLineStart = 0;
}
/**
* Retrieves the next valid token from the <code>InputStream</code> given by <code>setInput</code>. This is a helper method to
* skip whitespace that is equivalent to <code>QMLDirectoryLexer.nextToken(true)</code>.
*
* @return the next token in the <code>InputStream</code>
* @throws IllegalArgumentException
* if <code>setInput</code> has not been called
*/
public Token nextToken() throws IllegalArgumentException {
return nextToken(true);
}
/**
* Retrieves the next valid token from the <code>InputStream</code> given by <code>setInput</code>. This method has the ability
* to skip over whitespace tokens by setting <code>skipWhitespace</code> to <code>true</code>.
*
* @param skipWhitespace
* whether or not the lexer should skip whitespace tokens
* @return the next token in the <code>InputStream</code>
* @throws IllegalArgumentException
* if <code>setInput</code> has not been called
*/
public Token nextToken(boolean skipWhitespace) throws IllegalArgumentException {
if (input == null) {
throw new IllegalArgumentException("Input cannot be null"); //$NON-NLS-1$
}
if (input.findWithinHorizon(TokenType.patternForAllTerminals(), 0) == null) {
if (lastMatch != null) {
return new Token(TokenType.EOF, "", lastMatch.end(), lastMatch.end(), currentLine, currentLineStart); //$NON-NLS-1$
} else {
return new Token(TokenType.EOF, "", 0, 0, 1, 0); //$NON-NLS-1$
}
} else {
int groupNo = 1;
for (TokenType t : TokenType.values()) {
if (t.regex != null) {
if (input.match().start(groupNo) != -1) {
lastMatch = input.match();
Token next = null;
if (!(t.equals(TokenType.WHITESPACE) && skipWhitespace)) {
next = new Token(t, input.match(), currentLine, currentLineStart);
} else {
next = nextToken(skipWhitespace);
}
if (t.equals(TokenType.COMMAND_END)) {
// Advance the line number information
currentLine++;
currentLineStart = input.match().end();
}
return next;
}
groupNo++;
}
}
return new Token(TokenType.UNKNOWN, input.match(), currentLine, currentLineStart);
}
}
}

View file

@ -0,0 +1,423 @@
/*******************************************************************************
* 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.qmldir;
import java.io.InputStream;
import java.util.Stack;
import org.eclipse.cdt.internal.qt.core.location.SourceLocation;
import org.eclipse.cdt.internal.qt.core.qmldir.QDirAST;
import org.eclipse.cdt.internal.qt.core.qmldir.QDirASTNode;
import org.eclipse.cdt.internal.qt.core.qmldir.QDirClassnameCommand;
import org.eclipse.cdt.internal.qt.core.qmldir.QDirCommentCommand;
import org.eclipse.cdt.internal.qt.core.qmldir.QDirDependsCommand;
import org.eclipse.cdt.internal.qt.core.qmldir.QDirDesignerSupportedCommand;
import org.eclipse.cdt.internal.qt.core.qmldir.QDirInternalCommand;
import org.eclipse.cdt.internal.qt.core.qmldir.QDirModuleCommand;
import org.eclipse.cdt.internal.qt.core.qmldir.QDirPluginCommand;
import org.eclipse.cdt.internal.qt.core.qmldir.QDirResourceCommand;
import org.eclipse.cdt.internal.qt.core.qmldir.QDirSingletonCommand;
import org.eclipse.cdt.internal.qt.core.qmldir.QDirSyntaxError;
import org.eclipse.cdt.internal.qt.core.qmldir.QDirTypeInfoCommand;
import org.eclipse.cdt.internal.qt.core.qmldir.QDirVersion;
import org.eclipse.cdt.internal.qt.core.qmldir.QDirWord;
import org.eclipse.cdt.qt.core.qmldir.QMLDirectoryLexer.Token;
import org.eclipse.cdt.qt.core.qmldir.QMLDirectoryLexer.TokenType;
/**
* Converts an <code>InputStream</code> representing a qmldir file into an Abstract Syntax Tree. Uses the {@link QMLDirectoryLexer}
* under the hood to match tokens which it then uses to construct the AST. Also, a <code>QMLDirectoryParser</code> has the ability
* to skip over syntax errors and include them in its AST rather than returning upon the first error.
*/
public class QMLDirectoryParser {
/**
* An exception thrown when a <code>QMLDirectoryParser</code> encounters a syntax error. This class stores information on the
* offending token as well as the node the parser was working on before it failed (if available).
*/
public static class SyntaxError extends RuntimeException {
private static final long serialVersionUID = 6608815552297970623L;
private final IQDirASTNode incompleteNode;
private final Token offendingToken;
/**
* Creates a new <code>SyntaxError</code>.
*
* @param node
* the incomplete working node
* @param token
* the offending token
* @param message
* the message to display
*/
public SyntaxError(IQDirASTNode node, Token token, String message) {
super(message);
this.incompleteNode = node;
this.offendingToken = token;
}
/**
* Gets the token that caused the parser to fail.
*
* @return the offending token
*/
public Token getOffendingToken() {
return offendingToken;
}
/**
* Gets the last node that the parser was working on before it failed or null if that information isn't present.
*
* @return the incomplete node or <code>null</code> if not available
*/
public IQDirASTNode getIncompleteNode() {
return incompleteNode;
}
}
private final QMLDirectoryLexer lexer;
private final Stack<QDirASTNode> workingNodes;
private Token tok;
/**
* Initializes a new <code>QMLDirectoryParser</code> capable of parsing an <code>InputStream</code> and returning an AST.
*/
public QMLDirectoryParser() {
this.lexer = new QMLDirectoryLexer();
this.workingNodes = new Stack<>();
}
/**
* Parses the given <code>InputStream</code> into an Abstract Syntax Tree. This is a helper method equivalent to
* <code>parse(input, true)</code>. That is, the parser will attempt to recover once it hits an error and include an
* {@link IQDirSyntaxError} node in the AST.
*
* @param input
* the input to parse
* @return the Abstract Syntax Tree representing the input
* @see QMLDirectoryParser#parse(InputStream, boolean)
*/
public IQDirAST parse(InputStream input) {
return parse(input, true);
}
/**
* Parses the given <code>InputStream</code> into an Abstract Syntax Tree. If <code>tolerateErrors</code> is <code>true</code>,
* any syntax errors will be included in the AST as a separate {@link IQDirSyntaxErrorCommand}. The parser will then attempt to
* recover by jumping to the next line and continue parsing. A value of </code>false</code> tells the parser to throw a
* {@link SyntaxError} on the first problem it encounters.
*
* @param input
* the input to parse
* @param tolerateErrors
* whether or not the parser should be error tolerant
* @return the Abstract Syntax Tree representing the input
*/
public IQDirAST parse(InputStream input, boolean tolerateErrors) {
// Clear out any leftover state
this.lexer.setInput(input);
this.workingNodes.clear();
QDirAST ast = new QDirAST();
nextToken();
while (tok.getType() != TokenType.EOF) {
try {
switch (tok.getType()) {
case MODULE:
ast.addCommand(parseModuleCommand());
break;
case SINGLETON:
ast.addCommand(parseSingletonCommand());
break;
case INTERNAL:
ast.addCommand(parseInternalCommand());
break;
case WORD:
ast.addCommand(parseResourceCommand());
break;
case PLUGIN:
ast.addCommand(parsePluginCommand());
break;
case CLASSNAME:
ast.addCommand(parseClassnameCommand());
break;
case TYPEINFO:
ast.addCommand(parseTypeInfoCommand());
break;
case DEPENDS:
ast.addCommand(parseDependsCommand());
break;
case DESIGNERSUPPORTED:
ast.addCommand(parseDesignerSupportedCommand());
break;
case COMMENT:
ast.addCommand(parseCommentCommand());
break;
case COMMAND_END:
// This is just an empty line that should be ignored
nextToken();
break;
default:
throw unexpectedToken();
}
} catch (SyntaxError e) {
if (!tolerateErrors) {
throw e;
}
// Add the syntax error to the AST and jump to the next line
QDirSyntaxError errNode = new QDirSyntaxError(e);
markStart(errNode);
IQDirASTNode node = e.getIncompleteNode();
if (node != null) {
errNode.setLocation((SourceLocation) node.getLocation());
errNode.setStart(node.getStart());
errNode.setEnd(node.getEnd());
}
while (!eat(TokenType.COMMAND_END) && !eat(TokenType.EOF)) {
nextToken();
}
markEnd();
ast.addCommand(errNode);
}
}
return ast;
}
private void nextToken() {
nextToken(true);
}
private void nextToken(boolean skipWhitespace) {
tok = lexer.nextToken(skipWhitespace);
}
private void markStart(QDirASTNode node) {
workingNodes.push(node);
node.setStart(tok.getStart());
node.setLocation(new SourceLocation());
node.getLocation().setStart(tok.getLocation().getStart());
}
private void markEnd() {
QDirASTNode node = workingNodes.pop();
node.setEnd(tok.getEnd());
node.getLocation().setEnd(tok.getLocation().getEnd());
}
private boolean eat(TokenType type) {
if (tok.getType() == type) {
nextToken();
return true;
}
return false;
}
private SyntaxError syntaxError(String message) {
return new SyntaxError(workingNodes.peek(), tok, message + " " + tok.getLocation().getStart().toString()); //$NON-NLS-1$
}
private SyntaxError unexpectedToken() {
String tokenText = tok.getText();
if (tok.getType() == TokenType.EOF) {
tokenText = "EOF"; //$NON-NLS-1$
}
return syntaxError("Unexpected token '" + tokenText + "'"); //$NON-NLS-1$ //$NON-NLS-2$
}
private void expect(TokenType type) {
if (tok.getType() != type) {
throw unexpectedToken();
}
nextToken();
}
private void expectCommandEnd() {
// Allow EOF to be substituted for COMMAND_END
if (tok.getType() == TokenType.EOF) {
nextToken();
return;
}
if (tok.getType() != TokenType.COMMAND_END) {
throw syntaxError("Expected token '\\n' or 'EOF', but saw '" + tok.getText() + "'"); //$NON-NLS-1$ //$NON-NLS-2$
}
nextToken();
}
private QDirModuleCommand parseModuleCommand() {
QDirModuleCommand node = new QDirModuleCommand();
markStart(node);
expect(TokenType.MODULE);
if (tok.getType() == TokenType.WORD) {
node.setModuleIdentifier(parseWord());
expectCommandEnd();
markEnd();
return node;
}
throw unexpectedToken();
}
private QDirSingletonCommand parseSingletonCommand() {
QDirSingletonCommand node = new QDirSingletonCommand();
markStart(node);
expect(TokenType.SINGLETON);
if (tok.getType() == TokenType.WORD) {
node.setTypeName(parseWord());
if (tok.getType() == TokenType.DECIMAL) {
node.setInitialVersion(parseVersion());
if (tok.getType() == TokenType.WORD) {
node.setFile(parseWord());
expectCommandEnd();
markEnd();
return node;
}
}
}
throw unexpectedToken();
};
private QDirInternalCommand parseInternalCommand() {
QDirInternalCommand node = new QDirInternalCommand();
markStart(node);
expect(TokenType.INTERNAL);
if (tok.getType() == TokenType.WORD) {
node.setTypeName(parseWord());
if (tok.getType() == TokenType.WORD) {
node.setFile(parseWord());
expectCommandEnd();
markEnd();
return node;
}
}
throw unexpectedToken();
}
private QDirResourceCommand parseResourceCommand() {
QDirResourceCommand node = new QDirResourceCommand();
markStart(node);
if (tok.getType() == TokenType.WORD) {
node.setResourceIdentifier(parseWord());
if (tok.getType() == TokenType.DECIMAL) {
node.setInitialVersion(parseVersion());
if (tok.getType() == TokenType.WORD) {
node.setFile(parseWord());
expectCommandEnd();
markEnd();
return node;
}
}
}
throw unexpectedToken();
}
private QDirPluginCommand parsePluginCommand() {
QDirPluginCommand node = new QDirPluginCommand();
markStart(node);
expect(TokenType.PLUGIN);
if (tok.getType() == TokenType.WORD) {
node.setName(parseWord());
if (tok.getType() == TokenType.WORD) {
node.setPath(parseWord());
}
expectCommandEnd();
markEnd();
return node;
}
throw unexpectedToken();
}
private QDirClassnameCommand parseClassnameCommand() {
QDirClassnameCommand node = new QDirClassnameCommand();
markStart(node);
expect(TokenType.CLASSNAME);
if (tok.getType() == TokenType.WORD) {
node.setIdentifier(parseWord());
expectCommandEnd();
markEnd();
return node;
}
throw unexpectedToken();
}
private QDirTypeInfoCommand parseTypeInfoCommand() {
QDirTypeInfoCommand node = new QDirTypeInfoCommand();
markStart(node);
expect(TokenType.TYPEINFO);
if (tok.getType() == TokenType.WORD) {
node.setFile(parseWord());
expectCommandEnd();
markEnd();
return node;
}
throw unexpectedToken();
}
private QDirDependsCommand parseDependsCommand() {
QDirDependsCommand node = new QDirDependsCommand();
markStart(node);
expect(TokenType.DEPENDS);
if (tok.getType() == TokenType.WORD) {
node.setModuleIdentifier(parseWord());
if (tok.getType() == TokenType.DECIMAL) {
node.setInitialVersion(parseVersion());
expectCommandEnd();
markEnd();
return node;
}
}
throw unexpectedToken();
}
private QDirDesignerSupportedCommand parseDesignerSupportedCommand() {
QDirDesignerSupportedCommand node = new QDirDesignerSupportedCommand();
markStart(node);
expect(TokenType.DESIGNERSUPPORTED);
expectCommandEnd();
markEnd();
return node;
}
private QDirCommentCommand parseCommentCommand() {
QDirCommentCommand node = new QDirCommentCommand();
markStart(node);
if (tok.getType() == TokenType.COMMENT) {
node.setText(tok.getText());
nextToken();
expectCommandEnd();
markEnd();
return node;
}
throw unexpectedToken();
}
private QDirVersion parseVersion() {
QDirVersion node = new QDirVersion();
markStart(node);
if (tok.getType() == TokenType.DECIMAL) {
node.setVersionString(tok.getText());
nextToken();
markEnd();
return node;
}
throw unexpectedToken();
}
private QDirWord parseWord() {
QDirWord node = new QDirWord();
markStart(node);
if (tok.getType() == TokenType.WORD) {
node.setText(tok.getText());
nextToken();
markEnd();
return node;
}
throw unexpectedToken();
}
}