diff --git a/pig-runtime/src/org/partiql/pig/runtime/BoolPrimitive.kt b/pig-runtime/src/org/partiql/pig/runtime/BoolPrimitive.kt new file mode 100644 index 0000000..cdd36e3 --- /dev/null +++ b/pig-runtime/src/org/partiql/pig/runtime/BoolPrimitive.kt @@ -0,0 +1,63 @@ +/* + * 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://www.apache.org/licenses/LICENSE-2.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.pig.runtime + +import com.amazon.ionelement.api.IonElement +import com.amazon.ionelement.api.MetaContainer +import com.amazon.ionelement.api.ionBool +import com.amazon.ionelement.api.ionInt +import com.amazon.ionelement.api.metaContainerOf + +/** + * Represents a boolean value that is part of a generated type domain. + * + * This is needed to allow such values to have metas. + */ +class BoolPrimitive(val value: Boolean, override val metas: MetaContainer) : DomainNode { + + /** Creates a copy of the current node with the specified values. */ + fun copy(value: Boolean = this.value, metas: MetaContainer = this.metas): BoolPrimitive = + BoolPrimitive(value, metas) + + /** Creates a copy of the current [LongPrimitive] with [newMetas] as the new metas. */ + override fun copyMetas(newMetas: MetaContainer): BoolPrimitive = + BoolPrimitive(value, newMetas) + + /** Creates a copy of the current [LongPrimitive] with the specified additional meta. */ + override fun withMeta(metaKey: String, metaValue: Any): BoolPrimitive = + BoolPrimitive(this.value, metas + metaContainerOf(metaKey to metaValue)) + + /** Creates an `IonElement` representation of the current [LongPrimitive]. */ + override fun toIonElement(): IonElement = ionBool(value, metas = metas) + + /** Converts [value] to a string. */ + override fun toString(): String = value.toString() + + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (javaClass != other?.javaClass) return false + + other as BoolPrimitive + + if (value != other.value) return false + + return true + } + + override fun hashCode(): Int { + return value.hashCode() + } +} \ No newline at end of file diff --git a/pig-runtime/src/org/partiql/pig/runtime/DomainVisitorBase.kt b/pig-runtime/src/org/partiql/pig/runtime/DomainVisitorBase.kt index f4da7b8..4cc9519 100644 --- a/pig-runtime/src/org/partiql/pig/runtime/DomainVisitorBase.kt +++ b/pig-runtime/src/org/partiql/pig/runtime/DomainVisitorBase.kt @@ -20,6 +20,10 @@ import com.amazon.ionelement.api.MetaContainer open class DomainVisitorBase { + protected open fun visitBoolPrimitive(node: BoolPrimitive) { + // default does nothing + } + protected open fun visitLongPrimitive(node: LongPrimitive) { // default does nothing } @@ -38,6 +42,11 @@ open class DomainVisitorBase { /////////////////////////////////////////////////////// + open fun walkBoolPrimitive(node: BoolPrimitive) { + visitBoolPrimitive(node) + walkMetas(node.metas) + } + open fun walkLongPrimitive(node: LongPrimitive) { visitLongPrimitive(node) walkMetas(node.metas) diff --git a/pig-runtime/src/org/partiql/pig/runtime/DomainVisitorFoldBase.kt b/pig-runtime/src/org/partiql/pig/runtime/DomainVisitorFoldBase.kt index 96b078d..cd01e0a 100644 --- a/pig-runtime/src/org/partiql/pig/runtime/DomainVisitorFoldBase.kt +++ b/pig-runtime/src/org/partiql/pig/runtime/DomainVisitorFoldBase.kt @@ -20,6 +20,10 @@ import com.amazon.ionelement.api.MetaContainer open class DomainVisitorFoldBase { + protected open fun visitBoolPrimitive(node: BoolPrimitive, accumulator: T): T = + // default does nothing + + accumulator protected open fun visitLongPrimitive(node: LongPrimitive, accumulator: T): T = // default does nothing accumulator @@ -38,6 +42,12 @@ open class DomainVisitorFoldBase { /////////////////////////////////////////////////////// + open fun walkBoolPrimitive(node: BoolPrimitive, accumulator: T): T { + var current = accumulator + current = visitBoolPrimitive(node, current) + return walkMetas(node.metas, current) + } + open fun walkLongPrimitive(node: LongPrimitive, accumulator: T): T { var current = accumulator current = visitLongPrimitive(node, current) diff --git a/pig-runtime/src/org/partiql/pig/runtime/DomainVisitorTransformBase.kt b/pig-runtime/src/org/partiql/pig/runtime/DomainVisitorTransformBase.kt index 80e71cb..3c4d752 100644 --- a/pig-runtime/src/org/partiql/pig/runtime/DomainVisitorTransformBase.kt +++ b/pig-runtime/src/org/partiql/pig/runtime/DomainVisitorTransformBase.kt @@ -63,5 +63,19 @@ abstract class DomainVisitorTransformBase { open fun transformLongPrimitiveMetas(sym: LongPrimitive) = transformMetas(sym.metas) + open fun transformBoolPrimitive(b: BoolPrimitive): BoolPrimitive { + val newValue = transformBoolPrimitiveValue(b) + val newMetas = transformBoolPrimitiveMetas(b) + return if(b.value != newValue || b.metas !== newMetas) { + BoolPrimitive(newValue, newMetas) + } else { + b + } + } + + open fun transformBoolPrimitiveValue(sym: BoolPrimitive): Boolean = sym.value + + open fun transformBoolPrimitiveMetas(sym: BoolPrimitive) = transformMetas(sym.metas) + } diff --git a/pig-runtime/src/org/partiql/pig/runtime/PrimitiveUtils.kt b/pig-runtime/src/org/partiql/pig/runtime/PrimitiveUtils.kt index 1d49b28..0183a54 100644 --- a/pig-runtime/src/org/partiql/pig/runtime/PrimitiveUtils.kt +++ b/pig-runtime/src/org/partiql/pig/runtime/PrimitiveUtils.kt @@ -20,12 +20,18 @@ import com.amazon.ionelement.api.MetaContainer import com.amazon.ionelement.api.emptyMetaContainer +fun AnyElement.toBoolPrimitive() = + BoolPrimitive(this.booleanValue, this.metas) + fun AnyElement.toLongPrimitive() = LongPrimitive(this.longValue, this.metas) fun AnyElement.toSymbolPrimitive() = SymbolPrimitive(this.symbolValue, this.metas) +fun Boolean.asPrimitive(metas: MetaContainer = emptyMetaContainer()) = + BoolPrimitive(this, metas) + fun Long.asPrimitive(metas: MetaContainer = emptyMetaContainer()) = LongPrimitive(this, metas) diff --git a/pig-tests/src/org/partiql/pig/tests/generated/sample-universe.kt b/pig-tests/src/org/partiql/pig/tests/generated/sample-universe.kt index 75b0424..c4680b1 100644 --- a/pig-tests/src/org/partiql/pig/tests/generated/sample-universe.kt +++ b/pig-tests/src/org/partiql/pig/tests/generated/sample-universe.kt @@ -31,6 +31,29 @@ class TestDomain private constructor() { interface Builder { // Tuples + /** + * Creates an instance of [TestDomain.BoolPair]. + */ + fun boolPair( + first: Boolean, + second: Boolean, + metas: MetaContainer = emptyMetaContainer() + ): TestDomain.BoolPair + + /** + * Creates an instance of [TestDomain.BoolPair]. + * + * Use this variant when metas must be passed to primitive child elements. + * + * (The "_" suffix is needed to work-around conflicts due to type erasure and ambiguities with null arguments.) + */ + fun boolPair_( + first: org.partiql.pig.runtime.BoolPrimitive, + second: org.partiql.pig.runtime.BoolPrimitive, + metas: MetaContainer = emptyMetaContainer() + ): TestDomain.BoolPair + + /** * Creates an instance of [TestDomain.IntPair]. */ @@ -735,6 +758,27 @@ class TestDomain private constructor() { private object TestDomainBuilder : Builder { // Tuples + override fun boolPair( + first: Boolean, + second: Boolean, + metas: MetaContainer + ): TestDomain.BoolPair = + TestDomain.BoolPair( + first = first.asPrimitive(), + second = second.asPrimitive(), + metas = metas) + + override fun boolPair_( + first: org.partiql.pig.runtime.BoolPrimitive, + second: org.partiql.pig.runtime.BoolPrimitive, + metas: MetaContainer + ): TestDomain.BoolPair = + TestDomain.BoolPair( + first = first, + second = second, + metas = metas) + + override fun intPair( first: Long, second: Long, @@ -1399,6 +1443,63 @@ class TestDomain private constructor() { ///////////////////////////////////////////////////////////////////////////// // Tuple Types ///////////////////////////////////////////////////////////////////////////// + class BoolPair( + val first: org.partiql.pig.runtime.BoolPrimitive, + val second: org.partiql.pig.runtime.BoolPrimitive, + override val metas: MetaContainer = emptyMetaContainer() + ): TestDomainNode() { + + override fun copyMetas(newMetas: MetaContainer): BoolPair = + BoolPair( + first = first, + second = second, + metas = newMetas) + + override fun withMeta(metaKey: String, metaValue: Any): BoolPair = + BoolPair( + first = first, + second = second, + metas = metas + metaContainerOf(metaKey to metaValue)) + + override fun toIonElement(): SexpElement { + val elements = ionSexpOf( + ionSymbol("bool_pair"), + first.toIonElement(), + second.toIonElement(), + metas = metas) + return elements + } + + fun copy( + first: org.partiql.pig.runtime.BoolPrimitive = this.first, + second: org.partiql.pig.runtime.BoolPrimitive = this.second, + metas: MetaContainer = this.metas + ) = + BoolPair( + first, + second, + metas) + + override fun equals(other: Any?): Boolean { + if (other == null) return false + if (this === other) return true + if (other.javaClass != BoolPair::class.java) return false + + other as BoolPair + if (first != other.first) return false + if (second != other.second) return false + return true + } + + private val myHashCode by lazy(LazyThreadSafetyMode.NONE) { + var hc = first.hashCode() + hc = 31 * hc + second.hashCode() + hc + } + + override fun hashCode(): Int = myHashCode + } + class IntPair( val first: org.partiql.pig.runtime.LongPrimitive, val second: org.partiql.pig.runtime.LongPrimitive, @@ -3338,6 +3439,15 @@ class TestDomain private constructor() { ////////////////////////////////////// // Tuple Types ////////////////////////////////////// + "bool_pair" -> { + sexp.requireArityOrMalformed(IntRange(2, 2)) + val first = sexp.getRequired(0).toBoolPrimitive() + val second = sexp.getRequired(1).toBoolPrimitive() + TestDomain.BoolPair( + first, + second, + metas = sexp.metas) + } "int_pair" -> { sexp.requireArityOrMalformed(IntRange(2, 2)) val first = sexp.getRequired(0).toLongPrimitive() @@ -3658,6 +3768,7 @@ class TestDomain private constructor() { ////////////////////////////////////// // Tuple Types ////////////////////////////////////// + open fun visitBoolPair(node: TestDomain.BoolPair) { } open fun visitIntPair(node: TestDomain.IntPair) { } open fun visitSymbolPair(node: TestDomain.SymbolPair) { } open fun visitIonPair(node: TestDomain.IonPair) { } @@ -3716,6 +3827,13 @@ class TestDomain private constructor() { ////////////////////////////////////// // Tuple Types ////////////////////////////////////// + open fun walkBoolPair(node: TestDomain.BoolPair) { + visitBoolPair(node) + walkBoolPrimitive(node.first) + walkBoolPrimitive(node.second) + walkMetas(node.metas) + } + open fun walkIntPair(node: TestDomain.IntPair) { visitIntPair(node) walkLongPrimitive(node.first) @@ -4005,6 +4123,7 @@ class TestDomain private constructor() { ////////////////////////////////////// // Tuple Types ////////////////////////////////////// + open protected fun visitBoolPair(node: TestDomain.BoolPair, accumulator: T): T = accumulator open protected fun visitIntPair(node: TestDomain.IntPair, accumulator: T): T = accumulator open protected fun visitSymbolPair(node: TestDomain.SymbolPair, accumulator: T): T = accumulator open protected fun visitIonPair(node: TestDomain.IonPair, accumulator: T): T = accumulator @@ -4063,6 +4182,15 @@ class TestDomain private constructor() { ////////////////////////////////////// // Tuple Types ////////////////////////////////////// + open fun walkBoolPair(node: TestDomain.BoolPair, accumulator: T): T { + var current = accumulator + current = visitBoolPair(node, current) + current = walkBoolPrimitive(node.first, current) + current = walkBoolPrimitive(node.second, current) + current = walkMetas(node.metas, current) + return current + } + open fun walkIntPair(node: TestDomain.IntPair, accumulator: T): T { var current = accumulator current = visitIntPair(node, current) @@ -4414,6 +4542,32 @@ class TestDomain private constructor() { ////////////////////////////////////// // Tuple Types ////////////////////////////////////// + // Tuple BoolPair + open fun transformBoolPair(node: TestDomain.BoolPair): TestDomain.BoolPair { + val new_first = transformBoolPair_first(node) + val new_second = transformBoolPair_second(node) + val new_metas = transformBoolPair_metas(node) + return if ( + node.first !== new_first || + node.second !== new_second || + node.metas !== new_metas + ) { + TestDomain.BoolPair( + first = new_first, + second = new_second, + metas = new_metas + ) + } else { + node + } + } + open fun transformBoolPair_first(node: TestDomain.BoolPair) = + transformBoolPrimitive(node.first) + open fun transformBoolPair_second(node: TestDomain.BoolPair) = + transformBoolPrimitive(node.second) + open fun transformBoolPair_metas(node: TestDomain.BoolPair) = + transformMetas(node.metas) + // Tuple IntPair open fun transformIntPair(node: TestDomain.IntPair): TestDomain.IntPair { val new_first = transformIntPair_first(node) diff --git a/pig-tests/test/org/partiql/pig/tests/EqualityTests.kt b/pig-tests/test/org/partiql/pig/tests/EqualityTests.kt index 8f473bf..9d14710 100644 --- a/pig-tests/test/org/partiql/pig/tests/EqualityTests.kt +++ b/pig-tests/test/org/partiql/pig/tests/EqualityTests.kt @@ -50,10 +50,32 @@ class EqualityTests { data class TestCase(val left: TestDomain.TestDomainNode, val right: TestDomain.TestDomainNode, val isEqual: Boolean) class EqualityTestsArgumentsProvider: ArgumentsProviderBase() { + + @Suppress("BooleanLiteralArgument") override fun getParameters(): List = listOf( ///////////////////////////////////////////////// // Products ///////////////////////////////////////////////// + TestCase( + TestDomain.build { boolPair(true, true) }, + TestDomain.build { boolPair(true, true) }, + isEqual = true), + + TestCase( + TestDomain.build { boolPair(true, true) }, + TestDomain.build { boolPair(true, false) }, + isEqual = false), + + TestCase( + TestDomain.build { boolPair(false, true) }, + TestDomain.build { boolPair(true, false) }, + isEqual = false), + + TestCase( + TestDomain.build { boolPair(true, false) }, + TestDomain.build { boolPair(false, true) }, + isEqual = false), + TestCase( TestDomain.build { intPair(1, 1) }, TestDomain.build { intPair(1, 1) }, diff --git a/pig-tests/test/org/partiql/pig/tests/IonElementTransformerTests.kt b/pig-tests/test/org/partiql/pig/tests/IonElementTransformerTests.kt index 88073eb..a50a281 100644 --- a/pig-tests/test/org/partiql/pig/tests/IonElementTransformerTests.kt +++ b/pig-tests/test/org/partiql/pig/tests/IonElementTransformerTests.kt @@ -87,7 +87,24 @@ class IonElementTransformerTests { @ArgumentsSource(AllElementTypesTestArgumentProvider::class) fun allElementTypesTest(tc: TestCase) = runTestCase(tc) class AllElementTypesTestArgumentProvider: ArgumentsProviderBase() { + @Suppress("BooleanLiteralArgument") override fun getParameters(): List = listOf( + TestCase( + TestDomain.build { boolPair(true, true) }, + "(bool_pair true true)" + ), + TestCase( + TestDomain.build { boolPair(true, false) }, + "(bool_pair true false)" + ), + TestCase( + TestDomain.build { boolPair(false, true) }, + "(bool_pair false true)" + ), + TestCase( + TestDomain.build { boolPair(false, false) }, + "(bool_pair false false)" + ), TestCase( TestDomain.build { intPair(1, 2) }, "(int_pair 1 2)" diff --git a/pig-tests/type-domains/sample-universe.ion b/pig-tests/type-domains/sample-universe.ion index 327e9be..c6dd196 100644 --- a/pig-tests/type-domains/sample-universe.ion +++ b/pig-tests/type-domains/sample-universe.ion @@ -25,6 +25,7 @@ (define test_domain (domain + (product bool_pair first::bool second::bool) (product int_pair first::int second::int) (product symbol_pair first::symbol second::symbol) (product ion_pair first::ion second::ion) diff --git a/pig/src/org/partiql/pig/domain/model/DataType.kt b/pig/src/org/partiql/pig/domain/model/DataType.kt index 2e317d9..cc66894 100644 --- a/pig/src/org/partiql/pig/domain/model/DataType.kt +++ b/pig/src/org/partiql/pig/domain/model/DataType.kt @@ -68,6 +68,18 @@ sealed class DataType { override val metas: MetaContainer get() = emptyMetaContainer() } + /** + * Represents the equivalent of an Ion `bool` in the target language. + * + * This is one of pig's "primitive" types. + */ + object Bool : DataType() { + override val isPrimitive: Boolean get() = true + override val isBuiltin: Boolean get() = true + override val tag: String get() = "bool" + override val metas: MetaContainer get() = emptyMetaContainer() + } + /** * Represents the equivalent of an Ion `int` in the target language. * This is one of pig's "primitive" types. diff --git a/pig/src/org/partiql/pig/domain/model/Statement.kt b/pig/src/org/partiql/pig/domain/model/Statement.kt index a5ea17b..a7764df 100644 --- a/pig/src/org/partiql/pig/domain/model/Statement.kt +++ b/pig/src/org/partiql/pig/domain/model/Statement.kt @@ -40,7 +40,7 @@ class TypeDomain( ): Statement() { /** All data types. (User types + primitives). */ - val types: List = listOf(DataType.Int, DataType.Symbol, DataType.Ion) + userTypes + val types: List = listOf(DataType.Bool, DataType.Int, DataType.Symbol, DataType.Ion) + userTypes fun resolveTypeRef(typeRef: TypeRef) = /** diff --git a/pig/src/org/partiql/pig/domain/model/TypeDomainSemanticChecker.kt b/pig/src/org/partiql/pig/domain/model/TypeDomainSemanticChecker.kt index c4903e0..43bbfc8 100644 --- a/pig/src/org/partiql/pig/domain/model/TypeDomainSemanticChecker.kt +++ b/pig/src/org/partiql/pig/domain/model/TypeDomainSemanticChecker.kt @@ -69,10 +69,11 @@ private class TypeDomainSemanticChecker(private val typeDomain: TypeDomain) { } DataType.Ion, DataType.Int, + DataType.Bool, DataType.Symbol -> { /* do nothing, these are always valid */ } - } + }.let{} } } @@ -126,10 +127,11 @@ private class TypeDomainSemanticChecker(private val typeDomain: TypeDomain) { } DataType.Ion, DataType.Int, + DataType.Bool, DataType.Symbol -> { /* do nothing, these are always valid */ } - } + }.let {} } } diff --git a/pig/src/org/partiql/pig/generator/kotlin/KTypeDomainConverter.kt b/pig/src/org/partiql/pig/generator/kotlin/KTypeDomainConverter.kt index fbfb826..433558f 100644 --- a/pig/src/org/partiql/pig/generator/kotlin/KTypeDomainConverter.kt +++ b/pig/src/org/partiql/pig/generator/kotlin/KTypeDomainConverter.kt @@ -359,6 +359,7 @@ private class KTypeDomainConverter( when (typeDomain.resolveTypeRef(typeRef)) { DataType.Ion -> "" DataType.Int -> ".toLongPrimitive()" + DataType.Bool -> ".toBoolPrimitive()" DataType.Symbol -> ".toSymbolPrimitive()" is DataType.UserType.Tuple, is DataType.UserType.Sum -> ".transformExpect<${typeRef.typeName.snakeToPascalCase()}>()" @@ -377,6 +378,7 @@ private class KTypeDomainConverter( return when (typeName) { "ion" -> "com.amazon.ionelement.api." + if(useAnyElement) "AnyElement" else "IonElement" "int" -> if (kotlinPrimitives) "Long" else "org.partiql.pig.runtime.LongPrimitive" + "bool" -> if (kotlinPrimitives) "Boolean" else "org.partiql.pig.runtime.BoolPrimitive" "symbol" -> if (kotlinPrimitives) "String" else "org.partiql.pig.runtime.SymbolPrimitive" else -> this.typeName.snakeToPascalCase() } @@ -387,6 +389,7 @@ private class KTypeDomainConverter( get() { return when (typeName) { "ion" -> "AnyElement" + "bool" -> "BoolPrimitive" "int" -> "LongPrimitive" "symbol" -> "SymbolPrimitive" else -> this.typeName.snakeToPascalCase() diff --git a/pig/test/org/partiql/pig/domain/Util.kt b/pig/test/org/partiql/pig/domain/Util.kt index 6bff621..cba249f 100644 --- a/pig/test/org/partiql/pig/domain/Util.kt +++ b/pig/test/org/partiql/pig/domain/Util.kt @@ -97,6 +97,7 @@ fun PermutedSum.toIonElement(): IonElement = fun DataType.toIonElement(includeTypeTag: Boolean): IonElement = when(this) { DataType.Ion -> ionSymbol("ion") + DataType.Bool -> ionSymbol("bool") DataType.Int -> ionSymbol("int") DataType.Symbol -> ionSymbol("symbol") is DataType.UserType.Tuple ->