From 276234c6719d78e6b13a4cd56ccc8620b838dcd5 Mon Sep 17 00:00:00 2001 From: John Ed Quinn Date: Tue, 29 Oct 2024 16:21:14 -0700 Subject: [PATCH 01/12] Migrates parser APIs to Java --- .../org/partiql/cli/pipeline/Pipeline.kt | 5 +- .../eval/internal/PartiQLEvaluatorTest.kt | 15 ++- partiql-parser/api/partiql-parser.api | 110 ++++------------- .../org/partiql/parser/PartiQLParser.java | 93 ++++++++++++++ .../partiql/parser/PartiQLParserBuilder.java} | 14 ++- .../org/partiql/parser/SourceLocations.java | 113 ++++++++++++++++++ .../kotlin/org/partiql/parser/Exceptions.kt | 8 +- .../org/partiql/parser/PartiQLParser.kt | 71 ----------- .../org/partiql/parser/SourceLocation.kt | 44 ------- .../org/partiql/parser/SourceLocations.kt | 49 -------- .../parser/internal/PartiQLParserDefault.kt | 33 +++-- .../parser/internal/V1PartiQLParserDefault.kt | 23 ++-- .../kotlin/org/partiql/planner/PlanTest.kt | 4 +- .../planner/PlannerPErrorReportingTests.kt | 6 +- .../internal/exclude/SubsumptionTest.kt | 6 +- .../internal/typer/PartiQLTyperTestBase.kt | 6 +- .../internal/typer/PlanTyperTestsPorted.kt | 6 +- partiql-spi/api/partiql-spi.api | 1 + .../java/org/partiql/spi/SourceLocation.java | 6 + .../org/partiql/lang/randomized/eval/Utils.kt | 3 +- .../partiql/runner/executor/EvalExecutor.kt | 12 +- 21 files changed, 328 insertions(+), 300 deletions(-) create mode 100644 partiql-parser/src/main/java/org/partiql/parser/PartiQLParser.java rename partiql-parser/src/main/{kotlin/org/partiql/parser/PartiQLParserBuilder.kt => java/org/partiql/parser/PartiQLParserBuilder.java} (72%) create mode 100644 partiql-parser/src/main/java/org/partiql/parser/SourceLocations.java delete mode 100644 partiql-parser/src/main/kotlin/org/partiql/parser/PartiQLParser.kt delete mode 100644 partiql-parser/src/main/kotlin/org/partiql/parser/SourceLocation.kt delete mode 100644 partiql-parser/src/main/kotlin/org/partiql/parser/SourceLocations.kt diff --git a/partiql-cli/src/main/kotlin/org/partiql/cli/pipeline/Pipeline.kt b/partiql-cli/src/main/kotlin/org/partiql/cli/pipeline/Pipeline.kt index 3a1ce7f779..c6eef0b6b3 100644 --- a/partiql-cli/src/main/kotlin/org/partiql/cli/pipeline/Pipeline.kt +++ b/partiql-cli/src/main/kotlin/org/partiql/cli/pipeline/Pipeline.kt @@ -37,7 +37,10 @@ internal class Pipeline private constructor( val result = listen(ctx.errorListener as AppPErrorListener) { parser.parse(source, ctx) } - return result.root + if (result.statements.size != 1) { + throw PipelineException("Expected exactly one statement, got: ${result.statements.size}") + } + return result.statements[0] } private fun plan(statement: Statement, session: Session): Plan { diff --git a/partiql-eval/src/test/kotlin/org/partiql/eval/internal/PartiQLEvaluatorTest.kt b/partiql-eval/src/test/kotlin/org/partiql/eval/internal/PartiQLEvaluatorTest.kt index 413d3c385a..b64eddeff1 100644 --- a/partiql-eval/src/test/kotlin/org/partiql/eval/internal/PartiQLEvaluatorTest.kt +++ b/partiql-eval/src/test/kotlin/org/partiql/eval/internal/PartiQLEvaluatorTest.kt @@ -1320,7 +1320,11 @@ class PartiQLEvaluatorTest { ) internal fun assert() { - val statement = parser.parse(input).root + val parseResult = parser.parse(input) + if (parseResult.statements.size != 1) { + throw RuntimeException("Expected exactly one statement: $input") + } + val statement = parseResult.statements[0] val catalog = Catalog.builder() .name("memory") .apply { @@ -1406,8 +1410,17 @@ class PartiQLEvaluatorTest { } private fun run(mode: Mode): Pair { +<<<<<<< HEAD val statement = parser.parse(input).root val catalog = Catalog.builder().name("memory").build() +======= + val parseResult = parser.parse(input) + if (parseResult.statements.size != 1) { + throw RuntimeException("Expected exactly one statement: $input") + } + val statement = parseResult.statements[0] + val catalog = MemoryCatalog.builder().name("memory").build() +>>>>>>> 1c662c3be (Migrates parser APIs to Java) val session = Session.builder() .catalog("memory") .catalogs(catalog) diff --git a/partiql-parser/api/partiql-parser.api b/partiql-parser/api/partiql-parser.api index 1cc5585abe..69a6c5d485 100644 --- a/partiql-parser/api/partiql-parser.api +++ b/partiql-parser/api/partiql-parser.api @@ -1,107 +1,43 @@ public abstract interface class org/partiql/parser/PartiQLParser { - public static final field Companion Lorg/partiql/parser/PartiQLParser$Companion; public static fun builder ()Lorg/partiql/parser/PartiQLParserBuilder; - public abstract fun parse (Ljava/lang/String;)Lorg/partiql/parser/PartiQLParser$Result; + public fun parse (Ljava/lang/String;)Lorg/partiql/parser/PartiQLParser$Result; public abstract fun parse (Ljava/lang/String;Lorg/partiql/spi/Context;)Lorg/partiql/parser/PartiQLParser$Result; public static fun standard ()Lorg/partiql/parser/PartiQLParser; } -public final class org/partiql/parser/PartiQLParser$Companion { - public final fun builder ()Lorg/partiql/parser/PartiQLParserBuilder; - public final fun standard ()Lorg/partiql/parser/PartiQLParser; -} - -public final class org/partiql/parser/PartiQLParser$DefaultImpls { - public static fun parse (Lorg/partiql/parser/PartiQLParser;Ljava/lang/String;)Lorg/partiql/parser/PartiQLParser$Result; -} - public final class org/partiql/parser/PartiQLParser$Result { - public static final field Companion Lorg/partiql/parser/PartiQLParser$Result$Companion; - public fun (Ljava/lang/String;Lorg/partiql/ast/Statement;Lorg/partiql/parser/SourceLocations;)V - public final fun component1 ()Ljava/lang/String; - public final fun component2 ()Lorg/partiql/ast/Statement; - public final fun component3 ()Lorg/partiql/parser/SourceLocations; - public final fun copy (Ljava/lang/String;Lorg/partiql/ast/Statement;Lorg/partiql/parser/SourceLocations;)Lorg/partiql/parser/PartiQLParser$Result; - public static synthetic fun copy$default (Lorg/partiql/parser/PartiQLParser$Result;Ljava/lang/String;Lorg/partiql/ast/Statement;Lorg/partiql/parser/SourceLocations;ILjava/lang/Object;)Lorg/partiql/parser/PartiQLParser$Result; - public fun equals (Ljava/lang/Object;)Z - public final fun getLocations ()Lorg/partiql/parser/SourceLocations; - public final fun getRoot ()Lorg/partiql/ast/Statement; - public final fun getSource ()Ljava/lang/String; - public fun hashCode ()I - public fun toString ()Ljava/lang/String; + public field locations Lorg/partiql/parser/SourceLocations; + public field statements Ljava/util/List; + public fun (Ljava/util/List;Lorg/partiql/parser/SourceLocations;)V } -public final class org/partiql/parser/PartiQLParser$Result$Companion { -} - -public final class org/partiql/parser/PartiQLParserBuilder { +public class org/partiql/parser/PartiQLParserBuilder { public fun ()V - public final fun build ()Lorg/partiql/parser/PartiQLParser; -} - -public final class org/partiql/parser/SourceLocation { - public static final field Companion Lorg/partiql/parser/SourceLocation$Companion; - public fun (IIII)V - public synthetic fun (IIIIILkotlin/jvm/internal/DefaultConstructorMarker;)V - public final fun component1 ()I - public final fun component2 ()I - public final fun component3 ()I - public final fun component4 ()I - public final fun copy (IIII)Lorg/partiql/parser/SourceLocation; - public static synthetic fun copy$default (Lorg/partiql/parser/SourceLocation;IIIIILjava/lang/Object;)Lorg/partiql/parser/SourceLocation; - public fun equals (Ljava/lang/Object;)Z - public final fun getLength ()I - public final fun getLengthLegacy ()I - public final fun getLine ()I - public final fun getOffset ()I - public fun hashCode ()I - public fun toString ()Ljava/lang/String; + public fun build ()Lorg/partiql/parser/PartiQLParser; } -public final class org/partiql/parser/SourceLocation$Companion { - public final fun getUNKNOWN ()Lorg/partiql/parser/SourceLocation; -} - -public final class org/partiql/parser/SourceLocations : java/util/Map, kotlin/jvm/internal/markers/KMappedMarker { - public synthetic fun (Ljava/util/Map;Lkotlin/jvm/internal/DefaultConstructorMarker;)V +public class org/partiql/parser/SourceLocations : java/util/Map { public fun clear ()V - public synthetic fun compute (Ljava/lang/Object;Ljava/util/function/BiFunction;)Ljava/lang/Object; - public fun compute (Ljava/lang/String;Ljava/util/function/BiFunction;)Lorg/partiql/parser/SourceLocation; - public synthetic fun computeIfAbsent (Ljava/lang/Object;Ljava/util/function/Function;)Ljava/lang/Object; - public fun computeIfAbsent (Ljava/lang/String;Ljava/util/function/Function;)Lorg/partiql/parser/SourceLocation; - public synthetic fun computeIfPresent (Ljava/lang/Object;Ljava/util/function/BiFunction;)Ljava/lang/Object; - public fun computeIfPresent (Ljava/lang/String;Ljava/util/function/BiFunction;)Lorg/partiql/parser/SourceLocation; - public final fun containsKey (Ljava/lang/Object;)Z - public fun containsKey (Ljava/lang/String;)Z - public final fun containsValue (Ljava/lang/Object;)Z - public fun containsValue (Lorg/partiql/parser/SourceLocation;)Z - public final fun entrySet ()Ljava/util/Set; - public final synthetic fun get (Ljava/lang/Object;)Ljava/lang/Object; - public final fun get (Ljava/lang/Object;)Lorg/partiql/parser/SourceLocation; - public fun get (Ljava/lang/String;)Lorg/partiql/parser/SourceLocation; - public fun getEntries ()Ljava/util/Set; - public fun getKeys ()Ljava/util/Set; - public fun getSize ()I - public fun getValues ()Ljava/util/Collection; + public fun containsKey (Ljava/lang/Object;)Z + public fun containsValue (Ljava/lang/Object;)Z + public fun entrySet ()Ljava/util/Set; + public synthetic fun get (Ljava/lang/Object;)Ljava/lang/Object; + public fun get (Ljava/lang/Object;)Lorg/partiql/spi/SourceLocation; public fun isEmpty ()Z - public final fun keySet ()Ljava/util/Set; - public synthetic fun merge (Ljava/lang/Object;Ljava/lang/Object;Ljava/util/function/BiFunction;)Ljava/lang/Object; - public fun merge (Ljava/lang/String;Lorg/partiql/parser/SourceLocation;Ljava/util/function/BiFunction;)Lorg/partiql/parser/SourceLocation; + public fun keySet ()Ljava/util/Set; public synthetic fun put (Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object; - public fun put (Ljava/lang/String;Lorg/partiql/parser/SourceLocation;)Lorg/partiql/parser/SourceLocation; + public fun put (Ljava/lang/String;Lorg/partiql/spi/SourceLocation;)Lorg/partiql/spi/SourceLocation; public fun putAll (Ljava/util/Map;)V - public synthetic fun putIfAbsent (Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object; - public fun putIfAbsent (Ljava/lang/String;Lorg/partiql/parser/SourceLocation;)Lorg/partiql/parser/SourceLocation; public synthetic fun remove (Ljava/lang/Object;)Ljava/lang/Object; - public fun remove (Ljava/lang/Object;)Lorg/partiql/parser/SourceLocation; - public fun remove (Ljava/lang/Object;Ljava/lang/Object;)Z - public synthetic fun replace (Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object; - public synthetic fun replace (Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;)Z - public fun replace (Ljava/lang/String;Lorg/partiql/parser/SourceLocation;)Lorg/partiql/parser/SourceLocation; - public fun replace (Ljava/lang/String;Lorg/partiql/parser/SourceLocation;Lorg/partiql/parser/SourceLocation;)Z - public fun replaceAll (Ljava/util/function/BiFunction;)V - public final fun size ()I - public final fun values ()Ljava/util/Collection; + public fun remove (Ljava/lang/Object;)Lorg/partiql/spi/SourceLocation; + public fun size ()I + public fun values ()Ljava/util/Collection; +} + +public class org/partiql/parser/SourceLocations$Mutable { + public fun ()V + public fun set (Ljava/lang/String;Lorg/partiql/spi/SourceLocation;)V + public fun toMap ()Lorg/partiql/parser/SourceLocations; } public abstract interface class org/partiql/parser/V1PartiQLParser { diff --git a/partiql-parser/src/main/java/org/partiql/parser/PartiQLParser.java b/partiql-parser/src/main/java/org/partiql/parser/PartiQLParser.java new file mode 100644 index 0000000000..b2584633eb --- /dev/null +++ b/partiql-parser/src/main/java/org/partiql/parser/PartiQLParser.java @@ -0,0 +1,93 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. 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. + * A copy of the License is located at: + * + * http://aws.amazon.com/apache2.0/ + * + * or in the "license" file accompanying this file. This file is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific + * language governing permissions and limitations under the License. + */ + +package org.partiql.parser; + +import org.jetbrains.annotations.NotNull; +import org.partiql.ast.Statement; +import org.partiql.parser.internal.PartiQLParserDefault; +import org.partiql.spi.Context; +import org.partiql.spi.errors.PErrorListenerException; + +import java.util.List; + +/** + * TODO + */ +public interface PartiQLParser { + + /** + * Parses the [source] into an AST. + * @param source the user's input + * @param ctx a configuration object for the parser + * @throws PErrorListenerException when the [org.partiql.spi.errors.PErrorListener] defined in the [ctx] throws an + * [PErrorListenerException], this method halts execution and propagates the exception. + */ + @NotNull + Result parse(@NotNull String source, @NotNull Context ctx) throws PErrorListenerException; + + /** + * Parses the [source] into an AST. + * @param source the user's input + * @throws PErrorListenerException when the [org.partiql.spi.errors.PErrorListener] defined in the context throws an + * [PErrorListenerException], this method halts execution and propagates the exception. + */ + default Result parse(@NotNull String source) throws PErrorListenerException { + return parse(source, Context.standard()); + } + + /** + * TODO + */ + final class Result { + + /** + * TODO + */ + @NotNull + public List statements; + + /** + * TODO + */ + @NotNull + public SourceLocations locations; + + /** + * TODO + * @param statements TODO + * @param locations TODO + */ + public Result(@NotNull List statements, @NotNull SourceLocations locations) { + this.statements = statements; + this.locations = locations; + } + } + + /** + * TODO + * @return TODO + */ + public static PartiQLParserBuilder builder() { + return new PartiQLParserBuilder(); + } + + /** + * TODO + * @return TODO + */ + public static PartiQLParser standard() { + return new PartiQLParserDefault(); + } +} diff --git a/partiql-parser/src/main/kotlin/org/partiql/parser/PartiQLParserBuilder.kt b/partiql-parser/src/main/java/org/partiql/parser/PartiQLParserBuilder.java similarity index 72% rename from partiql-parser/src/main/kotlin/org/partiql/parser/PartiQLParserBuilder.kt rename to partiql-parser/src/main/java/org/partiql/parser/PartiQLParserBuilder.java index 1a918b0094..447424d76e 100644 --- a/partiql-parser/src/main/kotlin/org/partiql/parser/PartiQLParserBuilder.kt +++ b/partiql-parser/src/main/java/org/partiql/parser/PartiQLParserBuilder.java @@ -12,16 +12,22 @@ * language governing permissions and limitations under the License. */ -package org.partiql.parser +package org.partiql.parser; -import org.partiql.parser.internal.PartiQLParserDefault +import org.jetbrains.annotations.NotNull; +import org.partiql.parser.internal.PartiQLParserDefault; /** * A builder class to instantiate a [PartiQLParser]. */ public class PartiQLParserBuilder { - public fun build(): PartiQLParser { - return PartiQLParserDefault() + /** + * TODO + * @return TODO + */ + @NotNull + public PartiQLParser build() { + return new PartiQLParserDefault(); } } diff --git a/partiql-parser/src/main/java/org/partiql/parser/SourceLocations.java b/partiql-parser/src/main/java/org/partiql/parser/SourceLocations.java new file mode 100644 index 0000000000..8aaaba6b08 --- /dev/null +++ b/partiql-parser/src/main/java/org/partiql/parser/SourceLocations.java @@ -0,0 +1,113 @@ +package org.partiql.parser; + +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.partiql.spi.SourceLocation; + +import java.util.Collection; +import java.util.Map; +import java.util.Set; + +/** + * TODO + */ +public class SourceLocations implements Map { + + private final Map delegate; + + private SourceLocations(Map delegate) { + this.delegate = delegate; + } + + @NotNull + @Override + public Set> entrySet() { + return delegate.entrySet(); + } + + @NotNull + @Override + public Set keySet() { + return delegate.keySet(); + } + + @Override + public int size() { + return delegate.size(); + } + + @NotNull + @Override + public Collection values() { + return delegate.values(); + } + + @Override + public boolean containsKey(Object key) { + return delegate.containsKey(key); + } + + @Override + public boolean containsValue(Object value) { + return delegate.containsValue(value); + } + + @Override + public SourceLocation get(Object key) { + return delegate.get(key); + } + + @Nullable + @Override + public SourceLocation put(String key, SourceLocation value) { + // TODO: This isn't allowed (yet?) + return null; + } + + @Override + public SourceLocation remove(Object key) { + // TODO: This isn't allowed (yet?) + return null; + } + + @Override + public void putAll(@NotNull Map m) { + // TODO: This isn't allowed (yet?) + } + + @Override + public void clear() { + // TODO: This isn't allowed (yet?) + } + + @Override + public boolean isEmpty() { + return delegate.isEmpty(); + } + + /** + * TODO + */ + public static class Mutable { + + private final Map delegate = new java.util.HashMap<>(); + + /** + * TODO + * @param id TODO + * @param value TODO + */ + public void set(String id, SourceLocation value) { + delegate.put(id, value); + } + + /** + * TODO + * @return TODO + */ + public SourceLocations toMap() { + return new SourceLocations(delegate); + } + } +} + diff --git a/partiql-parser/src/main/kotlin/org/partiql/parser/Exceptions.kt b/partiql-parser/src/main/kotlin/org/partiql/parser/Exceptions.kt index 9efa54599e..02193f1a86 100644 --- a/partiql-parser/src/main/kotlin/org/partiql/parser/Exceptions.kt +++ b/partiql-parser/src/main/kotlin/org/partiql/parser/Exceptions.kt @@ -14,6 +14,8 @@ package org.partiql.parser +import org.partiql.spi.SourceLocation + /** * PartiQLParser Syntax Exception * TODO: Delete this in favor of the error listener. @@ -25,7 +27,7 @@ package org.partiql.parser internal open class PartiQLSyntaxException( override val message: String, override val cause: Throwable? = null, - val location: SourceLocation = SourceLocation.UNKNOWN, + private val location: SourceLocation? = null, ) : Exception() /** @@ -44,7 +46,7 @@ internal class PartiQLLexerException( public val tokenType: String, message: String = "", cause: Throwable? = null, - location: SourceLocation = SourceLocation.UNKNOWN, + location: SourceLocation? = null, ) : PartiQLSyntaxException(message, cause, location) /** @@ -65,5 +67,5 @@ internal class PartiQLParserException( public val tokenType: String, message: String = "", cause: Throwable? = null, - location: SourceLocation = SourceLocation.UNKNOWN, + location: SourceLocation? = null, ) : PartiQLSyntaxException(message, cause, location) diff --git a/partiql-parser/src/main/kotlin/org/partiql/parser/PartiQLParser.kt b/partiql-parser/src/main/kotlin/org/partiql/parser/PartiQLParser.kt deleted file mode 100644 index b34912e2af..0000000000 --- a/partiql-parser/src/main/kotlin/org/partiql/parser/PartiQLParser.kt +++ /dev/null @@ -1,71 +0,0 @@ -/* - * Copyright Amazon.com, Inc. or its affiliates. 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. - * A copy of the License is located at: - * - * http://aws.amazon.com/apache2.0/ - * - * or in the "license" file accompanying this file. This file is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific - * language governing permissions and limitations under the License. - */ - -package org.partiql.parser - -import org.partiql.ast.Expr -import org.partiql.ast.Statement -import org.partiql.parser.internal.PartiQLParserDefault -import org.partiql.spi.Context -import org.partiql.spi.errors.PErrorListenerException -import org.partiql.value.PartiQLValueExperimental -import org.partiql.value.nullValue -import kotlin.jvm.Throws - -public interface PartiQLParser { - - /** - * Parses the [source] into an AST. - * @param source the user's input - * @param ctx a configuration object for the parser - * @throws PErrorListenerException when the [org.partiql.spi.errors.PErrorListener] defined in the [ctx] throws an - * [PErrorListenerException], this method halts execution and propagates the exception. - */ - @Throws(PErrorListenerException::class) - public fun parse(source: String, ctx: Context): Result - - /** - * Parses the [source] into an AST. - * @param source the user's input - * @throws PErrorListenerException when the [org.partiql.spi.errors.PErrorListener] defined in the context throws an - * [PErrorListenerException], this method halts execution and propagates the exception. - */ - @Throws(PErrorListenerException::class) - public fun parse(source: String): Result { - return parse(source, Context.standard()) - } - - public data class Result( - val source: String, - val root: Statement, - val locations: SourceLocations, - ) { - public companion object { - @OptIn(PartiQLValueExperimental::class) - internal fun empty(source: String): Result { - val locations = SourceLocations.Mutable().toMap() - return Result(source, Statement.Query(Expr.Lit(nullValue())), locations) - } - } - } - - public companion object { - - @JvmStatic - public fun builder(): PartiQLParserBuilder = PartiQLParserBuilder() - - @JvmStatic - public fun standard(): PartiQLParser = PartiQLParserDefault() - } -} diff --git a/partiql-parser/src/main/kotlin/org/partiql/parser/SourceLocation.kt b/partiql-parser/src/main/kotlin/org/partiql/parser/SourceLocation.kt deleted file mode 100644 index 521934f30c..0000000000 --- a/partiql-parser/src/main/kotlin/org/partiql/parser/SourceLocation.kt +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Copyright Amazon.com, Inc. or its affiliates. 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. - * A copy of the License is located at: - * - * http://aws.amazon.com/apache2.0/ - * - * or in the "license" file accompanying this file. This file is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific - * language governing permissions and limitations under the License. - */ - -package org.partiql.parser - -/** - * SourceLocation represents the span of a given grammar rule; which corresponds to an AST subtree. - * - * TODO Fix Source Location Tests https://github.com/partiql/partiql-lang-kotlin/issues/1114 - * Unfortunately several mistakes were made that are hard to undo altogether. The legacy parser incorrectly - * used the first token length rather than rule span for source location length. Then we have asserted on these - * incorrect SourceLocations in many unit tests unrelated to SourceLocations. - * - * @property line - * @property offset - * @property length - * @property lengthLegacy - */ -public data class SourceLocation( - public val line: Int, - public val offset: Int, - public val length: Int, - public val lengthLegacy: Int = 0, -) { - - public companion object { - - /** - * This is a flag for backwards compatibility when converting to the legacy AST. - */ - public val UNKNOWN: SourceLocation = SourceLocation(-1, -1, -1) - } -} diff --git a/partiql-parser/src/main/kotlin/org/partiql/parser/SourceLocations.kt b/partiql-parser/src/main/kotlin/org/partiql/parser/SourceLocations.kt deleted file mode 100644 index 5d7c2ce4ae..0000000000 --- a/partiql-parser/src/main/kotlin/org/partiql/parser/SourceLocations.kt +++ /dev/null @@ -1,49 +0,0 @@ -/* - * Copyright Amazon.com, Inc. or its affiliates. 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. - * A copy of the License is located at: - * - * http://aws.amazon.com/apache2.0/ - * - * or in the "license" file accompanying this file. This file is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific - * language governing permissions and limitations under the License. - */ - -package org.partiql.parser - -/** - * Each node is hashable and has a unique identifier. Metadata is kept externally. - * Delegate once we are on Kotlin 1.7 - */ -public class SourceLocations private constructor( - private val delegate: Map -) : Map { - - override val entries: Set> = delegate.entries - - override val keys: Set = delegate.keys - - override val size: Int = delegate.size - - override val values: Collection = delegate.values - - override fun containsKey(key: String): Boolean = delegate.containsKey(key) - - override fun containsValue(value: SourceLocation): Boolean = delegate.containsValue(value) - - override fun get(key: String): SourceLocation? = delegate[key] - - override fun isEmpty(): Boolean = delegate.isEmpty() - - internal class Mutable { - - private val delegate = mutableMapOf() - - operator fun set(id: String, value: SourceLocation) = delegate.put(id, value) - - fun toMap() = SourceLocations(delegate) - } -} diff --git a/partiql-parser/src/main/kotlin/org/partiql/parser/internal/PartiQLParserDefault.kt b/partiql-parser/src/main/kotlin/org/partiql/parser/internal/PartiQLParserDefault.kt index ffac0ce607..8e5c46147c 100644 --- a/partiql-parser/src/main/kotlin/org/partiql/parser/internal/PartiQLParserDefault.kt +++ b/partiql-parser/src/main/kotlin/org/partiql/parser/internal/PartiQLParserDefault.kt @@ -174,11 +174,11 @@ import org.partiql.ast.typeVarchar import org.partiql.parser.PartiQLLexerException import org.partiql.parser.PartiQLParser import org.partiql.parser.PartiQLParserException -import org.partiql.parser.SourceLocation import org.partiql.parser.SourceLocations import org.partiql.parser.internal.antlr.PartiQLParserBaseVisitor import org.partiql.parser.internal.util.DateTimeUtils import org.partiql.spi.Context +import org.partiql.spi.SourceLocation import org.partiql.spi.errors.PError import org.partiql.spi.errors.PErrorKind import org.partiql.spi.errors.PErrorListener @@ -229,6 +229,7 @@ import org.partiql.parser.internal.antlr.PartiQLTokens as GeneratedLexer */ internal class PartiQLParserDefault : PartiQLParser { + @OptIn(PartiQLValueExperimental::class) @Throws(PErrorListenerException::class) override fun parse(source: String, ctx: Context): PartiQLParser.Result { try { @@ -238,7 +239,8 @@ internal class PartiQLParserDefault : PartiQLParser { } catch (throwable: Throwable) { val error = PError.INTERNAL_ERROR(PErrorKind.SYNTAX(), null, throwable) ctx.errorListener.report(error) - return PartiQLParser.Result.empty(source) + val locations = SourceLocations.Mutable().toMap() + return PartiQLParser.Result(listOf(Statement.Query(Expr.Lit(nullValue()))), locations) } } @@ -397,11 +399,7 @@ internal class PartiQLParserDefault : PartiQLParser { val locations = SourceLocations.Mutable() val visitor = Visitor(tokens, locations, tokens.parameterIndexes) val root = visitor.visitAs(tree) as Statement - return PartiQLParser.Result( - source = source, - root = root, - locations = locations.toMap(), - ) + return PartiQLParser.Result(listOf(root), locations.toMap()) // TODO: Parse multiple statements } fun error( @@ -415,10 +413,9 @@ internal class PartiQLParserDefault : PartiQLParser { message = message, cause = cause, location = SourceLocation( - line = ctx.start.line, - offset = ctx.start.charPositionInLine + 1, - length = ctx.stop.stopIndex - ctx.start.startIndex, - lengthLegacy = ctx.start.text.length, + ctx.start.line, + ctx.start.charPositionInLine + 1, + ctx.stop.stopIndex - ctx.start.startIndex, ), ) @@ -432,10 +429,9 @@ internal class PartiQLParserDefault : PartiQLParser { message = message, cause = cause, location = SourceLocation( - line = token.line, - offset = token.charPositionInLine + 1, - length = token.stopIndex - token.startIndex, - lengthLegacy = token.text.length, + token.line, + token.charPositionInLine + 1, + token.stopIndex - token.startIndex, ), ) @@ -451,10 +447,9 @@ internal class PartiQLParserDefault : PartiQLParser { val node = block() if (ctx.start != null) { locations[node.tag] = SourceLocation( - line = ctx.start.line, - offset = ctx.start.charPositionInLine + 1, - length = (ctx.stop?.stopIndex ?: ctx.start.stopIndex) - ctx.start.startIndex + 1, - lengthLegacy = ctx.start.text.length, // LEGACY LENGTH + ctx.start.line, + ctx.start.charPositionInLine + 1, + (ctx.stop?.stopIndex ?: ctx.start.stopIndex) - ctx.start.startIndex + 1, ) } return node diff --git a/partiql-parser/src/main/kotlin/org/partiql/parser/internal/V1PartiQLParserDefault.kt b/partiql-parser/src/main/kotlin/org/partiql/parser/internal/V1PartiQLParserDefault.kt index 0caa5228cc..3fa698a34a 100644 --- a/partiql-parser/src/main/kotlin/org/partiql/parser/internal/V1PartiQLParserDefault.kt +++ b/partiql-parser/src/main/kotlin/org/partiql/parser/internal/V1PartiQLParserDefault.kt @@ -154,13 +154,13 @@ import org.partiql.ast.v1.graph.GraphRestrictor import org.partiql.ast.v1.graph.GraphSelector import org.partiql.parser.PartiQLLexerException import org.partiql.parser.PartiQLParserException -import org.partiql.parser.SourceLocation import org.partiql.parser.SourceLocations import org.partiql.parser.V1PartiQLParser import org.partiql.parser.internal.antlr.PartiQLParser import org.partiql.parser.internal.antlr.PartiQLParserBaseVisitor import org.partiql.parser.internal.util.DateTimeUtils import org.partiql.spi.Context +import org.partiql.spi.SourceLocation import org.partiql.spi.errors.PError import org.partiql.spi.errors.PErrorKind import org.partiql.spi.errors.PErrorListener @@ -386,10 +386,9 @@ internal class V1PartiQLParserDefault : V1PartiQLParser { message = message, cause = cause, location = SourceLocation( - line = ctx.start.line, - offset = ctx.start.charPositionInLine + 1, - length = ctx.stop.stopIndex - ctx.start.startIndex, - lengthLegacy = ctx.start.text.length, + ctx.start.line, + ctx.start.charPositionInLine + 1, + ctx.stop.stopIndex - ctx.start.startIndex, ), ) @@ -403,10 +402,9 @@ internal class V1PartiQLParserDefault : V1PartiQLParser { message = message, cause = cause, location = SourceLocation( - line = token.line, - offset = token.charPositionInLine + 1, - length = token.stopIndex - token.startIndex, - lengthLegacy = token.text.length, + token.line, + token.charPositionInLine + 1, + token.stopIndex - token.startIndex, ), ) @@ -422,10 +420,9 @@ internal class V1PartiQLParserDefault : V1PartiQLParser { val node = block() if (ctx.start != null) { locations[node.tag] = SourceLocation( - line = ctx.start.line, - offset = ctx.start.charPositionInLine + 1, - length = (ctx.stop?.stopIndex ?: ctx.start.stopIndex) - ctx.start.startIndex + 1, - lengthLegacy = ctx.start.text.length, // LEGACY LENGTH + ctx.start.line, + ctx.start.charPositionInLine + 1, + (ctx.stop?.stopIndex ?: ctx.start.stopIndex) - ctx.start.startIndex + 1, ) } return node diff --git a/partiql-planner/src/test/kotlin/org/partiql/planner/PlanTest.kt b/partiql-planner/src/test/kotlin/org/partiql/planner/PlanTest.kt index f8ca7fbe81..c51b4470b5 100644 --- a/partiql-planner/src/test/kotlin/org/partiql/planner/PlanTest.kt +++ b/partiql-planner/src/test/kotlin/org/partiql/planner/PlanTest.kt @@ -75,7 +75,9 @@ class PlanTest { ) .namespace("SCHEMA") .build() - val ast = V1PartiQLParser.standard().parse(test.statement).root + val parseResult = V1PartiQLParser.standard().parse(test.statement) + if (parseResult.statements.size != 1) error("expected single statement") + val ast = parseResult.statements[0] val planner = PartiQLPlanner.builder().signal(isSignalMode).build() planner.plan(ast, session) } diff --git a/partiql-planner/src/test/kotlin/org/partiql/planner/PlannerPErrorReportingTests.kt b/partiql-planner/src/test/kotlin/org/partiql/planner/PlannerPErrorReportingTests.kt index 8747b0f537..507b79639b 100644 --- a/partiql-planner/src/test/kotlin/org/partiql/planner/PlannerPErrorReportingTests.kt +++ b/partiql-planner/src/test/kotlin/org/partiql/planner/PlannerPErrorReportingTests.kt @@ -45,7 +45,11 @@ internal class PlannerPErrorReportingTests { private val parser = V1PartiQLParserBuilder().build() private val statement: ((String) -> Statement) = { query -> - parser.parse(query).root + val parseResult = parser.parse(query) + if (parseResult.statements.size != 1) { + error("Expected single statement.") + } + parseResult.statements[0] } private fun assertProblem( diff --git a/partiql-planner/src/test/kotlin/org/partiql/planner/internal/exclude/SubsumptionTest.kt b/partiql-planner/src/test/kotlin/org/partiql/planner/internal/exclude/SubsumptionTest.kt index 11060cfc5c..3264045ca3 100644 --- a/partiql-planner/src/test/kotlin/org/partiql/planner/internal/exclude/SubsumptionTest.kt +++ b/partiql-planner/src/test/kotlin/org/partiql/planner/internal/exclude/SubsumptionTest.kt @@ -38,7 +38,11 @@ class SubsumptionTest { private fun testExcludeExprSubsumption(tc: SubsumptionTC) { val text = "SELECT * EXCLUDE ${tc.excludeExprStr} FROM <<>> AS s, <<>> AS t;" - val statement = parser.parse(text).root + val parseResult = parser.parse(text) + if (parseResult.statements.size != 1) { + throw IllegalStateException("Expected exactly one statement") + } + val statement = parseResult.statements[0] val session = Session.builder().catalog("default").catalogs(catalog).build() val plan = planner.plan(statement, session).plan val excludeClause = getExcludeClause(plan.getOperation()).getExclusions() diff --git a/partiql-planner/src/test/kotlin/org/partiql/planner/internal/typer/PartiQLTyperTestBase.kt b/partiql-planner/src/test/kotlin/org/partiql/planner/internal/typer/PartiQLTyperTestBase.kt index de978e8213..7359689068 100644 --- a/partiql-planner/src/test/kotlin/org/partiql/planner/internal/typer/PartiQLTyperTestBase.kt +++ b/partiql-planner/src/test/kotlin/org/partiql/planner/internal/typer/PartiQLTyperTestBase.kt @@ -52,7 +52,11 @@ abstract class PartiQLTyperTestBase { private val testingPipeline: ((String, String, Catalog, PErrorListener) -> PartiQLPlanner.Result) = { query, catalog, metadata, collector -> - val ast = parser.parse(query).root + val parseResult = parser.parse(query) + if (parseResult.statements.size != 1) { + throw IllegalArgumentException("Only single statement is supported for testing") + } + val ast = parseResult.statements[0] val config = Context.of(collector) planner.plan(ast, session(catalog, metadata), config) } diff --git a/partiql-planner/src/test/kotlin/org/partiql/planner/internal/typer/PlanTyperTestsPorted.kt b/partiql-planner/src/test/kotlin/org/partiql/planner/internal/typer/PlanTyperTestsPorted.kt index a5340499bd..c03f20ed92 100644 --- a/partiql-planner/src/test/kotlin/org/partiql/planner/internal/typer/PlanTyperTestsPorted.kt +++ b/partiql-planner/src/test/kotlin/org/partiql/planner/internal/typer/PlanTyperTestsPorted.kt @@ -3850,7 +3850,11 @@ internal class PlanTyperTestsPorted { session: Session, listener: PErrorListener, ): org.partiql.plan.Plan { - val ast = parser.parse(query).root + val parseResult = parser.parse(query) + if (parseResult.statements.size != 1) { + error("Expected exactly one statement in query: $query") + } + val ast = parseResult.statements[0] val plannerConfig = Context.of(listener) return planner.plan(ast, session, plannerConfig).plan } diff --git a/partiql-spi/api/partiql-spi.api b/partiql-spi/api/partiql-spi.api index 3e6b55e2a6..fa115613f9 100644 --- a/partiql-spi/api/partiql-spi.api +++ b/partiql-spi/api/partiql-spi.api @@ -41,6 +41,7 @@ public class org/partiql/spi/SourceLocation { public field length J public field line J public field offset J + public fun (III)V public fun (JJJ)V public fun equals (Ljava/lang/Object;)Z public fun hashCode ()I diff --git a/partiql-spi/src/main/java/org/partiql/spi/SourceLocation.java b/partiql-spi/src/main/java/org/partiql/spi/SourceLocation.java index 4cb6acb1c9..3a22a91c81 100644 --- a/partiql-spi/src/main/java/org/partiql/spi/SourceLocation.java +++ b/partiql-spi/src/main/java/org/partiql/spi/SourceLocation.java @@ -29,6 +29,12 @@ public SourceLocation(long line, long offset, long length) { this.length = length; } + public SourceLocation(int line, int offset, int length) { + this.line = line; + this.offset = offset; + this.length = length; + } + @Override public boolean equals(Object o) { if (this == o) return true; diff --git a/test/partiql-randomized-tests/src/test/kotlin/org/partiql/lang/randomized/eval/Utils.kt b/test/partiql-randomized-tests/src/test/kotlin/org/partiql/lang/randomized/eval/Utils.kt index 865c7cebc1..bd9f251e1c 100644 --- a/test/partiql-randomized-tests/src/test/kotlin/org/partiql/lang/randomized/eval/Utils.kt +++ b/test/partiql-randomized-tests/src/test/kotlin/org/partiql/lang/randomized/eval/Utils.kt @@ -32,7 +32,8 @@ private fun execute(query: String): PartiQLValue { // Execute val stmt = parser.parse(query) - val plan = planner.plan(stmt.root, session) + if (stmt.statements.size != 1) error("Expected exactly one statement, got ${stmt.statements.size}") + val plan = planner.plan(stmt.statements[0], session) TODO("Plan returns the sprout-generated plan, but this needs the v1 plan.") // val compiled = engine.prepare(plan.plan, PartiQLEngine.Mode.STRICT, session) // return (compiled.execute(session) as PartiQLResult.Value).value diff --git a/test/partiql-tests-runner/src/test/kotlin/org/partiql/runner/executor/EvalExecutor.kt b/test/partiql-tests-runner/src/test/kotlin/org/partiql/runner/executor/EvalExecutor.kt index 8e01b67629..92c52bb460 100644 --- a/test/partiql-tests-runner/src/test/kotlin/org/partiql/runner/executor/EvalExecutor.kt +++ b/test/partiql-tests-runner/src/test/kotlin/org/partiql/runner/executor/EvalExecutor.kt @@ -45,7 +45,11 @@ class EvalExecutor( override fun prepare(input: String): Statement { val listener = getErrorListener(mode) val ctx = Context.of(listener) - val ast = parser.parse(input, ctx).root + val parseResult = parser.parse(input, ctx) + if (parseResult.statements.size != 1) { + error("Expected exactly one statement") + } + val ast = parseResult.statements[0] val plan = planner.plan(ast, session, ctx).plan return compiler.prepare(plan, mode, ctx) } @@ -188,7 +192,11 @@ class EvalExecutor( .catalog("default") .catalogs(catalog) .build() - val stmt = parser.parse("`$env`").root + val parseResult = parser.parse("`$env`") + if (parseResult.statements.size != 1) { + error("Expected exactly one statement") + } + val stmt = parseResult.statements[0] val plan = planner.plan(stmt, session).plan return (plan.getOperation() as Query).getRex().getType().getPType() } From d911396236211cefba47cc631b2fbf7f708a1756 Mon Sep 17 00:00:00 2001 From: John Ed Quinn Date: Tue, 29 Oct 2024 17:05:50 -0700 Subject: [PATCH 02/12] Adds support for parsing multiple statements --- .../org/partiql/cli/shell/ShellHighlighter.kt | 2 +- .../eval/internal/PartiQLEvaluatorTest.kt | 9 +- partiql-parser/api/partiql-parser.api | 59 ++++-------- .../src/main/antlr/PartiQLParser.g4 | 14 +-- .../parser/PartiQLParserBuilderV1.java} | 15 ++- .../org/partiql/parser/PartiQLParserV1.java | 93 +++++++++++++++++++ .../org/partiql/parser/V1PartiQLParser.kt | 72 -------------- .../org/partiql/parser/internal/PFile.kt | 16 ++++ .../org/partiql/parser/internal/PFileV1.kt | 17 ++++ .../parser/internal/PartiQLParserDefault.kt | 66 ++++++------- ...erDefault.kt => PartiQLParserDefaultV1.kt} | 90 +++++++++--------- .../internal/PartiQLParserBagOpTests.kt | 7 +- .../parser/internal/PartiQLParserDDLTests.kt | 5 +- .../PartiQLParserFunctionCallTests.kt | 5 +- .../internal/PartiQLParserOperatorTests.kt | 5 +- .../PartiQLParserSessionAttributeTests.kt | 5 +- .../kotlin/org/partiql/planner/PlanTest.kt | 7 +- .../planner/PlannerPErrorReportingTests.kt | 4 +- .../internal/exclude/SubsumptionTest.kt | 4 +- .../internal/typer/PartiQLTyperTestBase.kt | 5 +- .../internal/typer/PlanTyperTestsPorted.kt | 4 +- .../partiql/runner/executor/EvalExecutor.kt | 9 +- 22 files changed, 273 insertions(+), 240 deletions(-) rename partiql-parser/src/main/{kotlin/org/partiql/parser/V1PartiQLParserBuilder.kt => java/org/partiql/parser/PartiQLParserBuilderV1.java} (66%) create mode 100644 partiql-parser/src/main/java/org/partiql/parser/PartiQLParserV1.java delete mode 100644 partiql-parser/src/main/kotlin/org/partiql/parser/V1PartiQLParser.kt create mode 100644 partiql-parser/src/main/kotlin/org/partiql/parser/internal/PFile.kt create mode 100644 partiql-parser/src/main/kotlin/org/partiql/parser/internal/PFileV1.kt rename partiql-parser/src/main/kotlin/org/partiql/parser/internal/{V1PartiQLParserDefault.kt => PartiQLParserDefaultV1.kt} (97%) diff --git a/partiql-cli/src/main/kotlin/org/partiql/cli/shell/ShellHighlighter.kt b/partiql-cli/src/main/kotlin/org/partiql/cli/shell/ShellHighlighter.kt index 48b73fd6d3..058d66220f 100644 --- a/partiql-cli/src/main/kotlin/org/partiql/cli/shell/ShellHighlighter.kt +++ b/partiql-cli/src/main/kotlin/org/partiql/cli/shell/ShellHighlighter.kt @@ -89,7 +89,7 @@ internal object ShellHighlighter : Highlighter { // Parse and Replace Token Style if Failures try { - parser.root() + parser.file() } catch (e: RethrowErrorListener.OffendingSymbolException) { val offending = e.offendingSymbol val prefix = builder.substring(0, offending.startIndex) diff --git a/partiql-eval/src/test/kotlin/org/partiql/eval/internal/PartiQLEvaluatorTest.kt b/partiql-eval/src/test/kotlin/org/partiql/eval/internal/PartiQLEvaluatorTest.kt index b64eddeff1..9f1fad3486 100644 --- a/partiql-eval/src/test/kotlin/org/partiql/eval/internal/PartiQLEvaluatorTest.kt +++ b/partiql-eval/src/test/kotlin/org/partiql/eval/internal/PartiQLEvaluatorTest.kt @@ -38,6 +38,7 @@ import org.partiql.value.symbolValue import java.io.ByteArrayOutputStream import java.math.BigDecimal import java.math.BigInteger +import kotlin.test.assertEquals import kotlin.test.assertNotNull /** @@ -1321,9 +1322,7 @@ class PartiQLEvaluatorTest { internal fun assert() { val parseResult = parser.parse(input) - if (parseResult.statements.size != 1) { - throw RuntimeException("Expected exactly one statement: $input") - } + assertEquals(1, parseResult.statements.size) val statement = parseResult.statements[0] val catalog = Catalog.builder() .name("memory") @@ -1415,9 +1414,7 @@ class PartiQLEvaluatorTest { val catalog = Catalog.builder().name("memory").build() ======= val parseResult = parser.parse(input) - if (parseResult.statements.size != 1) { - throw RuntimeException("Expected exactly one statement: $input") - } + assertEquals(1, parseResult.statements.size) val statement = parseResult.statements[0] val catalog = MemoryCatalog.builder().name("memory").build() >>>>>>> 1c662c3be (Migrates parser APIs to Java) diff --git a/partiql-parser/api/partiql-parser.api b/partiql-parser/api/partiql-parser.api index 69a6c5d485..84b8df262d 100644 --- a/partiql-parser/api/partiql-parser.api +++ b/partiql-parser/api/partiql-parser.api @@ -16,6 +16,24 @@ public class org/partiql/parser/PartiQLParserBuilder { public fun build ()Lorg/partiql/parser/PartiQLParser; } +public class org/partiql/parser/PartiQLParserBuilderV1 { + public fun ()V + public fun build ()Lorg/partiql/parser/PartiQLParserV1; +} + +public abstract interface class org/partiql/parser/PartiQLParserV1 { + public static fun builder ()Lorg/partiql/parser/PartiQLParserBuilderV1; + public fun parse (Ljava/lang/String;)Lorg/partiql/parser/PartiQLParserV1$Result; + public abstract fun parse (Ljava/lang/String;Lorg/partiql/spi/Context;)Lorg/partiql/parser/PartiQLParserV1$Result; + public static fun standard ()Lorg/partiql/parser/PartiQLParserV1; +} + +public final class org/partiql/parser/PartiQLParserV1$Result { + public field locations Lorg/partiql/parser/SourceLocations; + public field statements Ljava/util/List; + public fun (Ljava/util/List;Lorg/partiql/parser/SourceLocations;)V +} + public class org/partiql/parser/SourceLocations : java/util/Map { public fun clear ()V public fun containsKey (Ljava/lang/Object;)Z @@ -40,44 +58,3 @@ public class org/partiql/parser/SourceLocations$Mutable { public fun toMap ()Lorg/partiql/parser/SourceLocations; } -public abstract interface class org/partiql/parser/V1PartiQLParser { - public static final field Companion Lorg/partiql/parser/V1PartiQLParser$Companion; - public static fun builder ()Lorg/partiql/parser/V1PartiQLParserBuilder; - public abstract fun parse (Ljava/lang/String;)Lorg/partiql/parser/V1PartiQLParser$Result; - public abstract fun parse (Ljava/lang/String;Lorg/partiql/spi/Context;)Lorg/partiql/parser/V1PartiQLParser$Result; - public static fun standard ()Lorg/partiql/parser/V1PartiQLParser; -} - -public final class org/partiql/parser/V1PartiQLParser$Companion { - public final fun builder ()Lorg/partiql/parser/V1PartiQLParserBuilder; - public final fun standard ()Lorg/partiql/parser/V1PartiQLParser; -} - -public final class org/partiql/parser/V1PartiQLParser$DefaultImpls { - public static fun parse (Lorg/partiql/parser/V1PartiQLParser;Ljava/lang/String;)Lorg/partiql/parser/V1PartiQLParser$Result; -} - -public final class org/partiql/parser/V1PartiQLParser$Result { - public static final field Companion Lorg/partiql/parser/V1PartiQLParser$Result$Companion; - public fun (Ljava/lang/String;Lorg/partiql/ast/v1/Statement;Lorg/partiql/parser/SourceLocations;)V - public final fun component1 ()Ljava/lang/String; - public final fun component2 ()Lorg/partiql/ast/v1/Statement; - public final fun component3 ()Lorg/partiql/parser/SourceLocations; - public final fun copy (Ljava/lang/String;Lorg/partiql/ast/v1/Statement;Lorg/partiql/parser/SourceLocations;)Lorg/partiql/parser/V1PartiQLParser$Result; - public static synthetic fun copy$default (Lorg/partiql/parser/V1PartiQLParser$Result;Ljava/lang/String;Lorg/partiql/ast/v1/Statement;Lorg/partiql/parser/SourceLocations;ILjava/lang/Object;)Lorg/partiql/parser/V1PartiQLParser$Result; - public fun equals (Ljava/lang/Object;)Z - public final fun getLocations ()Lorg/partiql/parser/SourceLocations; - public final fun getRoot ()Lorg/partiql/ast/v1/Statement; - public final fun getSource ()Ljava/lang/String; - public fun hashCode ()I - public fun toString ()Ljava/lang/String; -} - -public final class org/partiql/parser/V1PartiQLParser$Result$Companion { -} - -public final class org/partiql/parser/V1PartiQLParserBuilder { - public fun ()V - public final fun build ()Lorg/partiql/parser/V1PartiQLParser; -} - diff --git a/partiql-parser/src/main/antlr/PartiQLParser.g4 b/partiql-parser/src/main/antlr/PartiQLParser.g4 index f4f7cb8fb5..b32a2d20c0 100644 --- a/partiql-parser/src/main/antlr/PartiQLParser.g4 +++ b/partiql-parser/src/main/antlr/PartiQLParser.g4 @@ -11,14 +11,16 @@ options { * */ -root - : (EXPLAIN (PAREN_LEFT explainOption (COMMA explainOption)* PAREN_RIGHT)? )? statement; +file + : statement (COLON_SEMI statement)* COLON_SEMI? EOF + ; statement - : dql COLON_SEMI? EOF # QueryDql - | dml COLON_SEMI? EOF # QueryDml - | ddl COLON_SEMI? EOF # QueryDdl - | execCommand COLON_SEMI? EOF # QueryExec // TODO delete in `v1` release + : dql # QueryDql + | dml # QueryDml + | ddl # QueryDdl + | execCommand # QueryExec // TODO delete in `v1` release + | EXPLAIN (PAREN_LEFT explainOption (COMMA explainOption)* PAREN_RIGHT)? statement # Explain ; /** diff --git a/partiql-parser/src/main/kotlin/org/partiql/parser/V1PartiQLParserBuilder.kt b/partiql-parser/src/main/java/org/partiql/parser/PartiQLParserBuilderV1.java similarity index 66% rename from partiql-parser/src/main/kotlin/org/partiql/parser/V1PartiQLParserBuilder.kt rename to partiql-parser/src/main/java/org/partiql/parser/PartiQLParserBuilderV1.java index ae1a972ba7..b004891313 100644 --- a/partiql-parser/src/main/kotlin/org/partiql/parser/V1PartiQLParserBuilder.kt +++ b/partiql-parser/src/main/java/org/partiql/parser/PartiQLParserBuilderV1.java @@ -12,18 +12,17 @@ * language governing permissions and limitations under the License. */ -package org.partiql.parser +package org.partiql.parser; -import org.partiql.parser.internal.V1PartiQLParserDefault +import org.partiql.parser.internal.PartiQLParserDefaultV1; /** - * A builder class to instantiate a [V1PartiQLParser]. https://github.com/partiql/partiql-lang-kotlin/issues/1632 - * - * TODO replace with Lombok builder once [V1PartiQLParser] is migrated to Java. + * A builder class to instantiate a [PartiQLParserV1]. https://github.com/partiql/partiql-lang-kotlin/issues/1632 + * TODO replace with Lombok builder once [PartiQLParserV1] is migrated to Java. */ -public class V1PartiQLParserBuilder { +public class PartiQLParserBuilderV1 { - public fun build(): V1PartiQLParser { - return V1PartiQLParserDefault() + public PartiQLParserV1 build() { + return new PartiQLParserDefaultV1(); } } diff --git a/partiql-parser/src/main/java/org/partiql/parser/PartiQLParserV1.java b/partiql-parser/src/main/java/org/partiql/parser/PartiQLParserV1.java new file mode 100644 index 0000000000..bd9516782d --- /dev/null +++ b/partiql-parser/src/main/java/org/partiql/parser/PartiQLParserV1.java @@ -0,0 +1,93 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. 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. + * A copy of the License is located at: + * + * http://aws.amazon.com/apache2.0/ + * + * or in the "license" file accompanying this file. This file is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific + * language governing permissions and limitations under the License. + */ + +package org.partiql.parser; + +import org.jetbrains.annotations.NotNull; +import org.partiql.ast.v1.Statement; +import org.partiql.parser.internal.PartiQLParserDefaultV1; +import org.partiql.spi.Context; +import org.partiql.spi.errors.PErrorListenerException; + +import java.util.List; + +/** + * TODO: Rename to PartiQLParser + */ +public interface PartiQLParserV1 { + + /** + * Parses the [source] into an AST. + * @param source the user's input + * @param ctx a configuration object for the parser + * @throws PErrorListenerException when the [org.partiql.spi.errors.PErrorListener] defined in the [ctx] throws an + * [PErrorListenerException], this method halts execution and propagates the exception. + */ + @NotNull + Result parse(@NotNull String source, @NotNull Context ctx) throws PErrorListenerException; + + /** + * Parses the [source] into an AST. + * @param source the user's input + * @throws PErrorListenerException when the [org.partiql.spi.errors.PErrorListener] defined in the context throws an + * [PErrorListenerException], this method halts execution and propagates the exception. + */ + default Result parse(@NotNull String source) throws PErrorListenerException { + return parse(source, Context.standard()); + } + + /** + * TODO + */ + final class Result { + + /** + * TODO + */ + @NotNull + public List statements; + + /** + * TODO + */ + @NotNull + public SourceLocations locations; + + /** + * TODO + * @param statements TODO + * @param locations TODO + */ + public Result(@NotNull List statements, @NotNull SourceLocations locations) { + this.statements = statements; + this.locations = locations; + } + } + + /** + * TODO + * @return TODO + */ + public static PartiQLParserBuilderV1 builder() { + return new PartiQLParserBuilderV1(); + } + + /** + * TODO + * @return TODO + */ + public static PartiQLParserV1 standard() { + return new PartiQLParserDefaultV1(); + } +} diff --git a/partiql-parser/src/main/kotlin/org/partiql/parser/V1PartiQLParser.kt b/partiql-parser/src/main/kotlin/org/partiql/parser/V1PartiQLParser.kt deleted file mode 100644 index 1535f66b30..0000000000 --- a/partiql-parser/src/main/kotlin/org/partiql/parser/V1PartiQLParser.kt +++ /dev/null @@ -1,72 +0,0 @@ -/* - * Copyright Amazon.com, Inc. or its affiliates. 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. - * A copy of the License is located at: - * - * http://aws.amazon.com/apache2.0/ - * - * or in the "license" file accompanying this file. This file is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific - * language governing permissions and limitations under the License. - */ - -package org.partiql.parser - -import org.partiql.ast.v1.Query -import org.partiql.ast.v1.Statement -import org.partiql.ast.v1.expr.ExprLit -import org.partiql.parser.internal.V1PartiQLParserDefault -import org.partiql.spi.Context -import org.partiql.spi.errors.PErrorListenerException -import org.partiql.value.PartiQLValueExperimental -import org.partiql.value.nullValue - -// TODO migrate public interfaces and classes to Java https://github.com/partiql/partiql-lang-kotlin/issues/1632 -public interface V1PartiQLParser { - - /** - * Parses the [source] into an AST. - * @param source the user's input - * @param ctx a configuration object for the parser - * @throws PErrorListenerException when the [org.partiql.spi.errors.PErrorListener] defined in the [ctx] throws an - * [PErrorListenerException], this method halts execution and propagates the exception. - */ - @Throws(PErrorListenerException::class) - public fun parse(source: String, ctx: Context): Result - - /** - * Parses the [source] into an AST. - * @param source the user's input - * @throws PErrorListenerException when the [org.partiql.spi.errors.PErrorListener] defined in the context throws an - * [PErrorListenerException], this method halts execution and propagates the exception. - */ - @Throws(PErrorListenerException::class) - public fun parse(source: String): Result { - return parse(source, Context.standard()) - } - - public data class Result( - val source: String, - val root: Statement, - val locations: SourceLocations, - ) { - public companion object { - @OptIn(PartiQLValueExperimental::class) - internal fun empty(source: String): Result { - val locations = SourceLocations.Mutable().toMap() - return Result(source, Query(ExprLit(nullValue())), locations) - } - } - } - - public companion object { - - @JvmStatic - public fun builder(): V1PartiQLParserBuilder = V1PartiQLParserBuilder() - - @JvmStatic - public fun standard(): V1PartiQLParser = V1PartiQLParserDefault() - } -} diff --git a/partiql-parser/src/main/kotlin/org/partiql/parser/internal/PFile.kt b/partiql-parser/src/main/kotlin/org/partiql/parser/internal/PFile.kt new file mode 100644 index 0000000000..db8942d478 --- /dev/null +++ b/partiql-parser/src/main/kotlin/org/partiql/parser/internal/PFile.kt @@ -0,0 +1,16 @@ +package org.partiql.parser.internal + +import org.partiql.ast.AstNode +import org.partiql.ast.Statement +import org.partiql.ast.visitor.AstVisitor + +internal class PFile( + internal val statements: List, +) : AstNode() { + + override val children: List = this.statements + + override fun accept(visitor: AstVisitor, ctx: C): R { + error("This is an internal ast node and cannot be passed to a visitor.") + } +} diff --git a/partiql-parser/src/main/kotlin/org/partiql/parser/internal/PFileV1.kt b/partiql-parser/src/main/kotlin/org/partiql/parser/internal/PFileV1.kt new file mode 100644 index 0000000000..ff59342d1b --- /dev/null +++ b/partiql-parser/src/main/kotlin/org/partiql/parser/internal/PFileV1.kt @@ -0,0 +1,17 @@ +package org.partiql.parser.internal + +import org.partiql.ast.v1.AstNode +import org.partiql.ast.v1.AstVisitor +import org.partiql.ast.v1.Statement + +internal class PFileV1( + internal val statements: List, +) : AstNode() { + override fun children(): MutableCollection { + return statements.toMutableList() + } + + override fun accept(visitor: AstVisitor, ctx: C): R { + error("This is an internal ast node and cannot be passed to a visitor.") + } +} diff --git a/partiql-parser/src/main/kotlin/org/partiql/parser/internal/PartiQLParserDefault.kt b/partiql-parser/src/main/kotlin/org/partiql/parser/internal/PartiQLParserDefault.kt index 8e5c46147c..5c9bec3fe3 100644 --- a/partiql-parser/src/main/kotlin/org/partiql/parser/internal/PartiQLParserDefault.kt +++ b/partiql-parser/src/main/kotlin/org/partiql/parser/internal/PartiQLParserDefault.kt @@ -270,7 +270,7 @@ internal class PartiQLParserDefault : PartiQLParser { PredictionMode.LL -> parser.addErrorListener(ParseErrorListener(listener)) else -> throw IllegalArgumentException("Unsupported parser mode: $mode") } - val tree = parser.root() + val tree = parser.file() return Visitor.translate(source, tokens, tree) } @@ -394,12 +394,12 @@ internal class PartiQLParserDefault : PartiQLParser { fun translate( source: String, tokens: CountingTokenStream, - tree: GeneratedParser.RootContext, + tree: GeneratedParser.FileContext, ): PartiQLParser.Result { val locations = SourceLocations.Mutable() val visitor = Visitor(tokens, locations, tokens.parameterIndexes) - val root = visitor.visitAs(tree) as Statement - return PartiQLParser.Result(listOf(root), locations.toMap()) // TODO: Parse multiple statements + val root = visitor.visitAs(tree) as PFile + return PartiQLParser.Result(root.statements, locations.toMap()) } fun error( @@ -467,36 +467,40 @@ internal class PartiQLParserDefault : PartiQLParser { throw error(ctx, "DML no longer supported in the default PartiQLParser.") } - override fun visitRoot(ctx: GeneratedParser.RootContext) = translate(ctx) { - when (ctx.EXPLAIN()) { - null -> visit(ctx.statement()) as Statement - else -> { - var type: String? = null - var format: String? = null - ctx.explainOption().forEach { option -> - val parameter = try { - ExplainParameters.valueOf(option.param.text.uppercase()) - } catch (ex: java.lang.IllegalArgumentException) { - throw error(option.param, "Unknown EXPLAIN parameter.", ex) - } - when (parameter) { - ExplainParameters.TYPE -> { - type = parameter.getCompliantString(type, option.value) - } - ExplainParameters.FORMAT -> { - format = parameter.getCompliantString(format, option.value) - } - } - } - statementExplain( - target = statementExplainTargetDomain( - statement = visit(ctx.statement()) as Statement, - type = type, - format = format, - ), + override fun visitExplain(ctx: GeneratedParser.ExplainContext) = translate(ctx) { + var type: String? = null + var format: String? = null + ctx.explainOption().forEach { option -> + val parameter = try { + ExplainParameters.valueOf(option.param.text.uppercase()) + } catch (ex: java.lang.IllegalArgumentException) { + throw error( + option.param, + "Unknown EXPLAIN parameter.", + ex ) } + when (parameter) { + ExplainParameters.TYPE -> { + type = parameter.getCompliantString(type, option.value) + } + ExplainParameters.FORMAT -> { + format = parameter.getCompliantString(format, option.value) + } + } } + statementExplain( + target = statementExplainTargetDomain( + statement = visit(ctx.statement()) as Statement, + type = type, + format = format + ) + ) + } + + override fun visitFile(ctx: GeneratedParser.FileContext) = translate(ctx) { + val stmts = visitOrEmpty(ctx.statement()) + PFile(stmts) } /** diff --git a/partiql-parser/src/main/kotlin/org/partiql/parser/internal/V1PartiQLParserDefault.kt b/partiql-parser/src/main/kotlin/org/partiql/parser/internal/PartiQLParserDefaultV1.kt similarity index 97% rename from partiql-parser/src/main/kotlin/org/partiql/parser/internal/V1PartiQLParserDefault.kt rename to partiql-parser/src/main/kotlin/org/partiql/parser/internal/PartiQLParserDefaultV1.kt index 3fa698a34a..6ecf154f3c 100644 --- a/partiql-parser/src/main/kotlin/org/partiql/parser/internal/V1PartiQLParserDefault.kt +++ b/partiql-parser/src/main/kotlin/org/partiql/parser/internal/PartiQLParserDefaultV1.kt @@ -154,8 +154,8 @@ import org.partiql.ast.v1.graph.GraphRestrictor import org.partiql.ast.v1.graph.GraphSelector import org.partiql.parser.PartiQLLexerException import org.partiql.parser.PartiQLParserException +import org.partiql.parser.PartiQLParserV1 import org.partiql.parser.SourceLocations -import org.partiql.parser.V1PartiQLParser import org.partiql.parser.internal.antlr.PartiQLParser import org.partiql.parser.internal.antlr.PartiQLParserBaseVisitor import org.partiql.parser.internal.util.DateTimeUtils @@ -207,10 +207,11 @@ import org.partiql.parser.internal.antlr.PartiQLTokens as GeneratedLexer * The [PredictionMode.LL] mode is capable of parsing all valid inputs for a grammar, * but is slower than [PredictionMode.SLL]. Upon seeing a syntax error, this parser throws a [PartiQLParserException]. */ -internal class V1PartiQLParserDefault : V1PartiQLParser { +internal class PartiQLParserDefaultV1 : PartiQLParserV1 { + @OptIn(PartiQLValueExperimental::class) @Throws(PErrorListenerException::class) - override fun parse(source: String, ctx: Context): V1PartiQLParser.Result { + override fun parse(source: String, ctx: Context): PartiQLParserV1.Result { try { return parse(source, ctx.errorListener) } catch (e: PErrorListenerException) { @@ -218,15 +219,19 @@ internal class V1PartiQLParserDefault : V1PartiQLParser { } catch (throwable: Throwable) { val error = PError.INTERNAL_ERROR(PErrorKind.SYNTAX(), null, throwable) ctx.errorListener.report(error) - return V1PartiQLParser.Result.empty(source) + val locations = SourceLocations.Mutable().toMap() + return PartiQLParserV1.Result( + mutableListOf(org.partiql.ast.v1.Query(org.partiql.ast.v1.expr.ExprLit(nullValue()))) as List, + locations + ) } } /** - * To reduce latency costs, the [V1PartiQLParserDefault] attempts to use [PredictionMode.SLL] and falls back to + * To reduce latency costs, the [PartiQLParserDefaultV1] attempts to use [PredictionMode.SLL] and falls back to * [PredictionMode.LL] if a [ParseCancellationException] is thrown by the [BailErrorStrategy]. */ - private fun parse(source: String, listener: PErrorListener): V1PartiQLParser.Result = try { + private fun parse(source: String, listener: PErrorListener): PartiQLParserV1.Result = try { parse(source, PredictionMode.SLL, listener) } catch (ex: ParseCancellationException) { parse(source, PredictionMode.LL, listener) @@ -235,7 +240,7 @@ internal class V1PartiQLParserDefault : V1PartiQLParser { /** * Parses an input string [source] using the given prediction mode. */ - private fun parse(source: String, mode: PredictionMode, listener: PErrorListener): V1PartiQLParser.Result { + private fun parse(source: String, mode: PredictionMode, listener: PErrorListener): PartiQLParserV1.Result { val tokens = createTokenStream(source, listener) val parser = InterruptibleParser(tokens) parser.reset() @@ -246,7 +251,7 @@ internal class V1PartiQLParserDefault : V1PartiQLParser { PredictionMode.LL -> parser.addErrorListener(ParseErrorListener(listener)) else -> throw IllegalArgumentException("Unsupported parser mode: $mode") } - val tree = parser.root() + val tree = parser.file() return Visitor.translate(source, tokens, tree) } @@ -363,15 +368,14 @@ internal class V1PartiQLParserDefault : V1PartiQLParser { fun translate( source: String, tokens: CountingTokenStream, - tree: GeneratedParser.RootContext, - ): V1PartiQLParser.Result { + tree: GeneratedParser.FileContext, + ): PartiQLParserV1.Result { val locations = SourceLocations.Mutable() val visitor = Visitor(tokens, locations, tokens.parameterIndexes) - val root = visitor.visitAs(tree) as Statement - return V1PartiQLParser.Result( - source = source, - root = root, - locations = locations.toMap(), + val root = visitor.visitAs(tree) as PFileV1 + return PartiQLParserV1.Result( + root.statements.toMutableList(), + locations.toMap(), ) } @@ -440,37 +444,37 @@ internal class V1PartiQLParserDefault : V1PartiQLParser { throw error(ctx, "DML no longer supported in the default PartiQLParser.") } - override fun visitRoot(ctx: GeneratedParser.RootContext) = translate(ctx) { - when (ctx.EXPLAIN()) { - null -> visit(ctx.statement()) as Statement - else -> { - var type: String? = null - var format: String? = null - ctx.explainOption().forEach { option -> - val parameter = try { - ExplainParameters.valueOf(option.param.text.uppercase()) - } catch (ex: java.lang.IllegalArgumentException) { - throw error(option.param, "Unknown EXPLAIN parameter.", ex) - } - when (parameter) { - ExplainParameters.TYPE -> { - type = parameter.getCompliantString(type, option.value) - } - ExplainParameters.FORMAT -> { - format = parameter.getCompliantString(format, option.value) - } - } + override fun visitExplain(ctx: PartiQLParser.ExplainContext) = translate(ctx) { + var type: String? = null + var format: String? = null + ctx.explainOption().forEach { option -> + val parameter = try { + ExplainParameters.valueOf(option.param.text.uppercase()) + } catch (ex: java.lang.IllegalArgumentException) { + throw error(option.param, "Unknown EXPLAIN parameter.", ex) + } + when (parameter) { + ExplainParameters.TYPE -> { + type = parameter.getCompliantString(type, option.value) + } + ExplainParameters.FORMAT -> { + format = parameter.getCompliantString(format, option.value) } - explain( - // TODO get rid of usage of PartiQLValue https://github.com/partiql/partiql-lang-kotlin/issues/1589 - options = mapOf( - "type" to stringValue(type), - "format" to stringValue(format) - ), - statement = visit(ctx.statement()) as Statement, - ) } } + explain( + // TODO get rid of usage of PartiQLValue https://github.com/partiql/partiql-lang-kotlin/issues/1589 + options = mapOf( + "type" to stringValue(type), + "format" to stringValue(format) + ), + statement = visit(ctx.statement()) as Statement, + ) + } + + override fun visitFile(ctx: GeneratedParser.FileContext): PFileV1 = translate(ctx) { + val stmts = visitOrEmpty(ctx.statement()) + PFileV1(stmts) } /** diff --git a/partiql-parser/src/test/kotlin/org/partiql/parser/internal/PartiQLParserBagOpTests.kt b/partiql-parser/src/test/kotlin/org/partiql/parser/internal/PartiQLParserBagOpTests.kt index f886401019..52794b27fa 100644 --- a/partiql-parser/src/test/kotlin/org/partiql/parser/internal/PartiQLParserBagOpTests.kt +++ b/partiql-parser/src/test/kotlin/org/partiql/parser/internal/PartiQLParserBagOpTests.kt @@ -26,7 +26,7 @@ import kotlin.test.assertEquals class PartiQLParserBagOpTests { - private val parser = V1PartiQLParserDefault() + private val parser = PartiQLParserDefaultV1() private fun queryBody(body: () -> Expr) = query(body()) @@ -676,8 +676,9 @@ class PartiQLParserBagOpTests { ) private fun assertExpression(input: String, expected: AstNode) { - val result = parser.parse(input) - val actual = result.root + val parseResult = parser.parse(input) + assertEquals(1, parseResult.statements.size) + val actual = parseResult.statements[0] assertEquals(expected, actual) } } diff --git a/partiql-parser/src/test/kotlin/org/partiql/parser/internal/PartiQLParserDDLTests.kt b/partiql-parser/src/test/kotlin/org/partiql/parser/internal/PartiQLParserDDLTests.kt index bd4a3d7ddb..dbbacafdf5 100644 --- a/partiql-parser/src/test/kotlin/org/partiql/parser/internal/PartiQLParserDDLTests.kt +++ b/partiql-parser/src/test/kotlin/org/partiql/parser/internal/PartiQLParserDDLTests.kt @@ -5,7 +5,7 @@ import kotlin.test.assertEquals class PartiQLParserDDLTests { - private val parser = V1PartiQLParserDefault() + private val parser = PartiQLParserDefaultV1() data class SuccessTestCase( val description: String? = null, @@ -118,7 +118,8 @@ class PartiQLParserDDLTests { private fun assertExpression(input: String, expected: AstNode) { val result = parser.parse(input) - val actual = result.root + assertEquals(1, result.statements.size) + val actual = result.statements[0] assertEquals(expected, actual) } } diff --git a/partiql-parser/src/test/kotlin/org/partiql/parser/internal/PartiQLParserFunctionCallTests.kt b/partiql-parser/src/test/kotlin/org/partiql/parser/internal/PartiQLParserFunctionCallTests.kt index 6cc3947584..2bda30224f 100644 --- a/partiql-parser/src/test/kotlin/org/partiql/parser/internal/PartiQLParserFunctionCallTests.kt +++ b/partiql-parser/src/test/kotlin/org/partiql/parser/internal/PartiQLParserFunctionCallTests.kt @@ -11,7 +11,7 @@ import kotlin.test.assertEquals class PartiQLParserFunctionCallTests { - private val parser = V1PartiQLParserDefault() + private val parser = PartiQLParserDefaultV1() private inline fun queryBody(body: () -> Expr) = query(body()) @@ -137,7 +137,8 @@ class PartiQLParserFunctionCallTests { private fun assertExpression(input: String, expected: AstNode) { val result = parser.parse(input) - val actual = result.root + assertEquals(1, result.statements.size) + val actual = result.statements[0] assertEquals(expected, actual) } } diff --git a/partiql-parser/src/test/kotlin/org/partiql/parser/internal/PartiQLParserOperatorTests.kt b/partiql-parser/src/test/kotlin/org/partiql/parser/internal/PartiQLParserOperatorTests.kt index 7b25f97e43..a5c8634066 100644 --- a/partiql-parser/src/test/kotlin/org/partiql/parser/internal/PartiQLParserOperatorTests.kt +++ b/partiql-parser/src/test/kotlin/org/partiql/parser/internal/PartiQLParserOperatorTests.kt @@ -13,7 +13,7 @@ import kotlin.test.assertEquals @OptIn(PartiQLValueExperimental::class) class PartiQLParserOperatorTests { - private val parser = V1PartiQLParserDefault() + private val parser = PartiQLParserDefaultV1() private inline fun queryBody(body: () -> Expr) = query(body()) @@ -67,7 +67,8 @@ class PartiQLParserOperatorTests { private fun assertExpression(input: String, expected: AstNode) { val result = parser.parse(input) - val actual = result.root + assertEquals(1, result.statements.size) + val actual = result.statements[0] assertEquals(expected, actual) } } diff --git a/partiql-parser/src/test/kotlin/org/partiql/parser/internal/PartiQLParserSessionAttributeTests.kt b/partiql-parser/src/test/kotlin/org/partiql/parser/internal/PartiQLParserSessionAttributeTests.kt index 7f0604759c..6dd55d49e8 100644 --- a/partiql-parser/src/test/kotlin/org/partiql/parser/internal/PartiQLParserSessionAttributeTests.kt +++ b/partiql-parser/src/test/kotlin/org/partiql/parser/internal/PartiQLParserSessionAttributeTests.kt @@ -15,7 +15,7 @@ import kotlin.test.assertEquals @OptIn(PartiQLValueExperimental::class) class PartiQLParserSessionAttributeTests { - private val parser = V1PartiQLParserDefault() + private val parser = PartiQLParserDefaultV1() private inline fun queryBody(body: () -> Expr) = query(body()) @@ -81,7 +81,8 @@ class PartiQLParserSessionAttributeTests { private fun assertExpression(input: String, expected: AstNode) { val result = parser.parse(input) - val actual = result.root + assertEquals(1, result.statements.size) + val actual = result.statements[0] assertEquals(expected, actual) } } diff --git a/partiql-planner/src/test/kotlin/org/partiql/planner/PlanTest.kt b/partiql-planner/src/test/kotlin/org/partiql/planner/PlanTest.kt index c51b4470b5..bc5aa6c9c8 100644 --- a/partiql-planner/src/test/kotlin/org/partiql/planner/PlanTest.kt +++ b/partiql-planner/src/test/kotlin/org/partiql/planner/PlanTest.kt @@ -5,7 +5,7 @@ import org.junit.jupiter.api.DynamicContainer.dynamicContainer import org.junit.jupiter.api.DynamicNode import org.junit.jupiter.api.DynamicTest import org.junit.jupiter.api.TestFactory -import org.partiql.parser.V1PartiQLParser +import org.partiql.parser.PartiQLParserV1 import org.partiql.plan.Plan import org.partiql.planner.internal.TestCatalog import org.partiql.planner.test.PartiQLTest @@ -21,6 +21,7 @@ import java.io.File import java.nio.file.Path import java.util.stream.Stream import kotlin.io.path.toPath +import kotlin.test.assertEquals /** * This class test asserts a normalized query produces the same plans as the original input query. @@ -75,8 +76,8 @@ class PlanTest { ) .namespace("SCHEMA") .build() - val parseResult = V1PartiQLParser.standard().parse(test.statement) - if (parseResult.statements.size != 1) error("expected single statement") + val parseResult = PartiQLParserV1.standard().parse(test.statement) + assertEquals(1, parseResult.statements.size) val ast = parseResult.statements[0] val planner = PartiQLPlanner.builder().signal(isSignalMode).build() planner.plan(ast, session) diff --git a/partiql-planner/src/test/kotlin/org/partiql/planner/PlannerPErrorReportingTests.kt b/partiql-planner/src/test/kotlin/org/partiql/planner/PlannerPErrorReportingTests.kt index 507b79639b..0cba397ffc 100644 --- a/partiql-planner/src/test/kotlin/org/partiql/planner/PlannerPErrorReportingTests.kt +++ b/partiql-planner/src/test/kotlin/org/partiql/planner/PlannerPErrorReportingTests.kt @@ -46,9 +46,7 @@ internal class PlannerPErrorReportingTests { private val statement: ((String) -> Statement) = { query -> val parseResult = parser.parse(query) - if (parseResult.statements.size != 1) { - error("Expected single statement.") - } + assertEquals(1, parseResult.statements.size) parseResult.statements[0] } diff --git a/partiql-planner/src/test/kotlin/org/partiql/planner/internal/exclude/SubsumptionTest.kt b/partiql-planner/src/test/kotlin/org/partiql/planner/internal/exclude/SubsumptionTest.kt index 3264045ca3..9267e1d7d2 100644 --- a/partiql-planner/src/test/kotlin/org/partiql/planner/internal/exclude/SubsumptionTest.kt +++ b/partiql-planner/src/test/kotlin/org/partiql/planner/internal/exclude/SubsumptionTest.kt @@ -39,9 +39,7 @@ class SubsumptionTest { private fun testExcludeExprSubsumption(tc: SubsumptionTC) { val text = "SELECT * EXCLUDE ${tc.excludeExprStr} FROM <<>> AS s, <<>> AS t;" val parseResult = parser.parse(text) - if (parseResult.statements.size != 1) { - throw IllegalStateException("Expected exactly one statement") - } + assertEquals(1, parseResult.statements.size) val statement = parseResult.statements[0] val session = Session.builder().catalog("default").catalogs(catalog).build() val plan = planner.plan(statement, session).plan diff --git a/partiql-planner/src/test/kotlin/org/partiql/planner/internal/typer/PartiQLTyperTestBase.kt b/partiql-planner/src/test/kotlin/org/partiql/planner/internal/typer/PartiQLTyperTestBase.kt index 7359689068..7cc28c6a62 100644 --- a/partiql-planner/src/test/kotlin/org/partiql/planner/internal/typer/PartiQLTyperTestBase.kt +++ b/partiql-planner/src/test/kotlin/org/partiql/planner/internal/typer/PartiQLTyperTestBase.kt @@ -20,6 +20,7 @@ import org.partiql.types.PType import org.partiql.types.PType.Kind import org.partiql.types.StaticType import java.util.stream.Stream +import kotlin.test.assertEquals abstract class PartiQLTyperTestBase { sealed class TestResult { @@ -53,9 +54,7 @@ abstract class PartiQLTyperTestBase { private val testingPipeline: ((String, String, Catalog, PErrorListener) -> PartiQLPlanner.Result) = { query, catalog, metadata, collector -> val parseResult = parser.parse(query) - if (parseResult.statements.size != 1) { - throw IllegalArgumentException("Only single statement is supported for testing") - } + assertEquals(1, parseResult.statements.size) val ast = parseResult.statements[0] val config = Context.of(collector) planner.plan(ast, session(catalog, metadata), config) diff --git a/partiql-planner/src/test/kotlin/org/partiql/planner/internal/typer/PlanTyperTestsPorted.kt b/partiql-planner/src/test/kotlin/org/partiql/planner/internal/typer/PlanTyperTestsPorted.kt index c03f20ed92..bf988e5b03 100644 --- a/partiql-planner/src/test/kotlin/org/partiql/planner/internal/typer/PlanTyperTestsPorted.kt +++ b/partiql-planner/src/test/kotlin/org/partiql/planner/internal/typer/PlanTyperTestsPorted.kt @@ -3851,9 +3851,7 @@ internal class PlanTyperTestsPorted { listener: PErrorListener, ): org.partiql.plan.Plan { val parseResult = parser.parse(query) - if (parseResult.statements.size != 1) { - error("Expected exactly one statement in query: $query") - } + assertEquals(1, parseResult.statements.size) val ast = parseResult.statements[0] val plannerConfig = Context.of(listener) return planner.plan(ast, session, plannerConfig).plan diff --git a/test/partiql-tests-runner/src/test/kotlin/org/partiql/runner/executor/EvalExecutor.kt b/test/partiql-tests-runner/src/test/kotlin/org/partiql/runner/executor/EvalExecutor.kt index 92c52bb460..bff86a3d5e 100644 --- a/test/partiql-tests-runner/src/test/kotlin/org/partiql/runner/executor/EvalExecutor.kt +++ b/test/partiql-tests-runner/src/test/kotlin/org/partiql/runner/executor/EvalExecutor.kt @@ -31,6 +31,7 @@ import org.partiql.value.PartiQLValue import org.partiql.value.PartiQLValueExperimental import org.partiql.value.io.PartiQLValueIonReaderBuilder import org.partiql.value.toIon +import kotlin.test.assertEquals /** * @property session @@ -46,9 +47,7 @@ class EvalExecutor( val listener = getErrorListener(mode) val ctx = Context.of(listener) val parseResult = parser.parse(input, ctx) - if (parseResult.statements.size != 1) { - error("Expected exactly one statement") - } + assertEquals(1, parseResult.statements.size) val ast = parseResult.statements[0] val plan = planner.plan(ast, session, ctx).plan return compiler.prepare(plan, mode, ctx) @@ -193,9 +192,7 @@ class EvalExecutor( .catalogs(catalog) .build() val parseResult = parser.parse("`$env`") - if (parseResult.statements.size != 1) { - error("Expected exactly one statement") - } + assertEquals(1, parseResult.statements.size) val stmt = parseResult.statements[0] val plan = planner.plan(stmt, session).plan return (plan.getOperation() as Query).getRex().getType().getPType() From 68a5e48adc2b614162a78dbf2b8bf1ea071087a9 Mon Sep 17 00:00:00 2001 From: John Ed Quinn Date: Wed, 30 Oct 2024 11:50:09 -0700 Subject: [PATCH 03/12] Adds tests for parsing multiple statements --- .../parser/internal/PartiQLParserDefault.kt | 7 +- .../parser/internal/PartiQLParserDefaultV1.kt | 7 +- .../org/partiql/parser/internal/PTestDef.kt | 13 ++++ .../parser/internal/ParserTestCaseSimple.kt | 31 ++++++++ .../partiql/parser/internal/SemiColonTests.kt | 73 +++++++++++++++++++ 5 files changed, 123 insertions(+), 8 deletions(-) create mode 100644 partiql-parser/src/test/kotlin/org/partiql/parser/internal/PTestDef.kt create mode 100644 partiql-parser/src/test/kotlin/org/partiql/parser/internal/ParserTestCaseSimple.kt create mode 100644 partiql-parser/src/test/kotlin/org/partiql/parser/internal/SemiColonTests.kt diff --git a/partiql-parser/src/main/kotlin/org/partiql/parser/internal/PartiQLParserDefault.kt b/partiql-parser/src/main/kotlin/org/partiql/parser/internal/PartiQLParserDefault.kt index 5c9bec3fe3..4e934fc47d 100644 --- a/partiql-parser/src/main/kotlin/org/partiql/parser/internal/PartiQLParserDefault.kt +++ b/partiql-parser/src/main/kotlin/org/partiql/parser/internal/PartiQLParserDefault.kt @@ -271,7 +271,7 @@ internal class PartiQLParserDefault : PartiQLParser { else -> throw IllegalArgumentException("Unsupported parser mode: $mode") } val tree = parser.file() - return Visitor.translate(source, tokens, tree) + return Visitor.translate(tokens, tree) } private fun createTokenStream(source: String, listener: PErrorListener): CountingTokenStream { @@ -392,13 +392,12 @@ internal class PartiQLParserDefault : PartiQLParser { * Expose an (internal) friendly entry point into the traversal; mostly for keeping mutable state contained. */ fun translate( - source: String, tokens: CountingTokenStream, - tree: GeneratedParser.FileContext, + tree: org.partiql.parser.internal.antlr.PartiQLParser.FileContext, ): PartiQLParser.Result { val locations = SourceLocations.Mutable() val visitor = Visitor(tokens, locations, tokens.parameterIndexes) - val root = visitor.visitAs(tree) as PFile + val root = visitor.visitFile(tree) return PartiQLParser.Result(root.statements, locations.toMap()) } diff --git a/partiql-parser/src/main/kotlin/org/partiql/parser/internal/PartiQLParserDefaultV1.kt b/partiql-parser/src/main/kotlin/org/partiql/parser/internal/PartiQLParserDefaultV1.kt index 6ecf154f3c..ca560de375 100644 --- a/partiql-parser/src/main/kotlin/org/partiql/parser/internal/PartiQLParserDefaultV1.kt +++ b/partiql-parser/src/main/kotlin/org/partiql/parser/internal/PartiQLParserDefaultV1.kt @@ -252,7 +252,7 @@ internal class PartiQLParserDefaultV1 : PartiQLParserV1 { else -> throw IllegalArgumentException("Unsupported parser mode: $mode") } val tree = parser.file() - return Visitor.translate(source, tokens, tree) + return Visitor.translate(tokens, tree) } private fun createTokenStream(source: String, listener: PErrorListener): CountingTokenStream { @@ -366,13 +366,12 @@ internal class PartiQLParserDefaultV1 : PartiQLParserV1 { * Expose an (internal) friendly entry point into the traversal; mostly for keeping mutable state contained. */ fun translate( - source: String, tokens: CountingTokenStream, - tree: GeneratedParser.FileContext, + tree: PartiQLParser.FileContext, ): PartiQLParserV1.Result { val locations = SourceLocations.Mutable() val visitor = Visitor(tokens, locations, tokens.parameterIndexes) - val root = visitor.visitAs(tree) as PFileV1 + val root = visitor.visitFile(tree) return PartiQLParserV1.Result( root.statements.toMutableList(), locations.toMap(), diff --git a/partiql-parser/src/test/kotlin/org/partiql/parser/internal/PTestDef.kt b/partiql-parser/src/test/kotlin/org/partiql/parser/internal/PTestDef.kt new file mode 100644 index 0000000000..c5438ded3c --- /dev/null +++ b/partiql-parser/src/test/kotlin/org/partiql/parser/internal/PTestDef.kt @@ -0,0 +1,13 @@ +package org.partiql.parser.internal + +interface PTestDef { + /** + * Returns the name of the test + */ + fun name(): String + + /** + * Runs the test case. + */ + fun assert(): Unit +} diff --git a/partiql-parser/src/test/kotlin/org/partiql/parser/internal/ParserTestCaseSimple.kt b/partiql-parser/src/test/kotlin/org/partiql/parser/internal/ParserTestCaseSimple.kt new file mode 100644 index 0000000000..a3e526948d --- /dev/null +++ b/partiql-parser/src/test/kotlin/org/partiql/parser/internal/ParserTestCaseSimple.kt @@ -0,0 +1,31 @@ +package org.partiql.parser.internal + +import org.partiql.parser.PartiQLParserV1 + +/** + * This test case simply cares about whether the [input] can be parsed or not. + */ +class ParserTestCaseSimple( + private val name: String, + private val input: String, + private val isValid: Boolean = true +) : PTestDef { + + override fun name(): String = name + + private val parser: PartiQLParserV1 = PartiQLParserV1.standard() + + override fun assert() { + when (isValid) { + true -> parser.parse(input) + false -> { + try { + parser.parse(input) + throw AssertionError("Expected parse failure for input: $input") + } catch (e: Exception) { + // Expected exception + } + } + } + } +} diff --git a/partiql-parser/src/test/kotlin/org/partiql/parser/internal/SemiColonTests.kt b/partiql-parser/src/test/kotlin/org/partiql/parser/internal/SemiColonTests.kt new file mode 100644 index 0000000000..309d2c2e6a --- /dev/null +++ b/partiql-parser/src/test/kotlin/org/partiql/parser/internal/SemiColonTests.kt @@ -0,0 +1,73 @@ +package org.partiql.parser.internal + +import org.junit.jupiter.api.Test + +class SemiColonTests { + @Test + fun t1() { + val tc = ParserTestCaseSimple( + "Valid multi-statement", + "1 + 1; 1 + 2; SELECT * FROM t; SELECT * FROM t2 AS t2;" + ) + tc.assert() + } + + @Test + fun t2() { + val tc = ParserTestCaseSimple( + "Valid multi-statement where last statement doesn't have a semi-colon", + "1 + 1; 1 + 2; SELECT * FROM t; SELECT * FROM t2 AS t2" + ) + tc.assert() + } + + @Test + fun t3() { + val tc = ParserTestCaseSimple( + "First statement has two semi-colons.", + "1 + 1;; 1 + 2; SELECT * FROM t; SELECT * FROM t2 AS t2;", + false + ) + tc.assert() + } + + @Test + fun t4() { + val tc = ParserTestCaseSimple( + "Trailing semi-colon", + "1 + 1; 1 + 2; SELECT * FROM t; SELECT * FROM t2 AS t2;;", + false + ) + tc.assert() + } + + @Test + fun t5() { + val tc = ParserTestCaseSimple( + "Preceding semi-colon", + ";1 + 1; 1 + 2; SELECT * FROM t; SELECT * FROM t2 AS t2;", + false + ) + tc.assert() + } + + @Test + fun t6() { + val tc = ParserTestCaseSimple( + "Empty statement with semi-colon", + ";", + false + ) + tc.assert() + } + + @Test + fun t7() { + val tc = ParserTestCaseSimple( + "Empty statement", + "", + false + ) + tc.assert() + } +} From 89883403a78c5429f6cb727e25f0f21b56d016c5 Mon Sep 17 00:00:00 2001 From: John Ed Quinn Date: Wed, 30 Oct 2024 13:27:35 -0700 Subject: [PATCH 04/12] Adds @NotNull annotations Adds type information for PR readability --- .../src/main/java/org/partiql/parser/PartiQLParser.java | 3 +++ .../java/org/partiql/parser/PartiQLParserBuilderV1.java | 2 ++ .../src/main/java/org/partiql/parser/PartiQLParserV1.java | 3 +++ .../org/partiql/parser/internal/PartiQLParserDefault.kt | 6 +++--- .../org/partiql/parser/internal/PartiQLParserDefaultV1.kt | 2 +- 5 files changed, 12 insertions(+), 4 deletions(-) diff --git a/partiql-parser/src/main/java/org/partiql/parser/PartiQLParser.java b/partiql-parser/src/main/java/org/partiql/parser/PartiQLParser.java index b2584633eb..80eee46bf7 100644 --- a/partiql-parser/src/main/java/org/partiql/parser/PartiQLParser.java +++ b/partiql-parser/src/main/java/org/partiql/parser/PartiQLParser.java @@ -43,6 +43,7 @@ public interface PartiQLParser { * @throws PErrorListenerException when the [org.partiql.spi.errors.PErrorListener] defined in the context throws an * [PErrorListenerException], this method halts execution and propagates the exception. */ + @NotNull default Result parse(@NotNull String source) throws PErrorListenerException { return parse(source, Context.standard()); } @@ -79,6 +80,7 @@ public Result(@NotNull List statements, @NotNull SourceLocations loca * TODO * @return TODO */ + @NotNull public static PartiQLParserBuilder builder() { return new PartiQLParserBuilder(); } @@ -87,6 +89,7 @@ public static PartiQLParserBuilder builder() { * TODO * @return TODO */ + @NotNull public static PartiQLParser standard() { return new PartiQLParserDefault(); } diff --git a/partiql-parser/src/main/java/org/partiql/parser/PartiQLParserBuilderV1.java b/partiql-parser/src/main/java/org/partiql/parser/PartiQLParserBuilderV1.java index b004891313..24b8e405cb 100644 --- a/partiql-parser/src/main/java/org/partiql/parser/PartiQLParserBuilderV1.java +++ b/partiql-parser/src/main/java/org/partiql/parser/PartiQLParserBuilderV1.java @@ -14,6 +14,7 @@ package org.partiql.parser; +import org.jetbrains.annotations.NotNull; import org.partiql.parser.internal.PartiQLParserDefaultV1; /** @@ -22,6 +23,7 @@ */ public class PartiQLParserBuilderV1 { + @NotNull public PartiQLParserV1 build() { return new PartiQLParserDefaultV1(); } diff --git a/partiql-parser/src/main/java/org/partiql/parser/PartiQLParserV1.java b/partiql-parser/src/main/java/org/partiql/parser/PartiQLParserV1.java index bd9516782d..6c0ca4700f 100644 --- a/partiql-parser/src/main/java/org/partiql/parser/PartiQLParserV1.java +++ b/partiql-parser/src/main/java/org/partiql/parser/PartiQLParserV1.java @@ -43,6 +43,7 @@ public interface PartiQLParserV1 { * @throws PErrorListenerException when the [org.partiql.spi.errors.PErrorListener] defined in the context throws an * [PErrorListenerException], this method halts execution and propagates the exception. */ + @NotNull default Result parse(@NotNull String source) throws PErrorListenerException { return parse(source, Context.standard()); } @@ -79,6 +80,7 @@ public Result(@NotNull List statements, @NotNull SourceLocations loca * TODO * @return TODO */ + @NotNull public static PartiQLParserBuilderV1 builder() { return new PartiQLParserBuilderV1(); } @@ -87,6 +89,7 @@ public static PartiQLParserBuilderV1 builder() { * TODO * @return TODO */ + @NotNull public static PartiQLParserV1 standard() { return new PartiQLParserDefaultV1(); } diff --git a/partiql-parser/src/main/kotlin/org/partiql/parser/internal/PartiQLParserDefault.kt b/partiql-parser/src/main/kotlin/org/partiql/parser/internal/PartiQLParserDefault.kt index 4e934fc47d..8072af5517 100644 --- a/partiql-parser/src/main/kotlin/org/partiql/parser/internal/PartiQLParserDefault.kt +++ b/partiql-parser/src/main/kotlin/org/partiql/parser/internal/PartiQLParserDefault.kt @@ -393,11 +393,11 @@ internal class PartiQLParserDefault : PartiQLParser { */ fun translate( tokens: CountingTokenStream, - tree: org.partiql.parser.internal.antlr.PartiQLParser.FileContext, + tree: GeneratedParser.FileContext, ): PartiQLParser.Result { val locations = SourceLocations.Mutable() val visitor = Visitor(tokens, locations, tokens.parameterIndexes) - val root = visitor.visitFile(tree) + val root: PFile = visitor.visitFile(tree) return PartiQLParser.Result(root.statements, locations.toMap()) } @@ -497,7 +497,7 @@ internal class PartiQLParserDefault : PartiQLParser { ) } - override fun visitFile(ctx: GeneratedParser.FileContext) = translate(ctx) { + override fun visitFile(ctx: GeneratedParser.FileContext): PFile = translate(ctx) { val stmts = visitOrEmpty(ctx.statement()) PFile(stmts) } diff --git a/partiql-parser/src/main/kotlin/org/partiql/parser/internal/PartiQLParserDefaultV1.kt b/partiql-parser/src/main/kotlin/org/partiql/parser/internal/PartiQLParserDefaultV1.kt index ca560de375..f8d67d2c70 100644 --- a/partiql-parser/src/main/kotlin/org/partiql/parser/internal/PartiQLParserDefaultV1.kt +++ b/partiql-parser/src/main/kotlin/org/partiql/parser/internal/PartiQLParserDefaultV1.kt @@ -371,7 +371,7 @@ internal class PartiQLParserDefaultV1 : PartiQLParserV1 { ): PartiQLParserV1.Result { val locations = SourceLocations.Mutable() val visitor = Visitor(tokens, locations, tokens.parameterIndexes) - val root = visitor.visitFile(tree) + val root: PFileV1 = visitor.visitFile(tree) return PartiQLParserV1.Result( root.statements.toMutableList(), locations.toMap(), From 3e7b965ade71db42eb787378c8f72f4f07cb2be5 Mon Sep 17 00:00:00 2001 From: John Ed Quinn Date: Wed, 30 Oct 2024 13:50:31 -0700 Subject: [PATCH 05/12] Migrates SourceLocations to SPI and removes the Mutable class Adds Javadocs to SourceLocations --- partiql-parser/api/partiql-parser.api | 32 ++-------- .../org/partiql/parser/PartiQLParser.java | 1 + .../org/partiql/parser/PartiQLParserV1.java | 1 + .../parser/internal/PartiQLParserDefault.kt | 10 ++-- .../parser/internal/PartiQLParserDefaultV1.kt | 10 ++-- partiql-spi/api/partiql-spi.api | 20 +++++++ .../org/partiql/spi}/SourceLocations.java | 60 ++++++++----------- 7 files changed, 60 insertions(+), 74 deletions(-) rename {partiql-parser/src/main/java/org/partiql/parser => partiql-spi/src/main/java/org/partiql/spi}/SourceLocations.java (59%) diff --git a/partiql-parser/api/partiql-parser.api b/partiql-parser/api/partiql-parser.api index 84b8df262d..bd84b15a30 100644 --- a/partiql-parser/api/partiql-parser.api +++ b/partiql-parser/api/partiql-parser.api @@ -6,9 +6,9 @@ public abstract interface class org/partiql/parser/PartiQLParser { } public final class org/partiql/parser/PartiQLParser$Result { - public field locations Lorg/partiql/parser/SourceLocations; + public field locations Lorg/partiql/spi/SourceLocations; public field statements Ljava/util/List; - public fun (Ljava/util/List;Lorg/partiql/parser/SourceLocations;)V + public fun (Ljava/util/List;Lorg/partiql/spi/SourceLocations;)V } public class org/partiql/parser/PartiQLParserBuilder { @@ -29,32 +29,8 @@ public abstract interface class org/partiql/parser/PartiQLParserV1 { } public final class org/partiql/parser/PartiQLParserV1$Result { - public field locations Lorg/partiql/parser/SourceLocations; + public field locations Lorg/partiql/spi/SourceLocations; public field statements Ljava/util/List; - public fun (Ljava/util/List;Lorg/partiql/parser/SourceLocations;)V -} - -public class org/partiql/parser/SourceLocations : java/util/Map { - public fun clear ()V - public fun containsKey (Ljava/lang/Object;)Z - public fun containsValue (Ljava/lang/Object;)Z - public fun entrySet ()Ljava/util/Set; - public synthetic fun get (Ljava/lang/Object;)Ljava/lang/Object; - public fun get (Ljava/lang/Object;)Lorg/partiql/spi/SourceLocation; - public fun isEmpty ()Z - public fun keySet ()Ljava/util/Set; - public synthetic fun put (Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object; - public fun put (Ljava/lang/String;Lorg/partiql/spi/SourceLocation;)Lorg/partiql/spi/SourceLocation; - public fun putAll (Ljava/util/Map;)V - public synthetic fun remove (Ljava/lang/Object;)Ljava/lang/Object; - public fun remove (Ljava/lang/Object;)Lorg/partiql/spi/SourceLocation; - public fun size ()I - public fun values ()Ljava/util/Collection; -} - -public class org/partiql/parser/SourceLocations$Mutable { - public fun ()V - public fun set (Ljava/lang/String;Lorg/partiql/spi/SourceLocation;)V - public fun toMap ()Lorg/partiql/parser/SourceLocations; + public fun (Ljava/util/List;Lorg/partiql/spi/SourceLocations;)V } diff --git a/partiql-parser/src/main/java/org/partiql/parser/PartiQLParser.java b/partiql-parser/src/main/java/org/partiql/parser/PartiQLParser.java index 80eee46bf7..ef85820992 100644 --- a/partiql-parser/src/main/java/org/partiql/parser/PartiQLParser.java +++ b/partiql-parser/src/main/java/org/partiql/parser/PartiQLParser.java @@ -18,6 +18,7 @@ import org.partiql.ast.Statement; import org.partiql.parser.internal.PartiQLParserDefault; import org.partiql.spi.Context; +import org.partiql.spi.SourceLocations; import org.partiql.spi.errors.PErrorListenerException; import java.util.List; diff --git a/partiql-parser/src/main/java/org/partiql/parser/PartiQLParserV1.java b/partiql-parser/src/main/java/org/partiql/parser/PartiQLParserV1.java index 6c0ca4700f..d16acc2677 100644 --- a/partiql-parser/src/main/java/org/partiql/parser/PartiQLParserV1.java +++ b/partiql-parser/src/main/java/org/partiql/parser/PartiQLParserV1.java @@ -18,6 +18,7 @@ import org.partiql.ast.v1.Statement; import org.partiql.parser.internal.PartiQLParserDefaultV1; import org.partiql.spi.Context; +import org.partiql.spi.SourceLocations; import org.partiql.spi.errors.PErrorListenerException; import java.util.List; diff --git a/partiql-parser/src/main/kotlin/org/partiql/parser/internal/PartiQLParserDefault.kt b/partiql-parser/src/main/kotlin/org/partiql/parser/internal/PartiQLParserDefault.kt index 8072af5517..7fe7de28f2 100644 --- a/partiql-parser/src/main/kotlin/org/partiql/parser/internal/PartiQLParserDefault.kt +++ b/partiql-parser/src/main/kotlin/org/partiql/parser/internal/PartiQLParserDefault.kt @@ -174,11 +174,11 @@ import org.partiql.ast.typeVarchar import org.partiql.parser.PartiQLLexerException import org.partiql.parser.PartiQLParser import org.partiql.parser.PartiQLParserException -import org.partiql.parser.SourceLocations import org.partiql.parser.internal.antlr.PartiQLParserBaseVisitor import org.partiql.parser.internal.util.DateTimeUtils import org.partiql.spi.Context import org.partiql.spi.SourceLocation +import org.partiql.spi.SourceLocations import org.partiql.spi.errors.PError import org.partiql.spi.errors.PErrorKind import org.partiql.spi.errors.PErrorListener @@ -239,7 +239,7 @@ internal class PartiQLParserDefault : PartiQLParser { } catch (throwable: Throwable) { val error = PError.INTERNAL_ERROR(PErrorKind.SYNTAX(), null, throwable) ctx.errorListener.report(error) - val locations = SourceLocations.Mutable().toMap() + val locations = SourceLocations() return PartiQLParser.Result(listOf(Statement.Query(Expr.Lit(nullValue()))), locations) } } @@ -380,7 +380,7 @@ internal class PartiQLParserDefault : PartiQLParser { @OptIn(PartiQLValueExperimental::class) private class Visitor( private val tokens: CommonTokenStream, - private val locations: SourceLocations.Mutable, + private val locations: MutableMap, private val parameters: Map = mapOf(), ) : PartiQLParserBaseVisitor() { @@ -395,10 +395,10 @@ internal class PartiQLParserDefault : PartiQLParser { tokens: CountingTokenStream, tree: GeneratedParser.FileContext, ): PartiQLParser.Result { - val locations = SourceLocations.Mutable() + val locations = mutableMapOf() val visitor = Visitor(tokens, locations, tokens.parameterIndexes) val root: PFile = visitor.visitFile(tree) - return PartiQLParser.Result(root.statements, locations.toMap()) + return PartiQLParser.Result(root.statements, SourceLocations(locations)) } fun error( diff --git a/partiql-parser/src/main/kotlin/org/partiql/parser/internal/PartiQLParserDefaultV1.kt b/partiql-parser/src/main/kotlin/org/partiql/parser/internal/PartiQLParserDefaultV1.kt index f8d67d2c70..6914391b20 100644 --- a/partiql-parser/src/main/kotlin/org/partiql/parser/internal/PartiQLParserDefaultV1.kt +++ b/partiql-parser/src/main/kotlin/org/partiql/parser/internal/PartiQLParserDefaultV1.kt @@ -155,12 +155,12 @@ import org.partiql.ast.v1.graph.GraphSelector import org.partiql.parser.PartiQLLexerException import org.partiql.parser.PartiQLParserException import org.partiql.parser.PartiQLParserV1 -import org.partiql.parser.SourceLocations import org.partiql.parser.internal.antlr.PartiQLParser import org.partiql.parser.internal.antlr.PartiQLParserBaseVisitor import org.partiql.parser.internal.util.DateTimeUtils import org.partiql.spi.Context import org.partiql.spi.SourceLocation +import org.partiql.spi.SourceLocations import org.partiql.spi.errors.PError import org.partiql.spi.errors.PErrorKind import org.partiql.spi.errors.PErrorListener @@ -219,7 +219,7 @@ internal class PartiQLParserDefaultV1 : PartiQLParserV1 { } catch (throwable: Throwable) { val error = PError.INTERNAL_ERROR(PErrorKind.SYNTAX(), null, throwable) ctx.errorListener.report(error) - val locations = SourceLocations.Mutable().toMap() + val locations = SourceLocations() return PartiQLParserV1.Result( mutableListOf(org.partiql.ast.v1.Query(org.partiql.ast.v1.expr.ExprLit(nullValue()))) as List, locations @@ -354,7 +354,7 @@ internal class PartiQLParserDefaultV1 : PartiQLParserV1 { @OptIn(PartiQLValueExperimental::class) private class Visitor( private val tokens: CommonTokenStream, - private val locations: SourceLocations.Mutable, + private val locations: MutableMap, private val parameters: Map = mapOf(), ) : PartiQLParserBaseVisitor() { @@ -369,12 +369,12 @@ internal class PartiQLParserDefaultV1 : PartiQLParserV1 { tokens: CountingTokenStream, tree: PartiQLParser.FileContext, ): PartiQLParserV1.Result { - val locations = SourceLocations.Mutable() + val locations = mutableMapOf() val visitor = Visitor(tokens, locations, tokens.parameterIndexes) val root: PFileV1 = visitor.visitFile(tree) return PartiQLParserV1.Result( root.statements.toMutableList(), - locations.toMap(), + SourceLocations(locations), ) } diff --git a/partiql-spi/api/partiql-spi.api b/partiql-spi/api/partiql-spi.api index fa115613f9..4b0f7aa022 100644 --- a/partiql-spi/api/partiql-spi.api +++ b/partiql-spi/api/partiql-spi.api @@ -47,6 +47,26 @@ public class org/partiql/spi/SourceLocation { public fun hashCode ()I } +public class org/partiql/spi/SourceLocations : java/util/Map { + public fun ()V + public fun (Ljava/util/Map;)V + public fun clear ()V + public fun containsKey (Ljava/lang/Object;)Z + public fun containsValue (Ljava/lang/Object;)Z + public fun entrySet ()Ljava/util/Set; + public synthetic fun get (Ljava/lang/Object;)Ljava/lang/Object; + public fun get (Ljava/lang/Object;)Lorg/partiql/spi/SourceLocation; + public fun isEmpty ()Z + public fun keySet ()Ljava/util/Set; + public synthetic fun put (Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object; + public fun put (Ljava/lang/String;Lorg/partiql/spi/SourceLocation;)Lorg/partiql/spi/SourceLocation; + public fun putAll (Ljava/util/Map;)V + public synthetic fun remove (Ljava/lang/Object;)Ljava/lang/Object; + public fun remove (Ljava/lang/Object;)Lorg/partiql/spi/SourceLocation; + public fun size ()I + public fun values ()Ljava/util/Collection; +} + public abstract interface class org/partiql/spi/catalog/Catalog { public static final field Companion Lorg/partiql/spi/catalog/Catalog$Companion; public static fun builder ()Lorg/partiql/spi/catalog/Catalog$Builder; diff --git a/partiql-parser/src/main/java/org/partiql/parser/SourceLocations.java b/partiql-spi/src/main/java/org/partiql/spi/SourceLocations.java similarity index 59% rename from partiql-parser/src/main/java/org/partiql/parser/SourceLocations.java rename to partiql-spi/src/main/java/org/partiql/spi/SourceLocations.java index 8aaaba6b08..e70820cd8b 100644 --- a/partiql-parser/src/main/java/org/partiql/parser/SourceLocations.java +++ b/partiql-spi/src/main/java/org/partiql/spi/SourceLocations.java @@ -1,22 +1,37 @@ -package org.partiql.parser; +package org.partiql.spi; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import org.partiql.spi.SourceLocation; import java.util.Collection; import java.util.Map; import java.util.Set; /** - * TODO + * This class maps a set of identifiers to their corresponding source locations. + *
+ * Note!: This class is immutable and does not support {@link Map#put(Object, Object)}, amongst others. Please + * handle the runtime exceptions indicated by {@link Map}'s Javadocs. */ public class SourceLocations implements Map { private final Map delegate; - private SourceLocations(Map delegate) { - this.delegate = delegate; + /** + * Creates an empty instance. + */ + public SourceLocations() { + this.delegate = new java.util.HashMap<>(); + } + + /** + * Creates an instance holding the contents of {@code delegate}. To enforce immutability, these contents are copied + * to an internal structure. + * @param delegate the delegate holding the locations. + */ + public SourceLocations(Map delegate) { + this.delegate = new java.util.HashMap<>(); + this.delegate.putAll(delegate); } @NotNull @@ -60,54 +75,27 @@ public SourceLocation get(Object key) { @Nullable @Override public SourceLocation put(String key, SourceLocation value) { - // TODO: This isn't allowed (yet?) - return null; + throw new UnsupportedOperationException(); } @Override public SourceLocation remove(Object key) { - // TODO: This isn't allowed (yet?) - return null; + throw new UnsupportedOperationException(); } @Override public void putAll(@NotNull Map m) { - // TODO: This isn't allowed (yet?) + throw new UnsupportedOperationException(); } @Override public void clear() { - // TODO: This isn't allowed (yet?) + throw new UnsupportedOperationException(); } @Override public boolean isEmpty() { return delegate.isEmpty(); } - - /** - * TODO - */ - public static class Mutable { - - private final Map delegate = new java.util.HashMap<>(); - - /** - * TODO - * @param id TODO - * @param value TODO - */ - public void set(String id, SourceLocation value) { - delegate.put(id, value); - } - - /** - * TODO - * @return TODO - */ - public SourceLocations toMap() { - return new SourceLocations(delegate); - } - } } From 147419fd87c93016c1ac41b03f12a59d42c88c84 Mon Sep 17 00:00:00 2001 From: John Ed Quinn Date: Fri, 1 Nov 2024 10:40:39 -0700 Subject: [PATCH 06/12] Fixes rebase issues --- .../kotlin/org/partiql/cli/pipeline/Pipeline.kt | 8 ++++---- .../partiql/eval/internal/PartiQLEvaluatorTest.kt | 13 ++++--------- .../partiql/planner/PlannerPErrorReportingTests.kt | 4 ++-- .../planner/internal/exclude/SubsumptionTest.kt | 4 ++-- .../planner/internal/typer/PartiQLTyperTestBase.kt | 4 ++-- .../planner/internal/typer/PlanTyperTestsPorted.kt | 4 ++-- .../org/partiql/lang/randomized/eval/Utils.kt | 4 ++-- .../org/partiql/runner/executor/EvalExecutor.kt | 4 ++-- 8 files changed, 20 insertions(+), 25 deletions(-) diff --git a/partiql-cli/src/main/kotlin/org/partiql/cli/pipeline/Pipeline.kt b/partiql-cli/src/main/kotlin/org/partiql/cli/pipeline/Pipeline.kt index c6eef0b6b3..59b7ff4ba5 100644 --- a/partiql-cli/src/main/kotlin/org/partiql/cli/pipeline/Pipeline.kt +++ b/partiql-cli/src/main/kotlin/org/partiql/cli/pipeline/Pipeline.kt @@ -4,8 +4,8 @@ import org.partiql.ast.v1.Statement import org.partiql.cli.ErrorCodeString import org.partiql.eval.Mode import org.partiql.eval.compiler.PartiQLCompiler -import org.partiql.parser.V1PartiQLParser -import org.partiql.parser.V1PartiQLParserBuilder +import org.partiql.parser.PartiQLParserBuilderV1 +import org.partiql.parser.PartiQLParserV1 import org.partiql.plan.Plan import org.partiql.planner.PartiQLPlanner import org.partiql.spi.Context @@ -15,7 +15,7 @@ import org.partiql.spi.value.Datum import java.io.PrintStream internal class Pipeline private constructor( - private val parser: V1PartiQLParser, + private val parser: PartiQLParserV1, private val planner: PartiQLPlanner, private val compiler: PartiQLCompiler, private val ctx: Context, @@ -83,7 +83,7 @@ internal class Pipeline private constructor( private fun create(mode: Mode, out: PrintStream, config: Config): Pipeline { val listener = config.getErrorListener(out) val ctx = Context.of(listener) - val parser = V1PartiQLParserBuilder().build() + val parser = PartiQLParserBuilderV1().build() val planner = PartiQLPlanner.builder().build() val compiler = PartiQLCompiler.builder().build() return Pipeline(parser, planner, compiler, ctx, mode) diff --git a/partiql-eval/src/test/kotlin/org/partiql/eval/internal/PartiQLEvaluatorTest.kt b/partiql-eval/src/test/kotlin/org/partiql/eval/internal/PartiQLEvaluatorTest.kt index 9f1fad3486..cedeaa173f 100644 --- a/partiql-eval/src/test/kotlin/org/partiql/eval/internal/PartiQLEvaluatorTest.kt +++ b/partiql-eval/src/test/kotlin/org/partiql/eval/internal/PartiQLEvaluatorTest.kt @@ -9,7 +9,7 @@ import org.junit.jupiter.params.ParameterizedTest import org.junit.jupiter.params.provider.MethodSource import org.partiql.eval.Mode import org.partiql.eval.compiler.PartiQLCompiler -import org.partiql.parser.V1PartiQLParser +import org.partiql.parser.PartiQLParserV1 import org.partiql.plan.Plan import org.partiql.planner.PartiQLPlanner import org.partiql.spi.catalog.Catalog @@ -1308,7 +1308,7 @@ class PartiQLEvaluatorTest { ) { private val compiler = PartiQLCompiler.standard() - private val parser = V1PartiQLParser.standard() + private val parser = PartiQLParserV1.standard() private val planner = PartiQLPlanner.standard() /** @@ -1376,7 +1376,7 @@ class PartiQLEvaluatorTest { ) { private val compiler = PartiQLCompiler.standard() - private val parser = V1PartiQLParser.standard() + private val parser = PartiQLParserV1.standard() private val planner = PartiQLPlanner.standard() internal fun assert() { @@ -1409,15 +1409,10 @@ class PartiQLEvaluatorTest { } private fun run(mode: Mode): Pair { -<<<<<<< HEAD - val statement = parser.parse(input).root - val catalog = Catalog.builder().name("memory").build() -======= val parseResult = parser.parse(input) assertEquals(1, parseResult.statements.size) val statement = parseResult.statements[0] - val catalog = MemoryCatalog.builder().name("memory").build() ->>>>>>> 1c662c3be (Migrates parser APIs to Java) + val catalog = Catalog.builder().name("memory").build() val session = Session.builder() .catalog("memory") .catalogs(catalog) diff --git a/partiql-planner/src/test/kotlin/org/partiql/planner/PlannerPErrorReportingTests.kt b/partiql-planner/src/test/kotlin/org/partiql/planner/PlannerPErrorReportingTests.kt index 0cba397ffc..ba609f886b 100644 --- a/partiql-planner/src/test/kotlin/org/partiql/planner/PlannerPErrorReportingTests.kt +++ b/partiql-planner/src/test/kotlin/org/partiql/planner/PlannerPErrorReportingTests.kt @@ -3,7 +3,7 @@ package org.partiql.planner import org.junit.jupiter.params.ParameterizedTest import org.junit.jupiter.params.provider.MethodSource import org.partiql.ast.v1.Statement -import org.partiql.parser.V1PartiQLParserBuilder +import org.partiql.parser.PartiQLParserBuilderV1 import org.partiql.plan.Operation import org.partiql.planner.internal.typer.CompilerType import org.partiql.planner.internal.typer.PlanTyper.Companion.toCType @@ -42,7 +42,7 @@ internal class PlannerPErrorReportingTests { .catalogs(catalog) .build() - private val parser = V1PartiQLParserBuilder().build() + private val parser = PartiQLParserBuilderV1().build() private val statement: ((String) -> Statement) = { query -> val parseResult = parser.parse(query) diff --git a/partiql-planner/src/test/kotlin/org/partiql/planner/internal/exclude/SubsumptionTest.kt b/partiql-planner/src/test/kotlin/org/partiql/planner/internal/exclude/SubsumptionTest.kt index 9267e1d7d2..c56a1852aa 100644 --- a/partiql-planner/src/test/kotlin/org/partiql/planner/internal/exclude/SubsumptionTest.kt +++ b/partiql-planner/src/test/kotlin/org/partiql/planner/internal/exclude/SubsumptionTest.kt @@ -7,7 +7,7 @@ import org.junit.jupiter.params.ParameterizedTest import org.junit.jupiter.params.provider.Arguments import org.junit.jupiter.params.provider.ArgumentsProvider import org.junit.jupiter.params.provider.ArgumentsSource -import org.partiql.parser.V1PartiQLParser +import org.partiql.parser.PartiQLParserV1 import org.partiql.plan.Exclusion import org.partiql.plan.Operation import org.partiql.plan.builder.PlanFactory @@ -26,7 +26,7 @@ class SubsumptionTest { companion object { private val planner = PartiQLPlanner.standard() - private val parser = V1PartiQLParser.standard() + private val parser = PartiQLParserV1.standard() private val catalog = Catalog.builder().name("default").build() } diff --git a/partiql-planner/src/test/kotlin/org/partiql/planner/internal/typer/PartiQLTyperTestBase.kt b/partiql-planner/src/test/kotlin/org/partiql/planner/internal/typer/PartiQLTyperTestBase.kt index 7cc28c6a62..33d5c71a80 100644 --- a/partiql-planner/src/test/kotlin/org/partiql/planner/internal/typer/PartiQLTyperTestBase.kt +++ b/partiql-planner/src/test/kotlin/org/partiql/planner/internal/typer/PartiQLTyperTestBase.kt @@ -2,7 +2,7 @@ package org.partiql.planner.internal.typer import org.junit.jupiter.api.DynamicContainer import org.junit.jupiter.api.DynamicTest -import org.partiql.parser.V1PartiQLParser +import org.partiql.parser.PartiQLParserV1 import org.partiql.plan.Operation import org.partiql.planner.PartiQLPlanner import org.partiql.planner.test.PartiQLTest @@ -38,7 +38,7 @@ abstract class PartiQLTyperTestBase { companion object { - public val parser = V1PartiQLParser.standard() + public val parser = PartiQLParserV1.standard() public val planner = PartiQLPlanner.standard() internal val session: ((String, Catalog) -> Session) = { catalog, metadata -> diff --git a/partiql-planner/src/test/kotlin/org/partiql/planner/internal/typer/PlanTyperTestsPorted.kt b/partiql-planner/src/test/kotlin/org/partiql/planner/internal/typer/PlanTyperTestsPorted.kt index bf988e5b03..123a57dc71 100644 --- a/partiql-planner/src/test/kotlin/org/partiql/planner/internal/typer/PlanTyperTestsPorted.kt +++ b/partiql-planner/src/test/kotlin/org/partiql/planner/internal/typer/PlanTyperTestsPorted.kt @@ -12,7 +12,7 @@ import org.junit.jupiter.params.provider.Arguments import org.junit.jupiter.params.provider.ArgumentsProvider import org.junit.jupiter.params.provider.ArgumentsSource import org.junit.jupiter.params.provider.MethodSource -import org.partiql.parser.V1PartiQLParser +import org.partiql.parser.PartiQLParserV1 import org.partiql.planner.PartiQLPlanner import org.partiql.planner.internal.PErrors import org.partiql.planner.internal.TestCatalog @@ -125,7 +125,7 @@ internal class PlanTyperTestsPorted { companion object { - private val parser = V1PartiQLParser.standard() + private val parser = PartiQLParserV1.standard() private val planner = PartiQLPlanner.builder().signal().build() private fun assertProblemExists(problem: PError) = ProblemHandler { problems, _ -> diff --git a/test/partiql-randomized-tests/src/test/kotlin/org/partiql/lang/randomized/eval/Utils.kt b/test/partiql-randomized-tests/src/test/kotlin/org/partiql/lang/randomized/eval/Utils.kt index bd9f251e1c..8fa1dd3db3 100644 --- a/test/partiql-randomized-tests/src/test/kotlin/org/partiql/lang/randomized/eval/Utils.kt +++ b/test/partiql-randomized-tests/src/test/kotlin/org/partiql/lang/randomized/eval/Utils.kt @@ -1,7 +1,7 @@ package org.partiql.lang.randomized.eval import org.partiql.eval.compiler.PartiQLCompiler -import org.partiql.parser.V1PartiQLParser +import org.partiql.parser.PartiQLParserV1 import org.partiql.planner.PartiQLPlanner import org.partiql.spi.catalog.Catalog import org.partiql.spi.catalog.Session @@ -22,7 +22,7 @@ fun runEvaluatorTestCase( @OptIn(PartiQLValueExperimental::class) private fun execute(query: String): PartiQLValue { - val parser = V1PartiQLParser.builder().build() + val parser = PartiQLParserV1.builder().build() val planner = PartiQLPlanner.builder().build() val catalog = object : Catalog { override fun getName(): String = "default" diff --git a/test/partiql-tests-runner/src/test/kotlin/org/partiql/runner/executor/EvalExecutor.kt b/test/partiql-tests-runner/src/test/kotlin/org/partiql/runner/executor/EvalExecutor.kt index bff86a3d5e..63aabb3b27 100644 --- a/test/partiql-tests-runner/src/test/kotlin/org/partiql/runner/executor/EvalExecutor.kt +++ b/test/partiql-tests-runner/src/test/kotlin/org/partiql/runner/executor/EvalExecutor.kt @@ -10,7 +10,7 @@ import com.amazon.ionelement.api.toIonValue import org.partiql.eval.Mode import org.partiql.eval.Statement import org.partiql.eval.compiler.PartiQLCompiler -import org.partiql.parser.V1PartiQLParser +import org.partiql.parser.PartiQLParserV1 import org.partiql.plan.Operation.Query import org.partiql.planner.PartiQLPlanner import org.partiql.runner.CompileType @@ -143,7 +143,7 @@ class EvalExecutor( companion object { val compiler = PartiQLCompiler.standard() - val parser = V1PartiQLParser.standard() + val parser = PartiQLParserV1.standard() val planner = PartiQLPlanner.standard() // TODO REPLACE WITH DATUM COMPARATOR val comparator = PartiQLValue.comparator() From b40e03e4e1397f774ba02efc8e52831d28f5eacf Mon Sep 17 00:00:00 2001 From: John Ed Quinn Date: Fri, 1 Nov 2024 10:47:00 -0700 Subject: [PATCH 07/12] Updates naming of ANTLR root to statements Removes use of PFile and PFileV1 --- .../org/partiql/cli/shell/ShellHighlighter.kt | 2 +- partiql-parser/src/main/antlr/PartiQLParser.g4 | 2 +- .../kotlin/org/partiql/parser/internal/PFile.kt | 16 ---------------- .../org/partiql/parser/internal/PFileV1.kt | 17 ----------------- .../parser/internal/PartiQLParserDefault.kt | 15 ++++++--------- .../parser/internal/PartiQLParserDefaultV1.kt | 15 ++++++--------- 6 files changed, 14 insertions(+), 53 deletions(-) delete mode 100644 partiql-parser/src/main/kotlin/org/partiql/parser/internal/PFile.kt delete mode 100644 partiql-parser/src/main/kotlin/org/partiql/parser/internal/PFileV1.kt diff --git a/partiql-cli/src/main/kotlin/org/partiql/cli/shell/ShellHighlighter.kt b/partiql-cli/src/main/kotlin/org/partiql/cli/shell/ShellHighlighter.kt index 058d66220f..e89c39bed1 100644 --- a/partiql-cli/src/main/kotlin/org/partiql/cli/shell/ShellHighlighter.kt +++ b/partiql-cli/src/main/kotlin/org/partiql/cli/shell/ShellHighlighter.kt @@ -89,7 +89,7 @@ internal object ShellHighlighter : Highlighter { // Parse and Replace Token Style if Failures try { - parser.file() + parser.statements() } catch (e: RethrowErrorListener.OffendingSymbolException) { val offending = e.offendingSymbol val prefix = builder.substring(0, offending.startIndex) diff --git a/partiql-parser/src/main/antlr/PartiQLParser.g4 b/partiql-parser/src/main/antlr/PartiQLParser.g4 index b32a2d20c0..dafc1fd006 100644 --- a/partiql-parser/src/main/antlr/PartiQLParser.g4 +++ b/partiql-parser/src/main/antlr/PartiQLParser.g4 @@ -11,7 +11,7 @@ options { * */ -file +statements : statement (COLON_SEMI statement)* COLON_SEMI? EOF ; diff --git a/partiql-parser/src/main/kotlin/org/partiql/parser/internal/PFile.kt b/partiql-parser/src/main/kotlin/org/partiql/parser/internal/PFile.kt deleted file mode 100644 index db8942d478..0000000000 --- a/partiql-parser/src/main/kotlin/org/partiql/parser/internal/PFile.kt +++ /dev/null @@ -1,16 +0,0 @@ -package org.partiql.parser.internal - -import org.partiql.ast.AstNode -import org.partiql.ast.Statement -import org.partiql.ast.visitor.AstVisitor - -internal class PFile( - internal val statements: List, -) : AstNode() { - - override val children: List = this.statements - - override fun accept(visitor: AstVisitor, ctx: C): R { - error("This is an internal ast node and cannot be passed to a visitor.") - } -} diff --git a/partiql-parser/src/main/kotlin/org/partiql/parser/internal/PFileV1.kt b/partiql-parser/src/main/kotlin/org/partiql/parser/internal/PFileV1.kt deleted file mode 100644 index ff59342d1b..0000000000 --- a/partiql-parser/src/main/kotlin/org/partiql/parser/internal/PFileV1.kt +++ /dev/null @@ -1,17 +0,0 @@ -package org.partiql.parser.internal - -import org.partiql.ast.v1.AstNode -import org.partiql.ast.v1.AstVisitor -import org.partiql.ast.v1.Statement - -internal class PFileV1( - internal val statements: List, -) : AstNode() { - override fun children(): MutableCollection { - return statements.toMutableList() - } - - override fun accept(visitor: AstVisitor, ctx: C): R { - error("This is an internal ast node and cannot be passed to a visitor.") - } -} diff --git a/partiql-parser/src/main/kotlin/org/partiql/parser/internal/PartiQLParserDefault.kt b/partiql-parser/src/main/kotlin/org/partiql/parser/internal/PartiQLParserDefault.kt index 7fe7de28f2..04cdcf5f79 100644 --- a/partiql-parser/src/main/kotlin/org/partiql/parser/internal/PartiQLParserDefault.kt +++ b/partiql-parser/src/main/kotlin/org/partiql/parser/internal/PartiQLParserDefault.kt @@ -270,7 +270,7 @@ internal class PartiQLParserDefault : PartiQLParser { PredictionMode.LL -> parser.addErrorListener(ParseErrorListener(listener)) else -> throw IllegalArgumentException("Unsupported parser mode: $mode") } - val tree = parser.file() + val tree = parser.statements() return Visitor.translate(tokens, tree) } @@ -393,12 +393,14 @@ internal class PartiQLParserDefault : PartiQLParser { */ fun translate( tokens: CountingTokenStream, - tree: GeneratedParser.FileContext, + tree: GeneratedParser.StatementsContext, ): PartiQLParser.Result { val locations = mutableMapOf() val visitor = Visitor(tokens, locations, tokens.parameterIndexes) - val root: PFile = visitor.visitFile(tree) - return PartiQLParser.Result(root.statements, SourceLocations(locations)) + val statements = tree.statement().map { statementCtx -> + visitor.visit(statementCtx) as Statement + } + return PartiQLParser.Result(statements, SourceLocations(locations)) } fun error( @@ -497,11 +499,6 @@ internal class PartiQLParserDefault : PartiQLParser { ) } - override fun visitFile(ctx: GeneratedParser.FileContext): PFile = translate(ctx) { - val stmts = visitOrEmpty(ctx.statement()) - PFile(stmts) - } - /** * * COMMON USAGES diff --git a/partiql-parser/src/main/kotlin/org/partiql/parser/internal/PartiQLParserDefaultV1.kt b/partiql-parser/src/main/kotlin/org/partiql/parser/internal/PartiQLParserDefaultV1.kt index 6914391b20..e861073bc1 100644 --- a/partiql-parser/src/main/kotlin/org/partiql/parser/internal/PartiQLParserDefaultV1.kt +++ b/partiql-parser/src/main/kotlin/org/partiql/parser/internal/PartiQLParserDefaultV1.kt @@ -251,7 +251,7 @@ internal class PartiQLParserDefaultV1 : PartiQLParserV1 { PredictionMode.LL -> parser.addErrorListener(ParseErrorListener(listener)) else -> throw IllegalArgumentException("Unsupported parser mode: $mode") } - val tree = parser.file() + val tree = parser.statements() return Visitor.translate(tokens, tree) } @@ -367,13 +367,15 @@ internal class PartiQLParserDefaultV1 : PartiQLParserV1 { */ fun translate( tokens: CountingTokenStream, - tree: PartiQLParser.FileContext, + tree: PartiQLParser.StatementsContext, ): PartiQLParserV1.Result { val locations = mutableMapOf() val visitor = Visitor(tokens, locations, tokens.parameterIndexes) - val root: PFileV1 = visitor.visitFile(tree) + val statements = tree.statement().map { statementCtx -> + visitor.visit(statementCtx) as Statement + } return PartiQLParserV1.Result( - root.statements.toMutableList(), + statements, SourceLocations(locations), ) } @@ -471,11 +473,6 @@ internal class PartiQLParserDefaultV1 : PartiQLParserV1 { ) } - override fun visitFile(ctx: GeneratedParser.FileContext): PFileV1 = translate(ctx) { - val stmts = visitOrEmpty(ctx.statement()) - PFileV1(stmts) - } - /** * * COMMON USAGES From b94dfc4f757ebd007a2de7240017cb50c0655970 Mon Sep 17 00:00:00 2001 From: John Ed Quinn Date: Fri, 1 Nov 2024 11:04:15 -0700 Subject: [PATCH 08/12] Adds a parseSingle() method for the parser Moves parser builder as nested class of parser --- .../org/partiql/cli/pipeline/Pipeline.kt | 8 +-- partiql-parser/api/partiql-parser.api | 13 +++-- .../parser/PartiQLParserBuilderV1.java | 30 ---------- .../org/partiql/parser/PartiQLParserV1.java | 58 ++++++++++++++++++- .../planner/PlannerPErrorReportingTests.kt | 4 +- 5 files changed, 67 insertions(+), 46 deletions(-) delete mode 100644 partiql-parser/src/main/java/org/partiql/parser/PartiQLParserBuilderV1.java diff --git a/partiql-cli/src/main/kotlin/org/partiql/cli/pipeline/Pipeline.kt b/partiql-cli/src/main/kotlin/org/partiql/cli/pipeline/Pipeline.kt index 59b7ff4ba5..4d28407378 100644 --- a/partiql-cli/src/main/kotlin/org/partiql/cli/pipeline/Pipeline.kt +++ b/partiql-cli/src/main/kotlin/org/partiql/cli/pipeline/Pipeline.kt @@ -4,7 +4,6 @@ import org.partiql.ast.v1.Statement import org.partiql.cli.ErrorCodeString import org.partiql.eval.Mode import org.partiql.eval.compiler.PartiQLCompiler -import org.partiql.parser.PartiQLParserBuilderV1 import org.partiql.parser.PartiQLParserV1 import org.partiql.plan.Plan import org.partiql.planner.PartiQLPlanner @@ -35,10 +34,7 @@ internal class Pipeline private constructor( private fun parse(source: String): Statement { val result = listen(ctx.errorListener as AppPErrorListener) { - parser.parse(source, ctx) - } - if (result.statements.size != 1) { - throw PipelineException("Expected exactly one statement, got: ${result.statements.size}") + parser.parseSingle(source, ctx) } return result.statements[0] } @@ -83,7 +79,7 @@ internal class Pipeline private constructor( private fun create(mode: Mode, out: PrintStream, config: Config): Pipeline { val listener = config.getErrorListener(out) val ctx = Context.of(listener) - val parser = PartiQLParserBuilderV1().build() + val parser = PartiQLParserV1.Builder().build() val planner = PartiQLPlanner.builder().build() val compiler = PartiQLCompiler.builder().build() return Pipeline(parser, planner, compiler, ctx, mode) diff --git a/partiql-parser/api/partiql-parser.api b/partiql-parser/api/partiql-parser.api index bd84b15a30..9909984863 100644 --- a/partiql-parser/api/partiql-parser.api +++ b/partiql-parser/api/partiql-parser.api @@ -16,18 +16,19 @@ public class org/partiql/parser/PartiQLParserBuilder { public fun build ()Lorg/partiql/parser/PartiQLParser; } -public class org/partiql/parser/PartiQLParserBuilderV1 { - public fun ()V - public fun build ()Lorg/partiql/parser/PartiQLParserV1; -} - public abstract interface class org/partiql/parser/PartiQLParserV1 { - public static fun builder ()Lorg/partiql/parser/PartiQLParserBuilderV1; + public static fun builder ()Lorg/partiql/parser/PartiQLParserV1$Builder; public fun parse (Ljava/lang/String;)Lorg/partiql/parser/PartiQLParserV1$Result; public abstract fun parse (Ljava/lang/String;Lorg/partiql/spi/Context;)Lorg/partiql/parser/PartiQLParserV1$Result; + public fun parseSingle (Ljava/lang/String;Lorg/partiql/spi/Context;)Lorg/partiql/parser/PartiQLParserV1$Result; public static fun standard ()Lorg/partiql/parser/PartiQLParserV1; } +public class org/partiql/parser/PartiQLParserV1$Builder { + public fun ()V + public fun build ()Lorg/partiql/parser/PartiQLParserV1; +} + public final class org/partiql/parser/PartiQLParserV1$Result { public field locations Lorg/partiql/spi/SourceLocations; public field statements Ljava/util/List; diff --git a/partiql-parser/src/main/java/org/partiql/parser/PartiQLParserBuilderV1.java b/partiql-parser/src/main/java/org/partiql/parser/PartiQLParserBuilderV1.java deleted file mode 100644 index 24b8e405cb..0000000000 --- a/partiql-parser/src/main/java/org/partiql/parser/PartiQLParserBuilderV1.java +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Copyright Amazon.com, Inc. or its affiliates. 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. - * A copy of the License is located at: - * - * http://aws.amazon.com/apache2.0/ - * - * or in the "license" file accompanying this file. This file is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific - * language governing permissions and limitations under the License. - */ - -package org.partiql.parser; - -import org.jetbrains.annotations.NotNull; -import org.partiql.parser.internal.PartiQLParserDefaultV1; - -/** - * A builder class to instantiate a [PartiQLParserV1]. https://github.com/partiql/partiql-lang-kotlin/issues/1632 - * TODO replace with Lombok builder once [PartiQLParserV1] is migrated to Java. - */ -public class PartiQLParserBuilderV1 { - - @NotNull - public PartiQLParserV1 build() { - return new PartiQLParserDefaultV1(); - } -} diff --git a/partiql-parser/src/main/java/org/partiql/parser/PartiQLParserV1.java b/partiql-parser/src/main/java/org/partiql/parser/PartiQLParserV1.java index d16acc2677..fcb03616dd 100644 --- a/partiql-parser/src/main/java/org/partiql/parser/PartiQLParserV1.java +++ b/partiql-parser/src/main/java/org/partiql/parser/PartiQLParserV1.java @@ -18,10 +18,17 @@ import org.partiql.ast.v1.Statement; import org.partiql.parser.internal.PartiQLParserDefaultV1; import org.partiql.spi.Context; +import org.partiql.spi.SourceLocation; import org.partiql.spi.SourceLocations; +import org.partiql.spi.errors.PError; +import org.partiql.spi.errors.PErrorKind; import org.partiql.spi.errors.PErrorListenerException; +import org.partiql.spi.errors.Severity; +import java.util.ArrayList; +import java.util.HashMap; import java.util.List; +import java.util.Map; /** * TODO: Rename to PartiQLParser @@ -34,10 +41,40 @@ public interface PartiQLParserV1 { * @param ctx a configuration object for the parser * @throws PErrorListenerException when the [org.partiql.spi.errors.PErrorListener] defined in the [ctx] throws an * [PErrorListenerException], this method halts execution and propagates the exception. + * @see PartiQLParserV1#parseSingle(String, Context) */ @NotNull Result parse(@NotNull String source, @NotNull Context ctx) throws PErrorListenerException; + /** + * TODO + * @param source TODO + * @param ctx TODO + * @return TODO + * @throws PErrorListenerException TODO + * @see PartiQLParserV1#parse(String, Context) + */ + @NotNull + default Result parseSingle(@NotNull String source, @NotNull Context ctx) throws PErrorListenerException { + Result result = parse(source, ctx); + if (result.statements.size() != 1) { + SourceLocation location; + if (result.statements.size() > 1) { + location = result.locations.get(result.statements.get(1).tag); + } else { + location = null; + } + Map properties = new HashMap() {{ + put("EXPECTED_TOKENS", new ArrayList() {{ + add("EOF"); + }}); + }}; + PError pError = new PError(PError.UNEXPECTED_TOKEN, Severity.ERROR(), PErrorKind.SYNTAX(), location, properties); + ctx.getErrorListener().report(pError); + } + return result; + } + /** * Parses the [source] into an AST. * @param source the user's input @@ -82,8 +119,8 @@ public Result(@NotNull List statements, @NotNull SourceLocations loca * @return TODO */ @NotNull - public static PartiQLParserBuilderV1 builder() { - return new PartiQLParserBuilderV1(); + public static Builder builder() { + return new Builder(); } /** @@ -94,4 +131,21 @@ public static PartiQLParserBuilderV1 builder() { public static PartiQLParserV1 standard() { return new PartiQLParserDefaultV1(); } + + /** + * A builder class to instantiate a {@link PartiQLParserV1}. + */ + public class Builder { + // TODO: Can this be replaced with Lombok? + // TODO: https://github.com/partiql/partiql-lang-kotlin/issues/1632 + + /** + * TODO + * @return TODO + */ + @NotNull + public PartiQLParserV1 build() { + return new PartiQLParserDefaultV1(); + } + } } diff --git a/partiql-planner/src/test/kotlin/org/partiql/planner/PlannerPErrorReportingTests.kt b/partiql-planner/src/test/kotlin/org/partiql/planner/PlannerPErrorReportingTests.kt index ba609f886b..969a75ffb9 100644 --- a/partiql-planner/src/test/kotlin/org/partiql/planner/PlannerPErrorReportingTests.kt +++ b/partiql-planner/src/test/kotlin/org/partiql/planner/PlannerPErrorReportingTests.kt @@ -3,7 +3,7 @@ package org.partiql.planner import org.junit.jupiter.params.ParameterizedTest import org.junit.jupiter.params.provider.MethodSource import org.partiql.ast.v1.Statement -import org.partiql.parser.PartiQLParserBuilderV1 +import org.partiql.parser.PartiQLParserV1 import org.partiql.plan.Operation import org.partiql.planner.internal.typer.CompilerType import org.partiql.planner.internal.typer.PlanTyper.Companion.toCType @@ -42,7 +42,7 @@ internal class PlannerPErrorReportingTests { .catalogs(catalog) .build() - private val parser = PartiQLParserBuilderV1().build() + private val parser = PartiQLParserV1.Builder().build() private val statement: ((String) -> Statement) = { query -> val parseResult = parser.parse(query) From 602b9e09cea98c2d986cf6a9669b45809b59a0d7 Mon Sep 17 00:00:00 2001 From: John Ed Quinn Date: Fri, 1 Nov 2024 11:08:10 -0700 Subject: [PATCH 09/12] Adds a stricter use of semi-colons for multiple statements --- partiql-parser/src/main/antlr/PartiQLParser.g4 | 3 ++- .../kotlin/org/partiql/parser/internal/SemiColonTests.kt | 6 ++++-- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/partiql-parser/src/main/antlr/PartiQLParser.g4 b/partiql-parser/src/main/antlr/PartiQLParser.g4 index dafc1fd006..358ad0c770 100644 --- a/partiql-parser/src/main/antlr/PartiQLParser.g4 +++ b/partiql-parser/src/main/antlr/PartiQLParser.g4 @@ -12,7 +12,8 @@ options { */ statements - : statement (COLON_SEMI statement)* COLON_SEMI? EOF + : statement EOF + | statement COLON_SEMI (statement COLON_SEMI)* EOF ; statement diff --git a/partiql-parser/src/test/kotlin/org/partiql/parser/internal/SemiColonTests.kt b/partiql-parser/src/test/kotlin/org/partiql/parser/internal/SemiColonTests.kt index 309d2c2e6a..d288ea6fa0 100644 --- a/partiql-parser/src/test/kotlin/org/partiql/parser/internal/SemiColonTests.kt +++ b/partiql-parser/src/test/kotlin/org/partiql/parser/internal/SemiColonTests.kt @@ -12,11 +12,13 @@ class SemiColonTests { tc.assert() } + // TODO: This is not completely settled, however, we can always allow for this in the future. @Test fun t2() { val tc = ParserTestCaseSimple( - "Valid multi-statement where last statement doesn't have a semi-colon", - "1 + 1; 1 + 2; SELECT * FROM t; SELECT * FROM t2 AS t2" + "Invalid multi-statement where last statement doesn't have a semi-colon", + "1 + 1; 1 + 2; SELECT * FROM t; SELECT * FROM t2 AS t2", + false ) tc.assert() } From 75e97e74aab9338ad97d83964f91b925920342b5 Mon Sep 17 00:00:00 2001 From: John Ed Quinn Date: Fri, 1 Nov 2024 11:26:29 -0700 Subject: [PATCH 10/12] Adds a no-context parseSingle() method to PartiQLParser Replaces all internal parse() invocations with parseSingle() --- .../partiql/eval/internal/PartiQLEvaluatorTest.kt | 7 ++----- partiql-parser/api/partiql-parser.api | 1 + .../java/org/partiql/parser/PartiQLParserV1.java | 13 +++++++++++++ .../parser/internal/PartiQLParserBagOpTests.kt | 3 +-- .../parser/internal/PartiQLParserDDLTests.kt | 3 +-- .../internal/PartiQLParserFunctionCallTests.kt | 3 +-- .../parser/internal/PartiQLParserOperatorTests.kt | 3 +-- .../internal/PartiQLParserSessionAttributeTests.kt | 3 +-- .../src/test/kotlin/org/partiql/planner/PlanTest.kt | 4 +--- .../partiql/planner/PlannerPErrorReportingTests.kt | 3 +-- .../planner/internal/exclude/SubsumptionTest.kt | 3 +-- .../planner/internal/typer/PartiQLTyperTestBase.kt | 4 +--- .../planner/internal/typer/PlanTyperTestsPorted.kt | 3 +-- .../org/partiql/runner/executor/EvalExecutor.kt | 7 ++----- 14 files changed, 28 insertions(+), 32 deletions(-) diff --git a/partiql-eval/src/test/kotlin/org/partiql/eval/internal/PartiQLEvaluatorTest.kt b/partiql-eval/src/test/kotlin/org/partiql/eval/internal/PartiQLEvaluatorTest.kt index cedeaa173f..647b6fadc1 100644 --- a/partiql-eval/src/test/kotlin/org/partiql/eval/internal/PartiQLEvaluatorTest.kt +++ b/partiql-eval/src/test/kotlin/org/partiql/eval/internal/PartiQLEvaluatorTest.kt @@ -38,7 +38,6 @@ import org.partiql.value.symbolValue import java.io.ByteArrayOutputStream import java.math.BigDecimal import java.math.BigInteger -import kotlin.test.assertEquals import kotlin.test.assertNotNull /** @@ -1321,8 +1320,7 @@ class PartiQLEvaluatorTest { ) internal fun assert() { - val parseResult = parser.parse(input) - assertEquals(1, parseResult.statements.size) + val parseResult = parser.parseSingle(input) val statement = parseResult.statements[0] val catalog = Catalog.builder() .name("memory") @@ -1409,8 +1407,7 @@ class PartiQLEvaluatorTest { } private fun run(mode: Mode): Pair { - val parseResult = parser.parse(input) - assertEquals(1, parseResult.statements.size) + val parseResult = parser.parseSingle(input) val statement = parseResult.statements[0] val catalog = Catalog.builder().name("memory").build() val session = Session.builder() diff --git a/partiql-parser/api/partiql-parser.api b/partiql-parser/api/partiql-parser.api index 9909984863..f3f5f96a13 100644 --- a/partiql-parser/api/partiql-parser.api +++ b/partiql-parser/api/partiql-parser.api @@ -20,6 +20,7 @@ public abstract interface class org/partiql/parser/PartiQLParserV1 { public static fun builder ()Lorg/partiql/parser/PartiQLParserV1$Builder; public fun parse (Ljava/lang/String;)Lorg/partiql/parser/PartiQLParserV1$Result; public abstract fun parse (Ljava/lang/String;Lorg/partiql/spi/Context;)Lorg/partiql/parser/PartiQLParserV1$Result; + public fun parseSingle (Ljava/lang/String;)Lorg/partiql/parser/PartiQLParserV1$Result; public fun parseSingle (Ljava/lang/String;Lorg/partiql/spi/Context;)Lorg/partiql/parser/PartiQLParserV1$Result; public static fun standard ()Lorg/partiql/parser/PartiQLParserV1; } diff --git a/partiql-parser/src/main/java/org/partiql/parser/PartiQLParserV1.java b/partiql-parser/src/main/java/org/partiql/parser/PartiQLParserV1.java index fcb03616dd..c31abb9087 100644 --- a/partiql-parser/src/main/java/org/partiql/parser/PartiQLParserV1.java +++ b/partiql-parser/src/main/java/org/partiql/parser/PartiQLParserV1.java @@ -86,6 +86,19 @@ default Result parse(@NotNull String source) throws PErrorListenerException { return parse(source, Context.standard()); } + /** + * Parses the {@code source} into an AST while requiring that the {@code source} only contains a single statement. + * @param source the user's input + * @throws PErrorListenerException when the {@link org.partiql.spi.errors.PErrorListener} defined in the context throws an + * {@link PErrorListenerException}, this method halts execution and propagates the exception. This is also thrown + * when the {@code source} does not contain exactly one statement. + * @see PartiQLParserV1#parse(String, Context) + */ + @NotNull + default Result parseSingle(@NotNull String source) throws PErrorListenerException { + return parseSingle(source, Context.standard()); + } + /** * TODO */ diff --git a/partiql-parser/src/test/kotlin/org/partiql/parser/internal/PartiQLParserBagOpTests.kt b/partiql-parser/src/test/kotlin/org/partiql/parser/internal/PartiQLParserBagOpTests.kt index 52794b27fa..73558cd678 100644 --- a/partiql-parser/src/test/kotlin/org/partiql/parser/internal/PartiQLParserBagOpTests.kt +++ b/partiql-parser/src/test/kotlin/org/partiql/parser/internal/PartiQLParserBagOpTests.kt @@ -676,8 +676,7 @@ class PartiQLParserBagOpTests { ) private fun assertExpression(input: String, expected: AstNode) { - val parseResult = parser.parse(input) - assertEquals(1, parseResult.statements.size) + val parseResult = parser.parseSingle(input) val actual = parseResult.statements[0] assertEquals(expected, actual) } diff --git a/partiql-parser/src/test/kotlin/org/partiql/parser/internal/PartiQLParserDDLTests.kt b/partiql-parser/src/test/kotlin/org/partiql/parser/internal/PartiQLParserDDLTests.kt index dbbacafdf5..4476f5f6e5 100644 --- a/partiql-parser/src/test/kotlin/org/partiql/parser/internal/PartiQLParserDDLTests.kt +++ b/partiql-parser/src/test/kotlin/org/partiql/parser/internal/PartiQLParserDDLTests.kt @@ -117,8 +117,7 @@ class PartiQLParserDDLTests { // } private fun assertExpression(input: String, expected: AstNode) { - val result = parser.parse(input) - assertEquals(1, result.statements.size) + val result = parser.parseSingle(input) val actual = result.statements[0] assertEquals(expected, actual) } diff --git a/partiql-parser/src/test/kotlin/org/partiql/parser/internal/PartiQLParserFunctionCallTests.kt b/partiql-parser/src/test/kotlin/org/partiql/parser/internal/PartiQLParserFunctionCallTests.kt index 2bda30224f..7d4b91d182 100644 --- a/partiql-parser/src/test/kotlin/org/partiql/parser/internal/PartiQLParserFunctionCallTests.kt +++ b/partiql-parser/src/test/kotlin/org/partiql/parser/internal/PartiQLParserFunctionCallTests.kt @@ -136,8 +136,7 @@ class PartiQLParserFunctionCallTests { ) private fun assertExpression(input: String, expected: AstNode) { - val result = parser.parse(input) - assertEquals(1, result.statements.size) + val result = parser.parseSingle(input) val actual = result.statements[0] assertEquals(expected, actual) } diff --git a/partiql-parser/src/test/kotlin/org/partiql/parser/internal/PartiQLParserOperatorTests.kt b/partiql-parser/src/test/kotlin/org/partiql/parser/internal/PartiQLParserOperatorTests.kt index a5c8634066..e50459f84f 100644 --- a/partiql-parser/src/test/kotlin/org/partiql/parser/internal/PartiQLParserOperatorTests.kt +++ b/partiql-parser/src/test/kotlin/org/partiql/parser/internal/PartiQLParserOperatorTests.kt @@ -66,8 +66,7 @@ class PartiQLParserOperatorTests { ) private fun assertExpression(input: String, expected: AstNode) { - val result = parser.parse(input) - assertEquals(1, result.statements.size) + val result = parser.parseSingle(input) val actual = result.statements[0] assertEquals(expected, actual) } diff --git a/partiql-parser/src/test/kotlin/org/partiql/parser/internal/PartiQLParserSessionAttributeTests.kt b/partiql-parser/src/test/kotlin/org/partiql/parser/internal/PartiQLParserSessionAttributeTests.kt index 6dd55d49e8..5671600e6e 100644 --- a/partiql-parser/src/test/kotlin/org/partiql/parser/internal/PartiQLParserSessionAttributeTests.kt +++ b/partiql-parser/src/test/kotlin/org/partiql/parser/internal/PartiQLParserSessionAttributeTests.kt @@ -80,8 +80,7 @@ class PartiQLParserSessionAttributeTests { ) private fun assertExpression(input: String, expected: AstNode) { - val result = parser.parse(input) - assertEquals(1, result.statements.size) + val result = parser.parseSingle(input) val actual = result.statements[0] assertEquals(expected, actual) } diff --git a/partiql-planner/src/test/kotlin/org/partiql/planner/PlanTest.kt b/partiql-planner/src/test/kotlin/org/partiql/planner/PlanTest.kt index bc5aa6c9c8..66dd0f15fe 100644 --- a/partiql-planner/src/test/kotlin/org/partiql/planner/PlanTest.kt +++ b/partiql-planner/src/test/kotlin/org/partiql/planner/PlanTest.kt @@ -21,7 +21,6 @@ import java.io.File import java.nio.file.Path import java.util.stream.Stream import kotlin.io.path.toPath -import kotlin.test.assertEquals /** * This class test asserts a normalized query produces the same plans as the original input query. @@ -76,8 +75,7 @@ class PlanTest { ) .namespace("SCHEMA") .build() - val parseResult = PartiQLParserV1.standard().parse(test.statement) - assertEquals(1, parseResult.statements.size) + val parseResult = PartiQLParserV1.standard().parseSingle(test.statement) val ast = parseResult.statements[0] val planner = PartiQLPlanner.builder().signal(isSignalMode).build() planner.plan(ast, session) diff --git a/partiql-planner/src/test/kotlin/org/partiql/planner/PlannerPErrorReportingTests.kt b/partiql-planner/src/test/kotlin/org/partiql/planner/PlannerPErrorReportingTests.kt index 969a75ffb9..68802cfc32 100644 --- a/partiql-planner/src/test/kotlin/org/partiql/planner/PlannerPErrorReportingTests.kt +++ b/partiql-planner/src/test/kotlin/org/partiql/planner/PlannerPErrorReportingTests.kt @@ -45,8 +45,7 @@ internal class PlannerPErrorReportingTests { private val parser = PartiQLParserV1.Builder().build() private val statement: ((String) -> Statement) = { query -> - val parseResult = parser.parse(query) - assertEquals(1, parseResult.statements.size) + val parseResult = parser.parseSingle(query) parseResult.statements[0] } diff --git a/partiql-planner/src/test/kotlin/org/partiql/planner/internal/exclude/SubsumptionTest.kt b/partiql-planner/src/test/kotlin/org/partiql/planner/internal/exclude/SubsumptionTest.kt index c56a1852aa..3918b19211 100644 --- a/partiql-planner/src/test/kotlin/org/partiql/planner/internal/exclude/SubsumptionTest.kt +++ b/partiql-planner/src/test/kotlin/org/partiql/planner/internal/exclude/SubsumptionTest.kt @@ -38,8 +38,7 @@ class SubsumptionTest { private fun testExcludeExprSubsumption(tc: SubsumptionTC) { val text = "SELECT * EXCLUDE ${tc.excludeExprStr} FROM <<>> AS s, <<>> AS t;" - val parseResult = parser.parse(text) - assertEquals(1, parseResult.statements.size) + val parseResult = parser.parseSingle(text) val statement = parseResult.statements[0] val session = Session.builder().catalog("default").catalogs(catalog).build() val plan = planner.plan(statement, session).plan diff --git a/partiql-planner/src/test/kotlin/org/partiql/planner/internal/typer/PartiQLTyperTestBase.kt b/partiql-planner/src/test/kotlin/org/partiql/planner/internal/typer/PartiQLTyperTestBase.kt index 33d5c71a80..71977e7f1f 100644 --- a/partiql-planner/src/test/kotlin/org/partiql/planner/internal/typer/PartiQLTyperTestBase.kt +++ b/partiql-planner/src/test/kotlin/org/partiql/planner/internal/typer/PartiQLTyperTestBase.kt @@ -20,7 +20,6 @@ import org.partiql.types.PType import org.partiql.types.PType.Kind import org.partiql.types.StaticType import java.util.stream.Stream -import kotlin.test.assertEquals abstract class PartiQLTyperTestBase { sealed class TestResult { @@ -53,8 +52,7 @@ abstract class PartiQLTyperTestBase { private val testingPipeline: ((String, String, Catalog, PErrorListener) -> PartiQLPlanner.Result) = { query, catalog, metadata, collector -> - val parseResult = parser.parse(query) - assertEquals(1, parseResult.statements.size) + val parseResult = parser.parseSingle(query) val ast = parseResult.statements[0] val config = Context.of(collector) planner.plan(ast, session(catalog, metadata), config) diff --git a/partiql-planner/src/test/kotlin/org/partiql/planner/internal/typer/PlanTyperTestsPorted.kt b/partiql-planner/src/test/kotlin/org/partiql/planner/internal/typer/PlanTyperTestsPorted.kt index 123a57dc71..4dcf382f3b 100644 --- a/partiql-planner/src/test/kotlin/org/partiql/planner/internal/typer/PlanTyperTestsPorted.kt +++ b/partiql-planner/src/test/kotlin/org/partiql/planner/internal/typer/PlanTyperTestsPorted.kt @@ -3850,8 +3850,7 @@ internal class PlanTyperTestsPorted { session: Session, listener: PErrorListener, ): org.partiql.plan.Plan { - val parseResult = parser.parse(query) - assertEquals(1, parseResult.statements.size) + val parseResult = parser.parseSingle(query) val ast = parseResult.statements[0] val plannerConfig = Context.of(listener) return planner.plan(ast, session, plannerConfig).plan diff --git a/test/partiql-tests-runner/src/test/kotlin/org/partiql/runner/executor/EvalExecutor.kt b/test/partiql-tests-runner/src/test/kotlin/org/partiql/runner/executor/EvalExecutor.kt index 63aabb3b27..2652e8b4e4 100644 --- a/test/partiql-tests-runner/src/test/kotlin/org/partiql/runner/executor/EvalExecutor.kt +++ b/test/partiql-tests-runner/src/test/kotlin/org/partiql/runner/executor/EvalExecutor.kt @@ -31,7 +31,6 @@ import org.partiql.value.PartiQLValue import org.partiql.value.PartiQLValueExperimental import org.partiql.value.io.PartiQLValueIonReaderBuilder import org.partiql.value.toIon -import kotlin.test.assertEquals /** * @property session @@ -46,8 +45,7 @@ class EvalExecutor( override fun prepare(input: String): Statement { val listener = getErrorListener(mode) val ctx = Context.of(listener) - val parseResult = parser.parse(input, ctx) - assertEquals(1, parseResult.statements.size) + val parseResult = parser.parseSingle(input, ctx) val ast = parseResult.statements[0] val plan = planner.plan(ast, session, ctx).plan return compiler.prepare(plan, mode, ctx) @@ -191,8 +189,7 @@ class EvalExecutor( .catalog("default") .catalogs(catalog) .build() - val parseResult = parser.parse("`$env`") - assertEquals(1, parseResult.statements.size) + val parseResult = parser.parseSingle("`$env`") val stmt = parseResult.statements[0] val plan = planner.plan(stmt, session).plan return (plan.getOperation() as Query).getRex().getType().getPType() From f36d0f45088a9e1df4bcbcd437680785c47b151e Mon Sep 17 00:00:00 2001 From: John Ed Quinn Date: Fri, 1 Nov 2024 11:41:45 -0700 Subject: [PATCH 11/12] Revert "Adds a no-context parseSingle() method to PartiQLParser" This reverts commit 75e97e74aab9338ad97d83964f91b925920342b5. --- .../partiql/eval/internal/PartiQLEvaluatorTest.kt | 7 +++++-- partiql-parser/api/partiql-parser.api | 1 - .../java/org/partiql/parser/PartiQLParserV1.java | 13 ------------- .../parser/internal/PartiQLParserBagOpTests.kt | 3 ++- .../parser/internal/PartiQLParserDDLTests.kt | 3 ++- .../internal/PartiQLParserFunctionCallTests.kt | 3 ++- .../parser/internal/PartiQLParserOperatorTests.kt | 3 ++- .../internal/PartiQLParserSessionAttributeTests.kt | 3 ++- .../src/test/kotlin/org/partiql/planner/PlanTest.kt | 4 +++- .../partiql/planner/PlannerPErrorReportingTests.kt | 3 ++- .../planner/internal/exclude/SubsumptionTest.kt | 3 ++- .../planner/internal/typer/PartiQLTyperTestBase.kt | 4 +++- .../planner/internal/typer/PlanTyperTestsPorted.kt | 3 ++- .../org/partiql/runner/executor/EvalExecutor.kt | 7 +++++-- 14 files changed, 32 insertions(+), 28 deletions(-) diff --git a/partiql-eval/src/test/kotlin/org/partiql/eval/internal/PartiQLEvaluatorTest.kt b/partiql-eval/src/test/kotlin/org/partiql/eval/internal/PartiQLEvaluatorTest.kt index 647b6fadc1..cedeaa173f 100644 --- a/partiql-eval/src/test/kotlin/org/partiql/eval/internal/PartiQLEvaluatorTest.kt +++ b/partiql-eval/src/test/kotlin/org/partiql/eval/internal/PartiQLEvaluatorTest.kt @@ -38,6 +38,7 @@ import org.partiql.value.symbolValue import java.io.ByteArrayOutputStream import java.math.BigDecimal import java.math.BigInteger +import kotlin.test.assertEquals import kotlin.test.assertNotNull /** @@ -1320,7 +1321,8 @@ class PartiQLEvaluatorTest { ) internal fun assert() { - val parseResult = parser.parseSingle(input) + val parseResult = parser.parse(input) + assertEquals(1, parseResult.statements.size) val statement = parseResult.statements[0] val catalog = Catalog.builder() .name("memory") @@ -1407,7 +1409,8 @@ class PartiQLEvaluatorTest { } private fun run(mode: Mode): Pair { - val parseResult = parser.parseSingle(input) + val parseResult = parser.parse(input) + assertEquals(1, parseResult.statements.size) val statement = parseResult.statements[0] val catalog = Catalog.builder().name("memory").build() val session = Session.builder() diff --git a/partiql-parser/api/partiql-parser.api b/partiql-parser/api/partiql-parser.api index f3f5f96a13..9909984863 100644 --- a/partiql-parser/api/partiql-parser.api +++ b/partiql-parser/api/partiql-parser.api @@ -20,7 +20,6 @@ public abstract interface class org/partiql/parser/PartiQLParserV1 { public static fun builder ()Lorg/partiql/parser/PartiQLParserV1$Builder; public fun parse (Ljava/lang/String;)Lorg/partiql/parser/PartiQLParserV1$Result; public abstract fun parse (Ljava/lang/String;Lorg/partiql/spi/Context;)Lorg/partiql/parser/PartiQLParserV1$Result; - public fun parseSingle (Ljava/lang/String;)Lorg/partiql/parser/PartiQLParserV1$Result; public fun parseSingle (Ljava/lang/String;Lorg/partiql/spi/Context;)Lorg/partiql/parser/PartiQLParserV1$Result; public static fun standard ()Lorg/partiql/parser/PartiQLParserV1; } diff --git a/partiql-parser/src/main/java/org/partiql/parser/PartiQLParserV1.java b/partiql-parser/src/main/java/org/partiql/parser/PartiQLParserV1.java index c31abb9087..fcb03616dd 100644 --- a/partiql-parser/src/main/java/org/partiql/parser/PartiQLParserV1.java +++ b/partiql-parser/src/main/java/org/partiql/parser/PartiQLParserV1.java @@ -86,19 +86,6 @@ default Result parse(@NotNull String source) throws PErrorListenerException { return parse(source, Context.standard()); } - /** - * Parses the {@code source} into an AST while requiring that the {@code source} only contains a single statement. - * @param source the user's input - * @throws PErrorListenerException when the {@link org.partiql.spi.errors.PErrorListener} defined in the context throws an - * {@link PErrorListenerException}, this method halts execution and propagates the exception. This is also thrown - * when the {@code source} does not contain exactly one statement. - * @see PartiQLParserV1#parse(String, Context) - */ - @NotNull - default Result parseSingle(@NotNull String source) throws PErrorListenerException { - return parseSingle(source, Context.standard()); - } - /** * TODO */ diff --git a/partiql-parser/src/test/kotlin/org/partiql/parser/internal/PartiQLParserBagOpTests.kt b/partiql-parser/src/test/kotlin/org/partiql/parser/internal/PartiQLParserBagOpTests.kt index 73558cd678..52794b27fa 100644 --- a/partiql-parser/src/test/kotlin/org/partiql/parser/internal/PartiQLParserBagOpTests.kt +++ b/partiql-parser/src/test/kotlin/org/partiql/parser/internal/PartiQLParserBagOpTests.kt @@ -676,7 +676,8 @@ class PartiQLParserBagOpTests { ) private fun assertExpression(input: String, expected: AstNode) { - val parseResult = parser.parseSingle(input) + val parseResult = parser.parse(input) + assertEquals(1, parseResult.statements.size) val actual = parseResult.statements[0] assertEquals(expected, actual) } diff --git a/partiql-parser/src/test/kotlin/org/partiql/parser/internal/PartiQLParserDDLTests.kt b/partiql-parser/src/test/kotlin/org/partiql/parser/internal/PartiQLParserDDLTests.kt index 4476f5f6e5..dbbacafdf5 100644 --- a/partiql-parser/src/test/kotlin/org/partiql/parser/internal/PartiQLParserDDLTests.kt +++ b/partiql-parser/src/test/kotlin/org/partiql/parser/internal/PartiQLParserDDLTests.kt @@ -117,7 +117,8 @@ class PartiQLParserDDLTests { // } private fun assertExpression(input: String, expected: AstNode) { - val result = parser.parseSingle(input) + val result = parser.parse(input) + assertEquals(1, result.statements.size) val actual = result.statements[0] assertEquals(expected, actual) } diff --git a/partiql-parser/src/test/kotlin/org/partiql/parser/internal/PartiQLParserFunctionCallTests.kt b/partiql-parser/src/test/kotlin/org/partiql/parser/internal/PartiQLParserFunctionCallTests.kt index 7d4b91d182..2bda30224f 100644 --- a/partiql-parser/src/test/kotlin/org/partiql/parser/internal/PartiQLParserFunctionCallTests.kt +++ b/partiql-parser/src/test/kotlin/org/partiql/parser/internal/PartiQLParserFunctionCallTests.kt @@ -136,7 +136,8 @@ class PartiQLParserFunctionCallTests { ) private fun assertExpression(input: String, expected: AstNode) { - val result = parser.parseSingle(input) + val result = parser.parse(input) + assertEquals(1, result.statements.size) val actual = result.statements[0] assertEquals(expected, actual) } diff --git a/partiql-parser/src/test/kotlin/org/partiql/parser/internal/PartiQLParserOperatorTests.kt b/partiql-parser/src/test/kotlin/org/partiql/parser/internal/PartiQLParserOperatorTests.kt index e50459f84f..a5c8634066 100644 --- a/partiql-parser/src/test/kotlin/org/partiql/parser/internal/PartiQLParserOperatorTests.kt +++ b/partiql-parser/src/test/kotlin/org/partiql/parser/internal/PartiQLParserOperatorTests.kt @@ -66,7 +66,8 @@ class PartiQLParserOperatorTests { ) private fun assertExpression(input: String, expected: AstNode) { - val result = parser.parseSingle(input) + val result = parser.parse(input) + assertEquals(1, result.statements.size) val actual = result.statements[0] assertEquals(expected, actual) } diff --git a/partiql-parser/src/test/kotlin/org/partiql/parser/internal/PartiQLParserSessionAttributeTests.kt b/partiql-parser/src/test/kotlin/org/partiql/parser/internal/PartiQLParserSessionAttributeTests.kt index 5671600e6e..6dd55d49e8 100644 --- a/partiql-parser/src/test/kotlin/org/partiql/parser/internal/PartiQLParserSessionAttributeTests.kt +++ b/partiql-parser/src/test/kotlin/org/partiql/parser/internal/PartiQLParserSessionAttributeTests.kt @@ -80,7 +80,8 @@ class PartiQLParserSessionAttributeTests { ) private fun assertExpression(input: String, expected: AstNode) { - val result = parser.parseSingle(input) + val result = parser.parse(input) + assertEquals(1, result.statements.size) val actual = result.statements[0] assertEquals(expected, actual) } diff --git a/partiql-planner/src/test/kotlin/org/partiql/planner/PlanTest.kt b/partiql-planner/src/test/kotlin/org/partiql/planner/PlanTest.kt index 66dd0f15fe..bc5aa6c9c8 100644 --- a/partiql-planner/src/test/kotlin/org/partiql/planner/PlanTest.kt +++ b/partiql-planner/src/test/kotlin/org/partiql/planner/PlanTest.kt @@ -21,6 +21,7 @@ import java.io.File import java.nio.file.Path import java.util.stream.Stream import kotlin.io.path.toPath +import kotlin.test.assertEquals /** * This class test asserts a normalized query produces the same plans as the original input query. @@ -75,7 +76,8 @@ class PlanTest { ) .namespace("SCHEMA") .build() - val parseResult = PartiQLParserV1.standard().parseSingle(test.statement) + val parseResult = PartiQLParserV1.standard().parse(test.statement) + assertEquals(1, parseResult.statements.size) val ast = parseResult.statements[0] val planner = PartiQLPlanner.builder().signal(isSignalMode).build() planner.plan(ast, session) diff --git a/partiql-planner/src/test/kotlin/org/partiql/planner/PlannerPErrorReportingTests.kt b/partiql-planner/src/test/kotlin/org/partiql/planner/PlannerPErrorReportingTests.kt index 68802cfc32..969a75ffb9 100644 --- a/partiql-planner/src/test/kotlin/org/partiql/planner/PlannerPErrorReportingTests.kt +++ b/partiql-planner/src/test/kotlin/org/partiql/planner/PlannerPErrorReportingTests.kt @@ -45,7 +45,8 @@ internal class PlannerPErrorReportingTests { private val parser = PartiQLParserV1.Builder().build() private val statement: ((String) -> Statement) = { query -> - val parseResult = parser.parseSingle(query) + val parseResult = parser.parse(query) + assertEquals(1, parseResult.statements.size) parseResult.statements[0] } diff --git a/partiql-planner/src/test/kotlin/org/partiql/planner/internal/exclude/SubsumptionTest.kt b/partiql-planner/src/test/kotlin/org/partiql/planner/internal/exclude/SubsumptionTest.kt index 3918b19211..c56a1852aa 100644 --- a/partiql-planner/src/test/kotlin/org/partiql/planner/internal/exclude/SubsumptionTest.kt +++ b/partiql-planner/src/test/kotlin/org/partiql/planner/internal/exclude/SubsumptionTest.kt @@ -38,7 +38,8 @@ class SubsumptionTest { private fun testExcludeExprSubsumption(tc: SubsumptionTC) { val text = "SELECT * EXCLUDE ${tc.excludeExprStr} FROM <<>> AS s, <<>> AS t;" - val parseResult = parser.parseSingle(text) + val parseResult = parser.parse(text) + assertEquals(1, parseResult.statements.size) val statement = parseResult.statements[0] val session = Session.builder().catalog("default").catalogs(catalog).build() val plan = planner.plan(statement, session).plan diff --git a/partiql-planner/src/test/kotlin/org/partiql/planner/internal/typer/PartiQLTyperTestBase.kt b/partiql-planner/src/test/kotlin/org/partiql/planner/internal/typer/PartiQLTyperTestBase.kt index 71977e7f1f..33d5c71a80 100644 --- a/partiql-planner/src/test/kotlin/org/partiql/planner/internal/typer/PartiQLTyperTestBase.kt +++ b/partiql-planner/src/test/kotlin/org/partiql/planner/internal/typer/PartiQLTyperTestBase.kt @@ -20,6 +20,7 @@ import org.partiql.types.PType import org.partiql.types.PType.Kind import org.partiql.types.StaticType import java.util.stream.Stream +import kotlin.test.assertEquals abstract class PartiQLTyperTestBase { sealed class TestResult { @@ -52,7 +53,8 @@ abstract class PartiQLTyperTestBase { private val testingPipeline: ((String, String, Catalog, PErrorListener) -> PartiQLPlanner.Result) = { query, catalog, metadata, collector -> - val parseResult = parser.parseSingle(query) + val parseResult = parser.parse(query) + assertEquals(1, parseResult.statements.size) val ast = parseResult.statements[0] val config = Context.of(collector) planner.plan(ast, session(catalog, metadata), config) diff --git a/partiql-planner/src/test/kotlin/org/partiql/planner/internal/typer/PlanTyperTestsPorted.kt b/partiql-planner/src/test/kotlin/org/partiql/planner/internal/typer/PlanTyperTestsPorted.kt index 4dcf382f3b..123a57dc71 100644 --- a/partiql-planner/src/test/kotlin/org/partiql/planner/internal/typer/PlanTyperTestsPorted.kt +++ b/partiql-planner/src/test/kotlin/org/partiql/planner/internal/typer/PlanTyperTestsPorted.kt @@ -3850,7 +3850,8 @@ internal class PlanTyperTestsPorted { session: Session, listener: PErrorListener, ): org.partiql.plan.Plan { - val parseResult = parser.parseSingle(query) + val parseResult = parser.parse(query) + assertEquals(1, parseResult.statements.size) val ast = parseResult.statements[0] val plannerConfig = Context.of(listener) return planner.plan(ast, session, plannerConfig).plan diff --git a/test/partiql-tests-runner/src/test/kotlin/org/partiql/runner/executor/EvalExecutor.kt b/test/partiql-tests-runner/src/test/kotlin/org/partiql/runner/executor/EvalExecutor.kt index 2652e8b4e4..63aabb3b27 100644 --- a/test/partiql-tests-runner/src/test/kotlin/org/partiql/runner/executor/EvalExecutor.kt +++ b/test/partiql-tests-runner/src/test/kotlin/org/partiql/runner/executor/EvalExecutor.kt @@ -31,6 +31,7 @@ import org.partiql.value.PartiQLValue import org.partiql.value.PartiQLValueExperimental import org.partiql.value.io.PartiQLValueIonReaderBuilder import org.partiql.value.toIon +import kotlin.test.assertEquals /** * @property session @@ -45,7 +46,8 @@ class EvalExecutor( override fun prepare(input: String): Statement { val listener = getErrorListener(mode) val ctx = Context.of(listener) - val parseResult = parser.parseSingle(input, ctx) + val parseResult = parser.parse(input, ctx) + assertEquals(1, parseResult.statements.size) val ast = parseResult.statements[0] val plan = planner.plan(ast, session, ctx).plan return compiler.prepare(plan, mode, ctx) @@ -189,7 +191,8 @@ class EvalExecutor( .catalog("default") .catalogs(catalog) .build() - val parseResult = parser.parseSingle("`$env`") + val parseResult = parser.parse("`$env`") + assertEquals(1, parseResult.statements.size) val stmt = parseResult.statements[0] val plan = planner.plan(stmt, session).plan return (plan.getOperation() as Query).getRex().getType().getPType() From da677e43f0efd98460b51230eb8f2d8bc96f704e Mon Sep 17 00:00:00 2001 From: John Ed Quinn Date: Fri, 1 Nov 2024 11:41:49 -0700 Subject: [PATCH 12/12] Revert "Adds a parseSingle() method for the parser" This reverts commit b94dfc4f757ebd007a2de7240017cb50c0655970. --- .../org/partiql/cli/pipeline/Pipeline.kt | 8 ++- partiql-parser/api/partiql-parser.api | 13 ++--- .../parser/PartiQLParserBuilderV1.java | 30 ++++++++++ .../org/partiql/parser/PartiQLParserV1.java | 58 +------------------ .../planner/PlannerPErrorReportingTests.kt | 4 +- 5 files changed, 46 insertions(+), 67 deletions(-) create mode 100644 partiql-parser/src/main/java/org/partiql/parser/PartiQLParserBuilderV1.java diff --git a/partiql-cli/src/main/kotlin/org/partiql/cli/pipeline/Pipeline.kt b/partiql-cli/src/main/kotlin/org/partiql/cli/pipeline/Pipeline.kt index 4d28407378..59b7ff4ba5 100644 --- a/partiql-cli/src/main/kotlin/org/partiql/cli/pipeline/Pipeline.kt +++ b/partiql-cli/src/main/kotlin/org/partiql/cli/pipeline/Pipeline.kt @@ -4,6 +4,7 @@ import org.partiql.ast.v1.Statement import org.partiql.cli.ErrorCodeString import org.partiql.eval.Mode import org.partiql.eval.compiler.PartiQLCompiler +import org.partiql.parser.PartiQLParserBuilderV1 import org.partiql.parser.PartiQLParserV1 import org.partiql.plan.Plan import org.partiql.planner.PartiQLPlanner @@ -34,7 +35,10 @@ internal class Pipeline private constructor( private fun parse(source: String): Statement { val result = listen(ctx.errorListener as AppPErrorListener) { - parser.parseSingle(source, ctx) + parser.parse(source, ctx) + } + if (result.statements.size != 1) { + throw PipelineException("Expected exactly one statement, got: ${result.statements.size}") } return result.statements[0] } @@ -79,7 +83,7 @@ internal class Pipeline private constructor( private fun create(mode: Mode, out: PrintStream, config: Config): Pipeline { val listener = config.getErrorListener(out) val ctx = Context.of(listener) - val parser = PartiQLParserV1.Builder().build() + val parser = PartiQLParserBuilderV1().build() val planner = PartiQLPlanner.builder().build() val compiler = PartiQLCompiler.builder().build() return Pipeline(parser, planner, compiler, ctx, mode) diff --git a/partiql-parser/api/partiql-parser.api b/partiql-parser/api/partiql-parser.api index 9909984863..bd84b15a30 100644 --- a/partiql-parser/api/partiql-parser.api +++ b/partiql-parser/api/partiql-parser.api @@ -16,19 +16,18 @@ public class org/partiql/parser/PartiQLParserBuilder { public fun build ()Lorg/partiql/parser/PartiQLParser; } +public class org/partiql/parser/PartiQLParserBuilderV1 { + public fun ()V + public fun build ()Lorg/partiql/parser/PartiQLParserV1; +} + public abstract interface class org/partiql/parser/PartiQLParserV1 { - public static fun builder ()Lorg/partiql/parser/PartiQLParserV1$Builder; + public static fun builder ()Lorg/partiql/parser/PartiQLParserBuilderV1; public fun parse (Ljava/lang/String;)Lorg/partiql/parser/PartiQLParserV1$Result; public abstract fun parse (Ljava/lang/String;Lorg/partiql/spi/Context;)Lorg/partiql/parser/PartiQLParserV1$Result; - public fun parseSingle (Ljava/lang/String;Lorg/partiql/spi/Context;)Lorg/partiql/parser/PartiQLParserV1$Result; public static fun standard ()Lorg/partiql/parser/PartiQLParserV1; } -public class org/partiql/parser/PartiQLParserV1$Builder { - public fun ()V - public fun build ()Lorg/partiql/parser/PartiQLParserV1; -} - public final class org/partiql/parser/PartiQLParserV1$Result { public field locations Lorg/partiql/spi/SourceLocations; public field statements Ljava/util/List; diff --git a/partiql-parser/src/main/java/org/partiql/parser/PartiQLParserBuilderV1.java b/partiql-parser/src/main/java/org/partiql/parser/PartiQLParserBuilderV1.java new file mode 100644 index 0000000000..24b8e405cb --- /dev/null +++ b/partiql-parser/src/main/java/org/partiql/parser/PartiQLParserBuilderV1.java @@ -0,0 +1,30 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. 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. + * A copy of the License is located at: + * + * http://aws.amazon.com/apache2.0/ + * + * or in the "license" file accompanying this file. This file is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific + * language governing permissions and limitations under the License. + */ + +package org.partiql.parser; + +import org.jetbrains.annotations.NotNull; +import org.partiql.parser.internal.PartiQLParserDefaultV1; + +/** + * A builder class to instantiate a [PartiQLParserV1]. https://github.com/partiql/partiql-lang-kotlin/issues/1632 + * TODO replace with Lombok builder once [PartiQLParserV1] is migrated to Java. + */ +public class PartiQLParserBuilderV1 { + + @NotNull + public PartiQLParserV1 build() { + return new PartiQLParserDefaultV1(); + } +} diff --git a/partiql-parser/src/main/java/org/partiql/parser/PartiQLParserV1.java b/partiql-parser/src/main/java/org/partiql/parser/PartiQLParserV1.java index fcb03616dd..d16acc2677 100644 --- a/partiql-parser/src/main/java/org/partiql/parser/PartiQLParserV1.java +++ b/partiql-parser/src/main/java/org/partiql/parser/PartiQLParserV1.java @@ -18,17 +18,10 @@ import org.partiql.ast.v1.Statement; import org.partiql.parser.internal.PartiQLParserDefaultV1; import org.partiql.spi.Context; -import org.partiql.spi.SourceLocation; import org.partiql.spi.SourceLocations; -import org.partiql.spi.errors.PError; -import org.partiql.spi.errors.PErrorKind; import org.partiql.spi.errors.PErrorListenerException; -import org.partiql.spi.errors.Severity; -import java.util.ArrayList; -import java.util.HashMap; import java.util.List; -import java.util.Map; /** * TODO: Rename to PartiQLParser @@ -41,40 +34,10 @@ public interface PartiQLParserV1 { * @param ctx a configuration object for the parser * @throws PErrorListenerException when the [org.partiql.spi.errors.PErrorListener] defined in the [ctx] throws an * [PErrorListenerException], this method halts execution and propagates the exception. - * @see PartiQLParserV1#parseSingle(String, Context) */ @NotNull Result parse(@NotNull String source, @NotNull Context ctx) throws PErrorListenerException; - /** - * TODO - * @param source TODO - * @param ctx TODO - * @return TODO - * @throws PErrorListenerException TODO - * @see PartiQLParserV1#parse(String, Context) - */ - @NotNull - default Result parseSingle(@NotNull String source, @NotNull Context ctx) throws PErrorListenerException { - Result result = parse(source, ctx); - if (result.statements.size() != 1) { - SourceLocation location; - if (result.statements.size() > 1) { - location = result.locations.get(result.statements.get(1).tag); - } else { - location = null; - } - Map properties = new HashMap() {{ - put("EXPECTED_TOKENS", new ArrayList() {{ - add("EOF"); - }}); - }}; - PError pError = new PError(PError.UNEXPECTED_TOKEN, Severity.ERROR(), PErrorKind.SYNTAX(), location, properties); - ctx.getErrorListener().report(pError); - } - return result; - } - /** * Parses the [source] into an AST. * @param source the user's input @@ -119,8 +82,8 @@ public Result(@NotNull List statements, @NotNull SourceLocations loca * @return TODO */ @NotNull - public static Builder builder() { - return new Builder(); + public static PartiQLParserBuilderV1 builder() { + return new PartiQLParserBuilderV1(); } /** @@ -131,21 +94,4 @@ public static Builder builder() { public static PartiQLParserV1 standard() { return new PartiQLParserDefaultV1(); } - - /** - * A builder class to instantiate a {@link PartiQLParserV1}. - */ - public class Builder { - // TODO: Can this be replaced with Lombok? - // TODO: https://github.com/partiql/partiql-lang-kotlin/issues/1632 - - /** - * TODO - * @return TODO - */ - @NotNull - public PartiQLParserV1 build() { - return new PartiQLParserDefaultV1(); - } - } } diff --git a/partiql-planner/src/test/kotlin/org/partiql/planner/PlannerPErrorReportingTests.kt b/partiql-planner/src/test/kotlin/org/partiql/planner/PlannerPErrorReportingTests.kt index 969a75ffb9..ba609f886b 100644 --- a/partiql-planner/src/test/kotlin/org/partiql/planner/PlannerPErrorReportingTests.kt +++ b/partiql-planner/src/test/kotlin/org/partiql/planner/PlannerPErrorReportingTests.kt @@ -3,7 +3,7 @@ package org.partiql.planner import org.junit.jupiter.params.ParameterizedTest import org.junit.jupiter.params.provider.MethodSource import org.partiql.ast.v1.Statement -import org.partiql.parser.PartiQLParserV1 +import org.partiql.parser.PartiQLParserBuilderV1 import org.partiql.plan.Operation import org.partiql.planner.internal.typer.CompilerType import org.partiql.planner.internal.typer.PlanTyper.Companion.toCType @@ -42,7 +42,7 @@ internal class PlannerPErrorReportingTests { .catalogs(catalog) .build() - private val parser = PartiQLParserV1.Builder().build() + private val parser = PartiQLParserBuilderV1().build() private val statement: ((String) -> Statement) = { query -> val parseResult = parser.parse(query)