Skip to content

Commit

Permalink
Make FROM node an Expression<Sequence<Unit>> (PolymerLabs#6043)
Browse files Browse the repository at this point in the history
  • Loading branch information
piotrswigon authored Aug 28, 2020
1 parent 22284f1 commit 04e70b5
Show file tree
Hide file tree
Showing 8 changed files with 89 additions and 102 deletions.
32 changes: 16 additions & 16 deletions java/arcs/core/data/expression/Builders.kt
Original file line number Diff line number Diff line change
Expand Up @@ -135,17 +135,17 @@ infix fun Expression<Number>.lte(other: Expression<Number>) = Expression.BinaryE
)

/** Constructs a [Expression.BinaryExpression] with [Expression.BinaryOp.Equals]. */
infix fun Expression<out Any>.eq(other: Expression<out Any>) = Expression.BinaryExpression(
infix fun Expression<Any>.eq(other: Expression<Any>) = Expression.BinaryExpression(
Expression.BinaryOp.Equals,
this as Expression<Any>,
other as Expression<Any>
this,
other
)

/** Constructs a [Expression.BinaryExpression] with [Expression.BinaryOp.NotEquals]. */
infix fun Expression<out Any>.neq(other: Expression<out Any>) = Expression.BinaryExpression(
infix fun Expression<Any>.neq(other: Expression<Any>) = Expression.BinaryExpression(
Expression.BinaryOp.NotEquals,
this as Expression<Any>,
other as Expression<Any>
this,
other
)

/** Constructs a [Expression.FieldExpression] given a [Scope] and [field]. */
Expand Down Expand Up @@ -206,35 +206,35 @@ fun <T> Map<String, T>.asScope(scopeName: String = "<object>") = MapScope(
fun <T> query(queryArgName: String) = Expression.QueryParameterExpression<T>(queryArgName)

/** Helper used to build [FromExpression]. */
data class FromBuilder<T, Q>(val iterName: String, val qualifier: Expression<Sequence<Q>>?)
data class FromBuilder(val iterName: String, val qualifier: Expression<Sequence<Unit>>?)

/** Build a [FromExpression] whose [Sequence] iterates using a scope variable named [iterName]. */
fun <T> from(iterName: String) = FromBuilder<T, T>(iterName, null)
fun from(iterName: String) = FromBuilder(iterName, null)

/** Build a nested [FromExpression] whose [Sequence] iterates a scope variable named [iterName]. */
fun <Q, T> Expression<Sequence<Q>>.from(iterName: String) = FromBuilder<T, Q>(iterName, this)
infix fun Expression<Sequence<Unit>>.from(iterName: String) = FromBuilder(iterName, this)

/** Designates the expression which holds the [Sequence] the from expression iterates on. */
infix fun <T, Q> FromBuilder<T, Q>.on(sequence: Expression<Sequence<T>>) =
Expression.FromExpression<Q, T>(this.qualifier, sequence, this.iterName)
infix fun FromBuilder.on(sequence: Expression<Sequence<Any>>) =
Expression.FromExpression(this.qualifier, sequence, this.iterName)

/** Constructs a [WhereExpression]. */
infix fun <T> Expression<Sequence<T>>.where(expr: Expression<Boolean>) =
infix fun Expression<Sequence<Unit>>.where(expr: Expression<Boolean>) =
Expression.WhereExpression(this, expr)

/** Constructs a [SelectExpression]. */
infix fun <E, T> Expression<Sequence<E>>.select(expr: Expression<T>) =
infix fun <T> Expression<Sequence<Unit>>.select(expr: Expression<T>) =
Expression.SelectExpression(this, expr)

/** Helper to construct [NewExpression]. */
data class NewBuilder<E, T>(val schemaNames: Set<String>) {
data class NewBuilder(val schemaNames: Set<String>) {
operator fun invoke(
block: () -> List<Pair<String, Expression<*>>>
): Expression<T> = Expression.NewExpression(schemaNames, block())
): Expression<Scope> = Expression.NewExpression(schemaNames, block())
}

/** Constructs a [NewBuilder] for the given [schemaName]. */
fun <E, T> new(vararg schemaNames: String) = NewBuilder<E, T>(schemaNames.toSet())
fun new(vararg schemaNames: String) = NewBuilder(schemaNames.toSet())

/** Constructs a [FunctionExpression] to invoke [Max]. */
fun max(expr: Expression<*>) = FunctionExpression<Number>(Max, listOf(expr))
Expand Down
19 changes: 9 additions & 10 deletions java/arcs/core/data/expression/Evaluator.kt
Original file line number Diff line number Diff line change
Expand Up @@ -66,28 +66,27 @@ class ExpressionEvaluator(

override fun <T> visit(expr: Expression.ObjectLiteralExpression<T>): Any = expr.value as Any

override fun <E, T> visit(expr: Expression.FromExpression<E, T>): Any {
return (expr.qualifier?.accept(this) as Sequence<T>? ?: sequenceOf(null)).flatMap {
asSequence<T>(expr.expr.accept(this)).map { value ->
currentScope.set(expr.iterationVar, value as Any)
value
override fun visit(expr: Expression.FromExpression): Any {
return (expr.qualifier?.accept(this) as Sequence<*>? ?: sequenceOf(null)).flatMap {
asSequence<Any>(expr.source.accept(this)).map {
currentScope.set(expr.iterationVar, it)
}
}
}

override fun <T> visit(expr: Expression.WhereExpression<T>): Any {
return (expr.qualifier.accept(this) as Sequence<T>).filter {
override fun visit(expr: Expression.WhereExpression): Any {
return (expr.qualifier.accept(this) as Sequence<*>).filter {
expr.expr.accept(this) == true
}
}

override fun <E, T> visit(expr: Expression.SelectExpression<E, T>): Any {
return (expr.qualifier.accept(this) as Sequence<E>).map {
override fun <T> visit(expr: Expression.SelectExpression<T>): Any {
return (expr.qualifier.accept(this) as Sequence<*>).map {
expr.expr.accept(this) as T
}
}

override fun <T> visit(expr: Expression.NewExpression<T>): Any {
override fun visit(expr: Expression.NewExpression): Any {
val newScope = scopeCreator(expr.schemaName.firstOrNull() ?: "")
expr.fields.forEach { (fieldName, fieldExpr) ->
newScope.set(fieldName, fieldExpr.accept(this))
Expand Down
48 changes: 21 additions & 27 deletions java/arcs/core/data/expression/Expression.kt
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ import java.math.BigInteger
*
* @param T the resulting type of the expression.
*/
sealed class Expression<T> {
sealed class Expression<out T> {

/**
* Implementors denote sub-properties that may be looked up. This is not necessarily limited
Expand Down Expand Up @@ -72,19 +72,19 @@ sealed class Expression<T> {
fun <T> visit(expr: ObjectLiteralExpression<T>): Result

/** Called when [FromExpression] encountered. */
fun <E, T> visit(expr: FromExpression<E, T>): Result
fun visit(expr: FromExpression): Result

/** Called when [WhereExpression] encountered. */
fun <T> visit(expr: WhereExpression<T>): Result
fun visit(expr: WhereExpression): Result

/** Called when [SelectExpression] encountered. */
fun <E, T> visit(expr: SelectExpression<E, T>): Result
fun <T> visit(expr: SelectExpression<T>): Result

/** Called when [FunctionExpression] encountered. */
fun <T> visit(expr: FunctionExpression<T>): Result

/** Called when [NewExpression] encountered. */
fun <T> visit(expr: NewExpression<T>): Result
fun visit(expr: NewExpression): Result
}

/** Accepts a visitor and invokes the appropriate [Visitor.visit] method. */
Expand Down Expand Up @@ -348,23 +348,20 @@ sealed class Expression<T> {
}

/** Subtypes represent a [Expression]s that operate over the result of the [qualifier]. */
interface QualifiedExpression<T> {
val qualifier: Expression<Sequence<T>>?
interface QualifiedExpression {
val qualifier: Expression<Sequence<Unit>>?
}

/**
* Represents an iteration over a [Sequence] in the current scope under the identifier [source],
* placing each member of the sequence in a scope under [iterationVar] and evaluating
* [iterationExpr], returning a new sequence.
*
* @param E the type of the [qualifier] expression if any
* @param T the type of elements in the resulting [Sequence]
* placing each member of the sequence in a scope under [iterationVar] and returning
* a new sequence.
*/
data class FromExpression<E, T>(
override val qualifier: Expression<Sequence<E>>?,
val expr: Expression<Sequence<T>>,
data class FromExpression(
override val qualifier: Expression<Sequence<Unit>>?,
val source: Expression<Sequence<Any>>,
val iterationVar: String
) : QualifiedExpression<E>, Expression<Sequence<T>>() {
) : QualifiedExpression, Expression<Sequence<Unit>>() {
override fun <Result> accept(visitor: Visitor<Result>) = visitor.visit(this)
override fun toString() = this.stringify()
}
Expand All @@ -374,38 +371,35 @@ sealed class Expression<T> {
*
* @param T the type of elements in the [qualfier] [Sequence].
*/
data class WhereExpression<T>(
override val qualifier: Expression<Sequence<T>>,
data class WhereExpression(
override val qualifier: Expression<Sequence<Unit>>,
val expr: Expression<Boolean>
) : QualifiedExpression<T>, Expression<Sequence<T>>() {
) : QualifiedExpression, Expression<Sequence<Unit>>() {
override fun <Result> accept(visitor: Visitor<Result>): Result = visitor.visit(this)
override fun toString() = this.stringify()
}

/**
* Represents an expression that outputs a value.
*
* @param E the type of elements in the [qualfier] [Sequence]
* @param T the type of the new elements in the sequence
*/
data class SelectExpression<E, T>(
override val qualifier: Expression<Sequence<E>>,
data class SelectExpression<T>(
override val qualifier: Expression<Sequence<Unit>>,
val expr: Expression<T>
) : QualifiedExpression<E>, Expression<Sequence<T>>() {
) : QualifiedExpression, Expression<Sequence<T>>() {
override fun <Result> accept(visitor: Visitor<Result>): Result = visitor.visit(this)
override fun toString() = this.stringify()
}

/**
* Represents an expression that constructs a new value corresponding to the given
* [schemaName] with a field for each declared (name, expression) in [fields].
*
* @param T the type of the new elements in the [Sequence]
*/
data class NewExpression<T>(
data class NewExpression(
val schemaName: Set<String>,
val fields: List<Pair<String, Expression<*>>>
) : Expression<T>() {
) : Expression<Scope>() {
override fun <Result> accept(visitor: Visitor<Result>): Result = visitor.visit(this)
override fun toString() = this.stringify()
}
Expand Down
20 changes: 10 additions & 10 deletions java/arcs/core/data/expression/Serializer.kt
Original file line number Diff line number Diff line change
Expand Up @@ -80,17 +80,17 @@ class ExpressionSerializer() : Expression.Visitor<JsonValue<*>> {
override fun <T> visit(expr: Expression.ObjectLiteralExpression<T>) =
throw IllegalArgumentException("Can't serialize an ObjectLiteralExpression")

override fun <T, R> visit(expr: Expression.FromExpression<T, R>) =
override fun visit(expr: Expression.FromExpression) =
JsonObject(
mapOf(
"op" to JsonString("from"),
"source" to expr.expr.accept(this),
"source" to expr.source.accept(this),
"var" to JsonString(expr.iterationVar),
"qualifier" to (expr.qualifier?.accept(this) ?: JsonNull)
)
)

override fun <T> visit(expr: Expression.WhereExpression<T>) =
override fun visit(expr: Expression.WhereExpression) =
JsonObject(
mapOf(
"op" to JsonString("where"),
Expand All @@ -99,7 +99,7 @@ class ExpressionSerializer() : Expression.Visitor<JsonValue<*>> {
)
)

override fun <E, T> visit(expr: Expression.SelectExpression<E, T>) =
override fun <T> visit(expr: Expression.SelectExpression<T>) =
JsonObject(
mapOf(
"op" to JsonString("select"),
Expand All @@ -108,7 +108,7 @@ class ExpressionSerializer() : Expression.Visitor<JsonValue<*>> {
)
)

override fun <T> visit(expr: Expression.NewExpression<T>) =
override fun visit(expr: Expression.NewExpression) =
JsonObject(
mapOf(
"op" to JsonString("new"),
Expand Down Expand Up @@ -165,27 +165,27 @@ class ExpressionDeserializer : JsonVisitor<Expression<*>> {
type == "this" -> Expression.CurrentScopeExpression<Expression.Scope>()
type == "?" -> Expression.QueryParameterExpression<Any>(value["identifier"].string()!!)
type == "from" ->
Expression.FromExpression<Any, Any>(
Expression.FromExpression(
if (value["qualifier"] == JsonNull) {
null
} else {
visit(value["qualifier"].obj()!!) as Expression<Sequence<Any>>
visit(value["qualifier"].obj()!!) as Expression<Sequence<Unit>>
},
visit(value["source"].obj()!!) as Expression<Sequence<Any>>,
value["var"].string()!!
)
type == "where" ->
Expression.WhereExpression(
visit(value["qualifier"].obj()!!) as Expression<Sequence<Any>>,
visit(value["qualifier"].obj()!!) as Expression<Sequence<Unit>>,
visit(value["expr"]) as Expression<Boolean>
)
type == "select" ->
Expression.SelectExpression(
visit(value["qualifier"].obj()!!) as Expression<Sequence<Any>>,
visit(value["qualifier"].obj()!!) as Expression<Sequence<Unit>>,
visit(value["expr"]) as Expression<Sequence<Any>>
)
type == "new" ->
Expression.NewExpression<Any>(
Expression.NewExpression(
value["schemaName"].array()!!.value.map { it.string()!! }.toSet(),
value["expr"].obj()!!.value.map { (name, expr) ->
name to visit(expr)
Expand Down
10 changes: 5 additions & 5 deletions java/arcs/core/data/expression/Stringifier.kt
Original file line number Diff line number Diff line change
Expand Up @@ -47,17 +47,17 @@ class ExpressionStringifier(val parameterScope: Expression.Scope = ParameterScop
override fun <T> visit(expr: Expression.ObjectLiteralExpression<T>) =
(expr.value as? Expression.Scope)?.scopeName ?: "<object>"

override fun <T, R> visit(expr: Expression.FromExpression<T, R>): String =
override fun visit(expr: Expression.FromExpression): String =
(expr.qualifier?.accept(this) ?: "") +
"\nfrom ${expr.iterationVar} in ${expr.expr.accept(this)}\n"
"\nfrom ${expr.iterationVar} in ${expr.source.accept(this)}\n"

override fun <T> visit(expr: Expression.WhereExpression<T>): String =
override fun visit(expr: Expression.WhereExpression): String =
expr.qualifier.accept(this) + "\nwhere " + expr.expr.accept(this) + "\n"

override fun <E, T> visit(expr: Expression.SelectExpression<E, T>): String =
override fun <T> visit(expr: Expression.SelectExpression<T>): String =
expr.qualifier.accept(this) + "\nselect " + expr.expr.accept(this) + "\n"

override fun <T> visit(expr: Expression.NewExpression<T>): String =
override fun visit(expr: Expression.NewExpression): String =
"new " + expr.schemaName.joinToString(" ") + expr.fields.joinToString(
", \n",
"{\n",
Expand Down
15 changes: 6 additions & 9 deletions javatests/arcs/core/data/expression/EvaluatorParticleTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -41,24 +41,21 @@ class EvaluatorParticleTest {
// from x in inputNumbers select new Value {
// value: x.value * scalar.magnitude
// }
val scaledNumbersExpression = from<Number>("x") on
seq("inputNumbers") select
new<Number, Scope>("Value")() {
val scaledNumbersExpression = from("x") on seq("inputNumbers") select
new("Value")() {
listOf(
"value" to scope("x")
.get<Scope, Expression<*>>("value").asNumber() *
"value" to scope("x").get<Scope, Number>("value") *
scope("scalar")["magnitude"]
)
}

// average: writes Average {average: Number} =
// Average(from x in inputNumbers select x.value)
val averageExpression = new<Number, Scope>("Average")() {
val averageExpression = new("Average")() {
listOf(
"average" to average(
from<Number>("x") on seq("inputNumbers")
select (scope("x")
.get<Scope, Number>("value"))
from("x") on seq("inputNumbers")
select (scope("x").get<Scope, Number>("value"))
)
)
}
Expand Down
Loading

0 comments on commit 04e70b5

Please sign in to comment.