From 85a4f37785dfe54f908d485e922d6cdb0d907419 Mon Sep 17 00:00:00 2001 From: Peva Blanchard Date: Fri, 19 Jan 2024 22:49:06 +0100 Subject: [PATCH 01/37] core: map expression --- .../lcaac/core/lang/evaluator/ToValue.kt | 3 +- .../reducer/DataExpressionReducer.kt | 22 +- .../core/lang/expression/DataExpression.kt | 18 + .../lang/expression/optics/EveryDataRef.kt | 26 +- .../ch/kleis/lcaac/core/lang/type/Type.kt | 7 +- .../kleis/lcaac/core/lang/value/DataValue.kt | 6 + .../reducer/DataExpressionReducerTest.kt | 417 +++++++++++------- 7 files changed, 317 insertions(+), 182 deletions(-) diff --git a/core/src/main/kotlin/ch/kleis/lcaac/core/lang/evaluator/ToValue.kt b/core/src/main/kotlin/ch/kleis/lcaac/core/lang/evaluator/ToValue.kt index 11c405a7..34634507 100644 --- a/core/src/main/kotlin/ch/kleis/lcaac/core/lang/evaluator/ToValue.kt +++ b/core/src/main/kotlin/ch/kleis/lcaac/core/lang/evaluator/ToValue.kt @@ -136,7 +136,8 @@ class ToValue( when (val e = it.value) { is QuantityExpression<*> -> e.toValue() is StringExpression -> e.toValue() - is EDataRef -> throw EvaluatorException("$it is not reduced") + is EMap -> MapValue(e.entries.mapValues { it.value.toValue() }) + is EDataRef, is EMapEntry -> throw EvaluatorException("$it is not reduced") } }, ) diff --git a/core/src/main/kotlin/ch/kleis/lcaac/core/lang/evaluator/reducer/DataExpressionReducer.kt b/core/src/main/kotlin/ch/kleis/lcaac/core/lang/evaluator/reducer/DataExpressionReducer.kt index 0cb9bde4..2717cb44 100644 --- a/core/src/main/kotlin/ch/kleis/lcaac/core/lang/evaluator/reducer/DataExpressionReducer.kt +++ b/core/src/main/kotlin/ch/kleis/lcaac/core/lang/evaluator/reducer/DataExpressionReducer.kt @@ -1,10 +1,10 @@ package ch.kleis.lcaac.core.lang.evaluator.reducer -import ch.kleis.lcaac.core.lang.register.DataKey -import ch.kleis.lcaac.core.lang.register.DataRegister import ch.kleis.lcaac.core.lang.dimension.UnitSymbol import ch.kleis.lcaac.core.lang.evaluator.EvaluatorException import ch.kleis.lcaac.core.lang.expression.* +import ch.kleis.lcaac.core.lang.register.DataKey +import ch.kleis.lcaac.core.lang.register.DataRegister import ch.kleis.lcaac.core.math.QuantityOperations import kotlin.math.pow @@ -30,10 +30,28 @@ class DataExpressionReducer( is EUnitLiteral -> EQuantityScale(pure(1.0), expression) is EUnitOf -> reduceUnitOf(expression) is EStringLiteral -> expression + is EMap -> reduceMap(expression) + is EMapEntry -> reduceMapEntry(expression) } } } + private fun reduceMapEntry(expression: EMapEntry): DataExpression { + return when (val map = reduce(expression.map)) { + is EMap -> map.entries[expression.index] + ?: throw EvaluatorException("invalid index: '${expression.index}' not in ${map.entries.keys}") + else -> EMapEntry(map, expression.index) + } + } + + private fun reduceMap(expression: EMap): DataExpression { + return EMap( + expression.entries.mapValues { + reduce(it.value) + } + ) + } + private fun reduceUnitOf(unitOf: EUnitOf): DataExpression { with(ops) { diff --git a/core/src/main/kotlin/ch/kleis/lcaac/core/lang/expression/DataExpression.kt b/core/src/main/kotlin/ch/kleis/lcaac/core/lang/expression/DataExpression.kt index 03713bb2..3560e6e9 100644 --- a/core/src/main/kotlin/ch/kleis/lcaac/core/lang/expression/DataExpression.kt +++ b/core/src/main/kotlin/ch/kleis/lcaac/core/lang/expression/DataExpression.kt @@ -13,6 +13,24 @@ sealed interface DataExpression { sealed interface QuantityExpression sealed interface StringExpression +/* + Map + */ + +@optics +data class EMap(val entries: Map>): DataExpression { + companion object +} + +@optics +data class EMapEntry(val map: DataExpression, val index: String): DataExpression { + companion object +} + +/* + Ref + */ + @optics data class EDataRef(val name: String) : DataExpression { fun name(): String { diff --git a/core/src/main/kotlin/ch/kleis/lcaac/core/lang/expression/optics/EveryDataRef.kt b/core/src/main/kotlin/ch/kleis/lcaac/core/lang/expression/optics/EveryDataRef.kt index 06a35ff9..e08a5989 100644 --- a/core/src/main/kotlin/ch/kleis/lcaac/core/lang/expression/optics/EveryDataRef.kt +++ b/core/src/main/kotlin/ch/kleis/lcaac/core/lang/expression/optics/EveryDataRef.kt @@ -47,6 +47,11 @@ fun everyDataRefInDataExpression(): PEvery, DataExpression is EUnitLiteral -> M.empty() is EUnitOf -> foldMap(M, source.expression, map) is EStringLiteral -> M.empty() + is EMap -> M.fold( + source.entries.values + .map { foldMap(M, it, map) } + ) + is EMapEntry -> foldMap(M, source.map, map) } } @@ -102,6 +107,15 @@ fun everyDataRefInDataExpression(): PEvery, DataExpression ) is EStringLiteral -> source + is EMap -> EMap( + source.entries.mapValues { + modify(it.value, map) + } + ) + is EMapEntry -> EMapEntry( + modify(source.map, map), + source.index, + ) } } } @@ -172,15 +186,3 @@ fun everyDataRefInLcaExpression(): PEvery, LcaExpression everyDataRefInSubstanceCharacterization() ) ) - -fun everyDataRefInTemplateExpression(): PEvery, ProcessTemplateExpression, EDataRef, DataExpression> = - everyProcessTemplateInTemplateExpression() compose Merge( - listOf( - EProcessTemplate.params() compose Every.map() compose - everyDataRefInDataExpression(), - EProcessTemplate.locals() compose Every.map() compose - everyDataRefInDataExpression(), - EProcessTemplate.body() compose everyDataRefInProcess(), - ) - ) - diff --git a/core/src/main/kotlin/ch/kleis/lcaac/core/lang/type/Type.kt b/core/src/main/kotlin/ch/kleis/lcaac/core/lang/type/Type.kt index 1e9c81bc..2f06393d 100644 --- a/core/src/main/kotlin/ch/kleis/lcaac/core/lang/type/Type.kt +++ b/core/src/main/kotlin/ch/kleis/lcaac/core/lang/type/Type.kt @@ -7,14 +7,11 @@ sealed interface Type data class TUnit(val dimension: Dimension) : Type sealed interface TypeDataExpression : Type -object TString : TypeDataExpression { - override fun toString(): String { - return "TString" - } -} +data object TString : TypeDataExpression data class TQuantity(val dimension: Dimension) : TypeDataExpression +data class TMap(val entries: Map) : TypeDataExpression sealed interface TypeLcaExpression : Type diff --git a/core/src/main/kotlin/ch/kleis/lcaac/core/lang/value/DataValue.kt b/core/src/main/kotlin/ch/kleis/lcaac/core/lang/value/DataValue.kt index 6927f1c9..deac1ea3 100644 --- a/core/src/main/kotlin/ch/kleis/lcaac/core/lang/value/DataValue.kt +++ b/core/src/main/kotlin/ch/kleis/lcaac/core/lang/value/DataValue.kt @@ -13,3 +13,9 @@ data class QuantityValue(val amount: Q, val unit: UnitValue) : DataValue(val entries: Map>) : DataValue { + override fun toString(): String { + return entries.toString() + } +} diff --git a/core/src/test/kotlin/ch/kleis/lcaac/core/lang/evaluator/reducer/DataExpressionReducerTest.kt b/core/src/test/kotlin/ch/kleis/lcaac/core/lang/evaluator/reducer/DataExpressionReducerTest.kt index e13370d4..c6c655f0 100644 --- a/core/src/test/kotlin/ch/kleis/lcaac/core/lang/evaluator/reducer/DataExpressionReducerTest.kt +++ b/core/src/test/kotlin/ch/kleis/lcaac/core/lang/evaluator/reducer/DataExpressionReducerTest.kt @@ -1,8 +1,5 @@ package ch.kleis.lcaac.core.lang.evaluator.reducer -import ch.kleis.lcaac.core.lang.register.DataKey -import ch.kleis.lcaac.core.lang.register.DataRegister -import ch.kleis.lcaac.core.lang.register.Register import ch.kleis.lcaac.core.lang.SymbolTable import ch.kleis.lcaac.core.lang.dimension.Dimension import ch.kleis.lcaac.core.lang.dimension.UnitSymbol @@ -11,9 +8,13 @@ import ch.kleis.lcaac.core.lang.expression.* import ch.kleis.lcaac.core.lang.fixture.DimensionFixture import ch.kleis.lcaac.core.lang.fixture.QuantityFixture import ch.kleis.lcaac.core.lang.fixture.UnitFixture +import ch.kleis.lcaac.core.lang.register.DataKey +import ch.kleis.lcaac.core.lang.register.DataRegister +import ch.kleis.lcaac.core.lang.register.Register import ch.kleis.lcaac.core.math.basic.BasicNumber import ch.kleis.lcaac.core.math.basic.BasicOperations import org.junit.jupiter.api.Test +import org.junit.jupiter.api.assertThrows import kotlin.math.pow import kotlin.test.assertEquals import kotlin.test.assertFailsWith @@ -21,6 +22,98 @@ import kotlin.test.assertFailsWith class DataExpressionReducerTest { private val ops = BasicOperations + /* + DICT + */ + @Test + fun reduce_whenMap() { + // given + val q = QuantityFixture.oneKilogram + val map = EMap(mapOf( + "mass" to EDataRef("m") + )) + val reducer = DataExpressionReducer( + Register.from(mapOf( + DataKey("m") to q + )), + BasicOperations + ) + + // when + val actual = reducer.reduce(map) + + // then + val expected = EMap(mapOf( + "mass" to q + )) + assertEquals(expected, actual) + } + + @Test + fun reduce_whenMapEntry_withRef() { + // given + val q = QuantityFixture.oneKilogram + val map = EMap(mapOf( + "mass" to EDataRef("m") + )) + val entry = EMapEntry(EDataRef("my_map"), "mass") + val reducer = DataExpressionReducer( + Register.from(mapOf( + DataKey("m") to q, + DataKey("my_map") to map, + )), + BasicOperations + ) + + // when + val actual = reducer.reduce(entry) + + // then + assertEquals(q, actual) + } + + @Test + fun reduce_whenMapEntry() { + // given + val q = QuantityFixture.oneKilogram + val map = EMap(mapOf( + "mass" to EDataRef("m") + )) + val entry = EMapEntry(map, "mass") + val reducer = DataExpressionReducer( + Register.from(mapOf( + DataKey("m") to q + )), + BasicOperations + ) + + // when + val actual = reducer.reduce(entry) + + // then + assertEquals(q, actual) + } + + @Test + fun reduce_whenMapEntry_invalidIndex() { + // given + val q = QuantityFixture.oneKilogram + val map = EMap(mapOf( + "mass" to EDataRef("m") + )) + val entry = EMapEntry(map, "foo") + val reducer = DataExpressionReducer( + Register.from(mapOf( + DataKey("m") to q + )), + BasicOperations + ) + + // when + val e = assertThrows { reducer.reduce(entry) } + assertEquals("invalid index: 'foo' not in [mass]", e.message) + } + /* QUANTITIES */ @@ -30,8 +123,8 @@ class DataExpressionReducerTest { // given val unit = EUnitLiteral(UnitSymbol.of("a"), 123.0, Dimension.of("a")) val reducer = DataExpressionReducer( - Register.empty(), - ops, + Register.empty(), + ops, ) // when @@ -48,8 +141,8 @@ class DataExpressionReducerTest { val innerQuantity = QuantityFixture.oneKilogram val quantity = EQuantityScale(ops.pure(2.0), innerQuantity) val reducer = DataExpressionReducer( - Register.empty(), - ops, + Register.empty(), + ops, ) // when @@ -80,8 +173,8 @@ class DataExpressionReducerTest { val innerQuantity = EDataRef("a") val quantity = EQuantityScale(ops.pure(2.0), innerQuantity) val reducer = DataExpressionReducer( - Register.empty(), - ops, + Register.empty(), + ops, ) // when @@ -95,9 +188,9 @@ class DataExpressionReducerTest { fun reduce_whenLiteral_shouldReduceUnit() { // given val quantityEnvironment = DataRegister( - mapOf( - DataKey("kg") to UnitFixture.kg - ) + mapOf( + DataKey("kg") to UnitFixture.kg + ) ) val quantity = EQuantityScale(ops.pure(1.0), EDataRef("kg")) val reducer = DataExpressionReducer(quantityEnvironment, ops) @@ -116,8 +209,8 @@ class DataExpressionReducerTest { val a = EQuantityScale(ops.pure(2.0), UnitFixture.kg) val b = EQuantityScale(ops.pure(1000.0), UnitFixture.g) val reducer = DataExpressionReducer( - Register.empty(), - ops, + Register.empty(), + ops, ) // when @@ -150,14 +243,14 @@ class DataExpressionReducerTest { val b = EQuantityScale(ops.pure(1000.0), UnitFixture.m) val quantity = EQuantityAdd(a, b) val reducer = DataExpressionReducer( - Register.empty(), - ops, + Register.empty(), + ops, ) // when/then assertFailsWith( - EvaluatorException::class, - "incompatible dimensions: mass vs length in left=2.0 kg and right=1000.0 m" + EvaluatorException::class, + "incompatible dimensions: mass vs length in left=2.0 kg and right=1000.0 m" ) { reducer.reduce(quantity) } } @@ -212,8 +305,8 @@ class DataExpressionReducerTest { val a = EQuantityScale(ops.pure(2.0), UnitFixture.kg) val b = EQuantityScale(ops.pure(1000.0), UnitFixture.g) val reducer = DataExpressionReducer( - Register.empty(), - ops, + Register.empty(), + ops, ) // when @@ -230,15 +323,15 @@ class DataExpressionReducerTest { val a = EQuantityScale(ops.pure(2.0), UnitFixture.kg) val b = EQuantityScale(ops.pure(1000.0), UnitFixture.m) val reducer = DataExpressionReducer( - Register.empty(), - ops, + Register.empty(), + ops, ) val eQuantitySub = EQuantitySub(a, b) // when/then assertFailsWith( - EvaluatorException::class, - "incompatible dimensions: mass vs length in left=2.0 kg and right=1000.0 m" + EvaluatorException::class, + "incompatible dimensions: mass vs length in left=2.0 kg and right=1000.0 m" ) { reducer.reduce(eQuantitySub) } } @@ -308,8 +401,8 @@ class DataExpressionReducerTest { val a = EQuantityScale(ops.pure(2.0), UnitFixture.person) val b = EQuantityScale(ops.pure(2.0), UnitFixture.km) val reducer = DataExpressionReducer( - Register.empty(), - ops, + Register.empty(), + ops, ) // when @@ -317,12 +410,12 @@ class DataExpressionReducerTest { // then val expected = EQuantityScale( - ops.pure(4.0), - EUnitLiteral( - UnitSymbol.of("person").multiply(UnitSymbol.of("km")), - 1.0 * 1000.0, - Dimension.None.multiply(DimensionFixture.length) - ) + ops.pure(4.0), + EUnitLiteral( + UnitSymbol.of("person").multiply(UnitSymbol.of("km")), + 1.0 * 1000.0, + Dimension.None.multiply(DimensionFixture.length) + ) ) assertEquals(expected, actual) } @@ -333,8 +426,8 @@ class DataExpressionReducerTest { val a = UnitFixture.person val b = UnitFixture.km val reducer = DataExpressionReducer( - Register.empty(), - ops, + Register.empty(), + ops, ) // when @@ -342,12 +435,12 @@ class DataExpressionReducerTest { // then val expected = EQuantityScale( - ops.pure(1.0), - EUnitLiteral( - UnitSymbol.of("person").multiply(UnitSymbol.of("km")), - 1.0 * 1000.0, - Dimension.None.multiply(DimensionFixture.length) - ) + ops.pure(1.0), + EUnitLiteral( + UnitSymbol.of("person").multiply(UnitSymbol.of("km")), + 1.0 * 1000.0, + Dimension.None.multiply(DimensionFixture.length) + ) ) assertEquals(expected, actual) } @@ -358,15 +451,15 @@ class DataExpressionReducerTest { val a = UnitFixture.kg val b = QuantityFixture.twoKilograms val expected = - EQuantityScale( - ops.pure(2.0), - EUnitLiteral(UnitSymbol.of("kg").pow(2.0), 1.0, DimensionFixture.mass.multiply(DimensionFixture.mass)) - ) + EQuantityScale( + ops.pure(2.0), + EUnitLiteral(UnitSymbol.of("kg").pow(2.0), 1.0, DimensionFixture.mass.multiply(DimensionFixture.mass)) + ) // when val reducer = DataExpressionReducer( - Register.empty(), - ops, + Register.empty(), + ops, ) // when @@ -382,15 +475,15 @@ class DataExpressionReducerTest { val a = QuantityFixture.twoKilograms val b = UnitFixture.kg val expected = - EQuantityScale( - ops.pure(2.0), - EUnitLiteral(UnitSymbol.of("kg").pow(2.0), 1.0, DimensionFixture.mass.multiply(DimensionFixture.mass)) - ) + EQuantityScale( + ops.pure(2.0), + EUnitLiteral(UnitSymbol.of("kg").pow(2.0), 1.0, DimensionFixture.mass.multiply(DimensionFixture.mass)) + ) // when val reducer = DataExpressionReducer( - Register.empty(), - ops, + Register.empty(), + ops, ) // when @@ -406,8 +499,8 @@ class DataExpressionReducerTest { val a = EQuantityScale(ops.pure(4.0), UnitFixture.km) val b = EQuantityScale(ops.pure(2.0), UnitFixture.hour) val reducer = DataExpressionReducer( - Register.empty(), - ops, + Register.empty(), + ops, ) // when @@ -415,12 +508,12 @@ class DataExpressionReducerTest { // then val expected = EQuantityScale( - ops.pure(2.0), - EUnitLiteral( - UnitSymbol.of("km").divide(UnitSymbol.of("hour")), - 1000.0 / 3600.0, - DimensionFixture.length.divide(DimensionFixture.time) - ) + ops.pure(2.0), + EUnitLiteral( + UnitSymbol.of("km").divide(UnitSymbol.of("hour")), + 1000.0 / 3600.0, + DimensionFixture.length.divide(DimensionFixture.time) + ) ) assertEquals(expected, actual) } @@ -437,12 +530,12 @@ class DataExpressionReducerTest { // then val expected = EQuantityScale( - ops.pure(1.0), - EUnitLiteral( - UnitSymbol.of("km").divide(UnitSymbol.of("hour")), - 1000.0 / 3600.0, - DimensionFixture.length.divide(DimensionFixture.time) - ) + ops.pure(1.0), + EUnitLiteral( + UnitSymbol.of("km").divide(UnitSymbol.of("hour")), + 1000.0 / 3600.0, + DimensionFixture.length.divide(DimensionFixture.time) + ) ) assertEquals(expected, actual) } @@ -453,12 +546,12 @@ class DataExpressionReducerTest { val a = EQuantityScale(ops.pure(4.0), UnitFixture.km) val b = UnitFixture.hour val expected = EQuantityScale( - ops.pure(4.0), - EUnitLiteral( - UnitSymbol.of("km").divide(UnitSymbol.of("hour")), - 1000.0 / 3600.0, - DimensionFixture.length.divide(DimensionFixture.time) - ) + ops.pure(4.0), + EUnitLiteral( + UnitSymbol.of("km").divide(UnitSymbol.of("hour")), + 1000.0 / 3600.0, + DimensionFixture.length.divide(DimensionFixture.time) + ) ) val reducer = DataExpressionReducer(Register.empty(), ops) @@ -476,8 +569,8 @@ class DataExpressionReducerTest { // given val a = EQuantityScale(ops.pure(4.0), UnitFixture.km) val reducer = DataExpressionReducer( - Register.empty(), - ops, + Register.empty(), + ops, ) // when @@ -485,12 +578,12 @@ class DataExpressionReducerTest { // then val expected = EQuantityScale( - ops.pure(16.0), - EUnitLiteral( - UnitSymbol.of("km").pow(2.0), - 1e6, - DimensionFixture.length.pow(2.0) - ) + ops.pure(16.0), + EUnitLiteral( + UnitSymbol.of("km").pow(2.0), + 1e6, + DimensionFixture.length.pow(2.0) + ) ) assertEquals(expected, actual) } @@ -500,12 +593,12 @@ class DataExpressionReducerTest { // given val a = EDataRef("a") val reducer = DataExpressionReducer( - DataRegister( - mapOf( - DataKey("a") to EQuantityScale(ops.pure(1.0), UnitFixture.kg) - ) - ), - ops, + DataRegister( + mapOf( + DataKey("a") to EQuantityScale(ops.pure(1.0), UnitFixture.kg) + ) + ), + ops, ) // when @@ -527,15 +620,15 @@ class DataExpressionReducerTest { val quantityConversion = EQuantityScale(ops.pure(2.2), kg) val unitComposition = EUnitAlias("lbs", quantityConversion) val reducer = DataExpressionReducer( - Register.empty(), - ops, + Register.empty(), + ops, ) // when val actual = reducer.reduce(unitComposition) // then val expect = EQuantityScale( - ops.pure(1.0), - EUnitLiteral(UnitSymbol.of("lbs"), scale = 2.2, Dimension.of("mass")) + ops.pure(1.0), + EUnitLiteral(UnitSymbol.of("lbs"), scale = 2.2, Dimension.of("mass")) ) assertEquals(actual, expect) } @@ -547,15 +640,15 @@ class DataExpressionReducerTest { val quantityConversion = EQuantityScale(ops.pure(2200.0), g) val unitComposition = EUnitAlias("lbs", quantityConversion) val reducer = DataExpressionReducer( - Register.empty(), - ops, + Register.empty(), + ops, ) // when val actual = reducer.reduce(unitComposition) // then val expect = EQuantityScale( - ops.pure(1.0), - EUnitLiteral(UnitSymbol.of("lbs"), scale = 2.2, Dimension.of("mass")) + ops.pure(1.0), + EUnitLiteral(UnitSymbol.of("lbs"), scale = 2.2, Dimension.of("mass")) ) assertEquals(actual, expect) } @@ -578,16 +671,16 @@ class DataExpressionReducerTest { fun reduce_whenUnitClosure_shouldReduceWithGivenTable() { // given val symbolTable = SymbolTable( - data = DataRegister( - mapOf(DataKey("a") to UnitFixture.kg) - ), + data = DataRegister( + mapOf(DataKey("a") to UnitFixture.kg) + ), ) val unit = EQuantityClosure(symbolTable, EDataRef("a")) val reducer = DataExpressionReducer( - DataRegister( - mapOf(DataKey("a") to UnitFixture.l) - ), - ops, + DataRegister( + mapOf(DataKey("a") to UnitFixture.l) + ), + ops, ) // when @@ -603,8 +696,8 @@ class DataExpressionReducerTest { // given val kg = UnitFixture.kg val reducer = DataExpressionReducer( - Register.empty(), - ops, + Register.empty(), + ops, ) // when @@ -647,14 +740,14 @@ class DataExpressionReducerTest { // given val expr = EUnitOf(EQuantityMul(UnitFixture.kg, QuantityFixture.twoLitres)) val expected = - EQuantityScale( - ops.pure(1.0), - EUnitLiteral( - UnitSymbol.of("kg").multiply(UnitSymbol.of("l")), - 1.0e-3, - DimensionFixture.mass.multiply(DimensionFixture.volume) + EQuantityScale( + ops.pure(1.0), + EUnitLiteral( + UnitSymbol.of("kg").multiply(UnitSymbol.of("l")), + 1.0e-3, + DimensionFixture.mass.multiply(DimensionFixture.volume) + ) ) - ) val reducer = DataExpressionReducer(Register.empty(), ops) // when @@ -669,14 +762,14 @@ class DataExpressionReducerTest { // given val expr = EUnitOf(EQuantityMul(QuantityFixture.twoLitres, UnitFixture.kg)) val expected = - EQuantityScale( - ops.pure(1.0), - EUnitLiteral( - UnitSymbol.of("l").multiply(UnitSymbol.of("kg")), - 1.0e-3, - DimensionFixture.mass.multiply(DimensionFixture.volume) + EQuantityScale( + ops.pure(1.0), + EUnitLiteral( + UnitSymbol.of("l").multiply(UnitSymbol.of("kg")), + 1.0e-3, + DimensionFixture.mass.multiply(DimensionFixture.volume) + ) ) - ) val reducer = DataExpressionReducer(Register.empty(), ops) // when @@ -706,8 +799,8 @@ class DataExpressionReducerTest { val kg = UnitFixture.kg val l = UnitFixture.l val reducer = DataExpressionReducer( - Register.empty(), - ops, + Register.empty(), + ops, ) // when @@ -715,12 +808,12 @@ class DataExpressionReducerTest { // then val expected = EQuantityScale( - ops.pure(1.0), - EUnitLiteral( - UnitSymbol.of("kg").divide(UnitSymbol.of("l")), - 1.0 / 1.0e-3, - DimensionFixture.mass.divide(DimensionFixture.volume), - ) + ops.pure(1.0), + EUnitLiteral( + UnitSymbol.of("kg").divide(UnitSymbol.of("l")), + 1.0 / 1.0e-3, + DimensionFixture.mass.divide(DimensionFixture.volume), + ) ) assertEquals(expected, actual) } @@ -731,8 +824,8 @@ class DataExpressionReducerTest { val kg = UnitFixture.kg val l = UnitFixture.l val reducer = DataExpressionReducer( - Register.empty(), - ops, + Register.empty(), + ops, ) // when @@ -740,12 +833,12 @@ class DataExpressionReducerTest { // then val expected = EQuantityScale( - ops.pure(1.0), - EUnitLiteral( - UnitSymbol.of("kg").multiply(UnitSymbol.of("l")), - 1.0 * 1.0e-3, - DimensionFixture.mass.multiply(DimensionFixture.volume), - ) + ops.pure(1.0), + EUnitLiteral( + UnitSymbol.of("kg").multiply(UnitSymbol.of("l")), + 1.0 * 1.0e-3, + DimensionFixture.mass.multiply(DimensionFixture.volume), + ) ) assertEquals(expected, actual) } @@ -755,8 +848,8 @@ class DataExpressionReducerTest { // given val m = UnitFixture.m val reducer = DataExpressionReducer( - Register.empty(), - ops, + Register.empty(), + ops, ) // when @@ -764,12 +857,12 @@ class DataExpressionReducerTest { // then val expected = EQuantityScale( - ops.pure(1.0), - EUnitLiteral( - UnitSymbol.of("m").pow(2.0), - 1.0.pow(2.0), - DimensionFixture.length.pow(2.0), - ) + ops.pure(1.0), + EUnitLiteral( + UnitSymbol.of("m").pow(2.0), + 1.0.pow(2.0), + DimensionFixture.length.pow(2.0), + ) ) assertEquals(expected, actual) } @@ -779,13 +872,13 @@ class DataExpressionReducerTest { // given val ref = EDataRef("kg") val units = DataRegister( - mapOf( - DataKey("kg") to UnitFixture.kg - ) + mapOf( + DataKey("kg") to UnitFixture.kg + ) ) val reducer = DataExpressionReducer( - units, - ops, + units, + ops, ) // when @@ -806,8 +899,8 @@ class DataExpressionReducerTest { // given val expression = EStringLiteral("FR") val reducer = DataExpressionReducer( - DataRegister.empty(), - ops, + DataRegister.empty(), + ops, ) // when @@ -822,13 +915,13 @@ class DataExpressionReducerTest { // given val expression = EDataRef("geo") val reducer = DataExpressionReducer( - DataRegister( - mapOf( - DataKey("geo") to EDataRef("geo2"), - DataKey("geo2") to EStringLiteral("FR"), - ) - ), - ops, + DataRegister( + mapOf( + DataKey("geo") to EDataRef("geo2"), + DataKey("geo2") to EStringLiteral("FR"), + ) + ), + ops, ) // when @@ -844,13 +937,13 @@ class DataExpressionReducerTest { // given val expression = EDataRef("foo") val reducer = DataExpressionReducer( - DataRegister( - mapOf( - DataKey("geo") to EDataRef("geo2"), - DataKey("geo2") to EStringLiteral("FR"), - ) - ), - ops, + DataRegister( + mapOf( + DataKey("geo") to EDataRef("geo2"), + DataKey("geo2") to EStringLiteral("FR"), + ) + ), + ops, ) // when From c768b6ec296bf15fd1b302f18cc9873510a31a29 Mon Sep 17 00:00:00 2001 From: Peva Blanchard Date: Fri, 19 Jan 2024 23:33:01 +0100 Subject: [PATCH 02/37] core: data source expression --- .../core/datasource/DataSourceOperations.kt | 8 ++++++ .../ch/kleis/lcaac/core/lang/SymbolTable.kt | 27 ++++++++++--------- .../lang/expression/DataSourceExpression.kt | 15 +++++++++++ .../core/lang/register/DataSourceRegister.kt | 12 +++++++++ 4 files changed, 49 insertions(+), 13 deletions(-) create mode 100644 core/src/main/kotlin/ch/kleis/lcaac/core/datasource/DataSourceOperations.kt create mode 100644 core/src/main/kotlin/ch/kleis/lcaac/core/lang/expression/DataSourceExpression.kt create mode 100644 core/src/main/kotlin/ch/kleis/lcaac/core/lang/register/DataSourceRegister.kt diff --git a/core/src/main/kotlin/ch/kleis/lcaac/core/datasource/DataSourceOperations.kt b/core/src/main/kotlin/ch/kleis/lcaac/core/datasource/DataSourceOperations.kt new file mode 100644 index 00000000..9ba8513d --- /dev/null +++ b/core/src/main/kotlin/ch/kleis/lcaac/core/datasource/DataSourceOperations.kt @@ -0,0 +1,8 @@ +package ch.kleis.lcaac.core.datasource + +import ch.kleis.lcaac.core.lang.expression.DataSourceExpression +import ch.kleis.lcaac.core.lang.expression.EMap + +interface DataSourceOperations { + fun readAll(source: DataSourceExpression): Sequence> +} diff --git a/core/src/main/kotlin/ch/kleis/lcaac/core/lang/SymbolTable.kt b/core/src/main/kotlin/ch/kleis/lcaac/core/lang/SymbolTable.kt index 566f9e5c..0115ddb7 100644 --- a/core/src/main/kotlin/ch/kleis/lcaac/core/lang/SymbolTable.kt +++ b/core/src/main/kotlin/ch/kleis/lcaac/core/lang/SymbolTable.kt @@ -6,10 +6,11 @@ import ch.kleis.lcaac.core.lang.register.* data class SymbolTable( - val data: DataRegister = DataRegister.empty(), - val dimensions: DimensionRegister = DimensionRegister.empty(), - val processTemplates: ProcessTemplateRegister = ProcessTemplateRegister.empty(), - val substanceCharacterizations: SubstanceCharacterizationRegister = SubstanceCharacterizationRegister.empty(), + val data: DataRegister = DataRegister.empty(), + val dimensions: DimensionRegister = DimensionRegister.empty(), + val processTemplates: ProcessTemplateRegister = ProcessTemplateRegister.empty(), + val substanceCharacterizations: SubstanceCharacterizationRegister = SubstanceCharacterizationRegister.empty(), + val dataSources: DataSourceRegister = DataSourceRegister.empty(), ) { companion object { fun empty() = SymbolTable() @@ -26,11 +27,11 @@ data class SymbolTable( */ private val templatesIndexedByProductName: Index> = Index( - processTemplates, - EProcessTemplate.body().products() compose - Every.list() compose - ETechnoExchange.product() compose - EProductSpec.name() + processTemplates, + EProcessTemplate.body().products() compose + Every.list() compose + ETechnoExchange.product() compose + EProductSpec.name() ) fun getTemplate(name: String): EProcessTemplate? { @@ -50,10 +51,10 @@ data class SymbolTable( Substances */ fun getSubstanceCharacterization( - name: String, - type: SubstanceType, - compartment: String, - subCompartment: String? = null, + name: String, + type: SubstanceType, + compartment: String, + subCompartment: String? = null, ): ESubstanceCharacterization? { return substanceCharacterizations[SubstanceKey(name, type, compartment, subCompartment)] } diff --git a/core/src/main/kotlin/ch/kleis/lcaac/core/lang/expression/DataSourceExpression.kt b/core/src/main/kotlin/ch/kleis/lcaac/core/lang/expression/DataSourceExpression.kt new file mode 100644 index 00000000..444023df --- /dev/null +++ b/core/src/main/kotlin/ch/kleis/lcaac/core/lang/expression/DataSourceExpression.kt @@ -0,0 +1,15 @@ +package ch.kleis.lcaac.core.lang.expression + +sealed interface ColumnType +class CText : ColumnType +data class CQuantity(val defaultValue: DataExpression): ColumnType + +sealed interface DataSourceExpression { + val schema: Map> +} + +data class ECsvSource( + val location: String, + override val schema: Map>, +) : DataSourceExpression + diff --git a/core/src/main/kotlin/ch/kleis/lcaac/core/lang/register/DataSourceRegister.kt b/core/src/main/kotlin/ch/kleis/lcaac/core/lang/register/DataSourceRegister.kt new file mode 100644 index 00000000..b97e6d96 --- /dev/null +++ b/core/src/main/kotlin/ch/kleis/lcaac/core/lang/register/DataSourceRegister.kt @@ -0,0 +1,12 @@ +package ch.kleis.lcaac.core.lang.register + +import ch.kleis.lcaac.core.lang.expression.DataSourceExpression + +data class DataSourceKey( + val name: String, +) { + override fun toString(): String = name +} + +typealias DataSourceRegister = Register> + From 77c524cda843922d256195ccefab6f6bb4ec5e2f Mon Sep 17 00:00:00 2001 From: Peva Blanchard Date: Fri, 19 Jan 2024 23:37:23 +0100 Subject: [PATCH 03/37] core: renaming Map -> Record --- .../core/datasource/DataSourceOperations.kt | 4 +-- .../lcaac/core/lang/evaluator/ToValue.kt | 4 +-- .../reducer/DataExpressionReducer.kt | 16 +++++----- .../core/lang/expression/DataExpression.kt | 6 ++-- .../lang/expression/optics/EveryDataRef.kt | 10 +++---- .../ch/kleis/lcaac/core/lang/type/Type.kt | 2 +- .../kleis/lcaac/core/lang/value/DataValue.kt | 2 +- .../reducer/DataExpressionReducerTest.kt | 30 +++++++++---------- 8 files changed, 37 insertions(+), 37 deletions(-) diff --git a/core/src/main/kotlin/ch/kleis/lcaac/core/datasource/DataSourceOperations.kt b/core/src/main/kotlin/ch/kleis/lcaac/core/datasource/DataSourceOperations.kt index 9ba8513d..2acd5bfa 100644 --- a/core/src/main/kotlin/ch/kleis/lcaac/core/datasource/DataSourceOperations.kt +++ b/core/src/main/kotlin/ch/kleis/lcaac/core/datasource/DataSourceOperations.kt @@ -1,8 +1,8 @@ package ch.kleis.lcaac.core.datasource import ch.kleis.lcaac.core.lang.expression.DataSourceExpression -import ch.kleis.lcaac.core.lang.expression.EMap +import ch.kleis.lcaac.core.lang.expression.ERecord interface DataSourceOperations { - fun readAll(source: DataSourceExpression): Sequence> + fun readAll(source: DataSourceExpression): Sequence> } diff --git a/core/src/main/kotlin/ch/kleis/lcaac/core/lang/evaluator/ToValue.kt b/core/src/main/kotlin/ch/kleis/lcaac/core/lang/evaluator/ToValue.kt index 34634507..6eff2205 100644 --- a/core/src/main/kotlin/ch/kleis/lcaac/core/lang/evaluator/ToValue.kt +++ b/core/src/main/kotlin/ch/kleis/lcaac/core/lang/evaluator/ToValue.kt @@ -136,8 +136,8 @@ class ToValue( when (val e = it.value) { is QuantityExpression<*> -> e.toValue() is StringExpression -> e.toValue() - is EMap -> MapValue(e.entries.mapValues { it.value.toValue() }) - is EDataRef, is EMapEntry -> throw EvaluatorException("$it is not reduced") + is ERecord -> RecordValue(e.entries.mapValues { it.value.toValue() }) + is EDataRef, is ERecordEntry -> throw EvaluatorException("$it is not reduced") } }, ) diff --git a/core/src/main/kotlin/ch/kleis/lcaac/core/lang/evaluator/reducer/DataExpressionReducer.kt b/core/src/main/kotlin/ch/kleis/lcaac/core/lang/evaluator/reducer/DataExpressionReducer.kt index 2717cb44..735db6ee 100644 --- a/core/src/main/kotlin/ch/kleis/lcaac/core/lang/evaluator/reducer/DataExpressionReducer.kt +++ b/core/src/main/kotlin/ch/kleis/lcaac/core/lang/evaluator/reducer/DataExpressionReducer.kt @@ -30,22 +30,22 @@ class DataExpressionReducer( is EUnitLiteral -> EQuantityScale(pure(1.0), expression) is EUnitOf -> reduceUnitOf(expression) is EStringLiteral -> expression - is EMap -> reduceMap(expression) - is EMapEntry -> reduceMapEntry(expression) + is ERecord -> reduceMap(expression) + is ERecordEntry -> reduceMapEntry(expression) } } } - private fun reduceMapEntry(expression: EMapEntry): DataExpression { - return when (val map = reduce(expression.map)) { - is EMap -> map.entries[expression.index] + private fun reduceMapEntry(expression: ERecordEntry): DataExpression { + return when (val map = reduce(expression.record)) { + is ERecord -> map.entries[expression.index] ?: throw EvaluatorException("invalid index: '${expression.index}' not in ${map.entries.keys}") - else -> EMapEntry(map, expression.index) + else -> ERecordEntry(map, expression.index) } } - private fun reduceMap(expression: EMap): DataExpression { - return EMap( + private fun reduceMap(expression: ERecord): DataExpression { + return ERecord( expression.entries.mapValues { reduce(it.value) } diff --git a/core/src/main/kotlin/ch/kleis/lcaac/core/lang/expression/DataExpression.kt b/core/src/main/kotlin/ch/kleis/lcaac/core/lang/expression/DataExpression.kt index 3560e6e9..fa4d9a5e 100644 --- a/core/src/main/kotlin/ch/kleis/lcaac/core/lang/expression/DataExpression.kt +++ b/core/src/main/kotlin/ch/kleis/lcaac/core/lang/expression/DataExpression.kt @@ -14,16 +14,16 @@ sealed interface QuantityExpression sealed interface StringExpression /* - Map + Record */ @optics -data class EMap(val entries: Map>): DataExpression { +data class ERecord(val entries: Map>): DataExpression { companion object } @optics -data class EMapEntry(val map: DataExpression, val index: String): DataExpression { +data class ERecordEntry(val record: DataExpression, val index: String): DataExpression { companion object } diff --git a/core/src/main/kotlin/ch/kleis/lcaac/core/lang/expression/optics/EveryDataRef.kt b/core/src/main/kotlin/ch/kleis/lcaac/core/lang/expression/optics/EveryDataRef.kt index e08a5989..f1cb7e7b 100644 --- a/core/src/main/kotlin/ch/kleis/lcaac/core/lang/expression/optics/EveryDataRef.kt +++ b/core/src/main/kotlin/ch/kleis/lcaac/core/lang/expression/optics/EveryDataRef.kt @@ -47,11 +47,11 @@ fun everyDataRefInDataExpression(): PEvery, DataExpression is EUnitLiteral -> M.empty() is EUnitOf -> foldMap(M, source.expression, map) is EStringLiteral -> M.empty() - is EMap -> M.fold( + is ERecord -> M.fold( source.entries.values .map { foldMap(M, it, map) } ) - is EMapEntry -> foldMap(M, source.map, map) + is ERecordEntry -> foldMap(M, source.record, map) } } @@ -107,13 +107,13 @@ fun everyDataRefInDataExpression(): PEvery, DataExpression ) is EStringLiteral -> source - is EMap -> EMap( + is ERecord -> ERecord( source.entries.mapValues { modify(it.value, map) } ) - is EMapEntry -> EMapEntry( - modify(source.map, map), + is ERecordEntry -> ERecordEntry( + modify(source.record, map), source.index, ) } diff --git a/core/src/main/kotlin/ch/kleis/lcaac/core/lang/type/Type.kt b/core/src/main/kotlin/ch/kleis/lcaac/core/lang/type/Type.kt index 2f06393d..d2c0ecfc 100644 --- a/core/src/main/kotlin/ch/kleis/lcaac/core/lang/type/Type.kt +++ b/core/src/main/kotlin/ch/kleis/lcaac/core/lang/type/Type.kt @@ -11,7 +11,7 @@ data object TString : TypeDataExpression data class TQuantity(val dimension: Dimension) : TypeDataExpression -data class TMap(val entries: Map) : TypeDataExpression +data class TRecord(val entries: Map) : TypeDataExpression sealed interface TypeLcaExpression : Type diff --git a/core/src/main/kotlin/ch/kleis/lcaac/core/lang/value/DataValue.kt b/core/src/main/kotlin/ch/kleis/lcaac/core/lang/value/DataValue.kt index deac1ea3..4de7d444 100644 --- a/core/src/main/kotlin/ch/kleis/lcaac/core/lang/value/DataValue.kt +++ b/core/src/main/kotlin/ch/kleis/lcaac/core/lang/value/DataValue.kt @@ -14,7 +14,7 @@ data class QuantityValue(val amount: Q, val unit: UnitValue) : DataValue(val entries: Map>) : DataValue { +data class RecordValue(val entries: Map>) : DataValue { override fun toString(): String { return entries.toString() } diff --git a/core/src/test/kotlin/ch/kleis/lcaac/core/lang/evaluator/reducer/DataExpressionReducerTest.kt b/core/src/test/kotlin/ch/kleis/lcaac/core/lang/evaluator/reducer/DataExpressionReducerTest.kt index c6c655f0..0d506cca 100644 --- a/core/src/test/kotlin/ch/kleis/lcaac/core/lang/evaluator/reducer/DataExpressionReducerTest.kt +++ b/core/src/test/kotlin/ch/kleis/lcaac/core/lang/evaluator/reducer/DataExpressionReducerTest.kt @@ -23,13 +23,13 @@ class DataExpressionReducerTest { private val ops = BasicOperations /* - DICT + RECORD */ @Test - fun reduce_whenMap() { + fun reduce_whenRecord() { // given val q = QuantityFixture.oneKilogram - val map = EMap(mapOf( + val record = ERecord(mapOf( "mass" to EDataRef("m") )) val reducer = DataExpressionReducer( @@ -40,27 +40,27 @@ class DataExpressionReducerTest { ) // when - val actual = reducer.reduce(map) + val actual = reducer.reduce(record) // then - val expected = EMap(mapOf( + val expected = ERecord(mapOf( "mass" to q )) assertEquals(expected, actual) } @Test - fun reduce_whenMapEntry_withRef() { + fun reduce_whenRecordEntry_withRef() { // given val q = QuantityFixture.oneKilogram - val map = EMap(mapOf( + val record = ERecord(mapOf( "mass" to EDataRef("m") )) - val entry = EMapEntry(EDataRef("my_map"), "mass") + val entry = ERecordEntry(EDataRef("my_map"), "mass") val reducer = DataExpressionReducer( Register.from(mapOf( DataKey("m") to q, - DataKey("my_map") to map, + DataKey("my_map") to record, )), BasicOperations ) @@ -73,13 +73,13 @@ class DataExpressionReducerTest { } @Test - fun reduce_whenMapEntry() { + fun reduce_whenRecordEntry() { // given val q = QuantityFixture.oneKilogram - val map = EMap(mapOf( + val record = ERecord(mapOf( "mass" to EDataRef("m") )) - val entry = EMapEntry(map, "mass") + val entry = ERecordEntry(record, "mass") val reducer = DataExpressionReducer( Register.from(mapOf( DataKey("m") to q @@ -95,13 +95,13 @@ class DataExpressionReducerTest { } @Test - fun reduce_whenMapEntry_invalidIndex() { + fun reduce_whenRecordEntry_invalidIndex() { // given val q = QuantityFixture.oneKilogram - val map = EMap(mapOf( + val record = ERecord(mapOf( "mass" to EDataRef("m") )) - val entry = EMapEntry(map, "foo") + val entry = ERecordEntry(record, "foo") val reducer = DataExpressionReducer( Register.from(mapOf( DataKey("m") to q From dc9b3d7a8d26fcddd20c90daec488526b4ddcbae Mon Sep 17 00:00:00 2001 From: Peva Blanchard Date: Sat, 20 Jan 2024 00:03:10 +0100 Subject: [PATCH 04/37] core: data source register in data reducer --- .../lcaac/core/lang/evaluator/ToValue.kt | 1 + .../reducer/DataExpressionReducer.kt | 6 +- .../evaluator/reducer/LcaExpressionReducer.kt | 4 +- .../reducer/TemplateExpressionReducer.kt | 7 +- .../lcaac/core/lang/evaluator/step/Reduce.kt | 3 +- .../evaluator/step/ReduceLabelSelectors.kt | 31 +- .../core/lang/expression/DataExpression.kt | 5 + .../lang/expression/DataSourceExpression.kt | 6 +- .../lang/expression/optics/EveryDataRef.kt | 3 + .../lcaac/core/testing/BasicTestRunner.kt | 52 ++- .../reducer/DataExpressionReducerTest.kt | 427 ++++++++++-------- .../reducer/LcaExpressionReducerTest.kt | 12 + 12 files changed, 311 insertions(+), 246 deletions(-) diff --git a/core/src/main/kotlin/ch/kleis/lcaac/core/lang/evaluator/ToValue.kt b/core/src/main/kotlin/ch/kleis/lcaac/core/lang/evaluator/ToValue.kt index 6eff2205..f3195e98 100644 --- a/core/src/main/kotlin/ch/kleis/lcaac/core/lang/evaluator/ToValue.kt +++ b/core/src/main/kotlin/ch/kleis/lcaac/core/lang/evaluator/ToValue.kt @@ -138,6 +138,7 @@ class ToValue( is StringExpression -> e.toValue() is ERecord -> RecordValue(e.entries.mapValues { it.value.toValue() }) is EDataRef, is ERecordEntry -> throw EvaluatorException("$it is not reduced") + is EAnyRecordFrom -> TODO() } }, ) diff --git a/core/src/main/kotlin/ch/kleis/lcaac/core/lang/evaluator/reducer/DataExpressionReducer.kt b/core/src/main/kotlin/ch/kleis/lcaac/core/lang/evaluator/reducer/DataExpressionReducer.kt index 735db6ee..d3d7e43b 100644 --- a/core/src/main/kotlin/ch/kleis/lcaac/core/lang/evaluator/reducer/DataExpressionReducer.kt +++ b/core/src/main/kotlin/ch/kleis/lcaac/core/lang/evaluator/reducer/DataExpressionReducer.kt @@ -5,14 +5,17 @@ import ch.kleis.lcaac.core.lang.evaluator.EvaluatorException import ch.kleis.lcaac.core.lang.expression.* import ch.kleis.lcaac.core.lang.register.DataKey import ch.kleis.lcaac.core.lang.register.DataRegister +import ch.kleis.lcaac.core.lang.register.DataSourceRegister import ch.kleis.lcaac.core.math.QuantityOperations import kotlin.math.pow class DataExpressionReducer( dataRegister: DataRegister, + dataSourceRegister: DataSourceRegister, private val ops: QuantityOperations, ) { private val dataRegister = DataRegister(dataRegister) + private val dataSourceRegister = DataSourceRegister(dataSourceRegister) private val infiniteUnitLoopChecker = InfiniteUnitLoopChecker() fun reduce(expression: DataExpression): DataExpression { @@ -32,6 +35,7 @@ class DataExpressionReducer( is EStringLiteral -> expression is ERecord -> reduceMap(expression) is ERecordEntry -> reduceMapEntry(expression) + is EAnyRecordFrom -> TODO() } } } @@ -86,7 +90,7 @@ class DataExpressionReducer( } private fun reduceClosure(closure: EQuantityClosure): DataExpression = - DataExpressionReducer(closure.symbolTable.data, ops).reduce(closure.expression) + DataExpressionReducer(closure.symbolTable.data, closure.symbolTable.dataSources, ops).reduce(closure.expression) private fun reduceDiv(expression: EQuantityDiv): DataExpression { with(ops) { diff --git a/core/src/main/kotlin/ch/kleis/lcaac/core/lang/evaluator/reducer/LcaExpressionReducer.kt b/core/src/main/kotlin/ch/kleis/lcaac/core/lang/evaluator/reducer/LcaExpressionReducer.kt index 5407f034..e2a39e34 100644 --- a/core/src/main/kotlin/ch/kleis/lcaac/core/lang/evaluator/reducer/LcaExpressionReducer.kt +++ b/core/src/main/kotlin/ch/kleis/lcaac/core/lang/evaluator/reducer/LcaExpressionReducer.kt @@ -2,13 +2,15 @@ package ch.kleis.lcaac.core.lang.evaluator.reducer import ch.kleis.lcaac.core.lang.register.DataRegister import ch.kleis.lcaac.core.lang.expression.* +import ch.kleis.lcaac.core.lang.register.DataSourceRegister import ch.kleis.lcaac.core.math.QuantityOperations class LcaExpressionReducer( dataRegister: DataRegister = DataRegister.empty(), + dataSourceRegister: DataSourceRegister = DataSourceRegister.empty(), ops: QuantityOperations, ) { - private val dataExpressionReducer = DataExpressionReducer(dataRegister, ops) + private val dataExpressionReducer = DataExpressionReducer(dataRegister, dataSourceRegister, ops) fun reduce(expression: LcaExpression): LcaExpression { return when (expression) { diff --git a/core/src/main/kotlin/ch/kleis/lcaac/core/lang/evaluator/reducer/TemplateExpressionReducer.kt b/core/src/main/kotlin/ch/kleis/lcaac/core/lang/evaluator/reducer/TemplateExpressionReducer.kt index 04a71c9a..cc02f57e 100644 --- a/core/src/main/kotlin/ch/kleis/lcaac/core/lang/evaluator/reducer/TemplateExpressionReducer.kt +++ b/core/src/main/kotlin/ch/kleis/lcaac/core/lang/evaluator/reducer/TemplateExpressionReducer.kt @@ -7,13 +7,16 @@ import ch.kleis.lcaac.core.lang.register.Register import ch.kleis.lcaac.core.lang.evaluator.EvaluatorException import ch.kleis.lcaac.core.lang.evaluator.Helper import ch.kleis.lcaac.core.lang.expression.* +import ch.kleis.lcaac.core.lang.register.DataSourceRegister import ch.kleis.lcaac.core.math.QuantityOperations class TemplateExpressionReducer( private val ops: QuantityOperations, dataRegister: DataRegister = DataRegister.empty(), + dataSourceRegister: DataSourceRegister = DataSourceRegister.empty(), ) { private val dataRegister = Register(dataRegister) + private val dataSourceRegister = Register(dataSourceRegister) private val helper = Helper() fun reduce(expression: EProcessTemplateApplication): EProcess { @@ -32,8 +35,8 @@ class TemplateExpressionReducer( .plus(actualArguments.mapKeys { DataKey(it.key) }) .plus(template.locals.mapKeys { DataKey(it.key) }) - val reducer = LcaExpressionReducer(localRegister, ops) - val dataReducer = DataExpressionReducer(localRegister, ops) + val reducer = LcaExpressionReducer(localRegister, dataSourceRegister, ops) + val dataReducer = DataExpressionReducer(localRegister, dataSourceRegister, ops) var result = template.body actualArguments.forEach { diff --git a/core/src/main/kotlin/ch/kleis/lcaac/core/lang/evaluator/step/Reduce.kt b/core/src/main/kotlin/ch/kleis/lcaac/core/lang/evaluator/step/Reduce.kt index 86a82433..0284d526 100644 --- a/core/src/main/kotlin/ch/kleis/lcaac/core/lang/evaluator/step/Reduce.kt +++ b/core/src/main/kotlin/ch/kleis/lcaac/core/lang/evaluator/step/Reduce.kt @@ -16,7 +16,8 @@ class Reduce( ) { private val lcaReducer = LcaExpressionReducer( symbolTable.data, - ops + symbolTable.dataSources, + ops, ) private val templateReducer = TemplateExpressionReducer( ops, diff --git a/core/src/main/kotlin/ch/kleis/lcaac/core/lang/evaluator/step/ReduceLabelSelectors.kt b/core/src/main/kotlin/ch/kleis/lcaac/core/lang/evaluator/step/ReduceLabelSelectors.kt index cc52b43c..538dc3af 100644 --- a/core/src/main/kotlin/ch/kleis/lcaac/core/lang/evaluator/step/ReduceLabelSelectors.kt +++ b/core/src/main/kotlin/ch/kleis/lcaac/core/lang/evaluator/step/ReduceLabelSelectors.kt @@ -1,26 +1,26 @@ package ch.kleis.lcaac.core.lang.evaluator.step import arrow.optics.Every -import ch.kleis.lcaac.core.lang.register.DataKey -import ch.kleis.lcaac.core.lang.register.Register import ch.kleis.lcaac.core.lang.SymbolTable import ch.kleis.lcaac.core.lang.evaluator.reducer.DataExpressionReducer import ch.kleis.lcaac.core.lang.expression.* import ch.kleis.lcaac.core.lang.expression.optics.everyDataRefInDataExpression +import ch.kleis.lcaac.core.lang.register.DataKey +import ch.kleis.lcaac.core.lang.register.Register import ch.kleis.lcaac.core.math.QuantityOperations class ReduceLabelSelectors( - private val symbolTable: SymbolTable, - private val ops: QuantityOperations, + private val symbolTable: SymbolTable, + private val ops: QuantityOperations, ) { private val everyInputProduct = - EProcessTemplateApplication.template().body().inputs() compose - Every.list() compose - ETechnoExchange.product() + EProcessTemplateApplication.template().body().inputs() compose + Every.list() compose + ETechnoExchange.product() private val everyLabelSelector = everyInputProduct compose - EProductSpec.fromProcess().matchLabels().elements() compose - Every.map() compose - everyDataRefInDataExpression() + EProductSpec.fromProcess().matchLabels().elements() compose + Every.map() compose + everyDataRefInDataExpression() fun apply(expression: EProcessTemplateApplication): EProcessTemplateApplication { val template = expression.template @@ -28,11 +28,12 @@ class ReduceLabelSelectors( val actualArguments = template.params.plus(expression.arguments) val locals = template.locals val reducer = DataExpressionReducer( - Register(symbolTable.data) - .plus(actualArguments.mapKeys { DataKey(it.key) }) - .plus(labels.mapKeys { DataKey(it.key) }) - .plus(locals.mapKeys { DataKey(it.key) }), - ops, + Register(symbolTable.data) + .plus(actualArguments.mapKeys { DataKey(it.key) }) + .plus(labels.mapKeys { DataKey(it.key) }) + .plus(locals.mapKeys { DataKey(it.key) }), + symbolTable.dataSources, + ops, ) return everyLabelSelector.modify(expression) { ref -> reducer.reduce(ref) } } diff --git a/core/src/main/kotlin/ch/kleis/lcaac/core/lang/expression/DataExpression.kt b/core/src/main/kotlin/ch/kleis/lcaac/core/lang/expression/DataExpression.kt index fa4d9a5e..135603e7 100644 --- a/core/src/main/kotlin/ch/kleis/lcaac/core/lang/expression/DataExpression.kt +++ b/core/src/main/kotlin/ch/kleis/lcaac/core/lang/expression/DataExpression.kt @@ -17,6 +17,11 @@ sealed interface StringExpression Record */ +@optics +data class EAnyRecordFrom(val dataSourceRef: String): DataExpression { + companion object +} + @optics data class ERecord(val entries: Map>): DataExpression { companion object diff --git a/core/src/main/kotlin/ch/kleis/lcaac/core/lang/expression/DataSourceExpression.kt b/core/src/main/kotlin/ch/kleis/lcaac/core/lang/expression/DataSourceExpression.kt index 444023df..5f2f9d66 100644 --- a/core/src/main/kotlin/ch/kleis/lcaac/core/lang/expression/DataSourceExpression.kt +++ b/core/src/main/kotlin/ch/kleis/lcaac/core/lang/expression/DataSourceExpression.kt @@ -1,8 +1,8 @@ package ch.kleis.lcaac.core.lang.expression -sealed interface ColumnType -class CText : ColumnType -data class CQuantity(val defaultValue: DataExpression): ColumnType +data class ColumnType( + val defaultValue: DataExpression +) sealed interface DataSourceExpression { val schema: Map> diff --git a/core/src/main/kotlin/ch/kleis/lcaac/core/lang/expression/optics/EveryDataRef.kt b/core/src/main/kotlin/ch/kleis/lcaac/core/lang/expression/optics/EveryDataRef.kt index f1cb7e7b..1ac2e518 100644 --- a/core/src/main/kotlin/ch/kleis/lcaac/core/lang/expression/optics/EveryDataRef.kt +++ b/core/src/main/kotlin/ch/kleis/lcaac/core/lang/expression/optics/EveryDataRef.kt @@ -52,6 +52,7 @@ fun everyDataRefInDataExpression(): PEvery, DataExpression .map { foldMap(M, it, map) } ) is ERecordEntry -> foldMap(M, source.record, map) + is EAnyRecordFrom -> TODO() } } @@ -116,6 +117,8 @@ fun everyDataRefInDataExpression(): PEvery, DataExpression modify(source.record, map), source.index, ) + + is EAnyRecordFrom -> TODO() } } } diff --git a/core/src/main/kotlin/ch/kleis/lcaac/core/testing/BasicTestRunner.kt b/core/src/main/kotlin/ch/kleis/lcaac/core/testing/BasicTestRunner.kt index bf8f4a49..1b40de42 100644 --- a/core/src/main/kotlin/ch/kleis/lcaac/core/testing/BasicTestRunner.kt +++ b/core/src/main/kotlin/ch/kleis/lcaac/core/testing/BasicTestRunner.kt @@ -15,35 +15,39 @@ import ch.kleis.lcaac.core.math.basic.BasicOperations class BasicTestRunner( symbolTable: SymbolTable, private val evaluator: Evaluator = Evaluator(symbolTable, BasicOperations), - private val dataReducer: DataExpressionReducer = DataExpressionReducer(symbolTable.data, BasicOperations), + private val dataReducer: DataExpressionReducer = DataExpressionReducer( + symbolTable.data, + symbolTable.dataSources, + BasicOperations, + ), ) { fun run(case: TestCase): TestResult { try { - val trace = evaluator.with(case.template).trace(case.template) - val program = ContributionAnalysisProgram(trace.getSystemValue(), trace.getEntryPoint()) - val analysis = program.run() - val target = trace.getEntryPoint().products.first().port() - val results = case.assertions.map { assertion -> - val ports = analysis.findAllPortsByShortName(assertion.ref) - if (ports.isEmpty()) { - GenericFailure("unknown reference '${assertion.ref}'") - } else { - val impact = with(QuantityValueOperations(BasicOperations)) { - ports.map { - if (analysis.isControllable(it)) analysis.getPortContribution(target, it) - else analysis.supplyOf(it) - }.reduce { acc, quantityValue -> acc + quantityValue } + val trace = evaluator.with(case.template).trace(case.template) + val program = ContributionAnalysisProgram(trace.getSystemValue(), trace.getEntryPoint()) + val analysis = program.run() + val target = trace.getEntryPoint().products.first().port() + val results = case.assertions.map { assertion -> + val ports = analysis.findAllPortsByShortName(assertion.ref) + if (ports.isEmpty()) { + GenericFailure("unknown reference '${assertion.ref}'") + } else { + val impact = with(QuantityValueOperations(BasicOperations)) { + ports.map { + if (analysis.isControllable(it)) analysis.getPortContribution(target, it) + else analysis.supplyOf(it) + }.reduce { acc, quantityValue -> acc + quantityValue } + } + val lo = with(ToValue(BasicOperations)) { dataReducer.reduce(assertion.lo).toValue() } + val hi = with(ToValue(BasicOperations)) { dataReducer.reduce(assertion.hi).toValue() } + test(assertion.ref, impact, lo, hi) } - val lo = with(ToValue(BasicOperations)) { dataReducer.reduce(assertion.lo).toValue() } - val hi = with(ToValue(BasicOperations)) { dataReducer.reduce(assertion.hi).toValue() } - test(assertion.ref, impact, lo, hi) } - } - return TestResult( - case.source, - case.name, - results, - ) + return TestResult( + case.source, + case.name, + results, + ) } catch (e: EvaluatorException) { return TestResult( case.source, diff --git a/core/src/test/kotlin/ch/kleis/lcaac/core/lang/evaluator/reducer/DataExpressionReducerTest.kt b/core/src/test/kotlin/ch/kleis/lcaac/core/lang/evaluator/reducer/DataExpressionReducerTest.kt index 0d506cca..dbe341db 100644 --- a/core/src/test/kotlin/ch/kleis/lcaac/core/lang/evaluator/reducer/DataExpressionReducerTest.kt +++ b/core/src/test/kotlin/ch/kleis/lcaac/core/lang/evaluator/reducer/DataExpressionReducerTest.kt @@ -30,13 +30,14 @@ class DataExpressionReducerTest { // given val q = QuantityFixture.oneKilogram val record = ERecord(mapOf( - "mass" to EDataRef("m") + "mass" to EDataRef("m") )) val reducer = DataExpressionReducer( - Register.from(mapOf( - DataKey("m") to q - )), - BasicOperations + Register.from(mapOf( + DataKey("m") to q + )), + Register.empty(), + BasicOperations ) // when @@ -44,7 +45,7 @@ class DataExpressionReducerTest { // then val expected = ERecord(mapOf( - "mass" to q + "mass" to q )) assertEquals(expected, actual) } @@ -54,15 +55,16 @@ class DataExpressionReducerTest { // given val q = QuantityFixture.oneKilogram val record = ERecord(mapOf( - "mass" to EDataRef("m") + "mass" to EDataRef("m") )) val entry = ERecordEntry(EDataRef("my_map"), "mass") val reducer = DataExpressionReducer( - Register.from(mapOf( - DataKey("m") to q, - DataKey("my_map") to record, - )), - BasicOperations + Register.from(mapOf( + DataKey("m") to q, + DataKey("my_map") to record, + )), + Register.empty(), + BasicOperations ) // when @@ -77,14 +79,15 @@ class DataExpressionReducerTest { // given val q = QuantityFixture.oneKilogram val record = ERecord(mapOf( - "mass" to EDataRef("m") + "mass" to EDataRef("m") )) val entry = ERecordEntry(record, "mass") val reducer = DataExpressionReducer( - Register.from(mapOf( - DataKey("m") to q - )), - BasicOperations + Register.from(mapOf( + DataKey("m") to q + )), + Register.empty(), + BasicOperations ) // when @@ -99,14 +102,15 @@ class DataExpressionReducerTest { // given val q = QuantityFixture.oneKilogram val record = ERecord(mapOf( - "mass" to EDataRef("m") + "mass" to EDataRef("m") )) val entry = ERecordEntry(record, "foo") val reducer = DataExpressionReducer( - Register.from(mapOf( - DataKey("m") to q - )), - BasicOperations + Register.from(mapOf( + DataKey("m") to q + )), + Register.empty(), + BasicOperations ) // when @@ -123,8 +127,9 @@ class DataExpressionReducerTest { // given val unit = EUnitLiteral(UnitSymbol.of("a"), 123.0, Dimension.of("a")) val reducer = DataExpressionReducer( - Register.empty(), - ops, + Register.empty(), + Register.empty(), + ops, ) // when @@ -141,8 +146,9 @@ class DataExpressionReducerTest { val innerQuantity = QuantityFixture.oneKilogram val quantity = EQuantityScale(ops.pure(2.0), innerQuantity) val reducer = DataExpressionReducer( - Register.empty(), - ops, + Register.empty(), + Register.empty(), + ops, ) // when @@ -157,7 +163,7 @@ class DataExpressionReducerTest { fun test_reduce_whenScaleOfScale_shouldReduce() { // given val quantity = EQuantityScale(ops.pure(1.0), QuantityFixture.twoKilograms) - val reducer = DataExpressionReducer(Register.empty(), ops) + val reducer = DataExpressionReducer(Register.empty(), Register.empty(), ops) val expected = EQuantityScale(ops.pure(2.0), UnitFixture.kg) // when @@ -173,8 +179,9 @@ class DataExpressionReducerTest { val innerQuantity = EDataRef("a") val quantity = EQuantityScale(ops.pure(2.0), innerQuantity) val reducer = DataExpressionReducer( - Register.empty(), - ops, + Register.empty(), + Register.empty(), + ops, ) // when @@ -188,12 +195,12 @@ class DataExpressionReducerTest { fun reduce_whenLiteral_shouldReduceUnit() { // given val quantityEnvironment = DataRegister( - mapOf( - DataKey("kg") to UnitFixture.kg - ) + mapOf( + DataKey("kg") to UnitFixture.kg + ) ) val quantity = EQuantityScale(ops.pure(1.0), EDataRef("kg")) - val reducer = DataExpressionReducer(quantityEnvironment, ops) + val reducer = DataExpressionReducer(quantityEnvironment, Register.empty(), ops) // when val actual = reducer.reduce(quantity) @@ -209,8 +216,9 @@ class DataExpressionReducerTest { val a = EQuantityScale(ops.pure(2.0), UnitFixture.kg) val b = EQuantityScale(ops.pure(1000.0), UnitFixture.g) val reducer = DataExpressionReducer( - Register.empty(), - ops, + Register.empty(), + Register.empty(), + ops, ) // when @@ -227,7 +235,7 @@ class DataExpressionReducerTest { val a = UnitFixture.kg val b = UnitFixture.kg val expected = EQuantityScale(ops.pure(2.0), UnitFixture.kg) - val reducer = DataExpressionReducer(Register.empty(), ops) + val reducer = DataExpressionReducer(Register.empty(), Register.empty(), ops) // when val actual = reducer.reduce(EQuantityAdd(a, b)) @@ -243,14 +251,15 @@ class DataExpressionReducerTest { val b = EQuantityScale(ops.pure(1000.0), UnitFixture.m) val quantity = EQuantityAdd(a, b) val reducer = DataExpressionReducer( - Register.empty(), - ops, + Register.empty(), + Register.empty(), + ops, ) // when/then assertFailsWith( - EvaluatorException::class, - "incompatible dimensions: mass vs length in left=2.0 kg and right=1000.0 m" + EvaluatorException::class, + "incompatible dimensions: mass vs length in left=2.0 kg and right=1000.0 m" ) { reducer.reduce(quantity) } } @@ -260,7 +269,7 @@ class DataExpressionReducerTest { val a = UnitFixture.g val b = UnitFixture.kg val expected = EQuantityScale(ops.pure(1.001), EUnitLiteral(UnitSymbol.of("kg"), 1.0, DimensionFixture.mass)) - val reducer = DataExpressionReducer(Register.empty(), ops) + val reducer = DataExpressionReducer(Register.empty(), Register.empty(), ops) // when val actual = reducer.reduce(EQuantityAdd(a, b)) @@ -275,7 +284,7 @@ class DataExpressionReducerTest { val a = QuantityFixture.twoKilograms val b = UnitFixture.kg val expected = EQuantityScale(ops.pure(3.0), EUnitLiteral(UnitSymbol.of("kg"), 1.0, DimensionFixture.mass)) - val reducer = DataExpressionReducer(Register.empty(), ops) + val reducer = DataExpressionReducer(Register.empty(), Register.empty(), ops) // when val actual = reducer.reduce(EQuantityAdd(a, b)) @@ -290,7 +299,7 @@ class DataExpressionReducerTest { val a = UnitFixture.kg val b = QuantityFixture.twoKilograms val expected = EQuantityScale(ops.pure(3.0), EUnitLiteral(UnitSymbol.of("kg"), 1.0, DimensionFixture.mass)) - val reducer = DataExpressionReducer(Register.empty(), ops) + val reducer = DataExpressionReducer(Register.empty(), Register.empty(), ops) // when val actual = reducer.reduce(EQuantityAdd(a, b)) @@ -305,8 +314,9 @@ class DataExpressionReducerTest { val a = EQuantityScale(ops.pure(2.0), UnitFixture.kg) val b = EQuantityScale(ops.pure(1000.0), UnitFixture.g) val reducer = DataExpressionReducer( - Register.empty(), - ops, + Register.empty(), + Register.empty(), + ops, ) // when @@ -323,15 +333,16 @@ class DataExpressionReducerTest { val a = EQuantityScale(ops.pure(2.0), UnitFixture.kg) val b = EQuantityScale(ops.pure(1000.0), UnitFixture.m) val reducer = DataExpressionReducer( - Register.empty(), - ops, + Register.empty(), + Register.empty(), + ops, ) val eQuantitySub = EQuantitySub(a, b) // when/then assertFailsWith( - EvaluatorException::class, - "incompatible dimensions: mass vs length in left=2.0 kg and right=1000.0 m" + EvaluatorException::class, + "incompatible dimensions: mass vs length in left=2.0 kg and right=1000.0 m" ) { reducer.reduce(eQuantitySub) } } @@ -341,7 +352,7 @@ class DataExpressionReducerTest { val a = UnitFixture.kg val b = UnitFixture.kg val expected = EQuantityScale(ops.pure(0.0), UnitFixture.kg) - val reducer = DataExpressionReducer(Register.empty(), ops) + val reducer = DataExpressionReducer(Register.empty(), Register.empty(), ops) // when val actual = reducer.reduce(EQuantitySub(a, b)) @@ -356,7 +367,7 @@ class DataExpressionReducerTest { val a = UnitFixture.kg val b = UnitFixture.g val expected = EQuantityScale(ops.pure(0.999), EUnitLiteral(UnitSymbol.of("kg"), 1.0, DimensionFixture.mass)) - val reducer = DataExpressionReducer(Register.empty(), ops) + val reducer = DataExpressionReducer(Register.empty(), Register.empty(), ops) // when val actual = reducer.reduce(EQuantitySub(a, b)) @@ -371,7 +382,7 @@ class DataExpressionReducerTest { val a = QuantityFixture.twoKilograms val b = UnitFixture.kg val expected = EQuantityScale(ops.pure(1.0), EUnitLiteral(UnitSymbol.of("kg"), 1.0, DimensionFixture.mass)) - val reducer = DataExpressionReducer(Register.empty(), ops) + val reducer = DataExpressionReducer(Register.empty(), Register.empty(), ops) // when val actual = reducer.reduce(EQuantitySub(a, b)) @@ -386,7 +397,7 @@ class DataExpressionReducerTest { val a = UnitFixture.kg val b = EQuantityScale(ops.pure(0.5), UnitFixture.kg) val expected = EQuantityScale(ops.pure(0.5), EUnitLiteral(UnitSymbol.of("kg"), 1.0, DimensionFixture.mass)) - val reducer = DataExpressionReducer(Register.empty(), ops) + val reducer = DataExpressionReducer(Register.empty(), Register.empty(), ops) // when val actual = reducer.reduce(EQuantitySub(a, b)) @@ -401,8 +412,9 @@ class DataExpressionReducerTest { val a = EQuantityScale(ops.pure(2.0), UnitFixture.person) val b = EQuantityScale(ops.pure(2.0), UnitFixture.km) val reducer = DataExpressionReducer( - Register.empty(), - ops, + Register.empty(), + Register.empty(), + ops, ) // when @@ -410,12 +422,12 @@ class DataExpressionReducerTest { // then val expected = EQuantityScale( - ops.pure(4.0), - EUnitLiteral( - UnitSymbol.of("person").multiply(UnitSymbol.of("km")), - 1.0 * 1000.0, - Dimension.None.multiply(DimensionFixture.length) - ) + ops.pure(4.0), + EUnitLiteral( + UnitSymbol.of("person").multiply(UnitSymbol.of("km")), + 1.0 * 1000.0, + Dimension.None.multiply(DimensionFixture.length) + ) ) assertEquals(expected, actual) } @@ -426,8 +438,9 @@ class DataExpressionReducerTest { val a = UnitFixture.person val b = UnitFixture.km val reducer = DataExpressionReducer( - Register.empty(), - ops, + Register.empty(), + Register.empty(), + ops, ) // when @@ -435,12 +448,12 @@ class DataExpressionReducerTest { // then val expected = EQuantityScale( - ops.pure(1.0), - EUnitLiteral( - UnitSymbol.of("person").multiply(UnitSymbol.of("km")), - 1.0 * 1000.0, - Dimension.None.multiply(DimensionFixture.length) - ) + ops.pure(1.0), + EUnitLiteral( + UnitSymbol.of("person").multiply(UnitSymbol.of("km")), + 1.0 * 1000.0, + Dimension.None.multiply(DimensionFixture.length) + ) ) assertEquals(expected, actual) } @@ -451,15 +464,16 @@ class DataExpressionReducerTest { val a = UnitFixture.kg val b = QuantityFixture.twoKilograms val expected = - EQuantityScale( - ops.pure(2.0), - EUnitLiteral(UnitSymbol.of("kg").pow(2.0), 1.0, DimensionFixture.mass.multiply(DimensionFixture.mass)) - ) + EQuantityScale( + ops.pure(2.0), + EUnitLiteral(UnitSymbol.of("kg").pow(2.0), 1.0, DimensionFixture.mass.multiply(DimensionFixture.mass)) + ) // when val reducer = DataExpressionReducer( - Register.empty(), - ops, + Register.empty(), + Register.empty(), + ops, ) // when @@ -475,15 +489,16 @@ class DataExpressionReducerTest { val a = QuantityFixture.twoKilograms val b = UnitFixture.kg val expected = - EQuantityScale( - ops.pure(2.0), - EUnitLiteral(UnitSymbol.of("kg").pow(2.0), 1.0, DimensionFixture.mass.multiply(DimensionFixture.mass)) - ) + EQuantityScale( + ops.pure(2.0), + EUnitLiteral(UnitSymbol.of("kg").pow(2.0), 1.0, DimensionFixture.mass.multiply(DimensionFixture.mass)) + ) // when val reducer = DataExpressionReducer( - Register.empty(), - ops, + Register.empty(), + Register.empty(), + ops, ) // when @@ -499,8 +514,9 @@ class DataExpressionReducerTest { val a = EQuantityScale(ops.pure(4.0), UnitFixture.km) val b = EQuantityScale(ops.pure(2.0), UnitFixture.hour) val reducer = DataExpressionReducer( - Register.empty(), - ops, + Register.empty(), + Register.empty(), + ops, ) // when @@ -508,12 +524,12 @@ class DataExpressionReducerTest { // then val expected = EQuantityScale( - ops.pure(2.0), - EUnitLiteral( - UnitSymbol.of("km").divide(UnitSymbol.of("hour")), - 1000.0 / 3600.0, - DimensionFixture.length.divide(DimensionFixture.time) - ) + ops.pure(2.0), + EUnitLiteral( + UnitSymbol.of("km").divide(UnitSymbol.of("hour")), + 1000.0 / 3600.0, + DimensionFixture.length.divide(DimensionFixture.time) + ) ) assertEquals(expected, actual) } @@ -523,19 +539,19 @@ class DataExpressionReducerTest { // given val a = UnitFixture.km val b = UnitFixture.hour - val reducer = DataExpressionReducer(Register.empty(), ops) + val reducer = DataExpressionReducer(Register.empty(), Register.empty(), ops) // when val actual = reducer.reduce(EQuantityDiv(a, b)) // then val expected = EQuantityScale( - ops.pure(1.0), - EUnitLiteral( - UnitSymbol.of("km").divide(UnitSymbol.of("hour")), - 1000.0 / 3600.0, - DimensionFixture.length.divide(DimensionFixture.time) - ) + ops.pure(1.0), + EUnitLiteral( + UnitSymbol.of("km").divide(UnitSymbol.of("hour")), + 1000.0 / 3600.0, + DimensionFixture.length.divide(DimensionFixture.time) + ) ) assertEquals(expected, actual) } @@ -546,15 +562,15 @@ class DataExpressionReducerTest { val a = EQuantityScale(ops.pure(4.0), UnitFixture.km) val b = UnitFixture.hour val expected = EQuantityScale( - ops.pure(4.0), - EUnitLiteral( - UnitSymbol.of("km").divide(UnitSymbol.of("hour")), - 1000.0 / 3600.0, - DimensionFixture.length.divide(DimensionFixture.time) - ) + ops.pure(4.0), + EUnitLiteral( + UnitSymbol.of("km").divide(UnitSymbol.of("hour")), + 1000.0 / 3600.0, + DimensionFixture.length.divide(DimensionFixture.time) + ) ) - val reducer = DataExpressionReducer(Register.empty(), ops) + val reducer = DataExpressionReducer(Register.empty(), Register.empty(), ops) // when val actual = reducer.reduce(EQuantityDiv(a, b)) @@ -569,8 +585,9 @@ class DataExpressionReducerTest { // given val a = EQuantityScale(ops.pure(4.0), UnitFixture.km) val reducer = DataExpressionReducer( - Register.empty(), - ops, + Register.empty(), + Register.empty(), + ops, ) // when @@ -578,12 +595,12 @@ class DataExpressionReducerTest { // then val expected = EQuantityScale( - ops.pure(16.0), - EUnitLiteral( - UnitSymbol.of("km").pow(2.0), - 1e6, - DimensionFixture.length.pow(2.0) - ) + ops.pure(16.0), + EUnitLiteral( + UnitSymbol.of("km").pow(2.0), + 1e6, + DimensionFixture.length.pow(2.0) + ) ) assertEquals(expected, actual) } @@ -593,12 +610,13 @@ class DataExpressionReducerTest { // given val a = EDataRef("a") val reducer = DataExpressionReducer( - DataRegister( - mapOf( - DataKey("a") to EQuantityScale(ops.pure(1.0), UnitFixture.kg) - ) - ), - ops, + DataRegister( + mapOf( + DataKey("a") to EQuantityScale(ops.pure(1.0), UnitFixture.kg) + ) + ), + Register.empty(), + ops, ) // when @@ -620,15 +638,16 @@ class DataExpressionReducerTest { val quantityConversion = EQuantityScale(ops.pure(2.2), kg) val unitComposition = EUnitAlias("lbs", quantityConversion) val reducer = DataExpressionReducer( - Register.empty(), - ops, + Register.empty(), + Register.empty(), + ops, ) // when val actual = reducer.reduce(unitComposition) // then val expect = EQuantityScale( - ops.pure(1.0), - EUnitLiteral(UnitSymbol.of("lbs"), scale = 2.2, Dimension.of("mass")) + ops.pure(1.0), + EUnitLiteral(UnitSymbol.of("lbs"), scale = 2.2, Dimension.of("mass")) ) assertEquals(actual, expect) } @@ -640,15 +659,16 @@ class DataExpressionReducerTest { val quantityConversion = EQuantityScale(ops.pure(2200.0), g) val unitComposition = EUnitAlias("lbs", quantityConversion) val reducer = DataExpressionReducer( - Register.empty(), - ops, + Register.empty(), + Register.empty(), + ops, ) // when val actual = reducer.reduce(unitComposition) // then val expect = EQuantityScale( - ops.pure(1.0), - EUnitLiteral(UnitSymbol.of("lbs"), scale = 2.2, Dimension.of("mass")) + ops.pure(1.0), + EUnitLiteral(UnitSymbol.of("lbs"), scale = 2.2, Dimension.of("mass")) ) assertEquals(actual, expect) } @@ -657,7 +677,7 @@ class DataExpressionReducerTest { fun reduce_whenUnitCompositionComposition_shouldDeepReduce() { // given val expr = EUnitAlias("foo", EUnitAlias("bar", UnitFixture.kg)) - val reducer = DataExpressionReducer(Register.empty(), ops) + val reducer = DataExpressionReducer(Register.empty(), Register.empty(), ops) // when val actual = reducer.reduce(expr) @@ -671,16 +691,17 @@ class DataExpressionReducerTest { fun reduce_whenUnitClosure_shouldReduceWithGivenTable() { // given val symbolTable = SymbolTable( - data = DataRegister( - mapOf(DataKey("a") to UnitFixture.kg) - ), + data = DataRegister( + mapOf(DataKey("a") to UnitFixture.kg) + ), ) val unit = EQuantityClosure(symbolTable, EDataRef("a")) val reducer = DataExpressionReducer( - DataRegister( - mapOf(DataKey("a") to UnitFixture.l) - ), - ops, + DataRegister( + mapOf(DataKey("a") to UnitFixture.l) + ), + Register.empty(), + ops, ) // when @@ -696,8 +717,9 @@ class DataExpressionReducerTest { // given val kg = UnitFixture.kg val reducer = DataExpressionReducer( - Register.empty(), - ops, + Register.empty(), + Register.empty(), + ops, ) // when @@ -713,7 +735,7 @@ class DataExpressionReducerTest { // given val expr = EUnitOf(UnitFixture.l) val expected = QuantityFixture.oneLitre - val reducer = DataExpressionReducer(Register.empty(), ops) + val reducer = DataExpressionReducer(Register.empty(), Register.empty(), ops) // when val actual = reducer.reduce(expr) @@ -726,7 +748,7 @@ class DataExpressionReducerTest { fun reduce_whenUnitOfRef_shouldReturnAsIs() { // given val expr = EUnitOf(EDataRef("beer")) - val reducer = DataExpressionReducer(Register.empty(), ops) + val reducer = DataExpressionReducer(Register.empty(), Register.empty(), ops) // when val actual = reducer.reduce(expr) @@ -740,15 +762,15 @@ class DataExpressionReducerTest { // given val expr = EUnitOf(EQuantityMul(UnitFixture.kg, QuantityFixture.twoLitres)) val expected = - EQuantityScale( - ops.pure(1.0), - EUnitLiteral( - UnitSymbol.of("kg").multiply(UnitSymbol.of("l")), - 1.0e-3, - DimensionFixture.mass.multiply(DimensionFixture.volume) - ) + EQuantityScale( + ops.pure(1.0), + EUnitLiteral( + UnitSymbol.of("kg").multiply(UnitSymbol.of("l")), + 1.0e-3, + DimensionFixture.mass.multiply(DimensionFixture.volume) ) - val reducer = DataExpressionReducer(Register.empty(), ops) + ) + val reducer = DataExpressionReducer(Register.empty(), Register.empty(), ops) // when val actual = reducer.reduce(expr) @@ -762,15 +784,15 @@ class DataExpressionReducerTest { // given val expr = EUnitOf(EQuantityMul(QuantityFixture.twoLitres, UnitFixture.kg)) val expected = - EQuantityScale( - ops.pure(1.0), - EUnitLiteral( - UnitSymbol.of("l").multiply(UnitSymbol.of("kg")), - 1.0e-3, - DimensionFixture.mass.multiply(DimensionFixture.volume) - ) + EQuantityScale( + ops.pure(1.0), + EUnitLiteral( + UnitSymbol.of("l").multiply(UnitSymbol.of("kg")), + 1.0e-3, + DimensionFixture.mass.multiply(DimensionFixture.volume) ) - val reducer = DataExpressionReducer(Register.empty(), ops) + ) + val reducer = DataExpressionReducer(Register.empty(), Register.empty(), ops) // when val actual = reducer.reduce(expr) @@ -783,7 +805,7 @@ class DataExpressionReducerTest { fun reduce_whenUnitOfUnitOfRef_shouldMerge() { // given val expr = EUnitOf(EUnitOf(EDataRef("beer"))) - val reducer = DataExpressionReducer(Register.empty(), ops) + val reducer = DataExpressionReducer(Register.empty(), Register.empty(), ops) // when val actual = reducer.reduce(expr) @@ -799,8 +821,9 @@ class DataExpressionReducerTest { val kg = UnitFixture.kg val l = UnitFixture.l val reducer = DataExpressionReducer( - Register.empty(), - ops, + Register.empty(), + Register.empty(), + ops, ) // when @@ -808,12 +831,12 @@ class DataExpressionReducerTest { // then val expected = EQuantityScale( - ops.pure(1.0), - EUnitLiteral( - UnitSymbol.of("kg").divide(UnitSymbol.of("l")), - 1.0 / 1.0e-3, - DimensionFixture.mass.divide(DimensionFixture.volume), - ) + ops.pure(1.0), + EUnitLiteral( + UnitSymbol.of("kg").divide(UnitSymbol.of("l")), + 1.0 / 1.0e-3, + DimensionFixture.mass.divide(DimensionFixture.volume), + ) ) assertEquals(expected, actual) } @@ -824,8 +847,9 @@ class DataExpressionReducerTest { val kg = UnitFixture.kg val l = UnitFixture.l val reducer = DataExpressionReducer( - Register.empty(), - ops, + Register.empty(), + Register.empty(), + ops, ) // when @@ -833,12 +857,12 @@ class DataExpressionReducerTest { // then val expected = EQuantityScale( - ops.pure(1.0), - EUnitLiteral( - UnitSymbol.of("kg").multiply(UnitSymbol.of("l")), - 1.0 * 1.0e-3, - DimensionFixture.mass.multiply(DimensionFixture.volume), - ) + ops.pure(1.0), + EUnitLiteral( + UnitSymbol.of("kg").multiply(UnitSymbol.of("l")), + 1.0 * 1.0e-3, + DimensionFixture.mass.multiply(DimensionFixture.volume), + ) ) assertEquals(expected, actual) } @@ -848,8 +872,9 @@ class DataExpressionReducerTest { // given val m = UnitFixture.m val reducer = DataExpressionReducer( - Register.empty(), - ops, + Register.empty(), + Register.empty(), + ops, ) // when @@ -857,12 +882,12 @@ class DataExpressionReducerTest { // then val expected = EQuantityScale( - ops.pure(1.0), - EUnitLiteral( - UnitSymbol.of("m").pow(2.0), - 1.0.pow(2.0), - DimensionFixture.length.pow(2.0), - ) + ops.pure(1.0), + EUnitLiteral( + UnitSymbol.of("m").pow(2.0), + 1.0.pow(2.0), + DimensionFixture.length.pow(2.0), + ) ) assertEquals(expected, actual) } @@ -872,13 +897,14 @@ class DataExpressionReducerTest { // given val ref = EDataRef("kg") val units = DataRegister( - mapOf( - DataKey("kg") to UnitFixture.kg - ) + mapOf( + DataKey("kg") to UnitFixture.kg + ) ) val reducer = DataExpressionReducer( - units, - ops, + units, + Register.empty(), + ops, ) // when @@ -899,8 +925,9 @@ class DataExpressionReducerTest { // given val expression = EStringLiteral("FR") val reducer = DataExpressionReducer( - DataRegister.empty(), - ops, + DataRegister.empty(), + Register.empty(), + ops, ) // when @@ -915,13 +942,14 @@ class DataExpressionReducerTest { // given val expression = EDataRef("geo") val reducer = DataExpressionReducer( - DataRegister( - mapOf( - DataKey("geo") to EDataRef("geo2"), - DataKey("geo2") to EStringLiteral("FR"), - ) - ), - ops, + DataRegister( + mapOf( + DataKey("geo") to EDataRef("geo2"), + DataKey("geo2") to EStringLiteral("FR"), + ) + ), + Register.empty(), + ops, ) // when @@ -937,13 +965,14 @@ class DataExpressionReducerTest { // given val expression = EDataRef("foo") val reducer = DataExpressionReducer( - DataRegister( - mapOf( - DataKey("geo") to EDataRef("geo2"), - DataKey("geo2") to EStringLiteral("FR"), - ) - ), - ops, + DataRegister( + mapOf( + DataKey("geo") to EDataRef("geo2"), + DataKey("geo2") to EStringLiteral("FR"), + ) + ), + Register.empty(), + ops, ) // when diff --git a/core/src/test/kotlin/ch/kleis/lcaac/core/lang/evaluator/reducer/LcaExpressionReducerTest.kt b/core/src/test/kotlin/ch/kleis/lcaac/core/lang/evaluator/reducer/LcaExpressionReducerTest.kt index 0107e914..0df798a1 100644 --- a/core/src/test/kotlin/ch/kleis/lcaac/core/lang/evaluator/reducer/LcaExpressionReducerTest.kt +++ b/core/src/test/kotlin/ch/kleis/lcaac/core/lang/evaluator/reducer/LcaExpressionReducerTest.kt @@ -4,6 +4,7 @@ import ch.kleis.lcaac.core.lang.register.DataKey import ch.kleis.lcaac.core.lang.register.DataRegister import ch.kleis.lcaac.core.lang.expression.* import ch.kleis.lcaac.core.lang.fixture.* +import ch.kleis.lcaac.core.lang.register.Register import ch.kleis.lcaac.core.math.basic.BasicNumber import ch.kleis.lcaac.core.math.basic.BasicOperations import org.junit.jupiter.api.Test @@ -30,6 +31,7 @@ class LcaExpressionReducerTest { DataRegister( mapOf(DataKey("geo") to EStringLiteral("FR")) ), + Register.empty(), ops, ) @@ -74,6 +76,7 @@ class LcaExpressionReducerTest { "q_propanol" to QuantityFixture.oneKilogram, ).mapKeys { DataKey(it.key) } ), + Register.empty(), ops, ) @@ -118,6 +121,7 @@ class LcaExpressionReducerTest { DataKey("q") to QuantityFixture.oneKilogram, ) ), + Register.empty(), ops, ) @@ -145,6 +149,7 @@ class LcaExpressionReducerTest { DataKey("q") to QuantityFixture.oneKilogram, ) ), + Register.empty(), ops, ) @@ -172,6 +177,7 @@ class LcaExpressionReducerTest { DataKey("q") to QuantityFixture.oneKilogram, ) ), + Register.empty(), ops, ) @@ -199,6 +205,7 @@ class LcaExpressionReducerTest { DataKey("kg") to UnitFixture.kg ) ), + Register.empty(), ops, ) @@ -230,6 +237,7 @@ class LcaExpressionReducerTest { DataKey("kg") to UnitFixture.kg ) ), + Register.empty(), ops, ) @@ -261,6 +269,7 @@ class LcaExpressionReducerTest { DataKey("kg") to UnitFixture.kg ) ), + Register.empty(), ops, ) @@ -288,6 +297,7 @@ class LcaExpressionReducerTest { DataKey("kg") to UnitFixture.kg ) ), + Register.empty(), ops, ) @@ -323,6 +333,7 @@ class LcaExpressionReducerTest { "kg" to UnitFixture.kg ).mapKeys { DataKey(it.key) } ), + Register.empty(), ops, ) @@ -366,6 +377,7 @@ class LcaExpressionReducerTest { "q_cc" to QuantityFixture.oneKilogram, ).mapKeys { DataKey(it.key) } ), + Register.empty(), ops, ) From db57eff49bf53f534cead21f752c3cf86d85b86d Mon Sep 17 00:00:00 2001 From: Peva Blanchard Date: Sat, 20 Jan 2024 00:16:58 +0100 Subject: [PATCH 05/37] core: default record of data source --- .../lcaac/core/lang/evaluator/ToValue.kt | 2 +- .../reducer/DataExpressionReducer.kt | 10 +++- .../core/lang/expression/DataExpression.kt | 2 +- .../lang/expression/optics/EveryDataRef.kt | 4 +- .../reducer/DataExpressionReducerTest.kt | 56 +++++++++++++++++++ 5 files changed, 69 insertions(+), 5 deletions(-) diff --git a/core/src/main/kotlin/ch/kleis/lcaac/core/lang/evaluator/ToValue.kt b/core/src/main/kotlin/ch/kleis/lcaac/core/lang/evaluator/ToValue.kt index f3195e98..26cbb11c 100644 --- a/core/src/main/kotlin/ch/kleis/lcaac/core/lang/evaluator/ToValue.kt +++ b/core/src/main/kotlin/ch/kleis/lcaac/core/lang/evaluator/ToValue.kt @@ -138,7 +138,7 @@ class ToValue( is StringExpression -> e.toValue() is ERecord -> RecordValue(e.entries.mapValues { it.value.toValue() }) is EDataRef, is ERecordEntry -> throw EvaluatorException("$it is not reduced") - is EAnyRecordFrom -> TODO() + is EDefaultRecordOf -> TODO() } }, ) diff --git a/core/src/main/kotlin/ch/kleis/lcaac/core/lang/evaluator/reducer/DataExpressionReducer.kt b/core/src/main/kotlin/ch/kleis/lcaac/core/lang/evaluator/reducer/DataExpressionReducer.kt index d3d7e43b..c65474ac 100644 --- a/core/src/main/kotlin/ch/kleis/lcaac/core/lang/evaluator/reducer/DataExpressionReducer.kt +++ b/core/src/main/kotlin/ch/kleis/lcaac/core/lang/evaluator/reducer/DataExpressionReducer.kt @@ -5,6 +5,7 @@ import ch.kleis.lcaac.core.lang.evaluator.EvaluatorException import ch.kleis.lcaac.core.lang.expression.* import ch.kleis.lcaac.core.lang.register.DataKey import ch.kleis.lcaac.core.lang.register.DataRegister +import ch.kleis.lcaac.core.lang.register.DataSourceKey import ch.kleis.lcaac.core.lang.register.DataSourceRegister import ch.kleis.lcaac.core.math.QuantityOperations import kotlin.math.pow @@ -35,11 +36,18 @@ class DataExpressionReducer( is EStringLiteral -> expression is ERecord -> reduceMap(expression) is ERecordEntry -> reduceMapEntry(expression) - is EAnyRecordFrom -> TODO() + is EDefaultRecordOf -> reduceDefaultRecordOf(expression) } } } + private fun reduceDefaultRecordOf(expression: EDefaultRecordOf): DataExpression { + val dataSource = dataSourceRegister[DataSourceKey( expression.dataSourceRef)] + ?: throw EvaluatorException("unknown data source '${expression.dataSourceRef}'") + val schema = dataSource.schema + return ERecord(schema.mapValues { reduce(it.value.defaultValue) }) + } + private fun reduceMapEntry(expression: ERecordEntry): DataExpression { return when (val map = reduce(expression.record)) { is ERecord -> map.entries[expression.index] diff --git a/core/src/main/kotlin/ch/kleis/lcaac/core/lang/expression/DataExpression.kt b/core/src/main/kotlin/ch/kleis/lcaac/core/lang/expression/DataExpression.kt index 135603e7..01c65df5 100644 --- a/core/src/main/kotlin/ch/kleis/lcaac/core/lang/expression/DataExpression.kt +++ b/core/src/main/kotlin/ch/kleis/lcaac/core/lang/expression/DataExpression.kt @@ -18,7 +18,7 @@ sealed interface StringExpression */ @optics -data class EAnyRecordFrom(val dataSourceRef: String): DataExpression { +data class EDefaultRecordOf(val dataSourceRef: String): DataExpression { companion object } diff --git a/core/src/main/kotlin/ch/kleis/lcaac/core/lang/expression/optics/EveryDataRef.kt b/core/src/main/kotlin/ch/kleis/lcaac/core/lang/expression/optics/EveryDataRef.kt index 1ac2e518..786fa886 100644 --- a/core/src/main/kotlin/ch/kleis/lcaac/core/lang/expression/optics/EveryDataRef.kt +++ b/core/src/main/kotlin/ch/kleis/lcaac/core/lang/expression/optics/EveryDataRef.kt @@ -52,7 +52,7 @@ fun everyDataRefInDataExpression(): PEvery, DataExpression .map { foldMap(M, it, map) } ) is ERecordEntry -> foldMap(M, source.record, map) - is EAnyRecordFrom -> TODO() + is EDefaultRecordOf -> TODO() } } @@ -118,7 +118,7 @@ fun everyDataRefInDataExpression(): PEvery, DataExpression source.index, ) - is EAnyRecordFrom -> TODO() + is EDefaultRecordOf -> TODO() } } } diff --git a/core/src/test/kotlin/ch/kleis/lcaac/core/lang/evaluator/reducer/DataExpressionReducerTest.kt b/core/src/test/kotlin/ch/kleis/lcaac/core/lang/evaluator/reducer/DataExpressionReducerTest.kt index dbe341db..c866a5d9 100644 --- a/core/src/test/kotlin/ch/kleis/lcaac/core/lang/evaluator/reducer/DataExpressionReducerTest.kt +++ b/core/src/test/kotlin/ch/kleis/lcaac/core/lang/evaluator/reducer/DataExpressionReducerTest.kt @@ -10,6 +10,7 @@ import ch.kleis.lcaac.core.lang.fixture.QuantityFixture import ch.kleis.lcaac.core.lang.fixture.UnitFixture import ch.kleis.lcaac.core.lang.register.DataKey import ch.kleis.lcaac.core.lang.register.DataRegister +import ch.kleis.lcaac.core.lang.register.DataSourceKey import ch.kleis.lcaac.core.lang.register.Register import ch.kleis.lcaac.core.math.basic.BasicNumber import ch.kleis.lcaac.core.math.basic.BasicOperations @@ -25,6 +26,61 @@ class DataExpressionReducerTest { /* RECORD */ + @Test + fun reduce_whenDefaultRecordOfDataSourceRef() { + // given + val dataSource = ECsvSource( + location = "source.csv", + schema = mapOf( + "x" to ColumnType(EDataRef("a")), + ) + ) + val record = EDefaultRecordOf("source") + val reducer = DataExpressionReducer( + Register.from(mapOf( + DataKey("a") to QuantityFixture.oneKilogram, + )), + Register.from(mapOf( + DataSourceKey("source") to dataSource, + )), + BasicOperations, + ) + + // when + val actual = reducer.reduce(record) + + // then + val expected = ERecord(mapOf( + "x" to QuantityFixture.oneKilogram, + )) + assertEquals(expected, actual) + } + + @Test + fun reduce_whenDefaultRecordOfDataSourceRef_invalidRef() { + // given + val dataSource = ECsvSource( + location = "source.csv", + schema = mapOf( + "x" to ColumnType(EDataRef("a")), + ) + ) + val record = EDefaultRecordOf("foo") + val reducer = DataExpressionReducer( + Register.from(mapOf( + DataKey("a") to QuantityFixture.oneKilogram, + )), + Register.from(mapOf( + DataSourceKey("source") to dataSource, + )), + BasicOperations, + ) + + // when/then + val e = assertThrows { reducer.reduce(record) } + assertEquals("unknown data source 'foo'", e.message) + } + @Test fun reduce_whenRecord() { // given From 5c636d647d45013288351fb76e4e357a316b42d5 Mon Sep 17 00:00:00 2001 From: Peva Blanchard Date: Sat, 20 Jan 2024 00:45:33 +0100 Subject: [PATCH 06/37] core: inject data source operations in relevant parts --- .../core/datasource/DataSourceOperations.kt | 2 + .../lcaac/core/lang/evaluator/Evaluator.kt | 6 +- .../lcaac/core/lang/evaluator/ToValue.kt | 4 +- .../core/lang/evaluator/protocol/Oracle.kt | 6 +- .../reducer/DataExpressionReducer.kt | 25 +++--- .../evaluator/reducer/LcaExpressionReducer.kt | 4 +- .../reducer/TemplateExpressionReducer.kt | 6 +- .../lcaac/core/lang/evaluator/step/Reduce.kt | 4 + .../evaluator/step/ReduceLabelSelectors.kt | 31 +++---- .../core/lang/expression/DataExpression.kt | 11 +++ .../lang/expression/optics/EveryDataRef.kt | 6 +- .../lcaac/core/testing/BasicTestRunner.kt | 5 +- .../core/lang/evaluator/EvaluatorTest.kt | 25 +++--- .../reducer/DataExpressionReducerTest.kt | 84 +++++++++++++------ .../reducer/LcaExpressionReducerTest.kt | 18 +++- .../ProcessTemplateExpressionReducerTest.kt | 7 +- .../step/ReduceLabelSelectorsTest.kt | 17 ++-- .../core/lang/evaluator/step/ReduceTest.kt | 12 ++- .../lcaac/core/testing/BasicTestRunnerTest.kt | 8 +- 19 files changed, 191 insertions(+), 90 deletions(-) diff --git a/core/src/main/kotlin/ch/kleis/lcaac/core/datasource/DataSourceOperations.kt b/core/src/main/kotlin/ch/kleis/lcaac/core/datasource/DataSourceOperations.kt index 2acd5bfa..256724c8 100644 --- a/core/src/main/kotlin/ch/kleis/lcaac/core/datasource/DataSourceOperations.kt +++ b/core/src/main/kotlin/ch/kleis/lcaac/core/datasource/DataSourceOperations.kt @@ -1,8 +1,10 @@ package ch.kleis.lcaac.core.datasource +import ch.kleis.lcaac.core.lang.expression.DataExpression import ch.kleis.lcaac.core.lang.expression.DataSourceExpression import ch.kleis.lcaac.core.lang.expression.ERecord interface DataSourceOperations { fun readAll(source: DataSourceExpression): Sequence> + fun sum(source: DataSourceExpression, column: String): DataExpression } diff --git a/core/src/main/kotlin/ch/kleis/lcaac/core/lang/evaluator/Evaluator.kt b/core/src/main/kotlin/ch/kleis/lcaac/core/lang/evaluator/Evaluator.kt index 07ad1528..9e1d0381 100644 --- a/core/src/main/kotlin/ch/kleis/lcaac/core/lang/evaluator/Evaluator.kt +++ b/core/src/main/kotlin/ch/kleis/lcaac/core/lang/evaluator/Evaluator.kt @@ -1,5 +1,6 @@ package ch.kleis.lcaac.core.lang.evaluator +import ch.kleis.lcaac.core.datasource.DataSourceOperations import ch.kleis.lcaac.core.lang.SymbolTable import ch.kleis.lcaac.core.lang.evaluator.protocol.Learner import ch.kleis.lcaac.core.lang.evaluator.protocol.Oracle @@ -11,13 +12,14 @@ import org.slf4j.LoggerFactory class Evaluator( private val symbolTable: SymbolTable, private val ops: QuantityOperations, + private val sourceOps: DataSourceOperations, ) { @Suppress("PrivatePropertyName") private val LOG = LoggerFactory.getLogger(Evaluator::class.java) fun trace(initialRequests: Set>): EvaluationTrace { val learner = Learner(initialRequests, ops) - val oracle = Oracle(symbolTable, ops) + val oracle = Oracle(symbolTable, ops, sourceOps) LOG.info("Start evaluation") try { var requests = learner.start() @@ -41,7 +43,7 @@ class Evaluator( mapOf(processKey to template) ) ) - return Evaluator(st, ops) + return Evaluator(st, ops, sourceOps) } fun trace( diff --git a/core/src/main/kotlin/ch/kleis/lcaac/core/lang/evaluator/ToValue.kt b/core/src/main/kotlin/ch/kleis/lcaac/core/lang/evaluator/ToValue.kt index 26cbb11c..ffc1dde4 100644 --- a/core/src/main/kotlin/ch/kleis/lcaac/core/lang/evaluator/ToValue.kt +++ b/core/src/main/kotlin/ch/kleis/lcaac/core/lang/evaluator/ToValue.kt @@ -137,8 +137,8 @@ class ToValue( is QuantityExpression<*> -> e.toValue() is StringExpression -> e.toValue() is ERecord -> RecordValue(e.entries.mapValues { it.value.toValue() }) - is EDataRef, is ERecordEntry -> throw EvaluatorException("$it is not reduced") - is EDefaultRecordOf -> TODO() + is EDataRef, is ERecordEntry, + is EDefaultRecordOf, is ESum -> throw EvaluatorException("$it is not reduced") } }, ) diff --git a/core/src/main/kotlin/ch/kleis/lcaac/core/lang/evaluator/protocol/Oracle.kt b/core/src/main/kotlin/ch/kleis/lcaac/core/lang/evaluator/protocol/Oracle.kt index 38c7c9c1..4147bfb3 100644 --- a/core/src/main/kotlin/ch/kleis/lcaac/core/lang/evaluator/protocol/Oracle.kt +++ b/core/src/main/kotlin/ch/kleis/lcaac/core/lang/evaluator/protocol/Oracle.kt @@ -1,5 +1,6 @@ package ch.kleis.lcaac.core.lang.evaluator.protocol +import ch.kleis.lcaac.core.datasource.DataSourceOperations import ch.kleis.lcaac.core.lang.SymbolTable import ch.kleis.lcaac.core.lang.evaluator.step.CompleteTerminals import ch.kleis.lcaac.core.lang.evaluator.step.Reduce @@ -13,9 +14,10 @@ import ch.kleis.lcaac.core.math.QuantityOperations class Oracle( val symbolTable: SymbolTable, val ops: QuantityOperations, + private val sourceOps: DataSourceOperations, ) { - private val reduceLabelSelectors = ReduceLabelSelectors(symbolTable, ops) - private val reduceDataExpressions = Reduce(symbolTable, ops) + private val reduceLabelSelectors = ReduceLabelSelectors(symbolTable, ops, sourceOps) + private val reduceDataExpressions = Reduce(symbolTable, ops, sourceOps) private val completeTerminals = CompleteTerminals(ops) private val processResolver = ProcessResolver(symbolTable) private val substanceCharacterizationResolver = SubstanceCharacterizationResolver(symbolTable) diff --git a/core/src/main/kotlin/ch/kleis/lcaac/core/lang/evaluator/reducer/DataExpressionReducer.kt b/core/src/main/kotlin/ch/kleis/lcaac/core/lang/evaluator/reducer/DataExpressionReducer.kt index c65474ac..9919498b 100644 --- a/core/src/main/kotlin/ch/kleis/lcaac/core/lang/evaluator/reducer/DataExpressionReducer.kt +++ b/core/src/main/kotlin/ch/kleis/lcaac/core/lang/evaluator/reducer/DataExpressionReducer.kt @@ -1,5 +1,6 @@ package ch.kleis.lcaac.core.lang.evaluator.reducer +import ch.kleis.lcaac.core.datasource.DataSourceOperations import ch.kleis.lcaac.core.lang.dimension.UnitSymbol import ch.kleis.lcaac.core.lang.evaluator.EvaluatorException import ch.kleis.lcaac.core.lang.expression.* @@ -14,6 +15,7 @@ class DataExpressionReducer( dataRegister: DataRegister, dataSourceRegister: DataSourceRegister, private val ops: QuantityOperations, + private val sourceOps: DataSourceOperations, ) { private val dataRegister = DataRegister(dataRegister) private val dataSourceRegister = DataSourceRegister(dataSourceRegister) @@ -37,12 +39,13 @@ class DataExpressionReducer( is ERecord -> reduceMap(expression) is ERecordEntry -> reduceMapEntry(expression) is EDefaultRecordOf -> reduceDefaultRecordOf(expression) + is ESum -> TODO() } } } private fun reduceDefaultRecordOf(expression: EDefaultRecordOf): DataExpression { - val dataSource = dataSourceRegister[DataSourceKey( expression.dataSourceRef)] + val dataSource = dataSourceRegister[DataSourceKey(expression.dataSourceRef)] ?: throw EvaluatorException("unknown data source '${expression.dataSourceRef}'") val schema = dataSource.schema return ERecord(schema.mapValues { reduce(it.value.defaultValue) }) @@ -51,16 +54,17 @@ class DataExpressionReducer( private fun reduceMapEntry(expression: ERecordEntry): DataExpression { return when (val map = reduce(expression.record)) { is ERecord -> map.entries[expression.index] - ?: throw EvaluatorException("invalid index: '${expression.index}' not in ${map.entries.keys}") + ?: throw EvaluatorException("invalid index: '${expression.index}' not in ${map.entries.keys}") + else -> ERecordEntry(map, expression.index) } } private fun reduceMap(expression: ERecord): DataExpression { return ERecord( - expression.entries.mapValues { - reduce(it.value) - } + expression.entries.mapValues { + reduce(it.value) + } ) } @@ -98,7 +102,8 @@ class DataExpressionReducer( } private fun reduceClosure(closure: EQuantityClosure): DataExpression = - DataExpressionReducer(closure.symbolTable.data, closure.symbolTable.dataSources, ops).reduce(closure.expression) + DataExpressionReducer(closure.symbolTable.data, closure.symbolTable.dataSources, ops, sourceOps) + .reduce(closure.expression) private fun reduceDiv(expression: EQuantityDiv): DataExpression { with(ops) { @@ -200,10 +205,10 @@ class DataExpressionReducer( aliasForExpression is EQuantityScale && aliasForExpression.base is EUnitLiteral -> { EQuantityScale( pure(1.0), EUnitLiteral( - UnitSymbol.of(expression.symbol), - aliasForExpression.scale.toDouble() * aliasForExpression.base.scale, - aliasForExpression.base.dimension - ) + UnitSymbol.of(expression.symbol), + aliasForExpression.scale.toDouble() * aliasForExpression.base.scale, + aliasForExpression.base.dimension + ) ) } diff --git a/core/src/main/kotlin/ch/kleis/lcaac/core/lang/evaluator/reducer/LcaExpressionReducer.kt b/core/src/main/kotlin/ch/kleis/lcaac/core/lang/evaluator/reducer/LcaExpressionReducer.kt index e2a39e34..2d7e1c7f 100644 --- a/core/src/main/kotlin/ch/kleis/lcaac/core/lang/evaluator/reducer/LcaExpressionReducer.kt +++ b/core/src/main/kotlin/ch/kleis/lcaac/core/lang/evaluator/reducer/LcaExpressionReducer.kt @@ -1,5 +1,6 @@ package ch.kleis.lcaac.core.lang.evaluator.reducer +import ch.kleis.lcaac.core.datasource.DataSourceOperations import ch.kleis.lcaac.core.lang.register.DataRegister import ch.kleis.lcaac.core.lang.expression.* import ch.kleis.lcaac.core.lang.register.DataSourceRegister @@ -9,8 +10,9 @@ class LcaExpressionReducer( dataRegister: DataRegister = DataRegister.empty(), dataSourceRegister: DataSourceRegister = DataSourceRegister.empty(), ops: QuantityOperations, + sourceOps: DataSourceOperations, ) { - private val dataExpressionReducer = DataExpressionReducer(dataRegister, dataSourceRegister, ops) + private val dataExpressionReducer = DataExpressionReducer(dataRegister, dataSourceRegister, ops, sourceOps) fun reduce(expression: LcaExpression): LcaExpression { return when (expression) { diff --git a/core/src/main/kotlin/ch/kleis/lcaac/core/lang/evaluator/reducer/TemplateExpressionReducer.kt b/core/src/main/kotlin/ch/kleis/lcaac/core/lang/evaluator/reducer/TemplateExpressionReducer.kt index cc02f57e..fe88a089 100644 --- a/core/src/main/kotlin/ch/kleis/lcaac/core/lang/evaluator/reducer/TemplateExpressionReducer.kt +++ b/core/src/main/kotlin/ch/kleis/lcaac/core/lang/evaluator/reducer/TemplateExpressionReducer.kt @@ -1,6 +1,7 @@ package ch.kleis.lcaac.core.lang.evaluator.reducer import arrow.optics.Every +import ch.kleis.lcaac.core.datasource.DataSourceOperations import ch.kleis.lcaac.core.lang.register.DataKey import ch.kleis.lcaac.core.lang.register.DataRegister import ch.kleis.lcaac.core.lang.register.Register @@ -12,6 +13,7 @@ import ch.kleis.lcaac.core.math.QuantityOperations class TemplateExpressionReducer( private val ops: QuantityOperations, + private val sourceOps: DataSourceOperations, dataRegister: DataRegister = DataRegister.empty(), dataSourceRegister: DataSourceRegister = DataSourceRegister.empty(), ) { @@ -35,8 +37,8 @@ class TemplateExpressionReducer( .plus(actualArguments.mapKeys { DataKey(it.key) }) .plus(template.locals.mapKeys { DataKey(it.key) }) - val reducer = LcaExpressionReducer(localRegister, dataSourceRegister, ops) - val dataReducer = DataExpressionReducer(localRegister, dataSourceRegister, ops) + val reducer = LcaExpressionReducer(localRegister, dataSourceRegister, ops, sourceOps) + val dataReducer = DataExpressionReducer(localRegister, dataSourceRegister, ops, sourceOps) var result = template.body actualArguments.forEach { diff --git a/core/src/main/kotlin/ch/kleis/lcaac/core/lang/evaluator/step/Reduce.kt b/core/src/main/kotlin/ch/kleis/lcaac/core/lang/evaluator/step/Reduce.kt index 0284d526..9c94a4d6 100644 --- a/core/src/main/kotlin/ch/kleis/lcaac/core/lang/evaluator/step/Reduce.kt +++ b/core/src/main/kotlin/ch/kleis/lcaac/core/lang/evaluator/step/Reduce.kt @@ -1,5 +1,6 @@ package ch.kleis.lcaac.core.lang.evaluator.step +import ch.kleis.lcaac.core.datasource.DataSourceOperations import ch.kleis.lcaac.core.lang.SymbolTable import ch.kleis.lcaac.core.lang.evaluator.EvaluatorException import ch.kleis.lcaac.core.lang.evaluator.Helper @@ -13,14 +14,17 @@ import ch.kleis.lcaac.core.math.QuantityOperations class Reduce( symbolTable: SymbolTable, ops: QuantityOperations, + sourceOps: DataSourceOperations, ) { private val lcaReducer = LcaExpressionReducer( symbolTable.data, symbolTable.dataSources, ops, + sourceOps, ) private val templateReducer = TemplateExpressionReducer( ops, + sourceOps, symbolTable.data, ) diff --git a/core/src/main/kotlin/ch/kleis/lcaac/core/lang/evaluator/step/ReduceLabelSelectors.kt b/core/src/main/kotlin/ch/kleis/lcaac/core/lang/evaluator/step/ReduceLabelSelectors.kt index 538dc3af..4671b94c 100644 --- a/core/src/main/kotlin/ch/kleis/lcaac/core/lang/evaluator/step/ReduceLabelSelectors.kt +++ b/core/src/main/kotlin/ch/kleis/lcaac/core/lang/evaluator/step/ReduceLabelSelectors.kt @@ -1,6 +1,7 @@ package ch.kleis.lcaac.core.lang.evaluator.step import arrow.optics.Every +import ch.kleis.lcaac.core.datasource.DataSourceOperations import ch.kleis.lcaac.core.lang.SymbolTable import ch.kleis.lcaac.core.lang.evaluator.reducer.DataExpressionReducer import ch.kleis.lcaac.core.lang.expression.* @@ -10,17 +11,18 @@ import ch.kleis.lcaac.core.lang.register.Register import ch.kleis.lcaac.core.math.QuantityOperations class ReduceLabelSelectors( - private val symbolTable: SymbolTable, - private val ops: QuantityOperations, + private val symbolTable: SymbolTable, + private val ops: QuantityOperations, + private val sourceOps: DataSourceOperations, ) { private val everyInputProduct = - EProcessTemplateApplication.template().body().inputs() compose - Every.list() compose - ETechnoExchange.product() + EProcessTemplateApplication.template().body().inputs() compose + Every.list() compose + ETechnoExchange.product() private val everyLabelSelector = everyInputProduct compose - EProductSpec.fromProcess().matchLabels().elements() compose - Every.map() compose - everyDataRefInDataExpression() + EProductSpec.fromProcess().matchLabels().elements() compose + Every.map() compose + everyDataRefInDataExpression() fun apply(expression: EProcessTemplateApplication): EProcessTemplateApplication { val template = expression.template @@ -28,12 +30,13 @@ class ReduceLabelSelectors( val actualArguments = template.params.plus(expression.arguments) val locals = template.locals val reducer = DataExpressionReducer( - Register(symbolTable.data) - .plus(actualArguments.mapKeys { DataKey(it.key) }) - .plus(labels.mapKeys { DataKey(it.key) }) - .plus(locals.mapKeys { DataKey(it.key) }), - symbolTable.dataSources, - ops, + Register(symbolTable.data) + .plus(actualArguments.mapKeys { DataKey(it.key) }) + .plus(labels.mapKeys { DataKey(it.key) }) + .plus(locals.mapKeys { DataKey(it.key) }), + symbolTable.dataSources, + ops, + sourceOps, ) return everyLabelSelector.modify(expression) { ref -> reducer.reduce(ref) } } diff --git a/core/src/main/kotlin/ch/kleis/lcaac/core/lang/expression/DataExpression.kt b/core/src/main/kotlin/ch/kleis/lcaac/core/lang/expression/DataExpression.kt index 01c65df5..c3791bae 100644 --- a/core/src/main/kotlin/ch/kleis/lcaac/core/lang/expression/DataExpression.kt +++ b/core/src/main/kotlin/ch/kleis/lcaac/core/lang/expression/DataExpression.kt @@ -32,6 +32,17 @@ data class ERecordEntry(val record: DataExpression, val index: String): Da companion object } +/* + Column operations + */ + +sealed interface ColumnOperationExpression + +@optics +data class ESum(val dataSourceRef: String, val column: String): DataExpression, ColumnOperationExpression { + companion object +} + /* Ref */ diff --git a/core/src/main/kotlin/ch/kleis/lcaac/core/lang/expression/optics/EveryDataRef.kt b/core/src/main/kotlin/ch/kleis/lcaac/core/lang/expression/optics/EveryDataRef.kt index 786fa886..3e8c6bba 100644 --- a/core/src/main/kotlin/ch/kleis/lcaac/core/lang/expression/optics/EveryDataRef.kt +++ b/core/src/main/kotlin/ch/kleis/lcaac/core/lang/expression/optics/EveryDataRef.kt @@ -52,7 +52,8 @@ fun everyDataRefInDataExpression(): PEvery, DataExpression .map { foldMap(M, it, map) } ) is ERecordEntry -> foldMap(M, source.record, map) - is EDefaultRecordOf -> TODO() + is EDefaultRecordOf -> M.empty() + is ESum -> TODO() } } @@ -118,7 +119,8 @@ fun everyDataRefInDataExpression(): PEvery, DataExpression source.index, ) - is EDefaultRecordOf -> TODO() + is EDefaultRecordOf -> source + is ESum -> TODO() } } } diff --git a/core/src/main/kotlin/ch/kleis/lcaac/core/testing/BasicTestRunner.kt b/core/src/main/kotlin/ch/kleis/lcaac/core/testing/BasicTestRunner.kt index 1b40de42..2a2565f5 100644 --- a/core/src/main/kotlin/ch/kleis/lcaac/core/testing/BasicTestRunner.kt +++ b/core/src/main/kotlin/ch/kleis/lcaac/core/testing/BasicTestRunner.kt @@ -1,6 +1,7 @@ package ch.kleis.lcaac.core.testing import ch.kleis.lcaac.core.assessment.ContributionAnalysisProgram +import ch.kleis.lcaac.core.datasource.DataSourceOperations import ch.kleis.lcaac.core.lang.SymbolTable import ch.kleis.lcaac.core.lang.evaluator.Evaluator import ch.kleis.lcaac.core.lang.evaluator.EvaluatorException @@ -14,11 +15,13 @@ import ch.kleis.lcaac.core.math.basic.BasicOperations class BasicTestRunner( symbolTable: SymbolTable, - private val evaluator: Evaluator = Evaluator(symbolTable, BasicOperations), + sourceOps: DataSourceOperations, + private val evaluator: Evaluator = Evaluator(symbolTable, BasicOperations, sourceOps), private val dataReducer: DataExpressionReducer = DataExpressionReducer( symbolTable.data, symbolTable.dataSources, BasicOperations, + sourceOps, ), ) { fun run(case: TestCase): TestResult { diff --git a/core/src/test/kotlin/ch/kleis/lcaac/core/lang/evaluator/EvaluatorTest.kt b/core/src/test/kotlin/ch/kleis/lcaac/core/lang/evaluator/EvaluatorTest.kt index f24610a6..691bace2 100644 --- a/core/src/test/kotlin/ch/kleis/lcaac/core/lang/evaluator/EvaluatorTest.kt +++ b/core/src/test/kotlin/ch/kleis/lcaac/core/lang/evaluator/EvaluatorTest.kt @@ -1,5 +1,6 @@ package ch.kleis.lcaac.core.lang.evaluator +import ch.kleis.lcaac.core.datasource.DataSourceOperations import ch.kleis.lcaac.core.lang.* import ch.kleis.lcaac.core.lang.dimension.UnitSymbol import ch.kleis.lcaac.core.lang.expression.* @@ -11,6 +12,7 @@ import ch.kleis.lcaac.core.lang.register.SubstanceKey import ch.kleis.lcaac.core.lang.value.* import ch.kleis.lcaac.core.math.basic.BasicNumber import ch.kleis.lcaac.core.math.basic.BasicOperations +import io.mockk.mockk import org.junit.jupiter.api.Test import org.junit.jupiter.api.Timeout import org.junit.jupiter.api.assertThrows @@ -20,6 +22,7 @@ import kotlin.test.assertNotEquals class EvaluatorTest { private val ops = BasicOperations + private val sourceOps = mockk>() @Test fun with_whenNewTemplate() { @@ -35,7 +38,7 @@ class EvaluatorTest { ), ) ) - val evaluator = Evaluator(SymbolTable.empty(), BasicOperations) + val evaluator = Evaluator(SymbolTable.empty(), ops, sourceOps) .with(template) val expected = ImpactValue( QuantityValueFixture.oneKilogram, @@ -68,7 +71,7 @@ class EvaluatorTest { ProcessKey("eProcess") to template )) ) - val evaluator = Evaluator(symbolTable, BasicOperations) + val evaluator = Evaluator(symbolTable, ops, sourceOps) // when/then val e = assertThrows { evaluator.with(template) } @@ -96,7 +99,7 @@ class EvaluatorTest { ) ) ) - val evaluator = Evaluator(symbolTable, ops) + val evaluator = Evaluator(symbolTable, ops, sourceOps) val expected = ImpactValue( QuantityValueFixture.oneKilogram, IndicatorValueFixture.climateChange, @@ -139,7 +142,7 @@ class EvaluatorTest { ) ) ) - val evaluator = Evaluator(symbolTable, ops) + val evaluator = Evaluator(symbolTable, ops, sourceOps) val expected = FullyQualifiedSubstanceValue( "doesNotExist", type = SubstanceType.EMISSION, @@ -166,7 +169,7 @@ class EvaluatorTest { ) ) ) - val evaluator = Evaluator(symbolTable, ops) + val evaluator = Evaluator(symbolTable, ops, sourceOps) // when val p1 = evaluator.trace(template, mapOf("q_water" to QuantityFixture.oneLitre)) @@ -188,7 +191,7 @@ class EvaluatorTest { val register = ProcessTemplateRegister(mapOf(ProcessKey("carrot_production") to template)) val symbolTable = SymbolTable(processTemplates = register) - val evaluator = Evaluator(symbolTable, BasicOperations) + val evaluator = Evaluator(symbolTable, ops, sourceOps) // when @@ -228,7 +231,7 @@ class EvaluatorTest { ) ), ) - val evaluator = Evaluator(symbolTable, ops) + val evaluator = Evaluator(symbolTable, ops, sourceOps) // when val actual = evaluator.trace(template).getSystemValue().processes @@ -324,7 +327,7 @@ class EvaluatorTest { val symbolTable = SymbolTable( processTemplates = processTemplates, ) - val evaluator = Evaluator(symbolTable, ops) + val evaluator = Evaluator(symbolTable, ops, sourceOps) // when val actual = evaluator.trace(template).getSystemValue().processes @@ -419,7 +422,7 @@ class EvaluatorTest { ).mapKeys { ProcessKey(it.key) } ) ) - val evaluator = Evaluator(symbolTable, ops) + val evaluator = Evaluator(symbolTable, ops, sourceOps) // when/then val e = assertFailsWith( @@ -463,7 +466,7 @@ class EvaluatorTest { ) ) ) - val evaluator = Evaluator(symbolTable, ops) + val evaluator = Evaluator(symbolTable, ops, sourceOps) // when val actual = evaluator.trace(template).getSystemValue().substanceCharacterizations @@ -503,7 +506,7 @@ class EvaluatorTest { ).mapKeys { ProcessKey(it.key) } ) ) - val evaluator = Evaluator(symbolTable, ops) + val evaluator = Evaluator(symbolTable, ops, sourceOps) // when/then val e = assertFailsWith( diff --git a/core/src/test/kotlin/ch/kleis/lcaac/core/lang/evaluator/reducer/DataExpressionReducerTest.kt b/core/src/test/kotlin/ch/kleis/lcaac/core/lang/evaluator/reducer/DataExpressionReducerTest.kt index c866a5d9..bed5fc87 100644 --- a/core/src/test/kotlin/ch/kleis/lcaac/core/lang/evaluator/reducer/DataExpressionReducerTest.kt +++ b/core/src/test/kotlin/ch/kleis/lcaac/core/lang/evaluator/reducer/DataExpressionReducerTest.kt @@ -1,5 +1,6 @@ package ch.kleis.lcaac.core.lang.evaluator.reducer +import ch.kleis.lcaac.core.datasource.DataSourceOperations import ch.kleis.lcaac.core.lang.SymbolTable import ch.kleis.lcaac.core.lang.dimension.Dimension import ch.kleis.lcaac.core.lang.dimension.UnitSymbol @@ -14,6 +15,7 @@ import ch.kleis.lcaac.core.lang.register.DataSourceKey import ch.kleis.lcaac.core.lang.register.Register import ch.kleis.lcaac.core.math.basic.BasicNumber import ch.kleis.lcaac.core.math.basic.BasicOperations +import io.mockk.mockk import org.junit.jupiter.api.Test import org.junit.jupiter.api.assertThrows import kotlin.math.pow @@ -22,6 +24,7 @@ import kotlin.test.assertFailsWith class DataExpressionReducerTest { private val ops = BasicOperations + private val sourceOps = mockk>() /* RECORD @@ -43,7 +46,8 @@ class DataExpressionReducerTest { Register.from(mapOf( DataSourceKey("source") to dataSource, )), - BasicOperations, + ops, + sourceOps, ) // when @@ -73,11 +77,12 @@ class DataExpressionReducerTest { Register.from(mapOf( DataSourceKey("source") to dataSource, )), - BasicOperations, + ops, + sourceOps, ) // when/then - val e = assertThrows { reducer.reduce(record) } + val e = assertThrows { reducer.reduce(record) } assertEquals("unknown data source 'foo'", e.message) } @@ -93,7 +98,8 @@ class DataExpressionReducerTest { DataKey("m") to q )), Register.empty(), - BasicOperations + ops, + sourceOps, ) // when @@ -120,7 +126,8 @@ class DataExpressionReducerTest { DataKey("my_map") to record, )), Register.empty(), - BasicOperations + ops, + sourceOps, ) // when @@ -143,7 +150,8 @@ class DataExpressionReducerTest { DataKey("m") to q )), Register.empty(), - BasicOperations + ops, + sourceOps, ) // when @@ -166,7 +174,8 @@ class DataExpressionReducerTest { DataKey("m") to q )), Register.empty(), - BasicOperations + ops, + sourceOps, ) // when @@ -186,6 +195,7 @@ class DataExpressionReducerTest { Register.empty(), Register.empty(), ops, + sourceOps, ) // when @@ -205,6 +215,7 @@ class DataExpressionReducerTest { Register.empty(), Register.empty(), ops, + sourceOps, ) // when @@ -219,7 +230,7 @@ class DataExpressionReducerTest { fun test_reduce_whenScaleOfScale_shouldReduce() { // given val quantity = EQuantityScale(ops.pure(1.0), QuantityFixture.twoKilograms) - val reducer = DataExpressionReducer(Register.empty(), Register.empty(), ops) + val reducer = DataExpressionReducer(Register.empty(), Register.empty(), ops,sourceOps) val expected = EQuantityScale(ops.pure(2.0), UnitFixture.kg) // when @@ -238,6 +249,7 @@ class DataExpressionReducerTest { Register.empty(), Register.empty(), ops, + sourceOps, ) // when @@ -256,7 +268,7 @@ class DataExpressionReducerTest { ) ) val quantity = EQuantityScale(ops.pure(1.0), EDataRef("kg")) - val reducer = DataExpressionReducer(quantityEnvironment, Register.empty(), ops) + val reducer = DataExpressionReducer(quantityEnvironment, Register.empty(), ops,sourceOps) // when val actual = reducer.reduce(quantity) @@ -275,6 +287,7 @@ class DataExpressionReducerTest { Register.empty(), Register.empty(), ops, + sourceOps, ) // when @@ -291,7 +304,7 @@ class DataExpressionReducerTest { val a = UnitFixture.kg val b = UnitFixture.kg val expected = EQuantityScale(ops.pure(2.0), UnitFixture.kg) - val reducer = DataExpressionReducer(Register.empty(), Register.empty(), ops) + val reducer = DataExpressionReducer(Register.empty(), Register.empty(), ops,sourceOps) // when val actual = reducer.reduce(EQuantityAdd(a, b)) @@ -310,6 +323,7 @@ class DataExpressionReducerTest { Register.empty(), Register.empty(), ops, + sourceOps, ) // when/then @@ -325,7 +339,7 @@ class DataExpressionReducerTest { val a = UnitFixture.g val b = UnitFixture.kg val expected = EQuantityScale(ops.pure(1.001), EUnitLiteral(UnitSymbol.of("kg"), 1.0, DimensionFixture.mass)) - val reducer = DataExpressionReducer(Register.empty(), Register.empty(), ops) + val reducer = DataExpressionReducer(Register.empty(), Register.empty(), ops,sourceOps) // when val actual = reducer.reduce(EQuantityAdd(a, b)) @@ -340,7 +354,7 @@ class DataExpressionReducerTest { val a = QuantityFixture.twoKilograms val b = UnitFixture.kg val expected = EQuantityScale(ops.pure(3.0), EUnitLiteral(UnitSymbol.of("kg"), 1.0, DimensionFixture.mass)) - val reducer = DataExpressionReducer(Register.empty(), Register.empty(), ops) + val reducer = DataExpressionReducer(Register.empty(), Register.empty(), ops,sourceOps) // when val actual = reducer.reduce(EQuantityAdd(a, b)) @@ -355,7 +369,7 @@ class DataExpressionReducerTest { val a = UnitFixture.kg val b = QuantityFixture.twoKilograms val expected = EQuantityScale(ops.pure(3.0), EUnitLiteral(UnitSymbol.of("kg"), 1.0, DimensionFixture.mass)) - val reducer = DataExpressionReducer(Register.empty(), Register.empty(), ops) + val reducer = DataExpressionReducer(Register.empty(), Register.empty(), ops,sourceOps) // when val actual = reducer.reduce(EQuantityAdd(a, b)) @@ -373,6 +387,7 @@ class DataExpressionReducerTest { Register.empty(), Register.empty(), ops, + sourceOps, ) // when @@ -392,6 +407,7 @@ class DataExpressionReducerTest { Register.empty(), Register.empty(), ops, + sourceOps, ) val eQuantitySub = EQuantitySub(a, b) @@ -408,7 +424,7 @@ class DataExpressionReducerTest { val a = UnitFixture.kg val b = UnitFixture.kg val expected = EQuantityScale(ops.pure(0.0), UnitFixture.kg) - val reducer = DataExpressionReducer(Register.empty(), Register.empty(), ops) + val reducer = DataExpressionReducer(Register.empty(), Register.empty(), ops,sourceOps) // when val actual = reducer.reduce(EQuantitySub(a, b)) @@ -423,7 +439,7 @@ class DataExpressionReducerTest { val a = UnitFixture.kg val b = UnitFixture.g val expected = EQuantityScale(ops.pure(0.999), EUnitLiteral(UnitSymbol.of("kg"), 1.0, DimensionFixture.mass)) - val reducer = DataExpressionReducer(Register.empty(), Register.empty(), ops) + val reducer = DataExpressionReducer(Register.empty(), Register.empty(), ops,sourceOps) // when val actual = reducer.reduce(EQuantitySub(a, b)) @@ -438,7 +454,7 @@ class DataExpressionReducerTest { val a = QuantityFixture.twoKilograms val b = UnitFixture.kg val expected = EQuantityScale(ops.pure(1.0), EUnitLiteral(UnitSymbol.of("kg"), 1.0, DimensionFixture.mass)) - val reducer = DataExpressionReducer(Register.empty(), Register.empty(), ops) + val reducer = DataExpressionReducer(Register.empty(), Register.empty(), ops,sourceOps) // when val actual = reducer.reduce(EQuantitySub(a, b)) @@ -453,7 +469,7 @@ class DataExpressionReducerTest { val a = UnitFixture.kg val b = EQuantityScale(ops.pure(0.5), UnitFixture.kg) val expected = EQuantityScale(ops.pure(0.5), EUnitLiteral(UnitSymbol.of("kg"), 1.0, DimensionFixture.mass)) - val reducer = DataExpressionReducer(Register.empty(), Register.empty(), ops) + val reducer = DataExpressionReducer(Register.empty(), Register.empty(), ops,sourceOps) // when val actual = reducer.reduce(EQuantitySub(a, b)) @@ -471,6 +487,7 @@ class DataExpressionReducerTest { Register.empty(), Register.empty(), ops, + sourceOps, ) // when @@ -497,6 +514,7 @@ class DataExpressionReducerTest { Register.empty(), Register.empty(), ops, + sourceOps, ) // when @@ -530,6 +548,7 @@ class DataExpressionReducerTest { Register.empty(), Register.empty(), ops, + sourceOps, ) // when @@ -555,6 +574,7 @@ class DataExpressionReducerTest { Register.empty(), Register.empty(), ops, + sourceOps, ) // when @@ -573,6 +593,7 @@ class DataExpressionReducerTest { Register.empty(), Register.empty(), ops, + sourceOps, ) // when @@ -595,7 +616,7 @@ class DataExpressionReducerTest { // given val a = UnitFixture.km val b = UnitFixture.hour - val reducer = DataExpressionReducer(Register.empty(), Register.empty(), ops) + val reducer = DataExpressionReducer(Register.empty(), Register.empty(), ops,sourceOps) // when val actual = reducer.reduce(EQuantityDiv(a, b)) @@ -626,7 +647,7 @@ class DataExpressionReducerTest { ) ) - val reducer = DataExpressionReducer(Register.empty(), Register.empty(), ops) + val reducer = DataExpressionReducer(Register.empty(), Register.empty(), ops,sourceOps) // when val actual = reducer.reduce(EQuantityDiv(a, b)) @@ -644,6 +665,7 @@ class DataExpressionReducerTest { Register.empty(), Register.empty(), ops, + sourceOps, ) // when @@ -673,6 +695,7 @@ class DataExpressionReducerTest { ), Register.empty(), ops, + sourceOps, ) // when @@ -697,6 +720,7 @@ class DataExpressionReducerTest { Register.empty(), Register.empty(), ops, + sourceOps, ) // when val actual = reducer.reduce(unitComposition) @@ -718,6 +742,7 @@ class DataExpressionReducerTest { Register.empty(), Register.empty(), ops, + sourceOps, ) // when val actual = reducer.reduce(unitComposition) @@ -733,7 +758,7 @@ class DataExpressionReducerTest { fun reduce_whenUnitCompositionComposition_shouldDeepReduce() { // given val expr = EUnitAlias("foo", EUnitAlias("bar", UnitFixture.kg)) - val reducer = DataExpressionReducer(Register.empty(), Register.empty(), ops) + val reducer = DataExpressionReducer(Register.empty(), Register.empty(), ops,sourceOps) // when val actual = reducer.reduce(expr) @@ -758,6 +783,7 @@ class DataExpressionReducerTest { ), Register.empty(), ops, + sourceOps, ) // when @@ -776,6 +802,7 @@ class DataExpressionReducerTest { Register.empty(), Register.empty(), ops, + sourceOps, ) // when @@ -791,7 +818,7 @@ class DataExpressionReducerTest { // given val expr = EUnitOf(UnitFixture.l) val expected = QuantityFixture.oneLitre - val reducer = DataExpressionReducer(Register.empty(), Register.empty(), ops) + val reducer = DataExpressionReducer(Register.empty(), Register.empty(), ops,sourceOps) // when val actual = reducer.reduce(expr) @@ -804,7 +831,7 @@ class DataExpressionReducerTest { fun reduce_whenUnitOfRef_shouldReturnAsIs() { // given val expr = EUnitOf(EDataRef("beer")) - val reducer = DataExpressionReducer(Register.empty(), Register.empty(), ops) + val reducer = DataExpressionReducer(Register.empty(), Register.empty(), ops,sourceOps) // when val actual = reducer.reduce(expr) @@ -826,7 +853,7 @@ class DataExpressionReducerTest { DimensionFixture.mass.multiply(DimensionFixture.volume) ) ) - val reducer = DataExpressionReducer(Register.empty(), Register.empty(), ops) + val reducer = DataExpressionReducer(Register.empty(), Register.empty(), ops,sourceOps) // when val actual = reducer.reduce(expr) @@ -848,7 +875,7 @@ class DataExpressionReducerTest { DimensionFixture.mass.multiply(DimensionFixture.volume) ) ) - val reducer = DataExpressionReducer(Register.empty(), Register.empty(), ops) + val reducer = DataExpressionReducer(Register.empty(), Register.empty(), ops,sourceOps) // when val actual = reducer.reduce(expr) @@ -861,7 +888,7 @@ class DataExpressionReducerTest { fun reduce_whenUnitOfUnitOfRef_shouldMerge() { // given val expr = EUnitOf(EUnitOf(EDataRef("beer"))) - val reducer = DataExpressionReducer(Register.empty(), Register.empty(), ops) + val reducer = DataExpressionReducer(Register.empty(), Register.empty(), ops,sourceOps) // when val actual = reducer.reduce(expr) @@ -880,6 +907,7 @@ class DataExpressionReducerTest { Register.empty(), Register.empty(), ops, + sourceOps, ) // when @@ -906,6 +934,7 @@ class DataExpressionReducerTest { Register.empty(), Register.empty(), ops, + sourceOps, ) // when @@ -931,6 +960,7 @@ class DataExpressionReducerTest { Register.empty(), Register.empty(), ops, + sourceOps, ) // when @@ -961,6 +991,7 @@ class DataExpressionReducerTest { units, Register.empty(), ops, + sourceOps, ) // when @@ -984,6 +1015,7 @@ class DataExpressionReducerTest { DataRegister.empty(), Register.empty(), ops, + sourceOps, ) // when @@ -1006,6 +1038,7 @@ class DataExpressionReducerTest { ), Register.empty(), ops, + sourceOps, ) // when @@ -1029,6 +1062,7 @@ class DataExpressionReducerTest { ), Register.empty(), ops, + sourceOps, ) // when diff --git a/core/src/test/kotlin/ch/kleis/lcaac/core/lang/evaluator/reducer/LcaExpressionReducerTest.kt b/core/src/test/kotlin/ch/kleis/lcaac/core/lang/evaluator/reducer/LcaExpressionReducerTest.kt index 0df798a1..8b9a68ab 100644 --- a/core/src/test/kotlin/ch/kleis/lcaac/core/lang/evaluator/reducer/LcaExpressionReducerTest.kt +++ b/core/src/test/kotlin/ch/kleis/lcaac/core/lang/evaluator/reducer/LcaExpressionReducerTest.kt @@ -1,17 +1,20 @@ package ch.kleis.lcaac.core.lang.evaluator.reducer -import ch.kleis.lcaac.core.lang.register.DataKey -import ch.kleis.lcaac.core.lang.register.DataRegister +import ch.kleis.lcaac.core.datasource.DataSourceOperations import ch.kleis.lcaac.core.lang.expression.* import ch.kleis.lcaac.core.lang.fixture.* +import ch.kleis.lcaac.core.lang.register.DataKey +import ch.kleis.lcaac.core.lang.register.DataRegister import ch.kleis.lcaac.core.lang.register.Register import ch.kleis.lcaac.core.math.basic.BasicNumber import ch.kleis.lcaac.core.math.basic.BasicOperations +import io.mockk.mockk import org.junit.jupiter.api.Test import kotlin.test.assertEquals class LcaExpressionReducerTest { private val ops = BasicOperations + private val sourceOps = mockk>() @Test fun reduce_whenTechnoExchange_shouldReduceLabelSelectors() { @@ -33,6 +36,7 @@ class LcaExpressionReducerTest { ), Register.empty(), ops, + sourceOps, ) // when @@ -78,6 +82,7 @@ class LcaExpressionReducerTest { ), Register.empty(), ops, + sourceOps, ) // when @@ -123,6 +128,7 @@ class LcaExpressionReducerTest { ), Register.empty(), ops, + sourceOps, ) // when @@ -151,6 +157,7 @@ class LcaExpressionReducerTest { ), Register.empty(), ops, + sourceOps, ) // when @@ -179,6 +186,7 @@ class LcaExpressionReducerTest { ), Register.empty(), ops, + sourceOps, ) // when @@ -207,6 +215,7 @@ class LcaExpressionReducerTest { ), Register.empty(), ops, + sourceOps, ) // when @@ -239,6 +248,7 @@ class LcaExpressionReducerTest { ), Register.empty(), ops, + sourceOps, ) // when @@ -271,6 +281,7 @@ class LcaExpressionReducerTest { ), Register.empty(), ops, + sourceOps, ) // when @@ -299,6 +310,7 @@ class LcaExpressionReducerTest { ), Register.empty(), ops, + sourceOps, ) // when @@ -335,6 +347,7 @@ class LcaExpressionReducerTest { ), Register.empty(), ops, + sourceOps, ) // when @@ -379,6 +392,7 @@ class LcaExpressionReducerTest { ), Register.empty(), ops, + sourceOps, ) // when diff --git a/core/src/test/kotlin/ch/kleis/lcaac/core/lang/evaluator/reducer/ProcessTemplateExpressionReducerTest.kt b/core/src/test/kotlin/ch/kleis/lcaac/core/lang/evaluator/reducer/ProcessTemplateExpressionReducerTest.kt index d763dfec..0ba0da1c 100644 --- a/core/src/test/kotlin/ch/kleis/lcaac/core/lang/evaluator/reducer/ProcessTemplateExpressionReducerTest.kt +++ b/core/src/test/kotlin/ch/kleis/lcaac/core/lang/evaluator/reducer/ProcessTemplateExpressionReducerTest.kt @@ -1,5 +1,6 @@ package ch.kleis.lcaac.core.lang.evaluator.reducer +import ch.kleis.lcaac.core.datasource.DataSourceOperations import ch.kleis.lcaac.core.lang.evaluator.EvaluatorException import ch.kleis.lcaac.core.lang.expression.* import ch.kleis.lcaac.core.lang.fixture.ProductFixture @@ -7,12 +8,14 @@ import ch.kleis.lcaac.core.lang.fixture.QuantityFixture import ch.kleis.lcaac.core.lang.fixture.UnitFixture import ch.kleis.lcaac.core.math.basic.BasicNumber import ch.kleis.lcaac.core.math.basic.BasicOperations +import io.mockk.mockk import org.junit.jupiter.api.Test import kotlin.test.assertEquals import kotlin.test.assertFailsWith class ProcessTemplateExpressionReducerTest { private val ops = BasicOperations + private val sourceOps = mockk>() @Test fun reduce_whenInstance_shouldReduce() { @@ -44,7 +47,7 @@ class ProcessTemplateExpressionReducerTest { Pair("q_carrot", QuantityFixture.twoKilograms), ) val expression = EProcessTemplateApplication(template, arguments) - val reducer = TemplateExpressionReducer(ops) + val reducer = TemplateExpressionReducer(ops, sourceOps) // when val actual = reducer.reduce(expression) @@ -109,7 +112,7 @@ class ProcessTemplateExpressionReducerTest { Pair("foo", QuantityFixture.twoKilograms), ) val expression = EProcessTemplateApplication(template, arguments) - val reducer = TemplateExpressionReducer(ops) + val reducer = TemplateExpressionReducer(ops, sourceOps) // when/then val e = assertFailsWith(EvaluatorException::class, null) { reducer.reduce(expression) } diff --git a/core/src/test/kotlin/ch/kleis/lcaac/core/lang/evaluator/step/ReduceLabelSelectorsTest.kt b/core/src/test/kotlin/ch/kleis/lcaac/core/lang/evaluator/step/ReduceLabelSelectorsTest.kt index e9cd9bf6..7d448742 100644 --- a/core/src/test/kotlin/ch/kleis/lcaac/core/lang/evaluator/step/ReduceLabelSelectorsTest.kt +++ b/core/src/test/kotlin/ch/kleis/lcaac/core/lang/evaluator/step/ReduceLabelSelectorsTest.kt @@ -1,17 +1,21 @@ package ch.kleis.lcaac.core.lang.evaluator.step -import ch.kleis.lcaac.core.lang.register.DataKey -import ch.kleis.lcaac.core.lang.register.DataRegister +import ch.kleis.lcaac.core.datasource.DataSourceOperations import ch.kleis.lcaac.core.lang.SymbolTable import ch.kleis.lcaac.core.lang.expression.* import ch.kleis.lcaac.core.lang.fixture.QuantityFixture +import ch.kleis.lcaac.core.lang.register.DataKey +import ch.kleis.lcaac.core.lang.register.DataRegister +import ch.kleis.lcaac.core.math.basic.BasicNumber import ch.kleis.lcaac.core.math.basic.BasicOperations +import io.mockk.mockk import org.junit.jupiter.api.Test import kotlin.test.assertEquals class ReduceLabelSelectorsTest { private val ops = BasicOperations + private val sourceOps = mockk>() @Test fun reduce_whenPassingByArguments() { @@ -38,7 +42,7 @@ class ReduceLabelSelectorsTest { ), mapOf("geo" to EStringLiteral("FR")), ) - val reduceLabelSelectors = ReduceLabelSelectors(SymbolTable(), ops) + val reduceLabelSelectors = ReduceLabelSelectors(SymbolTable(), ops, sourceOps) // when val actual = reduceLabelSelectors.apply(instance) @@ -93,7 +97,7 @@ class ReduceLabelSelectorsTest { ) ), ) - val reduceLabelSelectors = ReduceLabelSelectors(SymbolTable(), ops) + val reduceLabelSelectors = ReduceLabelSelectors(SymbolTable(), ops, sourceOps) // when val actual = reduceLabelSelectors.apply(instance) @@ -147,7 +151,7 @@ class ReduceLabelSelectorsTest { ) ), ) - val reduceLabelSelectors = ReduceLabelSelectors(SymbolTable(), ops) + val reduceLabelSelectors = ReduceLabelSelectors(SymbolTable(), ops, sourceOps) // when val actual = reduceLabelSelectors.apply(instance) @@ -206,6 +210,7 @@ class ReduceLabelSelectorsTest { data = DataRegister(mapOf(DataKey("geo") to EStringLiteral("FR"))) ), ops, + sourceOps, ) // when @@ -259,7 +264,7 @@ class ReduceLabelSelectorsTest { ) ), ) - val reduceLabelSelectors = ReduceLabelSelectors(SymbolTable(), ops) + val reduceLabelSelectors = ReduceLabelSelectors(SymbolTable(), ops, sourceOps) // when val actual = reduceLabelSelectors.apply(instance) diff --git a/core/src/test/kotlin/ch/kleis/lcaac/core/lang/evaluator/step/ReduceTest.kt b/core/src/test/kotlin/ch/kleis/lcaac/core/lang/evaluator/step/ReduceTest.kt index 5c65fb7d..7c20ea7c 100644 --- a/core/src/test/kotlin/ch/kleis/lcaac/core/lang/evaluator/step/ReduceTest.kt +++ b/core/src/test/kotlin/ch/kleis/lcaac/core/lang/evaluator/step/ReduceTest.kt @@ -1,5 +1,6 @@ package ch.kleis.lcaac.core.lang.evaluator.step +import ch.kleis.lcaac.core.datasource.DataSourceOperations import ch.kleis.lcaac.core.lang.SymbolTable import ch.kleis.lcaac.core.lang.evaluator.EvaluatorException import ch.kleis.lcaac.core.lang.evaluator.ToValue @@ -12,7 +13,9 @@ import ch.kleis.lcaac.core.lang.fixture.TemplateFixture import ch.kleis.lcaac.core.lang.value.FromProcessRefValue import ch.kleis.lcaac.core.lang.value.ProcessValue import ch.kleis.lcaac.core.lang.value.TechnoExchangeValue +import ch.kleis.lcaac.core.math.basic.BasicNumber import ch.kleis.lcaac.core.math.basic.BasicOperations +import io.mockk.mockk import org.junit.jupiter.api.Test import kotlin.test.assertEquals import kotlin.test.assertFailsWith @@ -20,7 +23,8 @@ import kotlin.test.assertFailsWith class ReduceTest { private val ops = BasicOperations - + private val sourceOps = mockk>() + @Test fun eval_whenInstanceOfProcessTemplate_shouldEvaluateToProcessValue() { // given @@ -35,7 +39,7 @@ class ReduceTest { ) ) ) - val reduceAndComplete = Reduce(SymbolTable.empty(), ops) + val reduceAndComplete = Reduce(SymbolTable.empty(), ops, sourceOps) // when val actual = with(ToValue(BasicOperations)) { reduceAndComplete.apply(instance).toValue() } @@ -68,7 +72,7 @@ class ReduceTest { fun eval_whenProcessTemplate_shouldAutomaticallyInstantiateWithoutArguments() { // given val template = EProcessTemplateApplication(TemplateFixture.carrotProduction, emptyMap()) - val reduceAndComplete = Reduce(SymbolTable.empty(), ops) + val reduceAndComplete = Reduce(SymbolTable.empty(), ops, sourceOps) // when val actual = with(ToValue(BasicOperations)) { reduceAndComplete.apply(template).toValue() } @@ -101,7 +105,7 @@ class ReduceTest { fun eval_whenContainsUnboundedReference_shouldThrow() { // given val template = EProcessTemplateApplication(TemplateFixture.withUnboundedRef, emptyMap()) - val reduceAndComplete = Reduce(SymbolTable.empty(), ops) + val reduceAndComplete = Reduce(SymbolTable.empty(), ops, sourceOps) // when/then assertFailsWith( diff --git a/core/src/test/kotlin/ch/kleis/lcaac/core/testing/BasicTestRunnerTest.kt b/core/src/test/kotlin/ch/kleis/lcaac/core/testing/BasicTestRunnerTest.kt index a55c4868..b2752363 100644 --- a/core/src/test/kotlin/ch/kleis/lcaac/core/testing/BasicTestRunnerTest.kt +++ b/core/src/test/kotlin/ch/kleis/lcaac/core/testing/BasicTestRunnerTest.kt @@ -37,7 +37,7 @@ class BasicTestRunnerTest { arguments = emptyMap(), ) val symbolTable = SymbolTable.empty() - val runner = BasicTestRunner(symbolTable) + val runner = BasicTestRunner(symbolTable, mockk()) // when val actual = runner.run(case) @@ -77,7 +77,7 @@ class BasicTestRunnerTest { arguments = emptyMap(), ) val symbolTable = SymbolTable.empty() - val runner = BasicTestRunner(symbolTable) + val runner = BasicTestRunner(symbolTable, mockk()) // when val actual = runner.run(case) @@ -117,7 +117,7 @@ class BasicTestRunnerTest { arguments = emptyMap(), ) val symbolTable = SymbolTable.empty() - val runner = BasicTestRunner(symbolTable) + val runner = BasicTestRunner(symbolTable, mockk()) // when val actual = runner.run(case) @@ -141,7 +141,7 @@ class BasicTestRunnerTest { val evaluator = mockk>() every { evaluator.with(carrotProduction) } returns evaluator every { evaluator.trace(carrotProduction) } throws EvaluatorException("some error") - val runner = BasicTestRunner(SymbolTable.empty(), evaluator) + val runner = BasicTestRunner(SymbolTable.empty(), mockk(), evaluator) val case = TestCase( source = "source", name = "carrot_production", From ac97f71647a97158fd03216d06858d9f75416776 Mon Sep 17 00:00:00 2001 From: Peva Blanchard Date: Sat, 20 Jan 2024 00:54:34 +0100 Subject: [PATCH 07/37] core: reducer: sum source column --- .../reducer/DataExpressionReducer.kt | 8 +- .../reducer/DataExpressionReducerTest.kt | 105 ++++++++++++++---- 2 files changed, 90 insertions(+), 23 deletions(-) diff --git a/core/src/main/kotlin/ch/kleis/lcaac/core/lang/evaluator/reducer/DataExpressionReducer.kt b/core/src/main/kotlin/ch/kleis/lcaac/core/lang/evaluator/reducer/DataExpressionReducer.kt index 9919498b..e273e2fe 100644 --- a/core/src/main/kotlin/ch/kleis/lcaac/core/lang/evaluator/reducer/DataExpressionReducer.kt +++ b/core/src/main/kotlin/ch/kleis/lcaac/core/lang/evaluator/reducer/DataExpressionReducer.kt @@ -39,11 +39,17 @@ class DataExpressionReducer( is ERecord -> reduceMap(expression) is ERecordEntry -> reduceMapEntry(expression) is EDefaultRecordOf -> reduceDefaultRecordOf(expression) - is ESum -> TODO() + is ESum -> reduceESum(expression) } } } + private fun reduceESum(expression: ESum): DataExpression { + val dataSource = dataSourceRegister[DataSourceKey(expression.dataSourceRef)] + ?: throw EvaluatorException("unknown data source '${expression.dataSourceRef}'") + return reduce(sourceOps.sum(dataSource, expression.column)) + } + private fun reduceDefaultRecordOf(expression: EDefaultRecordOf): DataExpression { val dataSource = dataSourceRegister[DataSourceKey(expression.dataSourceRef)] ?: throw EvaluatorException("unknown data source '${expression.dataSourceRef}'") diff --git a/core/src/test/kotlin/ch/kleis/lcaac/core/lang/evaluator/reducer/DataExpressionReducerTest.kt b/core/src/test/kotlin/ch/kleis/lcaac/core/lang/evaluator/reducer/DataExpressionReducerTest.kt index bed5fc87..8158ec9b 100644 --- a/core/src/test/kotlin/ch/kleis/lcaac/core/lang/evaluator/reducer/DataExpressionReducerTest.kt +++ b/core/src/test/kotlin/ch/kleis/lcaac/core/lang/evaluator/reducer/DataExpressionReducerTest.kt @@ -9,13 +9,12 @@ import ch.kleis.lcaac.core.lang.expression.* import ch.kleis.lcaac.core.lang.fixture.DimensionFixture import ch.kleis.lcaac.core.lang.fixture.QuantityFixture import ch.kleis.lcaac.core.lang.fixture.UnitFixture -import ch.kleis.lcaac.core.lang.register.DataKey -import ch.kleis.lcaac.core.lang.register.DataRegister -import ch.kleis.lcaac.core.lang.register.DataSourceKey -import ch.kleis.lcaac.core.lang.register.Register +import ch.kleis.lcaac.core.lang.register.* import ch.kleis.lcaac.core.math.basic.BasicNumber import ch.kleis.lcaac.core.math.basic.BasicOperations +import io.mockk.every import io.mockk.mockk +import io.mockk.verify import org.junit.jupiter.api.Test import org.junit.jupiter.api.assertThrows import kotlin.math.pow @@ -26,6 +25,68 @@ class DataExpressionReducerTest { private val ops = BasicOperations private val sourceOps = mockk>() + /* + Column Operations + */ + + @Test + fun sum() { + // given + val expression = ESum("source", "mass") + val dataSource = ECsvSource( + location = "source.csv", + schema = mapOf( + "mass" to ColumnType(QuantityFixture.oneKilogram), + ), + ) + val sops = mockk>() + val total = QuantityFixture.twoKilograms + every { sops.sum(dataSource, "mass") } returns total + val reducer = DataExpressionReducer( + DataRegister.empty(), + DataSourceRegister.from(mapOf( + DataSourceKey("source") to dataSource + )), + ops, + sops, + ) + + // when + val actual = reducer.reduce(expression) + + // then + assertEquals(total, actual) + verify { sops.sum(dataSource, "mass") } + } + + + @Test + fun sum_invalidDataSourceRef() { + // given + val expression = ESum("foo", "mass") + val dataSource = ECsvSource( + location = "source.csv", + schema = mapOf( + "mass" to ColumnType(QuantityFixture.oneKilogram), + ), + ) + val sops = mockk>() + val total = QuantityFixture.twoKilograms + every { sops.sum(dataSource, "mass") } returns total + val reducer = DataExpressionReducer( + DataRegister.empty(), + DataSourceRegister.from(mapOf( + DataSourceKey("source") to dataSource + )), + ops, + sops, + ) + + // when/then + val e = assertThrows { reducer.reduce(expression) } + assertEquals("unknown data source 'foo'", e.message) + } + /* RECORD */ @@ -230,7 +291,7 @@ class DataExpressionReducerTest { fun test_reduce_whenScaleOfScale_shouldReduce() { // given val quantity = EQuantityScale(ops.pure(1.0), QuantityFixture.twoKilograms) - val reducer = DataExpressionReducer(Register.empty(), Register.empty(), ops,sourceOps) + val reducer = DataExpressionReducer(Register.empty(), Register.empty(), ops, sourceOps) val expected = EQuantityScale(ops.pure(2.0), UnitFixture.kg) // when @@ -268,7 +329,7 @@ class DataExpressionReducerTest { ) ) val quantity = EQuantityScale(ops.pure(1.0), EDataRef("kg")) - val reducer = DataExpressionReducer(quantityEnvironment, Register.empty(), ops,sourceOps) + val reducer = DataExpressionReducer(quantityEnvironment, Register.empty(), ops, sourceOps) // when val actual = reducer.reduce(quantity) @@ -304,7 +365,7 @@ class DataExpressionReducerTest { val a = UnitFixture.kg val b = UnitFixture.kg val expected = EQuantityScale(ops.pure(2.0), UnitFixture.kg) - val reducer = DataExpressionReducer(Register.empty(), Register.empty(), ops,sourceOps) + val reducer = DataExpressionReducer(Register.empty(), Register.empty(), ops, sourceOps) // when val actual = reducer.reduce(EQuantityAdd(a, b)) @@ -339,7 +400,7 @@ class DataExpressionReducerTest { val a = UnitFixture.g val b = UnitFixture.kg val expected = EQuantityScale(ops.pure(1.001), EUnitLiteral(UnitSymbol.of("kg"), 1.0, DimensionFixture.mass)) - val reducer = DataExpressionReducer(Register.empty(), Register.empty(), ops,sourceOps) + val reducer = DataExpressionReducer(Register.empty(), Register.empty(), ops, sourceOps) // when val actual = reducer.reduce(EQuantityAdd(a, b)) @@ -354,7 +415,7 @@ class DataExpressionReducerTest { val a = QuantityFixture.twoKilograms val b = UnitFixture.kg val expected = EQuantityScale(ops.pure(3.0), EUnitLiteral(UnitSymbol.of("kg"), 1.0, DimensionFixture.mass)) - val reducer = DataExpressionReducer(Register.empty(), Register.empty(), ops,sourceOps) + val reducer = DataExpressionReducer(Register.empty(), Register.empty(), ops, sourceOps) // when val actual = reducer.reduce(EQuantityAdd(a, b)) @@ -369,7 +430,7 @@ class DataExpressionReducerTest { val a = UnitFixture.kg val b = QuantityFixture.twoKilograms val expected = EQuantityScale(ops.pure(3.0), EUnitLiteral(UnitSymbol.of("kg"), 1.0, DimensionFixture.mass)) - val reducer = DataExpressionReducer(Register.empty(), Register.empty(), ops,sourceOps) + val reducer = DataExpressionReducer(Register.empty(), Register.empty(), ops, sourceOps) // when val actual = reducer.reduce(EQuantityAdd(a, b)) @@ -424,7 +485,7 @@ class DataExpressionReducerTest { val a = UnitFixture.kg val b = UnitFixture.kg val expected = EQuantityScale(ops.pure(0.0), UnitFixture.kg) - val reducer = DataExpressionReducer(Register.empty(), Register.empty(), ops,sourceOps) + val reducer = DataExpressionReducer(Register.empty(), Register.empty(), ops, sourceOps) // when val actual = reducer.reduce(EQuantitySub(a, b)) @@ -439,7 +500,7 @@ class DataExpressionReducerTest { val a = UnitFixture.kg val b = UnitFixture.g val expected = EQuantityScale(ops.pure(0.999), EUnitLiteral(UnitSymbol.of("kg"), 1.0, DimensionFixture.mass)) - val reducer = DataExpressionReducer(Register.empty(), Register.empty(), ops,sourceOps) + val reducer = DataExpressionReducer(Register.empty(), Register.empty(), ops, sourceOps) // when val actual = reducer.reduce(EQuantitySub(a, b)) @@ -454,7 +515,7 @@ class DataExpressionReducerTest { val a = QuantityFixture.twoKilograms val b = UnitFixture.kg val expected = EQuantityScale(ops.pure(1.0), EUnitLiteral(UnitSymbol.of("kg"), 1.0, DimensionFixture.mass)) - val reducer = DataExpressionReducer(Register.empty(), Register.empty(), ops,sourceOps) + val reducer = DataExpressionReducer(Register.empty(), Register.empty(), ops, sourceOps) // when val actual = reducer.reduce(EQuantitySub(a, b)) @@ -469,7 +530,7 @@ class DataExpressionReducerTest { val a = UnitFixture.kg val b = EQuantityScale(ops.pure(0.5), UnitFixture.kg) val expected = EQuantityScale(ops.pure(0.5), EUnitLiteral(UnitSymbol.of("kg"), 1.0, DimensionFixture.mass)) - val reducer = DataExpressionReducer(Register.empty(), Register.empty(), ops,sourceOps) + val reducer = DataExpressionReducer(Register.empty(), Register.empty(), ops, sourceOps) // when val actual = reducer.reduce(EQuantitySub(a, b)) @@ -616,7 +677,7 @@ class DataExpressionReducerTest { // given val a = UnitFixture.km val b = UnitFixture.hour - val reducer = DataExpressionReducer(Register.empty(), Register.empty(), ops,sourceOps) + val reducer = DataExpressionReducer(Register.empty(), Register.empty(), ops, sourceOps) // when val actual = reducer.reduce(EQuantityDiv(a, b)) @@ -647,7 +708,7 @@ class DataExpressionReducerTest { ) ) - val reducer = DataExpressionReducer(Register.empty(), Register.empty(), ops,sourceOps) + val reducer = DataExpressionReducer(Register.empty(), Register.empty(), ops, sourceOps) // when val actual = reducer.reduce(EQuantityDiv(a, b)) @@ -758,7 +819,7 @@ class DataExpressionReducerTest { fun reduce_whenUnitCompositionComposition_shouldDeepReduce() { // given val expr = EUnitAlias("foo", EUnitAlias("bar", UnitFixture.kg)) - val reducer = DataExpressionReducer(Register.empty(), Register.empty(), ops,sourceOps) + val reducer = DataExpressionReducer(Register.empty(), Register.empty(), ops, sourceOps) // when val actual = reducer.reduce(expr) @@ -818,7 +879,7 @@ class DataExpressionReducerTest { // given val expr = EUnitOf(UnitFixture.l) val expected = QuantityFixture.oneLitre - val reducer = DataExpressionReducer(Register.empty(), Register.empty(), ops,sourceOps) + val reducer = DataExpressionReducer(Register.empty(), Register.empty(), ops, sourceOps) // when val actual = reducer.reduce(expr) @@ -831,7 +892,7 @@ class DataExpressionReducerTest { fun reduce_whenUnitOfRef_shouldReturnAsIs() { // given val expr = EUnitOf(EDataRef("beer")) - val reducer = DataExpressionReducer(Register.empty(), Register.empty(), ops,sourceOps) + val reducer = DataExpressionReducer(Register.empty(), Register.empty(), ops, sourceOps) // when val actual = reducer.reduce(expr) @@ -853,7 +914,7 @@ class DataExpressionReducerTest { DimensionFixture.mass.multiply(DimensionFixture.volume) ) ) - val reducer = DataExpressionReducer(Register.empty(), Register.empty(), ops,sourceOps) + val reducer = DataExpressionReducer(Register.empty(), Register.empty(), ops, sourceOps) // when val actual = reducer.reduce(expr) @@ -875,7 +936,7 @@ class DataExpressionReducerTest { DimensionFixture.mass.multiply(DimensionFixture.volume) ) ) - val reducer = DataExpressionReducer(Register.empty(), Register.empty(), ops,sourceOps) + val reducer = DataExpressionReducer(Register.empty(), Register.empty(), ops, sourceOps) // when val actual = reducer.reduce(expr) @@ -888,7 +949,7 @@ class DataExpressionReducerTest { fun reduce_whenUnitOfUnitOfRef_shouldMerge() { // given val expr = EUnitOf(EUnitOf(EDataRef("beer"))) - val reducer = DataExpressionReducer(Register.empty(), Register.empty(), ops,sourceOps) + val reducer = DataExpressionReducer(Register.empty(), Register.empty(), ops, sourceOps) // when val actual = reducer.reduce(expr) From da5fdec7ab3397cf39e4d307dcc4850feb01974b Mon Sep 17 00:00:00 2001 From: Peva Blanchard Date: Sat, 20 Jan 2024 09:12:42 +0100 Subject: [PATCH 08/37] core: lca expression: added block expression (wip) --- .../core/lang/expression/LcaExpression.kt | 30 +++++++++++++++++-- 1 file changed, 27 insertions(+), 3 deletions(-) diff --git a/core/src/main/kotlin/ch/kleis/lcaac/core/lang/expression/LcaExpression.kt b/core/src/main/kotlin/ch/kleis/lcaac/core/lang/expression/LcaExpression.kt index a647183d..32628924 100644 --- a/core/src/main/kotlin/ch/kleis/lcaac/core/lang/expression/LcaExpression.kt +++ b/core/src/main/kotlin/ch/kleis/lcaac/core/lang/expression/LcaExpression.kt @@ -88,15 +88,39 @@ data class EImpact(override val quantity: DataExpression, val indicator: E companion object } +// Block +@optics +sealed interface BlockExpression { + companion object +} + +@optics +data class EBlockEntry(val entry: E) : BlockExpression { + companion object +} + +@optics +data class EBlockForEach( + val rowRef: String, + val dataSourceRef: String, + val body: List>, +) : BlockExpression { + companion object +} + +typealias TechnoBlock = BlockExpression, Q> +typealias BioBlock = BlockExpression, Q> +typealias ImpactBlock = BlockExpression, Q> + // Process @optics data class EProcess( val name: String, val labels: Map> = emptyMap(), val products: List> = emptyList(), - val inputs: List> = emptyList(), - val biosphere: List> = emptyList(), - val impacts: List> = emptyList(), + val inputs: List> = emptyList(), + val biosphere: List> = emptyList(), + val impacts: List> = emptyList(), ) : LcaExpression, ConnectionExpression { companion object } From 0d340fe312bb723968d6876a351835722292855f Mon Sep 17 00:00:00 2001 From: Peva Blanchard Date: Sat, 20 Jan 2024 09:42:06 +0100 Subject: [PATCH 09/37] core: make it compile --- .../lcaac/core/lang/evaluator/ToValue.kt | 16 +- .../core/lang/evaluator/protocol/Learner.kt | 69 +++++--- .../evaluator/reducer/LcaExpressionReducer.kt | 49 +++++- .../lang/evaluator/step/CompleteTerminals.kt | 20 ++- .../evaluator/step/ReduceLabelSelectors.kt | 2 + .../core/lang/expression/LcaExpression.kt | 10 +- .../lang/expression/optics/EveryDataRef.kt | 16 +- .../expression/optics/EveryProcessTemplate.kt | 10 -- .../expression/optics/LcaExpressionOptics.kt | 26 +++ .../core/lang/evaluator/EvaluatorTest.kt | 96 ++++++----- .../lcaac/core/lang/evaluator/HelperTest.kt | 70 ++++---- .../reducer/LcaExpressionReducerTest.kt | 36 ++-- .../ProcessTemplateExpressionReducerTest.kt | 12 +- .../evaluator/step/CompleteTerminalsTest.kt | 16 +- .../step/ReduceLabelSelectorsTest.kt | 162 ++++++++++-------- .../lcaac/core/lang/fixture/ImpactFixture.kt | 7 + .../lcaac/core/lang/fixture/ProcessFixture.kt | 10 +- .../SubstanceCharacterizationFixture.kt | 9 +- .../core/lang/fixture/TemplateFixture.kt | 11 +- .../core/lang/resolver/ProcessResolverTest.kt | 18 +- 20 files changed, 411 insertions(+), 254 deletions(-) delete mode 100644 core/src/main/kotlin/ch/kleis/lcaac/core/lang/expression/optics/EveryProcessTemplate.kt diff --git a/core/src/main/kotlin/ch/kleis/lcaac/core/lang/evaluator/ToValue.kt b/core/src/main/kotlin/ch/kleis/lcaac/core/lang/evaluator/ToValue.kt index ffc1dde4..e104b2ad 100644 --- a/core/src/main/kotlin/ch/kleis/lcaac/core/lang/evaluator/ToValue.kt +++ b/core/src/main/kotlin/ch/kleis/lcaac/core/lang/evaluator/ToValue.kt @@ -16,12 +16,19 @@ class ToValue( this.name, this.labels.mapValues { it.value.toValue() as StringValue }, this.products.map { it.toValue() }, - this.inputs.map { it.toValue() }, - this.biosphere.map { it.toValue() }, - this.impacts.map { it.toValue() } + this.inputs.flatMap { blockToValue(it) { block -> block.toValue() } }, + this.biosphere.flatMap { blockToValue(it) { block -> block.toValue() } }, + this.impacts.flatMap { blockToValue(it) { block -> block.toValue() } }, ) } + private fun blockToValue(block: BlockExpression, map: (E) -> V): List { + return when(block) { + is EBlockEntry -> listOf(map(block.entry)) + is EBlockForEach -> throw EvaluatorException("block $block is not reduced") + } + } + fun DataExpression.toValue(): DataValue { return when (this) { is EStringLiteral -> StringValue(this.value) @@ -62,6 +69,7 @@ class ToValue( fun EProductSpec.toValue(): ProductValue { val name = this.name + @Suppress("UNCHECKED_CAST") val referenceUnitValue = (this.referenceUnit as QuantityExpression?) ?.toUnitValue() @@ -124,7 +132,7 @@ class ToValue( fun ESubstanceCharacterization.toValue(): SubstanceCharacterizationValue { return SubstanceCharacterizationValue( referenceExchange = this.referenceExchange.toValue(), - impacts = this.impacts.map { it.toValue() }, + impacts = this.impacts.flatMap { blockToValue(it) { block -> block.toValue() } }, ) } diff --git a/core/src/main/kotlin/ch/kleis/lcaac/core/lang/evaluator/protocol/Learner.kt b/core/src/main/kotlin/ch/kleis/lcaac/core/lang/evaluator/protocol/Learner.kt index 3a2db6df..28f10d0c 100644 --- a/core/src/main/kotlin/ch/kleis/lcaac/core/lang/evaluator/protocol/Learner.kt +++ b/core/src/main/kotlin/ch/kleis/lcaac/core/lang/evaluator/protocol/Learner.kt @@ -1,8 +1,11 @@ package ch.kleis.lcaac.core.lang.evaluator.protocol +import arrow.optics.Every import ch.kleis.lcaac.core.lang.evaluator.EvaluationTrace +import ch.kleis.lcaac.core.lang.evaluator.EvaluatorException import ch.kleis.lcaac.core.lang.evaluator.ToValue import ch.kleis.lcaac.core.lang.expression.* +import ch.kleis.lcaac.core.lang.expression.optics.everyEntry import ch.kleis.lcaac.core.math.QuantityOperations /* @@ -58,18 +61,24 @@ class Learner( } private fun applyKnowledge(process: EProcess): EProcess { - return process.copy( - inputs = process.inputs.map { exchange -> - exchange.copy( - product = knowledge[exchange.product] as EProductSpec? ?: exchange.product - ) - }, - biosphere = process.biosphere.map { exchange -> - exchange.copy( - substance = knowledge[exchange.substance] as ESubstanceSpec? ?: exchange.substance - ) + val everyInputProductSpec = EProcess.inputs() compose + Every.list() compose + BlockExpression.everyEntry() compose + ETechnoExchange.product() + val everySubstance = EProcess.biosphere() compose + Every.list() compose + BlockExpression.everyEntry() compose + EBioExchange.substance() + return process + .let { p -> + everyInputProductSpec.modify(p) { + knowledge[it] as EProductSpec? ?: it + } + }.let { p -> + everySubstance.modify(p) { + knowledge[it] as ESubstanceSpec? ?: it + } } - ) } private fun applyKnowledge(substanceCharacterization: ESubstanceCharacterization): ESubstanceCharacterization { @@ -91,7 +100,7 @@ class Learner( private fun nextRequests(pairs: Set, Int>>): Set> = pairs.flatMap { (connection, index) -> - when(connection) { + when (connection) { is EProcess -> next(connection, index) is ESubstanceCharacterization -> emptySet() } @@ -132,7 +141,9 @@ class Learner( staging.find(address.connectionIndex) ?.let { existingConnection -> if (existingConnection is EProcess) { - knowledge[existingConnection.inputs[address.portIndex].product] = product + val block = existingConnection.inputs[address.portIndex] + if (block !is ETechnoBlockEntry) throw EvaluatorException("$block is not reduced") + knowledge[block.entry.product] = product } } staging.modify( @@ -146,11 +157,13 @@ class Learner( private fun next(process: EProcess, connectionIndex: Int): Set> { val productRequests = process.inputs.mapIndexed { portIndex, it -> - ProductRequest(Address(connectionIndex, portIndex), it.product) + if (it !is ETechnoBlockEntry) throw EvaluatorException("$it is not reduced") + ProductRequest(Address(connectionIndex, portIndex), it.entry.product) }.toSet() val substanceRequest = process.biosphere.mapIndexed { portIndex, it -> - SubstanceRequest(Address(connectionIndex, portIndex), it.substance) + if (it !is EBioBlockEntry) throw EvaluatorException("$it is not reduced") + SubstanceRequest(Address(connectionIndex, portIndex), it.entry.substance) }.toSet() return productRequests + substanceRequest @@ -170,7 +183,9 @@ class Learner( staging.find(address.connectionIndex) ?.let { existingConnection -> if (existingConnection is EProcess) { - knowledge[existingConnection.biosphere[address.portIndex].substance] = substance + val block = existingConnection.biosphere[address.portIndex] + if (block !is EBioBlockEntry) throw EvaluatorException("$block is not reduced") + knowledge[block.entry.substance] = substance } } staging.modify( @@ -188,11 +203,14 @@ class Learner( return { when (it) { is EProcess -> it.copy( - inputs = it.inputs.mapIndexed { index, exchange -> - if (index == portIndex) exchange.copy( - product = product + inputs = it.inputs.mapIndexed { index, block -> + if (block !is ETechnoBlockEntry) throw EvaluatorException("$block is not reduced") + if (index == portIndex) ETechnoBlockEntry( + entry = block.entry.copy( + product = product + ) ) - else exchange + else block } ) @@ -208,11 +226,14 @@ class Learner( return { when (it) { is EProcess -> it.copy( - biosphere = it.biosphere.mapIndexed { index, exchange -> - if (index == portIndex) exchange.copy( - substance = substance + biosphere = it.biosphere.mapIndexed { index, block -> + if (block !is EBioBlockEntry) throw EvaluatorException("$block is not reduced") + if (index == portIndex) EBioBlockEntry( + entry = block.entry.copy( + substance = substance + ) ) - else exchange + else block } ) diff --git a/core/src/main/kotlin/ch/kleis/lcaac/core/lang/evaluator/reducer/LcaExpressionReducer.kt b/core/src/main/kotlin/ch/kleis/lcaac/core/lang/evaluator/reducer/LcaExpressionReducer.kt index 2d7e1c7f..91ac287e 100644 --- a/core/src/main/kotlin/ch/kleis/lcaac/core/lang/evaluator/reducer/LcaExpressionReducer.kt +++ b/core/src/main/kotlin/ch/kleis/lcaac/core/lang/evaluator/reducer/LcaExpressionReducer.kt @@ -1,19 +1,26 @@ package ch.kleis.lcaac.core.lang.evaluator.reducer import ch.kleis.lcaac.core.datasource.DataSourceOperations -import ch.kleis.lcaac.core.lang.register.DataRegister +import ch.kleis.lcaac.core.lang.evaluator.EvaluatorException import ch.kleis.lcaac.core.lang.expression.* +import ch.kleis.lcaac.core.lang.register.DataKey +import ch.kleis.lcaac.core.lang.register.DataRegister +import ch.kleis.lcaac.core.lang.register.DataSourceKey import ch.kleis.lcaac.core.lang.register.DataSourceRegister import ch.kleis.lcaac.core.math.QuantityOperations class LcaExpressionReducer( - dataRegister: DataRegister = DataRegister.empty(), - dataSourceRegister: DataSourceRegister = DataSourceRegister.empty(), - ops: QuantityOperations, - sourceOps: DataSourceOperations, + private val dataRegister: DataRegister = DataRegister.empty(), + private val dataSourceRegister: DataSourceRegister = DataSourceRegister.empty(), + private val ops: QuantityOperations, + private val sourceOps: DataSourceOperations, ) { private val dataExpressionReducer = DataExpressionReducer(dataRegister, dataSourceRegister, ops, sourceOps) + private fun push(data: Map>): LcaExpressionReducer { + TODO() + } + fun reduce(expression: LcaExpression): LcaExpression { return when (expression) { is EProcess -> reduceProcess(expression) @@ -32,7 +39,7 @@ class LcaExpressionReducer( fun reduceSubstanceCharacterization(expression: ESubstanceCharacterization): ESubstanceCharacterization { return ESubstanceCharacterization( reduceBioExchange(expression.referenceExchange), - expression.impacts.map(::reduceImpact), + expression.impacts.flatMap { reduceBlock(it, this::reduceImpact) }, ) } @@ -42,9 +49,9 @@ class LcaExpressionReducer( expression.name, expression.labels, expression.products.map(::reduceTechnoExchange), - expression.inputs.map(::reduceTechnoExchange), - expression.biosphere.map(::reduceBioExchange), - expression.impacts.map(::reduceImpact) + expression.inputs.flatMap { reduceBlock(it, this::reduceTechnoExchange) }, + expression.biosphere.flatMap { reduceBlock(it, this::reduceBioExchange) }, + expression.impacts.flatMap { reduceBlock(it, this::reduceImpact) }, ) } @@ -66,6 +73,30 @@ class LcaExpressionReducer( ) } + // TODO: Test me + private fun > reduceBlock(expression: BlockExpression, onEntry: (E) -> E): + List> { + return when (expression) { + is EBlockEntry -> listOf( + EBlockEntry(onEntry(expression.entry)) + ) + + is EBlockForEach -> { + val ds = dataSourceRegister[DataSourceKey(expression.dataSourceRef)] + ?: throw EvaluatorException("unknown data source '${expression.dataSourceRef}'") + sourceOps.readAll(ds) + .flatMap { record -> + val reducer = push(mapOf( + DataKey(expression.rowRef) to record + )) + expression.body + .flatMap { reducer.reduceBlock(it, onEntry) } + }.toList() + } + } + } + private fun reduceProductSpec(expression: EProductSpec): EProductSpec { return EProductSpec( expression.name, diff --git a/core/src/main/kotlin/ch/kleis/lcaac/core/lang/evaluator/step/CompleteTerminals.kt b/core/src/main/kotlin/ch/kleis/lcaac/core/lang/evaluator/step/CompleteTerminals.kt index 2334b2e2..07597707 100644 --- a/core/src/main/kotlin/ch/kleis/lcaac/core/lang/evaluator/step/CompleteTerminals.kt +++ b/core/src/main/kotlin/ch/kleis/lcaac/core/lang/evaluator/step/CompleteTerminals.kt @@ -3,6 +3,7 @@ package ch.kleis.lcaac.core.lang.evaluator.step import arrow.optics.Every import ch.kleis.lcaac.core.lang.evaluator.EvaluatorException import ch.kleis.lcaac.core.lang.expression.* +import ch.kleis.lcaac.core.lang.expression.optics.everyEntry import ch.kleis.lcaac.core.math.QuantityOperations class CompleteTerminals( @@ -10,7 +11,8 @@ class CompleteTerminals( ) { private val everyInputExchange = EProcess.inputs() compose - Every.list() + Every.list() compose + TechnoBlock.everyEntry() fun apply(expression: EProcess): EProcess = expression @@ -34,7 +36,11 @@ class CompleteTerminals( } private fun EProcess.completeSubstances(): EProcess { - return (EProcess.biosphere() compose Every.list()) + val everyBioExchange = + EProcess.biosphere() compose + Every.list() compose + BioBlock.everyEntry() + return everyBioExchange .modify(this) { exchange -> val referenceUnit = exchangeReferenceUnit(exchange) @@ -47,8 +53,11 @@ class CompleteTerminals( } } - private fun completeIndicators(impacts: Collection>): List> = - impacts.map { exchange -> + private fun completeIndicators(impacts: List>): List> { + val everyImpact = + Every.list>() compose + ImpactBlock.everyEntry() + return everyImpact.modify(impacts) { exchange -> val referenceUnit = exchangeReferenceUnit(exchange) EImpact.indicator() @@ -56,10 +65,11 @@ class CompleteTerminals( EIndicatorSpec(it.name, referenceUnit) } } + } private fun EProcess.completeProcessIndicators(): EProcess = this.copy( - impacts = completeIndicators(this.impacts) + impacts = completeIndicators(this.impacts) ) private fun ESubstanceCharacterization.completeSubstanceIndicators(): ESubstanceCharacterization = diff --git a/core/src/main/kotlin/ch/kleis/lcaac/core/lang/evaluator/step/ReduceLabelSelectors.kt b/core/src/main/kotlin/ch/kleis/lcaac/core/lang/evaluator/step/ReduceLabelSelectors.kt index 4671b94c..62405eec 100644 --- a/core/src/main/kotlin/ch/kleis/lcaac/core/lang/evaluator/step/ReduceLabelSelectors.kt +++ b/core/src/main/kotlin/ch/kleis/lcaac/core/lang/evaluator/step/ReduceLabelSelectors.kt @@ -6,6 +6,7 @@ import ch.kleis.lcaac.core.lang.SymbolTable import ch.kleis.lcaac.core.lang.evaluator.reducer.DataExpressionReducer import ch.kleis.lcaac.core.lang.expression.* import ch.kleis.lcaac.core.lang.expression.optics.everyDataRefInDataExpression +import ch.kleis.lcaac.core.lang.expression.optics.everyEntry import ch.kleis.lcaac.core.lang.register.DataKey import ch.kleis.lcaac.core.lang.register.Register import ch.kleis.lcaac.core.math.QuantityOperations @@ -18,6 +19,7 @@ class ReduceLabelSelectors( private val everyInputProduct = EProcessTemplateApplication.template().body().inputs() compose Every.list() compose + BlockExpression.everyEntry() compose ETechnoExchange.product() private val everyLabelSelector = everyInputProduct compose EProductSpec.fromProcess().matchLabels().elements() compose diff --git a/core/src/main/kotlin/ch/kleis/lcaac/core/lang/expression/LcaExpression.kt b/core/src/main/kotlin/ch/kleis/lcaac/core/lang/expression/LcaExpression.kt index 32628924..e2b826dc 100644 --- a/core/src/main/kotlin/ch/kleis/lcaac/core/lang/expression/LcaExpression.kt +++ b/core/src/main/kotlin/ch/kleis/lcaac/core/lang/expression/LcaExpression.kt @@ -109,8 +109,16 @@ data class EBlockForEach( } typealias TechnoBlock = BlockExpression, Q> +typealias ETechnoBlockEntry = EBlockEntry, Q> +typealias ETechnoBlockForEach = EBlockForEach, Q> + typealias BioBlock = BlockExpression, Q> +typealias EBioBlockEntry = EBlockEntry, Q> +typealias EBioBlockForEach = EBlockForEach, Q> + typealias ImpactBlock = BlockExpression, Q> +typealias EImpactBlockEntry = EBlockEntry, Q> +typealias EImpactBlockForEach = EBlockForEach, Q> // Process @optics @@ -129,7 +137,7 @@ data class EProcess( @optics data class ESubstanceCharacterization( val referenceExchange: EBioExchange, - val impacts: List> + val impacts: List> ) : LcaExpression, ConnectionExpression { companion object diff --git a/core/src/main/kotlin/ch/kleis/lcaac/core/lang/expression/optics/EveryDataRef.kt b/core/src/main/kotlin/ch/kleis/lcaac/core/lang/expression/optics/EveryDataRef.kt index 3e8c6bba..13c9095a 100644 --- a/core/src/main/kotlin/ch/kleis/lcaac/core/lang/expression/optics/EveryDataRef.kt +++ b/core/src/main/kotlin/ch/kleis/lcaac/core/lang/expression/optics/EveryDataRef.kt @@ -160,9 +160,15 @@ fun everyDataRefInProcess(): PEvery, EProcess, EDataRef, D Merge( listOf( EProcess.products() compose Every.list() compose everyDataRefInETechnoExchange(), - EProcess.inputs() compose Every.list() compose everyDataRefInETechnoExchange(), - EProcess.biosphere() compose Every.list() compose everyDataRefInEBioExchange(), - EProcess.impacts() compose Every.list() compose everyDataRefInEImpact(), + EProcess.inputs() compose Every.list() compose + BlockExpression.everyEntry() compose + everyDataRefInETechnoExchange(), + EProcess.biosphere() compose Every.list() compose + BlockExpression.everyEntry() compose + everyDataRefInEBioExchange(), + EProcess.impacts() compose Every.list() compose + BlockExpression.everyEntry() compose + everyDataRefInEImpact(), ) ) @@ -170,7 +176,9 @@ private fun everyDataRefInSubstanceCharacterization(): PEvery() compose everyDataRefInEBioExchange(), - ESubstanceCharacterization.impacts() compose Every.list() compose everyDataRefInEImpact(), + ESubstanceCharacterization.impacts() compose Every.list() compose + BlockExpression.everyEntry() compose + everyDataRefInEImpact(), ) ) diff --git a/core/src/main/kotlin/ch/kleis/lcaac/core/lang/expression/optics/EveryProcessTemplate.kt b/core/src/main/kotlin/ch/kleis/lcaac/core/lang/expression/optics/EveryProcessTemplate.kt deleted file mode 100644 index 064653f6..00000000 --- a/core/src/main/kotlin/ch/kleis/lcaac/core/lang/expression/optics/EveryProcessTemplate.kt +++ /dev/null @@ -1,10 +0,0 @@ -package ch.kleis.lcaac.core.lang.expression.optics - -import ch.kleis.lcaac.core.lang.expression.* - -fun everyProcessTemplateInTemplateExpression() = Merge( - listOf( - ProcessTemplateExpression.eProcessTemplateApplication().template(), - ProcessTemplateExpression.eProcessTemplate(), - ) -) \ No newline at end of file diff --git a/core/src/main/kotlin/ch/kleis/lcaac/core/lang/expression/optics/LcaExpressionOptics.kt b/core/src/main/kotlin/ch/kleis/lcaac/core/lang/expression/optics/LcaExpressionOptics.kt index fc2b5d57..3904ab00 100644 --- a/core/src/main/kotlin/ch/kleis/lcaac/core/lang/expression/optics/LcaExpressionOptics.kt +++ b/core/src/main/kotlin/ch/kleis/lcaac/core/lang/expression/optics/LcaExpressionOptics.kt @@ -5,8 +5,34 @@ package ch.kleis.lcaac.core.lang.expression.optics import arrow.core.identity import arrow.core.left import arrow.core.right +import arrow.optics.Every +import arrow.typeclasses.Monoid import ch.kleis.lcaac.core.lang.expression.* +inline fun BlockExpression.Companion.everyEntry(): Every, E> = + object : Every, E> { + override fun foldMap(M: Monoid, source: BlockExpression, map: (focus: E) -> R): R { + return when(source) { + is EBlockEntry -> map(source.entry) + is EBlockForEach -> M.fold( + source.body.map { foldMap(M, it, map) } + ) + } + } + + override fun modify(source: BlockExpression, map: (focus: E) -> E): BlockExpression { + return when(source) { + is EBlockEntry -> EBlockEntry(map(source.entry)) + is EBlockForEach -> EBlockForEach( + source.rowRef, + source.dataSourceRef, + source.body.map { + modify(it, map) + } + ) + } + } + } inline fun LcaExpression.Companion.eProcess(): arrow.optics.Prism, EProcess> = arrow.optics.Prism( diff --git a/core/src/test/kotlin/ch/kleis/lcaac/core/lang/evaluator/EvaluatorTest.kt b/core/src/test/kotlin/ch/kleis/lcaac/core/lang/evaluator/EvaluatorTest.kt index 691bace2..358fa1d4 100644 --- a/core/src/test/kotlin/ch/kleis/lcaac/core/lang/evaluator/EvaluatorTest.kt +++ b/core/src/test/kotlin/ch/kleis/lcaac/core/lang/evaluator/EvaluatorTest.kt @@ -1,7 +1,7 @@ package ch.kleis.lcaac.core.lang.evaluator import ch.kleis.lcaac.core.datasource.DataSourceOperations -import ch.kleis.lcaac.core.lang.* +import ch.kleis.lcaac.core.lang.SymbolTable import ch.kleis.lcaac.core.lang.dimension.UnitSymbol import ch.kleis.lcaac.core.lang.expression.* import ch.kleis.lcaac.core.lang.fixture.* @@ -34,7 +34,7 @@ class EvaluatorTest { ETechnoExchange(QuantityFixture.oneKilogram, ProductFixture.carrot) ), impacts = listOf( - ImpactFixture.oneClimateChange + ImpactBlockFixture.oneClimateChange ), ) ) @@ -62,7 +62,7 @@ class EvaluatorTest { ETechnoExchange(QuantityFixture.oneKilogram, ProductFixture.carrot) ), impacts = listOf( - ImpactFixture.oneClimateChange + ImpactBlockFixture.oneClimateChange ), ) ) @@ -88,7 +88,7 @@ class EvaluatorTest { ETechnoExchange(QuantityFixture.oneKilogram, ProductFixture.carrot) ), impacts = listOf( - ImpactFixture.oneClimateChange + ImpactBlockFixture.oneClimateChange ), ) ) @@ -122,14 +122,16 @@ class EvaluatorTest { ETechnoExchange(QuantityFixture.oneKilogram, EProductSpec("p", QuantityFixture.oneKilogram)) ), biosphere = listOf( - EBioExchange( - QuantityFixture.oneKilogram, - ESubstanceSpec( - "doesNotExist", - "doesNotExist", - SubstanceType.EMISSION, - "water", - "sea water" + EBioBlockEntry( + EBioExchange( + QuantityFixture.oneKilogram, + ESubstanceSpec( + "doesNotExist", + "doesNotExist", + SubstanceType.EMISSION, + "water", + "sea water" + ) ) ) ), @@ -214,10 +216,12 @@ class EvaluatorTest { ) ), inputs = listOf( - ETechnoExchange( - QuantityFixture.oneKilogram, - EProductSpec( - "carrot", + ETechnoBlockEntry( + ETechnoExchange( + QuantityFixture.oneKilogram, + EProductSpec( + "carrot", + ) ) ) ), @@ -302,23 +306,25 @@ class EvaluatorTest { ) ), inputs = listOf( - ETechnoExchange( - QuantityFixture.oneKilogram, - EProductSpec( - "carrot", - UnitFixture.kg, - FromProcess( - "carrot_production", - MatchLabels(emptyMap()), - mapOf("q_water" to QuantityFixture.twoLitres), - ), + ETechnoBlockEntry( + ETechnoExchange( + QuantityFixture.oneKilogram, + EProductSpec( + "carrot", + UnitFixture.kg, + FromProcess( + "carrot_production", + MatchLabels(emptyMap()), + mapOf("q_water" to QuantityFixture.twoLitres), + ), + ) ) ) ), ), ) // given - val processTemplates= ProcessTemplateRegister( + val processTemplates = ProcessTemplateRegister( mapOf( ProcessKey("carrot_production") to TemplateFixture.carrotProduction, ProcessKey("salad_production") to template, @@ -399,16 +405,18 @@ class EvaluatorTest { ) ), inputs = listOf( - ETechnoExchange( - QuantityFixture.oneKilogram, - EProductSpec( - "irrelevant_product", - UnitFixture.kg, - FromProcess( - "carrot_production", - MatchLabels(emptyMap()), - mapOf("q_water" to QuantityFixture.twoLitres), - ), + ETechnoBlockEntry( + ETechnoExchange( + QuantityFixture.oneKilogram, + EProductSpec( + "irrelevant_product", + UnitFixture.kg, + FromProcess( + "carrot_production", + MatchLabels(emptyMap()), + mapOf("q_water" to QuantityFixture.twoLitres), + ), + ) ) ) ), @@ -444,12 +452,14 @@ class EvaluatorTest { ) ), biosphere = listOf( - EBioExchange( - QuantityFixture.oneKilogram, ESubstanceSpec( + EBioBlockEntry( + EBioExchange( + QuantityFixture.oneKilogram, ESubstanceSpec( "propanol", compartment = "air", type = SubstanceType.RESOURCE, ) + ) ) ), ) @@ -491,9 +501,11 @@ class EvaluatorTest { ) ), inputs = listOf( - ETechnoExchange( - QuantityFixture.oneLitre, - ProductFixture.carrot, + ETechnoBlockEntry( + ETechnoExchange( + QuantityFixture.oneLitre, + ProductFixture.carrot, + ) ) ), ) diff --git a/core/src/test/kotlin/ch/kleis/lcaac/core/lang/evaluator/HelperTest.kt b/core/src/test/kotlin/ch/kleis/lcaac/core/lang/evaluator/HelperTest.kt index cebfc935..af0d6ac0 100644 --- a/core/src/test/kotlin/ch/kleis/lcaac/core/lang/evaluator/HelperTest.kt +++ b/core/src/test/kotlin/ch/kleis/lcaac/core/lang/evaluator/HelperTest.kt @@ -23,13 +23,15 @@ class HelperTest { ETechnoExchange(QuantityFixture.oneKilogram, ProductFixture.carrot) ), inputs = listOf( - ETechnoExchange( - QuantityFixture.oneKilogram, - ProductFixture.carrot.copy( - fromProcess = FromProcess( - name = "another_process", - matchLabels = MatchLabels(mapOf("class" to ref)), - ), + ETechnoBlockEntry( + ETechnoExchange( + QuantityFixture.oneKilogram, + ProductFixture.carrot.copy( + fromProcess = FromProcess( + name = "another_process", + matchLabels = MatchLabels(mapOf("class" to ref)), + ), + ) ) ) ), @@ -46,13 +48,15 @@ class HelperTest { ETechnoExchange(QuantityFixture.oneKilogram, ProductFixture.carrot) ), inputs = listOf( - ETechnoExchange( - QuantityFixture.oneKilogram, - ProductFixture.carrot.copy( - fromProcess = FromProcess( - name = "another_process", - matchLabels = MatchLabels(mapOf("class" to EStringLiteral("foo"))), - ), + ETechnoBlockEntry( + ETechnoExchange( + QuantityFixture.oneKilogram, + ProductFixture.carrot.copy( + fromProcess = FromProcess( + name = "another_process", + matchLabels = MatchLabels(mapOf("class" to EStringLiteral("foo"))), + ), + ) ) ) ), @@ -71,10 +75,10 @@ class HelperTest { ETechnoExchange(ref, ProductFixture.carrot) ), inputs = listOf( - ETechnoExchange(ref, ProductFixture.carrot) + ETechnoBlockEntry(ETechnoExchange(ref, ProductFixture.carrot)) ), biosphere = listOf( - EBioExchange(ref, SubstanceFixture.propanol) + EBioBlockEntry(EBioExchange(ref, SubstanceFixture.propanol)) ), ) val helper = Helper() @@ -89,10 +93,10 @@ class HelperTest { ETechnoExchange(QuantityFixture.oneKilogram, ProductFixture.carrot) ), inputs = listOf( - ETechnoExchange(QuantityFixture.oneKilogram, ProductFixture.carrot) + ETechnoBlockEntry(ETechnoExchange(QuantityFixture.oneKilogram, ProductFixture.carrot)) ), biosphere = listOf( - EBioExchange(QuantityFixture.oneKilogram, SubstanceFixture.propanol) + EBioBlockEntry(EBioExchange(QuantityFixture.oneKilogram, SubstanceFixture.propanol)) ), ) assertEquals(expected, actual) @@ -156,20 +160,24 @@ class HelperTest { name = "p", products = listOf(ETechnoExchange(EDataRef("quantity"), ProductFixture.carrot)), inputs = listOf( - ETechnoExchange( - EQuantityScale( - ops.pure(1.0), - EQuantityMul(EDataRef("ua"), EDataRef("ub")) - ), EProductSpec( + ETechnoBlockEntry( + ETechnoExchange( + EQuantityScale( + ops.pure(1.0), + EQuantityMul(EDataRef("ua"), EDataRef("ub")) + ), EProductSpec( "product", ) + ) ), - ETechnoExchange( - QuantityFixture.oneLitre, EProductSpec( + ETechnoBlockEntry( + ETechnoExchange( + QuantityFixture.oneLitre, EProductSpec( "water", UnitFixture.l, FromProcess(name = "template", matchLabels = MatchLabels(emptyMap())), ) + ) ), ), ) @@ -191,11 +199,13 @@ class HelperTest { val substanceCharacterization = ESubstanceCharacterization( EBioExchange(QuantityFixture.oneKilogram, ESubstanceSpec("substance")), listOf( - EImpact( - EQuantityAdd( - EQuantityScale(ops.pure(3.0), EDataRef("qa")), - EDataRef("qb") - ), EIndicatorSpec("indicator") + EImpactBlockEntry( + EImpact( + EQuantityAdd( + EQuantityScale(ops.pure(3.0), EDataRef("qa")), + EDataRef("qb") + ), EIndicatorSpec("indicator") + ) ) ), ) diff --git a/core/src/test/kotlin/ch/kleis/lcaac/core/lang/evaluator/reducer/LcaExpressionReducerTest.kt b/core/src/test/kotlin/ch/kleis/lcaac/core/lang/evaluator/reducer/LcaExpressionReducerTest.kt index 8b9a68ab..375e68e3 100644 --- a/core/src/test/kotlin/ch/kleis/lcaac/core/lang/evaluator/reducer/LcaExpressionReducerTest.kt +++ b/core/src/test/kotlin/ch/kleis/lcaac/core/lang/evaluator/reducer/LcaExpressionReducerTest.kt @@ -66,10 +66,10 @@ class LcaExpressionReducerTest { ETechnoExchange(EDataRef("q_carrot"), ProductFixture.carrot), ), inputs = listOf( - ETechnoExchange(EDataRef("q_water"), ProductFixture.water) + ETechnoBlockEntry(ETechnoExchange(EDataRef("q_water"), ProductFixture.water)) ), biosphere = listOf( - EBioExchange(EDataRef("q_propanol"), SubstanceFixture.propanol), + EBioBlockEntry(EBioExchange(EDataRef("q_propanol"), SubstanceFixture.propanol)), ), ) val reducer = LcaExpressionReducer( @@ -98,15 +98,19 @@ class LcaExpressionReducerTest { ), ), inputs = listOf( - ETechnoExchange( - QuantityFixture.oneLitre, - ProductFixture.water + ETechnoBlockEntry( + ETechnoExchange( + QuantityFixture.oneLitre, + ProductFixture.water + ) ) ), biosphere = listOf( - EBioExchange( - QuantityFixture.oneKilogram, - SubstanceFixture.propanol + EBioBlockEntry( + EBioExchange( + QuantityFixture.oneKilogram, + SubstanceFixture.propanol + ) ), ), ) @@ -377,9 +381,11 @@ class LcaExpressionReducerTest { SubstanceFixture.propanol ), impacts = listOf( - EImpact( - EDataRef("q_cc"), - IndicatorFixture.climateChange + EImpactBlockEntry( + EImpact( + EDataRef("q_cc"), + IndicatorFixture.climateChange + ) ), ) ) @@ -405,9 +411,11 @@ class LcaExpressionReducerTest { SubstanceFixture.propanol ), impacts = listOf( - EImpact( - QuantityFixture.oneKilogram, - IndicatorFixture.climateChange + EImpactBlockEntry( + EImpact( + QuantityFixture.oneKilogram, + IndicatorFixture.climateChange + ) ), ) ) diff --git a/core/src/test/kotlin/ch/kleis/lcaac/core/lang/evaluator/reducer/ProcessTemplateExpressionReducerTest.kt b/core/src/test/kotlin/ch/kleis/lcaac/core/lang/evaluator/reducer/ProcessTemplateExpressionReducerTest.kt index 0ba0da1c..85c8f11e 100644 --- a/core/src/test/kotlin/ch/kleis/lcaac/core/lang/evaluator/reducer/ProcessTemplateExpressionReducerTest.kt +++ b/core/src/test/kotlin/ch/kleis/lcaac/core/lang/evaluator/reducer/ProcessTemplateExpressionReducerTest.kt @@ -39,7 +39,7 @@ class ProcessTemplateExpressionReducerTest { ), ), inputs = listOf( - ETechnoExchange(EDataRef("q_water"), ProductFixture.water), + ETechnoBlockEntry(ETechnoExchange(EDataRef("q_water"), ProductFixture.water)), ), ) ) @@ -73,9 +73,11 @@ class ProcessTemplateExpressionReducerTest { ), ), inputs = listOf( - ETechnoExchange( - QuantityFixture.oneLitre, - ProductFixture.water + ETechnoBlockEntry( + ETechnoExchange( + QuantityFixture.oneLitre, + ProductFixture.water + ) ), ), ) @@ -104,7 +106,7 @@ class ProcessTemplateExpressionReducerTest { ), ), inputs = listOf( - ETechnoExchange(EDataRef("q_water"), ProductFixture.water), + ETechnoBlockEntry(ETechnoExchange(EDataRef("q_water"), ProductFixture.water)), ), ) ) diff --git a/core/src/test/kotlin/ch/kleis/lcaac/core/lang/evaluator/step/CompleteTerminalsTest.kt b/core/src/test/kotlin/ch/kleis/lcaac/core/lang/evaluator/step/CompleteTerminalsTest.kt index ce629b36..ef8bdcc8 100644 --- a/core/src/test/kotlin/ch/kleis/lcaac/core/lang/evaluator/step/CompleteTerminalsTest.kt +++ b/core/src/test/kotlin/ch/kleis/lcaac/core/lang/evaluator/step/CompleteTerminalsTest.kt @@ -22,7 +22,7 @@ class CompleteTerminalsTest { EProcess( name = "process", biosphere = listOf( - EBioExchange(QuantityFixture.oneKilogram, ESubstanceSpec("co2")) + EBioBlockEntry(EBioExchange(QuantityFixture.oneKilogram, ESubstanceSpec("co2"))) ), ) @@ -34,11 +34,13 @@ class CompleteTerminalsTest { EProcess( name = "process", biosphere = listOf( - EBioExchange( - QuantityFixture.oneKilogram, - ESubstanceSpec( - "co2", - referenceUnit = QuantityFixture.oneKilogram + EBioBlockEntry( + EBioExchange( + QuantityFixture.oneKilogram, + ESubstanceSpec( + "co2", + referenceUnit = QuantityFixture.oneKilogram + ) ) ) ), @@ -52,7 +54,7 @@ class CompleteTerminalsTest { val expression = ESubstanceCharacterization( EBioExchange(QuantityFixture.oneKilogram, SubstanceFixture.propanol), listOf( - EImpact(QuantityFixture.oneKilogram, EIndicatorSpec("cc")) + EImpactBlockEntry(EImpact(QuantityFixture.oneKilogram, EIndicatorSpec("cc"))) ) ) diff --git a/core/src/test/kotlin/ch/kleis/lcaac/core/lang/evaluator/step/ReduceLabelSelectorsTest.kt b/core/src/test/kotlin/ch/kleis/lcaac/core/lang/evaluator/step/ReduceLabelSelectorsTest.kt index 7d448742..22a4123f 100644 --- a/core/src/test/kotlin/ch/kleis/lcaac/core/lang/evaluator/step/ReduceLabelSelectorsTest.kt +++ b/core/src/test/kotlin/ch/kleis/lcaac/core/lang/evaluator/step/ReduceLabelSelectorsTest.kt @@ -26,14 +26,16 @@ class ReduceLabelSelectorsTest { body = EProcess( name = "salad_production", inputs = listOf( - ETechnoExchange( - quantity = QuantityFixture.oneKilogram, - product = EProductSpec( - name = "carrot", - referenceUnit = QuantityFixture.oneKilogram, - fromProcess = FromProcess( - name = "carrot_production", - matchLabels = MatchLabels(mapOf("geo" to EDataRef("geo"))), + ETechnoBlockEntry( + ETechnoExchange( + quantity = QuantityFixture.oneKilogram, + product = EProductSpec( + name = "carrot", + referenceUnit = QuantityFixture.oneKilogram, + fromProcess = FromProcess( + name = "carrot_production", + matchLabels = MatchLabels(mapOf("geo" to EDataRef("geo"))), + ) ) ) ) @@ -54,14 +56,16 @@ class ReduceLabelSelectorsTest { body = EProcess( "salad_production", inputs = listOf( - ETechnoExchange( - QuantityFixture.oneKilogram, - EProductSpec( - "carrot", + ETechnoBlockEntry( + ETechnoExchange( QuantityFixture.oneKilogram, - FromProcess( - name = "carrot_production", - matchLabels = MatchLabels(mapOf("geo" to EStringLiteral("FR"))), + EProductSpec( + "carrot", + QuantityFixture.oneKilogram, + FromProcess( + name = "carrot_production", + matchLabels = MatchLabels(mapOf("geo" to EStringLiteral("FR"))), + ) ) ) ) @@ -82,14 +86,16 @@ class ReduceLabelSelectorsTest { body = EProcess( name = "salad_production", inputs = listOf( - ETechnoExchange( - QuantityFixture.oneKilogram, - EProductSpec( - "carrot", + ETechnoBlockEntry( + ETechnoExchange( QuantityFixture.oneKilogram, - FromProcess( - name = "carrot_production", - matchLabels = MatchLabels(mapOf("geo" to EDataRef("geo"))), + EProductSpec( + "carrot", + QuantityFixture.oneKilogram, + FromProcess( + name = "carrot_production", + matchLabels = MatchLabels(mapOf("geo" to EDataRef("geo"))), + ) ) ) ) @@ -109,14 +115,16 @@ class ReduceLabelSelectorsTest { body = EProcess( name = "salad_production", inputs = listOf( - ETechnoExchange( - QuantityFixture.oneKilogram, - EProductSpec( - "carrot", + ETechnoBlockEntry( + ETechnoExchange( QuantityFixture.oneKilogram, - FromProcess( - name = "carrot_production", - matchLabels = MatchLabels(mapOf("geo" to EStringLiteral("GLO"))), + EProductSpec( + "carrot", + QuantityFixture.oneKilogram, + FromProcess( + name = "carrot_production", + matchLabels = MatchLabels(mapOf("geo" to EStringLiteral("GLO"))), + ) ) ) ) @@ -136,14 +144,16 @@ class ReduceLabelSelectorsTest { body = EProcess( "salad_production", inputs = listOf( - ETechnoExchange( - QuantityFixture.oneKilogram, - EProductSpec( - "carrot", + ETechnoBlockEntry( + ETechnoExchange( QuantityFixture.oneKilogram, - FromProcess( - name = "carrot_production", - matchLabels = MatchLabels(mapOf("geo" to EDataRef("geo"))), + EProductSpec( + "carrot", + QuantityFixture.oneKilogram, + FromProcess( + name = "carrot_production", + matchLabels = MatchLabels(mapOf("geo" to EDataRef("geo"))), + ) ) ) ) @@ -163,14 +173,16 @@ class ReduceLabelSelectorsTest { body = EProcess( name = "salad_production", inputs = listOf( - ETechnoExchange( - QuantityFixture.oneKilogram, - EProductSpec( - "carrot", + ETechnoBlockEntry( + ETechnoExchange( QuantityFixture.oneKilogram, - FromProcess( - name = "carrot_production", - matchLabels = MatchLabels(mapOf("geo" to EStringLiteral("GLO"))), + EProductSpec( + "carrot", + QuantityFixture.oneKilogram, + FromProcess( + name = "carrot_production", + matchLabels = MatchLabels(mapOf("geo" to EStringLiteral("GLO"))), + ) ) ) ) @@ -189,14 +201,16 @@ class ReduceLabelSelectorsTest { body = EProcess( name = "salad_production", inputs = listOf( - ETechnoExchange( - QuantityFixture.oneKilogram, - EProductSpec( - "carrot", + ETechnoBlockEntry( + ETechnoExchange( QuantityFixture.oneKilogram, - FromProcess( - name = "carrot_production", - matchLabels = MatchLabels(mapOf("geo" to EDataRef("geo"))), + EProductSpec( + "carrot", + QuantityFixture.oneKilogram, + FromProcess( + name = "carrot_production", + matchLabels = MatchLabels(mapOf("geo" to EDataRef("geo"))), + ) ) ) ) @@ -222,14 +236,16 @@ class ReduceLabelSelectorsTest { body = EProcess( "salad_production", inputs = listOf( - ETechnoExchange( - QuantityFixture.oneKilogram, - EProductSpec( - "carrot", + ETechnoBlockEntry( + ETechnoExchange( QuantityFixture.oneKilogram, - FromProcess( - name = "carrot_production", - matchLabels = MatchLabels(mapOf("geo" to EStringLiteral("FR"))), + EProductSpec( + "carrot", + QuantityFixture.oneKilogram, + FromProcess( + name = "carrot_production", + matchLabels = MatchLabels(mapOf("geo" to EStringLiteral("FR"))), + ) ) ) ) @@ -249,14 +265,16 @@ class ReduceLabelSelectorsTest { "salad_production", labels = mapOf("geo" to EStringLiteral("FR")), inputs = listOf( - ETechnoExchange( - QuantityFixture.oneKilogram, - EProductSpec( - "carrot", + ETechnoBlockEntry( + ETechnoExchange( QuantityFixture.oneKilogram, - FromProcess( - name = "carrot_production", - matchLabels = MatchLabels(mapOf("geo" to EDataRef("geo"))), + EProductSpec( + "carrot", + QuantityFixture.oneKilogram, + FromProcess( + name = "carrot_production", + matchLabels = MatchLabels(mapOf("geo" to EDataRef("geo"))), + ) ) ) ) @@ -276,14 +294,16 @@ class ReduceLabelSelectorsTest { "salad_production", labels = mapOf("geo" to EStringLiteral("FR")), inputs = listOf( - ETechnoExchange( - QuantityFixture.oneKilogram, - EProductSpec( - "carrot", + ETechnoBlockEntry( + ETechnoExchange( QuantityFixture.oneKilogram, - FromProcess( - name = "carrot_production", - matchLabels = MatchLabels(mapOf("geo" to EStringLiteral("FR"))), + EProductSpec( + "carrot", + QuantityFixture.oneKilogram, + FromProcess( + name = "carrot_production", + matchLabels = MatchLabels(mapOf("geo" to EStringLiteral("FR"))), + ) ) ) ) diff --git a/core/src/test/kotlin/ch/kleis/lcaac/core/lang/fixture/ImpactFixture.kt b/core/src/test/kotlin/ch/kleis/lcaac/core/lang/fixture/ImpactFixture.kt index 3261ef6b..44b1408d 100644 --- a/core/src/test/kotlin/ch/kleis/lcaac/core/lang/fixture/ImpactFixture.kt +++ b/core/src/test/kotlin/ch/kleis/lcaac/core/lang/fixture/ImpactFixture.kt @@ -1,6 +1,7 @@ package ch.kleis.lcaac.core.lang.fixture import ch.kleis.lcaac.core.lang.expression.EImpact +import ch.kleis.lcaac.core.lang.expression.EImpactBlockEntry import ch.kleis.lcaac.core.math.basic.BasicNumber object ImpactFixture { @@ -9,3 +10,9 @@ object ImpactFixture { indicator = IndicatorFixture.climateChange ) } + +object ImpactBlockFixture { + val oneClimateChange : EImpactBlockEntry = EImpactBlockEntry( + entry = ImpactFixture.oneClimateChange + ) +} diff --git a/core/src/test/kotlin/ch/kleis/lcaac/core/lang/fixture/ProcessFixture.kt b/core/src/test/kotlin/ch/kleis/lcaac/core/lang/fixture/ProcessFixture.kt index 277ea92a..ab1071f3 100644 --- a/core/src/test/kotlin/ch/kleis/lcaac/core/lang/fixture/ProcessFixture.kt +++ b/core/src/test/kotlin/ch/kleis/lcaac/core/lang/fixture/ProcessFixture.kt @@ -1,8 +1,6 @@ package ch.kleis.lcaac.core.lang.fixture -import ch.kleis.lcaac.core.lang.expression.EBioExchange -import ch.kleis.lcaac.core.lang.expression.EProcess -import ch.kleis.lcaac.core.lang.expression.ETechnoExchange +import ch.kleis.lcaac.core.lang.expression.* class ProcessFixture { companion object { @@ -13,13 +11,13 @@ class ProcessFixture { ETechnoExchange(QuantityFixture.oneKilogram, ProductFixture.carrot), ), inputs = listOf( - ETechnoExchange(QuantityFixture.oneLitre, ProductFixture.water), + ETechnoBlockEntry(ETechnoExchange(QuantityFixture.oneLitre, ProductFixture.water)), ), biosphere = listOf( - EBioExchange(QuantityFixture.oneKilogram, SubstanceFixture.propanol), + EBioBlockEntry(EBioExchange(QuantityFixture.oneKilogram, SubstanceFixture.propanol)), ), impacts = listOf( - ImpactFixture.oneClimateChange + EImpactBlockEntry(ImpactFixture.oneClimateChange), ) ) } diff --git a/core/src/test/kotlin/ch/kleis/lcaac/core/lang/fixture/SubstanceCharacterizationFixture.kt b/core/src/test/kotlin/ch/kleis/lcaac/core/lang/fixture/SubstanceCharacterizationFixture.kt index cb1dfcee..09fc08f6 100644 --- a/core/src/test/kotlin/ch/kleis/lcaac/core/lang/fixture/SubstanceCharacterizationFixture.kt +++ b/core/src/test/kotlin/ch/kleis/lcaac/core/lang/fixture/SubstanceCharacterizationFixture.kt @@ -1,9 +1,6 @@ package ch.kleis.lcaac.core.lang.fixture -import ch.kleis.lcaac.core.lang.expression.EBioExchange -import ch.kleis.lcaac.core.lang.expression.EImpact -import ch.kleis.lcaac.core.lang.expression.ESubstanceCharacterization -import ch.kleis.lcaac.core.lang.expression.ESubstanceSpec +import ch.kleis.lcaac.core.lang.expression.* import ch.kleis.lcaac.core.math.basic.BasicNumber class SubstanceCharacterizationFixture { @@ -11,7 +8,7 @@ class SubstanceCharacterizationFixture { val propanolCharacterization = ESubstanceCharacterization( referenceExchange = EBioExchange(QuantityFixture.oneKilogram, SubstanceFixture.propanol), impacts = listOf( - EImpact(QuantityFixture.oneKilogram, IndicatorFixture.climateChange), + EImpactBlockEntry(EImpact(QuantityFixture.oneKilogram, IndicatorFixture.climateChange)), ), ) @@ -19,7 +16,7 @@ class SubstanceCharacterizationFixture { ESubstanceCharacterization( referenceExchange = EBioExchange(QuantityFixture.oneKilogram, substance), impacts = listOf( - EImpact(QuantityFixture.oneKilogram, IndicatorFixture.climateChange) + EImpactBlockEntry(EImpact(QuantityFixture.oneKilogram, IndicatorFixture.climateChange)), ) ) } diff --git a/core/src/test/kotlin/ch/kleis/lcaac/core/lang/fixture/TemplateFixture.kt b/core/src/test/kotlin/ch/kleis/lcaac/core/lang/fixture/TemplateFixture.kt index ba784899..d258c75d 100644 --- a/core/src/test/kotlin/ch/kleis/lcaac/core/lang/fixture/TemplateFixture.kt +++ b/core/src/test/kotlin/ch/kleis/lcaac/core/lang/fixture/TemplateFixture.kt @@ -1,9 +1,6 @@ package ch.kleis.lcaac.core.lang.fixture -import ch.kleis.lcaac.core.lang.expression.EDataRef -import ch.kleis.lcaac.core.lang.expression.EProcess -import ch.kleis.lcaac.core.lang.expression.EProcessTemplate -import ch.kleis.lcaac.core.lang.expression.ETechnoExchange +import ch.kleis.lcaac.core.lang.expression.* class TemplateFixture { companion object { @@ -20,7 +17,7 @@ class TemplateFixture { ETechnoExchange(EDataRef("q_carrot"), ProductFixture.carrot), ), inputs = listOf( - ETechnoExchange(EDataRef("q_water"), ProductFixture.water), + ETechnoBlockEntry(ETechnoExchange(EDataRef("q_water"), ProductFixture.water)), ), ) ) @@ -34,7 +31,7 @@ class TemplateFixture { ETechnoExchange(QuantityFixture.twoKilograms, ProductFixture.carrot), ), inputs = listOf( - ETechnoExchange(QuantityFixture.oneGram, ProductFixture.carrot), + ETechnoBlockEntry(ETechnoExchange(QuantityFixture.oneGram, ProductFixture.carrot)), ), ) ) @@ -45,7 +42,7 @@ class TemplateFixture { ETechnoExchange(EDataRef("q_carrot"), ProductFixture.carrot), ), inputs = listOf( - ETechnoExchange(EDataRef("q_water"), ProductFixture.water), + ETechnoBlockEntry(ETechnoExchange(EDataRef("q_water"), ProductFixture.water)), ), ) ) diff --git a/core/src/test/kotlin/ch/kleis/lcaac/core/lang/resolver/ProcessResolverTest.kt b/core/src/test/kotlin/ch/kleis/lcaac/core/lang/resolver/ProcessResolverTest.kt index b9abbfba..186d899a 100644 --- a/core/src/test/kotlin/ch/kleis/lcaac/core/lang/resolver/ProcessResolverTest.kt +++ b/core/src/test/kotlin/ch/kleis/lcaac/core/lang/resolver/ProcessResolverTest.kt @@ -22,7 +22,7 @@ class ProcessResolverTest { ETechnoExchange(EDataRef("q_carrot"), ProductFixture.carrot), ), inputs = listOf( - ETechnoExchange(EDataRef("q_water"), ProductFixture.water), + ETechnoBlockEntry(ETechnoExchange(EDataRef("q_water"), ProductFixture.water)), ), ) val carrotProductionFR = EProcessTemplate( @@ -75,7 +75,7 @@ class ProcessResolverTest { ETechnoExchange(EDataRef("q_carrot"), ProductFixture.carrot), ), inputs = listOf( - ETechnoExchange(EDataRef("q_water"), ProductFixture.water), + ETechnoBlockEntry(ETechnoExchange(EDataRef("q_water"), ProductFixture.water)), ), ) ) @@ -116,7 +116,7 @@ class ProcessResolverTest { ETechnoExchange(EDataRef("q_carrot"), ProductFixture.carrot), ), inputs = listOf( - ETechnoExchange(EDataRef("q_water"), ProductFixture.water), + ETechnoBlockEntry(ETechnoExchange(EDataRef("q_water"), ProductFixture.water)), ), ) ) @@ -127,7 +127,7 @@ class ProcessResolverTest { ETechnoExchange(QuantityFixture.oneKilogram, ProductFixture.salad), ), inputs = listOf( - ETechnoExchange(QuantityFixture.oneKilogram, ProductFixture.carrot), + ETechnoBlockEntry(ETechnoExchange(QuantityFixture.oneKilogram, ProductFixture.carrot)), ), ) ) @@ -168,7 +168,7 @@ class ProcessResolverTest { ETechnoExchange(EDataRef("q_carrot"), ProductFixture.carrot), ), inputs = listOf( - ETechnoExchange(EDataRef("q_water"), ProductFixture.water), + ETechnoBlockEntry(ETechnoExchange(EDataRef("q_water"), ProductFixture.water)), ), ) ) @@ -179,7 +179,7 @@ class ProcessResolverTest { ETechnoExchange(QuantityFixture.oneKilogram, ProductFixture.salad), ), inputs = listOf( - ETechnoExchange(QuantityFixture.oneKilogram, ProductFixture.carrot), + ETechnoBlockEntry(ETechnoExchange(QuantityFixture.oneKilogram, ProductFixture.carrot)), ), ) ) @@ -213,7 +213,7 @@ class ProcessResolverTest { ETechnoExchange(QuantityFixture.oneKilogram, ProductFixture.carrot), ), inputs = listOf( - ETechnoExchange(QuantityFixture.oneKilogram, ProductFixture.water), + ETechnoBlockEntry(ETechnoExchange(QuantityFixture.oneKilogram, ProductFixture.water)), ), ) ) @@ -225,7 +225,7 @@ class ProcessResolverTest { ETechnoExchange(QuantityFixture.oneKilogram, ProductFixture.carrot), ), inputs = listOf( - ETechnoExchange(QuantityFixture.oneKilogram, ProductFixture.water), + ETechnoBlockEntry(ETechnoExchange(QuantityFixture.oneKilogram, ProductFixture.water)), ), ) ) @@ -237,7 +237,7 @@ class ProcessResolverTest { ETechnoExchange(QuantityFixture.oneKilogram, ProductFixture.salad), ), inputs = listOf( - ETechnoExchange(QuantityFixture.oneKilogram, ProductFixture.carrot), + ETechnoBlockEntry(ETechnoExchange(QuantityFixture.oneKilogram, ProductFixture.carrot)), ), ) ) From 35a048a83771dfe55fcf3f7e65a7ca243034ae7d Mon Sep 17 00:00:00 2001 From: Peva Blanchard Date: Sat, 20 Jan 2024 18:00:14 +0100 Subject: [PATCH 10/37] core: test lca reducer for blocks --- .../evaluator/reducer/LcaExpressionReducer.kt | 72 +++++- .../lang/expression/optics/EveryDataRef.kt | 4 +- .../reducer/LcaExpressionReducerTest.kt | 232 +++++++++++++++++- 3 files changed, 292 insertions(+), 16 deletions(-) diff --git a/core/src/main/kotlin/ch/kleis/lcaac/core/lang/evaluator/reducer/LcaExpressionReducer.kt b/core/src/main/kotlin/ch/kleis/lcaac/core/lang/evaluator/reducer/LcaExpressionReducer.kt index 91ac287e..0bf76c32 100644 --- a/core/src/main/kotlin/ch/kleis/lcaac/core/lang/evaluator/reducer/LcaExpressionReducer.kt +++ b/core/src/main/kotlin/ch/kleis/lcaac/core/lang/evaluator/reducer/LcaExpressionReducer.kt @@ -18,7 +18,9 @@ class LcaExpressionReducer( private val dataExpressionReducer = DataExpressionReducer(dataRegister, dataSourceRegister, ops, sourceOps) private fun push(data: Map>): LcaExpressionReducer { - TODO() + val localRegister = DataRegister(dataRegister) + .plus(data) + return LcaExpressionReducer(localRegister, dataSourceRegister, ops, sourceOps) } fun reduce(expression: LcaExpression): LcaExpression { @@ -39,7 +41,7 @@ class LcaExpressionReducer( fun reduceSubstanceCharacterization(expression: ESubstanceCharacterization): ESubstanceCharacterization { return ESubstanceCharacterization( reduceBioExchange(expression.referenceExchange), - expression.impacts.flatMap { reduceBlock(it, this::reduceImpact) }, + expression.impacts.flatMap { reduceImpactBlock(it) }, ) } @@ -49,9 +51,9 @@ class LcaExpressionReducer( expression.name, expression.labels, expression.products.map(::reduceTechnoExchange), - expression.inputs.flatMap { reduceBlock(it, this::reduceTechnoExchange) }, - expression.biosphere.flatMap { reduceBlock(it, this::reduceBioExchange) }, - expression.impacts.flatMap { reduceBlock(it, this::reduceImpact) }, + expression.inputs.flatMap { reduceTechnoBlock(it) }, + expression.biosphere.flatMap { reduceBioBlock(it) }, + expression.impacts.flatMap { reduceImpactBlock(it) }, ) } @@ -73,13 +75,61 @@ class LcaExpressionReducer( ) } - // TODO: Test me - private fun > reduceBlock(expression: BlockExpression, onEntry: (E) -> E): - List> { + private fun reduceTechnoBlock(expression: TechnoBlock): + List> { return when (expression) { is EBlockEntry -> listOf( - EBlockEntry(onEntry(expression.entry)) + ETechnoBlockEntry( + reduceTechnoExchange(expression.entry) + ) + ) + + is EBlockForEach -> { + val ds = dataSourceRegister[DataSourceKey(expression.dataSourceRef)] + ?: throw EvaluatorException("unknown data source '${expression.dataSourceRef}'") + sourceOps.readAll(ds) + .flatMap { record -> + val reducer = push(mapOf( + DataKey(expression.rowRef) to record + )) + expression.body + .flatMap { reducer.reduceTechnoBlock(it) } + }.toList() + } + } + } + + private fun reduceBioBlock(expression: BioBlock): + List> { + return when (expression) { + is EBlockEntry -> listOf( + EBioBlockEntry( + reduceBioExchange(expression.entry) + ) + ) + + is EBlockForEach -> { + val ds = dataSourceRegister[DataSourceKey(expression.dataSourceRef)] + ?: throw EvaluatorException("unknown data source '${expression.dataSourceRef}'") + sourceOps.readAll(ds) + .flatMap { record -> + val reducer = push(mapOf( + DataKey(expression.rowRef) to record + )) + expression.body + .flatMap { reducer.reduceBioBlock(it) } + }.toList() + } + } + } + + private fun reduceImpactBlock(expression: ImpactBlock): + List> { + return when (expression) { + is EBlockEntry -> listOf( + EImpactBlockEntry( + reduceImpact(expression.entry) + ) ) is EBlockForEach -> { @@ -91,7 +141,7 @@ class LcaExpressionReducer( DataKey(expression.rowRef) to record )) expression.body - .flatMap { reducer.reduceBlock(it, onEntry) } + .flatMap { reducer.reduceImpactBlock(it) } }.toList() } } diff --git a/core/src/main/kotlin/ch/kleis/lcaac/core/lang/expression/optics/EveryDataRef.kt b/core/src/main/kotlin/ch/kleis/lcaac/core/lang/expression/optics/EveryDataRef.kt index 13c9095a..e698f2c7 100644 --- a/core/src/main/kotlin/ch/kleis/lcaac/core/lang/expression/optics/EveryDataRef.kt +++ b/core/src/main/kotlin/ch/kleis/lcaac/core/lang/expression/optics/EveryDataRef.kt @@ -53,7 +53,7 @@ fun everyDataRefInDataExpression(): PEvery, DataExpression ) is ERecordEntry -> foldMap(M, source.record, map) is EDefaultRecordOf -> M.empty() - is ESum -> TODO() + is ESum -> M.empty() } } @@ -120,7 +120,7 @@ fun everyDataRefInDataExpression(): PEvery, DataExpression ) is EDefaultRecordOf -> source - is ESum -> TODO() + is ESum -> source } } } diff --git a/core/src/test/kotlin/ch/kleis/lcaac/core/lang/evaluator/reducer/LcaExpressionReducerTest.kt b/core/src/test/kotlin/ch/kleis/lcaac/core/lang/evaluator/reducer/LcaExpressionReducerTest.kt index 375e68e3..219cc9aa 100644 --- a/core/src/test/kotlin/ch/kleis/lcaac/core/lang/evaluator/reducer/LcaExpressionReducerTest.kt +++ b/core/src/test/kotlin/ch/kleis/lcaac/core/lang/evaluator/reducer/LcaExpressionReducerTest.kt @@ -3,19 +3,245 @@ package ch.kleis.lcaac.core.lang.evaluator.reducer import ch.kleis.lcaac.core.datasource.DataSourceOperations import ch.kleis.lcaac.core.lang.expression.* import ch.kleis.lcaac.core.lang.fixture.* -import ch.kleis.lcaac.core.lang.register.DataKey -import ch.kleis.lcaac.core.lang.register.DataRegister -import ch.kleis.lcaac.core.lang.register.Register +import ch.kleis.lcaac.core.lang.register.* import ch.kleis.lcaac.core.math.basic.BasicNumber import ch.kleis.lcaac.core.math.basic.BasicOperations import io.mockk.mockk import org.junit.jupiter.api.Test +import org.junit.jupiter.api.assertThrows import kotlin.test.assertEquals class LcaExpressionReducerTest { private val ops = BasicOperations private val sourceOps = mockk>() + /* + Block + */ + + @Test + fun reduce_whenBlockForEach_withRecordEntryOverride_shouldThrow() { + // given + val block = ETechnoBlockForEach( + "row", + "source", + listOf( + ETechnoBlockEntry( + ETechnoExchange( + ERecordEntry(EDataRef("row"), "mass"), + ProductFixture.carrot, + ) + ), + ) + ) + val expression = EProcess( + name = "foo", + inputs = listOf(block), + ) + val sourceOps = object : DataSourceOperations { + override fun readAll(source: DataSourceExpression): Sequence> { + return sequenceOf( + ERecord(mapOf( + "mass" to QuantityFixture.oneKilogram, + )), + ERecord(mapOf( + "mass" to QuantityFixture.twoKilograms, + )), + ) + } + + override fun sum(source: DataSourceExpression, column: String): DataExpression { + throw IllegalAccessException("should not have been called") + } + } + val reducer = LcaExpressionReducer( + DataRegister.from(mapOf( + DataKey("row") to QuantityFixture.oneLitre, + )), + DataSourceRegister.from(mapOf( + DataSourceKey("source") to mockk(), + )), + ops, + sourceOps, + ) + + // when/then + val e = assertThrows { reducer.reduce(expression) } + assertEquals("[row] is already bound", e.message) + } + + @Test + fun reduce_whenBlockForEach_withRecordEntry() { + // given + val block = ETechnoBlockForEach( + "row", + "source", + listOf( + ETechnoBlockEntry( + ETechnoExchange( + ERecordEntry(EDataRef("row"), "mass"), + ProductFixture.carrot, + ) + ), + ) + ) + val expression = EProcess( + name = "foo", + inputs = listOf(block), + ) + val sourceOps = object : DataSourceOperations { + override fun readAll(source: DataSourceExpression): Sequence> { + return sequenceOf( + ERecord(mapOf( + "mass" to QuantityFixture.oneKilogram, + )), + ERecord(mapOf( + "mass" to QuantityFixture.twoKilograms, + )), + ) + } + + override fun sum(source: DataSourceExpression, column: String): DataExpression { + throw IllegalAccessException("should not have been called") + } + } + val reducer = LcaExpressionReducer( + DataRegister.empty(), + DataSourceRegister.from(mapOf( + DataSourceKey("source") to mockk(), + )), + ops, + sourceOps, + ) + + // when + val actual = reducer.reduce(expression) + + // then + val expected = EProcess( + name = "foo", + inputs = listOf( + ETechnoBlockEntry( + ETechnoExchange( + QuantityFixture.oneKilogram, + ProductFixture.carrot, + ) + ), + ETechnoBlockEntry( + ETechnoExchange( + QuantityFixture.twoKilograms, + ProductFixture.carrot, + ) + ), + ), + ) + assertEquals(expected, actual) + } + + @Test + fun reduce_whenBlockForEach_shouldFlatten() { + // given + val block = ETechnoBlockForEach( + "row", + "source", + listOf( + ETechnoBlockEntry( + ETechnoExchange( + QuantityFixture.oneKilogram, + ProductFixture.carrot, + ) + ), + EBlockForEach( + "row2", + "source", + listOf( + ETechnoBlockEntry( + ETechnoExchange( + QuantityFixture.oneLitre, + ProductFixture.water, + ) + ), + ) + ) + ) + ) + val expression = EProcess( + name = "foo", + inputs = listOf(block), + ) + val sourceOps = object : DataSourceOperations { + override fun readAll(source: DataSourceExpression): Sequence> { + return sequenceOf( + ERecord(emptyMap()), + ERecord(emptyMap()), + ) + } + + override fun sum(source: DataSourceExpression, column: String): DataExpression { + throw IllegalAccessException("should not have been called") + } + } + val reducer = LcaExpressionReducer( + DataRegister.empty(), + DataSourceRegister.from(mapOf( + DataSourceKey("source") to mockk(), + )), + ops, + sourceOps, + ) + + // when + val actual = reducer.reduce(expression) + + // then + val expected = EProcess( + name = "foo", + inputs = listOf( + ETechnoBlockEntry( + ETechnoExchange( + QuantityFixture.oneKilogram, + ProductFixture.carrot, + ) + ), + ETechnoBlockEntry( + ETechnoExchange( + QuantityFixture.oneLitre, + ProductFixture.water, + ) + ), + ETechnoBlockEntry( + ETechnoExchange( + QuantityFixture.oneLitre, + ProductFixture.water, + ) + ), + ETechnoBlockEntry( + ETechnoExchange( + QuantityFixture.oneKilogram, + ProductFixture.carrot, + ) + ), + ETechnoBlockEntry( + ETechnoExchange( + QuantityFixture.oneLitre, + ProductFixture.water, + ) + ), + ETechnoBlockEntry( + ETechnoExchange( + QuantityFixture.oneLitre, + ProductFixture.water, + ) + ), + ), + ) + assertEquals(expected, actual) + } + + /* + Techno Exchange + */ + @Test fun reduce_whenTechnoExchange_shouldReduceLabelSelectors() { // given From 3769687fcc85c3de4284bc4a66922ca946e3b158 Mon Sep 17 00:00:00 2001 From: Peva Blanchard Date: Sat, 20 Jan 2024 18:46:58 +0100 Subject: [PATCH 11/37] grammar: datasource, column operation in g4 file --- grammar/src/main/antlr/LcaLang.g4 | 56 ++++++++++++++++++++++++++++--- 1 file changed, 51 insertions(+), 5 deletions(-) diff --git a/grammar/src/main/antlr/LcaLang.g4 b/grammar/src/main/antlr/LcaLang.g4 index 22292689..f64136db 100644 --- a/grammar/src/main/antlr/LcaLang.g4 +++ b/grammar/src/main/antlr/LcaLang.g4 @@ -1,7 +1,8 @@ grammar LcaLang; lcaFile - : pkg? pkgImport* ( processDefinition | testDefinition | unitDefinition | substanceDefinition | globalVariables )* EOF + : pkg? pkgImport* ( processDefinition | dataSourceDefinition | testDefinition | unitDefinition | + substanceDefinition | globalVariables )* EOF ; /* @@ -27,6 +28,29 @@ globalAssignment : dataRef EQUAL dataExpression ; +/* + Data source +*/ + +dataSourceDefinition + : DATASOURCE_KEYWORD dataSourceRef LBRACE + ( + locationField | schema + )* + RBRACE + ; +locationField + : LOCATION EQUAL STRING_LITERAL + ; +schema + : SCHEMA_KEYWORD LBRACE + columnDefinition* + RBRACE + ; +columnDefinition + : STRING_LITERAL EQUAL dataExpression + ; + /* Test */ @@ -174,16 +198,19 @@ block_impacts */ technoInputExchange - : quantity=dataExpression product=inputProductSpec + : quantity=dataExpression product=inputProductSpec # technoEntry + | FOR_EACH_KEYWORD dataRef IN_KEYWORD dataSourceRef LBRACE technoInputExchange* RBRACE # technoBlockForEach ; technoProductExchange : quantity=dataExpression product=outputProductSpec ; bioExchange - : quantity=dataExpression substance=substanceSpec + : quantity=dataExpression substance=substanceSpec # bioEntry + | FOR_EACH_KEYWORD dataRef IN_KEYWORD dataSourceRef LBRACE bioExchange* RBRACE # bioBlockForEach ; impactExchange - : quantity=dataExpression indicator=indicatorRef + : quantity=dataExpression indicator=indicatorRef # impactEntry + | FOR_EACH_KEYWORD dataRef IN_KEYWORD dataSourceRef LBRACE impactExchange* RBRACE # impactBlockForEach ; @@ -198,7 +225,11 @@ dataExpression | left=dataExpression op=(PLUS | MINUS) right=dataExpression # addGroup | parenExpression # baseGroup | stringExpression # baseGroup - | dataRef # baseGroup + | dataRef slice? # baseGroup + | op=SUM LPAREN dataSourceRef COMMA columnRef COMMA? RPAREN # colGroup + ; +slice + : LBRACK columnRef RBRACK ; parenExpression @@ -209,6 +240,10 @@ stringExpression : STRING_LITERAL ; +columnRef + : STRING_LITERAL + ; + /* Unit */ @@ -228,6 +263,7 @@ labelRef : uid ; dataRef : uid ; productRef : uid ; processRef : uid ; +dataSourceRef : uid ; substanceRef : uid ; indicatorRef : uid ; parameterRef : uid ; @@ -314,12 +350,22 @@ RESOURCES_KEYWORD : 'resources' ; MATCH_KEYWORD : 'match' ; LABELS_KEYWORD : 'labels' ; +DATASOURCE_KEYWORD : 'datasource' ; +LOCATION : 'location' ; +SCHEMA_KEYWORD : 'schema' ; + TEST_KEYWORD : 'test' ; GIVEN_KEYWORD : 'given' ; ASSERT_KEYWORD : 'assert' ; BETWEEN_KEYWORD : 'between' ; AND_KEYWORD : 'and' ; +FOR_EACH_KEYWORD : 'for_each' ; +IN_KEYWORD : 'in' ; + +SUM : 'sum' ; + + EQUAL : '=' ; LBRACK : '[' ; RBRACK : ']' ; From 2d73d2146d3f7e87717ca6ee6301185c9388e63d Mon Sep 17 00:00:00 2001 From: Peva Blanchard Date: Sat, 20 Jan 2024 19:16:10 +0100 Subject: [PATCH 12/37] grammar: make existing language features work --- .../ch/kleis/lcaac/grammar/CoreMapper.kt | 59 +++-- .../kleis/lcaac/grammar/CoreTestMapperTest.kt | 8 +- .../ch/kleis/lcaac/grammar/EvaluatorTest.kt | 33 +-- .../ch/kleis/lcaac/grammar/LoaderTest.kt | 227 ++++++++++-------- 4 files changed, 188 insertions(+), 139 deletions(-) diff --git a/grammar/src/main/kotlin/ch/kleis/lcaac/grammar/CoreMapper.kt b/grammar/src/main/kotlin/ch/kleis/lcaac/grammar/CoreMapper.kt index 27af44b2..1e55f93c 100644 --- a/grammar/src/main/kotlin/ch/kleis/lcaac/grammar/CoreMapper.kt +++ b/grammar/src/main/kotlin/ch/kleis/lcaac/grammar/CoreMapper.kt @@ -2,15 +2,13 @@ package ch.kleis.lcaac.grammar -import ch.kleis.lcaac.core.lang.* +import ch.kleis.lcaac.core.lang.SymbolTable import ch.kleis.lcaac.core.lang.dimension.Dimension import ch.kleis.lcaac.core.lang.dimension.UnitSymbol import ch.kleis.lcaac.core.lang.evaluator.EvaluatorException import ch.kleis.lcaac.core.lang.expression.* import ch.kleis.lcaac.core.lang.register.* import ch.kleis.lcaac.core.math.QuantityOperations -import ch.kleis.lcaac.core.math.basic.BasicNumber -import ch.kleis.lcaac.core.testing.TestCase import ch.kleis.lcaac.grammar.parser.LcaLangParser import org.antlr.v4.runtime.tree.TerminalNode import java.lang.Double.parseDouble @@ -73,11 +71,18 @@ class CoreMapper( ) } - fun impactExchange(ctx: LcaLangParser.ImpactExchangeContext): EImpact { - return EImpact( - dataExpression(ctx.quantity), - indicatorSpec(ctx.indicator), - ) + fun impactExchange(ctx: LcaLangParser.ImpactExchangeContext): ImpactBlock { + return when (ctx) { + is LcaLangParser.ImpactEntryContext -> EImpactBlockEntry( + EImpact( + dataExpression(ctx.quantity), + indicatorSpec(ctx.indicator), + ) + ) + + is LcaLangParser.ImpactBlockForEachContext -> TODO() + else -> throw IllegalStateException("parsing error: expecting an impact exchange context") + } } fun indicatorSpec(ctx: LcaLangParser.IndicatorRefContext): EIndicatorSpec { @@ -90,12 +95,21 @@ class CoreMapper( ctx: LcaLangParser.BioExchangeContext, symbolTable: SymbolTable, type: SubstanceType - ): EBioExchange { - val quantity = dataExpression(ctx.quantity) - return EBioExchange( - quantity, - substanceSpec(ctx.substance, type, quantity, symbolTable) - ) + ): BioBlock { + return when (ctx) { + is LcaLangParser.BioEntryContext -> { + val quantity = dataExpression(ctx.quantity) + EBioBlockEntry( + EBioExchange( + quantity, + substanceSpec(ctx.substance, type, quantity, symbolTable), + ) + ) + } + + is LcaLangParser.BioBlockForEachContext -> TODO() + else -> throw IllegalStateException("parsing error: expecting a bio exchange context") + } } fun substanceSpec( @@ -131,11 +145,18 @@ class CoreMapper( } - fun technoInputExchange(ctx: LcaLangParser.TechnoInputExchangeContext): ETechnoExchange { - return ETechnoExchange( - dataExpression(ctx.quantity), - inputProductSpec(ctx.product), - ) + fun technoInputExchange(ctx: LcaLangParser.TechnoInputExchangeContext): TechnoBlock { + return when (ctx) { + is LcaLangParser.TechnoEntryContext -> ETechnoBlockEntry( + ETechnoExchange( + dataExpression(ctx.quantity), + inputProductSpec(ctx.product), + ) + ) + + is LcaLangParser.TechnoBlockForEachContext -> TODO() + else -> throw IllegalStateException("parsing error: expecting a techno input exchange context") + } } fun inputProductSpec(ctx: LcaLangParser.InputProductSpecContext): EProductSpec { diff --git a/grammar/src/test/kotlin/ch/kleis/lcaac/grammar/CoreTestMapperTest.kt b/grammar/src/test/kotlin/ch/kleis/lcaac/grammar/CoreTestMapperTest.kt index a0f10a1b..9277aa23 100644 --- a/grammar/src/test/kotlin/ch/kleis/lcaac/grammar/CoreTestMapperTest.kt +++ b/grammar/src/test/kotlin/ch/kleis/lcaac/grammar/CoreTestMapperTest.kt @@ -50,9 +50,11 @@ class CoreTestMapperTest { ) ), inputs = listOf( - ETechnoExchange( - EQuantityScale(BasicNumber(1.0), EDataRef("kWh")), - EProductSpec("electricity"), + ETechnoBlockEntry( + ETechnoExchange( + EQuantityScale(BasicNumber(1.0), EDataRef("kWh")), + EProductSpec("electricity"), + ) ) ) ) diff --git a/grammar/src/test/kotlin/ch/kleis/lcaac/grammar/EvaluatorTest.kt b/grammar/src/test/kotlin/ch/kleis/lcaac/grammar/EvaluatorTest.kt index d47bb700..304f531f 100644 --- a/grammar/src/test/kotlin/ch/kleis/lcaac/grammar/EvaluatorTest.kt +++ b/grammar/src/test/kotlin/ch/kleis/lcaac/grammar/EvaluatorTest.kt @@ -1,6 +1,7 @@ package ch.kleis.lcaac.grammar import ch.kleis.lcaac.core.assessment.ContributionAnalysisProgram +import ch.kleis.lcaac.core.datasource.DataSourceOperations import ch.kleis.lcaac.core.lang.dimension.Dimension import ch.kleis.lcaac.core.lang.dimension.UnitSymbol import ch.kleis.lcaac.core.lang.evaluator.Evaluator @@ -12,10 +13,14 @@ import ch.kleis.lcaac.core.lang.value.UnitValue import ch.kleis.lcaac.core.math.basic.BasicNumber import ch.kleis.lcaac.core.math.basic.BasicOperations import ch.kleis.lcaac.grammar.LcaLangFixture.Companion.lcaFile +import io.mockk.mockk import org.junit.jupiter.api.Test import kotlin.test.assertEquals class EvaluatorTest { + private val ops = BasicOperations + private val sourceOps = mockk>() + @Test fun arena_shouldHandleKnowledgeCorrectly() { // given @@ -53,13 +58,13 @@ class EvaluatorTest { } """.trimIndent() ) - val loader = Loader(BasicOperations) + val loader = Loader(ops) val symbolTable = loader.load(sequenceOf(file), listOf(LoaderOption.WITH_PRELUDE)) val spec = EProductSpec( name = "a", fromProcess = FromProcess("a_proc", MatchLabels(emptyMap())), ) - val evaluator = Evaluator(symbolTable, BasicOperations) + val evaluator = Evaluator(symbolTable, ops, sourceOps) // when val trace = evaluator.trace(setOf(spec)) @@ -101,13 +106,13 @@ class EvaluatorTest { } """.trimIndent() ) - val loader = Loader(BasicOperations) + val loader = Loader(ops) val symbolTable = loader.load(sequenceOf(file), listOf(LoaderOption.WITH_PRELUDE)) val spec = EProductSpec( name = "A", fromProcess = FromProcess("p1", MatchLabels(emptyMap())), ) - val evaluator = Evaluator(symbolTable, BasicOperations) + val evaluator = Evaluator(symbolTable, ops, sourceOps) // when val trace = evaluator.trace(setOf(spec)) @@ -139,13 +144,13 @@ class EvaluatorTest { } """.trimIndent() ) - val loader = Loader(BasicOperations) + val loader = Loader(ops) val symbolTable = loader.load(sequenceOf(file), listOf(LoaderOption.WITH_PRELUDE)) val spec = EProductSpec( name = "carrot", fromProcess = FromProcess("p", MatchLabels(emptyMap())), ) - val evaluator = Evaluator(symbolTable, BasicOperations) + val evaluator = Evaluator(symbolTable, ops, sourceOps) // when val trace = evaluator.trace(setOf(spec)) @@ -192,13 +197,13 @@ class EvaluatorTest { } """.trimIndent() ) - val loader = Loader(BasicOperations) + val loader = Loader(ops) val symbolTable = loader.load(sequenceOf(file), listOf(LoaderOption.WITH_PRELUDE)) val spec = EProductSpec( name = "carrot", fromProcess = FromProcess("p", MatchLabels(emptyMap())), ) - val evaluator = Evaluator(symbolTable, BasicOperations) + val evaluator = Evaluator(symbolTable, ops, sourceOps) // when val trace = evaluator.trace(setOf(spec)) @@ -236,13 +241,13 @@ class EvaluatorTest { } """.trimIndent() ) - val loader = Loader(BasicOperations) + val loader = Loader(ops) val symbolTable = loader.load(sequenceOf(file), listOf(LoaderOption.WITH_PRELUDE)) val spec = EProductSpec( name = "carrot", fromProcess = FromProcess("p", MatchLabels(emptyMap())), ) - val evaluator = Evaluator(symbolTable, BasicOperations) + val evaluator = Evaluator(symbolTable, ops, sourceOps) // when val trace = evaluator.trace(setOf(spec)) @@ -281,13 +286,13 @@ class EvaluatorTest { } """.trimIndent() ) - val loader = Loader(BasicOperations) + val loader = Loader(ops) val symbolTable = loader.load(sequenceOf(file), listOf(LoaderOption.WITH_PRELUDE)) val spec = EProductSpec( name = "carrot", fromProcess = FromProcess("p", MatchLabels(emptyMap())), ) - val evaluator = Evaluator(symbolTable, BasicOperations) + val evaluator = Evaluator(symbolTable, ops, sourceOps) // when val trace = evaluator.trace(setOf(spec)) @@ -336,13 +341,13 @@ class EvaluatorTest { } """.trimIndent() ) - val loader = Loader(BasicOperations) + val loader = Loader(ops) val symbolTable = loader.load(sequenceOf(file), listOf(LoaderOption.WITH_PRELUDE)) val spec = EProductSpec( name = "carrot", fromProcess = FromProcess("p", MatchLabels(emptyMap())), ) - val evaluator = Evaluator(symbolTable, BasicOperations) + val evaluator = Evaluator(symbolTable, ops, sourceOps) // when val trace = evaluator.trace(setOf(spec)) diff --git a/grammar/src/test/kotlin/ch/kleis/lcaac/grammar/LoaderTest.kt b/grammar/src/test/kotlin/ch/kleis/lcaac/grammar/LoaderTest.kt index 5bbc50fb..94303ad7 100644 --- a/grammar/src/test/kotlin/ch/kleis/lcaac/grammar/LoaderTest.kt +++ b/grammar/src/test/kotlin/ch/kleis/lcaac/grammar/LoaderTest.kt @@ -1,11 +1,11 @@ package ch.kleis.lcaac.grammar -import ch.kleis.lcaac.core.lang.register.DataKey -import ch.kleis.lcaac.core.lang.register.DataRegister import ch.kleis.lcaac.core.lang.SymbolTable import ch.kleis.lcaac.core.lang.dimension.Dimension import ch.kleis.lcaac.core.lang.dimension.UnitSymbol import ch.kleis.lcaac.core.lang.expression.* +import ch.kleis.lcaac.core.lang.register.DataKey +import ch.kleis.lcaac.core.lang.register.DataRegister import ch.kleis.lcaac.core.math.basic.BasicNumber import ch.kleis.lcaac.core.math.basic.BasicOperations import ch.kleis.lcaac.grammar.LcaLangFixture.Companion.lcaFile @@ -95,9 +95,10 @@ class LoaderTest { val loader = Loader(BasicOperations) // when - val actual = loader.load(sequenceOf(file)).getTemplate("p")!! + val input = loader.load(sequenceOf(file)).getTemplate("p")!! .body - .inputs[0] + .inputs[0] as ETechnoBlockEntry + val actual = input.entry .product.fromProcess!! // then @@ -293,18 +294,20 @@ class LoaderTest { val expected = ESubstanceCharacterization( referenceExchange = EBioExchange( EDataRef("kg"), ESubstanceSpec( - name = "co2", - displayName = "carbon dioxide", - type = SubstanceType.EMISSION, - compartment = compartment, - subCompartment = null, - referenceUnit = EUnitOf(EDataRef("kg")), - ) + name = "co2", + displayName = "carbon dioxide", + type = SubstanceType.EMISSION, + compartment = compartment, + subCompartment = null, + referenceUnit = EUnitOf(EDataRef("kg")), + ) ), impacts = listOf( - EImpact( - oneKg, - EIndicatorSpec("GWP", null) + EImpactBlockEntry( + EImpact( + oneKg, + EIndicatorSpec("GWP", null) + ) ) ), ) @@ -341,18 +344,20 @@ class LoaderTest { val expected = ESubstanceCharacterization( referenceExchange = EBioExchange( EDataRef("kg"), ESubstanceSpec( - name = "co2", - displayName = "carbon dioxide", - type = SubstanceType.EMISSION, - compartment = compartment, - subCompartment = subCompartment, - referenceUnit = EUnitOf(EDataRef("kg")), - ) + name = "co2", + displayName = "carbon dioxide", + type = SubstanceType.EMISSION, + compartment = compartment, + subCompartment = subCompartment, + referenceUnit = EUnitOf(EDataRef("kg")), + ) ), impacts = listOf( - EImpact( - oneKg, - EIndicatorSpec("GWP", null) + EImpactBlockEntry( + EImpact( + oneKg, + EIndicatorSpec("GWP", null) + ) ) ), ) @@ -563,7 +568,7 @@ class LoaderTest { """ process p { inputs { - 1 kg in from q(a = 1 kg) match (geo = "FR") + 1 kg q_in from q(a = 1 kg) match (geo = "FR") } } """.trimIndent() @@ -579,17 +584,19 @@ class LoaderTest { body = EProcess( "p", inputs = listOf( - ETechnoExchange( - oneKg, - EProductSpec( - "in", - referenceUnit = null, - FromProcess( - "q", - matchLabels = MatchLabels(mapOf("geo" to EStringLiteral("FR"))), - arguments = mapOf("a" to oneKg), + ETechnoBlockEntry( + ETechnoExchange( + oneKg, + EProductSpec( + "q_in", + referenceUnit = null, + FromProcess( + "q", + matchLabels = MatchLabels(mapOf("geo" to EStringLiteral("FR"))), + arguments = mapOf("a" to oneKg), + ), ), - ), + ) ) ) ) @@ -622,15 +629,17 @@ class LoaderTest { body = EProcess( "p", biosphere = listOf( - EBioExchange( - oneKg, - ESubstanceSpec( - "co2", - "co2", - SubstanceType.EMISSION, - compartment = null, - subCompartment = null, - referenceUnit, + EBioBlockEntry( + EBioExchange( + oneKg, + ESubstanceSpec( + "co2", + "co2", + SubstanceType.EMISSION, + compartment = null, + subCompartment = null, + referenceUnit, + ) ) ) ) @@ -664,15 +673,17 @@ class LoaderTest { body = EProcess( "p", biosphere = listOf( - EBioExchange( - oneKg, - ESubstanceSpec( - "co2", - "co2", - SubstanceType.EMISSION, - "air", - "low pop", - referenceUnit, + EBioBlockEntry( + EBioExchange( + oneKg, + ESubstanceSpec( + "co2", + "co2", + SubstanceType.EMISSION, + "air", + "low pop", + referenceUnit, + ) ) ) ) @@ -706,15 +717,17 @@ class LoaderTest { body = EProcess( "p", biosphere = listOf( - EBioExchange( - oneKg, - ESubstanceSpec( - "co2", - "co2", - SubstanceType.RESOURCE, - compartment = null, - subCompartment = null, - referenceUnit, + EBioBlockEntry( + EBioExchange( + oneKg, + ESubstanceSpec( + "co2", + "co2", + SubstanceType.RESOURCE, + compartment = null, + subCompartment = null, + referenceUnit, + ) ) ) ) @@ -748,15 +761,17 @@ class LoaderTest { body = EProcess( "p", biosphere = listOf( - EBioExchange( - oneKg, - ESubstanceSpec( - "co2", - "co2", - SubstanceType.RESOURCE, - "air", - "low pop", - referenceUnit, + EBioBlockEntry( + EBioExchange( + oneKg, + ESubstanceSpec( + "co2", + "co2", + SubstanceType.RESOURCE, + "air", + "low pop", + referenceUnit, + ) ) ) ) @@ -790,15 +805,17 @@ class LoaderTest { body = EProcess( "p", biosphere = listOf( - EBioExchange( - oneKg, - ESubstanceSpec( - "co2", - "co2", - SubstanceType.LAND_USE, - compartment = null, - subCompartment = null, - referenceUnit, + EBioBlockEntry( + EBioExchange( + oneKg, + ESubstanceSpec( + "co2", + "co2", + SubstanceType.LAND_USE, + compartment = null, + subCompartment = null, + referenceUnit, + ) ) ) ) @@ -832,15 +849,17 @@ class LoaderTest { body = EProcess( "p", biosphere = listOf( - EBioExchange( - oneKg, - ESubstanceSpec( - "co2", - "co2", - SubstanceType.LAND_USE, - "air", - "low pop", - referenceUnit, + EBioBlockEntry( + EBioExchange( + oneKg, + ESubstanceSpec( + "co2", + "co2", + SubstanceType.LAND_USE, + "air", + "low pop", + referenceUnit, + ) ) ) ) @@ -872,10 +891,12 @@ class LoaderTest { body = EProcess( "p", impacts = listOf( - EImpact( - oneKg, - EIndicatorSpec( - "co2", + EImpactBlockEntry( + EImpact( + oneKg, + EIndicatorSpec( + "co2", + ) ) ) ) @@ -890,11 +911,11 @@ class LoaderTest { val file = lcaFile( """ variables { - sum = x + y - mul = x * y - div = x / y - scale = 2 x - pow = x^2.0 + op_sum = x + y + op_mul = x * y + op_div = x / y + op_scale = 2 x + op_pow = x^2.0 } """.trimIndent() ) @@ -909,11 +930,11 @@ class LoaderTest { val div = EQuantityDiv(EDataRef("x"), EDataRef("y")) val scale = EQuantityScale(BasicNumber(2.0), EDataRef("x")) val pow = EQuantityPow(EDataRef("x"), 2.0) - assertEquals(sum, actual.getData("sum")) - assertEquals(mul, actual.getData("mul")) - assertEquals(div, actual.getData("div")) - assertEquals(scale, actual.getData("scale")) - assertEquals(pow, actual.getData("pow")) + assertEquals(sum, actual.getData("op_sum")) + assertEquals(mul, actual.getData("op_mul")) + assertEquals(div, actual.getData("op_div")) + assertEquals(scale, actual.getData("op_scale")) + assertEquals(pow, actual.getData("op_pow")) } @Test From dc834714293b30a23b939bb48eefae9ce8d9aa59 Mon Sep 17 00:00:00 2001 From: Peva Blanchard Date: Sat, 20 Jan 2024 19:36:55 +0100 Subject: [PATCH 13/37] core: symbol table: getDataSource --- core/src/main/kotlin/ch/kleis/lcaac/core/lang/SymbolTable.kt | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/core/src/main/kotlin/ch/kleis/lcaac/core/lang/SymbolTable.kt b/core/src/main/kotlin/ch/kleis/lcaac/core/lang/SymbolTable.kt index 0115ddb7..c554006e 100644 --- a/core/src/main/kotlin/ch/kleis/lcaac/core/lang/SymbolTable.kt +++ b/core/src/main/kotlin/ch/kleis/lcaac/core/lang/SymbolTable.kt @@ -66,5 +66,9 @@ data class SymbolTable( return data[DataKey(name)] } + fun getDataSource(name: String): DataSourceExpression? { + return dataSources[DataSourceKey(name)] + } + } From 9377c16a23fbc5716264e5458b9fe8a6c1eda72c Mon Sep 17 00:00:00 2001 From: Peva Blanchard Date: Sat, 20 Jan 2024 19:37:08 +0100 Subject: [PATCH 14/37] grammar: loader: load datasource definition --- .../ch/kleis/lcaac/grammar/CoreMapper.kt | 22 ++++++++++ .../kotlin/ch/kleis/lcaac/grammar/Loader.kt | 13 ++++++ .../ch/kleis/lcaac/grammar/CoreMapperTest.kt | 44 +++++++++++++++++++ .../ch/kleis/lcaac/grammar/LcaLangFixture.kt | 7 +++ .../ch/kleis/lcaac/grammar/LoaderTest.kt | 31 +++++++++++++ 5 files changed, 117 insertions(+) create mode 100644 grammar/src/test/kotlin/ch/kleis/lcaac/grammar/CoreMapperTest.kt diff --git a/grammar/src/main/kotlin/ch/kleis/lcaac/grammar/CoreMapper.kt b/grammar/src/main/kotlin/ch/kleis/lcaac/grammar/CoreMapper.kt index 1e55f93c..a142dac7 100644 --- a/grammar/src/main/kotlin/ch/kleis/lcaac/grammar/CoreMapper.kt +++ b/grammar/src/main/kotlin/ch/kleis/lcaac/grammar/CoreMapper.kt @@ -329,6 +329,10 @@ class CoreMapper( return this.uid().ID().innerText() } + fun LcaLangParser.DataSourceRefContext.innerText(): String { + return this.uid().ID().innerText() + } + fun LcaLangParser.ProcessRefContext.innerText(): String { return this.uid().ID().innerText() } @@ -360,4 +364,22 @@ class CoreMapper( val subCompartment = this.subCompartmentField()?.STRING_LITERAL()?.innerText() return SubstanceKey(name, type, compartment, subCompartment) } + + fun dataSourceDefinition(ctx: LcaLangParser.DataSourceDefinitionContext): DataSourceExpression { + val name = ctx.dataSourceRef().uid().ID().innerText() + val locationField = ctx.locationField().firstOrNull() + ?: throw LoaderException("missing location field in datasource $name") + val location = locationField.STRING_LITERAL().innerText() + val schemaBlock = ctx.schema().firstOrNull() + ?: throw LoaderException("missing schema in datasource $name") + val schema = schemaBlock.columnDefinition().associate { column -> + val key = column.STRING_LITERAL().innerText() + val value = dataExpression(column.dataExpression()) + key to ColumnType(value) + } + return ECsvSource( + location, + schema, + ) + } } diff --git a/grammar/src/main/kotlin/ch/kleis/lcaac/grammar/Loader.kt b/grammar/src/main/kotlin/ch/kleis/lcaac/grammar/Loader.kt index a02a5d6b..4dba1eb7 100644 --- a/grammar/src/main/kotlin/ch/kleis/lcaac/grammar/Loader.kt +++ b/grammar/src/main/kotlin/ch/kleis/lcaac/grammar/Loader.kt @@ -23,6 +23,7 @@ class Loader( val unitDefinitions = files.flatMap { it.unitDefinition() } val processDefinitions = files.flatMap { it.processDefinition() } val substanceDefinitions = files.flatMap { it.substanceDefinition() } + val dataSourceDefinitions = files.flatMap { it.dataSourceDefinition() } val globalDefinitions = files.flatMap { it.globalVariables() } .flatMap { it.globalAssignment() } @@ -89,9 +90,21 @@ class Loader( } catch (e: RegisterException) { throw LoaderException("Duplicate process ${e.duplicates} defined") } + + val dataSources = try { + DataSourceRegister() + .plus( + dataSourceDefinitions + .map { DataSourceKey(it.dataSourceRef().innerText()) to dataSourceDefinition(it) } + .asIterable() + ) + } catch (e: RegisterException) { + throw LoaderException("Duplicate data sources ${e.duplicates} defined") + } return SymbolTable( data = globals, processTemplates = processTemplates, + dataSources = dataSources, dimensions = dimensions, substanceCharacterizations = substanceCharacterizations, ) diff --git a/grammar/src/test/kotlin/ch/kleis/lcaac/grammar/CoreMapperTest.kt b/grammar/src/test/kotlin/ch/kleis/lcaac/grammar/CoreMapperTest.kt new file mode 100644 index 00000000..33f4722f --- /dev/null +++ b/grammar/src/test/kotlin/ch/kleis/lcaac/grammar/CoreMapperTest.kt @@ -0,0 +1,44 @@ +package ch.kleis.lcaac.grammar + +import ch.kleis.lcaac.core.lang.expression.* +import ch.kleis.lcaac.core.math.basic.BasicNumber +import ch.kleis.lcaac.core.math.basic.BasicOperations +import kotlin.test.Test +import kotlin.test.assertEquals + +/* + TODO: Boyscout rule: Add mapper tests. + */ + +class CoreMapperTest { + private val ops = BasicOperations + + @Test + fun datasource() { + // given + val content = """ + datasource source { + location = "file.csv" + schema { + "mass" = 1 kg + "geo" = "FR" + } + } + """.trimIndent() + val ctx = LcaLangFixture.datasource(content) + val mapper = CoreMapper(ops) + + // when + val actual = mapper.dataSourceDefinition(ctx) + + // then + val expected = ECsvSource( + location = "file.csv", + schema = mapOf( + "mass" to ColumnType(EQuantityScale(BasicNumber(1.0), EDataRef("kg"))), + "geo" to ColumnType(EStringLiteral("FR")), + ) + ) + assertEquals(expected, actual) + } +} diff --git a/grammar/src/test/kotlin/ch/kleis/lcaac/grammar/LcaLangFixture.kt b/grammar/src/test/kotlin/ch/kleis/lcaac/grammar/LcaLangFixture.kt index bd8441fe..231a0a36 100644 --- a/grammar/src/test/kotlin/ch/kleis/lcaac/grammar/LcaLangFixture.kt +++ b/grammar/src/test/kotlin/ch/kleis/lcaac/grammar/LcaLangFixture.kt @@ -20,5 +20,12 @@ class LcaLangFixture { val parser = LcaLangParser(tokens) return parser.testDefinition() } + + fun datasource(content: String): LcaLangParser.DataSourceDefinitionContext { + val lexer = LcaLangLexer(CharStreams.fromString(content)) + val tokens = CommonTokenStream(lexer) + val parser = LcaLangParser(tokens) + return parser.dataSourceDefinition() + } } } diff --git a/grammar/src/test/kotlin/ch/kleis/lcaac/grammar/LoaderTest.kt b/grammar/src/test/kotlin/ch/kleis/lcaac/grammar/LoaderTest.kt index 94303ad7..f1f0aa7b 100644 --- a/grammar/src/test/kotlin/ch/kleis/lcaac/grammar/LoaderTest.kt +++ b/grammar/src/test/kotlin/ch/kleis/lcaac/grammar/LoaderTest.kt @@ -14,6 +14,37 @@ import kotlin.test.assertEquals import kotlin.test.assertNotNull class LoaderTest { + @Test + fun load_datasource() { + // given + val file = lcaFile( + """ + datasource source { + location = "file.csv" + schema { + "mass" = 1 kg + "geo" = "FR" + } + } + """.trimIndent() + ) + val loader = Loader(BasicOperations) + + // when + val actual = loader.load(sequenceOf(file)) + .getDataSource("source")!! + + // then + val expected = ECsvSource( + location = "file.csv", + schema = mapOf( + "mass" to ColumnType(EQuantityScale(BasicNumber(1.0), EDataRef("kg"))), + "geo" to ColumnType(EStringLiteral("FR")), + ) + ) + assertEquals(expected, actual) + } + @Test fun load_whenFileContainsTest_thenNoError() { val file = lcaFile( From 4a0507276cf6db43ad608eb5d52e4844ee6d3166 Mon Sep 17 00:00:00 2001 From: Peva Blanchard Date: Sat, 20 Jan 2024 20:01:12 +0100 Subject: [PATCH 15/37] grammar: map block for_each --- .../ch/kleis/lcaac/grammar/CoreMapper.kt | 23 ++++- .../ch/kleis/lcaac/grammar/CoreMapperTest.kt | 98 ++++++++++++++++++- .../ch/kleis/lcaac/grammar/LcaLangFixture.kt | 7 +- .../ch/kleis/lcaac/grammar/LoaderTest.kt | 2 +- 4 files changed, 120 insertions(+), 10 deletions(-) diff --git a/grammar/src/main/kotlin/ch/kleis/lcaac/grammar/CoreMapper.kt b/grammar/src/main/kotlin/ch/kleis/lcaac/grammar/CoreMapper.kt index a142dac7..bb846f1c 100644 --- a/grammar/src/main/kotlin/ch/kleis/lcaac/grammar/CoreMapper.kt +++ b/grammar/src/main/kotlin/ch/kleis/lcaac/grammar/CoreMapper.kt @@ -80,7 +80,13 @@ class CoreMapper( ) ) - is LcaLangParser.ImpactBlockForEachContext -> TODO() + is LcaLangParser.ImpactBlockForEachContext -> { + val rowRef = ctx.dataRef().innerText() + val dataSourceRef = ctx.dataSourceRef().innerText() + val body = ctx.impactExchange().map { this.impactExchange(it) } + EImpactBlockForEach(rowRef, dataSourceRef, body) + } + else -> throw IllegalStateException("parsing error: expecting an impact exchange context") } } @@ -107,7 +113,12 @@ class CoreMapper( ) } - is LcaLangParser.BioBlockForEachContext -> TODO() + is LcaLangParser.BioBlockForEachContext -> { + val rowRef = ctx.dataRef().innerText() + val dataSourceRef = ctx.dataSourceRef().innerText() + val body = ctx.bioExchange().map { this.bioExchange(it, symbolTable, type) } + EBioBlockForEach(rowRef, dataSourceRef, body) + } else -> throw IllegalStateException("parsing error: expecting a bio exchange context") } } @@ -154,7 +165,13 @@ class CoreMapper( ) ) - is LcaLangParser.TechnoBlockForEachContext -> TODO() + is LcaLangParser.TechnoBlockForEachContext -> { + val rowRef = ctx.dataRef().innerText() + val dataSourceRef = ctx.dataSourceRef().innerText() + val body = ctx.technoInputExchange().map { this.technoInputExchange(it) } + ETechnoBlockForEach(rowRef, dataSourceRef, body) + } + else -> throw IllegalStateException("parsing error: expecting a techno input exchange context") } } diff --git a/grammar/src/test/kotlin/ch/kleis/lcaac/grammar/CoreMapperTest.kt b/grammar/src/test/kotlin/ch/kleis/lcaac/grammar/CoreMapperTest.kt index 33f4722f..38b56c44 100644 --- a/grammar/src/test/kotlin/ch/kleis/lcaac/grammar/CoreMapperTest.kt +++ b/grammar/src/test/kotlin/ch/kleis/lcaac/grammar/CoreMapperTest.kt @@ -1,8 +1,11 @@ package ch.kleis.lcaac.grammar +import ch.kleis.lcaac.core.lang.SymbolTable import ch.kleis.lcaac.core.lang.expression.* import ch.kleis.lcaac.core.math.basic.BasicNumber import ch.kleis.lcaac.core.math.basic.BasicOperations +import io.mockk.mockk +import io.mockk.mockkObject import kotlin.test.Test import kotlin.test.assertEquals @@ -16,7 +19,7 @@ class CoreMapperTest { @Test fun datasource() { // given - val content = """ + val ctx = LcaLangFixture.parser(""" datasource source { location = "file.csv" schema { @@ -24,8 +27,7 @@ class CoreMapperTest { "geo" = "FR" } } - """.trimIndent() - val ctx = LcaLangFixture.datasource(content) + """.trimIndent()).dataSourceDefinition() val mapper = CoreMapper(ops) // when @@ -41,4 +43,94 @@ class CoreMapperTest { ) assertEquals(expected, actual) } + + @Test + fun technoInputExchange_blockForEach() { + // given + val ctx = LcaLangFixture.parser(""" + for_each row in source { + 1 kg co2 + } + """.trimIndent()).technoInputExchange() + val mapper = CoreMapper(ops) + + // when + val actual = mapper.technoInputExchange(ctx) + + // then + val expected = ETechnoBlockForEach( + "row", + "source", + listOf( + ETechnoBlockEntry( + ETechnoExchange( + EQuantityScale(BasicNumber(1.0), EDataRef("kg")), + EProductSpec("co2"), + ) + ) + ) + ) + assertEquals(expected, actual) + } + + @Test + fun impactExchange_blockForEach() { + // given + val ctx = LcaLangFixture.parser(""" + for_each row in source { + 1 kg co2 + } + """.trimIndent()).impactExchange() + val mapper = CoreMapper(ops) + + // when + val actual = mapper.impactExchange(ctx) + + // then + val expected = EImpactBlockForEach( + "row", + "source", + listOf( + EImpactBlockEntry( + EImpact( + EQuantityScale(BasicNumber(1.0), EDataRef("kg")), + EIndicatorSpec("co2"), + ) + ) + ) + ) + assertEquals(expected, actual) + } + + @Test + fun bioExchange_blockForEach() { + // given + val ctx = LcaLangFixture.parser(""" + for_each row in source { + 1 kg co2(compartment="air") + } + """.trimIndent()).bioExchange() + val symbolTable = mockk>() + val substanceType = SubstanceType.EMISSION + val mapper = CoreMapper(ops) + + // when + val actual = mapper.bioExchange(ctx, symbolTable, substanceType) + + // then + val referenceUnit = EUnitOf(EQuantityClosure(symbolTable, EQuantityScale(BasicNumber(1.0), EDataRef("kg")))) + val expected = EBioBlockForEach( + "row", + "source", + listOf( + EBioBlockEntry( + EBioExchange( + EQuantityScale(BasicNumber(1.0), EDataRef("kg")), + ESubstanceSpec("co2", compartment = "air", type=substanceType, referenceUnit = referenceUnit), + ) + ) + ) + ) + assertEquals(expected, actual) + } } diff --git a/grammar/src/test/kotlin/ch/kleis/lcaac/grammar/LcaLangFixture.kt b/grammar/src/test/kotlin/ch/kleis/lcaac/grammar/LcaLangFixture.kt index 231a0a36..756567a2 100644 --- a/grammar/src/test/kotlin/ch/kleis/lcaac/grammar/LcaLangFixture.kt +++ b/grammar/src/test/kotlin/ch/kleis/lcaac/grammar/LcaLangFixture.kt @@ -7,6 +7,7 @@ import org.antlr.v4.runtime.CommonTokenStream class LcaLangFixture { companion object { + @Deprecated("use parser instead") fun lcaFile(content: String): LcaLangParser.LcaFileContext { val lexer = LcaLangLexer(CharStreams.fromString(content)) val tokens = CommonTokenStream(lexer) @@ -14,6 +15,7 @@ class LcaLangFixture { return parser.lcaFile() } + @Deprecated("use parser instead") fun test(content: String): LcaLangParser.TestDefinitionContext { val lexer = LcaLangLexer(CharStreams.fromString(content)) val tokens = CommonTokenStream(lexer) @@ -21,11 +23,10 @@ class LcaLangFixture { return parser.testDefinition() } - fun datasource(content: String): LcaLangParser.DataSourceDefinitionContext { + fun parser(content: String): LcaLangParser { val lexer = LcaLangLexer(CharStreams.fromString(content)) val tokens = CommonTokenStream(lexer) - val parser = LcaLangParser(tokens) - return parser.dataSourceDefinition() + return LcaLangParser(tokens) } } } diff --git a/grammar/src/test/kotlin/ch/kleis/lcaac/grammar/LoaderTest.kt b/grammar/src/test/kotlin/ch/kleis/lcaac/grammar/LoaderTest.kt index f1f0aa7b..0aa1e894 100644 --- a/grammar/src/test/kotlin/ch/kleis/lcaac/grammar/LoaderTest.kt +++ b/grammar/src/test/kotlin/ch/kleis/lcaac/grammar/LoaderTest.kt @@ -44,7 +44,7 @@ class LoaderTest { ) assertEquals(expected, actual) } - + @Test fun load_whenFileContainsTest_thenNoError() { val file = lcaFile( From fdd7f07bd91e31d0f0be8ec5f5847c8b9583f7df Mon Sep 17 00:00:00 2001 From: Peva Blanchard Date: Sat, 20 Jan 2024 20:13:20 +0100 Subject: [PATCH 16/37] grammar: parse record expression, column operation expression --- .../ch/kleis/lcaac/grammar/CoreMapper.kt | 19 +++++++++++ .../ch/kleis/lcaac/grammar/CoreMapperTest.kt | 32 +++++++++++++++++++ 2 files changed, 51 insertions(+) diff --git a/grammar/src/main/kotlin/ch/kleis/lcaac/grammar/CoreMapper.kt b/grammar/src/main/kotlin/ch/kleis/lcaac/grammar/CoreMapper.kt index bb846f1c..d8db082a 100644 --- a/grammar/src/main/kotlin/ch/kleis/lcaac/grammar/CoreMapper.kt +++ b/grammar/src/main/kotlin/ch/kleis/lcaac/grammar/CoreMapper.kt @@ -269,6 +269,17 @@ class CoreMapper( fun dataExpression(ctx: LcaLangParser.DataExpressionContext): DataExpression { return when (ctx) { + is LcaLangParser.ColGroupContext -> { + when (ctx.op.text) { + ctx.SUM().innerText() -> { + val sourceRef = ctx.dataSourceRef().innerText() + val columnRef = ctx.columnRef().innerText() + ESum(sourceRef, columnRef) + } + else -> throw IllegalStateException("parsing error: invalid column operation '${ctx.op.text}'") + } + } + is LcaLangParser.AddGroupContext -> { val left = dataExpression(ctx.left) val right = dataExpression(ctx.right) @@ -321,6 +332,10 @@ class CoreMapper( dataExpression(it.dataExpression()) } ?: ctx.stringExpression()?.let { EStringLiteral(it.STRING_LITERAL().innerText()) + } ?: ctx.slice()?.let { + val recordRef = ctx.dataRef().innerText() + val columnRef = ctx.slice().columnRef().innerText() + ERecordEntry(EDataRef(recordRef), columnRef) } ?: ctx.dataRef()?.let { EDataRef(it.innerText()) } ?: throw IllegalStateException() @@ -346,6 +361,10 @@ class CoreMapper( return this.uid().ID().innerText() } + fun LcaLangParser.ColumnRefContext.innerText(): String { + return this.STRING_LITERAL().innerText() + } + fun LcaLangParser.DataSourceRefContext.innerText(): String { return this.uid().ID().innerText() } diff --git a/grammar/src/test/kotlin/ch/kleis/lcaac/grammar/CoreMapperTest.kt b/grammar/src/test/kotlin/ch/kleis/lcaac/grammar/CoreMapperTest.kt index 38b56c44..69ffc1ea 100644 --- a/grammar/src/test/kotlin/ch/kleis/lcaac/grammar/CoreMapperTest.kt +++ b/grammar/src/test/kotlin/ch/kleis/lcaac/grammar/CoreMapperTest.kt @@ -16,6 +16,38 @@ import kotlin.test.assertEquals class CoreMapperTest { private val ops = BasicOperations + @Test + fun recordEntry() { + // given + val ctx = LcaLangFixture.parser(""" + row["mass"] + """.trimIndent()).dataExpression() + val mapper = CoreMapper(ops) + + // when + val actual = mapper.dataExpression(ctx) + + // then + val expected = ERecordEntry(EDataRef("row"), "mass") + assertEquals(expected, actual) + } + + @Test + fun columnOperation_sum() { + // given + val ctx = LcaLangFixture.parser(""" + sum(source, "mass") + """.trimIndent()).dataExpression() + val mapper = CoreMapper(ops) + + // when + val actual = mapper.dataExpression(ctx) + + // then + val expected = ESum("source", "mass") + assertEquals(expected, actual) + } + @Test fun datasource() { // given From ffcf2c40aca85d548e76e12e16d6a5a66d569a02 Mon Sep 17 00:00:00 2001 From: Peva Blanchard Date: Sat, 20 Jan 2024 22:08:35 +0100 Subject: [PATCH 17/37] cli: basic csv source operations --- .../ch/kleis/lcaac/cli/cmd/AssessCommand.kt | 2 +- .../ch/kleis/lcaac/cli/cmd/TestCommand.kt | 3 +- .../lcaac/cli/csv/BasicCsvSourceOperations.kt | 90 +++++++++++++++++++ .../ch/kleis/lcaac/cli/csv/CsvProcessor.kt | 49 +++++----- .../kleis/lcaac/cli/csv/CsvProcessorTest.kt | 6 +- 5 files changed, 123 insertions(+), 27 deletions(-) create mode 100644 cli/src/main/kotlin/ch/kleis/lcaac/cli/csv/BasicCsvSourceOperations.kt diff --git a/cli/src/main/kotlin/ch/kleis/lcaac/cli/cmd/AssessCommand.kt b/cli/src/main/kotlin/ch/kleis/lcaac/cli/cmd/AssessCommand.kt index 1e598a8c..920036e4 100644 --- a/cli/src/main/kotlin/ch/kleis/lcaac/cli/cmd/AssessCommand.kt +++ b/cli/src/main/kotlin/ch/kleis/lcaac/cli/cmd/AssessCommand.kt @@ -44,7 +44,7 @@ class AssessCommand : CliktCommand(name = "assess", help = "Returns the unitary override fun run() { val files = lcaFiles(path) val symbolTable = Loader(BasicOperations).load(files, listOf(LoaderOption.WITH_PRELUDE)) - val processor = CsvProcessor(symbolTable) + val processor = CsvProcessor(path, symbolTable) val iterator = loadRequests() val writer = CsvResultWriter() var first = true diff --git a/cli/src/main/kotlin/ch/kleis/lcaac/cli/cmd/TestCommand.kt b/cli/src/main/kotlin/ch/kleis/lcaac/cli/cmd/TestCommand.kt index 7d9cfb6c..ccb38523 100644 --- a/cli/src/main/kotlin/ch/kleis/lcaac/cli/cmd/TestCommand.kt +++ b/cli/src/main/kotlin/ch/kleis/lcaac/cli/cmd/TestCommand.kt @@ -1,5 +1,6 @@ package ch.kleis.lcaac.cli.cmd +import ch.kleis.lcaac.cli.csv.BasicCsvSourceOperations import ch.kleis.lcaac.core.math.basic.BasicOperations import ch.kleis.lcaac.core.testing.BasicTestRunner import ch.kleis.lcaac.core.testing.GenericFailure @@ -37,7 +38,7 @@ class TestCommand : CliktCommand(name = "test", help = "Run specified tests") { val cases = files .flatMap { it.testDefinition() } .map { mapper.test(it) } - val runner = BasicTestRunner(symbolTable) + val runner = BasicTestRunner(symbolTable, BasicCsvSourceOperations(path)) val results = cases.map { runner.run(it) } results.forEach { result -> diff --git a/cli/src/main/kotlin/ch/kleis/lcaac/cli/csv/BasicCsvSourceOperations.kt b/cli/src/main/kotlin/ch/kleis/lcaac/cli/csv/BasicCsvSourceOperations.kt new file mode 100644 index 00000000..73523a7a --- /dev/null +++ b/cli/src/main/kotlin/ch/kleis/lcaac/cli/csv/BasicCsvSourceOperations.kt @@ -0,0 +1,90 @@ +package ch.kleis.lcaac.cli.csv + +import ch.kleis.lcaac.cli.cmd.parseQuantityWithDefaultUnit +import ch.kleis.lcaac.core.datasource.DataSourceOperations +import ch.kleis.lcaac.core.lang.evaluator.reducer.DataExpressionReducer +import ch.kleis.lcaac.core.lang.expression.* +import ch.kleis.lcaac.core.lang.register.DataSourceRegister +import ch.kleis.lcaac.core.math.basic.BasicNumber +import ch.kleis.lcaac.core.math.basic.BasicOperations +import ch.kleis.lcaac.core.prelude.Prelude +import org.apache.commons.csv.CSVFormat +import org.apache.commons.csv.CSVParser +import java.io.File +import java.nio.file.Paths + +class BasicCsvSourceOperations( + private val path: File, +) : DataSourceOperations { + private val format = CSVFormat.DEFAULT.builder() + .setHeader() + .setSkipHeaderRecord(true) + .build() + + override fun readAll(source: DataSourceExpression): Sequence> { + return when (source) { + is ECsvSource -> { + val location = Paths.get(path.absolutePath, source.location) + val inputStream = location.toFile().inputStream() + val parser = CSVParser(inputStream.reader(), format) + val header = parser.headerMap + parser.iterator().asSequence() + .map { record -> + val entries = header + .filter { entry -> source.schema.containsKey(entry.key) } + .mapValues { entry -> + val columnType = source.schema[entry.key]!! + val columnDefaultValue = columnType.defaultValue + val position = entry.value + val element = record[position] + when (columnDefaultValue) { + is QuantityExpression<*> -> + parseQuantityWithDefaultUnit(element, EUnitOf(columnDefaultValue)) + + is StringExpression -> + EStringLiteral(element) + + else -> throw IllegalStateException( + "invalid schema: column '${entry.key}' has an invalid default value" + ) + } + } + ERecord(entries) + } + } + } + } + + override fun sum(source: DataSourceExpression, column: String): DataExpression { + val reducer = DataExpressionReducer( + dataRegister = Prelude.units(), + dataSourceRegister = DataSourceRegister.empty(), + ops = BasicOperations, + sourceOps = this, + ) + return when (source) { + is ECsvSource -> { + val location = Paths.get(path.absolutePath, source.location) + val inputStream = location.toFile().inputStream() + val parser = CSVParser(inputStream.reader(), format) + val header = parser.headerMap + val position = header[column] + ?: throw IllegalStateException( + "${source.location}: invalid schema: unknown column '$column'" + ) + val columnType = source.schema[column] + ?: throw IllegalStateException( + "invalid schema: column '$column' has an invalid default value" + ) + val defaultValue = columnType.defaultValue + parser.iterator().asSequence() + .map { record -> + val element = record[position] + parseQuantityWithDefaultUnit(element, defaultValue) + }.reduce { acc, expression -> + reducer.reduce(EQuantityAdd(acc, expression)) + } + } + } + } +} diff --git a/cli/src/main/kotlin/ch/kleis/lcaac/cli/csv/CsvProcessor.kt b/cli/src/main/kotlin/ch/kleis/lcaac/cli/csv/CsvProcessor.kt index 7cf631b9..d0e553f9 100644 --- a/cli/src/main/kotlin/ch/kleis/lcaac/cli/csv/CsvProcessor.kt +++ b/cli/src/main/kotlin/ch/kleis/lcaac/cli/csv/CsvProcessor.kt @@ -11,34 +11,37 @@ import ch.kleis.lcaac.core.lang.expression.QuantityExpression import ch.kleis.lcaac.core.lang.expression.StringExpression import ch.kleis.lcaac.core.math.basic.BasicNumber import ch.kleis.lcaac.core.math.basic.BasicOperations +import java.io.File class CsvProcessor( - private val symbolTable: SymbolTable, + path: File, + private val symbolTable: SymbolTable, ) { private val ops = BasicOperations - private val evaluator = Evaluator(symbolTable, ops) + private val sourceOps = BasicCsvSourceOperations(path) + private val evaluator = Evaluator(symbolTable, ops, sourceOps) fun process(request: CsvRequest): List { val reqName = request.processName val reqLabels = request.matchLabels val template = - symbolTable.getTemplate(reqName, reqLabels) - ?: throw EvaluatorException("Could not get template for ${reqName}${reqLabels}") + symbolTable.getTemplate(reqName, reqLabels) + ?: throw EvaluatorException("Could not get template for ${reqName}${reqLabels}") val arguments = template.params - .mapValues { entry -> - when (val v = entry.value) { - is QuantityExpression<*> -> request[entry.key]?.let { - parseQuantityWithDefaultUnit(it, EUnitOf(v)) - } ?: entry.value + .mapValues { entry -> + when (val v = entry.value) { + is QuantityExpression<*> -> request[entry.key]?.let { + parseQuantityWithDefaultUnit(it, EUnitOf(v)) + } ?: entry.value - is StringExpression -> request[entry.key]?.let { - EStringLiteral(it) - } ?: entry.value + is StringExpression -> request[entry.key]?.let { + EStringLiteral(it) + } ?: entry.value - else -> throw EvaluatorException("$v is not a supported data expression") - } + else -> throw EvaluatorException("$v is not a supported data expression") } + } val trace = evaluator.trace(template, arguments) val systemValue = trace.getSystemValue() @@ -46,14 +49,14 @@ class CsvProcessor( val program = ContributionAnalysisProgram(systemValue, entryPoint) val analysis = program.run() return entryPoint.products - .map { output -> - val outputPort = output.product - val impacts = analysis.getUnitaryImpacts(outputPort) - CsvResult( - request, - outputPort, - impacts, - ) - } + .map { output -> + val outputPort = output.product + val impacts = analysis.getUnitaryImpacts(outputPort) + CsvResult( + request, + outputPort, + impacts, + ) + } } } diff --git a/cli/src/test/kotlin/ch/kleis/lcaac/cli/csv/CsvProcessorTest.kt b/cli/src/test/kotlin/ch/kleis/lcaac/cli/csv/CsvProcessorTest.kt index 4bdbca0a..3753d840 100644 --- a/cli/src/test/kotlin/ch/kleis/lcaac/cli/csv/CsvProcessorTest.kt +++ b/cli/src/test/kotlin/ch/kleis/lcaac/cli/csv/CsvProcessorTest.kt @@ -6,15 +6,16 @@ import ch.kleis.lcaac.core.lang.dimension.UnitSymbol import ch.kleis.lcaac.core.lang.value.* import ch.kleis.lcaac.core.math.basic.BasicNumber import ch.kleis.lcaac.core.math.basic.BasicOperations -import ch.kleis.lcaac.core.prelude.Prelude import ch.kleis.lcaac.grammar.Loader import ch.kleis.lcaac.grammar.LoaderOption import ch.kleis.lcaac.grammar.parser.LcaLangLexer import ch.kleis.lcaac.grammar.parser.LcaLangParser +import io.mockk.mockk import org.antlr.v4.runtime.CharStreams import org.antlr.v4.runtime.CommonTokenStream import org.junit.jupiter.api.Assertions.assertEquals import org.junit.jupiter.api.Test +import java.io.File class CsvProcessorTest { @@ -34,7 +35,8 @@ class CsvProcessorTest { } """.trimIndent() val symbolTable = load(content) - val processor = CsvProcessor(symbolTable) + val path = mockk() + val processor = CsvProcessor(path, symbolTable) val request = CsvRequest( "main", emptyMap(), From 783130b46cb6321bd17ddb02b512d9964acd83dd Mon Sep 17 00:00:00 2001 From: Peva Blanchard Date: Sat, 20 Jan 2024 22:43:25 +0100 Subject: [PATCH 18/37] core: include datasources register when creating template reducer --- .../lcaac/core/lang/evaluator/step/Reduce.kt | 1 + .../core/lang/evaluator/step/ReduceTest.kt | 57 ++++++++++++++++++- 2 files changed, 56 insertions(+), 2 deletions(-) diff --git a/core/src/main/kotlin/ch/kleis/lcaac/core/lang/evaluator/step/Reduce.kt b/core/src/main/kotlin/ch/kleis/lcaac/core/lang/evaluator/step/Reduce.kt index 9c94a4d6..98336186 100644 --- a/core/src/main/kotlin/ch/kleis/lcaac/core/lang/evaluator/step/Reduce.kt +++ b/core/src/main/kotlin/ch/kleis/lcaac/core/lang/evaluator/step/Reduce.kt @@ -26,6 +26,7 @@ class Reduce( ops, sourceOps, symbolTable.data, + symbolTable.dataSources, ) fun apply(expression: EProcessTemplateApplication): EProcess { diff --git a/core/src/test/kotlin/ch/kleis/lcaac/core/lang/evaluator/step/ReduceTest.kt b/core/src/test/kotlin/ch/kleis/lcaac/core/lang/evaluator/step/ReduceTest.kt index 7c20ea7c..58bbaf1e 100644 --- a/core/src/test/kotlin/ch/kleis/lcaac/core/lang/evaluator/step/ReduceTest.kt +++ b/core/src/test/kotlin/ch/kleis/lcaac/core/lang/evaluator/step/ReduceTest.kt @@ -4,17 +4,19 @@ import ch.kleis.lcaac.core.datasource.DataSourceOperations import ch.kleis.lcaac.core.lang.SymbolTable import ch.kleis.lcaac.core.lang.evaluator.EvaluatorException import ch.kleis.lcaac.core.lang.evaluator.ToValue -import ch.kleis.lcaac.core.lang.expression.EProcessTemplateApplication -import ch.kleis.lcaac.core.lang.expression.EQuantityAdd +import ch.kleis.lcaac.core.lang.expression.* import ch.kleis.lcaac.core.lang.fixture.ProductValueFixture import ch.kleis.lcaac.core.lang.fixture.QuantityFixture import ch.kleis.lcaac.core.lang.fixture.QuantityValueFixture import ch.kleis.lcaac.core.lang.fixture.TemplateFixture +import ch.kleis.lcaac.core.lang.register.DataSourceKey +import ch.kleis.lcaac.core.lang.register.DataSourceRegister import ch.kleis.lcaac.core.lang.value.FromProcessRefValue import ch.kleis.lcaac.core.lang.value.ProcessValue import ch.kleis.lcaac.core.lang.value.TechnoExchangeValue import ch.kleis.lcaac.core.math.basic.BasicNumber import ch.kleis.lcaac.core.math.basic.BasicOperations +import io.mockk.every import io.mockk.mockk import org.junit.jupiter.api.Test import kotlin.test.assertEquals @@ -25,6 +27,57 @@ class ReduceTest { private val ops = BasicOperations private val sourceOps = mockk>() + @Test + fun eval_withDataSource() { + // given + val template = TemplateFixture.carrotProduction + val instance = EProcessTemplateApplication( + template, + mapOf( + "q_water" to ESum("source", "mass") + ) + ) + val dataSource = ECsvSource( + location = "foo.csv", + schema = mapOf( + "mass" to ColumnType(QuantityFixture.oneLitre) + ) + ) + every { sourceOps.sum(dataSource, "mass") } returns QuantityFixture.twoLitres + val symbolTable = SymbolTable( + dataSources = DataSourceRegister.from(mapOf( + DataSourceKey("source") to dataSource, + )) + ) + val reduceAndComplete = Reduce(symbolTable, ops, sourceOps) + + // when + val actual = with(ToValue(BasicOperations)) { reduceAndComplete.apply(instance).toValue() } + + // then + val expected = ProcessValue( + name = "carrot_production", + products = listOf( + TechnoExchangeValue( + QuantityValueFixture.oneKilogram, + ProductValueFixture.carrot.withFromProcessRef( + FromProcessRefValue( + name = "carrot_production", + arguments = mapOf("q_water" to QuantityValueFixture.twoLitres), + ) + ), + ) + ), + inputs = listOf( + TechnoExchangeValue( + QuantityValueFixture.twoLitres, + ProductValueFixture.water, + ) + ), + ) + assertEquals(expected, actual) + } + @Test fun eval_whenInstanceOfProcessTemplate_shouldEvaluateToProcessValue() { // given From df8249adb322e1f9219236502657fbee31b08182 Mon Sep 17 00:00:00 2001 From: Peva Blanchard Date: Sat, 20 Jan 2024 22:50:21 +0100 Subject: [PATCH 19/37] grammar: modify syntax for column op --- grammar/src/main/antlr/LcaLang.g4 | 2 +- .../src/test/kotlin/ch/kleis/lcaac/grammar/CoreMapperTest.kt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/grammar/src/main/antlr/LcaLang.g4 b/grammar/src/main/antlr/LcaLang.g4 index f64136db..5c3d2230 100644 --- a/grammar/src/main/antlr/LcaLang.g4 +++ b/grammar/src/main/antlr/LcaLang.g4 @@ -226,7 +226,7 @@ dataExpression | parenExpression # baseGroup | stringExpression # baseGroup | dataRef slice? # baseGroup - | op=SUM LPAREN dataSourceRef COMMA columnRef COMMA? RPAREN # colGroup + | op=SUM LPAREN dataSourceRef LBRACK columnRef RBRACK RPAREN # colGroup ; slice : LBRACK columnRef RBRACK diff --git a/grammar/src/test/kotlin/ch/kleis/lcaac/grammar/CoreMapperTest.kt b/grammar/src/test/kotlin/ch/kleis/lcaac/grammar/CoreMapperTest.kt index 69ffc1ea..a0d6cd12 100644 --- a/grammar/src/test/kotlin/ch/kleis/lcaac/grammar/CoreMapperTest.kt +++ b/grammar/src/test/kotlin/ch/kleis/lcaac/grammar/CoreMapperTest.kt @@ -36,7 +36,7 @@ class CoreMapperTest { fun columnOperation_sum() { // given val ctx = LcaLangFixture.parser(""" - sum(source, "mass") + sum(source["mass"]) """.trimIndent()).dataExpression() val mapper = CoreMapper(ops) From e0a874ca4f92216d92432c8ba30cc5e32603c888 Mon Sep 17 00:00:00 2001 From: Peva Blanchard Date: Sat, 20 Jan 2024 22:57:51 +0100 Subject: [PATCH 20/37] cli: csv source ops: fix default unit --- .../kotlin/ch/kleis/lcaac/cli/csv/BasicCsvSourceOperations.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cli/src/main/kotlin/ch/kleis/lcaac/cli/csv/BasicCsvSourceOperations.kt b/cli/src/main/kotlin/ch/kleis/lcaac/cli/csv/BasicCsvSourceOperations.kt index 73523a7a..61a1c062 100644 --- a/cli/src/main/kotlin/ch/kleis/lcaac/cli/csv/BasicCsvSourceOperations.kt +++ b/cli/src/main/kotlin/ch/kleis/lcaac/cli/csv/BasicCsvSourceOperations.kt @@ -80,7 +80,7 @@ class BasicCsvSourceOperations( parser.iterator().asSequence() .map { record -> val element = record[position] - parseQuantityWithDefaultUnit(element, defaultValue) + parseQuantityWithDefaultUnit(element, EUnitOf(defaultValue)) }.reduce { acc, expression -> reducer.reduce(EQuantityAdd(acc, expression)) } From 56f4103f3208f67cea14f9178f1af329ab075be8 Mon Sep 17 00:00:00 2001 From: Peva Blanchard Date: Sat, 20 Jan 2024 23:14:32 +0100 Subject: [PATCH 21/37] grammar: loader: include datasources when building closures --- .../ch/kleis/lcaac/grammar/CoreMapper.kt | 2 + .../kotlin/ch/kleis/lcaac/grammar/Loader.kt | 22 ++++----- .../ch/kleis/lcaac/grammar/LoaderTest.kt | 49 +++++++++++++++++-- 3 files changed, 58 insertions(+), 15 deletions(-) diff --git a/grammar/src/main/kotlin/ch/kleis/lcaac/grammar/CoreMapper.kt b/grammar/src/main/kotlin/ch/kleis/lcaac/grammar/CoreMapper.kt index d8db082a..76737c21 100644 --- a/grammar/src/main/kotlin/ch/kleis/lcaac/grammar/CoreMapper.kt +++ b/grammar/src/main/kotlin/ch/kleis/lcaac/grammar/CoreMapper.kt @@ -19,6 +19,7 @@ class CoreMapper( fun process( ctx: LcaLangParser.ProcessDefinitionContext, globals: DataRegister = DataRegister.empty(), + dataSources: Register>, ): EProcessTemplate { val name = ctx.name.innerText() val labels = ctx.labels() @@ -36,6 +37,7 @@ class CoreMapper( } catch (e: RegisterException) { throw EvaluatorException("Conflict between local variable(s) ${e.duplicates} and a global definition.") }, + dataSources = dataSources, ) val products = ctx.block_products() .flatMap { it.technoProductExchange() } diff --git a/grammar/src/main/kotlin/ch/kleis/lcaac/grammar/Loader.kt b/grammar/src/main/kotlin/ch/kleis/lcaac/grammar/Loader.kt index 4dba1eb7..64bd36d8 100644 --- a/grammar/src/main/kotlin/ch/kleis/lcaac/grammar/Loader.kt +++ b/grammar/src/main/kotlin/ch/kleis/lcaac/grammar/Loader.kt @@ -79,28 +79,28 @@ class Loader( throw LoaderException("Duplicate global variable ${e.duplicates} defined") } - - val processTemplates = try { - ProcessTemplateRegister() + val dataSources = try { + DataSourceRegister() .plus( - processDefinitions - .map { Pair(it.buildUniqueKey(), process(it, globals)) } + dataSourceDefinitions + .map { DataSourceKey(it.dataSourceRef().innerText()) to dataSourceDefinition(it) } .asIterable() ) } catch (e: RegisterException) { - throw LoaderException("Duplicate process ${e.duplicates} defined") + throw LoaderException("Duplicate data sources ${e.duplicates} defined") } - val dataSources = try { - DataSourceRegister() + val processTemplates = try { + ProcessTemplateRegister() .plus( - dataSourceDefinitions - .map { DataSourceKey(it.dataSourceRef().innerText()) to dataSourceDefinition(it) } + processDefinitions + .map { Pair(it.buildUniqueKey(), process(it, globals, dataSources)) } .asIterable() ) } catch (e: RegisterException) { - throw LoaderException("Duplicate data sources ${e.duplicates} defined") + throw LoaderException("Duplicate process ${e.duplicates} defined") } + return SymbolTable( data = globals, processTemplates = processTemplates, diff --git a/grammar/src/test/kotlin/ch/kleis/lcaac/grammar/LoaderTest.kt b/grammar/src/test/kotlin/ch/kleis/lcaac/grammar/LoaderTest.kt index 0aa1e894..d3670103 100644 --- a/grammar/src/test/kotlin/ch/kleis/lcaac/grammar/LoaderTest.kt +++ b/grammar/src/test/kotlin/ch/kleis/lcaac/grammar/LoaderTest.kt @@ -14,10 +14,51 @@ import kotlin.test.assertEquals import kotlin.test.assertNotNull class LoaderTest { + @Test + fun load_whenSumOpInProduct_referenceUnitClosureShouldContainDataSource() { + // given + val file = LcaLangFixture.parser( + """ + datasource source { + location = "file.csv" + schema { + "mass" = 1 kg + } + } + + process p { + products { + sum(source["mass"]) p + } + } + """.trimIndent() + ).lcaFile() + val loader = Loader(BasicOperations) + + // when + val symbolTable = loader.load(sequenceOf(file)) + val referenceUnit = symbolTable + .getTemplate("p")!! + .body.products[0] + .product.referenceUnit!! as EUnitOf + val closure = referenceUnit.expression as EQuantityClosure + val actual = closure.symbolTable + + + // then + val expected = ECsvSource( + location = "file.csv", + schema = mapOf( + "mass" to ColumnType(EQuantityScale(BasicNumber(1.0), EDataRef("kg"))), + ) + ) + assertEquals(expected, actual.getDataSource("source")) + } + @Test fun load_datasource() { // given - val file = lcaFile( + val file = LcaLangFixture.parser( """ datasource source { location = "file.csv" @@ -27,7 +68,7 @@ class LoaderTest { } } """.trimIndent() - ) + ).lcaFile() val loader = Loader(BasicOperations) // when @@ -47,7 +88,7 @@ class LoaderTest { @Test fun load_whenFileContainsTest_thenNoError() { - val file = lcaFile( + val file = LcaLangFixture.parser( """ process p { products { @@ -67,7 +108,7 @@ class LoaderTest { } } """.trimIndent() - ) + ).lcaFile() val loader = Loader(BasicOperations) // when From f153340e3a158d0488649e055a75e01dfa4ed544 Mon Sep 17 00:00:00 2001 From: Peva Blanchard Date: Sat, 20 Jan 2024 23:50:09 +0100 Subject: [PATCH 22/37] grammar: parse default record of data source --- grammar/src/main/antlr/LcaLang.g4 | 3 +- .../ch/kleis/lcaac/grammar/CoreMapper.kt | 12 ++++- .../ch/kleis/lcaac/grammar/CoreMapperTest.kt | 32 ++++++++++++++ .../ch/kleis/lcaac/grammar/EvaluatorTest.kt | 44 +++++++++++++++++++ .../ch/kleis/lcaac/grammar/LoaderTest.kt | 29 ++++++++++++ 5 files changed, 117 insertions(+), 3 deletions(-) diff --git a/grammar/src/main/antlr/LcaLang.g4 b/grammar/src/main/antlr/LcaLang.g4 index 5c3d2230..c7b21623 100644 --- a/grammar/src/main/antlr/LcaLang.g4 +++ b/grammar/src/main/antlr/LcaLang.g4 @@ -162,7 +162,8 @@ variables ; assignment - : dataRef EQUAL dataExpression + : dataRef sep=EQUAL dataExpression + | dataRef sep=FROM_KEYWORD dataSourceRef ; /* diff --git a/grammar/src/main/kotlin/ch/kleis/lcaac/grammar/CoreMapper.kt b/grammar/src/main/kotlin/ch/kleis/lcaac/grammar/CoreMapper.kt index 76737c21..77d88ad4 100644 --- a/grammar/src/main/kotlin/ch/kleis/lcaac/grammar/CoreMapper.kt +++ b/grammar/src/main/kotlin/ch/kleis/lcaac/grammar/CoreMapper.kt @@ -27,10 +27,10 @@ class CoreMapper( .associate { it.labelRef().innerText() to EStringLiteral(it.STRING_LITERAL().innerText()) } val locals = ctx.variables() .flatMap { it.assignment() } - .associate { it.dataRef().innerText() to dataExpression(it.dataExpression()) } + .associate { assignment(it) } val params = ctx.params() .flatMap { it.assignment() } - .associate { it.dataRef().innerText() to dataExpression(it.dataExpression()) } + .associate { assignment(it) } val symbolTable = SymbolTable( data = try { DataRegister(globals.plus(params.mapKeys { DataKey(it.key) }).plus(locals.mapKeys { DataKey(it.key) })) @@ -73,6 +73,14 @@ class CoreMapper( ) } + fun assignment(ctx: LcaLangParser.AssignmentContext): Pair> { + return when(ctx.sep.text) { + ctx.EQUAL()?.innerText() -> ctx.dataRef().innerText() to dataExpression(ctx.dataExpression()) + ctx.FROM_KEYWORD()?.innerText() -> ctx.dataRef().innerText() to EDefaultRecordOf(ctx.dataSourceRef().innerText()) + else -> throw IllegalStateException("parsing error: invalid assignment '${ctx.text}'") + } + } + fun impactExchange(ctx: LcaLangParser.ImpactExchangeContext): ImpactBlock { return when (ctx) { is LcaLangParser.ImpactEntryContext -> EImpactBlockEntry( diff --git a/grammar/src/test/kotlin/ch/kleis/lcaac/grammar/CoreMapperTest.kt b/grammar/src/test/kotlin/ch/kleis/lcaac/grammar/CoreMapperTest.kt index a0d6cd12..8a896c94 100644 --- a/grammar/src/test/kotlin/ch/kleis/lcaac/grammar/CoreMapperTest.kt +++ b/grammar/src/test/kotlin/ch/kleis/lcaac/grammar/CoreMapperTest.kt @@ -16,6 +16,38 @@ import kotlin.test.assertEquals class CoreMapperTest { private val ops = BasicOperations + @Test + fun assignment_regular() { + // given + val ctx = LcaLangFixture.parser(""" + x = 1 kg + """.trimIndent()).assignment() + val mapper = CoreMapper(ops) + + // when + val actual = mapper.assignment(ctx) + + // then + val expected = "x" to EQuantityScale(BasicNumber(1.0), EDataRef("kg")) + assertEquals(expected, actual) + } + + @Test + fun assignment_defaultRecordOf() { + // given + val ctx = LcaLangFixture.parser(""" + x from inventory + """.trimIndent()).assignment() + val mapper = CoreMapper(ops) + + // when + val actual = mapper.assignment(ctx) + + // then + val expected = "x" to EDefaultRecordOf("inventory") + assertEquals(expected, actual) + } + @Test fun recordEntry() { // given diff --git a/grammar/src/test/kotlin/ch/kleis/lcaac/grammar/EvaluatorTest.kt b/grammar/src/test/kotlin/ch/kleis/lcaac/grammar/EvaluatorTest.kt index 304f531f..0dac8f26 100644 --- a/grammar/src/test/kotlin/ch/kleis/lcaac/grammar/EvaluatorTest.kt +++ b/grammar/src/test/kotlin/ch/kleis/lcaac/grammar/EvaluatorTest.kt @@ -20,6 +20,50 @@ import kotlin.test.assertEquals class EvaluatorTest { private val ops = BasicOperations private val sourceOps = mockk>() + + @Test + fun arena_shouldHandleDefaultRecordOf() { + // given + val file = LcaLangFixture.parser(""" + datasource source { + location = "file.csv" + schema { + "mass" = 1 kg + } + } + + process p { + params { + row from source + } + products { + 1 kg carrot + } + impacts { + row["mass"] co2 + } + } + """.trimIndent()).lcaFile() + val loader = Loader(ops) + val symbolTable = loader.load(sequenceOf(file), listOf(LoaderOption.WITH_PRELUDE)) + val spec = EProductSpec( + name = "carrot", + fromProcess = FromProcess("p", MatchLabels(emptyMap())), + ) + val evaluator = Evaluator(symbolTable, ops, sourceOps) + + // when + val trace = evaluator.trace(setOf(spec)) + val program = ContributionAnalysisProgram(trace.getSystemValue(), trace.getEntryPoint()) + val analysis = program.run() + + // then + val port = analysis.getObservablePorts().get("carrot from p{}{row={mass=1.0 kg}}") + val indicator = analysis.getControllablePorts().get("co2") + val expected = QuantityValue(BasicNumber(1.0), UnitValue(UnitSymbol.of("kg"), 1.0, Dimension.of("mass"))) + val actual = analysis.getPortContribution(port, indicator) + assertEquals(expected, actual) + } @Test fun arena_shouldHandleKnowledgeCorrectly() { diff --git a/grammar/src/test/kotlin/ch/kleis/lcaac/grammar/LoaderTest.kt b/grammar/src/test/kotlin/ch/kleis/lcaac/grammar/LoaderTest.kt index d3670103..db1faf81 100644 --- a/grammar/src/test/kotlin/ch/kleis/lcaac/grammar/LoaderTest.kt +++ b/grammar/src/test/kotlin/ch/kleis/lcaac/grammar/LoaderTest.kt @@ -14,6 +14,35 @@ import kotlin.test.assertEquals import kotlin.test.assertNotNull class LoaderTest { + @Test + fun load_params_defaultRecord() { + // given + val file = LcaLangFixture.parser(""" + datasource source { + location = "file.csv" + schema { + "mass" = 1 kg + } + } + + process p { + params { + row from source + } + } + """.trimIndent()).lcaFile() + val loader = Loader(BasicOperations) + + // when + val actual = loader.load(sequenceOf(file)) + .getTemplate("p")!! + .params["row"]!! + + // then + val expected = EDefaultRecordOf("source") + assertEquals(expected, actual) + } + @Test fun load_whenSumOpInProduct_referenceUnitClosureShouldContainDataSource() { // given From 47349feac0ffd79ae2e26a7d147d9ec6265b7005 Mon Sep 17 00:00:00 2001 From: Peva Blanchard Date: Sun, 21 Jan 2024 00:02:30 +0100 Subject: [PATCH 23/37] cli: fix parsing with default unit --- .../kotlin/ch/kleis/lcaac/cli/cmd/Utils.kt | 39 ++++++++----------- .../ch/kleis/lcaac/cli/cmd/UtilsKtTest.kt | 26 +++++++++++-- 2 files changed, 39 insertions(+), 26 deletions(-) diff --git a/cli/src/main/kotlin/ch/kleis/lcaac/cli/cmd/Utils.kt b/cli/src/main/kotlin/ch/kleis/lcaac/cli/cmd/Utils.kt index 0ab757a4..1799a312 100644 --- a/cli/src/main/kotlin/ch/kleis/lcaac/cli/cmd/Utils.kt +++ b/cli/src/main/kotlin/ch/kleis/lcaac/cli/cmd/Utils.kt @@ -3,8 +3,6 @@ package ch.kleis.lcaac.cli.cmd import ch.kleis.lcaac.core.lang.evaluator.EvaluatorException import ch.kleis.lcaac.core.lang.expression.DataExpression import ch.kleis.lcaac.core.lang.expression.EQuantityScale -import ch.kleis.lcaac.core.lang.expression.EUnitOf -import ch.kleis.lcaac.core.lang.expression.QuantityExpression import ch.kleis.lcaac.core.math.basic.BasicNumber import ch.kleis.lcaac.core.math.basic.BasicOperations import ch.kleis.lcaac.grammar.CoreMapper @@ -36,29 +34,24 @@ private fun lcaFile(inputStream: InputStream): LcaLangParser.LcaFileContext { fun parseQuantityWithDefaultUnit(s: String, defaultUnit: DataExpression): DataExpression { val parts = s.split(" ") - return when (parts.size) { - 1 -> { - val number = parts[0] - val amount = try { - parseDouble(number) - } catch (e: NumberFormatException) { - throw EvaluatorException("'$s' is not a valid quantity") - } - EQuantityScale(BasicNumber(amount), defaultUnit) + return if (parts.size == 1) { + val number = parts[0] + val amount = try { + parseDouble(number) + } catch (e: NumberFormatException) { + throw EvaluatorException("'$s' is not a valid quantity") } - 2 -> { - val lexer = LcaLangLexer(CharStreams.fromString(s)) - val tokens = CommonTokenStream(lexer) - val parser = LcaLangParser(tokens) - val ctx = parser.dataExpression() - try { - CoreMapper(BasicOperations).dataExpression(ctx) - } catch (e: IllegalStateException) { - throw EvaluatorException("'$s' is not a valid quantity") - } + EQuantityScale(BasicNumber(amount), defaultUnit) + } else { + val lexer = LcaLangLexer(CharStreams.fromString(s)) + val tokens = CommonTokenStream(lexer) + val parser = LcaLangParser(tokens) + val ctx = parser.dataExpression() + try { + CoreMapper(BasicOperations).dataExpression(ctx) + } catch (e: IllegalStateException) { + throw EvaluatorException("'$s' is not a valid quantity") } - else -> throw EvaluatorException("'$s' is not a valid quantity") } - } diff --git a/cli/src/test/kotlin/ch/kleis/lcaac/cli/cmd/UtilsKtTest.kt b/cli/src/test/kotlin/ch/kleis/lcaac/cli/cmd/UtilsKtTest.kt index 857f3fb2..85e65c23 100644 --- a/cli/src/test/kotlin/ch/kleis/lcaac/cli/cmd/UtilsKtTest.kt +++ b/cli/src/test/kotlin/ch/kleis/lcaac/cli/cmd/UtilsKtTest.kt @@ -2,6 +2,7 @@ package ch.kleis.lcaac.cli.cmd import ch.kleis.lcaac.core.lang.evaluator.EvaluatorException import ch.kleis.lcaac.core.lang.expression.EDataRef +import ch.kleis.lcaac.core.lang.expression.EQuantityMul import ch.kleis.lcaac.core.lang.expression.EQuantityScale import ch.kleis.lcaac.core.math.basic.BasicNumber import ch.kleis.lcaac.core.prelude.Prelude @@ -19,7 +20,7 @@ class UtilsKtTest { val defaultUnit = Prelude.unitMap()["kg"]!! // when/then - val actual = assertThrows { parseQuantityWithDefaultUnit(s, defaultUnit) } + val actual = assertThrows { parseQuantityWithDefaultUnit(s, defaultUnit) } assertEquals("'a@bc' is not a valid quantity", actual.message) } @@ -30,7 +31,7 @@ class UtilsKtTest { val defaultUnit = Prelude.unitMap()["kg"]!! // when/then - val actual = assertThrows { parseQuantityWithDefaultUnit(s, defaultUnit) } + val actual = assertThrows { parseQuantityWithDefaultUnit(s, defaultUnit) } assertEquals("'12 3 4' is not a valid quantity", actual.message) } @@ -41,7 +42,7 @@ class UtilsKtTest { val defaultUnit = Prelude.unitMap()["kg"]!! // when/then - val actual = assertThrows { parseQuantityWithDefaultUnit(s, defaultUnit) } + val actual = assertThrows { parseQuantityWithDefaultUnit(s, defaultUnit) } assertEquals("'12 \$3' is not a valid quantity", actual.message) } @@ -70,4 +71,23 @@ class UtilsKtTest { // then assertEquals(EQuantityScale(BasicNumber(12.0), EDataRef("kg")), actual) } + + @Test + fun parseQuantityWithDefaultUnit_whenComplexExpression() { + // given + val s = "12.0 kg * hour" + val kg = Prelude.unitMap()["kg"]!! + val hour = Prelude.unitMap()["hour"]!! + val defaultUnit = EQuantityMul(kg, hour) + + // when + val actual = parseQuantityWithDefaultUnit(s, defaultUnit) + + // then + assertEquals( + EQuantityScale( + BasicNumber(12.0), + EQuantityMul(EDataRef("kg"), EDataRef("hour"))), + actual) + } } From e77b2763c56ba85d73376b0a92782f3080c4d376 Mon Sep 17 00:00:00 2001 From: Peva Blanchard Date: Sun, 21 Jan 2024 01:12:18 +0100 Subject: [PATCH 24/37] core: ESum -> ESumProduct --- .../core/datasource/DataSourceOperations.kt | 2 +- .../lcaac/core/lang/evaluator/ToValue.kt | 2 +- .../reducer/DataExpressionReducer.kt | 6 +- .../core/lang/expression/DataExpression.kt | 9 +-- .../lang/expression/optics/EveryDataRef.kt | 4 +- .../reducer/DataExpressionReducerTest.kt | 22 +++--- .../reducer/LcaExpressionReducerTest.kt | 68 +++++++------------ .../core/lang/evaluator/step/ReduceTest.kt | 4 +- 8 files changed, 52 insertions(+), 65 deletions(-) diff --git a/core/src/main/kotlin/ch/kleis/lcaac/core/datasource/DataSourceOperations.kt b/core/src/main/kotlin/ch/kleis/lcaac/core/datasource/DataSourceOperations.kt index 256724c8..df937b56 100644 --- a/core/src/main/kotlin/ch/kleis/lcaac/core/datasource/DataSourceOperations.kt +++ b/core/src/main/kotlin/ch/kleis/lcaac/core/datasource/DataSourceOperations.kt @@ -6,5 +6,5 @@ import ch.kleis.lcaac.core.lang.expression.ERecord interface DataSourceOperations { fun readAll(source: DataSourceExpression): Sequence> - fun sum(source: DataSourceExpression, column: String): DataExpression + fun sumProduct(source: DataSourceExpression, columns: List): DataExpression } diff --git a/core/src/main/kotlin/ch/kleis/lcaac/core/lang/evaluator/ToValue.kt b/core/src/main/kotlin/ch/kleis/lcaac/core/lang/evaluator/ToValue.kt index e104b2ad..25b85e02 100644 --- a/core/src/main/kotlin/ch/kleis/lcaac/core/lang/evaluator/ToValue.kt +++ b/core/src/main/kotlin/ch/kleis/lcaac/core/lang/evaluator/ToValue.kt @@ -146,7 +146,7 @@ class ToValue( is StringExpression -> e.toValue() is ERecord -> RecordValue(e.entries.mapValues { it.value.toValue() }) is EDataRef, is ERecordEntry, - is EDefaultRecordOf, is ESum -> throw EvaluatorException("$it is not reduced") + is EDefaultRecordOf, is ESumProduct -> throw EvaluatorException("$it is not reduced") } }, ) diff --git a/core/src/main/kotlin/ch/kleis/lcaac/core/lang/evaluator/reducer/DataExpressionReducer.kt b/core/src/main/kotlin/ch/kleis/lcaac/core/lang/evaluator/reducer/DataExpressionReducer.kt index e273e2fe..aca12aa9 100644 --- a/core/src/main/kotlin/ch/kleis/lcaac/core/lang/evaluator/reducer/DataExpressionReducer.kt +++ b/core/src/main/kotlin/ch/kleis/lcaac/core/lang/evaluator/reducer/DataExpressionReducer.kt @@ -39,15 +39,15 @@ class DataExpressionReducer( is ERecord -> reduceMap(expression) is ERecordEntry -> reduceMapEntry(expression) is EDefaultRecordOf -> reduceDefaultRecordOf(expression) - is ESum -> reduceESum(expression) + is ESumProduct -> reduceESumProduct(expression) } } } - private fun reduceESum(expression: ESum): DataExpression { + private fun reduceESumProduct(expression: ESumProduct): DataExpression { val dataSource = dataSourceRegister[DataSourceKey(expression.dataSourceRef)] ?: throw EvaluatorException("unknown data source '${expression.dataSourceRef}'") - return reduce(sourceOps.sum(dataSource, expression.column)) + return reduce(sourceOps.sumProduct(dataSource, expression.columns)) } private fun reduceDefaultRecordOf(expression: EDefaultRecordOf): DataExpression { diff --git a/core/src/main/kotlin/ch/kleis/lcaac/core/lang/expression/DataExpression.kt b/core/src/main/kotlin/ch/kleis/lcaac/core/lang/expression/DataExpression.kt index c3791bae..a1a2bc79 100644 --- a/core/src/main/kotlin/ch/kleis/lcaac/core/lang/expression/DataExpression.kt +++ b/core/src/main/kotlin/ch/kleis/lcaac/core/lang/expression/DataExpression.kt @@ -18,17 +18,17 @@ sealed interface StringExpression */ @optics -data class EDefaultRecordOf(val dataSourceRef: String): DataExpression { +data class EDefaultRecordOf(val dataSourceRef: String) : DataExpression { companion object } @optics -data class ERecord(val entries: Map>): DataExpression { +data class ERecord(val entries: Map>) : DataExpression { companion object } @optics -data class ERecordEntry(val record: DataExpression, val index: String): DataExpression { +data class ERecordEntry(val record: DataExpression, val index: String) : DataExpression { companion object } @@ -39,7 +39,8 @@ data class ERecordEntry(val record: DataExpression, val index: String): Da sealed interface ColumnOperationExpression @optics -data class ESum(val dataSourceRef: String, val column: String): DataExpression, ColumnOperationExpression { +data class ESumProduct(val dataSourceRef: String, val columns: List) + : DataExpression, ColumnOperationExpression { companion object } diff --git a/core/src/main/kotlin/ch/kleis/lcaac/core/lang/expression/optics/EveryDataRef.kt b/core/src/main/kotlin/ch/kleis/lcaac/core/lang/expression/optics/EveryDataRef.kt index e698f2c7..db9f4c96 100644 --- a/core/src/main/kotlin/ch/kleis/lcaac/core/lang/expression/optics/EveryDataRef.kt +++ b/core/src/main/kotlin/ch/kleis/lcaac/core/lang/expression/optics/EveryDataRef.kt @@ -53,7 +53,7 @@ fun everyDataRefInDataExpression(): PEvery, DataExpression ) is ERecordEntry -> foldMap(M, source.record, map) is EDefaultRecordOf -> M.empty() - is ESum -> M.empty() + is ESumProduct -> M.empty() } } @@ -120,7 +120,7 @@ fun everyDataRefInDataExpression(): PEvery, DataExpression ) is EDefaultRecordOf -> source - is ESum -> source + is ESumProduct -> source } } } diff --git a/core/src/test/kotlin/ch/kleis/lcaac/core/lang/evaluator/reducer/DataExpressionReducerTest.kt b/core/src/test/kotlin/ch/kleis/lcaac/core/lang/evaluator/reducer/DataExpressionReducerTest.kt index 8158ec9b..5fa86638 100644 --- a/core/src/test/kotlin/ch/kleis/lcaac/core/lang/evaluator/reducer/DataExpressionReducerTest.kt +++ b/core/src/test/kotlin/ch/kleis/lcaac/core/lang/evaluator/reducer/DataExpressionReducerTest.kt @@ -30,18 +30,24 @@ class DataExpressionReducerTest { */ @Test - fun sum() { + fun sumProduct() { // given - val expression = ESum("source", "mass") + val expression = ESumProduct("source", listOf("volume", "mass")) val dataSource = ECsvSource( location = "source.csv", schema = mapOf( + "volume" to ColumnType(QuantityFixture.oneLitre), "mass" to ColumnType(QuantityFixture.oneKilogram), ), ) val sops = mockk>() - val total = QuantityFixture.twoKilograms - every { sops.sum(dataSource, "mass") } returns total + val total = EQuantityScale(BasicNumber(1.0), + EUnitLiteral( + UnitFixture.l.symbol.multiply(UnitFixture.kg.symbol), + 1.0, + UnitFixture.l.dimension.multiply(UnitFixture.kg.dimension), + )) + every { sops.sumProduct(dataSource, listOf("volume", "mass")) } returns total val reducer = DataExpressionReducer( DataRegister.empty(), DataSourceRegister.from(mapOf( @@ -56,14 +62,14 @@ class DataExpressionReducerTest { // then assertEquals(total, actual) - verify { sops.sum(dataSource, "mass") } + verify { sops.sumProduct(dataSource, listOf("volume", "mass")) } } @Test fun sum_invalidDataSourceRef() { // given - val expression = ESum("foo", "mass") + val expression = ESumProduct("foo", listOf("mass")) val dataSource = ECsvSource( location = "source.csv", schema = mapOf( @@ -72,7 +78,7 @@ class DataExpressionReducerTest { ) val sops = mockk>() val total = QuantityFixture.twoKilograms - every { sops.sum(dataSource, "mass") } returns total + every { sops.sumProduct(dataSource, listOf("mass")) } returns total val reducer = DataExpressionReducer( DataRegister.empty(), DataSourceRegister.from(mapOf( @@ -86,7 +92,7 @@ class DataExpressionReducerTest { val e = assertThrows { reducer.reduce(expression) } assertEquals("unknown data source 'foo'", e.message) } - + /* RECORD */ diff --git a/core/src/test/kotlin/ch/kleis/lcaac/core/lang/evaluator/reducer/LcaExpressionReducerTest.kt b/core/src/test/kotlin/ch/kleis/lcaac/core/lang/evaluator/reducer/LcaExpressionReducerTest.kt index 219cc9aa..22ffa418 100644 --- a/core/src/test/kotlin/ch/kleis/lcaac/core/lang/evaluator/reducer/LcaExpressionReducerTest.kt +++ b/core/src/test/kotlin/ch/kleis/lcaac/core/lang/evaluator/reducer/LcaExpressionReducerTest.kt @@ -6,6 +6,7 @@ import ch.kleis.lcaac.core.lang.fixture.* import ch.kleis.lcaac.core.lang.register.* import ch.kleis.lcaac.core.math.basic.BasicNumber import ch.kleis.lcaac.core.math.basic.BasicOperations +import io.mockk.every import io.mockk.mockk import org.junit.jupiter.api.Test import org.junit.jupiter.api.assertThrows @@ -38,22 +39,15 @@ class LcaExpressionReducerTest { name = "foo", inputs = listOf(block), ) - val sourceOps = object : DataSourceOperations { - override fun readAll(source: DataSourceExpression): Sequence> { - return sequenceOf( - ERecord(mapOf( - "mass" to QuantityFixture.oneKilogram, - )), - ERecord(mapOf( - "mass" to QuantityFixture.twoKilograms, - )), - ) - } - - override fun sum(source: DataSourceExpression, column: String): DataExpression { - throw IllegalAccessException("should not have been called") - } - } + val sourceOps = mockk>() + every { sourceOps.readAll(any()) } returns sequenceOf( + ERecord(mapOf( + "mass" to QuantityFixture.oneKilogram, + )), + ERecord(mapOf( + "mass" to QuantityFixture.twoKilograms, + )), + ) val reducer = LcaExpressionReducer( DataRegister.from(mapOf( DataKey("row") to QuantityFixture.oneLitre, @@ -89,22 +83,15 @@ class LcaExpressionReducerTest { name = "foo", inputs = listOf(block), ) - val sourceOps = object : DataSourceOperations { - override fun readAll(source: DataSourceExpression): Sequence> { - return sequenceOf( - ERecord(mapOf( - "mass" to QuantityFixture.oneKilogram, - )), - ERecord(mapOf( - "mass" to QuantityFixture.twoKilograms, - )), - ) - } - - override fun sum(source: DataSourceExpression, column: String): DataExpression { - throw IllegalAccessException("should not have been called") - } - } + val sourceOps = mockk>() + every { sourceOps.readAll(any()) } returns sequenceOf( + ERecord(mapOf( + "mass" to QuantityFixture.oneKilogram, + )), + ERecord(mapOf( + "mass" to QuantityFixture.twoKilograms, + )), + ) val reducer = LcaExpressionReducer( DataRegister.empty(), DataSourceRegister.from(mapOf( @@ -169,18 +156,11 @@ class LcaExpressionReducerTest { name = "foo", inputs = listOf(block), ) - val sourceOps = object : DataSourceOperations { - override fun readAll(source: DataSourceExpression): Sequence> { - return sequenceOf( - ERecord(emptyMap()), - ERecord(emptyMap()), - ) - } - - override fun sum(source: DataSourceExpression, column: String): DataExpression { - throw IllegalAccessException("should not have been called") - } - } + val sourceOps = mockk>() + every { sourceOps.readAll(any()) } returns sequenceOf( + ERecord(emptyMap()), + ERecord(emptyMap()), + ) val reducer = LcaExpressionReducer( DataRegister.empty(), DataSourceRegister.from(mapOf( diff --git a/core/src/test/kotlin/ch/kleis/lcaac/core/lang/evaluator/step/ReduceTest.kt b/core/src/test/kotlin/ch/kleis/lcaac/core/lang/evaluator/step/ReduceTest.kt index 58bbaf1e..9eadd611 100644 --- a/core/src/test/kotlin/ch/kleis/lcaac/core/lang/evaluator/step/ReduceTest.kt +++ b/core/src/test/kotlin/ch/kleis/lcaac/core/lang/evaluator/step/ReduceTest.kt @@ -34,7 +34,7 @@ class ReduceTest { val instance = EProcessTemplateApplication( template, mapOf( - "q_water" to ESum("source", "mass") + "q_water" to ESumProduct("source", listOf("mass")) ) ) val dataSource = ECsvSource( @@ -43,7 +43,7 @@ class ReduceTest { "mass" to ColumnType(QuantityFixture.oneLitre) ) ) - every { sourceOps.sum(dataSource, "mass") } returns QuantityFixture.twoLitres + every { sourceOps.sumProduct(dataSource, listOf("mass")) } returns QuantityFixture.twoLitres val symbolTable = SymbolTable( dataSources = DataSourceRegister.from(mapOf( DataSourceKey("source") to dataSource, From 050279971fdd969ee3ad9f5bf1dbb7d35c5ffdc5 Mon Sep 17 00:00:00 2001 From: Peva Blanchard Date: Sun, 21 Jan 2024 01:23:32 +0100 Subject: [PATCH 25/37] grammar: sum product syntax --- grammar/src/main/antlr/LcaLang.g4 | 2 +- grammar/src/main/kotlin/ch/kleis/lcaac/grammar/CoreMapper.kt | 5 +++-- .../src/test/kotlin/ch/kleis/lcaac/grammar/CoreMapperTest.kt | 5 ++--- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/grammar/src/main/antlr/LcaLang.g4 b/grammar/src/main/antlr/LcaLang.g4 index c7b21623..889041ca 100644 --- a/grammar/src/main/antlr/LcaLang.g4 +++ b/grammar/src/main/antlr/LcaLang.g4 @@ -227,7 +227,7 @@ dataExpression | parenExpression # baseGroup | stringExpression # baseGroup | dataRef slice? # baseGroup - | op=SUM LPAREN dataSourceRef LBRACK columnRef RBRACK RPAREN # colGroup + | op=SUM LPAREN dataSourceRef COMMA columnRef (STAR columnRef)* RPAREN # colGroup ; slice : LBRACK columnRef RBRACK diff --git a/grammar/src/main/kotlin/ch/kleis/lcaac/grammar/CoreMapper.kt b/grammar/src/main/kotlin/ch/kleis/lcaac/grammar/CoreMapper.kt index 77d88ad4..c7011171 100644 --- a/grammar/src/main/kotlin/ch/kleis/lcaac/grammar/CoreMapper.kt +++ b/grammar/src/main/kotlin/ch/kleis/lcaac/grammar/CoreMapper.kt @@ -283,8 +283,9 @@ class CoreMapper( when (ctx.op.text) { ctx.SUM().innerText() -> { val sourceRef = ctx.dataSourceRef().innerText() - val columnRef = ctx.columnRef().innerText() - ESum(sourceRef, columnRef) + val columns = ctx.columnRef() + .map { it.innerText() } + ESumProduct(sourceRef, columns) } else -> throw IllegalStateException("parsing error: invalid column operation '${ctx.op.text}'") } diff --git a/grammar/src/test/kotlin/ch/kleis/lcaac/grammar/CoreMapperTest.kt b/grammar/src/test/kotlin/ch/kleis/lcaac/grammar/CoreMapperTest.kt index 8a896c94..b42821c0 100644 --- a/grammar/src/test/kotlin/ch/kleis/lcaac/grammar/CoreMapperTest.kt +++ b/grammar/src/test/kotlin/ch/kleis/lcaac/grammar/CoreMapperTest.kt @@ -5,7 +5,6 @@ import ch.kleis.lcaac.core.lang.expression.* import ch.kleis.lcaac.core.math.basic.BasicNumber import ch.kleis.lcaac.core.math.basic.BasicOperations import io.mockk.mockk -import io.mockk.mockkObject import kotlin.test.Test import kotlin.test.assertEquals @@ -68,7 +67,7 @@ class CoreMapperTest { fun columnOperation_sum() { // given val ctx = LcaLangFixture.parser(""" - sum(source["mass"]) + sum(source, "mass" * "ratio") """.trimIndent()).dataExpression() val mapper = CoreMapper(ops) @@ -76,7 +75,7 @@ class CoreMapperTest { val actual = mapper.dataExpression(ctx) // then - val expected = ESum("source", "mass") + val expected = ESumProduct("source", listOf("mass", "ratio")) assertEquals(expected, actual) } From b4965977a11ef145b278f7fad222d4c92cded7f1 Mon Sep 17 00:00:00 2001 From: Peva Blanchard Date: Sun, 21 Jan 2024 01:26:39 +0100 Subject: [PATCH 26/37] cli: csv source ops: sum product --- .../lcaac/cli/csv/BasicCsvSourceOperations.kt | 57 +++++++++++-------- 1 file changed, 32 insertions(+), 25 deletions(-) diff --git a/cli/src/main/kotlin/ch/kleis/lcaac/cli/csv/BasicCsvSourceOperations.kt b/cli/src/main/kotlin/ch/kleis/lcaac/cli/csv/BasicCsvSourceOperations.kt index 61a1c062..77e9aa2b 100644 --- a/cli/src/main/kotlin/ch/kleis/lcaac/cli/csv/BasicCsvSourceOperations.kt +++ b/cli/src/main/kotlin/ch/kleis/lcaac/cli/csv/BasicCsvSourceOperations.kt @@ -33,29 +33,32 @@ class BasicCsvSourceOperations( val entries = header .filter { entry -> source.schema.containsKey(entry.key) } .mapValues { entry -> - val columnType = source.schema[entry.key]!! - val columnDefaultValue = columnType.defaultValue - val position = entry.value - val element = record[position] - when (columnDefaultValue) { - is QuantityExpression<*> -> - parseQuantityWithDefaultUnit(element, EUnitOf(columnDefaultValue)) + val columnType = source.schema[entry.key]!! + val columnDefaultValue = columnType.defaultValue + val position = entry.value + val element = record[position] + when (columnDefaultValue) { + is QuantityExpression<*> -> + parseQuantityWithDefaultUnit(element, EUnitOf(columnDefaultValue)) - is StringExpression -> - EStringLiteral(element) + is StringExpression -> + EStringLiteral(element) - else -> throw IllegalStateException( - "invalid schema: column '${entry.key}' has an invalid default value" - ) + else -> throw IllegalStateException( + "invalid schema: column '${entry.key}' has an invalid default value" + ) + } } - } ERecord(entries) } } } } - override fun sum(source: DataSourceExpression, column: String): DataExpression { + override fun sumProduct( + source: DataSourceExpression, + columns: List, + ): DataExpression { val reducer = DataExpressionReducer( dataRegister = Prelude.units(), dataSourceRegister = DataSourceRegister.empty(), @@ -68,19 +71,23 @@ class BasicCsvSourceOperations( val inputStream = location.toFile().inputStream() val parser = CSVParser(inputStream.reader(), format) val header = parser.headerMap - val position = header[column] - ?: throw IllegalStateException( - "${source.location}: invalid schema: unknown column '$column'" - ) - val columnType = source.schema[column] - ?: throw IllegalStateException( - "invalid schema: column '$column' has an invalid default value" - ) - val defaultValue = columnType.defaultValue parser.iterator().asSequence() .map { record -> - val element = record[position] - parseQuantityWithDefaultUnit(element, EUnitOf(defaultValue)) + columns.map { column -> + val position = header[column] + ?: throw IllegalStateException( + "${source.location}: invalid schema: unknown column '$column'" + ) + val columnType = source.schema[column] + ?: throw IllegalStateException( + "invalid schema: column '$column' has an invalid default value" + ) + val defaultValue = columnType.defaultValue + val element = record[position] + parseQuantityWithDefaultUnit(element, EUnitOf(defaultValue)) + }.reduce { acc, expression -> + reducer.reduce(EQuantityMul(acc, expression)) + } }.reduce { acc, expression -> reducer.reduce(EQuantityAdd(acc, expression)) } From 0331378eddb6cc0b111ef2e2c87069ecc491e6fb Mon Sep 17 00:00:00 2001 From: Peva Blanchard Date: Sun, 21 Jan 2024 12:53:28 +0100 Subject: [PATCH 27/37] bump lcaac version: 1.3.4 -> 1.3.5-rc --- gradle.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle.properties b/gradle.properties index 3723be23..3c461d7b 100644 --- a/gradle.properties +++ b/gradle.properties @@ -2,4 +2,4 @@ javaVersion=17 gradleVersion=7.6 org.gradle.jvmargs=-Xmx4096m lcaacGroup=ch.kleis.lcaac -lcaacVersion=1.3.4 +lcaacVersion=1.3.5-rc From 0dfc0c270c8ddb8c005ae6e288ae9d22b725cbb8 Mon Sep 17 00:00:00 2001 From: Peva Blanchard Date: Sun, 21 Jan 2024 16:02:54 +0100 Subject: [PATCH 28/37] tutorials: datasources --- .../02-advanced/05-datasources/inventory.csv | 4 + tutorials/02-advanced/05-datasources/main.lca | 125 ++++++++++++++++++ .../02-advanced/05-datasources/units.lca | 9 ++ tutorials/run.sh | 1 + 4 files changed, 139 insertions(+) create mode 100644 tutorials/02-advanced/05-datasources/inventory.csv create mode 100644 tutorials/02-advanced/05-datasources/main.lca create mode 100644 tutorials/02-advanced/05-datasources/units.lca diff --git a/tutorials/02-advanced/05-datasources/inventory.csv b/tutorials/02-advanced/05-datasources/inventory.csv new file mode 100644 index 00000000..577279ea --- /dev/null +++ b/tutorials/02-advanced/05-datasources/inventory.csv @@ -0,0 +1,4 @@ +id,quantity,ram_size,storage_size,amortization_period,power,ram_allocation,storage_allocation,GWP,WU +small,38,384,61.44,5,400,75,25,2855.502608725893,6.468493286421048 +medium,62,384,11.52,5,400,75,25,10469.589982157144,23.680314019609003 +large,3,768,76.8,5,400,75,25,13787.498425573982,31.193930932287838 diff --git a/tutorials/02-advanced/05-datasources/main.lca b/tutorials/02-advanced/05-datasources/main.lca new file mode 100644 index 00000000..eaf344cd --- /dev/null +++ b/tutorials/02-advanced/05-datasources/main.lca @@ -0,0 +1,125 @@ +/* + A datasource is assumed to provide a sequence of records. + For now, only the local CSV file source is supported. +*/ +datasource inventory { + location = "inventory.csv" + + /* + The schema is defined using default values. Note that the unit of + the default value will be the one chosen for the entire column. + */ + schema { + "quantity" = 1 p + "ram_size" = 16 GB + "storage_size" = 1 TB + "amortization_period" = 5 year + "power" = 400 W + "ram_allocation" = 75 percent + "storage_allocation" = 25 percent + + // embodied impact + "GWP" = 0 kg_CO2_Eq + "WU" = 0 m3 + } +} + +/* + Block 'for_each' +*/ + +process simple_pool { + products { + 1 p simple_pool + } + impacts { + // A block for_each allows to map over the records of the datasource + for_each row in inventory { + + // The variable row represents a record. + // The dimension of, e.g., row["quantity"] is defined by the schema. + row["quantity"] * row["GWP"] GWP + } + } +} + +test simple_pool { + given { + 1 p simple_pool + } + assert { + GWP between 700e3 kg_CO2_Eq and 800e3 kg_CO2_Eq + } +} + + +/* + Record as parameter +*/ + +process server { + // You can define a parameter as a row from inventory. + // The default value for this parameter is given by the schema. + params { + row from inventory + } + products { + 1 p server + } + impacts { + row["GWP"] GWP + } +} + +process pool_server { + products { + 1 p pool_server + } + inputs { + for_each row in inventory { + // record variable can be fed to the process invoked. + row["quantity"] server from server(row = row) + } + } +} + +test pool_server { + given { + 1 p pool_server + } + assert { + GWP between 700e3 kg_CO2_Eq and 800e3 kg_CO2_Eq + } +} + +/* + Column operations. +*/ + +process sum_prod { + products { + 1 p sum_prod + } + impacts { + /* + The 'sum' primitive allows compute the sum-product + of multiple columns. + + In this example, the columns "quantity" and "GWP" + are multiplied point-wise, and then summed. + + For now, only the point-wise product of columns is + supported. + */ + sum(inventory, "quantity" * "GWP") GWP + } +} + +test sum_prod { + given { + 1 p sum_prod + } + assert { + GWP between 700e3 kg_CO2_Eq and 800e3 kg_CO2_Eq + } +} diff --git a/tutorials/02-advanced/05-datasources/units.lca b/tutorials/02-advanced/05-datasources/units.lca new file mode 100644 index 00000000..90b5cbf8 --- /dev/null +++ b/tutorials/02-advanced/05-datasources/units.lca @@ -0,0 +1,9 @@ +unit GB { + symbol = "GB" + dimension = "memory" +} + +unit TB { + symbol = "TB" + alias_for = 1024 GB +} diff --git a/tutorials/run.sh b/tutorials/run.sh index 70fa14b9..4050872d 100755 --- a/tutorials/run.sh +++ b/tutorials/run.sh @@ -27,6 +27,7 @@ lcaac test -p $TUTORIALS_PATH/02-advanced/01-parametrized-process lcaac test -p $TUTORIALS_PATH/02-advanced/02-variables lcaac test -p $TUTORIALS_PATH/02-advanced/03-units lcaac test -p $TUTORIALS_PATH/02-advanced/04-labels +lcaac test -p $TUTORIALS_PATH/02-advanced/05-datasources # Check custom dimensions tutorial set -euo From 1f9ee45884192f5776032bdc38fcee7221dc35d7 Mon Sep 17 00:00:00 2001 From: Peva Blanchard Date: Sun, 21 Jan 2024 16:29:21 +0100 Subject: [PATCH 29/37] moved basic csv source ops to core package --- .../kotlin/ch/kleis/lcaac/cli/cmd/TestCommand.kt | 2 +- .../main/kotlin/ch/kleis/lcaac/cli/cmd/Utils.kt | 2 +- .../kotlin/ch/kleis/lcaac/cli/csv/CsvProcessor.kt | 5 +++-- .../main/kotlin/ch/kleis/lcaac/cli/csv/Utils.kt | 10 ++++++++++ .../kotlin/ch/kleis/lcaac/cli/cmd/UtilsKtTest.kt | 12 ++++++------ core/build.gradle.kts | 2 ++ .../core/datasource}/BasicCsvSourceOperations.kt | 15 ++++++++++++--- 7 files changed, 35 insertions(+), 13 deletions(-) create mode 100644 cli/src/main/kotlin/ch/kleis/lcaac/cli/csv/Utils.kt rename {cli/src/main/kotlin/ch/kleis/lcaac/cli/csv => core/src/main/kotlin/ch/kleis/lcaac/core/datasource}/BasicCsvSourceOperations.kt (89%) diff --git a/cli/src/main/kotlin/ch/kleis/lcaac/cli/cmd/TestCommand.kt b/cli/src/main/kotlin/ch/kleis/lcaac/cli/cmd/TestCommand.kt index ccb38523..cbdb6759 100644 --- a/cli/src/main/kotlin/ch/kleis/lcaac/cli/cmd/TestCommand.kt +++ b/cli/src/main/kotlin/ch/kleis/lcaac/cli/cmd/TestCommand.kt @@ -1,6 +1,6 @@ package ch.kleis.lcaac.cli.cmd -import ch.kleis.lcaac.cli.csv.BasicCsvSourceOperations +import ch.kleis.lcaac.core.datasource.BasicCsvSourceOperations import ch.kleis.lcaac.core.math.basic.BasicOperations import ch.kleis.lcaac.core.testing.BasicTestRunner import ch.kleis.lcaac.core.testing.GenericFailure diff --git a/cli/src/main/kotlin/ch/kleis/lcaac/cli/cmd/Utils.kt b/cli/src/main/kotlin/ch/kleis/lcaac/cli/cmd/Utils.kt index 1799a312..143060fb 100644 --- a/cli/src/main/kotlin/ch/kleis/lcaac/cli/cmd/Utils.kt +++ b/cli/src/main/kotlin/ch/kleis/lcaac/cli/cmd/Utils.kt @@ -32,7 +32,7 @@ private fun lcaFile(inputStream: InputStream): LcaLangParser.LcaFileContext { return parser.lcaFile() } -fun parseQuantityWithDefaultUnit(s: String, defaultUnit: DataExpression): DataExpression { +fun smartParseQuantityWithDefaultUnit(s: String, defaultUnit: DataExpression): DataExpression { val parts = s.split(" ") return if (parts.size == 1) { val number = parts[0] diff --git a/cli/src/main/kotlin/ch/kleis/lcaac/cli/csv/CsvProcessor.kt b/cli/src/main/kotlin/ch/kleis/lcaac/cli/csv/CsvProcessor.kt index d0e553f9..11c14b6c 100644 --- a/cli/src/main/kotlin/ch/kleis/lcaac/cli/csv/CsvProcessor.kt +++ b/cli/src/main/kotlin/ch/kleis/lcaac/cli/csv/CsvProcessor.kt @@ -1,7 +1,8 @@ package ch.kleis.lcaac.cli.csv -import ch.kleis.lcaac.cli.cmd.parseQuantityWithDefaultUnit +import ch.kleis.lcaac.cli.cmd.smartParseQuantityWithDefaultUnit import ch.kleis.lcaac.core.assessment.ContributionAnalysisProgram +import ch.kleis.lcaac.core.datasource.BasicCsvSourceOperations import ch.kleis.lcaac.core.lang.SymbolTable import ch.kleis.lcaac.core.lang.evaluator.Evaluator import ch.kleis.lcaac.core.lang.evaluator.EvaluatorException @@ -32,7 +33,7 @@ class CsvProcessor( .mapValues { entry -> when (val v = entry.value) { is QuantityExpression<*> -> request[entry.key]?.let { - parseQuantityWithDefaultUnit(it, EUnitOf(v)) + smartParseQuantityWithDefaultUnit(it, EUnitOf(v)) } ?: entry.value is StringExpression -> request[entry.key]?.let { diff --git a/cli/src/main/kotlin/ch/kleis/lcaac/cli/csv/Utils.kt b/cli/src/main/kotlin/ch/kleis/lcaac/cli/csv/Utils.kt new file mode 100644 index 00000000..43a76c28 --- /dev/null +++ b/cli/src/main/kotlin/ch/kleis/lcaac/cli/csv/Utils.kt @@ -0,0 +1,10 @@ +package ch.kleis.lcaac.cli.csv + +import ch.kleis.lcaac.core.lang.evaluator.EvaluatorException +import ch.kleis.lcaac.core.lang.expression.DataExpression +import ch.kleis.lcaac.core.lang.expression.EQuantityScale +import ch.kleis.lcaac.core.math.basic.BasicNumber +import java.lang.Double +import kotlin.NumberFormatException +import kotlin.String + diff --git a/cli/src/test/kotlin/ch/kleis/lcaac/cli/cmd/UtilsKtTest.kt b/cli/src/test/kotlin/ch/kleis/lcaac/cli/cmd/UtilsKtTest.kt index 85e65c23..b8b89af0 100644 --- a/cli/src/test/kotlin/ch/kleis/lcaac/cli/cmd/UtilsKtTest.kt +++ b/cli/src/test/kotlin/ch/kleis/lcaac/cli/cmd/UtilsKtTest.kt @@ -20,7 +20,7 @@ class UtilsKtTest { val defaultUnit = Prelude.unitMap()["kg"]!! // when/then - val actual = assertThrows { parseQuantityWithDefaultUnit(s, defaultUnit) } + val actual = assertThrows { smartParseQuantityWithDefaultUnit(s, defaultUnit) } assertEquals("'a@bc' is not a valid quantity", actual.message) } @@ -31,7 +31,7 @@ class UtilsKtTest { val defaultUnit = Prelude.unitMap()["kg"]!! // when/then - val actual = assertThrows { parseQuantityWithDefaultUnit(s, defaultUnit) } + val actual = assertThrows { smartParseQuantityWithDefaultUnit(s, defaultUnit) } assertEquals("'12 3 4' is not a valid quantity", actual.message) } @@ -42,7 +42,7 @@ class UtilsKtTest { val defaultUnit = Prelude.unitMap()["kg"]!! // when/then - val actual = assertThrows { parseQuantityWithDefaultUnit(s, defaultUnit) } + val actual = assertThrows { smartParseQuantityWithDefaultUnit(s, defaultUnit) } assertEquals("'12 \$3' is not a valid quantity", actual.message) } @@ -53,7 +53,7 @@ class UtilsKtTest { val defaultUnit = Prelude.unitMap()["kg"]!! // when - val actual = parseQuantityWithDefaultUnit(s, defaultUnit) + val actual = smartParseQuantityWithDefaultUnit(s, defaultUnit) // then assertEquals(EQuantityScale(BasicNumber(12.0), defaultUnit), actual) @@ -66,7 +66,7 @@ class UtilsKtTest { val defaultUnit = Prelude.unitMap()["kg"]!! // when - val actual = parseQuantityWithDefaultUnit(s, defaultUnit) + val actual = smartParseQuantityWithDefaultUnit(s, defaultUnit) // then assertEquals(EQuantityScale(BasicNumber(12.0), EDataRef("kg")), actual) @@ -81,7 +81,7 @@ class UtilsKtTest { val defaultUnit = EQuantityMul(kg, hour) // when - val actual = parseQuantityWithDefaultUnit(s, defaultUnit) + val actual = smartParseQuantityWithDefaultUnit(s, defaultUnit) // then assertEquals( diff --git a/core/build.gradle.kts b/core/build.gradle.kts index 49b57e17..33fbda11 100644 --- a/core/build.gradle.kts +++ b/core/build.gradle.kts @@ -48,6 +48,8 @@ dependencies { implementation("org.apache.logging.log4j:log4j-core:$log4jVersion") implementation("org.apache.logging.log4j:log4j-slf4j-impl:$log4jVersion") + implementation("org.apache.commons:commons-csv:1.10.0") + testImplementation(kotlin("test")) } diff --git a/cli/src/main/kotlin/ch/kleis/lcaac/cli/csv/BasicCsvSourceOperations.kt b/core/src/main/kotlin/ch/kleis/lcaac/core/datasource/BasicCsvSourceOperations.kt similarity index 89% rename from cli/src/main/kotlin/ch/kleis/lcaac/cli/csv/BasicCsvSourceOperations.kt rename to core/src/main/kotlin/ch/kleis/lcaac/core/datasource/BasicCsvSourceOperations.kt index 77e9aa2b..2fdcce76 100644 --- a/cli/src/main/kotlin/ch/kleis/lcaac/cli/csv/BasicCsvSourceOperations.kt +++ b/core/src/main/kotlin/ch/kleis/lcaac/core/datasource/BasicCsvSourceOperations.kt @@ -1,7 +1,6 @@ -package ch.kleis.lcaac.cli.csv +package ch.kleis.lcaac.core.datasource -import ch.kleis.lcaac.cli.cmd.parseQuantityWithDefaultUnit -import ch.kleis.lcaac.core.datasource.DataSourceOperations +import ch.kleis.lcaac.core.lang.evaluator.EvaluatorException import ch.kleis.lcaac.core.lang.evaluator.reducer.DataExpressionReducer import ch.kleis.lcaac.core.lang.expression.* import ch.kleis.lcaac.core.lang.register.DataSourceRegister @@ -11,6 +10,7 @@ import ch.kleis.lcaac.core.prelude.Prelude import org.apache.commons.csv.CSVFormat import org.apache.commons.csv.CSVParser import java.io.File +import java.lang.Double.parseDouble import java.nio.file.Paths class BasicCsvSourceOperations( @@ -94,4 +94,13 @@ class BasicCsvSourceOperations( } } } + private fun parseQuantityWithDefaultUnit(s: String, defaultUnit: DataExpression): + DataExpression { + val amount = try { + parseDouble(s) + } catch (e: NumberFormatException) { + throw EvaluatorException("'$s' is not a valid number") + } + return EQuantityScale(BasicNumber(amount), defaultUnit) + } } From e55793b63fe0ee928cc032404df1e51385b4e3c4 Mon Sep 17 00:00:00 2001 From: Peva Blanchard Date: Sun, 21 Jan 2024 16:32:15 +0100 Subject: [PATCH 30/37] 1.3.5-rc2 --- gradle.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle.properties b/gradle.properties index 3c461d7b..d8cf8a83 100644 --- a/gradle.properties +++ b/gradle.properties @@ -2,4 +2,4 @@ javaVersion=17 gradleVersion=7.6 org.gradle.jvmargs=-Xmx4096m lcaacGroup=ch.kleis.lcaac -lcaacVersion=1.3.5-rc +lcaacVersion=1.3.5-rc2 From a2fb06b93fc5e8760d83ac63781e53f3da8f6276 Mon Sep 17 00:00:00 2001 From: Peva Blanchard Date: Sun, 21 Jan 2024 18:31:14 +0100 Subject: [PATCH 31/37] core: local variables in block for each --- .../evaluator/reducer/LcaExpressionReducer.kt | 4 +- .../core/lang/expression/LcaExpression.kt | 1 + .../expression/optics/LcaExpressionOptics.kt | 1 + .../reducer/LcaExpressionReducerTest.kt | 66 +++++++++++++++++++ 4 files changed, 71 insertions(+), 1 deletion(-) diff --git a/core/src/main/kotlin/ch/kleis/lcaac/core/lang/evaluator/reducer/LcaExpressionReducer.kt b/core/src/main/kotlin/ch/kleis/lcaac/core/lang/evaluator/reducer/LcaExpressionReducer.kt index 0bf76c32..dfc9f414 100644 --- a/core/src/main/kotlin/ch/kleis/lcaac/core/lang/evaluator/reducer/LcaExpressionReducer.kt +++ b/core/src/main/kotlin/ch/kleis/lcaac/core/lang/evaluator/reducer/LcaExpressionReducer.kt @@ -91,7 +91,9 @@ class LcaExpressionReducer( .flatMap { record -> val reducer = push(mapOf( DataKey(expression.rowRef) to record - )) + )).push( + expression.locals.mapKeys { DataKey(it.key) } + ) expression.body .flatMap { reducer.reduceTechnoBlock(it) } }.toList() diff --git a/core/src/main/kotlin/ch/kleis/lcaac/core/lang/expression/LcaExpression.kt b/core/src/main/kotlin/ch/kleis/lcaac/core/lang/expression/LcaExpression.kt index e2b826dc..7fc90cab 100644 --- a/core/src/main/kotlin/ch/kleis/lcaac/core/lang/expression/LcaExpression.kt +++ b/core/src/main/kotlin/ch/kleis/lcaac/core/lang/expression/LcaExpression.kt @@ -103,6 +103,7 @@ data class EBlockEntry(val entry: E) : BlockExpression { data class EBlockForEach( val rowRef: String, val dataSourceRef: String, + val locals: Map>, val body: List>, ) : BlockExpression { companion object diff --git a/core/src/main/kotlin/ch/kleis/lcaac/core/lang/expression/optics/LcaExpressionOptics.kt b/core/src/main/kotlin/ch/kleis/lcaac/core/lang/expression/optics/LcaExpressionOptics.kt index 3904ab00..fecc859b 100644 --- a/core/src/main/kotlin/ch/kleis/lcaac/core/lang/expression/optics/LcaExpressionOptics.kt +++ b/core/src/main/kotlin/ch/kleis/lcaac/core/lang/expression/optics/LcaExpressionOptics.kt @@ -26,6 +26,7 @@ inline fun BlockExpression.Companion.everyEntry(): Every EBlockForEach( source.rowRef, source.dataSourceRef, + source.locals, source.body.map { modify(it, map) } diff --git a/core/src/test/kotlin/ch/kleis/lcaac/core/lang/evaluator/reducer/LcaExpressionReducerTest.kt b/core/src/test/kotlin/ch/kleis/lcaac/core/lang/evaluator/reducer/LcaExpressionReducerTest.kt index 22ffa418..8d777d5c 100644 --- a/core/src/test/kotlin/ch/kleis/lcaac/core/lang/evaluator/reducer/LcaExpressionReducerTest.kt +++ b/core/src/test/kotlin/ch/kleis/lcaac/core/lang/evaluator/reducer/LcaExpressionReducerTest.kt @@ -20,12 +20,75 @@ class LcaExpressionReducerTest { Block */ + @Test + fun reduce_whenBlockForEach_withLocalVariables() { + // given + val block = ETechnoBlockForEach( + "row", + "source", + mapOf("x" to ERecordEntry(EDataRef("row"), "mass")), + listOf( + ETechnoBlockEntry( + ETechnoExchange( + EDataRef("x"), + ProductFixture.carrot, + ) + ), + ) + ) + val expression = EProcess( + name = "foo", + inputs = listOf(block), + ) + val sourceOps = mockk>() + every { sourceOps.readAll(any()) } returns sequenceOf( + ERecord(mapOf( + "mass" to QuantityFixture.oneKilogram, + )), + ERecord(mapOf( + "mass" to QuantityFixture.twoKilograms, + )), + ) + val reducer = LcaExpressionReducer( + DataRegister.empty(), + DataSourceRegister.from(mapOf( + DataSourceKey("source") to mockk(), + )), + ops, + sourceOps, + ) + + // when + val actual = reducer.reduce(expression) + + // then + val expected = EProcess( + name = "foo", + inputs = listOf( + ETechnoBlockEntry( + ETechnoExchange( + QuantityFixture.oneKilogram, + ProductFixture.carrot, + ) + ), + ETechnoBlockEntry( + ETechnoExchange( + QuantityFixture.twoKilograms, + ProductFixture.carrot, + ) + ), + ), + ) + assertEquals(expected, actual) + } + @Test fun reduce_whenBlockForEach_withRecordEntryOverride_shouldThrow() { // given val block = ETechnoBlockForEach( "row", "source", + emptyMap(), listOf( ETechnoBlockEntry( ETechnoExchange( @@ -70,6 +133,7 @@ class LcaExpressionReducerTest { val block = ETechnoBlockForEach( "row", "source", + emptyMap(), listOf( ETechnoBlockEntry( ETechnoExchange( @@ -131,6 +195,7 @@ class LcaExpressionReducerTest { val block = ETechnoBlockForEach( "row", "source", + emptyMap(), listOf( ETechnoBlockEntry( ETechnoExchange( @@ -141,6 +206,7 @@ class LcaExpressionReducerTest { EBlockForEach( "row2", "source", + emptyMap(), listOf( ETechnoBlockEntry( ETechnoExchange( From aa98af130ce69bc64a48232437159069b26a4eff Mon Sep 17 00:00:00 2001 From: Peva Blanchard Date: Sun, 21 Jan 2024 18:38:53 +0100 Subject: [PATCH 32/37] grammar: variables in block for_each --- grammar/src/main/antlr/LcaLang.g4 | 6 +++--- .../ch/kleis/lcaac/grammar/CoreMapper.kt | 15 ++++++++++++--- .../ch/kleis/lcaac/grammar/CoreMapperTest.kt | 18 ++++++++++++++++++ 3 files changed, 33 insertions(+), 6 deletions(-) diff --git a/grammar/src/main/antlr/LcaLang.g4 b/grammar/src/main/antlr/LcaLang.g4 index 889041ca..33f58062 100644 --- a/grammar/src/main/antlr/LcaLang.g4 +++ b/grammar/src/main/antlr/LcaLang.g4 @@ -200,18 +200,18 @@ block_impacts technoInputExchange : quantity=dataExpression product=inputProductSpec # technoEntry - | FOR_EACH_KEYWORD dataRef IN_KEYWORD dataSourceRef LBRACE technoInputExchange* RBRACE # technoBlockForEach + | FOR_EACH_KEYWORD dataRef IN_KEYWORD dataSourceRef LBRACE (variables | technoInputExchange)* RBRACE # technoBlockForEach ; technoProductExchange : quantity=dataExpression product=outputProductSpec ; bioExchange : quantity=dataExpression substance=substanceSpec # bioEntry - | FOR_EACH_KEYWORD dataRef IN_KEYWORD dataSourceRef LBRACE bioExchange* RBRACE # bioBlockForEach + | FOR_EACH_KEYWORD dataRef IN_KEYWORD dataSourceRef LBRACE (variables | bioExchange)* RBRACE # bioBlockForEach ; impactExchange : quantity=dataExpression indicator=indicatorRef # impactEntry - | FOR_EACH_KEYWORD dataRef IN_KEYWORD dataSourceRef LBRACE impactExchange* RBRACE # impactBlockForEach + | FOR_EACH_KEYWORD dataRef IN_KEYWORD dataSourceRef LBRACE (variables | impactExchange)* RBRACE # impactBlockForEach ; diff --git a/grammar/src/main/kotlin/ch/kleis/lcaac/grammar/CoreMapper.kt b/grammar/src/main/kotlin/ch/kleis/lcaac/grammar/CoreMapper.kt index c7011171..5c02eeb3 100644 --- a/grammar/src/main/kotlin/ch/kleis/lcaac/grammar/CoreMapper.kt +++ b/grammar/src/main/kotlin/ch/kleis/lcaac/grammar/CoreMapper.kt @@ -94,7 +94,10 @@ class CoreMapper( val rowRef = ctx.dataRef().innerText() val dataSourceRef = ctx.dataSourceRef().innerText() val body = ctx.impactExchange().map { this.impactExchange(it) } - EImpactBlockForEach(rowRef, dataSourceRef, body) + val locals = ctx.variables() + .flatMap { it.assignment() } + .associate { assignment(it) } + EImpactBlockForEach(rowRef, dataSourceRef, locals, body) } else -> throw IllegalStateException("parsing error: expecting an impact exchange context") @@ -127,7 +130,10 @@ class CoreMapper( val rowRef = ctx.dataRef().innerText() val dataSourceRef = ctx.dataSourceRef().innerText() val body = ctx.bioExchange().map { this.bioExchange(it, symbolTable, type) } - EBioBlockForEach(rowRef, dataSourceRef, body) + val locals = ctx.variables() + .flatMap { it.assignment() } + .associate { assignment(it) } + EBioBlockForEach(rowRef, dataSourceRef, locals, body) } else -> throw IllegalStateException("parsing error: expecting a bio exchange context") } @@ -179,7 +185,10 @@ class CoreMapper( val rowRef = ctx.dataRef().innerText() val dataSourceRef = ctx.dataSourceRef().innerText() val body = ctx.technoInputExchange().map { this.technoInputExchange(it) } - ETechnoBlockForEach(rowRef, dataSourceRef, body) + val locals = ctx.variables() + .flatMap { it.assignment() } + .associate { assignment(it) } + ETechnoBlockForEach(rowRef, dataSourceRef, locals, body) } else -> throw IllegalStateException("parsing error: expecting a techno input exchange context") diff --git a/grammar/src/test/kotlin/ch/kleis/lcaac/grammar/CoreMapperTest.kt b/grammar/src/test/kotlin/ch/kleis/lcaac/grammar/CoreMapperTest.kt index b42821c0..b266f788 100644 --- a/grammar/src/test/kotlin/ch/kleis/lcaac/grammar/CoreMapperTest.kt +++ b/grammar/src/test/kotlin/ch/kleis/lcaac/grammar/CoreMapperTest.kt @@ -112,6 +112,9 @@ class CoreMapperTest { // given val ctx = LcaLangFixture.parser(""" for_each row in source { + variables { + x = 1 l + } 1 kg co2 } """.trimIndent()).technoInputExchange() @@ -124,6 +127,9 @@ class CoreMapperTest { val expected = ETechnoBlockForEach( "row", "source", + mapOf( + "x" to EQuantityScale(BasicNumber(1.0), EDataRef("l")) + ), listOf( ETechnoBlockEntry( ETechnoExchange( @@ -141,6 +147,9 @@ class CoreMapperTest { // given val ctx = LcaLangFixture.parser(""" for_each row in source { + variables { + x = 1 l + } 1 kg co2 } """.trimIndent()).impactExchange() @@ -153,6 +162,9 @@ class CoreMapperTest { val expected = EImpactBlockForEach( "row", "source", + mapOf( + "x" to EQuantityScale(BasicNumber(1.0), EDataRef("l")) + ), listOf( EImpactBlockEntry( EImpact( @@ -170,6 +182,9 @@ class CoreMapperTest { // given val ctx = LcaLangFixture.parser(""" for_each row in source { + variables { + x = 1 l + } 1 kg co2(compartment="air") } """.trimIndent()).bioExchange() @@ -185,6 +200,9 @@ class CoreMapperTest { val expected = EBioBlockForEach( "row", "source", + mapOf( + "x" to EQuantityScale(BasicNumber(1.0), EDataRef("l")) + ), listOf( EBioBlockEntry( EBioExchange( From f3c804e615908740b27c2fbcb81fa229157ad374 Mon Sep 17 00:00:00 2001 From: Peva Blanchard Date: Sun, 21 Jan 2024 18:40:17 +0100 Subject: [PATCH 33/37] lca-1.3.5-rc3 --- gradle.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle.properties b/gradle.properties index d8cf8a83..5886b8d1 100644 --- a/gradle.properties +++ b/gradle.properties @@ -2,4 +2,4 @@ javaVersion=17 gradleVersion=7.6 org.gradle.jvmargs=-Xmx4096m lcaacGroup=ch.kleis.lcaac -lcaacVersion=1.3.5-rc2 +lcaacVersion=1.3.5-rc3 From 58dcef66d950c6a9716efac8acd2e523f048e7a0 Mon Sep 17 00:00:00 2001 From: Peva Blanchard Date: Sun, 21 Jan 2024 22:05:18 +0100 Subject: [PATCH 34/37] core: csv source operations with generic Q --- ...ceOperations.kt => CsvSourceOperations.kt} | 20 ++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) rename core/src/main/kotlin/ch/kleis/lcaac/core/datasource/{BasicCsvSourceOperations.kt => CsvSourceOperations.kt} (90%) diff --git a/core/src/main/kotlin/ch/kleis/lcaac/core/datasource/BasicCsvSourceOperations.kt b/core/src/main/kotlin/ch/kleis/lcaac/core/datasource/CsvSourceOperations.kt similarity index 90% rename from core/src/main/kotlin/ch/kleis/lcaac/core/datasource/BasicCsvSourceOperations.kt rename to core/src/main/kotlin/ch/kleis/lcaac/core/datasource/CsvSourceOperations.kt index 2fdcce76..27568961 100644 --- a/core/src/main/kotlin/ch/kleis/lcaac/core/datasource/BasicCsvSourceOperations.kt +++ b/core/src/main/kotlin/ch/kleis/lcaac/core/datasource/CsvSourceOperations.kt @@ -4,6 +4,7 @@ import ch.kleis.lcaac.core.lang.evaluator.EvaluatorException import ch.kleis.lcaac.core.lang.evaluator.reducer.DataExpressionReducer import ch.kleis.lcaac.core.lang.expression.* import ch.kleis.lcaac.core.lang.register.DataSourceRegister +import ch.kleis.lcaac.core.math.QuantityOperations import ch.kleis.lcaac.core.math.basic.BasicNumber import ch.kleis.lcaac.core.math.basic.BasicOperations import ch.kleis.lcaac.core.prelude.Prelude @@ -13,15 +14,16 @@ import java.io.File import java.lang.Double.parseDouble import java.nio.file.Paths -class BasicCsvSourceOperations( +class CsvSourceOperations( private val path: File, -) : DataSourceOperations { + private val ops: QuantityOperations, +) : DataSourceOperations { private val format = CSVFormat.DEFAULT.builder() .setHeader() .setSkipHeaderRecord(true) .build() - override fun readAll(source: DataSourceExpression): Sequence> { + override fun readAll(source: DataSourceExpression): Sequence> { return when (source) { is ECsvSource -> { val location = Paths.get(path.absolutePath, source.location) @@ -56,13 +58,13 @@ class BasicCsvSourceOperations( } override fun sumProduct( - source: DataSourceExpression, + source: DataSourceExpression, columns: List, - ): DataExpression { + ): DataExpression { val reducer = DataExpressionReducer( dataRegister = Prelude.units(), dataSourceRegister = DataSourceRegister.empty(), - ops = BasicOperations, + ops = ops, sourceOps = this, ) return when (source) { @@ -94,13 +96,13 @@ class BasicCsvSourceOperations( } } } - private fun parseQuantityWithDefaultUnit(s: String, defaultUnit: DataExpression): - DataExpression { + private fun parseQuantityWithDefaultUnit(s: String, defaultUnit: DataExpression): + DataExpression { val amount = try { parseDouble(s) } catch (e: NumberFormatException) { throw EvaluatorException("'$s' is not a valid number") } - return EQuantityScale(BasicNumber(amount), defaultUnit) + return EQuantityScale(ops.pure(amount), defaultUnit) } } From f486909b36ef486d1e7523e19f554206e8450f2b Mon Sep 17 00:00:00 2001 From: Peva Blanchard Date: Sun, 21 Jan 2024 22:05:25 +0100 Subject: [PATCH 35/37] cli: fixes --- .../main/kotlin/ch/kleis/lcaac/cli/cmd/TestCommand.kt | 9 ++++++--- .../main/kotlin/ch/kleis/lcaac/cli/csv/CsvProcessor.kt | 4 ++-- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/cli/src/main/kotlin/ch/kleis/lcaac/cli/cmd/TestCommand.kt b/cli/src/main/kotlin/ch/kleis/lcaac/cli/cmd/TestCommand.kt index cbdb6759..59d483c6 100644 --- a/cli/src/main/kotlin/ch/kleis/lcaac/cli/cmd/TestCommand.kt +++ b/cli/src/main/kotlin/ch/kleis/lcaac/cli/cmd/TestCommand.kt @@ -1,6 +1,6 @@ package ch.kleis.lcaac.cli.cmd -import ch.kleis.lcaac.core.datasource.BasicCsvSourceOperations +import ch.kleis.lcaac.core.datasource.CsvSourceOperations import ch.kleis.lcaac.core.math.basic.BasicOperations import ch.kleis.lcaac.core.testing.BasicTestRunner import ch.kleis.lcaac.core.testing.GenericFailure @@ -33,12 +33,15 @@ class TestCommand : CliktCommand(name = "test", help = "Run specified tests") { override fun run() { val files = lcaFiles(path) - val symbolTable = Loader(BasicOperations).load(files, listOf(LoaderOption.WITH_PRELUDE)) + val ops = BasicOperations + val symbolTable = Loader(ops).load(files, listOf(LoaderOption.WITH_PRELUDE)) val mapper = CoreTestMapper() val cases = files .flatMap { it.testDefinition() } .map { mapper.test(it) } - val runner = BasicTestRunner(symbolTable, BasicCsvSourceOperations(path)) + val runner = BasicTestRunner( + symbolTable, + CsvSourceOperations(path, ops)) val results = cases.map { runner.run(it) } results.forEach { result -> diff --git a/cli/src/main/kotlin/ch/kleis/lcaac/cli/csv/CsvProcessor.kt b/cli/src/main/kotlin/ch/kleis/lcaac/cli/csv/CsvProcessor.kt index 11c14b6c..f064abb8 100644 --- a/cli/src/main/kotlin/ch/kleis/lcaac/cli/csv/CsvProcessor.kt +++ b/cli/src/main/kotlin/ch/kleis/lcaac/cli/csv/CsvProcessor.kt @@ -2,7 +2,7 @@ package ch.kleis.lcaac.cli.csv import ch.kleis.lcaac.cli.cmd.smartParseQuantityWithDefaultUnit import ch.kleis.lcaac.core.assessment.ContributionAnalysisProgram -import ch.kleis.lcaac.core.datasource.BasicCsvSourceOperations +import ch.kleis.lcaac.core.datasource.CsvSourceOperations import ch.kleis.lcaac.core.lang.SymbolTable import ch.kleis.lcaac.core.lang.evaluator.Evaluator import ch.kleis.lcaac.core.lang.evaluator.EvaluatorException @@ -19,7 +19,7 @@ class CsvProcessor( private val symbolTable: SymbolTable, ) { private val ops = BasicOperations - private val sourceOps = BasicCsvSourceOperations(path) + private val sourceOps = CsvSourceOperations(path, ops) private val evaluator = Evaluator(symbolTable, ops, sourceOps) fun process(request: CsvRequest): List { From 25a7b43c9b71c5b2df19afcf529b82fa0668c5cd Mon Sep 17 00:00:00 2001 From: Peva Blanchard Date: Sun, 21 Jan 2024 22:06:11 +0100 Subject: [PATCH 36/37] bump version 1.3.5-rc3 -> 1.4-rc --- gradle.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle.properties b/gradle.properties index 5886b8d1..2c53062c 100644 --- a/gradle.properties +++ b/gradle.properties @@ -2,4 +2,4 @@ javaVersion=17 gradleVersion=7.6 org.gradle.jvmargs=-Xmx4096m lcaacGroup=ch.kleis.lcaac -lcaacVersion=1.3.5-rc3 +lcaacVersion=1.4-rc From 1fa55e1ad2f10836c36f4cda0be880a64a9f1cd6 Mon Sep 17 00:00:00 2001 From: Peva Blanchard Date: Tue, 23 Jan 2024 12:37:41 +0100 Subject: [PATCH 37/37] bump version 1.4-rc -> 1.4 --- gradle.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle.properties b/gradle.properties index 2c53062c..d3d05b3d 100644 --- a/gradle.properties +++ b/gradle.properties @@ -2,4 +2,4 @@ javaVersion=17 gradleVersion=7.6 org.gradle.jvmargs=-Xmx4096m lcaacGroup=ch.kleis.lcaac -lcaacVersion=1.4-rc +lcaacVersion=1.4