Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Kotlin DSL #3914

vanillajonathan started this conversation in Show and tell
Discussion options

Kotlin is a language with some interesting features which lets you write domain-specific languages (DSL), a language within the language.

It lets you have strongly-typed arguments and lets the IDE provide you with auto-completion for aggregates.

This just scratches the surface of what is possible, it is possible to have strongly typed columns, and use infix functions, operator overloading and other stuff to implement custom operators and keywords to have it more strongly typed with compiler help and less strings.

You could use it like this.

import org.prql_lang.dsl
fun main() {
 val prql = prql {
 from("invoices")
 derive("foo", "bar")
 filter("income > 1")
 select("customer_id", "first_name", "last_name")
 sort("-sum_income")
 take(10..20)
 }
 
 println(prql)
}

File: prql.kt

package org.prql_lang.dsl
@DslMarker
annotation class PrqlDsl
fun prql(init: PrqlQueryBuilder.() -> Unit): String {
 val builder = PrqlQueryBuilder()
 builder.init()
 return builder.build()
}
/**
 * Exposes methods to build a PRQL query.
 */
@PrqlDsl
class PrqlQueryBuilder {
 private lateinit var _from: String
 private lateinit var _sort: String
 private var skip: Int = 0
 private var take: Int = 0
 private val columns = mutableListOf<String>()
 private val filters = mutableListOf<String>()
 private val derives = mutableMapOf<String, String>()
 /**
 * Adds a derive.
 *
 * @param alias The alias.
 * @param expression The expression.
 */
 fun derive(alias: String, expression: String) {
 this.derives.put(alias, expression)
 }
 
 fun derive(map: Map<String, String>) {
 this.derives.putAll(map)
 }
 
 /**
 * Adds a filter predicate.
 *
 * @param filter The filter predicate.
 */
 fun filter(filter: String) {
 this.filters.add(filter)
 }
 
 fun from(from: String) {
 if (::_from.isInitialized) {
 throw IllegalStateException("Table already defined")
 }
 this._from = from
 }
 
 fun select(vararg columns: String) {
 require(columns.isNotEmpty()) { "At least one column should be defined" }
 this.columns.addAll(columns)
 }
 
 fun select(columns: List<String>) {
 this.columns.addAll(columns)
 }
 
 /**
 * Sorts the elements of a sequence.
 *
 * @param sort The key to sort by.
 * @throws IllegalStateException
 */
 fun sort(sort: String) {
 if (::_sort.isInitialized) {
 throw IllegalStateException("Already sorted")
 }
 this._sort = sort
 }
 
 /**
 * Returns a specified number of contiguous elements from the start of a sequence.
 *
 * @param take The number of elements to return.
 * @throws IllegalArgumentException
 */
 fun take(take: Int) {
 require(take > 0) { "Take needs to be greater than zero, was $take" }
 this.take = take
 }
 
 fun take(range: Iterable<Int>) {
 this.skip = range.first()
 this.take = range.last()
 }
 /**
 * Builds a PRQL query string.
 *
 * @return A PRQL query.
 * @throws IllegalStateException
 */
 fun build(): String {
 if (!::_from.isInitialized) {
 throw IllegalStateException("Need call 'from' first")
 }
 
 val stringBuilder = StringBuilder()
 stringBuilder.appendLine("from $_from")
 if (!this.derives.isEmpty())
 for ((key, value) in derives)
 stringBuilder.appendLine("derive $key = $value")
 if (!this.filters.isEmpty())
 for (filter in filters)
 stringBuilder.appendLine("filter $filter")
 if (!this.columns.isEmpty())
 stringBuilder.appendLine("select { ${this.columns.joinToString(", ")} }")
 if (::_sort.isInitialized)
 stringBuilder.appendLine("sort $_sort")
 if (this.take != 0)
 if (this.skip != 0)
 stringBuilder.appendLine("take $skip..$take")
 else
 stringBuilder.appendLine("take $take")
 
 return stringBuilder.toString()
 }
 
 override fun toString(): String {
 return build()
 }
}
You must be logged in to vote

Replies: 0 comments

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
1 participant

AltStyle によって変換されたページ (->オリジナル) /