From 04e70b5ea7d973cb1947ab048ab315b9b830b7ca Mon Sep 17 00:00:00 2001 From: Piotr Swigon Date: Fri, 28 Aug 2020 15:38:15 -0700 Subject: [PATCH] Make FROM node an Expression> (#6043) --- java/arcs/core/data/expression/Builders.kt | 32 ++++++------- java/arcs/core/data/expression/Evaluator.kt | 19 ++++---- java/arcs/core/data/expression/Expression.kt | 48 ++++++++----------- java/arcs/core/data/expression/Serializer.kt | 20 ++++---- java/arcs/core/data/expression/Stringifier.kt | 10 ++-- .../data/expression/EvaluatorParticleTest.kt | 15 +++--- .../core/data/expression/ExpressionTest.kt | 38 +++++++-------- .../expression/ExpressionShowcaseTest.kt | 9 ++-- 8 files changed, 89 insertions(+), 102 deletions(-) diff --git a/java/arcs/core/data/expression/Builders.kt b/java/arcs/core/data/expression/Builders.kt index e84e6f477cc..310c8011947 100644 --- a/java/arcs/core/data/expression/Builders.kt +++ b/java/arcs/core/data/expression/Builders.kt @@ -135,17 +135,17 @@ infix fun Expression.lte(other: Expression) = Expression.BinaryE ) /** Constructs a [Expression.BinaryExpression] with [Expression.BinaryOp.Equals]. */ -infix fun Expression.eq(other: Expression) = Expression.BinaryExpression( +infix fun Expression.eq(other: Expression) = Expression.BinaryExpression( Expression.BinaryOp.Equals, - this as Expression, - other as Expression + this, + other ) /** Constructs a [Expression.BinaryExpression] with [Expression.BinaryOp.NotEquals]. */ -infix fun Expression.neq(other: Expression) = Expression.BinaryExpression( +infix fun Expression.neq(other: Expression) = Expression.BinaryExpression( Expression.BinaryOp.NotEquals, - this as Expression, - other as Expression + this, + other ) /** Constructs a [Expression.FieldExpression] given a [Scope] and [field]. */ @@ -206,35 +206,35 @@ fun Map.asScope(scopeName: String = "") = MapScope( fun query(queryArgName: String) = Expression.QueryParameterExpression(queryArgName) /** Helper used to build [FromExpression]. */ -data class FromBuilder(val iterName: String, val qualifier: Expression>?) +data class FromBuilder(val iterName: String, val qualifier: Expression>?) /** Build a [FromExpression] whose [Sequence] iterates using a scope variable named [iterName]. */ -fun from(iterName: String) = FromBuilder(iterName, null) +fun from(iterName: String) = FromBuilder(iterName, null) /** Build a nested [FromExpression] whose [Sequence] iterates a scope variable named [iterName]. */ -fun Expression>.from(iterName: String) = FromBuilder(iterName, this) +infix fun Expression>.from(iterName: String) = FromBuilder(iterName, this) /** Designates the expression which holds the [Sequence] the from expression iterates on. */ -infix fun FromBuilder.on(sequence: Expression>) = - Expression.FromExpression(this.qualifier, sequence, this.iterName) +infix fun FromBuilder.on(sequence: Expression>) = + Expression.FromExpression(this.qualifier, sequence, this.iterName) /** Constructs a [WhereExpression]. */ -infix fun Expression>.where(expr: Expression) = +infix fun Expression>.where(expr: Expression) = Expression.WhereExpression(this, expr) /** Constructs a [SelectExpression]. */ -infix fun Expression>.select(expr: Expression) = +infix fun Expression>.select(expr: Expression) = Expression.SelectExpression(this, expr) /** Helper to construct [NewExpression]. */ -data class NewBuilder(val schemaNames: Set) { +data class NewBuilder(val schemaNames: Set) { operator fun invoke( block: () -> List>> - ): Expression = Expression.NewExpression(schemaNames, block()) + ): Expression = Expression.NewExpression(schemaNames, block()) } /** Constructs a [NewBuilder] for the given [schemaName]. */ -fun new(vararg schemaNames: String) = NewBuilder(schemaNames.toSet()) +fun new(vararg schemaNames: String) = NewBuilder(schemaNames.toSet()) /** Constructs a [FunctionExpression] to invoke [Max]. */ fun max(expr: Expression<*>) = FunctionExpression(Max, listOf(expr)) diff --git a/java/arcs/core/data/expression/Evaluator.kt b/java/arcs/core/data/expression/Evaluator.kt index da03abb2bef..8dd17f2f675 100644 --- a/java/arcs/core/data/expression/Evaluator.kt +++ b/java/arcs/core/data/expression/Evaluator.kt @@ -66,28 +66,27 @@ class ExpressionEvaluator( override fun visit(expr: Expression.ObjectLiteralExpression): Any = expr.value as Any - override fun visit(expr: Expression.FromExpression): Any { - return (expr.qualifier?.accept(this) as Sequence? ?: sequenceOf(null)).flatMap { - asSequence(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(expr.source.accept(this)).map { + currentScope.set(expr.iterationVar, it) } } } - override fun visit(expr: Expression.WhereExpression): Any { - return (expr.qualifier.accept(this) as Sequence).filter { + override fun visit(expr: Expression.WhereExpression): Any { + return (expr.qualifier.accept(this) as Sequence<*>).filter { expr.expr.accept(this) == true } } - override fun visit(expr: Expression.SelectExpression): Any { - return (expr.qualifier.accept(this) as Sequence).map { + override fun visit(expr: Expression.SelectExpression): Any { + return (expr.qualifier.accept(this) as Sequence<*>).map { expr.expr.accept(this) as T } } - override fun visit(expr: Expression.NewExpression): 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)) diff --git a/java/arcs/core/data/expression/Expression.kt b/java/arcs/core/data/expression/Expression.kt index fbe803d3221..5752492dab0 100644 --- a/java/arcs/core/data/expression/Expression.kt +++ b/java/arcs/core/data/expression/Expression.kt @@ -21,7 +21,7 @@ import java.math.BigInteger * * @param T the resulting type of the expression. */ -sealed class Expression { +sealed class Expression { /** * Implementors denote sub-properties that may be looked up. This is not necessarily limited @@ -72,19 +72,19 @@ sealed class Expression { fun visit(expr: ObjectLiteralExpression): Result /** Called when [FromExpression] encountered. */ - fun visit(expr: FromExpression): Result + fun visit(expr: FromExpression): Result /** Called when [WhereExpression] encountered. */ - fun visit(expr: WhereExpression): Result + fun visit(expr: WhereExpression): Result /** Called when [SelectExpression] encountered. */ - fun visit(expr: SelectExpression): Result + fun visit(expr: SelectExpression): Result /** Called when [FunctionExpression] encountered. */ fun visit(expr: FunctionExpression): Result /** Called when [NewExpression] encountered. */ - fun visit(expr: NewExpression): Result + fun visit(expr: NewExpression): Result } /** Accepts a visitor and invokes the appropriate [Visitor.visit] method. */ @@ -348,23 +348,20 @@ sealed class Expression { } /** Subtypes represent a [Expression]s that operate over the result of the [qualifier]. */ - interface QualifiedExpression { - val qualifier: Expression>? + interface QualifiedExpression { + val qualifier: Expression>? } /** * 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( - override val qualifier: Expression>?, - val expr: Expression>, + data class FromExpression( + override val qualifier: Expression>?, + val source: Expression>, val iterationVar: String - ) : QualifiedExpression, Expression>() { + ) : QualifiedExpression, Expression>() { override fun accept(visitor: Visitor) = visitor.visit(this) override fun toString() = this.stringify() } @@ -374,10 +371,10 @@ sealed class Expression { * * @param T the type of elements in the [qualfier] [Sequence]. */ - data class WhereExpression( - override val qualifier: Expression>, + data class WhereExpression( + override val qualifier: Expression>, val expr: Expression - ) : QualifiedExpression, Expression>() { + ) : QualifiedExpression, Expression>() { override fun accept(visitor: Visitor): Result = visitor.visit(this) override fun toString() = this.stringify() } @@ -385,13 +382,12 @@ sealed class Expression { /** * 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( - override val qualifier: Expression>, + data class SelectExpression( + override val qualifier: Expression>, val expr: Expression - ) : QualifiedExpression, Expression>() { + ) : QualifiedExpression, Expression>() { override fun accept(visitor: Visitor): Result = visitor.visit(this) override fun toString() = this.stringify() } @@ -399,13 +395,11 @@ sealed class Expression { /** * 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( + data class NewExpression( val schemaName: Set, val fields: List>> - ) : Expression() { + ) : Expression() { override fun accept(visitor: Visitor): Result = visitor.visit(this) override fun toString() = this.stringify() } diff --git a/java/arcs/core/data/expression/Serializer.kt b/java/arcs/core/data/expression/Serializer.kt index 4bd546b8aa6..343b6bd356f 100644 --- a/java/arcs/core/data/expression/Serializer.kt +++ b/java/arcs/core/data/expression/Serializer.kt @@ -80,17 +80,17 @@ class ExpressionSerializer() : Expression.Visitor> { override fun visit(expr: Expression.ObjectLiteralExpression) = throw IllegalArgumentException("Can't serialize an ObjectLiteralExpression") - override fun visit(expr: Expression.FromExpression) = + 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 visit(expr: Expression.WhereExpression) = + override fun visit(expr: Expression.WhereExpression) = JsonObject( mapOf( "op" to JsonString("where"), @@ -99,7 +99,7 @@ class ExpressionSerializer() : Expression.Visitor> { ) ) - override fun visit(expr: Expression.SelectExpression) = + override fun visit(expr: Expression.SelectExpression) = JsonObject( mapOf( "op" to JsonString("select"), @@ -108,7 +108,7 @@ class ExpressionSerializer() : Expression.Visitor> { ) ) - override fun visit(expr: Expression.NewExpression) = + override fun visit(expr: Expression.NewExpression) = JsonObject( mapOf( "op" to JsonString("new"), @@ -165,27 +165,27 @@ class ExpressionDeserializer : JsonVisitor> { type == "this" -> Expression.CurrentScopeExpression() type == "?" -> Expression.QueryParameterExpression(value["identifier"].string()!!) type == "from" -> - Expression.FromExpression( + Expression.FromExpression( if (value["qualifier"] == JsonNull) { null } else { - visit(value["qualifier"].obj()!!) as Expression> + visit(value["qualifier"].obj()!!) as Expression> }, visit(value["source"].obj()!!) as Expression>, value["var"].string()!! ) type == "where" -> Expression.WhereExpression( - visit(value["qualifier"].obj()!!) as Expression>, + visit(value["qualifier"].obj()!!) as Expression>, visit(value["expr"]) as Expression ) type == "select" -> Expression.SelectExpression( - visit(value["qualifier"].obj()!!) as Expression>, + visit(value["qualifier"].obj()!!) as Expression>, visit(value["expr"]) as Expression> ) type == "new" -> - Expression.NewExpression( + Expression.NewExpression( value["schemaName"].array()!!.value.map { it.string()!! }.toSet(), value["expr"].obj()!!.value.map { (name, expr) -> name to visit(expr) diff --git a/java/arcs/core/data/expression/Stringifier.kt b/java/arcs/core/data/expression/Stringifier.kt index 238a3bb5ccd..d6e7235535a 100644 --- a/java/arcs/core/data/expression/Stringifier.kt +++ b/java/arcs/core/data/expression/Stringifier.kt @@ -47,17 +47,17 @@ class ExpressionStringifier(val parameterScope: Expression.Scope = ParameterScop override fun visit(expr: Expression.ObjectLiteralExpression) = (expr.value as? Expression.Scope)?.scopeName ?: "" - override fun visit(expr: Expression.FromExpression): 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 visit(expr: Expression.WhereExpression): String = + override fun visit(expr: Expression.WhereExpression): String = expr.qualifier.accept(this) + "\nwhere " + expr.expr.accept(this) + "\n" - override fun visit(expr: Expression.SelectExpression): String = + override fun visit(expr: Expression.SelectExpression): String = expr.qualifier.accept(this) + "\nselect " + expr.expr.accept(this) + "\n" - override fun visit(expr: Expression.NewExpression): String = + override fun visit(expr: Expression.NewExpression): String = "new " + expr.schemaName.joinToString(" ") + expr.fields.joinToString( ", \n", "{\n", diff --git a/javatests/arcs/core/data/expression/EvaluatorParticleTest.kt b/javatests/arcs/core/data/expression/EvaluatorParticleTest.kt index 5ded381c5d5..b9f7a9c6513 100644 --- a/javatests/arcs/core/data/expression/EvaluatorParticleTest.kt +++ b/javatests/arcs/core/data/expression/EvaluatorParticleTest.kt @@ -41,24 +41,21 @@ class EvaluatorParticleTest { // from x in inputNumbers select new Value { // value: x.value * scalar.magnitude // } - val scaledNumbersExpression = from("x") on - seq("inputNumbers") select - new("Value")() { + val scaledNumbersExpression = from("x") on seq("inputNumbers") select + new("Value")() { listOf( - "value" to scope("x") - .get>("value").asNumber() * + "value" to scope("x").get("value") * scope("scalar")["magnitude"] ) } // average: writes Average {average: Number} = // Average(from x in inputNumbers select x.value) - val averageExpression = new("Average")() { + val averageExpression = new("Average")() { listOf( "average" to average( - from("x") on seq("inputNumbers") - select (scope("x") - .get("value")) + from("x") on seq("inputNumbers") + select (scope("x").get("value")) ) ) } diff --git a/javatests/arcs/core/data/expression/ExpressionTest.kt b/javatests/arcs/core/data/expression/ExpressionTest.kt index 892ad57201d..3f80352bc49 100644 --- a/javatests/arcs/core/data/expression/ExpressionTest.kt +++ b/javatests/arcs/core/data/expression/ExpressionTest.kt @@ -183,7 +183,7 @@ class ExpressionTest { @Test fun evaluate_paxel_from() { - val fromExpr = from("p") on lookup("numbers") + val fromExpr = from("p") on lookup("numbers") select num("p") assertThat( evalExpression(fromExpr, currentScope).toList() ).isEqualTo(numbers) @@ -194,9 +194,8 @@ class ExpressionTest { // from p in numbers // from foo in foos // select p + foo.val - val fromExpr = (from("p") on lookup("numbers")) - .from("foo") on lookup("foos") select - num("p") + scope("foo")["val"] + val fromExpr = from("p") on lookup("numbers") from("foo") on + lookup("foos") select (num("p") + scope("foo")["val"]) assertThat(evalExpression(fromExpr, currentScope).toList()).containsExactlyElementsIn(1..30) } @@ -205,8 +204,7 @@ class ExpressionTest { // from foo in foos // from word in foo.words // select word - val fromExpr = (from("foo") on lookup("foos")) - .from("word") on scope("foo")["words"] select + val fromExpr = from("foo") on lookup("foos") from("word") on scope("foo")["words"] select text("word") assertThat(evalExpression(fromExpr, currentScope).toList()).containsExactly( "Lorem", "ipsum", "dolor", "sit", "amet" @@ -215,7 +213,7 @@ class ExpressionTest { @Test fun evaluate_paxel_select() { - val selectExpr = from("p") on lookup("numbers") select 1.asExpr() + val selectExpr = from("p") on lookup("numbers") select 1.asExpr() assertThat( evalExpression(selectExpr, currentScope).toList() ).isEqualTo(numbers.map { 1 }) @@ -223,8 +221,8 @@ class ExpressionTest { @Test fun evaluate_paxel_where() { - val whereExpr = from("p") on lookup("numbers") where - (num("p") eq 5.asExpr()) + val whereExpr = from("p") on lookup("numbers") where + (num("p") eq 5.asExpr()) select num("p") assertThat( evalExpression(whereExpr, currentScope).toList() ).isEqualTo(listOf(5)) @@ -232,7 +230,7 @@ class ExpressionTest { @Test fun evaluate_paxel_max() { - val selectMaxExpr = from("p") on lookup("numbers") select + val selectMaxExpr = from("p") on lookup("numbers") select max(seq("numbers")) assertThat( @@ -242,7 +240,7 @@ class ExpressionTest { @Test fun evaluate_paxel_count() { - val selectCountExpr = from("p") on lookup("numbers") select + val selectCountExpr = from("p") on lookup("numbers") select count(seq("numbers")) assertThat( @@ -255,7 +253,7 @@ class ExpressionTest { @Test fun evaluate_paxel_min() { - val selectMinExpr = from("p") on lookup("numbers") select + val selectMinExpr = from("p") on lookup("numbers") select min(seq("numbers")) assertThat( @@ -265,7 +263,7 @@ class ExpressionTest { @Test fun evaluate_paxel_average() { - val selectAvgExpr = from("p") on lookup("numbers") select + val selectAvgExpr = from("p") on lookup("numbers") select average(seq("numbers")) assertThat( @@ -276,7 +274,7 @@ class ExpressionTest { @Test fun evaluate_paxel_average_onComplexExpression() { val selectAvgExpr = average( - from("p") on lookup("numbers") + from("p") on lookup("numbers") select num("p") + 10.asExpr() ) assertThat( @@ -295,10 +293,10 @@ class ExpressionTest { @Test fun evaluate_paxel_union() { - val lessThan8 = from("p") on lookup("numbers") where - (num("p") lt 8.asExpr()) - val greaterThan6 = from("p") on lookup("numbers") where - (num("p") gt 6.asExpr()) + val lessThan8 = from("p") on lookup("numbers") where + (num("p") lt 8.asExpr()) select num("p") + val greaterThan6 = from("p") on lookup("numbers") where + (num("p") gt 6.asExpr()) select num("p") val unionExpr = union(lessThan8, greaterThan6) assertThat( @@ -316,8 +314,8 @@ class ExpressionTest { // y: p + 2 // z: COUNT(numbers) // } - val paxelExpr = from("p") on lookup("numbers") where - (num("p") lt 5.asExpr()) select new("Example")() { + val paxelExpr = from("p") on lookup("numbers") where + (num("p") lt 5.asExpr()) select new("Example")() { listOf( "x" to num("p") + 1.asExpr(), "y" to num("p") + 2.asExpr(), diff --git a/javatests/arcs/showcase/expression/ExpressionShowcaseTest.kt b/javatests/arcs/showcase/expression/ExpressionShowcaseTest.kt index b6e141fab07..4d831edc89c 100644 --- a/javatests/arcs/showcase/expression/ExpressionShowcaseTest.kt +++ b/javatests/arcs/showcase/expression/ExpressionShowcaseTest.kt @@ -19,11 +19,11 @@ import arcs.core.data.expression.div import arcs.core.data.expression.eq import arcs.core.data.expression.from import arcs.core.data.expression.get -import arcs.core.data.expression.lookup import arcs.core.data.expression.new import arcs.core.data.expression.on import arcs.core.data.expression.scope import arcs.core.data.expression.select +import arcs.core.data.expression.seq import arcs.core.data.expression.where import arcs.core.host.toRegistration import arcs.core.testutil.handles.dispatchFetchAll @@ -109,11 +109,10 @@ class ExpressionShowcaseTest { // statePopulationRatio: county.population / state.population // } val calculateStats = - ((from("state") on lookup("states")) - .from("county") on lookup("counties")) where ( + from("state") on seq("states") from("county") on seq("counties") where ( scope("county").get("stateCode") eq - scope("state").get("code")) select - new("CountyStats")() { + scope("state").get("code") + ) select new("CountyStats")() { listOf( "name" to scope("county").get("name"), "state" to scope("state").get("name"),