Convert thrift parser to Kotlin (#175)
This commit is contained in:
Родитель
c2f4a2ede2
Коммит
7dd5f8f77e
23
build.gradle
23
build.gradle
|
@ -69,6 +69,7 @@ buildscript {
|
|||
dependencies {
|
||||
classpath 'net.ltgt.gradle:gradle-errorprone-plugin:0.0.14'
|
||||
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:1.2.41"
|
||||
classpath 'org.jetbrains.dokka:dokka-gradle-plugin:0.9.17'
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -102,22 +103,6 @@ subprojects { sp ->
|
|||
options.incremental = true
|
||||
}
|
||||
|
||||
task javadocJar(type: Jar, dependsOn: javadoc) {
|
||||
from 'build/docs/javadoc'
|
||||
classifier = 'javadoc'
|
||||
}
|
||||
|
||||
task sourcesJar(type: Jar) {
|
||||
from sourceSets.main.allSource
|
||||
classifier = 'sources'
|
||||
}
|
||||
|
||||
artifacts {
|
||||
archives jar
|
||||
archives javadocJar
|
||||
archives sourcesJar
|
||||
}
|
||||
|
||||
test {
|
||||
testLogging {
|
||||
events "failed"
|
||||
|
@ -139,10 +124,10 @@ task codeCoverageReport(type: JacocoReport) {
|
|||
}
|
||||
|
||||
reports {
|
||||
xml.enabled true
|
||||
xml.enabled = true
|
||||
xml.destination file("$buildDir/reports/jacoco/report.xml")
|
||||
html.enabled true
|
||||
csv.enabled false
|
||||
html.enabled = true
|
||||
csv.enabled = false
|
||||
}
|
||||
|
||||
afterEvaluate {
|
||||
|
|
|
@ -82,4 +82,44 @@ afterEvaluate { project ->
|
|||
required { isRelease && gradle.taskGraph.hasTask("uploadArchives") }
|
||||
sign configurations.archives
|
||||
}
|
||||
|
||||
if (project.plugins.hasPlugin('org.jetbrains.kotlin') || project.plugins.hasPlugin('kotlin')) {
|
||||
apply plugin: 'org.jetbrains.dokka'
|
||||
|
||||
dokka {
|
||||
includes = ['Module.md']
|
||||
|
||||
linkMapping {
|
||||
dir = project.file("src/main/kotlin")
|
||||
url = "https://github.com/Microsoft/thrifty/tree/master/${project.name}/src/main/kotlin"
|
||||
suffix = "#L"
|
||||
}
|
||||
|
||||
outputFormat = 'html'
|
||||
outputDirectory = "${project.buildDir}/docs/kdoc"
|
||||
}
|
||||
}
|
||||
|
||||
task javadocJar(type: Jar) {
|
||||
if (project.plugins.hasPlugin('org.jetbrains.dokka')) {
|
||||
dependsOn dokka
|
||||
from dokka.outputDirectory
|
||||
} else {
|
||||
dependsOn javadoc
|
||||
from 'build/docs/javadoc'
|
||||
}
|
||||
|
||||
classifier = 'javadoc'
|
||||
}
|
||||
|
||||
task sourcesJar(type: Jar) {
|
||||
from sourceSets.main.allSource
|
||||
classifier = 'sources'
|
||||
}
|
||||
|
||||
artifacts {
|
||||
archives jar
|
||||
archives javadocJar
|
||||
archives sourcesJar
|
||||
}
|
||||
}
|
||||
|
|
|
@ -65,9 +65,8 @@ internal class ConstantBuilder(
|
|||
initializer.addStatement("\$L = \$L", name, item)
|
||||
}
|
||||
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
override fun visitList(listType: ListType) {
|
||||
val list = value.value() as List<ConstValueElement>
|
||||
val list = value.getAsList()
|
||||
val elementType = listType.elementType().trueType
|
||||
val elementTypeName = typeResolver.getJavaClass(elementType)
|
||||
val genericName = ParameterizedTypeName.get(TypeNames.LIST, elementTypeName)
|
||||
|
@ -75,9 +74,8 @@ internal class ConstantBuilder(
|
|||
generateSingleElementCollection(elementType, genericName, listImplName, list)
|
||||
}
|
||||
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
override fun visitSet(setType: SetType) {
|
||||
val set = value.value() as List<ConstValueElement>
|
||||
val set = value.getAsList()
|
||||
val elementType = setType.elementType().trueType
|
||||
val elementTypeName = typeResolver.getJavaClass(elementType)
|
||||
val genericName = ParameterizedTypeName.get(TypeNames.SET, elementTypeName)
|
||||
|
@ -103,9 +101,8 @@ internal class ConstantBuilder(
|
|||
}
|
||||
}
|
||||
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
override fun visitMap(mapType: MapType) {
|
||||
val map = value.value() as Map<ConstValueElement, ConstValueElement>
|
||||
val map = value.getAsMap()
|
||||
val keyType = mapType.keyType().trueType
|
||||
val valueType = mapType.valueType().trueType
|
||||
|
||||
|
@ -167,22 +164,22 @@ internal class ConstantBuilder(
|
|||
|
||||
private fun getNumberLiteral(element: ConstValueElement): Any {
|
||||
if (!element.isInt) {
|
||||
throw AssertionError("Expected an int or double, got: " + element.kind())
|
||||
throw AssertionError("Expected an int or double, got: " + element.kind)
|
||||
}
|
||||
|
||||
return if (element.thriftText().startsWith("0x") || element.thriftText().startsWith("0X")) {
|
||||
element.thriftText()
|
||||
return if (element.thriftText.startsWith("0x") || element.thriftText.startsWith("0X")) {
|
||||
element.thriftText
|
||||
} else {
|
||||
element.asInt
|
||||
element.getAsInt()
|
||||
}
|
||||
}
|
||||
|
||||
override fun visitBool(boolType: BuiltinType): CodeBlock {
|
||||
val name: String
|
||||
if (value.isIdentifier && ("true" == value.asString || "false" == value.asString)) {
|
||||
name = if ("true" == value.value()) "true" else "false"
|
||||
if (value.isIdentifier && ("true" == value.getAsString() || "false" == value.getAsString())) {
|
||||
name = if ("true" == value.getAsString()) "true" else "false"
|
||||
} else if (value.isInt) {
|
||||
name = if (value.value() as Long == 0L) "false" else "true"
|
||||
name = if (value.getAsLong() == 0L) "false" else "true"
|
||||
} else {
|
||||
return constantOrError("Invalid boolean constant")
|
||||
}
|
||||
|
@ -224,7 +221,7 @@ internal class ConstantBuilder(
|
|||
|
||||
override fun visitDouble(doubleType: BuiltinType): CodeBlock {
|
||||
return if (value.isInt || value.isDouble) {
|
||||
CodeBlock.builder().add("(double) \$L", value.asDouble).build()
|
||||
CodeBlock.builder().add("(double) \$L", value.getAsDouble()).build()
|
||||
} else {
|
||||
constantOrError("Invalid double constant")
|
||||
}
|
||||
|
@ -232,7 +229,7 @@ internal class ConstantBuilder(
|
|||
|
||||
override fun visitString(stringType: BuiltinType): CodeBlock {
|
||||
return if (value.isString) {
|
||||
CodeBlock.builder().add("\$S", value.asString).build()
|
||||
CodeBlock.builder().add("\$S", value.getAsString()).build()
|
||||
} else {
|
||||
constantOrError("Invalid string constant")
|
||||
}
|
||||
|
@ -249,19 +246,19 @@ internal class ConstantBuilder(
|
|||
override fun visitEnum(enumType: EnumType): CodeBlock {
|
||||
// TODO(ben): Figure out how to handle const references
|
||||
try {
|
||||
val member = when (value.kind()) {
|
||||
val member = when (value.kind) {
|
||||
ConstValueElement.Kind.INTEGER ->
|
||||
enumType.findMemberById(value.asInt)
|
||||
enumType.findMemberById(value.getAsInt())
|
||||
|
||||
ConstValueElement.Kind.IDENTIFIER -> {
|
||||
// Remove the enum name prefix, assuming it is present
|
||||
val id = value.asString.split(".").last()
|
||||
val id = value.getAsString().split(".").last()
|
||||
|
||||
enumType.findMemberByName(id)
|
||||
}
|
||||
|
||||
else -> throw AssertionError(
|
||||
"Constant value kind " + value.kind() + " is not possibly an enum; validation bug")
|
||||
"Constant value kind ${value.kind} is not possibly an enum; validation bug")
|
||||
}
|
||||
|
||||
return CodeBlock.builder()
|
||||
|
@ -270,13 +267,13 @@ internal class ConstantBuilder(
|
|||
|
||||
} catch (e: NoSuchElementException) {
|
||||
throw IllegalStateException(
|
||||
"No enum member in " + enumType.name() + " with value " + value.value())
|
||||
"No enum member in ${enumType.name()} with value ${value.value}")
|
||||
}
|
||||
}
|
||||
|
||||
override fun visitList(listType: ListType): CodeBlock {
|
||||
if (value.isList) {
|
||||
if (value.asList.isEmpty()) {
|
||||
if (value.getAsList().isEmpty()) {
|
||||
val elementType = typeResolver.getJavaClass(listType.elementType())
|
||||
return CodeBlock.builder()
|
||||
.add("\$T.<\$T>emptyList()", TypeNames.COLLECTIONS, elementType)
|
||||
|
@ -290,7 +287,7 @@ internal class ConstantBuilder(
|
|||
|
||||
override fun visitSet(setType: SetType): CodeBlock {
|
||||
if (value.isList) { // not a typo; ConstantValueElement.Kind.LIST covers lists and sets.
|
||||
if (value.asList.isEmpty()) {
|
||||
if (value.getAsList().isEmpty()) {
|
||||
val elementType = typeResolver.getJavaClass(setType.elementType())
|
||||
return CodeBlock.builder()
|
||||
.add("\$T.<\$T>emptySet()", TypeNames.COLLECTIONS, elementType)
|
||||
|
@ -304,7 +301,7 @@ internal class ConstantBuilder(
|
|||
|
||||
override fun visitMap(mapType: MapType): CodeBlock {
|
||||
if (value.isMap) {
|
||||
if (value.asMap.isEmpty()) {
|
||||
if (value.getAsMap().isEmpty()) {
|
||||
val keyType = typeResolver.getJavaClass(mapType.keyType())
|
||||
val valueType = typeResolver.getJavaClass(mapType.valueType())
|
||||
return CodeBlock.builder()
|
||||
|
@ -339,7 +336,7 @@ internal class ConstantBuilder(
|
|||
}
|
||||
|
||||
private fun constantOrError(error: String): CodeBlock {
|
||||
val message = "$error: ${value.value()} + at ${value.location()}"
|
||||
val message = "$error: ${value.value} + at ${value.location}"
|
||||
|
||||
if (!value.isIdentifier) {
|
||||
throw IllegalStateException(message)
|
||||
|
@ -347,7 +344,7 @@ internal class ConstantBuilder(
|
|||
|
||||
val expectedType = type.trueType
|
||||
|
||||
var name = value.asString
|
||||
var name = value.getAsString()
|
||||
val ix = name.indexOf('.')
|
||||
var expectedProgram: String? = null
|
||||
if (ix != -1) {
|
||||
|
|
|
@ -888,7 +888,7 @@ class ThriftyCodeGenerator {
|
|||
}
|
||||
|
||||
override fun visitList(listType: ListType) {
|
||||
if (constant.value().asList.isEmpty()) {
|
||||
if (constant.value().getAsList().isEmpty()) {
|
||||
field.initializer("\$T.emptyList()", TypeNames.COLLECTIONS)
|
||||
} else {
|
||||
initCollection("list", "unmodifiableList")
|
||||
|
@ -896,7 +896,7 @@ class ThriftyCodeGenerator {
|
|||
}
|
||||
|
||||
override fun visitSet(setType: SetType) {
|
||||
if (constant.value().asList.isEmpty()) {
|
||||
if (constant.value().getAsList().isEmpty()) {
|
||||
field.initializer("\$T.emptySet()", TypeNames.COLLECTIONS)
|
||||
} else {
|
||||
initCollection("set", "unmodifiableSet")
|
||||
|
@ -904,7 +904,7 @@ class ThriftyCodeGenerator {
|
|||
}
|
||||
|
||||
override fun visitMap(mapType: MapType) {
|
||||
if (constant.value().asMap.isEmpty()) {
|
||||
if (constant.value().getAsMap().isEmpty()) {
|
||||
field.initializer("\$T.emptyMap()", TypeNames.COLLECTIONS)
|
||||
} else {
|
||||
initCollection("map", "unmodifiableMap")
|
||||
|
|
|
@ -0,0 +1,9 @@
|
|||
# Module thrifty-schema
|
||||
|
||||
|
||||
|
||||
# Package com.microsoft.thrifty.schema.parser
|
||||
|
||||
Contains a Thrift parser implementation.
|
||||
|
||||
The output of the parser is an untyped AST - types are not validated, typedefs are unresolved, etc.
|
|
@ -19,7 +19,7 @@
|
|||
* See the Apache Version 2.0 License for specific language governing permissions and limitations under the License.
|
||||
*/
|
||||
plugins {
|
||||
id 'net.ltgt.apt' version '0.15'
|
||||
id 'kotlin'
|
||||
id 'antlr'
|
||||
}
|
||||
|
||||
|
@ -39,15 +39,22 @@ dependencies {
|
|||
|
||||
implementation libraries.okio
|
||||
|
||||
api libraries.kotlin
|
||||
|
||||
api 'com.google.code.findbugs:jsr305:3.0.1'
|
||||
api libraries.guava
|
||||
|
||||
api 'com.google.auto.value:auto-value-annotations:1.6'
|
||||
annotationProcessor 'com.google.auto.value:auto-value:1.6'
|
||||
|
||||
testImplementation libraries.testing
|
||||
}
|
||||
|
||||
compileTestJava {
|
||||
options.compilerArgs += [ '-Xep:ImmutableModification:OFF', '-Xlint:deprecation,unchecked' ]
|
||||
}
|
||||
|
||||
// For some reason, Kotlin compilation is being run prior to antlr by default.
|
||||
tasks['compileKotlin'].dependsOn('generateGrammarSource')
|
||||
tasks['compileTestKotlin'].dependsOn('generateGrammarSource')
|
||||
tasks['compileTestKotlin'].dependsOn('generateTestGrammarSource')
|
||||
tasks['compileJava'].dependsOn('generateGrammarSource')
|
||||
tasks['compileTestJava'].dependsOn('generateGrammarSource')
|
||||
tasks['compileTestJava'].dependsOn('generateTestGrammarSource')
|
||||
|
|
|
@ -20,11 +20,11 @@
|
|||
*/
|
||||
package com.microsoft.thrifty.schema;
|
||||
|
||||
import com.google.auto.value.AutoValue;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
public class ErrorReporter {
|
||||
private boolean hasError = false;
|
||||
|
@ -70,14 +70,46 @@ public class ErrorReporter {
|
|||
return builder.build();
|
||||
}
|
||||
|
||||
@AutoValue
|
||||
abstract static class Report {
|
||||
public abstract Level level();
|
||||
public abstract Location location();
|
||||
public abstract String message();
|
||||
static class Report {
|
||||
private final Level level;
|
||||
private final Location location;
|
||||
private final String message;
|
||||
|
||||
public Level level() {
|
||||
return level;
|
||||
}
|
||||
|
||||
public Location location() {
|
||||
return location;
|
||||
}
|
||||
|
||||
public String message() {
|
||||
return message;
|
||||
}
|
||||
|
||||
private Report(Level level, Location location, String message) {
|
||||
this.level = level;
|
||||
this.location = location;
|
||||
this.message = message;
|
||||
}
|
||||
|
||||
static Report create(Level level, Location location, String message) {
|
||||
return new AutoValue_ErrorReporter_Report(level, location, message);
|
||||
return new Report(level, location, message);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
Report report = (Report) o;
|
||||
return level == report.level
|
||||
&& Objects.equals(location, report.location)
|
||||
&& Objects.equals(message, report.message);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(level, location, message);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -387,7 +387,7 @@ class Linker {
|
|||
@Nonnull
|
||||
ThriftType resolveType(TypeElement type) {
|
||||
AnnotationElement annotationElement = type.annotations();
|
||||
ImmutableMap<String, String> annotations = annotationElement != null
|
||||
Map<String, String> annotations = annotationElement != null
|
||||
? annotationElement.values()
|
||||
: ImmutableMap.of();
|
||||
|
||||
|
|
|
@ -21,7 +21,6 @@
|
|||
package com.microsoft.thrifty.schema;
|
||||
|
||||
import com.google.common.base.Preconditions;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.io.Closeables;
|
||||
import com.microsoft.thrifty.schema.parser.IncludeElement;
|
||||
import com.microsoft.thrifty.schema.parser.ThriftFileElement;
|
||||
|
@ -261,7 +260,7 @@ public final class Loader {
|
|||
|
||||
loadedFiles.put(file.normalize().toAbsolutePath(), element);
|
||||
|
||||
ImmutableList<IncludeElement> includes = element.includes();
|
||||
List<IncludeElement> includes = element.includes();
|
||||
if (includes.size() > 0) {
|
||||
includePaths.addFirst(dir);
|
||||
for (IncludeElement include : includes) {
|
||||
|
|
|
@ -1,56 +0,0 @@
|
|||
/*
|
||||
* Thrifty
|
||||
*
|
||||
* Copyright (c) Microsoft Corporation
|
||||
*
|
||||
* All rights reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the License);
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* THIS CODE IS PROVIDED ON AN *AS IS* BASIS, WITHOUT WARRANTIES OR
|
||||
* CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING
|
||||
* WITHOUT LIMITATION ANY IMPLIED WARRANTIES OR CONDITIONS OF TITLE,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE, MERCHANTABLITY OR NON-INFRINGEMENT.
|
||||
*
|
||||
* See the Apache Version 2.0 License for specific language governing permissions and limitations under the License.
|
||||
*/
|
||||
package com.microsoft.thrifty.schema.parser;
|
||||
|
||||
import com.google.auto.value.AutoValue;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.microsoft.thrifty.schema.Location;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.Map;
|
||||
|
||||
@AutoValue
|
||||
public abstract class AnnotationElement {
|
||||
public abstract Location location();
|
||||
public abstract ImmutableMap<String, String> values();
|
||||
|
||||
@Nullable
|
||||
public String get(@Nonnull String name) {
|
||||
return values().get(name);
|
||||
}
|
||||
|
||||
public boolean containsKey(@Nonnull String name) {
|
||||
return values().containsKey(name);
|
||||
}
|
||||
|
||||
public boolean isEmpty() {
|
||||
return values().isEmpty();
|
||||
}
|
||||
|
||||
public int size() {
|
||||
return values().size();
|
||||
}
|
||||
|
||||
public static AnnotationElement create(Location location, Map<String, String> values) {
|
||||
return new AutoValue_AnnotationElement(location, ImmutableMap.copyOf(values));
|
||||
}
|
||||
}
|
|
@ -1,57 +0,0 @@
|
|||
/*
|
||||
* Thrifty
|
||||
*
|
||||
* Copyright (c) Microsoft Corporation
|
||||
*
|
||||
* All rights reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the License);
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* THIS CODE IS PROVIDED ON AN *AS IS* BASIS, WITHOUT WARRANTIES OR
|
||||
* CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING
|
||||
* WITHOUT LIMITATION ANY IMPLIED WARRANTIES OR CONDITIONS OF TITLE,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE, MERCHANTABLITY OR NON-INFRINGEMENT.
|
||||
*
|
||||
* See the Apache Version 2.0 License for specific language governing permissions and limitations under the License.
|
||||
*/
|
||||
package com.microsoft.thrifty.schema.parser;
|
||||
|
||||
import com.google.auto.value.AutoValue;
|
||||
import com.microsoft.thrifty.schema.Location;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
@AutoValue
|
||||
public abstract class ConstElement {
|
||||
public abstract Location location();
|
||||
public abstract String documentation();
|
||||
public abstract TypeElement type();
|
||||
public abstract String name();
|
||||
public abstract UUID uuid();
|
||||
public abstract ConstValueElement value();
|
||||
|
||||
public static Builder builder(Location location) {
|
||||
return new AutoValue_ConstElement.Builder()
|
||||
.location(location)
|
||||
.documentation("")
|
||||
.uuid(ThriftyParserPlugins.createUUID());
|
||||
}
|
||||
|
||||
ConstElement() { }
|
||||
|
||||
@AutoValue.Builder
|
||||
public interface Builder {
|
||||
Builder location(Location location);
|
||||
Builder documentation(String documentation);
|
||||
Builder type(TypeElement type);
|
||||
Builder name(String name);
|
||||
Builder uuid(UUID uuid);
|
||||
Builder value(ConstValueElement value);
|
||||
|
||||
ConstElement build();
|
||||
}
|
||||
}
|
|
@ -1,149 +0,0 @@
|
|||
/*
|
||||
* Thrifty
|
||||
*
|
||||
* Copyright (c) Microsoft Corporation
|
||||
*
|
||||
* All rights reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the License);
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* THIS CODE IS PROVIDED ON AN *AS IS* BASIS, WITHOUT WARRANTIES OR
|
||||
* CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING
|
||||
* WITHOUT LIMITATION ANY IMPLIED WARRANTIES OR CONDITIONS OF TITLE,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE, MERCHANTABLITY OR NON-INFRINGEMENT.
|
||||
*
|
||||
* See the Apache Version 2.0 License for specific language governing permissions and limitations under the License.
|
||||
*/
|
||||
package com.microsoft.thrifty.schema.parser;
|
||||
|
||||
import com.google.auto.value.AutoValue;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.microsoft.thrifty.schema.Location;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
@AutoValue
|
||||
public abstract class ConstValueElement {
|
||||
public enum Kind {
|
||||
INTEGER,
|
||||
DOUBLE,
|
||||
STRING,
|
||||
IDENTIFIER,
|
||||
LIST,
|
||||
MAP,
|
||||
}
|
||||
|
||||
public abstract Location location();
|
||||
public abstract Kind kind();
|
||||
public abstract String thriftText();
|
||||
public abstract Object value();
|
||||
|
||||
public boolean isInt() {
|
||||
return kind() == Kind.INTEGER;
|
||||
}
|
||||
|
||||
public boolean isDouble() {
|
||||
return kind() == Kind.DOUBLE;
|
||||
}
|
||||
|
||||
public boolean isString() {
|
||||
return kind() == Kind.STRING;
|
||||
}
|
||||
|
||||
public boolean isIdentifier() {
|
||||
return kind() == Kind.IDENTIFIER;
|
||||
}
|
||||
|
||||
public boolean isList() {
|
||||
return kind() == Kind.LIST;
|
||||
}
|
||||
|
||||
public boolean isMap() {
|
||||
return kind() == Kind.MAP;
|
||||
}
|
||||
|
||||
public String getAsString() {
|
||||
if (kind() == Kind.STRING || kind() == Kind.IDENTIFIER) {
|
||||
return (String) value();
|
||||
} else {
|
||||
throw new IllegalStateException("Cannot convert to string, kind=" + kind());
|
||||
}
|
||||
}
|
||||
|
||||
public long getAsLong() {
|
||||
if (kind() == Kind.INTEGER) {
|
||||
return (Long) value();
|
||||
} else {
|
||||
throw new IllegalStateException("Cannot convert to long, kind=" + kind());
|
||||
}
|
||||
}
|
||||
|
||||
public int getAsInt() {
|
||||
if (kind() == Kind.INTEGER) {
|
||||
return ((Long) value()).intValue();
|
||||
} else {
|
||||
throw new IllegalStateException("Cannot convert to long, kind=" + kind());
|
||||
}
|
||||
}
|
||||
|
||||
public double getAsDouble() {
|
||||
if (kind() == Kind.DOUBLE) {
|
||||
return (Double) value();
|
||||
} else {
|
||||
throw new IllegalStateException("Cannot convert to double, kind=" + kind());
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public List<ConstValueElement> getAsList() {
|
||||
if (kind() == Kind.LIST) {
|
||||
return (List<ConstValueElement>) value();
|
||||
} else {
|
||||
throw new IllegalStateException("Cannot convert to list, kind=" + kind());
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public Map<ConstValueElement, ConstValueElement> getAsMap() {
|
||||
if (kind() == Kind.MAP) {
|
||||
return (Map<ConstValueElement, ConstValueElement>) value();
|
||||
} else {
|
||||
throw new IllegalStateException("Cannot convert to map, kind=" + kind());
|
||||
}
|
||||
}
|
||||
|
||||
ConstValueElement() { }
|
||||
|
||||
public static ConstValueElement integer(Location location, String text, long value) {
|
||||
return new AutoValue_ConstValueElement(location, Kind.INTEGER, text, value);
|
||||
}
|
||||
|
||||
public static ConstValueElement real(Location location, String text, double value) {
|
||||
return new AutoValue_ConstValueElement(location, Kind.DOUBLE, text, value);
|
||||
}
|
||||
|
||||
public static ConstValueElement literal(Location location, String text, String value) {
|
||||
return new AutoValue_ConstValueElement(location, Kind.STRING, text, value);
|
||||
}
|
||||
|
||||
public static ConstValueElement identifier(Location location, String text, String value) {
|
||||
return new AutoValue_ConstValueElement(location, Kind.IDENTIFIER, text, value);
|
||||
}
|
||||
|
||||
public static ConstValueElement list(Location location, String text, List<ConstValueElement> elements) {
|
||||
return new AutoValue_ConstValueElement(location, Kind.LIST, text, ImmutableList.copyOf(elements));
|
||||
}
|
||||
|
||||
public static ConstValueElement map(
|
||||
Location location,
|
||||
String text,
|
||||
Map<ConstValueElement, ConstValueElement> elements) {
|
||||
return new AutoValue_ConstValueElement(location, Kind.MAP, text, ImmutableMap.copyOf(elements));
|
||||
}
|
||||
}
|
|
@ -1,61 +0,0 @@
|
|||
/*
|
||||
* Thrifty
|
||||
*
|
||||
* Copyright (c) Microsoft Corporation
|
||||
*
|
||||
* All rights reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the License);
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* THIS CODE IS PROVIDED ON AN *AS IS* BASIS, WITHOUT WARRANTIES OR
|
||||
* CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING
|
||||
* WITHOUT LIMITATION ANY IMPLIED WARRANTIES OR CONDITIONS OF TITLE,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE, MERCHANTABLITY OR NON-INFRINGEMENT.
|
||||
*
|
||||
* See the Apache Version 2.0 License for specific language governing permissions and limitations under the License.
|
||||
*/
|
||||
package com.microsoft.thrifty.schema.parser;
|
||||
|
||||
import com.google.auto.value.AutoValue;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.microsoft.thrifty.schema.Location;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
@AutoValue
|
||||
public abstract class EnumElement {
|
||||
public abstract Location location();
|
||||
public abstract String documentation();
|
||||
public abstract String name();
|
||||
public abstract UUID uuid();
|
||||
public abstract ImmutableList<EnumMemberElement> members();
|
||||
@Nullable public abstract AnnotationElement annotations();
|
||||
|
||||
EnumElement() { }
|
||||
|
||||
public static Builder builder(Location location) {
|
||||
return new AutoValue_EnumElement.Builder()
|
||||
.location(location)
|
||||
.documentation("")
|
||||
.uuid(ThriftyParserPlugins.createUUID());
|
||||
}
|
||||
|
||||
@AutoValue.Builder
|
||||
public interface Builder {
|
||||
Builder location(Location location);
|
||||
Builder documentation(String documentation);
|
||||
Builder name(String name);
|
||||
Builder uuid(UUID uuid);
|
||||
Builder members(List<EnumMemberElement> members);
|
||||
Builder annotations(AnnotationElement annotations);
|
||||
|
||||
EnumElement build();
|
||||
}
|
||||
}
|
|
@ -1,59 +0,0 @@
|
|||
/*
|
||||
* Thrifty
|
||||
*
|
||||
* Copyright (c) Microsoft Corporation
|
||||
*
|
||||
* All rights reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the License);
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* THIS CODE IS PROVIDED ON AN *AS IS* BASIS, WITHOUT WARRANTIES OR
|
||||
* CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING
|
||||
* WITHOUT LIMITATION ANY IMPLIED WARRANTIES OR CONDITIONS OF TITLE,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE, MERCHANTABLITY OR NON-INFRINGEMENT.
|
||||
*
|
||||
* See the Apache Version 2.0 License for specific language governing permissions and limitations under the License.
|
||||
*/
|
||||
package com.microsoft.thrifty.schema.parser;
|
||||
|
||||
import com.google.auto.value.AutoValue;
|
||||
import com.microsoft.thrifty.schema.Location;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
@AutoValue
|
||||
public abstract class EnumMemberElement {
|
||||
public abstract Location location();
|
||||
public abstract String documentation();
|
||||
public abstract String name();
|
||||
public abstract UUID uuid();
|
||||
public abstract int value();
|
||||
@Nullable public abstract AnnotationElement annotations();
|
||||
|
||||
public static Builder builder(Location location) {
|
||||
return new AutoValue_EnumMemberElement.Builder()
|
||||
.location(location)
|
||||
.documentation("")
|
||||
.uuid(ThriftyParserPlugins.createUUID());
|
||||
}
|
||||
|
||||
EnumMemberElement() { }
|
||||
|
||||
@AutoValue.Builder
|
||||
public interface Builder {
|
||||
Builder location(Location location);
|
||||
Builder documentation(String documentation);
|
||||
Builder name(String name);
|
||||
Builder uuid(UUID uuid);
|
||||
Builder value(int value);
|
||||
Builder annotations(AnnotationElement annotations);
|
||||
|
||||
EnumMemberElement build();
|
||||
}
|
||||
}
|
|
@ -1,67 +0,0 @@
|
|||
/*
|
||||
* Thrifty
|
||||
*
|
||||
* Copyright (c) Microsoft Corporation
|
||||
*
|
||||
* All rights reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the License);
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* THIS CODE IS PROVIDED ON AN *AS IS* BASIS, WITHOUT WARRANTIES OR
|
||||
* CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING
|
||||
* WITHOUT LIMITATION ANY IMPLIED WARRANTIES OR CONDITIONS OF TITLE,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE, MERCHANTABLITY OR NON-INFRINGEMENT.
|
||||
*
|
||||
* See the Apache Version 2.0 License for specific language governing permissions and limitations under the License.
|
||||
*/
|
||||
package com.microsoft.thrifty.schema.parser;
|
||||
|
||||
import com.google.auto.value.AutoValue;
|
||||
import com.microsoft.thrifty.schema.Location;
|
||||
import com.microsoft.thrifty.schema.Requiredness;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
@AutoValue
|
||||
public abstract class FieldElement {
|
||||
public static Builder builder(Location location) {
|
||||
return new AutoValue_FieldElement.Builder()
|
||||
.location(location)
|
||||
.documentation("")
|
||||
.requiredness(Requiredness.DEFAULT)
|
||||
.uuid(ThriftyParserPlugins.createUUID());
|
||||
}
|
||||
|
||||
public abstract Location location();
|
||||
public abstract String documentation();
|
||||
public abstract int fieldId();
|
||||
public abstract Requiredness requiredness();
|
||||
public abstract TypeElement type();
|
||||
public abstract String name();
|
||||
public abstract UUID uuid();
|
||||
@Nullable public abstract ConstValueElement constValue();
|
||||
@Nullable public abstract AnnotationElement annotations();
|
||||
|
||||
FieldElement() { }
|
||||
|
||||
@AutoValue.Builder
|
||||
public interface Builder {
|
||||
Builder location(Location location);
|
||||
Builder documentation(String documentation);
|
||||
Builder fieldId(int fieldId);
|
||||
Builder requiredness(Requiredness requiredness);
|
||||
Builder type(TypeElement type);
|
||||
Builder name(String name);
|
||||
Builder uuid(UUID uuid);
|
||||
Builder constValue(ConstValueElement constValue);
|
||||
Builder annotations(AnnotationElement annotations);
|
||||
|
||||
FieldElement build();
|
||||
}
|
||||
}
|
|
@ -1,70 +0,0 @@
|
|||
/*
|
||||
* Thrifty
|
||||
*
|
||||
* Copyright (c) Microsoft Corporation
|
||||
*
|
||||
* All rights reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the License);
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* THIS CODE IS PROVIDED ON AN *AS IS* BASIS, WITHOUT WARRANTIES OR
|
||||
* CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING
|
||||
* WITHOUT LIMITATION ANY IMPLIED WARRANTIES OR CONDITIONS OF TITLE,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE, MERCHANTABLITY OR NON-INFRINGEMENT.
|
||||
*
|
||||
* See the Apache Version 2.0 License for specific language governing permissions and limitations under the License.
|
||||
*/
|
||||
package com.microsoft.thrifty.schema.parser;
|
||||
|
||||
import com.google.auto.value.AutoValue;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.microsoft.thrifty.schema.Location;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
@AutoValue
|
||||
public abstract class FunctionElement {
|
||||
public static Builder builder(Location location) {
|
||||
return new AutoValue_FunctionElement.Builder()
|
||||
.location(location)
|
||||
.documentation("")
|
||||
.oneWay(false)
|
||||
.params(ImmutableList.of())
|
||||
.exceptions(ImmutableList.of())
|
||||
.uuid(ThriftyParserPlugins.createUUID());
|
||||
}
|
||||
|
||||
public abstract Location location();
|
||||
public abstract String documentation();
|
||||
public abstract boolean oneWay();
|
||||
public abstract TypeElement returnType();
|
||||
public abstract String name();
|
||||
public abstract UUID uuid();
|
||||
public abstract ImmutableList<FieldElement> params();
|
||||
public abstract ImmutableList<FieldElement> exceptions();
|
||||
@Nullable public abstract AnnotationElement annotations();
|
||||
|
||||
FunctionElement() { }
|
||||
|
||||
@AutoValue.Builder
|
||||
public interface Builder {
|
||||
Builder location(Location location);
|
||||
Builder documentation(String documentation);
|
||||
Builder oneWay(boolean oneWay);
|
||||
Builder returnType(TypeElement returnType);
|
||||
Builder name(String name);
|
||||
Builder uuid(UUID uuid);
|
||||
Builder params(List<FieldElement> params);
|
||||
Builder exceptions(List<FieldElement> params);
|
||||
Builder annotations(AnnotationElement annotations);
|
||||
|
||||
FunctionElement build();
|
||||
}
|
||||
}
|
|
@ -1,37 +0,0 @@
|
|||
/*
|
||||
* Thrifty
|
||||
*
|
||||
* Copyright (c) Microsoft Corporation
|
||||
*
|
||||
* All rights reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the License);
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* THIS CODE IS PROVIDED ON AN *AS IS* BASIS, WITHOUT WARRANTIES OR
|
||||
* CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING
|
||||
* WITHOUT LIMITATION ANY IMPLIED WARRANTIES OR CONDITIONS OF TITLE,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE, MERCHANTABLITY OR NON-INFRINGEMENT.
|
||||
*
|
||||
* See the Apache Version 2.0 License for specific language governing permissions and limitations under the License.
|
||||
*/
|
||||
package com.microsoft.thrifty.schema.parser;
|
||||
|
||||
import com.google.auto.value.AutoValue;
|
||||
import com.microsoft.thrifty.schema.Location;
|
||||
|
||||
@AutoValue
|
||||
public abstract class IncludeElement {
|
||||
public abstract Location location();
|
||||
public abstract boolean isCpp();
|
||||
public abstract String path();
|
||||
|
||||
IncludeElement() { }
|
||||
|
||||
public static IncludeElement create(Location location, boolean isCpp, String path) {
|
||||
return new AutoValue_IncludeElement(location, isCpp, path);
|
||||
}
|
||||
}
|
|
@ -1,40 +0,0 @@
|
|||
/*
|
||||
* Thrifty
|
||||
*
|
||||
* Copyright (c) Microsoft Corporation
|
||||
*
|
||||
* All rights reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the License);
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* THIS CODE IS PROVIDED ON AN *AS IS* BASIS, WITHOUT WARRANTIES OR
|
||||
* CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING
|
||||
* WITHOUT LIMITATION ANY IMPLIED WARRANTIES OR CONDITIONS OF TITLE,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE, MERCHANTABLITY OR NON-INFRINGEMENT.
|
||||
*
|
||||
* See the Apache Version 2.0 License for specific language governing permissions and limitations under the License.
|
||||
*/
|
||||
package com.microsoft.thrifty.schema.parser;
|
||||
|
||||
import com.google.auto.value.AutoValue;
|
||||
import com.microsoft.thrifty.schema.Location;
|
||||
|
||||
@AutoValue
|
||||
public abstract class ListTypeElement extends TypeElement {
|
||||
public abstract TypeElement elementType();
|
||||
|
||||
ListTypeElement() {
|
||||
}
|
||||
|
||||
public static ListTypeElement create(
|
||||
Location location,
|
||||
TypeElement element,
|
||||
AnnotationElement annotations) {
|
||||
String name = "list<" + element.name() + ">";
|
||||
return new AutoValue_ListTypeElement(location, name, annotations, element);
|
||||
}
|
||||
}
|
|
@ -1,42 +0,0 @@
|
|||
/*
|
||||
* Thrifty
|
||||
*
|
||||
* Copyright (c) Microsoft Corporation
|
||||
*
|
||||
* All rights reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the License);
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* THIS CODE IS PROVIDED ON AN *AS IS* BASIS, WITHOUT WARRANTIES OR
|
||||
* CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING
|
||||
* WITHOUT LIMITATION ANY IMPLIED WARRANTIES OR CONDITIONS OF TITLE,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE, MERCHANTABLITY OR NON-INFRINGEMENT.
|
||||
*
|
||||
* See the Apache Version 2.0 License for specific language governing permissions and limitations under the License.
|
||||
*/
|
||||
package com.microsoft.thrifty.schema.parser;
|
||||
|
||||
import com.google.auto.value.AutoValue;
|
||||
import com.microsoft.thrifty.schema.Location;
|
||||
|
||||
@AutoValue
|
||||
public abstract class MapTypeElement extends TypeElement {
|
||||
public abstract TypeElement keyType();
|
||||
public abstract TypeElement valueType();
|
||||
|
||||
MapTypeElement() {
|
||||
}
|
||||
|
||||
public static MapTypeElement create(
|
||||
Location location,
|
||||
TypeElement key,
|
||||
TypeElement value,
|
||||
AnnotationElement annotations) {
|
||||
String name = "map<" + key.name() + ", " + value.name() + ">";
|
||||
return new AutoValue_MapTypeElement(location, name, annotations, key, value);
|
||||
}
|
||||
}
|
|
@ -1,55 +0,0 @@
|
|||
/*
|
||||
* Thrifty
|
||||
*
|
||||
* Copyright (c) Microsoft Corporation
|
||||
*
|
||||
* All rights reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the License);
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* THIS CODE IS PROVIDED ON AN *AS IS* BASIS, WITHOUT WARRANTIES OR
|
||||
* CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING
|
||||
* WITHOUT LIMITATION ANY IMPLIED WARRANTIES OR CONDITIONS OF TITLE,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE, MERCHANTABLITY OR NON-INFRINGEMENT.
|
||||
*
|
||||
* See the Apache Version 2.0 License for specific language governing permissions and limitations under the License.
|
||||
*/
|
||||
package com.microsoft.thrifty.schema.parser;
|
||||
|
||||
import com.google.auto.value.AutoValue;
|
||||
import com.microsoft.thrifty.schema.Location;
|
||||
import com.microsoft.thrifty.schema.NamespaceScope;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
@AutoValue
|
||||
public abstract class NamespaceElement {
|
||||
public abstract Location location();
|
||||
public abstract NamespaceScope scope();
|
||||
public abstract String namespace();
|
||||
|
||||
@Nullable
|
||||
public abstract AnnotationElement annotations();
|
||||
|
||||
NamespaceElement() { }
|
||||
|
||||
public static Builder builder(Location location) {
|
||||
return new AutoValue_NamespaceElement.Builder()
|
||||
.location(location)
|
||||
.annotations(null);
|
||||
}
|
||||
|
||||
@AutoValue.Builder
|
||||
public interface Builder {
|
||||
Builder location(Location location);
|
||||
Builder scope(NamespaceScope scope);
|
||||
Builder namespace(String namespace);
|
||||
Builder annotations(AnnotationElement annotations);
|
||||
|
||||
NamespaceElement build();
|
||||
}
|
||||
}
|
|
@ -1,37 +0,0 @@
|
|||
/*
|
||||
* Thrifty
|
||||
*
|
||||
* Copyright (c) Microsoft Corporation
|
||||
*
|
||||
* All rights reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the License);
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* THIS CODE IS PROVIDED ON AN *AS IS* BASIS, WITHOUT WARRANTIES OR
|
||||
* CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING
|
||||
* WITHOUT LIMITATION ANY IMPLIED WARRANTIES OR CONDITIONS OF TITLE,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE, MERCHANTABLITY OR NON-INFRINGEMENT.
|
||||
*
|
||||
* See the Apache Version 2.0 License for specific language governing permissions and limitations under the License.
|
||||
*/
|
||||
package com.microsoft.thrifty.schema.parser;
|
||||
|
||||
import com.google.auto.value.AutoValue;
|
||||
import com.microsoft.thrifty.schema.Location;
|
||||
|
||||
@AutoValue
|
||||
public abstract class ScalarTypeElement extends TypeElement {
|
||||
ScalarTypeElement() {
|
||||
}
|
||||
|
||||
public static ScalarTypeElement create(
|
||||
Location location,
|
||||
String name,
|
||||
AnnotationElement annotations) {
|
||||
return new AutoValue_ScalarTypeElement(location, name, annotations);
|
||||
}
|
||||
}
|
|
@ -1,64 +0,0 @@
|
|||
/*
|
||||
* Thrifty
|
||||
*
|
||||
* Copyright (c) Microsoft Corporation
|
||||
*
|
||||
* All rights reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the License);
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* THIS CODE IS PROVIDED ON AN *AS IS* BASIS, WITHOUT WARRANTIES OR
|
||||
* CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING
|
||||
* WITHOUT LIMITATION ANY IMPLIED WARRANTIES OR CONDITIONS OF TITLE,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE, MERCHANTABLITY OR NON-INFRINGEMENT.
|
||||
*
|
||||
* See the Apache Version 2.0 License for specific language governing permissions and limitations under the License.
|
||||
*/
|
||||
package com.microsoft.thrifty.schema.parser;
|
||||
|
||||
import com.google.auto.value.AutoValue;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.microsoft.thrifty.schema.Location;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
@AutoValue
|
||||
public abstract class ServiceElement {
|
||||
public abstract Location location();
|
||||
public abstract String documentation();
|
||||
public abstract String name();
|
||||
public abstract UUID uuid();
|
||||
@Nullable public abstract TypeElement extendsService();
|
||||
public abstract ImmutableList<FunctionElement> functions();
|
||||
@Nullable public abstract AnnotationElement annotations();
|
||||
|
||||
ServiceElement() { }
|
||||
|
||||
public static Builder builder(Location location) {
|
||||
return new AutoValue_ServiceElement.Builder()
|
||||
.location(location)
|
||||
.documentation("")
|
||||
.functions(ImmutableList.of())
|
||||
.uuid(ThriftyParserPlugins.createUUID());
|
||||
}
|
||||
|
||||
@AutoValue.Builder
|
||||
public interface Builder {
|
||||
Builder location(Location location);
|
||||
Builder documentation(String documentation);
|
||||
Builder name(String name);
|
||||
Builder uuid(UUID uuid);
|
||||
Builder extendsService(TypeElement serviceName);
|
||||
Builder functions(List<FunctionElement> functions);
|
||||
Builder annotations(AnnotationElement annotations);
|
||||
|
||||
ServiceElement build();
|
||||
}
|
||||
}
|
|
@ -1,40 +0,0 @@
|
|||
/*
|
||||
* Thrifty
|
||||
*
|
||||
* Copyright (c) Microsoft Corporation
|
||||
*
|
||||
* All rights reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the License);
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* THIS CODE IS PROVIDED ON AN *AS IS* BASIS, WITHOUT WARRANTIES OR
|
||||
* CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING
|
||||
* WITHOUT LIMITATION ANY IMPLIED WARRANTIES OR CONDITIONS OF TITLE,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE, MERCHANTABLITY OR NON-INFRINGEMENT.
|
||||
*
|
||||
* See the Apache Version 2.0 License for specific language governing permissions and limitations under the License.
|
||||
*/
|
||||
package com.microsoft.thrifty.schema.parser;
|
||||
|
||||
import com.google.auto.value.AutoValue;
|
||||
import com.microsoft.thrifty.schema.Location;
|
||||
|
||||
@AutoValue
|
||||
public abstract class SetTypeElement extends TypeElement {
|
||||
public abstract TypeElement elementType();
|
||||
|
||||
SetTypeElement() {
|
||||
}
|
||||
|
||||
public static SetTypeElement create(
|
||||
Location location,
|
||||
TypeElement element,
|
||||
AnnotationElement annotations) {
|
||||
String name = "set<" + element.name() + ">";
|
||||
return new AutoValue_SetTypeElement(location, name, annotations, element);
|
||||
}
|
||||
}
|
|
@ -1,73 +0,0 @@
|
|||
/*
|
||||
* Thrifty
|
||||
*
|
||||
* Copyright (c) Microsoft Corporation
|
||||
*
|
||||
* All rights reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the License);
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* THIS CODE IS PROVIDED ON AN *AS IS* BASIS, WITHOUT WARRANTIES OR
|
||||
* CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING
|
||||
* WITHOUT LIMITATION ANY IMPLIED WARRANTIES OR CONDITIONS OF TITLE,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE, MERCHANTABLITY OR NON-INFRINGEMENT.
|
||||
*
|
||||
* See the Apache Version 2.0 License for specific language governing permissions and limitations under the License.
|
||||
*/
|
||||
package com.microsoft.thrifty.schema.parser;
|
||||
|
||||
import com.google.auto.value.AutoValue;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.microsoft.thrifty.schema.Location;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
/**
|
||||
* Represents one of the three aggregate Thrift declarations: struct, union, or exception.
|
||||
*/
|
||||
@AutoValue
|
||||
public abstract class StructElement {
|
||||
public abstract Location location();
|
||||
public abstract String documentation();
|
||||
public abstract Type type();
|
||||
public abstract UUID uuid();
|
||||
public abstract String name();
|
||||
public abstract ImmutableList<FieldElement> fields();
|
||||
@Nullable
|
||||
public abstract AnnotationElement annotations();
|
||||
|
||||
public static Builder builder(Location location) {
|
||||
return new AutoValue_StructElement.Builder()
|
||||
.location(location)
|
||||
.documentation("")
|
||||
.uuid(ThriftyParserPlugins.createUUID());
|
||||
}
|
||||
|
||||
StructElement() { }
|
||||
|
||||
@AutoValue.Builder
|
||||
public interface Builder {
|
||||
Builder location(Location location);
|
||||
Builder documentation(String documentation);
|
||||
Builder type(Type type);
|
||||
Builder uuid(UUID uuid);
|
||||
Builder name(String name);
|
||||
Builder fields(List<FieldElement> fields);
|
||||
Builder annotations(AnnotationElement annotations);
|
||||
|
||||
StructElement build();
|
||||
}
|
||||
|
||||
public enum Type {
|
||||
STRUCT,
|
||||
UNION,
|
||||
EXCEPTION
|
||||
}
|
||||
}
|
|
@ -1,73 +0,0 @@
|
|||
/*
|
||||
* Thrifty
|
||||
*
|
||||
* Copyright (c) Microsoft Corporation
|
||||
*
|
||||
* All rights reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the License);
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* THIS CODE IS PROVIDED ON AN *AS IS* BASIS, WITHOUT WARRANTIES OR
|
||||
* CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING
|
||||
* WITHOUT LIMITATION ANY IMPLIED WARRANTIES OR CONDITIONS OF TITLE,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE, MERCHANTABLITY OR NON-INFRINGEMENT.
|
||||
*
|
||||
* See the Apache Version 2.0 License for specific language governing permissions and limitations under the License.
|
||||
*/
|
||||
package com.microsoft.thrifty.schema.parser;
|
||||
|
||||
import com.google.auto.value.AutoValue;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.microsoft.thrifty.schema.Location;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@AutoValue
|
||||
public abstract class ThriftFileElement {
|
||||
public abstract Location location();
|
||||
public abstract ImmutableList<NamespaceElement> namespaces();
|
||||
public abstract ImmutableList<IncludeElement> includes();
|
||||
public abstract ImmutableList<ConstElement> constants();
|
||||
public abstract ImmutableList<TypedefElement> typedefs();
|
||||
public abstract ImmutableList<EnumElement> enums();
|
||||
public abstract ImmutableList<StructElement> structs();
|
||||
public abstract ImmutableList<StructElement> unions();
|
||||
public abstract ImmutableList<StructElement> exceptions();
|
||||
public abstract ImmutableList<ServiceElement> services();
|
||||
|
||||
public static Builder builder(Location location) {
|
||||
return new AutoValue_ThriftFileElement.Builder()
|
||||
.location(location)
|
||||
.namespaces(ImmutableList.of())
|
||||
.includes(ImmutableList.of())
|
||||
.constants(ImmutableList.of())
|
||||
.typedefs(ImmutableList.of())
|
||||
.enums(ImmutableList.of())
|
||||
.structs(ImmutableList.of())
|
||||
.unions(ImmutableList.of())
|
||||
.exceptions(ImmutableList.of())
|
||||
.services(ImmutableList.of());
|
||||
}
|
||||
|
||||
ThriftFileElement() { }
|
||||
|
||||
@AutoValue.Builder
|
||||
public interface Builder {
|
||||
Builder location(Location location);
|
||||
Builder namespaces(List<NamespaceElement> namespaces);
|
||||
Builder includes(List<IncludeElement> includes);
|
||||
Builder constants(List<ConstElement> constants);
|
||||
Builder typedefs(List<TypedefElement> typedefs);
|
||||
Builder enums(List<EnumElement> enums);
|
||||
Builder structs(List<StructElement> structs);
|
||||
Builder unions(List<StructElement> unions);
|
||||
Builder exceptions(List<StructElement> exceptions);
|
||||
Builder services(List<ServiceElement> services);
|
||||
|
||||
ThriftFileElement build();
|
||||
}
|
||||
}
|
|
@ -1,831 +0,0 @@
|
|||
/*
|
||||
* Thrifty
|
||||
*
|
||||
* Copyright (c) Microsoft Corporation
|
||||
*
|
||||
* All rights reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the License);
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* THIS CODE IS PROVIDED ON AN *AS IS* BASIS, WITHOUT WARRANTIES OR
|
||||
* CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING
|
||||
* WITHOUT LIMITATION ANY IMPLIED WARRANTIES OR CONDITIONS OF TITLE,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE, MERCHANTABLITY OR NON-INFRINGEMENT.
|
||||
*
|
||||
* See the Apache Version 2.0 License for specific language governing permissions and limitations under the License.
|
||||
*/
|
||||
package com.microsoft.thrifty.schema.parser;
|
||||
|
||||
import com.google.common.base.Strings;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.microsoft.thrifty.schema.ErrorReporter;
|
||||
import com.microsoft.thrifty.schema.Location;
|
||||
import com.microsoft.thrifty.schema.NamespaceScope;
|
||||
import com.microsoft.thrifty.schema.Requiredness;
|
||||
import com.microsoft.thrifty.schema.antlr.AntlrThriftBaseListener;
|
||||
import com.microsoft.thrifty.schema.antlr.AntlrThriftLexer;
|
||||
import com.microsoft.thrifty.schema.antlr.AntlrThriftParser;
|
||||
import org.antlr.v4.runtime.CommonTokenStream;
|
||||
import org.antlr.v4.runtime.Lexer;
|
||||
import org.antlr.v4.runtime.ParserRuleContext;
|
||||
import org.antlr.v4.runtime.Token;
|
||||
import org.antlr.v4.runtime.tree.ErrorNode;
|
||||
import org.antlr.v4.runtime.tree.TerminalNode;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.BitSet;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* A set of callbacks that, when used with a {@link org.antlr.v4.runtime.tree.ParseTreeWalker},
|
||||
* assemble a {@link ThriftFileElement} from an {@link AntlrThriftParser}.
|
||||
*
|
||||
* Instances of this class are single-use; after walking a parse tree, it will contain
|
||||
* that parser's state as thrifty-schema parser elements.
|
||||
*/
|
||||
class ThriftListener extends AntlrThriftBaseListener {
|
||||
// A number of tokens that should comfortably accommodate most input files
|
||||
// without wildly re-allocating. Estimated based on the ClientTestThrift
|
||||
// and TestThrift files, which contain around ~1200 tokens each.
|
||||
private static final int INITIAL_BITSET_CAPACITY = 2048;
|
||||
|
||||
private final CommonTokenStream tokenStream;
|
||||
private final ErrorReporter errorReporter;
|
||||
private final Location location;
|
||||
|
||||
// We need to record which comment tokens have been treated as trailing documentation,
|
||||
// so that scanning for leading doc tokens for subsequent elements knows where to stop.
|
||||
// We can do this with a bitset tracking token indices of trailing-comment tokens.
|
||||
private final BitSet trailingDocTokenIndexes = new BitSet(INITIAL_BITSET_CAPACITY);
|
||||
|
||||
private final List<IncludeElement> includes = new ArrayList<>();
|
||||
private final List<NamespaceElement> namespaces = new ArrayList<>();
|
||||
private final List<EnumElement> enums = new ArrayList<>();
|
||||
private final List<TypedefElement> typedefs = new ArrayList<>();
|
||||
private final List<StructElement> structs = new ArrayList<>();
|
||||
private final List<StructElement> unions = new ArrayList<>();
|
||||
private final List<StructElement> exceptions = new ArrayList<>();
|
||||
private final List<ConstElement> consts = new ArrayList<>();
|
||||
private final List<ServiceElement> services = new ArrayList<>();
|
||||
|
||||
/**
|
||||
* Creates a new ThriftListener instance.
|
||||
*
|
||||
* @param tokenStream the same token stream used with the corresponding {@link AntlrThriftParser};
|
||||
* this stream will be queried for "hidden" tokens containing parsed doc
|
||||
* comments.
|
||||
* @param errorReporter an error reporting mechanism, used to communicate errors during parsing.
|
||||
* @param location a location pointing at the beginning of the file being parsed.
|
||||
*/
|
||||
ThriftListener(CommonTokenStream tokenStream, ErrorReporter errorReporter, Location location) {
|
||||
this.tokenStream = tokenStream;
|
||||
this.errorReporter = errorReporter;
|
||||
this.location = location;
|
||||
}
|
||||
|
||||
ThriftFileElement buildFileElement() {
|
||||
return ThriftFileElement.builder(location)
|
||||
.includes(includes)
|
||||
.namespaces(namespaces)
|
||||
.typedefs(typedefs)
|
||||
.enums(enums)
|
||||
.structs(structs)
|
||||
.unions(unions)
|
||||
.exceptions(exceptions)
|
||||
.constants(consts)
|
||||
.services(services)
|
||||
.build();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void exitInclude(AntlrThriftParser.IncludeContext ctx) {
|
||||
TerminalNode pathNode = ctx.LITERAL();
|
||||
String path = unquote(locationOf(pathNode), pathNode.getText(), false);
|
||||
|
||||
includes.add(IncludeElement.create(locationOf(ctx), false, path));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void exitCppInclude(AntlrThriftParser.CppIncludeContext ctx) {
|
||||
TerminalNode pathNode = ctx.LITERAL();
|
||||
String path = unquote(locationOf(pathNode), pathNode.getText(), false);
|
||||
|
||||
includes.add(IncludeElement.create(locationOf(ctx), true, path));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void exitStandardNamespace(AntlrThriftParser.StandardNamespaceContext ctx) {
|
||||
String scopeName = ctx.namespaceScope().getText();
|
||||
String name = ctx.ns.getText();
|
||||
|
||||
AnnotationElement annotations = annotationsFromAntlr(ctx.annotationList());
|
||||
|
||||
NamespaceScope scope = NamespaceScope.forThriftName(scopeName);
|
||||
if (scope == null) {
|
||||
errorReporter.warn(locationOf(ctx.namespaceScope()), "Unknown namespace scope '" + scopeName + "'");
|
||||
return;
|
||||
}
|
||||
|
||||
NamespaceElement element = NamespaceElement.builder(locationOf(ctx))
|
||||
.scope(scope)
|
||||
.namespace(name)
|
||||
.annotations(annotations)
|
||||
.build();
|
||||
|
||||
namespaces.add(element);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void exitPhpNamespace(AntlrThriftParser.PhpNamespaceContext ctx) {
|
||||
NamespaceElement element = NamespaceElement.builder(locationOf(ctx))
|
||||
.scope(NamespaceScope.PHP)
|
||||
.namespace(unquote(locationOf(ctx.LITERAL()), ctx.LITERAL().getText()))
|
||||
.annotations(annotationsFromAntlr(ctx.annotationList()))
|
||||
.build();
|
||||
|
||||
namespaces.add(element);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void exitXsdNamespace(AntlrThriftParser.XsdNamespaceContext ctx) {
|
||||
errorReporter.error(locationOf(ctx), "'xsd_namespace' is unsupported");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void exitSenum(AntlrThriftParser.SenumContext ctx) {
|
||||
errorReporter.error(locationOf(ctx), "'senum' is unsupported; use 'enum' instead");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void exitEnumDef(AntlrThriftParser.EnumDefContext ctx) {
|
||||
String enumName = ctx.IDENTIFIER().getText();
|
||||
|
||||
int nextValue = 0;
|
||||
Set<Integer> values = new HashSet<>();
|
||||
|
||||
List<EnumMemberElement> members = new ArrayList<>(ctx.enumMember().size());
|
||||
for (AntlrThriftParser.EnumMemberContext memberContext : ctx.enumMember()) {
|
||||
int value = nextValue;
|
||||
|
||||
TerminalNode valueToken = memberContext.INTEGER();
|
||||
if (valueToken != null) {
|
||||
value = parseInt(valueToken);
|
||||
}
|
||||
|
||||
if (!values.add(value)) {
|
||||
errorReporter.error(locationOf(memberContext), "duplicate enum value: " + value);
|
||||
continue;
|
||||
}
|
||||
|
||||
nextValue = value + 1;
|
||||
|
||||
EnumMemberElement element = EnumMemberElement.builder(locationOf(memberContext))
|
||||
.name(memberContext.IDENTIFIER().getText())
|
||||
.value(value)
|
||||
.documentation(formatJavadoc(memberContext))
|
||||
.annotations(annotationsFromAntlr(memberContext.annotationList()))
|
||||
.build();
|
||||
|
||||
members.add(element);
|
||||
}
|
||||
|
||||
String doc = formatJavadoc(ctx);
|
||||
EnumElement element = EnumElement.builder(locationOf(ctx))
|
||||
.name(enumName)
|
||||
.documentation(doc)
|
||||
.annotations(annotationsFromAntlr(ctx.annotationList()))
|
||||
.members(members)
|
||||
.build();
|
||||
|
||||
enums.add(element);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void exitStructDef(AntlrThriftParser.StructDefContext ctx) {
|
||||
String name = ctx.IDENTIFIER().getText();
|
||||
ImmutableList<FieldElement> fields = parseFieldList(ctx.field());
|
||||
|
||||
StructElement element = StructElement.builder(locationOf(ctx))
|
||||
.name(name)
|
||||
.fields(fields)
|
||||
.type(StructElement.Type.STRUCT)
|
||||
.documentation(formatJavadoc(ctx))
|
||||
.annotations(annotationsFromAntlr(ctx.annotationList()))
|
||||
.build();
|
||||
|
||||
structs.add(element);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void exitUnionDef(AntlrThriftParser.UnionDefContext ctx) {
|
||||
String name = ctx.IDENTIFIER().getText();
|
||||
ImmutableList<FieldElement> fields = parseFieldList(ctx.field());
|
||||
|
||||
int numFieldsWithDefaultValues = 0;
|
||||
for (int i = 0; i < fields.size(); ++i) {
|
||||
FieldElement element = fields.get(i);
|
||||
if (element.requiredness() == Requiredness.REQUIRED) {
|
||||
AntlrThriftParser.FieldContext fieldContext = ctx.field(i);
|
||||
errorReporter.error(locationOf(fieldContext), "unions cannot have required fields");
|
||||
}
|
||||
|
||||
if (element.constValue() != null) {
|
||||
++numFieldsWithDefaultValues;
|
||||
}
|
||||
}
|
||||
|
||||
if (numFieldsWithDefaultValues > 1) {
|
||||
errorReporter.error(locationOf(ctx), "unions can have at most one default value");
|
||||
}
|
||||
|
||||
StructElement element = StructElement.builder(locationOf(ctx))
|
||||
.name(name)
|
||||
.fields(fields)
|
||||
.type(StructElement.Type.UNION)
|
||||
.documentation(formatJavadoc(ctx))
|
||||
.annotations(annotationsFromAntlr(ctx.annotationList()))
|
||||
.build();
|
||||
|
||||
unions.add(element);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void exitExceptionDef(AntlrThriftParser.ExceptionDefContext ctx) {
|
||||
String name = ctx.IDENTIFIER().getText();
|
||||
ImmutableList<FieldElement> fields = parseFieldList(ctx.field());
|
||||
|
||||
StructElement element = StructElement.builder(locationOf(ctx))
|
||||
.name(name)
|
||||
.fields(fields)
|
||||
.type(StructElement.Type.EXCEPTION)
|
||||
.documentation(formatJavadoc(ctx))
|
||||
.annotations(annotationsFromAntlr(ctx.annotationList()))
|
||||
.build();
|
||||
|
||||
exceptions.add(element);
|
||||
}
|
||||
|
||||
private ImmutableList<FieldElement> parseFieldList(List<AntlrThriftParser.FieldContext> contexts) {
|
||||
return parseFieldList(contexts, Requiredness.DEFAULT);
|
||||
}
|
||||
|
||||
private ImmutableList<FieldElement> parseFieldList(
|
||||
List<AntlrThriftParser.FieldContext> contexts,
|
||||
Requiredness defaultRequiredness) {
|
||||
ImmutableList.Builder<FieldElement> builder = ImmutableList.builder();
|
||||
Set<Integer> ids = new HashSet<>();
|
||||
|
||||
int nextValue = 1;
|
||||
for (AntlrThriftParser.FieldContext fieldContext : contexts) {
|
||||
FieldElement element = parseField(nextValue, fieldContext, defaultRequiredness);
|
||||
if (element != null) {
|
||||
builder = builder.add(element);
|
||||
|
||||
if (!ids.add(element.fieldId())) {
|
||||
errorReporter.error(locationOf(fieldContext), "duplicate field ID: " + element.fieldId());
|
||||
}
|
||||
|
||||
if (element.fieldId() <= 0) {
|
||||
errorReporter.error(locationOf(fieldContext), "field ID must be greater than zero");
|
||||
}
|
||||
|
||||
if (element.fieldId() >= nextValue) {
|
||||
nextValue = element.fieldId() + 1;
|
||||
}
|
||||
} else {
|
||||
// assert-fail here?
|
||||
++nextValue; // this represents an error condition
|
||||
}
|
||||
}
|
||||
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
private FieldElement parseField(
|
||||
int defaultValue,
|
||||
AntlrThriftParser.FieldContext ctx,
|
||||
Requiredness defaultRequiredness) {
|
||||
int fieldId = defaultValue;
|
||||
if (ctx.INTEGER() != null) {
|
||||
fieldId = parseInt(ctx.INTEGER());
|
||||
}
|
||||
|
||||
String fieldName = ctx.IDENTIFIER().getText();
|
||||
|
||||
Requiredness requiredness = defaultRequiredness;
|
||||
if (ctx.requiredness() != null) {
|
||||
if (ctx.requiredness().getText().equals("required")) {
|
||||
requiredness = Requiredness.REQUIRED;
|
||||
} else if (ctx.requiredness().getText().equals("optional")) {
|
||||
requiredness = Requiredness.OPTIONAL;
|
||||
} else {
|
||||
throw new AssertionError("Unexpected requiredness value: " + ctx.requiredness().getText());
|
||||
}
|
||||
}
|
||||
|
||||
return FieldElement.builder(locationOf(ctx))
|
||||
.documentation(formatJavadoc(ctx))
|
||||
.fieldId(fieldId)
|
||||
.requiredness(requiredness)
|
||||
.type(typeElementOf(ctx.fieldType()))
|
||||
.name(fieldName)
|
||||
.annotations(annotationsFromAntlr(ctx.annotationList()))
|
||||
.constValue(constValueElementOf(ctx.constValue()))
|
||||
.build();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void exitTypedef(AntlrThriftParser.TypedefContext ctx) {
|
||||
TypeElement oldType = typeElementOf(ctx.fieldType());
|
||||
|
||||
TypedefElement typedef = TypedefElement.builder(locationOf(ctx))
|
||||
.documentation(formatJavadoc(ctx))
|
||||
.annotations(annotationsFromAntlr(ctx.annotationList()))
|
||||
.oldType(oldType)
|
||||
.newName(ctx.IDENTIFIER().getText())
|
||||
.build();
|
||||
|
||||
typedefs.add(typedef);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void exitConstDef(AntlrThriftParser.ConstDefContext ctx) {
|
||||
ConstElement element = ConstElement.builder(locationOf(ctx))
|
||||
.documentation(formatJavadoc(ctx))
|
||||
.type(typeElementOf(ctx.fieldType()))
|
||||
.name(ctx.IDENTIFIER().getText())
|
||||
.value(constValueElementOf(ctx.constValue()))
|
||||
.build();
|
||||
|
||||
consts.add(element);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void exitServiceDef(AntlrThriftParser.ServiceDefContext ctx) {
|
||||
String name = ctx.name.getText();
|
||||
|
||||
ServiceElement.Builder builder = ServiceElement.builder(locationOf(ctx))
|
||||
.name(name)
|
||||
.functions(parseFunctionList(ctx.function()))
|
||||
.documentation(formatJavadoc(ctx))
|
||||
.annotations(annotationsFromAntlr(ctx.annotationList()));
|
||||
|
||||
if (ctx.superType != null) {
|
||||
TypeElement superType = typeElementOf(ctx.superType);
|
||||
|
||||
if (!(superType instanceof ScalarTypeElement)) {
|
||||
errorReporter.error(locationOf(ctx), "services cannot extend collections");
|
||||
}
|
||||
|
||||
builder = builder.extendsService(superType);
|
||||
}
|
||||
|
||||
services.add(builder.build());
|
||||
}
|
||||
|
||||
private ImmutableList<FunctionElement> parseFunctionList(List<AntlrThriftParser.FunctionContext> functionContexts) {
|
||||
ImmutableList.Builder<FunctionElement> functions = ImmutableList.builder();
|
||||
|
||||
for (AntlrThriftParser.FunctionContext ctx : functionContexts) {
|
||||
String name = ctx.IDENTIFIER().getText();
|
||||
|
||||
TypeElement returnType;
|
||||
if (ctx.fieldType() != null) {
|
||||
returnType = typeElementOf(ctx.fieldType());
|
||||
} else {
|
||||
TerminalNode token = ctx.getToken(AntlrThriftLexer.VOID, 0);
|
||||
if (token == null) {
|
||||
throw new AssertionError("Function has no return type, and no VOID token - grammar error");
|
||||
}
|
||||
Location loc = locationOf(token);
|
||||
|
||||
// Do people actually annotation 'void'? We'll find out!
|
||||
returnType = TypeElement.scalar(loc, "void", null);
|
||||
}
|
||||
|
||||
boolean isOneway = ctx.ONEWAY() != null;
|
||||
|
||||
FunctionElement.Builder builder = FunctionElement.builder(locationOf(ctx))
|
||||
.oneWay(isOneway)
|
||||
.returnType(returnType)
|
||||
.name(name)
|
||||
.documentation(formatJavadoc(ctx))
|
||||
.annotations(annotationsFromAntlr(ctx.annotationList()))
|
||||
.params(parseFieldList(ctx.fieldList().field(), Requiredness.REQUIRED));
|
||||
|
||||
if (ctx.throwsList() != null) {
|
||||
builder = builder.exceptions(parseFieldList(ctx.throwsList().fieldList().field()));
|
||||
}
|
||||
|
||||
functions.add(builder.build());
|
||||
}
|
||||
|
||||
return functions.build();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitErrorNode(ErrorNode node) {
|
||||
errorReporter.error(locationOf(node), node.getText());
|
||||
}
|
||||
|
||||
// region Utilities
|
||||
|
||||
private AnnotationElement annotationsFromAntlr(AntlrThriftParser.AnnotationListContext ctx) {
|
||||
if (ctx == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
Map<String, String> annotations = new LinkedHashMap<>();
|
||||
for (AntlrThriftParser.AnnotationContext annotationContext : ctx.annotation()) {
|
||||
String name = annotationContext.IDENTIFIER().getText();
|
||||
String value;
|
||||
if (annotationContext.LITERAL() != null) {
|
||||
value = unquote(locationOf(annotationContext.LITERAL()), annotationContext.LITERAL().getText());
|
||||
} else {
|
||||
value = "true";
|
||||
}
|
||||
annotations.put(name, value);
|
||||
}
|
||||
|
||||
return AnnotationElement.create(locationOf(ctx), annotations);
|
||||
}
|
||||
|
||||
private Location locationOf(ParserRuleContext ctx) {
|
||||
return locationOf(ctx.getStart());
|
||||
}
|
||||
|
||||
private Location locationOf(TerminalNode node) {
|
||||
return locationOf(node.getSymbol());
|
||||
}
|
||||
|
||||
private Location locationOf(Token token) {
|
||||
int line = token.getLine();
|
||||
int col = token.getCharPositionInLine() + 1; // Location.col is 1-based, Token.col is 0-based
|
||||
return location.at(line, col);
|
||||
}
|
||||
|
||||
private String unquote(Location location, String literal) {
|
||||
return unquote(location, literal, /* processEscapes */ true);
|
||||
}
|
||||
|
||||
private String unquote(Location location, String literal, boolean processEscapes) {
|
||||
char[] chars = literal.toCharArray();
|
||||
char startChar = chars[0];
|
||||
char endChar = chars[chars.length - 1];
|
||||
|
||||
if (startChar != endChar || (startChar != '\'' && startChar != '"')) {
|
||||
throw new AssertionError("Incorrect UNESCAPED_LITERAL rule: " + literal);
|
||||
}
|
||||
|
||||
StringBuilder sb = new StringBuilder(literal.length() - 2);
|
||||
|
||||
int i = 1;
|
||||
int end = chars.length - 1;
|
||||
while (i < end) {
|
||||
char c = chars[i++];
|
||||
|
||||
if (processEscapes && c == '\\') {
|
||||
if (i == end) {
|
||||
errorReporter.error(location, "Unterminated literal");
|
||||
break;
|
||||
}
|
||||
|
||||
char escape = chars[i++];
|
||||
switch (escape) {
|
||||
case 'a':
|
||||
sb.append((char) 0x7);
|
||||
break;
|
||||
case 'b':
|
||||
sb.append('\b');
|
||||
break;
|
||||
case 'f':
|
||||
sb.append('\f');
|
||||
break;
|
||||
case 'n':
|
||||
sb.append('\n');
|
||||
break;
|
||||
case 'r':
|
||||
sb.append('\r');
|
||||
break;
|
||||
case 't':
|
||||
sb.append('\t');
|
||||
break;
|
||||
case 'v':
|
||||
sb.append((char) 0xB);
|
||||
break;
|
||||
case '\\':
|
||||
sb.append('\\');
|
||||
break;
|
||||
case 'u':
|
||||
throw new UnsupportedOperationException("unicode escapes not yet implemented");
|
||||
default:
|
||||
if (escape == startChar) {
|
||||
sb.append(startChar);
|
||||
} else {
|
||||
errorReporter.error(location, "invalid escape character: " + escape);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
sb.append(c);
|
||||
}
|
||||
}
|
||||
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
private TypeElement typeElementOf(AntlrThriftParser.FieldTypeContext context) {
|
||||
if (context.baseType() != null) {
|
||||
if (context.baseType().getText().equals("slist")) {
|
||||
errorReporter.error(locationOf(context), "slist is unsupported; use list<string> instead");
|
||||
}
|
||||
|
||||
return TypeElement.scalar(
|
||||
locationOf(context),
|
||||
context.baseType().getText(),
|
||||
annotationsFromAntlr(context.annotationList()));
|
||||
}
|
||||
|
||||
if (context.IDENTIFIER() != null) {
|
||||
return TypeElement.scalar(
|
||||
locationOf(context),
|
||||
context.IDENTIFIER().getText(),
|
||||
annotationsFromAntlr(context.annotationList()));
|
||||
}
|
||||
|
||||
if (context.containerType() != null) {
|
||||
AntlrThriftParser.ContainerTypeContext containerContext = context.containerType();
|
||||
if (containerContext.mapType() != null) {
|
||||
TypeElement keyType = typeElementOf(containerContext.mapType().key);
|
||||
TypeElement valueType = typeElementOf(containerContext.mapType().value);
|
||||
return TypeElement.map(
|
||||
locationOf(containerContext.mapType()),
|
||||
keyType,
|
||||
valueType,
|
||||
annotationsFromAntlr(context.annotationList()));
|
||||
}
|
||||
|
||||
if (containerContext.setType() != null) {
|
||||
return TypeElement.set(
|
||||
locationOf(containerContext.setType()),
|
||||
typeElementOf(containerContext.setType().fieldType()),
|
||||
annotationsFromAntlr(context.annotationList()));
|
||||
}
|
||||
|
||||
if (containerContext.listType() != null) {
|
||||
return TypeElement.list(
|
||||
locationOf(containerContext.listType()),
|
||||
typeElementOf(containerContext.listType().fieldType()),
|
||||
annotationsFromAntlr(context.annotationList()));
|
||||
}
|
||||
|
||||
throw new AssertionError("Unexpected container type - grammar error!");
|
||||
}
|
||||
|
||||
throw new AssertionError("Unexpected type - grammar error!");
|
||||
}
|
||||
|
||||
private ConstValueElement constValueElementOf(AntlrThriftParser.ConstValueContext ctx) {
|
||||
if (ctx == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (ctx.INTEGER() != null) {
|
||||
String text = ctx.INTEGER().getText();
|
||||
|
||||
int radix = 10;
|
||||
if (text.startsWith("0x") || text.startsWith("0X")) {
|
||||
text = text.substring(2);
|
||||
radix = 16;
|
||||
}
|
||||
|
||||
try {
|
||||
long value = Long.parseLong(text, radix);
|
||||
|
||||
return ConstValueElement.integer(locationOf(ctx), ctx.INTEGER().getText(), value);
|
||||
} catch (NumberFormatException e) {
|
||||
throw new AssertionError("Invalid integer accepted by ANTLR grammar: " + ctx.INTEGER().getText());
|
||||
}
|
||||
}
|
||||
|
||||
if (ctx.DOUBLE() != null) {
|
||||
String text = ctx.DOUBLE().getText();
|
||||
|
||||
try {
|
||||
double value = Double.parseDouble(text);
|
||||
return ConstValueElement.real(locationOf(ctx), ctx.DOUBLE().getText(), value);
|
||||
} catch (NumberFormatException e) {
|
||||
throw new AssertionError("Invalid double accepted by ANTLR grammar: " + text);
|
||||
}
|
||||
}
|
||||
|
||||
if (ctx.LITERAL() != null) {
|
||||
String text = unquote(locationOf(ctx.LITERAL()), ctx.LITERAL().getText());
|
||||
return ConstValueElement.literal(locationOf(ctx), ctx.LITERAL().getText(), text);
|
||||
}
|
||||
|
||||
if (ctx.IDENTIFIER() != null) {
|
||||
String id = ctx.IDENTIFIER().getText();
|
||||
return ConstValueElement.identifier(locationOf(ctx), ctx.IDENTIFIER().getText(), id);
|
||||
}
|
||||
|
||||
if (ctx.constList() != null) {
|
||||
ImmutableList.Builder<ConstValueElement> values = ImmutableList.builder();
|
||||
for (AntlrThriftParser.ConstValueContext valueContext : ctx.constList().constValue()) {
|
||||
values.add(constValueElementOf(valueContext));
|
||||
}
|
||||
return ConstValueElement.list(locationOf(ctx), ctx.constList().getText(), values.build());
|
||||
}
|
||||
|
||||
if (ctx.constMap() != null) {
|
||||
ImmutableMap.Builder<ConstValueElement, ConstValueElement> values = ImmutableMap.builder();
|
||||
for (AntlrThriftParser.ConstMapEntryContext entry : ctx.constMap().constMapEntry()) {
|
||||
ConstValueElement key = constValueElementOf(entry.key);
|
||||
ConstValueElement value = constValueElementOf(entry.value);
|
||||
values.put(key, value);
|
||||
}
|
||||
return ConstValueElement.map(locationOf(ctx), ctx.constMap().getText(), values.build());
|
||||
}
|
||||
|
||||
throw new AssertionError("unreachable");
|
||||
}
|
||||
|
||||
private static boolean isComment(Token token) {
|
||||
switch (token.getType()) {
|
||||
case AntlrThriftLexer.SLASH_SLASH_COMMENT:
|
||||
case AntlrThriftLexer.HASH_COMMENT:
|
||||
case AntlrThriftLexer.MULTILINE_COMMENT:
|
||||
return true;
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private String formatJavadoc(ParserRuleContext context) {
|
||||
List<Token> tokens = new ArrayList<>();
|
||||
tokens.addAll(getLeadingComments(context.getStart()));
|
||||
tokens.addAll(getTrailingComments(context.getStop()));
|
||||
|
||||
return formatJavadoc(tokens);
|
||||
}
|
||||
|
||||
private List<Token> getLeadingComments(Token token) {
|
||||
List<Token> hiddenTokens = tokenStream.getHiddenTokensToLeft(token.getTokenIndex(), Lexer.HIDDEN);
|
||||
|
||||
if (hiddenTokens == null || hiddenTokens.isEmpty()) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
List<Token> comments = new ArrayList<>(hiddenTokens.size());
|
||||
for (Token hiddenToken : hiddenTokens) {
|
||||
if (isComment(hiddenToken) && !trailingDocTokenIndexes.get(hiddenToken.getTokenIndex())) {
|
||||
comments.add(hiddenToken);
|
||||
}
|
||||
}
|
||||
|
||||
return comments;
|
||||
}
|
||||
|
||||
/**
|
||||
* Read comments following the given token, until the first newline is encountered.
|
||||
*
|
||||
* INVARIANT:
|
||||
* Assumes that the parse tree is being walked top-down, left to right!
|
||||
*
|
||||
* Trailing-doc tokens are marked as such, so that subsequent searches for "leading"
|
||||
* doc don't grab tokens already used as "trailing" doc. If the walk order is *not*
|
||||
* top-down, left-to-right, then the assumption underpinning the separation of leading
|
||||
* and trailing comments is broken.
|
||||
*
|
||||
* @param endToken the token from which to search for trailing comment tokens.
|
||||
* @return a list, possibly empty, of all trailing comment tokens.
|
||||
*/
|
||||
private List<Token> getTrailingComments(Token endToken) {
|
||||
List<Token> hiddenTokens = tokenStream.getHiddenTokensToRight(endToken.getTokenIndex(), Lexer.HIDDEN);
|
||||
|
||||
if (hiddenTokens == null || hiddenTokens.isEmpty()) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
Token maybeTrailingDoc = hiddenTokens.get(0); // only one trailing comment is possible
|
||||
|
||||
if (isComment(maybeTrailingDoc)) {
|
||||
trailingDocTokenIndexes.set(maybeTrailingDoc.getTokenIndex());
|
||||
return Collections.singletonList(maybeTrailingDoc);
|
||||
}
|
||||
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
private static String formatJavadoc(List<Token> commentTokens) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
|
||||
for (Token token : commentTokens) {
|
||||
String text = token.getText();
|
||||
switch (token.getType()) {
|
||||
case AntlrThriftLexer.SLASH_SLASH_COMMENT:
|
||||
formatSingleLineComment(sb, text, "//");
|
||||
break;
|
||||
|
||||
case AntlrThriftLexer.HASH_COMMENT:
|
||||
formatSingleLineComment(sb, text, "#");
|
||||
break;
|
||||
|
||||
case AntlrThriftLexer.MULTILINE_COMMENT:
|
||||
formatMultilineComment(sb, text);
|
||||
break;
|
||||
|
||||
default:
|
||||
// wut
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
String doc = sb.toString().trim();
|
||||
|
||||
if (!Strings.isNullOrEmpty(doc) && !doc.endsWith("\n")) {
|
||||
doc += "\n";
|
||||
}
|
||||
|
||||
return doc;
|
||||
}
|
||||
|
||||
private static void formatSingleLineComment(StringBuilder sb, String text, String prefix) {
|
||||
int start = prefix.length();
|
||||
int end = text.length();
|
||||
|
||||
while (start < end && Character.isWhitespace(text.charAt(start))) {
|
||||
++start;
|
||||
}
|
||||
|
||||
while (end > start && Character.isWhitespace(text.charAt(end - 1))) {
|
||||
--end;
|
||||
}
|
||||
|
||||
if (start != end) {
|
||||
sb.append(text.substring(start, end));
|
||||
}
|
||||
|
||||
sb.append("\n");
|
||||
}
|
||||
|
||||
private static void formatMultilineComment(StringBuilder sb, String text) {
|
||||
char[] chars = text.toCharArray();
|
||||
int pos = "/*".length();
|
||||
int length = chars.length;
|
||||
boolean isStartOfLine = true;
|
||||
|
||||
for (; pos + 1 < length; ++pos) {
|
||||
char c = chars[pos];
|
||||
if (c == '*' && chars[pos + 1] == '/') {
|
||||
sb.append("\n");
|
||||
return;
|
||||
}
|
||||
|
||||
if (c == '\n') {
|
||||
sb.append(c);
|
||||
isStartOfLine = true;
|
||||
} else if (!isStartOfLine) {
|
||||
sb.append(c);
|
||||
} else if (c == '*') {
|
||||
// skip a single subsequent space, if it exists
|
||||
if (chars[pos + 1] == ' ') {
|
||||
pos += 1;
|
||||
}
|
||||
|
||||
isStartOfLine = false;
|
||||
} else if (! Character.isWhitespace(c)) {
|
||||
sb.append(c);
|
||||
isStartOfLine = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static int parseInt(TerminalNode node) {
|
||||
return parseInt(node.getSymbol());
|
||||
}
|
||||
|
||||
private static int parseInt(Token token) {
|
||||
String text = token.getText();
|
||||
|
||||
int radix = 10;
|
||||
if (text.startsWith("0x") || text.startsWith("0X")) {
|
||||
radix = 16;
|
||||
text = text.substring(2);
|
||||
}
|
||||
|
||||
return Integer.parseInt(text, radix);
|
||||
}
|
||||
|
||||
// endregion
|
||||
}
|
|
@ -1,80 +0,0 @@
|
|||
/*
|
||||
* Thrifty
|
||||
*
|
||||
* Copyright (c) Microsoft Corporation
|
||||
*
|
||||
* All rights reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the License);
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* THIS CODE IS PROVIDED ON AN *AS IS* BASIS, WITHOUT WARRANTIES OR
|
||||
* CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING
|
||||
* WITHOUT LIMITATION ANY IMPLIED WARRANTIES OR CONDITIONS OF TITLE,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE, MERCHANTABLITY OR NON-INFRINGEMENT.
|
||||
*
|
||||
* See the Apache Version 2.0 License for specific language governing permissions and limitations under the License.
|
||||
*/
|
||||
package com.microsoft.thrifty.schema.parser;
|
||||
|
||||
import com.google.common.base.Joiner;
|
||||
import com.microsoft.thrifty.schema.ErrorReporter;
|
||||
import com.microsoft.thrifty.schema.Location;
|
||||
import com.microsoft.thrifty.schema.antlr.AntlrThriftLexer;
|
||||
import com.microsoft.thrifty.schema.antlr.AntlrThriftParser;
|
||||
import org.antlr.v4.runtime.CharStreams;
|
||||
import org.antlr.v4.runtime.CodePointCharStream;
|
||||
import org.antlr.v4.runtime.CommonTokenStream;
|
||||
import org.antlr.v4.runtime.tree.ParseTreeWalker;
|
||||
|
||||
import java.util.Locale;
|
||||
|
||||
@SuppressWarnings({"WeakerAccess", "unused"}) // public methods are part of our "official" API surface
|
||||
public final class ThriftParser {
|
||||
|
||||
/**
|
||||
* Parse the given Thrift {@code text}, using the given {@code location}
|
||||
* to anchor parsed elements withing the file.
|
||||
* @param location the {@link Location} of the data being parsed.
|
||||
* @param text the text to be parsed.
|
||||
* @return a representation of the parsed Thrift data.
|
||||
*/
|
||||
public static ThriftFileElement parse(Location location, String text) {
|
||||
return parse(location, text, new ErrorReporter());
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse the given Thrift {@code text}, using the given {@code location}
|
||||
* to anchor parsed elements withing the file.
|
||||
* @param location the {@link Location} of the data being parsed.
|
||||
* @param text the text to be parsed.
|
||||
* @param reporter an {@link ErrorReporter} to collect warnings.
|
||||
* @return a representation of the parsed Thrift data.
|
||||
*/
|
||||
public static ThriftFileElement parse(Location location, String text, ErrorReporter reporter) {
|
||||
CodePointCharStream charStream = CharStreams.fromString(text, location.path());
|
||||
AntlrThriftLexer lexer = new AntlrThriftLexer(charStream);
|
||||
CommonTokenStream tokenStream = new CommonTokenStream(lexer);
|
||||
AntlrThriftParser antlrParser = new AntlrThriftParser(tokenStream);
|
||||
|
||||
ThriftListener thriftListener = new ThriftListener(tokenStream, reporter, location);
|
||||
|
||||
ParseTreeWalker walker = new ParseTreeWalker();
|
||||
walker.walk(thriftListener, antlrParser.document());
|
||||
|
||||
if (reporter.hasError()) {
|
||||
String errorReports = Joiner.on('\n').join(reporter.formattedReports());
|
||||
String message = String.format(Locale.US, "Syntax errors in %s:\n%s", location, errorReports);
|
||||
throw new IllegalStateException(message);
|
||||
}
|
||||
|
||||
return thriftListener.buildFileElement();
|
||||
}
|
||||
|
||||
private ThriftParser() {
|
||||
// no instances
|
||||
}
|
||||
}
|
|
@ -1,72 +0,0 @@
|
|||
package com.microsoft.thrifty.schema.parser;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* Utility class to inject handlers to certain standard Thrifty operations.
|
||||
*/
|
||||
public final class ThriftyParserPlugins {
|
||||
|
||||
private static final UUIDProvider DEFAULT_UUID_PROVIDER = UUID::randomUUID;
|
||||
private static volatile UUIDProvider uuidProvider = DEFAULT_UUID_PROVIDER;
|
||||
|
||||
/**
|
||||
* Prevents changing the plugins.
|
||||
*/
|
||||
private static volatile boolean lockdown;
|
||||
|
||||
/**
|
||||
* Prevents changing the plugins from then on.
|
||||
* <p>
|
||||
* This allows container-like environments to prevent client messing with plugins.
|
||||
*/
|
||||
public static void lockdown() {
|
||||
lockdown = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the plugins were locked down.
|
||||
*
|
||||
* @return true if the plugins were locked down
|
||||
*/
|
||||
public static boolean isLockdown() {
|
||||
return lockdown;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param uuidProvider the provider to use for generating {@link UUID}s for elements.
|
||||
*/
|
||||
public static void setUUIDProvider(UUIDProvider uuidProvider) {
|
||||
if (lockdown) {
|
||||
throw new IllegalStateException("Plugins can't be changed anymore");
|
||||
}
|
||||
ThriftyParserPlugins.uuidProvider = uuidProvider;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return a {@link UUID} as dictated by {@link #uuidProvider}. Default is random UUIDs.
|
||||
*/
|
||||
public static UUID createUUID() {
|
||||
return uuidProvider.call();
|
||||
}
|
||||
|
||||
public static void reset() {
|
||||
uuidProvider = DEFAULT_UUID_PROVIDER;
|
||||
}
|
||||
|
||||
private ThriftyParserPlugins() {
|
||||
// No instances.
|
||||
}
|
||||
|
||||
/**
|
||||
* A simple provider interface for creating {@link UUID}s.
|
||||
*/
|
||||
public interface UUIDProvider {
|
||||
|
||||
/**
|
||||
* @return a {@link UUID}.
|
||||
*/
|
||||
UUID call();
|
||||
}
|
||||
|
||||
}
|
|
@ -1,59 +0,0 @@
|
|||
/*
|
||||
* Thrifty
|
||||
*
|
||||
* Copyright (c) Microsoft Corporation
|
||||
*
|
||||
* All rights reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the License);
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* THIS CODE IS PROVIDED ON AN *AS IS* BASIS, WITHOUT WARRANTIES OR
|
||||
* CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING
|
||||
* WITHOUT LIMITATION ANY IMPLIED WARRANTIES OR CONDITIONS OF TITLE,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE, MERCHANTABLITY OR NON-INFRINGEMENT.
|
||||
*
|
||||
* See the Apache Version 2.0 License for specific language governing permissions and limitations under the License.
|
||||
*/
|
||||
package com.microsoft.thrifty.schema.parser;
|
||||
|
||||
import com.microsoft.thrifty.schema.Location;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
public abstract class TypeElement {
|
||||
public abstract Location location();
|
||||
public abstract String name();
|
||||
|
||||
@Nullable
|
||||
public abstract AnnotationElement annotations();
|
||||
|
||||
public static TypeElement scalar(Location location, String name, AnnotationElement annotations) {
|
||||
return ScalarTypeElement.create(location, name, annotations);
|
||||
}
|
||||
|
||||
public static TypeElement list(
|
||||
Location location,
|
||||
TypeElement elementType,
|
||||
AnnotationElement annotations) {
|
||||
return ListTypeElement.create(location, elementType, annotations);
|
||||
}
|
||||
|
||||
public static TypeElement set(
|
||||
Location location,
|
||||
TypeElement elementType,
|
||||
AnnotationElement annotations) {
|
||||
return SetTypeElement.create(location, elementType, annotations);
|
||||
}
|
||||
|
||||
public static TypeElement map(
|
||||
Location location,
|
||||
TypeElement key,
|
||||
TypeElement value,
|
||||
AnnotationElement annotations) {
|
||||
return MapTypeElement.create(location, key, value, annotations);
|
||||
}
|
||||
}
|
|
@ -1,61 +0,0 @@
|
|||
/*
|
||||
* Thrifty
|
||||
*
|
||||
* Copyright (c) Microsoft Corporation
|
||||
*
|
||||
* All rights reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the License);
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* THIS CODE IS PROVIDED ON AN *AS IS* BASIS, WITHOUT WARRANTIES OR
|
||||
* CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING
|
||||
* WITHOUT LIMITATION ANY IMPLIED WARRANTIES OR CONDITIONS OF TITLE,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE, MERCHANTABLITY OR NON-INFRINGEMENT.
|
||||
*
|
||||
* See the Apache Version 2.0 License for specific language governing permissions and limitations under the License.
|
||||
*/
|
||||
package com.microsoft.thrifty.schema.parser;
|
||||
|
||||
import com.google.auto.value.AutoValue;
|
||||
import com.microsoft.thrifty.schema.Location;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
@AutoValue
|
||||
public abstract class TypedefElement {
|
||||
public abstract Location location();
|
||||
public abstract String documentation();
|
||||
public abstract TypeElement oldType();
|
||||
public abstract String newName();
|
||||
public abstract UUID uuid();
|
||||
|
||||
@Nullable
|
||||
public abstract AnnotationElement annotations();
|
||||
|
||||
TypedefElement() { }
|
||||
|
||||
public static Builder builder(Location location) {
|
||||
return new AutoValue_TypedefElement.Builder()
|
||||
.location(location)
|
||||
.documentation("")
|
||||
.uuid(ThriftyParserPlugins.createUUID());
|
||||
}
|
||||
|
||||
@AutoValue.Builder
|
||||
public interface Builder {
|
||||
Builder location(Location location);
|
||||
Builder documentation(String documentation);
|
||||
Builder oldType(TypeElement oldType);
|
||||
Builder newName(String newName);
|
||||
Builder uuid(UUID uuid);
|
||||
Builder annotations(AnnotationElement annotations);
|
||||
|
||||
TypedefElement build();
|
||||
}
|
||||
}
|
|
@ -1,28 +0,0 @@
|
|||
/*
|
||||
* Thrifty
|
||||
*
|
||||
* Copyright (c) Microsoft Corporation
|
||||
*
|
||||
* All rights reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the License);
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* THIS CODE IS PROVIDED ON AN *AS IS* BASIS, WITHOUT WARRANTIES OR
|
||||
* CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING
|
||||
* WITHOUT LIMITATION ANY IMPLIED WARRANTIES OR CONDITIONS OF TITLE,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE, MERCHANTABLITY OR NON-INFRINGEMENT.
|
||||
*
|
||||
* See the Apache Version 2.0 License for specific language governing permissions and limitations under the License.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Contains a Thrift parser implementation.
|
||||
*
|
||||
* The output of the parser is an untyped AST - types are not
|
||||
* validated, typedefs are unresolved, etc.
|
||||
*/
|
||||
package com.microsoft.thrifty.schema.parser;
|
|
@ -0,0 +1,185 @@
|
|||
/*
|
||||
* Thrifty
|
||||
*
|
||||
* Copyright (c) Microsoft Corporation
|
||||
*
|
||||
* All rights reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the License);
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* THIS CODE IS PROVIDED ON AN *AS IS* BASIS, WITHOUT WARRANTIES OR
|
||||
* CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING
|
||||
* WITHOUT LIMITATION ANY IMPLIED WARRANTIES OR CONDITIONS OF TITLE,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE, MERCHANTABLITY OR NON-INFRINGEMENT.
|
||||
*
|
||||
* See the Apache Version 2.0 License for specific language governing permissions and limitations under the License.
|
||||
*/
|
||||
package com.microsoft.thrifty.schema.parser
|
||||
|
||||
import com.microsoft.thrifty.schema.Location
|
||||
|
||||
/**
|
||||
* Represents a literal value in a Thrift file for a constant or a field's
|
||||
* default value.
|
||||
*/
|
||||
// TODO: Convert this to a sealed class
|
||||
data class ConstValueElement(
|
||||
/**
|
||||
* The location of the text corresponding to this element.
|
||||
*/
|
||||
@get:JvmName("location")
|
||||
val location: Location,
|
||||
|
||||
/**
|
||||
* The kind of constant value this is - integer, real, list, identifier, etc.
|
||||
*/
|
||||
@get:JvmName("kind")
|
||||
val kind: Kind,
|
||||
|
||||
/**
|
||||
* The actual Thrift text comprising this const value.
|
||||
*/
|
||||
@get:JvmName("thriftText")
|
||||
val thriftText: String,
|
||||
|
||||
/**
|
||||
* The parsed value itself. Will be of a type dictated by the value's [kind].
|
||||
*/
|
||||
@get:JvmName("value")
|
||||
val value: Any
|
||||
) {
|
||||
@get:JvmName("isInt")
|
||||
val isInt: Boolean
|
||||
get() = kind == Kind.INTEGER
|
||||
|
||||
@get:JvmName("isDouble")
|
||||
val isDouble: Boolean
|
||||
get() = kind == Kind.DOUBLE
|
||||
|
||||
@get:JvmName("isString")
|
||||
val isString: Boolean
|
||||
get() = kind == Kind.STRING
|
||||
|
||||
@get:JvmName("isIdentifier")
|
||||
val isIdentifier: Boolean
|
||||
get() = kind == Kind.IDENTIFIER
|
||||
|
||||
@get:JvmName("isList")
|
||||
val isList: Boolean
|
||||
get() = kind == Kind.LIST
|
||||
|
||||
@get:JvmName("isMap")
|
||||
val isMap: Boolean
|
||||
get() = kind == Kind.MAP
|
||||
|
||||
fun getAsInt(): Int {
|
||||
check(isInt) { "Cannot convert to int; kind=$kind" }
|
||||
return (value as Long).toInt()
|
||||
}
|
||||
|
||||
fun getAsLong(): Long {
|
||||
check(isInt) { "Cannot convert to long; kind=$kind" }
|
||||
return value as Long
|
||||
}
|
||||
|
||||
fun getAsDouble(): Double {
|
||||
check(isDouble) { "Cannot convert to double; kind=$kind" }
|
||||
return value as Double
|
||||
}
|
||||
|
||||
fun getAsString(): String {
|
||||
check(isString || isIdentifier) { "Cannot convert to string; kind=$kind" }
|
||||
return value as String
|
||||
}
|
||||
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
fun getAsList(): List<ConstValueElement> {
|
||||
check(isList) { "Cannot convert to list; kind=$kind"}
|
||||
return value as List<ConstValueElement>
|
||||
}
|
||||
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
fun getAsMap(): Map<ConstValueElement, ConstValueElement> {
|
||||
check(isMap) { "Cannot convert to map; kind=$kind" }
|
||||
return value as Map<ConstValueElement, ConstValueElement>
|
||||
}
|
||||
|
||||
/**
|
||||
* Defines the kinds of values representable as a [ConstValueElement].
|
||||
*/
|
||||
enum class Kind {
|
||||
/**
|
||||
* Ye Olde Integer.
|
||||
*
|
||||
* Will actually be a [Long], at runtime.
|
||||
*/
|
||||
INTEGER,
|
||||
|
||||
/**
|
||||
* A 64-it floating-point number.
|
||||
*/
|
||||
DOUBLE,
|
||||
|
||||
/**
|
||||
* A quoted string.
|
||||
*/
|
||||
STRING,
|
||||
|
||||
/**
|
||||
* An unquoted string, naming some other Thrift entity.
|
||||
*/
|
||||
IDENTIFIER,
|
||||
|
||||
/**
|
||||
* A list of [ConstValueElements][ConstValueElement]
|
||||
*
|
||||
* It is assumed that all values in the list share the same type; this
|
||||
* is not enforced by the parser.
|
||||
*/
|
||||
LIST,
|
||||
|
||||
/**
|
||||
* An key-value mapping of [ConstValueElements][ConstValueElement].
|
||||
*
|
||||
* It is assumed that all keys share the same type, and that all values
|
||||
* also share the same type. This is not enforced by the parser.
|
||||
*/
|
||||
MAP,
|
||||
}
|
||||
|
||||
companion object {
|
||||
@JvmStatic
|
||||
fun integer(location: Location, text: String, value: Long): ConstValueElement {
|
||||
return ConstValueElement(location, Kind.INTEGER, text, value)
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun real(location: Location, text: String, value: Double): ConstValueElement {
|
||||
return ConstValueElement(location, Kind.DOUBLE, text, value)
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun literal(location: Location, text: String, value: String): ConstValueElement {
|
||||
return ConstValueElement(location, Kind.STRING, text, value)
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun identifier(location: Location, text: String, value: String): ConstValueElement {
|
||||
return ConstValueElement(location, Kind.IDENTIFIER, text, value)
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun list(location: Location, text: String, value: List<ConstValueElement>): ConstValueElement {
|
||||
return ConstValueElement(location, Kind.LIST, text, value.toList())
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun map(location: Location, text: String, value: Map<ConstValueElement, ConstValueElement>): ConstValueElement {
|
||||
return ConstValueElement(location, Kind.MAP, text, value.toMap())
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,677 @@
|
|||
/*
|
||||
* Thrifty
|
||||
*
|
||||
* Copyright (c) Microsoft Corporation
|
||||
*
|
||||
* All rights reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the License);
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* THIS CODE IS PROVIDED ON AN *AS IS* BASIS, WITHOUT WARRANTIES OR
|
||||
* CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING
|
||||
* WITHOUT LIMITATION ANY IMPLIED WARRANTIES OR CONDITIONS OF TITLE,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE, MERCHANTABLITY OR NON-INFRINGEMENT.
|
||||
*
|
||||
* See the Apache Version 2.0 License for specific language governing permissions and limitations under the License.
|
||||
*/
|
||||
package com.microsoft.thrifty.schema.parser
|
||||
|
||||
import com.microsoft.thrifty.schema.Location
|
||||
import com.microsoft.thrifty.schema.NamespaceScope
|
||||
import com.microsoft.thrifty.schema.Requiredness
|
||||
import java.util.UUID
|
||||
|
||||
/**
|
||||
* Represents an instance of one or more Thrift annotations.
|
||||
*
|
||||
* @constructor Creates a new instance of [AnnotationElement].
|
||||
*/
|
||||
data class AnnotationElement(
|
||||
/**
|
||||
* The location of the text corresponding to this element.
|
||||
*/
|
||||
@get:JvmName("location")
|
||||
val location: Location,
|
||||
|
||||
/**
|
||||
* The annotation values.
|
||||
*/
|
||||
@get:JvmName("values")
|
||||
val values: Map<String, String>
|
||||
) {
|
||||
/**
|
||||
* True if this element contains no annotation values, otherwise false.
|
||||
*/
|
||||
@get:JvmName("isEmpty")
|
||||
val isEmpty: Boolean
|
||||
get() = values.isEmpty()
|
||||
|
||||
/**
|
||||
* The number of annotation values in this element.
|
||||
*/
|
||||
@get:JvmName("size")
|
||||
val size: Int
|
||||
get() = values.size
|
||||
|
||||
/**
|
||||
* Gets the value of the given annotation [key], if present.
|
||||
*/
|
||||
operator fun get(key: String): String? = values[key]
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents the inclusion of one Thrift program into another.
|
||||
*/
|
||||
data class IncludeElement(
|
||||
/**
|
||||
* The location of the text corresponding to this element.
|
||||
*/
|
||||
@get:JvmName("location")
|
||||
val location: Location,
|
||||
|
||||
/**
|
||||
* Indicates whether or not this is a `cpp_include` statement.
|
||||
*/
|
||||
@get:JvmName("isCpp")
|
||||
val isCpp: Boolean,
|
||||
|
||||
/**
|
||||
* The path (relative or absolute) of the included program.
|
||||
*/
|
||||
@get:JvmName("path")
|
||||
val path: String
|
||||
)
|
||||
|
||||
/**
|
||||
* Represents the declaration of a new name for an existing type.
|
||||
*/
|
||||
data class TypedefElement @JvmOverloads constructor(
|
||||
/**
|
||||
* The location of the text corresponding to this element.
|
||||
*/
|
||||
@get:JvmName("location")
|
||||
val location: Location,
|
||||
|
||||
/**
|
||||
* The type receiving a new name.
|
||||
*/
|
||||
@get:JvmName("oldType")
|
||||
val oldType: TypeElement,
|
||||
|
||||
/**
|
||||
* The new name for [oldType].
|
||||
*/
|
||||
@get:JvmName("newName")
|
||||
val newName: String,
|
||||
|
||||
/**
|
||||
* The documentation associated with this element, if any.
|
||||
*/
|
||||
@get:JvmName("documentation")
|
||||
val documentation: String = "",
|
||||
|
||||
/**
|
||||
* A UUID uniquely identifying this element.
|
||||
*/
|
||||
@get:JvmName("uuid")
|
||||
val uuid: UUID = ThriftyParserPlugins.createUUID(),
|
||||
|
||||
/**
|
||||
* The annotations associated with this element, if any.
|
||||
*/
|
||||
@get:JvmName("annotations")
|
||||
val annotations: AnnotationElement? = null
|
||||
)
|
||||
|
||||
/**
|
||||
* Represents the declaration of a language-specific namespace in a Thrift
|
||||
* program.
|
||||
*/
|
||||
data class NamespaceElement @JvmOverloads constructor(
|
||||
/**
|
||||
* The location of the text corresponding to this element.
|
||||
*/
|
||||
@get:JvmName("location")
|
||||
val location: Location,
|
||||
|
||||
/**
|
||||
* The language ("scope") to which this declaration applies.
|
||||
*/
|
||||
@get:JvmName("scope")
|
||||
val scope: NamespaceScope,
|
||||
|
||||
/**
|
||||
* The name of the namespace.
|
||||
*/
|
||||
@get:JvmName("namespace")
|
||||
val namespace: String,
|
||||
|
||||
/**
|
||||
* The annotations associated with this element, if any.
|
||||
*/
|
||||
@get:JvmName("annotations")
|
||||
val annotations: AnnotationElement? = null
|
||||
)
|
||||
|
||||
/**
|
||||
* Represents the declaration of a named constant value in a Thrift program.
|
||||
*/
|
||||
data class ConstElement @JvmOverloads constructor(
|
||||
/**
|
||||
* The location of the text corresponding to this element.
|
||||
*/
|
||||
@get:JvmName("location")
|
||||
val location: Location,
|
||||
|
||||
/**
|
||||
* The type of the constant's value.
|
||||
*/
|
||||
@get:JvmName("type")
|
||||
val type: TypeElement,
|
||||
|
||||
/**
|
||||
* The name of the constant.
|
||||
*/
|
||||
@get:JvmName("name")
|
||||
val name: String,
|
||||
|
||||
/**
|
||||
* The literal value of the constant.
|
||||
*
|
||||
* This is not guaranteed by the parser to conform to the declared [type].
|
||||
*/
|
||||
@get:JvmName("value")
|
||||
val value: ConstValueElement,
|
||||
|
||||
/**
|
||||
* The documentation associated with this element, if any.
|
||||
*/
|
||||
@get:JvmName("documentation")
|
||||
val documentation: String = "",
|
||||
|
||||
/**
|
||||
* A UUID uniquely identifying this element.
|
||||
*/
|
||||
@get:JvmName("uuid")
|
||||
val uuid: UUID = ThriftyParserPlugins.createUUID()
|
||||
)
|
||||
|
||||
/**
|
||||
* Represents a single named member of a Thrift enumeration.
|
||||
*/
|
||||
data class EnumMemberElement @JvmOverloads constructor(
|
||||
/**
|
||||
* The location of the text corresponding to this element.
|
||||
*/
|
||||
@get:JvmName("location")
|
||||
val location: Location,
|
||||
|
||||
/**
|
||||
* The name of the enum member.
|
||||
*/
|
||||
@get:JvmName("name")
|
||||
val name: String,
|
||||
|
||||
/**
|
||||
* The integral value associated with the enum member.
|
||||
*/
|
||||
@get:JvmName("value")
|
||||
val value: Int,
|
||||
|
||||
/**
|
||||
* The documentation associated with this element, if any.
|
||||
*/
|
||||
@get:JvmName("documentation")
|
||||
val documentation: String = "",
|
||||
|
||||
/**
|
||||
* The annotations associated with the enum member, if any.
|
||||
*/
|
||||
@get:JvmName("annotations")
|
||||
val annotations: AnnotationElement? = null,
|
||||
|
||||
/**
|
||||
* A UUID uniquely identifying this element.
|
||||
*/
|
||||
@get:JvmName("uuid")
|
||||
val uuid: UUID = ThriftyParserPlugins.createUUID()
|
||||
)
|
||||
|
||||
/**
|
||||
* Represents a Thrift enumeration.
|
||||
*/
|
||||
data class EnumElement @JvmOverloads constructor(
|
||||
/**
|
||||
* The location of the text corresponding to this element.
|
||||
*/
|
||||
@get:JvmName("location")
|
||||
val location: Location,
|
||||
|
||||
/**
|
||||
* The name of the enumeration.
|
||||
*/
|
||||
@get:JvmName("name")
|
||||
val name: String,
|
||||
|
||||
/**
|
||||
* The members comprising this enumeration.
|
||||
*/
|
||||
@get:JvmName("members")
|
||||
val members: List<EnumMemberElement>,
|
||||
|
||||
/**
|
||||
* The documentation associated with this enumeration, if any.
|
||||
*/
|
||||
@get:JvmName("documentation")
|
||||
val documentation: String = "",
|
||||
|
||||
/**
|
||||
* The annotations associated with this element, if any.
|
||||
*/
|
||||
@get:JvmName("annotations")
|
||||
val annotations: AnnotationElement? = null,
|
||||
|
||||
/**
|
||||
* A UUID uniquely identifying this element.
|
||||
*/
|
||||
@get:JvmName("uuid")
|
||||
val uuid: UUID = ThriftyParserPlugins.createUUID()
|
||||
)
|
||||
|
||||
/**
|
||||
* Represents a field in a Thrift struct, union, or exception.
|
||||
*/
|
||||
data class FieldElement @JvmOverloads constructor(
|
||||
/**
|
||||
* The location of the text corresponding to this element.
|
||||
*/
|
||||
@get:JvmName("location")
|
||||
val location: Location,
|
||||
|
||||
/**
|
||||
* The integer ID of the field.
|
||||
*/
|
||||
@get:JvmName("fieldId")
|
||||
val fieldId: Int,
|
||||
|
||||
/**
|
||||
* The type of the field.
|
||||
*/
|
||||
@get:JvmName("type")
|
||||
val type: TypeElement,
|
||||
|
||||
/**
|
||||
* The name of the field.
|
||||
*/
|
||||
@get:JvmName("name")
|
||||
val name: String,
|
||||
|
||||
/**
|
||||
* The [Requiredness] of the field.
|
||||
*/
|
||||
@get:JvmName("requiredness")
|
||||
val requiredness: Requiredness = Requiredness.DEFAULT,
|
||||
|
||||
/**
|
||||
* The documentation associated with the field, if any.
|
||||
*/
|
||||
@get:JvmName("documentation")
|
||||
val documentation: String = "",
|
||||
|
||||
/**
|
||||
* The default value of the field, if any.
|
||||
*/
|
||||
@get:JvmName("constValue")
|
||||
val constValue: ConstValueElement? = null,
|
||||
|
||||
/**
|
||||
* The annotations associated with the field, if any.
|
||||
*/
|
||||
@get:JvmName("annotations")
|
||||
val annotations: AnnotationElement? = null,
|
||||
|
||||
/**
|
||||
* A UUID uniquely identifying this element.
|
||||
*/
|
||||
@get:JvmName("uuid")
|
||||
val uuid: UUID = ThriftyParserPlugins.createUUID()
|
||||
)
|
||||
|
||||
/**
|
||||
* Represents the definition of a Thrift struct, union, or exception.
|
||||
*/
|
||||
data class StructElement @JvmOverloads constructor(
|
||||
/**
|
||||
* The location of the text corresponding to this element.
|
||||
*/
|
||||
@get:JvmName("location")
|
||||
val location: Location,
|
||||
|
||||
/**
|
||||
* The name of the struct, union, or exception.
|
||||
*/
|
||||
@get:JvmName("name")
|
||||
val name: String,
|
||||
|
||||
/**
|
||||
* The kind of struct represented by this element: struct, union, or exception.
|
||||
*/
|
||||
@get:JvmName("type")
|
||||
val type: Type,
|
||||
|
||||
/**
|
||||
* The fields comprising this struct.
|
||||
*/
|
||||
@get:JvmName("fields")
|
||||
val fields: List<FieldElement>,
|
||||
|
||||
/**
|
||||
* The documentation associated with this struct, if any.
|
||||
*/
|
||||
@get:JvmName("documentation")
|
||||
val documentation: String = "",
|
||||
|
||||
/**
|
||||
* The annotations associated with this element, if any.
|
||||
*/
|
||||
@get:JvmName("annotations")
|
||||
val annotations: AnnotationElement? = null,
|
||||
|
||||
/**
|
||||
* A UUID uniquely identifying this element.
|
||||
*/
|
||||
@get:JvmName("uuid")
|
||||
val uuid: UUID = ThriftyParserPlugins.createUUID()
|
||||
) {
|
||||
/**
|
||||
* Defines the different types of structured element in the Thrift language.
|
||||
*/
|
||||
enum class Type {
|
||||
/**
|
||||
* A struct, in the C sense of the term. That is, an ordered set of
|
||||
* named fields having heterogeneous types.
|
||||
*/
|
||||
STRUCT,
|
||||
|
||||
/**
|
||||
* A union, also in the C sense of the term. A set of named fields, of
|
||||
* which at most one may have a value at the same time.
|
||||
*/
|
||||
UNION,
|
||||
|
||||
/**
|
||||
* An exception is like a [STRUCT], except that it serves as an error
|
||||
* type that communicates failure from an RPC call. Declared as part
|
||||
* of a [FunctionElement].
|
||||
*/
|
||||
EXCEPTION
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents an RPC function declaration.
|
||||
*
|
||||
* Functions are always declared within a [ServiceElement].
|
||||
*/
|
||||
data class FunctionElement @JvmOverloads constructor(
|
||||
/**
|
||||
* The location of the text corresponding to this element.
|
||||
*/
|
||||
@get:JvmName("location")
|
||||
val location: Location,
|
||||
|
||||
/**
|
||||
* The name of the function.
|
||||
*/
|
||||
@get:JvmName("name")
|
||||
val name: String,
|
||||
|
||||
/**
|
||||
* The return type of the function. May be `void` to indicate no
|
||||
* return type.
|
||||
*/
|
||||
@get:JvmName("returnType")
|
||||
val returnType: TypeElement,
|
||||
|
||||
/**
|
||||
* A list, possibly empty, of function parameters.
|
||||
*/
|
||||
@get:JvmName("params")
|
||||
val params: List<FieldElement> = emptyList(),
|
||||
|
||||
/**
|
||||
* A list, possibly empty, of exceptions thrown by this function.
|
||||
*/
|
||||
@get:JvmName("exceptions")
|
||||
val exceptions: List<FieldElement> = emptyList(),
|
||||
|
||||
/**
|
||||
* True if the function is `oneway`, otherwise false.
|
||||
*
|
||||
* A function declared with the `oneway` keyword has no return type,
|
||||
* and generated service clients will not wait for a response from
|
||||
* the remote endpoint when making a one-way RPC call.
|
||||
*
|
||||
* One-way functions must have a return type of `void`; this is not
|
||||
* validated at parse time.
|
||||
*/
|
||||
@get:JvmName("oneWay")
|
||||
val oneWay: Boolean = false,
|
||||
|
||||
/**
|
||||
* The documentation associated with this function, if any.
|
||||
*/
|
||||
@get:JvmName("documentation")
|
||||
val documentation: String = "",
|
||||
|
||||
/**
|
||||
* The annotations associated with this function, if any.
|
||||
*/
|
||||
@get:JvmName("annotations")
|
||||
val annotations: AnnotationElement? = null,
|
||||
|
||||
/**
|
||||
* A UUID uniquely identifying this element.
|
||||
*/
|
||||
@get:JvmName("uuid")
|
||||
val uuid: UUID = ThriftyParserPlugins.createUUID()
|
||||
)
|
||||
|
||||
/**
|
||||
* Represents the declaration of a Thrift service.
|
||||
*
|
||||
* A service is an entity having zero or more functions that can be invoked by
|
||||
* remote clients. Services can inherit the definition of other services, very
|
||||
* much like inheritance in an object-oriented language.
|
||||
*/
|
||||
data class ServiceElement @JvmOverloads constructor(
|
||||
/**
|
||||
* The location of the text corresponding to this element.
|
||||
*/
|
||||
@get:JvmName("location")
|
||||
val location: Location,
|
||||
|
||||
/**
|
||||
* The name of the service.
|
||||
*/
|
||||
@get:JvmName("name")
|
||||
val name: String,
|
||||
|
||||
/**
|
||||
* The list, possibly empty, of functions defined by this service.
|
||||
*/
|
||||
@get:JvmName("functions")
|
||||
val functions: List<FunctionElement> = emptyList(),
|
||||
|
||||
/**
|
||||
* The base type, if any, of this service.
|
||||
*
|
||||
* A base type is presumed to refer to another service, but that is not
|
||||
* enforced by the parser.
|
||||
*/
|
||||
@get:JvmName("extendsService")
|
||||
val extendsService: TypeElement? = null,
|
||||
|
||||
/**
|
||||
* The documentation associated with this service, if any.
|
||||
*/
|
||||
@get:JvmName("documentation")
|
||||
val documentation: String = "",
|
||||
|
||||
/**
|
||||
* The annotations associated with this service, if any.
|
||||
*/
|
||||
@get:JvmName("annotations")
|
||||
val annotations: AnnotationElement? = null,
|
||||
|
||||
/**
|
||||
* A UUID uniquely identifying this element.
|
||||
*/
|
||||
@get:JvmName("uuid")
|
||||
val uuid: UUID = ThriftyParserPlugins.createUUID()
|
||||
)
|
||||
|
||||
/**
|
||||
* Represents a Thrift file, and everything defined within it.
|
||||
*/
|
||||
data class ThriftFileElement(
|
||||
/**
|
||||
* The location of the text corresponding to this element.
|
||||
*/
|
||||
@get:JvmName("location")
|
||||
val location: Location,
|
||||
|
||||
/**
|
||||
* The list of all namespaces defined within this file.
|
||||
*/
|
||||
@get:JvmName("namespaces")
|
||||
val namespaces: List<NamespaceElement> = emptyList(),
|
||||
|
||||
/**
|
||||
* The list of all other thrift files included by this file.
|
||||
*/
|
||||
@get:JvmName("includes")
|
||||
val includes: List<IncludeElement> = emptyList(),
|
||||
|
||||
/**
|
||||
* The list of all constants defined within this file.
|
||||
*/
|
||||
@get:JvmName("constants")
|
||||
val constants: List<ConstElement> = emptyList(),
|
||||
|
||||
/**
|
||||
* The list of all typedefs defined within this file.
|
||||
*/
|
||||
@get:JvmName("typedefs")
|
||||
val typedefs: List<TypedefElement> = emptyList(),
|
||||
|
||||
/**
|
||||
* The list of all enums defined within this file.
|
||||
*/
|
||||
@get:JvmName("enums")
|
||||
val enums: List<EnumElement> = emptyList(),
|
||||
|
||||
/**
|
||||
* The list of all structs defined within this file.
|
||||
*/
|
||||
@get:JvmName("structs")
|
||||
val structs: List<StructElement> = emptyList(),
|
||||
|
||||
/**
|
||||
* The list of all unions defined within this file.
|
||||
*/
|
||||
@get:JvmName("unions")
|
||||
val unions: List<StructElement> = emptyList(),
|
||||
|
||||
/**
|
||||
* The list of all exceptions defined within this file.
|
||||
*/
|
||||
@get:JvmName("exceptions")
|
||||
val exceptions: List<StructElement> = emptyList(),
|
||||
|
||||
/**
|
||||
* The list of all services defined within this file.
|
||||
*/
|
||||
@get:JvmName("services")
|
||||
val services: List<ServiceElement> = emptyList()
|
||||
) {
|
||||
class Builder(private val location: Location) {
|
||||
private var namespaces = emptyList<NamespaceElement>()
|
||||
private var includes = emptyList<IncludeElement>()
|
||||
private var constants = emptyList<ConstElement>()
|
||||
private var typedefs = emptyList<TypedefElement>()
|
||||
private var enums = emptyList<EnumElement>()
|
||||
private var structs = emptyList<StructElement>()
|
||||
private var unions = emptyList<StructElement>()
|
||||
private var exceptions = emptyList<StructElement>()
|
||||
private var services: List<ServiceElement> = emptyList()
|
||||
|
||||
fun namespaces(namespaces: List<NamespaceElement>): Builder {
|
||||
this.namespaces = namespaces
|
||||
return this
|
||||
}
|
||||
|
||||
fun includes(includes: List<IncludeElement>): Builder {
|
||||
this.includes = includes
|
||||
return this
|
||||
}
|
||||
|
||||
fun constants(constants: List<ConstElement>): Builder {
|
||||
this.constants = constants
|
||||
return this
|
||||
}
|
||||
|
||||
fun typedefs(typedefs: List<TypedefElement>): Builder {
|
||||
this.typedefs = typedefs
|
||||
return this
|
||||
}
|
||||
|
||||
fun enums(enums: List<EnumElement>): Builder {
|
||||
this.enums = enums
|
||||
return this
|
||||
}
|
||||
|
||||
fun structs(structs: List<StructElement>): Builder {
|
||||
this.structs = structs
|
||||
return this
|
||||
}
|
||||
|
||||
fun unions(unions: List<StructElement>): Builder {
|
||||
this.unions = unions
|
||||
return this
|
||||
}
|
||||
|
||||
fun exceptions(exceptions: List<StructElement>): Builder {
|
||||
this.exceptions = exceptions
|
||||
return this
|
||||
}
|
||||
|
||||
fun services(services: List<ServiceElement>): Builder {
|
||||
this.services = services
|
||||
return this
|
||||
}
|
||||
|
||||
fun build(): ThriftFileElement {
|
||||
return ThriftFileElement(
|
||||
location = location,
|
||||
namespaces = namespaces,
|
||||
includes = includes,
|
||||
constants = constants,
|
||||
typedefs = typedefs,
|
||||
enums = enums,
|
||||
structs = structs,
|
||||
unions = unions,
|
||||
exceptions = exceptions,
|
||||
services = services
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
@JvmStatic fun builder(location: Location) = Builder(location)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,781 @@
|
|||
/*
|
||||
* Thrifty
|
||||
*
|
||||
* Copyright (c) Microsoft Corporation
|
||||
*
|
||||
* All rights reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the License);
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* THIS CODE IS PROVIDED ON AN *AS IS* BASIS, WITHOUT WARRANTIES OR
|
||||
* CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING
|
||||
* WITHOUT LIMITATION ANY IMPLIED WARRANTIES OR CONDITIONS OF TITLE,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE, MERCHANTABLITY OR NON-INFRINGEMENT.
|
||||
*
|
||||
* See the Apache Version 2.0 License for specific language governing permissions and limitations under the License.
|
||||
*/
|
||||
package com.microsoft.thrifty.schema.parser
|
||||
|
||||
import com.google.common.collect.ImmutableList
|
||||
import com.google.common.collect.ImmutableMap
|
||||
import com.microsoft.thrifty.schema.ErrorReporter
|
||||
import com.microsoft.thrifty.schema.Location
|
||||
import com.microsoft.thrifty.schema.NamespaceScope
|
||||
import com.microsoft.thrifty.schema.Requiredness
|
||||
import com.microsoft.thrifty.schema.antlr.AntlrThriftBaseListener
|
||||
import com.microsoft.thrifty.schema.antlr.AntlrThriftLexer
|
||||
import com.microsoft.thrifty.schema.antlr.AntlrThriftParser
|
||||
import org.antlr.v4.runtime.CommonTokenStream
|
||||
import org.antlr.v4.runtime.Lexer
|
||||
import org.antlr.v4.runtime.ParserRuleContext
|
||||
import org.antlr.v4.runtime.Token
|
||||
import org.antlr.v4.runtime.tree.ErrorNode
|
||||
import org.antlr.v4.runtime.tree.TerminalNode
|
||||
import java.util.BitSet
|
||||
|
||||
/**
|
||||
* A set of callbacks that, when used with a [org.antlr.v4.runtime.tree.ParseTreeWalker],
|
||||
* assemble a [ThriftFileElement] from an [AntlrThriftParser].
|
||||
*
|
||||
* Instances of this class are single-use; after walking a parse tree, it will contain
|
||||
* that parser's state as thrifty-schema parser elements.
|
||||
*
|
||||
* @param tokenStream the same token stream used with the corresponding [AntlrThriftParser];
|
||||
* this stream will be queried for "hidden" tokens containing parsed doc
|
||||
* comments.
|
||||
* @param errorReporter an error reporting mechanism, used to communicate errors during parsing.
|
||||
* @param location a location pointing at the beginning of the file being parsed.
|
||||
*/
|
||||
internal class ThriftListener(
|
||||
private val tokenStream: CommonTokenStream,
|
||||
private val errorReporter: ErrorReporter,
|
||||
private val location: Location
|
||||
) : AntlrThriftBaseListener() {
|
||||
|
||||
// We need to record which comment tokens have been treated as trailing documentation,
|
||||
// so that scanning for leading doc tokens for subsequent elements knows where to stop.
|
||||
// We can do this with a bitset tracking token indices of trailing-comment tokens.
|
||||
private val trailingDocTokenIndexes = BitSet(INITIAL_BITSET_CAPACITY)
|
||||
|
||||
private val includes = mutableListOf<IncludeElement>()
|
||||
private val namespaces = mutableListOf<NamespaceElement>()
|
||||
private val enums = mutableListOf<EnumElement>()
|
||||
private val typedefs = mutableListOf<TypedefElement>()
|
||||
private val structs = mutableListOf<StructElement>()
|
||||
private val unions = mutableListOf<StructElement>()
|
||||
private val exceptions = mutableListOf<StructElement>()
|
||||
private val consts = mutableListOf<ConstElement>()
|
||||
private val services = mutableListOf<ServiceElement>()
|
||||
|
||||
fun buildFileElement(): ThriftFileElement {
|
||||
return ThriftFileElement.builder(location)
|
||||
.includes(includes)
|
||||
.namespaces(namespaces)
|
||||
.typedefs(typedefs)
|
||||
.enums(enums)
|
||||
.structs(structs)
|
||||
.unions(unions)
|
||||
.exceptions(exceptions)
|
||||
.constants(consts)
|
||||
.services(services)
|
||||
.build()
|
||||
}
|
||||
|
||||
override fun exitInclude(ctx: AntlrThriftParser.IncludeContext) {
|
||||
val pathNode = ctx.LITERAL()
|
||||
val path = unquote(locationOf(pathNode), pathNode.text, false)
|
||||
|
||||
includes.add(IncludeElement(locationOf(ctx), false, path))
|
||||
}
|
||||
|
||||
override fun exitCppInclude(ctx: AntlrThriftParser.CppIncludeContext) {
|
||||
val pathNode = ctx.LITERAL()
|
||||
val path = unquote(locationOf(pathNode), pathNode.text, false)
|
||||
|
||||
includes.add(IncludeElement(locationOf(ctx), true, path))
|
||||
}
|
||||
|
||||
override fun exitStandardNamespace(ctx: AntlrThriftParser.StandardNamespaceContext) {
|
||||
val scopeName = ctx.namespaceScope().text
|
||||
val name = ctx.ns.text
|
||||
|
||||
val annotations = annotationsFromAntlr(ctx.annotationList())
|
||||
|
||||
val scope = NamespaceScope.forThriftName(scopeName)
|
||||
if (scope == null) {
|
||||
errorReporter.warn(locationOf(ctx.namespaceScope()), "Unknown namespace scope '$scopeName'")
|
||||
return
|
||||
}
|
||||
|
||||
val element = NamespaceElement(
|
||||
location = locationOf(ctx),
|
||||
scope = scope,
|
||||
namespace = name,
|
||||
annotations = annotations)
|
||||
|
||||
namespaces.add(element)
|
||||
}
|
||||
|
||||
override fun exitPhpNamespace(ctx: AntlrThriftParser.PhpNamespaceContext) {
|
||||
val element = NamespaceElement(
|
||||
locationOf(ctx),
|
||||
scope = NamespaceScope.PHP,
|
||||
namespace = unquote(locationOf(ctx.LITERAL()), ctx.LITERAL().text),
|
||||
annotations = annotationsFromAntlr(ctx.annotationList()))
|
||||
|
||||
namespaces.add(element)
|
||||
}
|
||||
|
||||
override fun exitXsdNamespace(ctx: AntlrThriftParser.XsdNamespaceContext) {
|
||||
errorReporter.error(locationOf(ctx), "'xsd_namespace' is unsupported")
|
||||
}
|
||||
|
||||
override fun exitSenum(ctx: AntlrThriftParser.SenumContext) {
|
||||
errorReporter.error(locationOf(ctx), "'senum' is unsupported; use 'enum' instead")
|
||||
}
|
||||
|
||||
override fun exitEnumDef(ctx: AntlrThriftParser.EnumDefContext) {
|
||||
val enumName = ctx.IDENTIFIER().text
|
||||
|
||||
var nextValue = 0
|
||||
val values = mutableSetOf<Int>()
|
||||
|
||||
val members = ArrayList<EnumMemberElement>(ctx.enumMember().size)
|
||||
for (memberContext in ctx.enumMember()) {
|
||||
var value = nextValue
|
||||
|
||||
val valueToken = memberContext.INTEGER()
|
||||
if (valueToken != null) {
|
||||
value = parseInt(valueToken)
|
||||
}
|
||||
|
||||
if (!values.add(value)) {
|
||||
errorReporter.error(locationOf(memberContext), "duplicate enum value: $value")
|
||||
continue
|
||||
}
|
||||
|
||||
nextValue = value + 1
|
||||
|
||||
val element = EnumMemberElement(
|
||||
location = locationOf(memberContext),
|
||||
name = memberContext.IDENTIFIER().text,
|
||||
value = value,
|
||||
documentation = formatJavadoc(memberContext),
|
||||
annotations = annotationsFromAntlr(memberContext.annotationList()))
|
||||
|
||||
members.add(element)
|
||||
}
|
||||
|
||||
val doc = formatJavadoc(ctx)
|
||||
val element = EnumElement(
|
||||
location = locationOf(ctx),
|
||||
name = enumName,
|
||||
documentation = doc,
|
||||
annotations = annotationsFromAntlr(ctx.annotationList()),
|
||||
members = members)
|
||||
|
||||
enums.add(element)
|
||||
}
|
||||
|
||||
override fun exitStructDef(ctx: AntlrThriftParser.StructDefContext) {
|
||||
val name = ctx.IDENTIFIER().text
|
||||
val fields = parseFieldList(ctx.field())
|
||||
|
||||
val element = StructElement(
|
||||
location = locationOf(ctx),
|
||||
name = name,
|
||||
fields = fields,
|
||||
type = StructElement.Type.STRUCT,
|
||||
documentation = formatJavadoc(ctx),
|
||||
annotations = annotationsFromAntlr(ctx.annotationList()))
|
||||
|
||||
structs.add(element)
|
||||
}
|
||||
|
||||
override fun exitUnionDef(ctx: AntlrThriftParser.UnionDefContext) {
|
||||
val name = ctx.IDENTIFIER().text
|
||||
val fields = parseFieldList(ctx.field())
|
||||
|
||||
var numFieldsWithDefaultValues = 0
|
||||
for (i in fields.indices) {
|
||||
val element = fields[i]
|
||||
if (element.requiredness == Requiredness.REQUIRED) {
|
||||
val fieldContext = ctx.field(i)
|
||||
errorReporter.error(locationOf(fieldContext), "unions cannot have required fields")
|
||||
}
|
||||
|
||||
if (element.constValue != null) {
|
||||
++numFieldsWithDefaultValues
|
||||
}
|
||||
}
|
||||
|
||||
if (numFieldsWithDefaultValues > 1) {
|
||||
errorReporter.error(locationOf(ctx), "unions can have at most one default value")
|
||||
}
|
||||
|
||||
val element = StructElement(
|
||||
location = locationOf(ctx),
|
||||
name = name,
|
||||
fields = fields,
|
||||
type = StructElement.Type.UNION,
|
||||
documentation = formatJavadoc(ctx),
|
||||
annotations = annotationsFromAntlr(ctx.annotationList()))
|
||||
|
||||
unions.add(element)
|
||||
}
|
||||
|
||||
override fun exitExceptionDef(ctx: AntlrThriftParser.ExceptionDefContext) {
|
||||
val name = ctx.IDENTIFIER().text
|
||||
val fields = parseFieldList(ctx.field())
|
||||
|
||||
val element = StructElement(
|
||||
location = locationOf(ctx),
|
||||
name = name,
|
||||
fields = fields,
|
||||
type = StructElement.Type.EXCEPTION,
|
||||
documentation = formatJavadoc(ctx),
|
||||
annotations = annotationsFromAntlr(ctx.annotationList()))
|
||||
|
||||
exceptions.add(element)
|
||||
}
|
||||
|
||||
private fun parseFieldList(
|
||||
contexts: List<AntlrThriftParser.FieldContext>,
|
||||
defaultRequiredness: Requiredness = Requiredness.DEFAULT): ImmutableList<FieldElement> {
|
||||
var builder: ImmutableList.Builder<FieldElement> = ImmutableList.builder()
|
||||
val ids = mutableSetOf<Int>()
|
||||
|
||||
var nextValue = 1
|
||||
for (fieldContext in contexts) {
|
||||
val element = parseField(nextValue, fieldContext, defaultRequiredness)
|
||||
if (element != null) {
|
||||
builder = builder.add(element)
|
||||
|
||||
if (!ids.add(element.fieldId)) {
|
||||
errorReporter.error(locationOf(fieldContext), "duplicate field ID: ${element.fieldId}")
|
||||
}
|
||||
|
||||
if (element.fieldId <= 0) {
|
||||
errorReporter.error(locationOf(fieldContext), "field ID must be greater than zero")
|
||||
}
|
||||
|
||||
if (element.fieldId >= nextValue) {
|
||||
nextValue = element.fieldId + 1
|
||||
}
|
||||
} else {
|
||||
// assert-fail here?
|
||||
++nextValue // this represents an error condition
|
||||
}
|
||||
}
|
||||
|
||||
return builder.build()
|
||||
}
|
||||
|
||||
private fun parseField(
|
||||
defaultValue: Int,
|
||||
ctx: AntlrThriftParser.FieldContext,
|
||||
defaultRequiredness: Requiredness): FieldElement? {
|
||||
|
||||
val fieldId = ctx.INTEGER()?.let { parseInt(it) } ?: defaultValue
|
||||
val fieldName = ctx.IDENTIFIER().text
|
||||
|
||||
val requiredness = if (ctx.requiredness() != null) {
|
||||
when {
|
||||
ctx.requiredness().text == "required" -> Requiredness.REQUIRED
|
||||
ctx.requiredness().text == "optional" -> Requiredness.OPTIONAL
|
||||
else -> throw AssertionError("Unexpected requiredness value: " + ctx.requiredness().text)
|
||||
}
|
||||
} else {
|
||||
defaultRequiredness
|
||||
}
|
||||
|
||||
return FieldElement(
|
||||
location = locationOf(ctx),
|
||||
documentation = formatJavadoc(ctx),
|
||||
fieldId = fieldId,
|
||||
requiredness = requiredness,
|
||||
type = typeElementOf(ctx.fieldType()),
|
||||
name = fieldName,
|
||||
annotations = annotationsFromAntlr(ctx.annotationList()),
|
||||
constValue = constValueElementOf(ctx.constValue()))
|
||||
}
|
||||
|
||||
override fun exitTypedef(ctx: AntlrThriftParser.TypedefContext) {
|
||||
val oldType = typeElementOf(ctx.fieldType())
|
||||
|
||||
val typedef = TypedefElement(
|
||||
location = locationOf(ctx),
|
||||
documentation = formatJavadoc(ctx),
|
||||
annotations = annotationsFromAntlr(ctx.annotationList()),
|
||||
oldType = oldType,
|
||||
newName = ctx.IDENTIFIER().text)
|
||||
|
||||
typedefs.add(typedef)
|
||||
}
|
||||
|
||||
override fun exitConstDef(ctx: AntlrThriftParser.ConstDefContext) {
|
||||
val constValue = constValueElementOf(ctx.constValue())
|
||||
if (constValue == null) {
|
||||
errorReporter.error(
|
||||
locationOf(ctx.constValue()),
|
||||
"Invalid const value")
|
||||
return
|
||||
}
|
||||
|
||||
val element = ConstElement(
|
||||
location = locationOf(ctx),
|
||||
documentation = formatJavadoc(ctx),
|
||||
type = typeElementOf(ctx.fieldType()),
|
||||
name = ctx.IDENTIFIER().text,
|
||||
value = constValue)
|
||||
|
||||
consts.add(element)
|
||||
}
|
||||
|
||||
override fun exitServiceDef(ctx: AntlrThriftParser.ServiceDefContext) {
|
||||
val name = ctx.name.text
|
||||
|
||||
val extendsService = if (ctx.superType != null) {
|
||||
val superType = typeElementOf(ctx.superType)
|
||||
|
||||
if (superType !is ScalarTypeElement) {
|
||||
errorReporter.error(locationOf(ctx), "services cannot extend collections")
|
||||
return
|
||||
}
|
||||
|
||||
superType
|
||||
} else {
|
||||
null
|
||||
}
|
||||
|
||||
val service = ServiceElement(
|
||||
location = locationOf(ctx),
|
||||
name = name,
|
||||
functions = parseFunctionList(ctx.function()),
|
||||
extendsService = extendsService,
|
||||
documentation = formatJavadoc(ctx),
|
||||
annotations = annotationsFromAntlr(ctx.annotationList())
|
||||
)
|
||||
|
||||
services.add(service)
|
||||
}
|
||||
|
||||
private fun parseFunctionList(functionContexts: List<AntlrThriftParser.FunctionContext>): ImmutableList<FunctionElement> {
|
||||
val functions = ImmutableList.builder<FunctionElement>()
|
||||
|
||||
for (ctx in functionContexts) {
|
||||
val name = ctx.IDENTIFIER().text
|
||||
|
||||
val returnType = if (ctx.fieldType() != null) {
|
||||
typeElementOf(ctx.fieldType())
|
||||
} else {
|
||||
val token = ctx.getToken(AntlrThriftLexer.VOID, 0)
|
||||
?: throw AssertionError("Function has no return type, and no VOID token - grammar error")
|
||||
val loc = locationOf(token)
|
||||
|
||||
// Do people actually annotation 'void'? We'll find out!
|
||||
TypeElement.scalar(loc, "void", null)
|
||||
}
|
||||
|
||||
val isOneway = ctx.ONEWAY() != null
|
||||
|
||||
val maybeThrowsList = ctx.throwsList()?.let {
|
||||
parseFieldList(it.fieldList().field())
|
||||
}
|
||||
|
||||
val function = FunctionElement(
|
||||
location = locationOf(ctx),
|
||||
oneWay = isOneway,
|
||||
returnType = returnType,
|
||||
name = name,
|
||||
documentation = formatJavadoc(ctx),
|
||||
annotations = annotationsFromAntlr(ctx.annotationList()),
|
||||
params = parseFieldList(ctx.fieldList().field(), Requiredness.REQUIRED),
|
||||
exceptions = maybeThrowsList ?: emptyList()
|
||||
)
|
||||
|
||||
functions.add(function)
|
||||
}
|
||||
|
||||
return functions.build()
|
||||
}
|
||||
|
||||
override fun visitErrorNode(node: ErrorNode) {
|
||||
errorReporter.error(locationOf(node), node.text)
|
||||
}
|
||||
|
||||
// region Utilities
|
||||
|
||||
private fun annotationsFromAntlr(ctx: AntlrThriftParser.AnnotationListContext?): AnnotationElement? {
|
||||
if (ctx == null) {
|
||||
return null
|
||||
}
|
||||
|
||||
val annotations = mutableMapOf<String, String>()
|
||||
for (annotationContext in ctx.annotation()) {
|
||||
val name = annotationContext.IDENTIFIER().text
|
||||
annotations[name] = if (annotationContext.LITERAL() != null) {
|
||||
unquote(locationOf(annotationContext.LITERAL()), annotationContext.LITERAL().text)
|
||||
} else {
|
||||
"true"
|
||||
}
|
||||
}
|
||||
|
||||
return AnnotationElement(locationOf(ctx), annotations)
|
||||
}
|
||||
|
||||
private fun locationOf(ctx: ParserRuleContext): Location {
|
||||
return locationOf(ctx.getStart())
|
||||
}
|
||||
|
||||
private fun locationOf(node: TerminalNode): Location {
|
||||
return locationOf(node.symbol)
|
||||
}
|
||||
|
||||
private fun locationOf(token: Token): Location {
|
||||
val line = token.line
|
||||
val col = token.charPositionInLine + 1 // Location.col is 1-based, Token.col is 0-based
|
||||
return location.at(line, col)
|
||||
}
|
||||
|
||||
private fun unquote(location: Location, literal: String, processEscapes: Boolean = true): String {
|
||||
val chars = literal.toCharArray()
|
||||
val startChar = chars[0]
|
||||
val endChar = chars[chars.size - 1]
|
||||
|
||||
if (startChar != endChar || startChar != '\'' && startChar != '"') {
|
||||
throw AssertionError("Incorrect UNESCAPED_LITERAL rule: $literal")
|
||||
}
|
||||
|
||||
val sb = StringBuilder(literal.length - 2)
|
||||
|
||||
var i = 1
|
||||
val end = chars.size - 1
|
||||
while (i < end) {
|
||||
val c = chars[i++]
|
||||
|
||||
if (processEscapes && c == '\\') {
|
||||
if (i == end) {
|
||||
errorReporter.error(location, "Unterminated literal")
|
||||
break
|
||||
}
|
||||
|
||||
val escape = chars[i++]
|
||||
when (escape) {
|
||||
'a' -> sb.append(0x7.toChar())
|
||||
'b' -> sb.append('\b')
|
||||
'f' -> sb.append('\u000C')
|
||||
'n' -> sb.append('\n')
|
||||
'r' -> sb.append('\r')
|
||||
't' -> sb.append('\t')
|
||||
'v' -> sb.append(0xB.toChar())
|
||||
'\\' -> sb.append('\\')
|
||||
'u' -> throw UnsupportedOperationException("unicode escapes not yet implemented")
|
||||
else -> if (escape == startChar) {
|
||||
sb.append(startChar)
|
||||
} else {
|
||||
errorReporter.error(location, "invalid escape character: $escape")
|
||||
}
|
||||
}
|
||||
} else {
|
||||
sb.append(c)
|
||||
}
|
||||
}
|
||||
|
||||
return sb.toString()
|
||||
}
|
||||
|
||||
private fun typeElementOf(context: AntlrThriftParser.FieldTypeContext): TypeElement {
|
||||
if (context.baseType() != null) {
|
||||
if (context.baseType().text == "slist") {
|
||||
errorReporter.error(locationOf(context), "slist is unsupported; use list<string> instead")
|
||||
}
|
||||
|
||||
return TypeElement.scalar(
|
||||
locationOf(context),
|
||||
context.baseType().text,
|
||||
annotationsFromAntlr(context.annotationList()))
|
||||
}
|
||||
|
||||
if (context.IDENTIFIER() != null) {
|
||||
return TypeElement.scalar(
|
||||
locationOf(context),
|
||||
context.IDENTIFIER().text,
|
||||
annotationsFromAntlr(context.annotationList()))
|
||||
}
|
||||
|
||||
if (context.containerType() != null) {
|
||||
val containerContext = context.containerType()
|
||||
if (containerContext.mapType() != null) {
|
||||
val keyType = typeElementOf(containerContext.mapType().key)
|
||||
val valueType = typeElementOf(containerContext.mapType().value)
|
||||
return TypeElement.map(
|
||||
locationOf(containerContext.mapType()),
|
||||
keyType,
|
||||
valueType,
|
||||
annotationsFromAntlr(context.annotationList()))
|
||||
}
|
||||
|
||||
if (containerContext.setType() != null) {
|
||||
return TypeElement.set(
|
||||
locationOf(containerContext.setType()),
|
||||
typeElementOf(containerContext.setType().fieldType()),
|
||||
annotationsFromAntlr(context.annotationList()))
|
||||
}
|
||||
|
||||
if (containerContext.listType() != null) {
|
||||
return TypeElement.list(
|
||||
locationOf(containerContext.listType()),
|
||||
typeElementOf(containerContext.listType().fieldType()),
|
||||
annotationsFromAntlr(context.annotationList()))
|
||||
}
|
||||
|
||||
throw AssertionError("Unexpected container type - grammar error!")
|
||||
}
|
||||
|
||||
throw AssertionError("Unexpected type - grammar error!")
|
||||
}
|
||||
|
||||
private fun constValueElementOf(ctx: AntlrThriftParser.ConstValueContext?): ConstValueElement? {
|
||||
if (ctx == null) {
|
||||
return null
|
||||
}
|
||||
|
||||
if (ctx.INTEGER() != null) {
|
||||
try {
|
||||
val value = parseLong(ctx.INTEGER())
|
||||
|
||||
return ConstValueElement.integer(locationOf(ctx), ctx.INTEGER().text, value)
|
||||
} catch (e: NumberFormatException) {
|
||||
throw AssertionError("Invalid integer accepted by ANTLR grammar: " + ctx.INTEGER().text)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (ctx.DOUBLE() != null) {
|
||||
val text = ctx.DOUBLE().text
|
||||
|
||||
try {
|
||||
val value = java.lang.Double.parseDouble(text)
|
||||
return ConstValueElement.real(locationOf(ctx), ctx.DOUBLE().text, value)
|
||||
} catch (e: NumberFormatException) {
|
||||
throw AssertionError("Invalid double accepted by ANTLR grammar: $text")
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (ctx.LITERAL() != null) {
|
||||
val text = unquote(locationOf(ctx.LITERAL() as TerminalNode), ctx.LITERAL().text)
|
||||
return ConstValueElement.literal(locationOf(ctx), ctx.LITERAL().text, text)
|
||||
}
|
||||
|
||||
if (ctx.IDENTIFIER() != null) {
|
||||
val id = ctx.IDENTIFIER().text
|
||||
return ConstValueElement.identifier(locationOf(ctx), ctx.IDENTIFIER().text, id)
|
||||
}
|
||||
|
||||
if (ctx.constList() != null) {
|
||||
val values = ImmutableList.builder<ConstValueElement>()
|
||||
for (valueContext in ctx.constList().constValue()) {
|
||||
values.add(constValueElementOf(valueContext)!!)
|
||||
}
|
||||
return ConstValueElement.list(locationOf(ctx), ctx.constList().text, values.build())
|
||||
}
|
||||
|
||||
if (ctx.constMap() != null) {
|
||||
val values = ImmutableMap.builder<ConstValueElement, ConstValueElement>()
|
||||
for (entry in ctx.constMap().constMapEntry()) {
|
||||
val key = constValueElementOf(entry.key)
|
||||
val value = constValueElementOf(entry.value)
|
||||
values.put(key!!, value!!)
|
||||
}
|
||||
return ConstValueElement.map(locationOf(ctx), ctx.constMap().text, values.build())
|
||||
}
|
||||
|
||||
throw AssertionError("unreachable")
|
||||
}
|
||||
|
||||
private fun formatJavadoc(context: ParserRuleContext): String {
|
||||
return formatJavadoc(
|
||||
getLeadingComments(context.getStart()) + getTrailingComments(context.getStop()))
|
||||
}
|
||||
|
||||
private fun getLeadingComments(token: Token): List<Token> {
|
||||
val hiddenTokens = tokenStream.getHiddenTokensToLeft(token.tokenIndex, Lexer.HIDDEN)
|
||||
|
||||
return hiddenTokens?.filter {
|
||||
it.isComment && !trailingDocTokenIndexes.get(it.tokenIndex)
|
||||
} ?: emptyList()
|
||||
}
|
||||
|
||||
/**
|
||||
* Read comments following the given token, until the first newline is encountered.
|
||||
*
|
||||
* INVARIANT:
|
||||
* Assumes that the parse tree is being walked top-down, left to right!
|
||||
*
|
||||
* Trailing-doc tokens are marked as such, so that subsequent searches for "leading"
|
||||
* doc don't grab tokens already used as "trailing" doc. If the walk order is *not*
|
||||
* top-down, left-to-right, then the assumption underpinning the separation of leading
|
||||
* and trailing comments is broken.
|
||||
*
|
||||
* @param endToken the token from which to search for trailing comment tokens.
|
||||
* @return a list, possibly empty, of all trailing comment tokens.
|
||||
*/
|
||||
private fun getTrailingComments(endToken: Token): List<Token> {
|
||||
val hiddenTokens = tokenStream.getHiddenTokensToRight(endToken.tokenIndex, Lexer.HIDDEN)
|
||||
|
||||
if (hiddenTokens != null && hiddenTokens.isNotEmpty()) {
|
||||
val maybeTrailingDoc = hiddenTokens.first() // only one trailing comment is possible
|
||||
|
||||
if (maybeTrailingDoc.isComment) {
|
||||
trailingDocTokenIndexes.set(maybeTrailingDoc.tokenIndex)
|
||||
return listOf<Token>(maybeTrailingDoc)
|
||||
}
|
||||
}
|
||||
|
||||
return emptyList()
|
||||
}
|
||||
|
||||
companion object {
|
||||
// A number of tokens that should comfortably accommodate most input files
|
||||
// without wildly re-allocating. Estimated based on the ClientTestThrift
|
||||
// and TestThrift files, which contain around ~1200 tokens each.
|
||||
private const val INITIAL_BITSET_CAPACITY = 2048
|
||||
|
||||
|
||||
}
|
||||
|
||||
// endregion
|
||||
}
|
||||
|
||||
private val Token.isComment: Boolean
|
||||
get() {
|
||||
return when (this.type) {
|
||||
AntlrThriftLexer.SLASH_SLASH_COMMENT,
|
||||
AntlrThriftLexer.HASH_COMMENT,
|
||||
AntlrThriftLexer.MULTILINE_COMMENT -> true
|
||||
|
||||
else -> false
|
||||
}
|
||||
}
|
||||
|
||||
private fun formatJavadoc(commentTokens: List<Token>): String {
|
||||
val sb = StringBuilder()
|
||||
|
||||
for (token in commentTokens) {
|
||||
val text = token.text
|
||||
when (token.type) {
|
||||
AntlrThriftLexer.SLASH_SLASH_COMMENT -> formatSingleLineComment(sb, text, "//")
|
||||
|
||||
AntlrThriftLexer.HASH_COMMENT -> formatSingleLineComment(sb, text, "#")
|
||||
|
||||
AntlrThriftLexer.MULTILINE_COMMENT -> formatMultilineComment(sb, text)
|
||||
|
||||
else -> {
|
||||
// wat
|
||||
val symbolicName = AntlrThriftParser.VOCABULARY.getSymbolicName(token.type)
|
||||
val literalName = AntlrThriftParser.VOCABULARY.getLiteralName(token.type)
|
||||
val tokenNumber = "${token.type}"
|
||||
error("Unexpected comment-token type: ${symbolicName ?: literalName ?: tokenNumber}")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return sb.toString().trim { it <= ' ' }.let { doc ->
|
||||
if (doc.isNotEmpty() && !doc.endsWith("\n")) {
|
||||
doc + "\n"
|
||||
} else {
|
||||
doc
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun formatSingleLineComment(sb: StringBuilder, text: String, prefix: String) {
|
||||
var start = prefix.length
|
||||
var end = text.length
|
||||
|
||||
while (start < end && Character.isWhitespace(text[start])) {
|
||||
++start
|
||||
}
|
||||
|
||||
while (end > start && Character.isWhitespace(text[end - 1])) {
|
||||
--end
|
||||
}
|
||||
|
||||
if (start != end) {
|
||||
sb.append(text.substring(start, end))
|
||||
}
|
||||
|
||||
sb.append("\n")
|
||||
}
|
||||
|
||||
private fun formatMultilineComment(sb: StringBuilder, text: String) {
|
||||
val chars = text.toCharArray()
|
||||
var pos = "/*".length
|
||||
val length = chars.size
|
||||
var isStartOfLine = true
|
||||
|
||||
while (pos + 1 < length) {
|
||||
val c = chars[pos]
|
||||
if (c == '*' && chars[pos + 1] == '/') {
|
||||
sb.append("\n")
|
||||
return
|
||||
}
|
||||
|
||||
if (c == '\n') {
|
||||
sb.append(c)
|
||||
isStartOfLine = true
|
||||
} else if (!isStartOfLine) {
|
||||
sb.append(c)
|
||||
} else if (c == '*') {
|
||||
// skip a single subsequent space, if it exists
|
||||
if (chars[pos + 1] == ' ') {
|
||||
pos += 1
|
||||
}
|
||||
|
||||
isStartOfLine = false
|
||||
} else if (!Character.isWhitespace(c)) {
|
||||
sb.append(c)
|
||||
isStartOfLine = false
|
||||
}
|
||||
++pos
|
||||
}
|
||||
}
|
||||
|
||||
private fun parseInt(node: TerminalNode): Int {
|
||||
return parseInt(node.symbol)
|
||||
}
|
||||
|
||||
private fun parseInt(token: Token): Int {
|
||||
var text = token.text
|
||||
|
||||
var radix = 10
|
||||
if (text.startsWith("0x") || text.startsWith("0X")) {
|
||||
radix = 16
|
||||
text = text.substring(2)
|
||||
}
|
||||
|
||||
return Integer.parseInt(text, radix)
|
||||
}
|
||||
|
||||
private fun parseLong(node: TerminalNode): Long = parseLong(node.symbol)
|
||||
|
||||
private fun parseLong(token: Token): Long {
|
||||
val text: String
|
||||
val radix: Int
|
||||
|
||||
if (token.text.startsWith("0x", ignoreCase = true)) {
|
||||
text = token.text.substring(2)
|
||||
radix = 16
|
||||
} else {
|
||||
text = token.text
|
||||
radix = 10
|
||||
}
|
||||
|
||||
return java.lang.Long.parseLong(text, radix)
|
||||
}
|
|
@ -0,0 +1,65 @@
|
|||
/*
|
||||
* Thrifty
|
||||
*
|
||||
* Copyright (c) Microsoft Corporation
|
||||
*
|
||||
* All rights reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the License);
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* THIS CODE IS PROVIDED ON AN *AS IS* BASIS, WITHOUT WARRANTIES OR
|
||||
* CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING
|
||||
* WITHOUT LIMITATION ANY IMPLIED WARRANTIES OR CONDITIONS OF TITLE,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE, MERCHANTABLITY OR NON-INFRINGEMENT.
|
||||
*
|
||||
* See the Apache Version 2.0 License for specific language governing permissions and limitations under the License.
|
||||
*/
|
||||
package com.microsoft.thrifty.schema.parser
|
||||
|
||||
import com.google.common.base.Joiner
|
||||
import com.microsoft.thrifty.schema.ErrorReporter
|
||||
import com.microsoft.thrifty.schema.Location
|
||||
import com.microsoft.thrifty.schema.antlr.AntlrThriftLexer
|
||||
import com.microsoft.thrifty.schema.antlr.AntlrThriftParser
|
||||
import org.antlr.v4.runtime.CharStreams
|
||||
import org.antlr.v4.runtime.CommonTokenStream
|
||||
import org.antlr.v4.runtime.tree.ParseTreeWalker
|
||||
import java.util.Locale
|
||||
|
||||
object ThriftParser {
|
||||
|
||||
/**
|
||||
* Parse the given Thrift [text], using the given [location] to anchor
|
||||
* parsed elements within the file.
|
||||
*
|
||||
* @param location the [Location] of the file containing the given [text].
|
||||
* @param text the Thrift text to be parsed.
|
||||
* @param reporter an [ErrorReporter] to which parse errors will be reported.
|
||||
* @return a [ThriftFileElement] containing all Thrift elements in the input text.
|
||||
*/
|
||||
@JvmStatic
|
||||
@JvmOverloads
|
||||
fun parse(location: Location, text: String, reporter: ErrorReporter = ErrorReporter()): ThriftFileElement {
|
||||
val charStream = CharStreams.fromString(text, location.path())
|
||||
val lexer = AntlrThriftLexer(charStream)
|
||||
val tokenStream = CommonTokenStream(lexer)
|
||||
val antlrParser = AntlrThriftParser(tokenStream)
|
||||
val documentParseTree = antlrParser.document()
|
||||
|
||||
val thriftListener = ThriftListener(tokenStream, reporter, location)
|
||||
|
||||
ParseTreeWalker.DEFAULT.walk(thriftListener, documentParseTree)
|
||||
|
||||
if (reporter.hasError()) {
|
||||
val errorReports = Joiner.on('\n').join(reporter.formattedReports())
|
||||
val message = String.format(Locale.US, "Syntax errors in %s:\n%s", location, errorReports)
|
||||
throw IllegalStateException(message)
|
||||
}
|
||||
|
||||
return thriftListener.buildFileElement()
|
||||
}
|
||||
}
|
|
@ -0,0 +1,89 @@
|
|||
/*
|
||||
* Thrifty
|
||||
*
|
||||
* Copyright (c) Microsoft Corporation
|
||||
*
|
||||
* All rights reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the License);
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* THIS CODE IS PROVIDED ON AN *AS IS* BASIS, WITHOUT WARRANTIES OR
|
||||
* CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING
|
||||
* WITHOUT LIMITATION ANY IMPLIED WARRANTIES OR CONDITIONS OF TITLE,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE, MERCHANTABLITY OR NON-INFRINGEMENT.
|
||||
*
|
||||
* See the Apache Version 2.0 License for specific language governing permissions and limitations under the License.
|
||||
*/
|
||||
package com.microsoft.thrifty.schema.parser
|
||||
|
||||
import java.util.UUID
|
||||
|
||||
object ThriftyParserPlugins {
|
||||
private val DEFAULT_UUID_PROVIDER: UUIDProvider = object : UUIDProvider {
|
||||
override fun call(): UUID = UUID.randomUUID()
|
||||
}
|
||||
|
||||
@Volatile
|
||||
private var uuidProvider = DEFAULT_UUID_PROVIDER
|
||||
|
||||
/**
|
||||
* Prevents changing the plugins.
|
||||
*/
|
||||
@Volatile
|
||||
private var lockdown: Boolean = false
|
||||
|
||||
/**
|
||||
* Prevents changing the plugins from then on.
|
||||
*
|
||||
*
|
||||
* This allows container-like environments to prevent client messing with plugins.
|
||||
*/
|
||||
@JvmStatic fun lockdown() {
|
||||
lockdown = true
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the plugins were locked down.
|
||||
*
|
||||
* @return true if the plugins were locked down
|
||||
*/
|
||||
@JvmStatic fun isLockdown(): Boolean {
|
||||
return lockdown
|
||||
}
|
||||
|
||||
/**
|
||||
* @param uuidProvider the provider to use for generating [UUID]s for elements.
|
||||
*/
|
||||
@JvmStatic fun setUUIDProvider(uuidProvider: UUIDProvider) {
|
||||
if (lockdown) {
|
||||
throw IllegalStateException("Plugins can't be changed anymore")
|
||||
}
|
||||
ThriftyParserPlugins.uuidProvider = uuidProvider
|
||||
}
|
||||
|
||||
/**
|
||||
* @return a [UUID] as dictated by [uuidProvider]. Default is random UUIDs.
|
||||
*/
|
||||
@JvmStatic fun createUUID(): UUID {
|
||||
return uuidProvider.call()
|
||||
}
|
||||
|
||||
@JvmStatic fun reset() {
|
||||
uuidProvider = DEFAULT_UUID_PROVIDER
|
||||
}
|
||||
|
||||
/**
|
||||
* A simple provider interface for creating [UUID]s.
|
||||
*/
|
||||
interface UUIDProvider {
|
||||
|
||||
/**
|
||||
* @return a [UUID].
|
||||
*/
|
||||
fun call(): UUID
|
||||
}
|
||||
}
|
|
@ -0,0 +1,250 @@
|
|||
/*
|
||||
* Thrifty
|
||||
*
|
||||
* Copyright (c) Microsoft Corporation
|
||||
*
|
||||
* All rights reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the License);
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* THIS CODE IS PROVIDED ON AN *AS IS* BASIS, WITHOUT WARRANTIES OR
|
||||
* CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING
|
||||
* WITHOUT LIMITATION ANY IMPLIED WARRANTIES OR CONDITIONS OF TITLE,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE, MERCHANTABLITY OR NON-INFRINGEMENT.
|
||||
*
|
||||
* See the Apache Version 2.0 License for specific language governing permissions and limitations under the License.
|
||||
*/
|
||||
package com.microsoft.thrifty.schema.parser
|
||||
|
||||
import com.microsoft.thrifty.schema.Location
|
||||
|
||||
/**
|
||||
* Represents a reference to a named type.
|
||||
*
|
||||
* Types can be scalar (numbers, strings, identifiers, etc) or collections.
|
||||
*/
|
||||
sealed class TypeElement(
|
||||
/**
|
||||
* The location of the text corresponding to this element.
|
||||
*/
|
||||
@get:JvmName("location")
|
||||
val location: Location,
|
||||
|
||||
/**
|
||||
* The name of the type referenced by this element.
|
||||
*/
|
||||
@get:JvmName("name")
|
||||
val name: String,
|
||||
|
||||
/**
|
||||
* The annotations associated with this type reference, if any.
|
||||
*/
|
||||
@get:JvmName("annotations")
|
||||
val annotations: AnnotationElement? = null
|
||||
) {
|
||||
|
||||
companion object {
|
||||
/**
|
||||
* Creates and returns a scalar [TypeElement].
|
||||
*
|
||||
* @param location the location of the text corresponding to the scalar.
|
||||
* @param name the name of the scalar-type reference.
|
||||
* @param annotations the annotations associated with the reference, if any.
|
||||
* @return a [ScalarTypeElement].
|
||||
*/
|
||||
@JvmStatic
|
||||
fun scalar(location: Location, name: String, annotations: AnnotationElement? = null) =
|
||||
ScalarTypeElement(location, name, annotations)
|
||||
|
||||
/**
|
||||
* Creates and returns a list [TypeElement].
|
||||
*
|
||||
* @param location the location of the text corresponding to the reference.
|
||||
* @param elementType the type of element contained by the list type.
|
||||
* @param annotations the annotations associated with the reference, if any.
|
||||
* @return a [ListTypeElement].
|
||||
*/
|
||||
@JvmStatic
|
||||
fun list(location: Location, elementType: TypeElement, annotations: AnnotationElement? = null) =
|
||||
ListTypeElement(elementType, location, annotations)
|
||||
|
||||
/**
|
||||
* Creates and returns a set [TypeElement].
|
||||
*
|
||||
* @param location the location of the text corresponding to the reference.
|
||||
* @param elementType the type of element contained by the set type.
|
||||
* @param annotations the annotations associated with the reference, if any.
|
||||
* @return a [SetTypeElement].
|
||||
*/
|
||||
@JvmStatic
|
||||
fun set(location: Location, elementType: TypeElement, annotations: AnnotationElement? = null) =
|
||||
SetTypeElement(elementType, location, annotations)
|
||||
|
||||
/**
|
||||
* Creates and returns a map [TypeElement].
|
||||
*
|
||||
* @param location the location of the text corresponding to the reference.
|
||||
* @param keyType the ley-type of the map.
|
||||
* @param valueType the value-type of the map.
|
||||
* @param annotations the annotations associated with the reference, if any.
|
||||
* @return a [MapTypeElement].
|
||||
*/
|
||||
@JvmStatic
|
||||
fun map(location: Location,
|
||||
keyType: TypeElement,
|
||||
valueType: TypeElement,
|
||||
annotations: AnnotationElement? = null) = MapTypeElement(keyType, valueType, location, annotations)
|
||||
}
|
||||
|
||||
override fun equals(other: Any?): Boolean {
|
||||
if (this === other) return true
|
||||
if (javaClass != other?.javaClass) return false
|
||||
|
||||
other as TypeElement
|
||||
|
||||
if (location != other.location) return false
|
||||
if (name != other.name) return false
|
||||
if (annotations != other.annotations) return false
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
override fun hashCode(): Int {
|
||||
var result = location.hashCode()
|
||||
result = 31 * result + name.hashCode()
|
||||
result = 31 * result + (annotations?.hashCode() ?: 0)
|
||||
return result
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents a reference to a scalar type.
|
||||
*/
|
||||
class ScalarTypeElement(
|
||||
location: Location,
|
||||
name: String,
|
||||
annotations: AnnotationElement?
|
||||
) : TypeElement(location, name, annotations) {
|
||||
|
||||
override fun toString(): String {
|
||||
return "ScalarTypeElement{location=$location, name=$name, annotations=$annotations}"
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents a reference to a set-type.
|
||||
*/
|
||||
class SetTypeElement(
|
||||
/**
|
||||
* A reference to the type of element contained within this set type.
|
||||
*/
|
||||
@get:JvmName("elementType")
|
||||
val elementType: TypeElement,
|
||||
location: Location,
|
||||
annotations: AnnotationElement? = null
|
||||
) : TypeElement(location, "set<${elementType.name}>", annotations) {
|
||||
|
||||
override fun equals(other: Any?): Boolean {
|
||||
if (this === other) return true
|
||||
if (other !is SetTypeElement) return false
|
||||
if (!super.equals(other)) return false
|
||||
|
||||
if (elementType != other.elementType) return false
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
override fun hashCode(): Int {
|
||||
var result = super.hashCode()
|
||||
result = 31 * result + elementType.hashCode()
|
||||
return result
|
||||
}
|
||||
|
||||
override fun toString(): String {
|
||||
return "SetTypeElement{location=$location, name=$name, annotations=$annotations, elementType=$elementType}"
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents a reference to a list-type.
|
||||
*/
|
||||
class ListTypeElement(
|
||||
/**
|
||||
* A reference to the type of element contained within this list type.
|
||||
*/
|
||||
@get:JvmName("elementType")
|
||||
val elementType: TypeElement,
|
||||
location: Location,
|
||||
annotations: AnnotationElement? = null
|
||||
) : TypeElement(location, "list<${elementType.name}>", annotations) {
|
||||
|
||||
override fun equals(other: Any?): Boolean {
|
||||
if (this === other) return true
|
||||
if (other !is ListTypeElement) return false
|
||||
if (!super.equals(other)) return false
|
||||
|
||||
if (elementType != other.elementType) return false
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
override fun hashCode(): Int {
|
||||
var result = super.hashCode()
|
||||
result = 31 * result + elementType.hashCode()
|
||||
return result
|
||||
}
|
||||
|
||||
override fun toString(): String {
|
||||
return "ListTypeElement{location=$location, name=$name, annotations=$annotations, elementType=$elementType}"
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents a reference to a map-type.
|
||||
*
|
||||
* A map-type is a typical key-value lookup, a.k.a. associative array,
|
||||
* dictionary, etc.
|
||||
*/
|
||||
class MapTypeElement(
|
||||
/**
|
||||
* A reference to the type serving as the key-type of this map type.
|
||||
*/
|
||||
@get:JvmName("keyType")
|
||||
val keyType: TypeElement,
|
||||
|
||||
/**
|
||||
* A reference to the type serving as the value-type of this map type.
|
||||
*/
|
||||
@get:JvmName("valueType")
|
||||
val valueType: TypeElement,
|
||||
|
||||
location: Location,
|
||||
annotations: AnnotationElement? = null
|
||||
) : TypeElement(location, "map<${keyType.name}, ${valueType.name}>", annotations) {
|
||||
|
||||
override fun equals(other: Any?): Boolean {
|
||||
if (this === other) return true
|
||||
if (other !is MapTypeElement) return false
|
||||
if (!super.equals(other)) return false
|
||||
|
||||
if (keyType != other.keyType) return false
|
||||
if (valueType != other.valueType) return false
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
override fun hashCode(): Int {
|
||||
var result = super.hashCode()
|
||||
result = 31 * result + keyType.hashCode()
|
||||
result = 31 * result + valueType.hashCode()
|
||||
return result
|
||||
}
|
||||
|
||||
override fun toString(): String {
|
||||
return "MapTypeElement{location=$location, name=$name, annotations=$annotations, keyType=$keyType, valueType=$valueType}"
|
||||
}
|
||||
}
|
|
@ -171,7 +171,7 @@ public class ConstantTest {
|
|||
@Test
|
||||
public void enumWithMember() {
|
||||
ImmutableList.Builder<EnumMember> members = ImmutableList.builder();
|
||||
members.add(new EnumMember(EnumMemberElement.builder(loc).name("TEST").value(1).build()));
|
||||
members.add(new EnumMember(new EnumMemberElement(loc, "TEST", 1)));
|
||||
|
||||
EnumType et = mock(EnumType.class);
|
||||
when(et.name()).thenReturn("TestEnum");
|
||||
|
@ -185,7 +185,7 @@ public class ConstantTest {
|
|||
@Test
|
||||
public void enumWithNonMemberIdentifier() {
|
||||
ImmutableList.Builder<EnumMember> members = ImmutableList.builder();
|
||||
members.add(new EnumMember(EnumMemberElement.builder(loc).name("TEST").value(1).build()));
|
||||
members.add(new EnumMember(new EnumMemberElement(loc, "TEST", 1)));
|
||||
|
||||
EnumType et = mock(EnumType.class);
|
||||
when(et.name()).thenReturn("TestEnum");
|
||||
|
@ -206,7 +206,7 @@ public class ConstantTest {
|
|||
@Test
|
||||
public void unqualifiedEnumMember() {
|
||||
ImmutableList.Builder<EnumMember> members = ImmutableList.builder();
|
||||
members.add(new EnumMember(EnumMemberElement.builder(loc).name("TEST").value(1).build()));
|
||||
members.add(new EnumMember(new EnumMemberElement(loc, "TEST", 1)));
|
||||
|
||||
EnumType et = mock(EnumType.class);
|
||||
when(et.name()).thenReturn("TestEnum");
|
||||
|
@ -252,7 +252,7 @@ public class ConstantTest {
|
|||
|
||||
@Test
|
||||
public void typedefOfEnum() {
|
||||
EnumMember member = new EnumMember(EnumMemberElement.builder(loc).name("FOO").value(1).build());
|
||||
EnumMember member = new EnumMember(new EnumMemberElement(loc, "FOO", 1));
|
||||
ImmutableList<EnumMember> members = ImmutableList.of(member);
|
||||
|
||||
EnumType et = mock(EnumType.class);
|
||||
|
@ -273,7 +273,7 @@ public class ConstantTest {
|
|||
|
||||
@Test
|
||||
public void typedefOfWrongEnum() {
|
||||
EnumMember member = new EnumMember(EnumMemberElement.builder(loc).name("FOO").value(1).build());
|
||||
EnumMember member = new EnumMember(new EnumMemberElement(loc, "FOO", 1));
|
||||
ImmutableList<EnumMember> members = ImmutableList.of(member);
|
||||
|
||||
EnumType et = mock(EnumType.class);
|
||||
|
@ -282,7 +282,7 @@ public class ConstantTest {
|
|||
when(et.getTrueType()).thenReturn(et);
|
||||
when(et.isEnum()).thenReturn(true);
|
||||
|
||||
EnumMember wrongMember = new EnumMember(EnumMemberElement.builder(loc).name("BAR").value(2).build());
|
||||
EnumMember wrongMember = new EnumMember(new EnumMemberElement(loc, "BAR", 2));
|
||||
|
||||
EnumType wt = mock(EnumType.class);
|
||||
when(wt.name()).thenReturn("DifferentEnum");
|
||||
|
|
|
@ -15,8 +15,6 @@ import static org.mockito.Mockito.when;
|
|||
|
||||
@RunWith(MockitoJUnitRunner.class)
|
||||
public class EnumTypeTest {
|
||||
@Mock EnumElement element;
|
||||
@Mock ThriftType type;
|
||||
@Mock Program program;
|
||||
ImmutableMap<NamespaceScope, String> namespaces;
|
||||
|
||||
|
@ -27,19 +25,14 @@ public class EnumTypeTest {
|
|||
namespaces = ImmutableMap.of();
|
||||
|
||||
when(program.namespaces()).thenReturn(namespaces);
|
||||
when(element.name()).thenReturn("name");
|
||||
when(element.members()).thenReturn(ImmutableList.<EnumMemberElement>of());
|
||||
|
||||
Location location = Location.get("", "");
|
||||
EnumMemberElement memberElement = EnumMemberElement.builder(location)
|
||||
.name("FOO")
|
||||
.value(1)
|
||||
.build();
|
||||
EnumMemberElement memberElement = new EnumMemberElement(location, "FOO", 1);
|
||||
|
||||
EnumElement element = EnumElement.builder(location)
|
||||
.name("AnEnum")
|
||||
.members(ImmutableList.of(memberElement))
|
||||
.build();
|
||||
EnumElement element = new EnumElement(
|
||||
location,
|
||||
"AnEnum",
|
||||
ImmutableList.of(memberElement));
|
||||
|
||||
enumType = new EnumType(program, element);
|
||||
}
|
||||
|
|
|
@ -24,6 +24,7 @@ import com.google.common.collect.ImmutableMap;
|
|||
import com.microsoft.thrifty.schema.parser.AnnotationElement;
|
||||
import com.microsoft.thrifty.schema.parser.FieldElement;
|
||||
import com.microsoft.thrifty.schema.parser.TypeElement;
|
||||
import org.junit.Before;
|
||||
import org.junit.Ignore;
|
||||
import org.junit.Test;
|
||||
|
||||
|
@ -35,11 +36,30 @@ import static org.junit.Assert.assertTrue;
|
|||
import static org.mockito.Mockito.mock;
|
||||
|
||||
public class FieldTest {
|
||||
private Location location;
|
||||
private int fieldId;
|
||||
private String fieldName;
|
||||
private TypeElement fieldType;
|
||||
private Requiredness requiredness;
|
||||
private AnnotationElement annotations;
|
||||
private String documentation;
|
||||
|
||||
@Before
|
||||
public void setup() {
|
||||
location = Location.get("", "");
|
||||
fieldId = 1;
|
||||
fieldName = "foo";
|
||||
fieldType = TypeElement.scalar(location, "i32", null);
|
||||
requiredness = Requiredness.DEFAULT;
|
||||
annotations = null;
|
||||
documentation = "";
|
||||
}
|
||||
|
||||
@Test
|
||||
public void requiredFields() {
|
||||
FieldElement element = fieldBuilder()
|
||||
.requiredness(Requiredness.REQUIRED)
|
||||
.build();
|
||||
requiredness = Requiredness.REQUIRED;
|
||||
FieldElement element = field();
|
||||
|
||||
Field field = new Field(element);
|
||||
assertTrue(field.required());
|
||||
assertFalse(field.optional());
|
||||
|
@ -47,9 +67,8 @@ public class FieldTest {
|
|||
|
||||
@Test
|
||||
public void optionalFields() {
|
||||
FieldElement element = fieldBuilder()
|
||||
.requiredness(Requiredness.OPTIONAL)
|
||||
.build();
|
||||
requiredness = Requiredness.OPTIONAL;
|
||||
FieldElement element = field();
|
||||
Field field = new Field(element);
|
||||
assertFalse(field.required());
|
||||
assertTrue(field.optional());
|
||||
|
@ -57,9 +76,7 @@ public class FieldTest {
|
|||
|
||||
@Test
|
||||
public void defaultFields() {
|
||||
FieldElement element = fieldBuilder()
|
||||
.requiredness(Requiredness.DEFAULT)
|
||||
.build();
|
||||
FieldElement element = field();
|
||||
Field field = new Field(element);
|
||||
assertFalse(field.required());
|
||||
assertFalse(field.optional());
|
||||
|
@ -67,7 +84,7 @@ public class FieldTest {
|
|||
|
||||
@Test
|
||||
public void unredactedAndUnobfuscatedByDefault() {
|
||||
FieldElement element = fieldBuilder().build();
|
||||
FieldElement element = field();
|
||||
Field field = new Field(element);
|
||||
assertFalse(field.isRedacted());
|
||||
assertFalse(field.isObfuscated());
|
||||
|
@ -75,9 +92,8 @@ public class FieldTest {
|
|||
|
||||
@Test
|
||||
public void redactedByThriftAnnotation() {
|
||||
FieldElement element = fieldBuilder()
|
||||
.annotations(annotation("thrifty.redacted"))
|
||||
.build();
|
||||
annotations = annotation("thrifty.redacted");
|
||||
FieldElement element = field();
|
||||
|
||||
Field field = new Field(element);
|
||||
assertTrue(field.isRedacted());
|
||||
|
@ -85,9 +101,8 @@ public class FieldTest {
|
|||
|
||||
@Test
|
||||
public void redactedByShortThriftAnnotation() {
|
||||
FieldElement element = fieldBuilder()
|
||||
.annotations(annotation("redacted"))
|
||||
.build();
|
||||
annotations = annotation("redacted");
|
||||
FieldElement element = field();
|
||||
|
||||
Field field = new Field(element);
|
||||
assertTrue(field.isRedacted());
|
||||
|
@ -95,9 +110,8 @@ public class FieldTest {
|
|||
|
||||
@Test
|
||||
public void redactedByJavadocAnnotation() {
|
||||
FieldElement element = fieldBuilder()
|
||||
.documentation("/** @redacted */")
|
||||
.build();
|
||||
documentation = "/** @redacted */";
|
||||
FieldElement element = field();
|
||||
|
||||
Field field = new Field(element);
|
||||
assertTrue(field.isRedacted());
|
||||
|
@ -105,9 +119,8 @@ public class FieldTest {
|
|||
|
||||
@Test
|
||||
public void obfuscatedByThriftAnnotation() {
|
||||
FieldElement element = fieldBuilder()
|
||||
.annotations(annotation("thrifty.obfuscated"))
|
||||
.build();
|
||||
annotations = annotation("thrifty.obfuscated");
|
||||
FieldElement element = field();
|
||||
|
||||
Field field = new Field(element);
|
||||
assertTrue(field.isObfuscated());
|
||||
|
@ -115,9 +128,8 @@ public class FieldTest {
|
|||
|
||||
@Test
|
||||
public void obfuscatedByShortThriftAnnotation() {
|
||||
FieldElement element = fieldBuilder()
|
||||
.annotations(annotation("obfuscated"))
|
||||
.build();
|
||||
annotations = annotation("obfuscated");
|
||||
FieldElement element = field();
|
||||
|
||||
Field field = new Field(element);
|
||||
assertTrue(field.isObfuscated());
|
||||
|
@ -125,9 +137,8 @@ public class FieldTest {
|
|||
|
||||
@Test
|
||||
public void obfuscatedByJavadocAnnotation() {
|
||||
FieldElement element = fieldBuilder()
|
||||
.documentation("/** @obfuscated */")
|
||||
.build();
|
||||
documentation = "/** @obfuscated */";
|
||||
FieldElement element = field();
|
||||
|
||||
Field field = new Field(element);
|
||||
assertTrue(field.isObfuscated());
|
||||
|
@ -135,7 +146,7 @@ public class FieldTest {
|
|||
|
||||
@Test
|
||||
public void builderCreatesCorrectField() {
|
||||
FieldElement fieldElement = mock(FieldElement.class);
|
||||
FieldElement fieldElement = field();
|
||||
Field field = new Field(fieldElement);
|
||||
|
||||
ImmutableMap<String, String> annotations = ImmutableMap.of();
|
||||
|
@ -160,14 +171,18 @@ public class FieldTest {
|
|||
}
|
||||
|
||||
private AnnotationElement annotation(String name) {
|
||||
return AnnotationElement.create(Location.get("", ""), Collections.singletonMap(name, "true"));
|
||||
return new AnnotationElement(Location.get("", ""), Collections.singletonMap(name, "true"));
|
||||
}
|
||||
|
||||
private FieldElement.Builder fieldBuilder() {
|
||||
Location location = Location.get("", "");
|
||||
return FieldElement.builder(location)
|
||||
.fieldId(1)
|
||||
.name("foo")
|
||||
.type(TypeElement.scalar(location, "i32", null));
|
||||
private FieldElement field() {
|
||||
return new FieldElement(
|
||||
location,
|
||||
fieldId,
|
||||
fieldType,
|
||||
fieldName,
|
||||
requiredness,
|
||||
documentation,
|
||||
null, // const value
|
||||
annotations);
|
||||
}
|
||||
}
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
Загрузка…
Ссылка в новой задаче