From 9e0bbacad3bb0f7263a7cef993595bca4edf2341 Mon Sep 17 00:00:00 2001 From: Kamil Adam Date: Tue, 31 Oct 2023 21:50:09 +0100 Subject: [PATCH] Add interpret for combinators --- .../calculators/CombinatorCalculator.scala | 20 ++++++--- .../core/calculators/lazyk/Calculator.scala | 44 +++++++++++++++++++ .../core/calculators/lazyk/Evaluator.scala | 17 +++++++ .../core/reducers/AbstractionReducer.scala | 3 ++ .../calculators/lazyk/CalculatorSpec.scala | 42 ++++++++++++++++++ .../catculator/core/parsers/HaskellSpec.scala | 8 ++++ .../catculator/core/parsers/LambdaSpec.scala | 1 - .../catculator/core/reducers/LambdaTest.scala | 2 +- 8 files changed, 130 insertions(+), 7 deletions(-) create mode 100644 catculator-core/src/main/scala/pl/writeonly/catculator/core/calculators/lazyk/Calculator.scala create mode 100644 catculator-core/src/main/scala/pl/writeonly/catculator/core/calculators/lazyk/Evaluator.scala create mode 100644 catculator-core/src/test/scala/pl/writeonly/catculator/core/calculators/lazyk/CalculatorSpec.scala diff --git a/catculator-core/src/main/scala/pl/writeonly/catculator/core/calculators/CombinatorCalculator.scala b/catculator-core/src/main/scala/pl/writeonly/catculator/core/calculators/CombinatorCalculator.scala index 37a252d..398b4b2 100644 --- a/catculator-core/src/main/scala/pl/writeonly/catculator/core/calculators/CombinatorCalculator.scala +++ b/catculator-core/src/main/scala/pl/writeonly/catculator/core/calculators/CombinatorCalculator.scala @@ -1,14 +1,24 @@ package pl.writeonly.catculator.core.calculators -import pl.writeonly.catculator.core.adt.calculus.Combinator.CombinatorBT -import pl.writeonly.catculator.core.adt.tree.BinaryTree.Leaf -import pl.writeonly.catculator.core.adt.tree.BinaryTree.Node +import pl.writeonly.catculator.core.adt.calculus.Combinator._ +import pl.writeonly.catculator.core.adt.tree.BinaryTree._ object CombinatorCalculator { - def calculate(c: CombinatorBT): Unit = c match { + def interpret(c: CombinatorBT): CombinatorBT = c match { + case l @ Leaf(_) => l + case Node(Leaf(I), x) => x + case Node(Leaf(K), x) => Leaf(K) + case Node(Node(Leaf(K), x), y) => x + case Node(Node(Node(Leaf(S), x), y), z) => + val first = Node(x, z) + val second = Node(y, z) + Node(first, second) + case Node(f, x) => - case Leaf(a) => + val reducedF = interpret(f) + val reducedX = interpret(x) + interpret(Node(reducedF, reducedX)) } } diff --git a/catculator-core/src/main/scala/pl/writeonly/catculator/core/calculators/lazyk/Calculator.scala b/catculator-core/src/main/scala/pl/writeonly/catculator/core/calculators/lazyk/Calculator.scala new file mode 100644 index 0000000..a55f4e8 --- /dev/null +++ b/catculator-core/src/main/scala/pl/writeonly/catculator/core/calculators/lazyk/Calculator.scala @@ -0,0 +1,44 @@ +package pl.writeonly.catculator.core.calculators.lazyk + +import pl.writeonly.catculator.core.adt.calculus.Combinator +import pl.writeonly.catculator.core.adt.calculus.Combinator._ +import pl.writeonly.catculator.core.adt.tree.BinaryTree +import pl.writeonly.catculator.core.adt.tree.BinaryTree._ +import pl.writeonly.catculator.core.calculators.lazyk.ADT._ +import pl.writeonly.catculator.core.calculators.lazyk.Reducer.flippedApply +import spire.math.Natural + +object Calculator { + + val number0: ADTBT = Leaf(ADT.Num(Natural(0))) + + val trueVar: ADTBT = Leaf(ADT.Com(K)) + + val falseVar: ADTBT = Node(trueVar, Leaf(ADT.Com(I))) + + def run(program: ADTBT): Iterator[Safe[Natural]] = runWithTerminator(falseVar, program) + + private def runWithTerminator(terminator: ADTBT, combinator: ADTBT): Iterator[Safe[Natural]] = + println("runWithTerminator2") + Iterator.unfold[Safe[Natural], (ADTBT, Safe[ADTBT])](terminator, Right(combinator)) { case (t: ADTBT, cM: Safe[ADTBT]) => + val a = cM.flatMap(realizeWithTrue) + val s = cM.flatMap(Reducer.flippedApply(t, _)) + Option(a, (t, s)) + } + + private def runWithTerminatorNumber(terminator: ADTBT, combinator: ADTBT): Safe[Unit] = realizeWithTrue(combinator).flatMap(output(terminator, combinator, _)) + + def realizeWithTrue(combinator: ADTBT): Safe[Natural] = flippedApply(trueVar, combinator).flatMap(realize) + + def realize(combinator: ADTBT): Safe[Natural] = flippedApply(Leaf(ADT.Succ()), combinator).flatMap(flippedApply(number0, _)).flatMap(naturalSafe) + + private def naturalSafe(combinator: ADTBT): Safe[Natural] = combinator match { + case Leaf(ADT.Num(x)) => Right(x) + case x => Left(s"Invalid output format. Output should be the list of Church numerals. $x") + } + + private def output(terminator: ADTBT, combinator: ADTBT, number: Natural): Safe[Unit] = Reducer + .apply(combinator, terminator) + .flatMap(runWithTerminatorNumber(terminator, _)) + +} diff --git a/catculator-core/src/main/scala/pl/writeonly/catculator/core/calculators/lazyk/Evaluator.scala b/catculator-core/src/main/scala/pl/writeonly/catculator/core/calculators/lazyk/Evaluator.scala new file mode 100644 index 0000000..1207c99 --- /dev/null +++ b/catculator-core/src/main/scala/pl/writeonly/catculator/core/calculators/lazyk/Evaluator.scala @@ -0,0 +1,17 @@ +package pl.writeonly.catculator.core.calculators.lazyk + +import pl.writeonly.catculator.core.adt.calculus.InputEncoder._ +import pl.writeonly.catculator.core.adt.tree.BinaryTree.Node +import pl.writeonly.catculator.core.calculators.lazyk.ADT.ADTBT +import pl.writeonly.catculator.core.calculators.lazyk.ADT.Safe +import pl.writeonly.catculator.core.calculators.lazyk.ADT.fromCombinatorBT +import pl.writeonly.catculator.core.calculators.lazyk.Calculator.run +import pl.writeonly.catculator.core.calculators.lazyk.Reducer.reduce + +object Evaluator { + + def evalCombinator(combinator: ADTBT, input: String): Safe[String] = + for c <- reduce(Node(combinator, fromCombinatorBT(readInput(input)))) + yield run(c).map(_.toOption.get.toBigInt.toInt.toChar).mkString + +} diff --git a/catculator-core/src/main/scala/pl/writeonly/catculator/core/reducers/AbstractionReducer.scala b/catculator-core/src/main/scala/pl/writeonly/catculator/core/reducers/AbstractionReducer.scala index e8680c2..bdc5d07 100644 --- a/catculator-core/src/main/scala/pl/writeonly/catculator/core/reducers/AbstractionReducer.scala +++ b/catculator-core/src/main/scala/pl/writeonly/catculator/core/reducers/AbstractionReducer.scala @@ -2,8 +2,11 @@ package pl.writeonly.catculator.core.reducers import pl.writeonly.catculator.core.Extras.ifElse import pl.writeonly.catculator.core.adt.calculus.Combinator._ +import pl.writeonly.catculator.core.adt.calculus.Combinator._ import pl.writeonly.catculator.core.adt.calculus.Lambda import pl.writeonly.catculator.core.adt.calculus.Lambda._ +import pl.writeonly.catculator.core.adt.calculus.Lambda._ +import pl.writeonly.catculator.core.adt.calculus._ import spire.implicits.eqOps object AbstractionReducer { diff --git a/catculator-core/src/test/scala/pl/writeonly/catculator/core/calculators/lazyk/CalculatorSpec.scala b/catculator-core/src/test/scala/pl/writeonly/catculator/core/calculators/lazyk/CalculatorSpec.scala new file mode 100644 index 0000000..e1e89a7 --- /dev/null +++ b/catculator-core/src/test/scala/pl/writeonly/catculator/core/calculators/lazyk/CalculatorSpec.scala @@ -0,0 +1,42 @@ +package pl.writeonly.catculator.core.calculators.lazyk + +import mouse.all.anySyntaxMouse +import org.scalatest.prop.TableFor1 +import pl.writeonly.catculator.core.TableDrivenPropertySpec +import pl.writeonly.catculator.core.adt.calculus.Combinator.CombinatorBT +import pl.writeonly.catculator.core.adt.calculus.Constants._ +import pl.writeonly.catculator.core.adt.calculus.InputEncoder._ +import pl.writeonly.catculator.core.calculators.lazyk.ADT._ +import spire.math.Natural + +class CalculatorSpec extends TableDrivenPropertySpec { + + val numbers: TableFor1[Long] = Table("number", 0, 1, 4, 8, 9, 16, 27, 36, 64, 81, 100, 121, 125, 256) + + def realizeFromCombinatorBT(c: CombinatorBT): Safe[Natural] = c |> fromCombinatorBT |> Calculator.realize + + it should "realize false" in { + val result = realizeFromCombinatorBT(falseCom) + result.value shouldBe Natural.zero + } + + it should "realize I" in { + val result = realizeFromCombinatorBT(iCom) + result.value shouldBe Natural.one + } + + it should "realize successor false" in { + val c = successor(falseCom) + val result = realizeFromCombinatorBT(c) + result.value shouldBe Natural.one + } + + it should "realize numbers" in { + forAll(numbers) { number => + val natural = Natural(number) + val c = church(natural) + val result = realizeFromCombinatorBT(c) + result.value shouldBe natural + } + } +} diff --git a/catculator-core/src/test/scala/pl/writeonly/catculator/core/parsers/HaskellSpec.scala b/catculator-core/src/test/scala/pl/writeonly/catculator/core/parsers/HaskellSpec.scala index 2dc647e..0818a8f 100644 --- a/catculator-core/src/test/scala/pl/writeonly/catculator/core/parsers/HaskellSpec.scala +++ b/catculator-core/src/test/scala/pl/writeonly/catculator/core/parsers/HaskellSpec.scala @@ -4,7 +4,15 @@ import mouse.all.anySyntaxMouse import org.scalatest.prop._ import pl.writeonly.catculator.core.LambdaConfig._ import pl.writeonly.catculator.core.TableDrivenPropertySpec +import pl.writeonly.catculator.core.adt.calculus.Combinator.I import pl.writeonly.catculator.core.adt.calculus.Lambda._ +import pl.writeonly.catculator.core.adt.calculus.Lambda._ +import pl.writeonly.catculator.core.adt.tree.BinaryTree.Leaf +import pl.writeonly.catculator.core.calculators.lazyk.ADT +import pl.writeonly.catculator.core.calculators.lazyk.ADT.ADTBT +import pl.writeonly.catculator.core.calculators.lazyk.ADT.fromCombinatorBT +import pl.writeonly.catculator.core.calculators.lazyk.Calculator +import pl.writeonly.catculator.core.calculators.lazyk.Evaluator import pl.writeonly.catculator.core.generators.HaskellGenerator import pl.writeonly.catculator.core.parsers.HaskellParser import pl.writeonly.catculator.core.reducers.AbstractionReducer.reduceAbstraction diff --git a/catculator-core/src/test/scala/pl/writeonly/catculator/core/parsers/LambdaSpec.scala b/catculator-core/src/test/scala/pl/writeonly/catculator/core/parsers/LambdaSpec.scala index 7ec1e6e..d4c09b6 100644 --- a/catculator-core/src/test/scala/pl/writeonly/catculator/core/parsers/LambdaSpec.scala +++ b/catculator-core/src/test/scala/pl/writeonly/catculator/core/parsers/LambdaSpec.scala @@ -5,7 +5,6 @@ import pl.writeonly.catculator.core.TableDrivenPropertySpec import pl.writeonly.catculator.core.adt.calculus.Lambda import pl.writeonly.catculator.core.adt.calculus.Lambda._ import pl.writeonly.catculator.core.generators.LambdaGenerator -import pl.writeonly.catculator.core.parsers.LambdaParser import pl.writeonly.catculator.core.reducers.AbstractionReducer.reduceAbstraction import pl.writeonly.catculator.core.reducers.SugarReducer._ diff --git a/catculator-core/src/test/scala/pl/writeonly/catculator/core/reducers/LambdaTest.scala b/catculator-core/src/test/scala/pl/writeonly/catculator/core/reducers/LambdaTest.scala index 08fbc63..f4ef2f6 100644 --- a/catculator-core/src/test/scala/pl/writeonly/catculator/core/reducers/LambdaTest.scala +++ b/catculator-core/src/test/scala/pl/writeonly/catculator/core/reducers/LambdaTest.scala @@ -3,7 +3,7 @@ package pl.writeonly.catculator.core.reducers import pl.writeonly.catculator.core.UnitSpec import pl.writeonly.catculator.core.adt.calculus.Combinator.I import pl.writeonly.catculator.core.adt.calculus.Lambda.Com -import pl.writeonly.catculator.core.reducers.SugarReducer +import pl.writeonly.catculator.core.parsers.LambdaParser import pl.writeonly.catculator.core.reducers.SugarReducer.lambdaSugarReducer class LambdaTest extends UnitSpec {