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..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, @@ -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 { @@ -80,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-cli/src/main/kotlin/org/partiql/cli/shell/ShellHighlighter.kt b/partiql-cli/src/main/kotlin/org/partiql/cli/shell/ShellHighlighter.kt index 48b73fd6d3..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.root() + parser.statements() } 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 413d3c385a..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 @@ -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 /** @@ -1307,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() /** @@ -1320,7 +1321,9 @@ class PartiQLEvaluatorTest { ) internal fun assert() { - val statement = parser.parse(input).root + val parseResult = parser.parse(input) + assertEquals(1, parseResult.statements.size) + val statement = parseResult.statements[0] val catalog = Catalog.builder() .name("memory") .apply { @@ -1373,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() { @@ -1406,7 +1409,9 @@ class PartiQLEvaluatorTest { } private fun run(mode: Mode): Pair { - val statement = parser.parse(input).root + 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() .catalog("memory") diff --git a/partiql-parser/api/partiql-parser.api b/partiql-parser/api/partiql-parser.api index 1cc5585abe..bd84b15a30 100644 --- a/partiql-parser/api/partiql-parser.api +++ b/partiql-parser/api/partiql-parser.api @@ -1,147 +1,36 @@ 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 final class org/partiql/parser/PartiQLParser$Result$Companion { + public field locations Lorg/partiql/spi/SourceLocations; + public field statements Ljava/util/List; + public fun (Ljava/util/List;Lorg/partiql/spi/SourceLocations;)V } -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 final class org/partiql/parser/SourceLocation$Companion { - public final fun getUNKNOWN ()Lorg/partiql/parser/SourceLocation; + public fun build ()Lorg/partiql/parser/PartiQLParser; } -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 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 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 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 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 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 class org/partiql/parser/PartiQLParserBuilderV1 { + public fun ()V + public fun build ()Lorg/partiql/parser/PartiQLParserV1; } -public final class org/partiql/parser/V1PartiQLParser$Result$Companion { +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/V1PartiQLParserBuilder { - public fun ()V - public final fun build ()Lorg/partiql/parser/V1PartiQLParser; +public final class org/partiql/parser/PartiQLParserV1$Result { + public field locations Lorg/partiql/spi/SourceLocations; + public field statements Ljava/util/List; + public fun (Ljava/util/List;Lorg/partiql/spi/SourceLocations;)V } diff --git a/partiql-parser/src/main/antlr/PartiQLParser.g4 b/partiql-parser/src/main/antlr/PartiQLParser.g4 index f4f7cb8fb5..358ad0c770 100644 --- a/partiql-parser/src/main/antlr/PartiQLParser.g4 +++ b/partiql-parser/src/main/antlr/PartiQLParser.g4 @@ -11,14 +11,17 @@ options { * */ -root - : (EXPLAIN (PAREN_LEFT explainOption (COMMA explainOption)* PAREN_RIGHT)? )? statement; +statements + : statement EOF + | 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/java/org/partiql/parser/PartiQLParser.java b/partiql-parser/src/main/java/org/partiql/parser/PartiQLParser.java new file mode 100644 index 0000000000..ef85820992 --- /dev/null +++ b/partiql-parser/src/main/java/org/partiql/parser/PartiQLParser.java @@ -0,0 +1,97 @@ +/* + * 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.SourceLocations; +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. + */ + @NotNull + 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 + */ + @NotNull + public static PartiQLParserBuilder builder() { + return new PartiQLParserBuilder(); + } + + /** + * TODO + * @return TODO + */ + @NotNull + 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/kotlin/org/partiql/parser/V1PartiQLParserBuilder.kt b/partiql-parser/src/main/java/org/partiql/parser/PartiQLParserBuilderV1.java similarity index 63% 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..24b8e405cb 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,19 @@ * language governing permissions and limitations under the License. */ -package org.partiql.parser +package org.partiql.parser; -import org.partiql.parser.internal.V1PartiQLParserDefault +import org.jetbrains.annotations.NotNull; +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() + @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 new file mode 100644 index 0000000000..d16acc2677 --- /dev/null +++ b/partiql-parser/src/main/java/org/partiql/parser/PartiQLParserV1.java @@ -0,0 +1,97 @@ +/* + * 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.SourceLocations; +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. + */ + @NotNull + 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 + */ + @NotNull + public static PartiQLParserBuilderV1 builder() { + return new PartiQLParserBuilderV1(); + } + + /** + * TODO + * @return TODO + */ + @NotNull + public static PartiQLParserV1 standard() { + return new PartiQLParserDefaultV1(); + } +} 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/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/PartiQLParserDefault.kt b/partiql-parser/src/main/kotlin/org/partiql/parser/internal/PartiQLParserDefault.kt index ffac0ce607..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 @@ -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.SourceLocations 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() + return PartiQLParser.Result(listOf(Statement.Query(Expr.Lit(nullValue()))), locations) } } @@ -268,8 +270,8 @@ internal class PartiQLParserDefault : PartiQLParser { PredictionMode.LL -> parser.addErrorListener(ParseErrorListener(listener)) else -> throw IllegalArgumentException("Unsupported parser mode: $mode") } - val tree = parser.root() - return Visitor.translate(source, tokens, tree) + val tree = parser.statements() + return Visitor.translate(tokens, tree) } private fun createTokenStream(source: String, listener: PErrorListener): CountingTokenStream { @@ -378,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() { @@ -390,18 +392,15 @@ 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.RootContext, + tree: GeneratedParser.StatementsContext, ): PartiQLParser.Result { - val locations = SourceLocations.Mutable() + val locations = mutableMapOf() val visitor = Visitor(tokens, locations, tokens.parameterIndexes) - val root = visitor.visitAs(tree) as Statement - return PartiQLParser.Result( - source = source, - root = root, - locations = locations.toMap(), - ) + val statements = tree.statement().map { statementCtx -> + visitor.visit(statementCtx) as Statement + } + return PartiQLParser.Result(statements, SourceLocations(locations)) } fun error( @@ -415,10 +414,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 +430,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 +448,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 @@ -472,36 +468,35 @@ 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 + ) + ) } /** 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 96% 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 0caa5228cc..e861073bc1 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,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.PartiQLParserV1 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 @@ -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() + 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,8 +251,8 @@ internal class V1PartiQLParserDefault : V1PartiQLParser { PredictionMode.LL -> parser.addErrorListener(ParseErrorListener(listener)) else -> throw IllegalArgumentException("Unsupported parser mode: $mode") } - val tree = parser.root() - return Visitor.translate(source, tokens, tree) + val tree = parser.statements() + return Visitor.translate(tokens, tree) } private fun createTokenStream(source: String, listener: PErrorListener): CountingTokenStream { @@ -349,7 +354,7 @@ internal class V1PartiQLParserDefault : V1PartiQLParser { @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() { @@ -361,17 +366,17 @@ internal class V1PartiQLParserDefault : V1PartiQLParser { * Expose an (internal) friendly entry point into the traversal; mostly for keeping mutable state contained. */ fun translate( - source: String, tokens: CountingTokenStream, - tree: GeneratedParser.RootContext, - ): V1PartiQLParser.Result { - val locations = SourceLocations.Mutable() + tree: PartiQLParser.StatementsContext, + ): PartiQLParserV1.Result { + val locations = mutableMapOf() 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 statements = tree.statement().map { statementCtx -> + visitor.visit(statementCtx) as Statement + } + return PartiQLParserV1.Result( + statements, + SourceLocations(locations), ) } @@ -386,10 +391,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 +407,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 +425,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 @@ -443,37 +445,32 @@ 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, + ) } /** 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/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-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..d288ea6fa0 --- /dev/null +++ b/partiql-parser/src/test/kotlin/org/partiql/parser/internal/SemiColonTests.kt @@ -0,0 +1,75 @@ +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() + } + + // TODO: This is not completely settled, however, we can always allow for this in the future. + @Test + fun t2() { + val tc = ParserTestCaseSimple( + "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() + } + + @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() + } +} 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..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,7 +76,9 @@ class PlanTest { ) .namespace("SCHEMA") .build() - val ast = V1PartiQLParser.standard().parse(test.statement).root + 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 8747b0f537..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,10 +42,12 @@ internal class PlannerPErrorReportingTests { .catalogs(catalog) .build() - private val parser = V1PartiQLParserBuilder().build() + private val parser = PartiQLParserBuilderV1().build() private val statement: ((String) -> Statement) = { query -> - parser.parse(query).root + val parseResult = parser.parse(query) + assertEquals(1, parseResult.statements.size) + 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..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() } @@ -38,7 +38,9 @@ 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) + 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 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..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 @@ -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 { @@ -37,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 -> @@ -52,7 +53,9 @@ 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) + 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 a5340499bd..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, _ -> @@ -3850,7 +3850,9 @@ internal class PlanTyperTestsPorted { session: Session, listener: PErrorListener, ): org.partiql.plan.Plan { - val ast = parser.parse(query).root + 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/partiql-spi/api/partiql-spi.api b/partiql-spi/api/partiql-spi.api index 3e6b55e2a6..4b0f7aa022 100644 --- a/partiql-spi/api/partiql-spi.api +++ b/partiql-spi/api/partiql-spi.api @@ -41,11 +41,32 @@ 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 } +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-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/partiql-spi/src/main/java/org/partiql/spi/SourceLocations.java b/partiql-spi/src/main/java/org/partiql/spi/SourceLocations.java new file mode 100644 index 0000000000..e70820cd8b --- /dev/null +++ b/partiql-spi/src/main/java/org/partiql/spi/SourceLocations.java @@ -0,0 +1,101 @@ +package org.partiql.spi; + +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.Collection; +import java.util.Map; +import java.util.Set; + +/** + * 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; + + /** + * 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 + @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) { + throw new UnsupportedOperationException(); + } + + @Override + public SourceLocation remove(Object key) { + throw new UnsupportedOperationException(); + } + + @Override + public void putAll(@NotNull Map m) { + throw new UnsupportedOperationException(); + } + + @Override + public void clear() { + throw new UnsupportedOperationException(); + } + + @Override + public boolean isEmpty() { + return delegate.isEmpty(); + } +} + 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..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" @@ -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..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 @@ -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,9 @@ 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) + assertEquals(1, parseResult.statements.size) + val ast = parseResult.statements[0] val plan = planner.plan(ast, session, ctx).plan return compiler.prepare(plan, mode, ctx) } @@ -140,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() @@ -188,7 +191,9 @@ class EvalExecutor( .catalog("default") .catalogs(catalog) .build() - val stmt = parser.parse("`$env`").root + 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() }