Initial support for parsing module-infos.
MOE_MIGRATED_REVID=182253064
diff --git a/java/com/google/turbine/model/TurbineFlag.java b/java/com/google/turbine/model/TurbineFlag.java
index 18fc81e..37a11c3 100644
--- a/java/com/google/turbine/model/TurbineFlag.java
+++ b/java/com/google/turbine/model/TurbineFlag.java
@@ -30,6 +30,8 @@
public static final int ACC_FINAL = 0x0010;
public static final int ACC_SYNCHRONIZED = 0x0020;
public static final int ACC_SUPER = 0x0020;
+ public static final int ACC_TRANSITIVE = 0x0020;
+ public static final int ACC_STATIC_PHASE = 0x0040;
public static final int ACC_BRIDGE = 0x0040;
public static final int ACC_VOLATILE = 0x0040;
public static final int ACC_VARARGS = 0x0080;
diff --git a/java/com/google/turbine/parse/Parser.java b/java/com/google/turbine/parse/Parser.java
index c528dc9..012e7e2 100644
--- a/java/com/google/turbine/parse/Parser.java
+++ b/java/com/google/turbine/parse/Parser.java
@@ -40,6 +40,13 @@
import com.google.turbine.tree.Tree.ImportDecl;
import com.google.turbine.tree.Tree.Kind;
import com.google.turbine.tree.Tree.MethDecl;
+import com.google.turbine.tree.Tree.ModDecl;
+import com.google.turbine.tree.Tree.ModDirective;
+import com.google.turbine.tree.Tree.ModExports;
+import com.google.turbine.tree.Tree.ModOpens;
+import com.google.turbine.tree.Tree.ModProvides;
+import com.google.turbine.tree.Tree.ModRequires;
+import com.google.turbine.tree.Tree.ModUses;
import com.google.turbine.tree.Tree.PkgDecl;
import com.google.turbine.tree.Tree.PrimTy;
import com.google.turbine.tree.Tree.TyDecl;
@@ -52,6 +59,7 @@
import java.util.Deque;
import java.util.EnumSet;
import java.util.List;
+import javax.annotation.CheckReturnValue;
import javax.annotation.Nullable;
/**
@@ -85,6 +93,7 @@
// and make it bug-compatible with javac:
// http://mail.openjdk.java.net/pipermail/compiler-dev/2013-August/006968.html
Optional<PkgDecl> pkg = Optional.absent();
+ Optional<ModDecl> mod = Optional.absent();
EnumSet<TurbineModifier> access = EnumSet.noneOf(TurbineModifier.class);
ImmutableList.Builder<ImportDecl> imports = ImmutableList.builder();
ImmutableList.Builder<TyDecl> decls = ImmutableList.builder();
@@ -165,11 +174,30 @@
break;
case EOF:
// TODO(cushon): check for dangling modifiers?
- return new CompUnit(position, pkg, imports.build(), decls.build(), lexer.source());
+ return new CompUnit(position, pkg, mod, imports.build(), decls.build(), lexer.source());
case SEMI:
// TODO(cushon): check for dangling modifiers?
next();
continue;
+ case IDENT:
+ {
+ String ident = lexer.stringValue();
+ if (access.isEmpty() && (ident.equals("module") || ident.equals("open"))) {
+ boolean open = false;
+ if (ident.equals("open")) {
+ ident = eatIdent();
+ open = true;
+ }
+ if (!ident.equals("module")) {
+ throw error(token);
+ }
+ next();
+ mod = Optional.of(moduleDeclaration(open, annos.build()));
+ annos = ImmutableList.builder();
+ break;
+ }
+ }
+ // fall through
default:
throw error(token);
}
@@ -259,6 +287,121 @@
TurbineTyKind.ENUM);
}
+ private ModDecl moduleDeclaration(boolean open, ImmutableList<Anno> annos) {
+ ImmutableList<String> moduleName = qualIdent();
+ eat(Token.LBRACE);
+ ImmutableList.Builder<ModDirective> directives = ImmutableList.builder();
+ OUTER:
+ while (true) {
+ switch (token) {
+ case IDENT:
+ {
+ String ident = lexer.stringValue();
+ next();
+ switch (ident) {
+ case "requires":
+ directives.add(moduleRequires());
+ break;
+ case "exports":
+ directives.add(moduleExports());
+ break;
+ case "opens":
+ directives.add(moduleOpens());
+ break;
+ case "uses":
+ directives.add(moduleUses());
+ break;
+ case "provides":
+ directives.add(moduleProvides());
+ break;
+ default: // fall out
+ }
+ break;
+ }
+ case RBRACE:
+ break OUTER;
+ default:
+ throw error(token);
+ }
+ }
+ eat(Token.RBRACE);
+ return new ModDecl(position, annos, open, moduleName, directives.build());
+ }
+
+ private ModRequires moduleRequires() {
+ int pos = position;
+ EnumSet<TurbineModifier> access = EnumSet.noneOf(TurbineModifier.class);
+ while (true) {
+ if (token == Token.IDENT && lexer.stringValue().equals("transitive")) {
+ next();
+ access.add(TurbineModifier.TRANSITIVE);
+ break;
+ }
+ if (token == Token.STATIC) {
+ next();
+ // TODO(cushon): note that this needs to lower to ACC_STATIC_PHASE, not ACC_STATIC
+ access.add(TurbineModifier.STATIC);
+ break;
+ }
+ break;
+ }
+ ImmutableList<String> moduleName = qualIdent();
+ eat(Token.SEMI);
+ return new ModRequires(pos, ImmutableSet.copyOf(access), moduleName);
+ }
+
+ private ModExports moduleExports() {
+ int pos = position;
+ ImmutableList<String> packageName = qualIdent();
+ ImmutableList.Builder<ImmutableList<String>> moduleNames = ImmutableList.builder();
+ if (lexer.stringValue().equals("to")) {
+ next();
+ do {
+ ImmutableList<String> moduleName = qualIdent();
+ moduleNames.add(moduleName);
+ } while (maybe(Token.COMMA));
+ }
+ eat(Token.SEMI);
+ return new ModExports(pos, packageName, moduleNames.build());
+ }
+
+ private ModOpens moduleOpens() {
+ int pos = position;
+ ImmutableList<String> packageName = qualIdent();
+ ImmutableList.Builder<ImmutableList<String>> moduleNames = ImmutableList.builder();
+ if (lexer.stringValue().equals("to")) {
+ next();
+ do {
+ ImmutableList<String> moduleName = qualIdent();
+ moduleNames.add(moduleName);
+ } while (maybe(Token.COMMA));
+ }
+ eat(Token.SEMI);
+ return new ModOpens(pos, packageName, moduleNames.build());
+ }
+
+ private ModUses moduleUses() {
+ int pos = position;
+ ImmutableList<String> uses = qualIdent();
+ eat(Token.SEMI);
+ return new ModUses(pos, uses);
+ }
+
+ private ModProvides moduleProvides() {
+ int pos = position;
+ ImmutableList<String> typeName = qualIdent();
+ if (!eatIdent().equals("with")) {
+ throw error(token);
+ }
+ ImmutableList.Builder<ImmutableList<String>> implNames = ImmutableList.builder();
+ do {
+ ImmutableList<String> implName = qualIdent();
+ implNames.add(implName);
+ } while (maybe(Token.COMMA));
+ eat(Token.SEMI);
+ return new ModProvides(pos, typeName, implNames.build());
+ }
+
private static final ImmutableSet<TurbineModifier> ENUM_CONSTANT_MODIFIERS =
ImmutableSet.of(
TurbineModifier.PUBLIC,
@@ -1181,6 +1324,7 @@
return false;
}
+ @CheckReturnValue
TurbineError error(Token token) {
switch (token) {
case IDENT:
@@ -1192,6 +1336,7 @@
}
}
+ @CheckReturnValue
private TurbineError error(ErrorKind kind, Object... args) {
return TurbineError.format(
lexer.source(),
diff --git a/java/com/google/turbine/tree/Pretty.java b/java/com/google/turbine/tree/Pretty.java
index 820126d..be677f3 100644
--- a/java/com/google/turbine/tree/Pretty.java
+++ b/java/com/google/turbine/tree/Pretty.java
@@ -22,6 +22,13 @@
import com.google.common.collect.ImmutableSet;
import com.google.turbine.tree.Tree.Anno;
import com.google.turbine.tree.Tree.ClassLiteral;
+import com.google.turbine.tree.Tree.ModDecl;
+import com.google.turbine.tree.Tree.ModDirective;
+import com.google.turbine.tree.Tree.ModExports;
+import com.google.turbine.tree.Tree.ModOpens;
+import com.google.turbine.tree.Tree.ModProvides;
+import com.google.turbine.tree.Tree.ModRequires;
+import com.google.turbine.tree.Tree.ModUses;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
@@ -237,6 +244,10 @@
for (Tree.ImportDecl i : compUnit.imports()) {
i.accept(this, null);
}
+ if (compUnit.mod().isPresent()) {
+ printLine();
+ compUnit.mod().get().accept(this, null);
+ }
for (Tree.TyDecl decl : compUnit.decls()) {
printLine();
decl.accept(this, null);
@@ -472,6 +483,7 @@
case NATIVE:
case TRANSIENT:
case DEFAULT:
+ case TRANSITIVE:
append(mod.toString()).append(' ');
break;
case ACC_SUPER:
@@ -515,4 +527,109 @@
append("package ").append(Joiner.on('.').join(pkgDecl.name())).append(';');
return null;
}
+
+ @Override
+ public Void visitModDecl(ModDecl modDecl, Void input) {
+ for (Tree.Anno anno : modDecl.annos()) {
+ anno.accept(this, null);
+ printLine();
+ }
+ if (modDecl.open()) {
+ append("open ");
+ }
+ append("module ").append(Joiner.on('.').join(modDecl.moduleName())).append(" {");
+ indent++;
+ append('\n');
+ for (ModDirective directive : modDecl.directives()) {
+ directive.accept(this, null);
+ }
+ indent--;
+ append("}\n");
+ return null;
+ }
+
+ @Override
+ public Void visitModRequires(ModRequires modRequires, Void input) {
+ append("requires ");
+ printModifiers(modRequires.mods());
+ append(Joiner.on('.').join(modRequires.moduleName()));
+ append(";");
+ append('\n');
+ return null;
+ }
+
+ @Override
+ public Void visitModExports(ModExports modExports, Void input) {
+ append("exports ");
+ append(Joiner.on('.').join(modExports.packageName()));
+ if (!modExports.moduleNames().isEmpty()) {
+ append(" to").append('\n');
+ indent += 2;
+ boolean first = true;
+ for (ImmutableList<String> moduleName : modExports.moduleNames()) {
+ if (!first) {
+ append(',').append('\n');
+ }
+ append(Joiner.on('.').join(moduleName));
+ first = false;
+ }
+ indent -= 2;
+ }
+ append(";");
+ append('\n');
+ return null;
+ }
+
+ @Override
+ public Void visitModOpens(ModOpens modOpens, Void input) {
+ append("opens ");
+ append(Joiner.on('.').join(modOpens.packageName()));
+ if (!modOpens.moduleNames().isEmpty()) {
+ append(" to").append('\n');
+ indent += 2;
+ boolean first = true;
+ for (ImmutableList<String> moduleName : modOpens.moduleNames()) {
+ if (!first) {
+ append(',').append('\n');
+ }
+ append(Joiner.on('.').join(moduleName));
+ first = false;
+ }
+ indent -= 2;
+ }
+ append(";");
+ append('\n');
+ return null;
+ }
+
+ @Override
+ public Void visitModUses(ModUses modUses, Void input) {
+ append("uses ");
+ append(Joiner.on('.').join(modUses.typeName()));
+ append(";");
+ append('\n');
+ return null;
+ }
+
+ @Override
+ public Void visitModProvides(ModProvides modProvides, Void input) {
+ append("provides ");
+ append(Joiner.on('.').join(modProvides.typeName()));
+ if (!modProvides.implNames().isEmpty()) {
+ append(" with").append('\n');
+ indent += 2;
+ boolean first = true;
+ for (ImmutableList<String> implName : modProvides.implNames()) {
+ if (!first) {
+ append(',').append('\n');
+ }
+ append(Joiner.on('.').join(implName));
+ first = false;
+ }
+ indent -= 2;
+ }
+ append(";");
+ append('\n');
+ return null;
+ }
}
diff --git a/java/com/google/turbine/tree/Tree.java b/java/com/google/turbine/tree/Tree.java
index 6f46df5..16e9341 100644
--- a/java/com/google/turbine/tree/Tree.java
+++ b/java/com/google/turbine/tree/Tree.java
@@ -71,7 +71,13 @@
ANNO_EXPR,
TY_DECL,
TY_PARAM,
- PKG_DECL
+ PKG_DECL,
+ MOD_DECL,
+ MOD_REQUIRES,
+ MOD_EXPORTS,
+ MOD_OPENS,
+ MOD_USES,
+ MOD_PROVIDES
}
/** A type use. */
@@ -524,6 +530,7 @@
/** A JLS 7.3 compilation unit. */
public static class CompUnit extends Tree {
private final Optional<PkgDecl> pkg;
+ private final Optional<ModDecl> mod;
private final ImmutableList<ImportDecl> imports;
private final ImmutableList<TyDecl> decls;
private final SourceFile source;
@@ -531,11 +538,13 @@
public CompUnit(
int position,
Optional<PkgDecl> pkg,
+ Optional<ModDecl> mod,
ImmutableList<ImportDecl> imports,
ImmutableList<TyDecl> decls,
SourceFile source) {
super(position);
this.pkg = pkg;
+ this.mod = mod;
this.imports = imports;
this.decls = decls;
this.source = source;
@@ -555,6 +564,10 @@
return pkg;
}
+ public Optional<ModDecl> mod() {
+ return mod;
+ }
+
public ImmutableList<ImportDecl> imports() {
return imports;
}
@@ -936,6 +949,257 @@
}
}
+ /** A JLS 7.7 module declaration. */
+ public static class ModDecl extends Tree {
+
+ private final ImmutableList<Anno> annos;
+ private final boolean open;
+ private final ImmutableList<String> moduleName;
+ private final ImmutableList<ModDirective> directives;
+
+ public ModDecl(
+ int position,
+ ImmutableList<Anno> annos,
+ boolean open,
+ ImmutableList<String> moduleName,
+ ImmutableList<ModDirective> directives) {
+ super(position);
+ this.annos = annos;
+ this.open = open;
+ this.moduleName = moduleName;
+ this.directives = directives;
+ }
+
+ public boolean open() {
+ return open;
+ }
+
+ public ImmutableList<Anno> annos() {
+ return annos;
+ }
+
+ public ImmutableList<String> moduleName() {
+ return moduleName;
+ }
+
+ public ImmutableList<ModDirective> directives() {
+ return directives;
+ }
+
+ @Override
+ public Kind kind() {
+ return Kind.MOD_DECL;
+ }
+
+ @Override
+ public <I, O> O accept(Visitor<I, O> visitor, I input) {
+ return visitor.visitModDecl(this, input);
+ }
+ }
+
+ /** A kind of module directive. */
+ public abstract static class ModDirective extends Tree {
+
+ /** A module directive kind. */
+ public enum DirectiveKind {
+ REQUIRES,
+ EXPORTS,
+ OPENS,
+ USES,
+ PROVIDES
+ }
+
+ public abstract DirectiveKind directiveKind();
+
+ protected ModDirective(int position) {
+ super(position);
+ }
+ }
+
+ /** A JLS 7.7.1 module requires directive. */
+ public static class ModRequires extends ModDirective {
+
+ private final ImmutableSet<TurbineModifier> mods;
+ private final ImmutableList<String> moduleName;
+
+ @Override
+ public Kind kind() {
+ return Kind.MOD_REQUIRES;
+ }
+
+ @Override
+ public <I, O> O accept(Visitor<I, O> visitor, I input) {
+ return visitor.visitModRequires(this, input);
+ }
+
+ public ModRequires(
+ int position, ImmutableSet<TurbineModifier> mods, ImmutableList<String> moduleName) {
+ super(position);
+ this.mods = mods;
+ this.moduleName = moduleName;
+ }
+
+ public ImmutableSet<TurbineModifier> mods() {
+ return mods;
+ }
+
+ public ImmutableList<String> moduleName() {
+ return moduleName;
+ }
+
+ @Override
+ public DirectiveKind directiveKind() {
+ return DirectiveKind.REQUIRES;
+ }
+ }
+
+ /** A JLS 7.7.2 module exports directive. */
+ public static class ModExports extends ModDirective {
+
+ private final ImmutableList<String> packageName;
+ private final ImmutableList<ImmutableList<String>> moduleNames;
+
+ @Override
+ public Kind kind() {
+ return Kind.MOD_EXPORTS;
+ }
+
+ @Override
+ public <I, O> O accept(Visitor<I, O> visitor, I input) {
+ return visitor.visitModExports(this, input);
+ }
+
+ public ModExports(
+ int position,
+ ImmutableList<String> packageName,
+ ImmutableList<ImmutableList<String>> moduleNames) {
+ super(position);
+ this.packageName = packageName;
+ this.moduleNames = moduleNames;
+ }
+
+ public ImmutableList<String> packageName() {
+ return packageName;
+ }
+
+ public ImmutableList<ImmutableList<String>> moduleNames() {
+ return moduleNames;
+ }
+
+ @Override
+ public DirectiveKind directiveKind() {
+ return DirectiveKind.EXPORTS;
+ }
+ }
+
+ /** A JLS 7.7.2 module opens directive. */
+ public static class ModOpens extends ModDirective {
+
+ private final ImmutableList<String> packageName;
+ private final ImmutableList<ImmutableList<String>> moduleNames;
+
+ public ModOpens(
+ int position,
+ ImmutableList<String> packageName,
+ ImmutableList<ImmutableList<String>> moduleNames) {
+ super(position);
+ this.packageName = packageName;
+ this.moduleNames = moduleNames;
+ }
+
+ public ImmutableList<String> packageName() {
+ return packageName;
+ }
+
+ public ImmutableList<ImmutableList<String>> moduleNames() {
+ return moduleNames;
+ }
+
+ @Override
+ public Kind kind() {
+ return Kind.MOD_OPENS;
+ }
+
+ @Override
+ public <I, O> O accept(Visitor<I, O> visitor, I input) {
+ return visitor.visitModOpens(this, input);
+ }
+
+ @Override
+ public DirectiveKind directiveKind() {
+ return DirectiveKind.OPENS;
+ }
+ }
+
+ /** A JLS 7.7.3 module uses directive. */
+ public static class ModUses extends ModDirective {
+
+ private final ImmutableList<String> typeName;
+
+ public ModUses(int position, ImmutableList<String> typeName) {
+ super(position);
+ this.typeName = typeName;
+ }
+
+ public ImmutableList<String> typeName() {
+ return typeName;
+ }
+
+ @Override
+ public Kind kind() {
+ return Kind.MOD_USES;
+ }
+
+ @Override
+ public <I, O> O accept(Visitor<I, O> visitor, I input) {
+ return visitor.visitModUses(this, input);
+ }
+
+ @Override
+ public DirectiveKind directiveKind() {
+ return DirectiveKind.USES;
+ }
+ }
+
+ /** A JLS 7.7.4 module uses directive. */
+ public static class ModProvides extends ModDirective {
+
+ private final ImmutableList<String> typeName;
+ private final ImmutableList<ImmutableList<String>> implNames;
+
+ public ModProvides(
+ int position,
+ ImmutableList<String> typeName,
+ ImmutableList<ImmutableList<String>> implNames) {
+ super(position);
+ this.typeName = typeName;
+ this.implNames = implNames;
+ }
+
+ public ImmutableList<String> typeName() {
+ return typeName;
+ }
+
+ public ImmutableList<ImmutableList<String>> implNames() {
+ return implNames;
+ }
+
+ @Override
+ public Kind kind() {
+ return Kind.MOD_PROVIDES;
+ }
+
+ @Override
+ public <I, O> O accept(Visitor<I, O> visitor, I input) {
+ return visitor.visitModProvides(this, input);
+ }
+
+ @Override
+ public DirectiveKind directiveKind() {
+ return DirectiveKind.PROVIDES;
+ }
+ }
+
/** A visitor for {@link Tree}s. */
public interface Visitor<I, O> {
O visitWildTy(WildTy visitor, I input);
@@ -981,5 +1245,17 @@
O visitTyParam(TyParam tyParam, I input);
O visitPkgDecl(PkgDecl pkgDecl, I input);
+
+ O visitModDecl(ModDecl modDecl, I input);
+
+ O visitModRequires(ModRequires modRequires, I input);
+
+ O visitModExports(ModExports modExports, I input);
+
+ O visitModOpens(ModOpens modOpens, I input);
+
+ O visitModUses(ModUses modUses, I input);
+
+ O visitModProvides(ModProvides modProvides, I input);
}
}
diff --git a/java/com/google/turbine/tree/TurbineModifier.java b/java/com/google/turbine/tree/TurbineModifier.java
index 3d0e60d..35dc11c 100644
--- a/java/com/google/turbine/tree/TurbineModifier.java
+++ b/java/com/google/turbine/tree/TurbineModifier.java
@@ -44,7 +44,8 @@
ACC_ANNOTATION(TurbineFlag.ACC_ANNOTATION),
ACC_SYNTHETIC(TurbineFlag.ACC_SYNTHETIC),
ACC_BRIDGE(TurbineFlag.ACC_BRIDGE),
- DEFAULT(TurbineFlag.ACC_DEFAULT);
+ DEFAULT(TurbineFlag.ACC_DEFAULT),
+ TRANSITIVE(TurbineFlag.ACC_TRANSITIVE);
private final int flag;
diff --git a/javatests/com/google/turbine/parse/ParserIntegrationTest.java b/javatests/com/google/turbine/parse/ParserIntegrationTest.java
index d856cb3..2503553 100644
--- a/javatests/com/google/turbine/parse/ParserIntegrationTest.java
+++ b/javatests/com/google/turbine/parse/ParserIntegrationTest.java
@@ -21,6 +21,7 @@
import static java.nio.charset.StandardCharsets.UTF_8;
import com.google.common.base.Function;
+import com.google.common.base.Splitter;
import com.google.common.collect.Iterables;
import com.google.common.io.CharStreams;
import com.google.turbine.tree.Tree;
@@ -28,6 +29,7 @@
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.Arrays;
+import java.util.List;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
@@ -72,6 +74,7 @@
"packinfo1.input",
"weirdstring.input",
"type_annotations.input",
+ "module-info.input",
};
return Iterables.transform(
Arrays.asList(tests),
@@ -97,9 +100,9 @@
try (InputStreamReader in = new InputStreamReader(stream, UTF_8)) {
result = CharStreams.toString(in);
}
- String[] pieces = result.split("===+");
- String input = pieces[0].trim();
- String expected = pieces.length > 1 ? pieces[1].trim() : input;
+ List<String> pieces = Splitter.onPattern("===+").splitToList(result);
+ String input = pieces.get(0).trim();
+ String expected = pieces.size() > 1 ? pieces.get(1).trim() : input;
Tree.CompUnit unit = Parser.parse(input);
assertThat(unit.toString().trim()).isEqualTo(expected);
}
diff --git a/javatests/com/google/turbine/parse/testdata/module-info.input b/javatests/com/google/turbine/parse/testdata/module-info.input
new file mode 100644
index 0000000..892a3b1
--- /dev/null
+++ b/javatests/com/google/turbine/parse/testdata/module-info.input
@@ -0,0 +1,29 @@
+import a.A;
+import a.B;
+import com.google.Foo;
+import com.google.Baz;
+
+@A
+@B
+module com.google.m {
+ requires java.compiler;
+ requires transitive jdk.compiler;
+ requires static java.base;
+ exports com.google.p1;
+ exports com.google.p2 to
+ java.base;
+ exports com.google.p3 to
+ java.base,
+ java.compiler;
+ opens com.google.p1;
+ opens com.google.p2 to
+ java.base;
+ opens com.google.p3 to
+ java.base,
+ java.compiler;
+ uses Foo;
+ uses com.google.Bar;
+ provides com.google.Baz with
+ Foo,
+ com.google.Bar;
+}