![JAR search and dependency download from the Maven repository](/logo.png)
org.partiql.lang.eval.binding.LocalsBinder.kt Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of partiql-lang-kotlin Show documentation
Show all versions of partiql-lang-kotlin Show documentation
An implementation of PartiQL for the JVM written in Kotlin.
/*
* Copyright 2019 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.lang.eval.binding
import org.partiql.lang.eval.BindingCase
import org.partiql.lang.eval.BindingName
import org.partiql.lang.eval.Bindings
import org.partiql.lang.eval.ExprValue
import org.partiql.lang.eval.address
import org.partiql.lang.eval.name
import org.partiql.lang.util.errAmbiguousBinding
/**
* Creates a list of bindings from a list of locals.
* The various implementations (such as List.[localsBinder]) will assign names to the locals.
* Think of this as a factory which precomputes the name-bindings map for a list of locals.
*/
abstract class LocalsBinder {
fun bindLocals(locals: List): Bindings {
return object : Bindings {
override fun get(bindingName: BindingName): ExprValue? = binderForName(bindingName)(locals)
}
}
/** This method is the backbone of [bindLocals] and should be used when optimizing lookups. */
abstract fun binderForName(bindingName: BindingName): (List) -> ExprValue?
}
/** Sources can be aliased to names with 'AS', 'AT' or 'BY' */
data class Alias(val asName: String, val atName: String?, val byName: String?)
/**
* Returns a [LocalsBinder] for the bindings specified in the [Alias] ('AS' and optionally 'AT').
* Each local [ExprValue] will be bound to the [Alias] with the same ordinal.
*
* For example, `[(as: 'x'), (as: 'y', at: 'z')].localsBinder.bindLocals(a, b)` will return the [Bindings]
* `x => a, y => b, z => b.name`.
*
* A name can resolve to 0,1,.. bindings. For these cases:
* * 0 could be optimized (see [dynamicLocalsBinder]).
* * 1 is optimized and is fast. Further optimization most likely requires caching lookups by name (see [LocalsBinder.binderForName]).
* * 2+ throws an error.
*/
fun List.localsBinder(missingValue: ExprValue): LocalsBinder {
// For each 'as' and 'at' alias, create a locals accessor => { name: binding_accessor }
fun compileBindings(keyMangler: (String) -> String = { it }): Map) -> ExprValue?> {
data class Binder(val name: String, val func: (List) -> ExprValue)
return this.mapIndexed { index, alias ->
sequenceOf(
// the alias binds to the value itself
Binder(alias.asName) { it[index] },
// the alias binds to the name of the value
when {
alias.atName == null -> null
else -> Binder(alias.atName) { it[index].name ?: missingValue }
},
when {
alias.byName == null -> null
else -> Binder(alias.byName) { it[index].address ?: missingValue }
}
)
}.asSequence()
.flatten()
.filterNotNull()
// There may be multiple accessors per name.
// Squash the accessor list to either the sole element or an error function
.groupBy { keyMangler(it.name) }
.mapValues { (name, binders) ->
when (binders.size) {
1 -> binders[0].func
else -> { _ ->
errAmbiguousBinding(name, binders.map { it.name })
}
}
}
}
/**
* Nothing found at our scope, attempt to look at the attributes in our variables
* TODO fix dynamic scoping to be in line with PartiQL rules
*/
val dynamicLocalsBinder: (BindingName) -> (List) -> ExprValue? = when (this.count()) {
0 -> { _ -> { _ -> null } }
1 -> { name -> { locals -> locals.first().bindings[name] } }
else -> { name ->
{ locals ->
locals.asSequence()
.map { it.bindings[name] }
.filterNotNull()
.firstOrNull()
}
}
}
// Compile case-[in]sensitive bindings and return the accessor
return object : LocalsBinder() {
val caseSensitiveBindings = compileBindings()
val caseInsensitiveBindings = compileBindings { it.toLowerCase() }
override fun binderForName(bindingName: BindingName): (List) -> ExprValue? {
return when (bindingName.bindingCase) {
BindingCase.INSENSITIVE -> caseInsensitiveBindings[bindingName.name.toLowerCase()]
BindingCase.SENSITIVE -> caseSensitiveBindings[bindingName.name]
} ?: dynamicLocalsBinder(bindingName)
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy