├── growl.sbt ├── project ├── build.properties ├── growl.sbt └── plugins.sbt ├── .gitignore ├── src ├── test │ └── scala │ │ └── ilc │ │ ├── util │ │ ├── Util.scala │ │ ├── EvalScalaSuite.scala │ │ └── process │ │ │ └── FunProcessSuite.scala │ │ ├── howTo │ │ └── GetConstraintsOutOfConstantsSuite.scala │ │ ├── language │ │ ├── bacchus │ │ │ ├── BacchusBasicDerivationSuite.scala │ │ │ ├── ChangingTerms.scala │ │ │ ├── TypeCheckingSuite.scala │ │ │ ├── BacchusPrettySuite.scala │ │ │ ├── ReificationSuite.scala │ │ │ ├── CorrectnessAssertion.scala │ │ │ ├── Subjects.scala │ │ │ ├── BacchusEvaluationSuite.scala │ │ │ └── BacchusToScalaSuite.scala │ │ └── testbed │ │ │ └── FullPrettySuite.scala │ │ ├── examples │ │ ├── FastBenchmarksFlag.scala │ │ └── BenchData.scala │ │ ├── metaprogs │ │ ├── CachingDeriveSuite.scala │ │ └── MemoizeSpec.scala │ │ ├── feature │ │ ├── maps │ │ │ ├── MapsPrettySuite.scala │ │ │ └── DeltaMapSuite.scala │ │ ├── base │ │ │ ├── PrettySuite.scala │ │ │ ├── TermBuilderSuite.scala │ │ │ └── TypeInversionTest.scala │ │ ├── bags │ │ │ ├── DeltaBagSuite.scala │ │ │ └── BagChanges.scala │ │ ├── products │ │ │ ├── ProductToScalaSuite.scala │ │ │ └── ProductSugarSuite.scala │ │ ├── sums │ │ │ └── DeltaSumSuite.scala │ │ ├── integers │ │ │ └── IntegerSuite.scala │ │ ├── maybe │ │ │ └── DeltaMaybeSuite.scala │ │ ├── functions │ │ │ └── PrettySuite.scala │ │ ├── abelianMaps │ │ │ └── MapChanges.scala │ │ ├── cbpv │ │ │ └── CBPVTest.sc │ │ └── let │ │ │ └── Instantiations.scala │ │ └── analysis │ │ └── StabilitySuite.scala └── main │ └── scala │ └── ilc │ ├── feature │ ├── unit │ │ ├── Types.scala │ │ ├── Syntax.scala │ │ ├── Pretty.scala │ │ ├── ToScala.scala │ │ ├── Evaluation.scala │ │ └── Derivation.scala │ ├── base │ │ ├── Library.scala │ │ ├── Reification.scala │ │ ├── Context.scala │ │ ├── ContextSensitiveDerivation.scala │ │ ├── FreshGen.scala │ │ ├── Types.scala │ │ ├── ToScala.scala │ │ └── Evaluation.scala │ ├── booleans │ │ ├── Types.scala │ │ ├── ToScala.scala │ │ ├── Derivation.scala │ │ ├── Evaluation.scala │ │ └── Syntax.scala │ ├── integers │ │ ├── Types.scala │ │ ├── Pretty.scala │ │ ├── ToScala.scala │ │ ├── Library.scala │ │ ├── AbelianDerivation.scala │ │ ├── Evaluation.scala │ │ └── Syntax.scala │ ├── naturals │ │ ├── Types.scala │ │ ├── Pretty.scala │ │ ├── Reification.scala │ │ ├── ToScala.scala │ │ ├── Syntax.scala │ │ ├── ReplacementValuesDerivation.scala │ │ └── Evaluation.scala │ ├── bags │ │ ├── Types.scala │ │ ├── ToScala.scala │ │ └── Derivation.scala │ ├── lists │ │ ├── Types.scala │ │ ├── Pretty.scala │ │ └── Syntax.scala │ ├── bintrees │ │ ├── Types.scala │ │ ├── Pretty.scala │ │ └── Syntax.scala │ ├── maybe │ │ ├── Types.scala │ │ ├── Reification.scala │ │ ├── ToScala.scala │ │ ├── Evaluation.scala │ │ ├── ReplacementValuesDerivation.scala │ │ └── Syntax.scala │ ├── fixpoint │ │ ├── Library.scala │ │ ├── Syntax.scala │ │ ├── ToScala.scala │ │ └── Derivation.scala │ ├── maps │ │ ├── Types.scala │ │ ├── Reification.scala │ │ ├── ToScala.scala │ │ └── Evaluation.scala │ ├── sums │ │ ├── Types.scala │ │ ├── Reification.scala │ │ ├── Pretty.scala │ │ ├── ToScala.scala │ │ ├── Evaluation.scala │ │ └── ReplacementValuesDerivation.scala │ ├── let │ │ ├── IsAtomic.scala │ │ ├── Syntax.scala │ │ ├── Evaluation.scala │ │ ├── FreeVariables.scala │ │ ├── Derivation.scala │ │ ├── ToScala.scala │ │ ├── FullErasure.scala │ │ ├── Pretty.scala │ │ ├── ToHaskell.scala │ │ └── Traversals.scala │ ├── abelianGroups │ │ ├── Types.scala │ │ ├── Derivation.scala │ │ ├── ToScala.scala │ │ ├── Library.scala │ │ ├── Evaluation.scala │ │ └── Syntax.scala │ ├── functions │ │ ├── IsAtomic.scala │ │ ├── LetRecSyntax.scala │ │ ├── LambdaDelta.scala │ │ ├── Evaluation.scala │ │ ├── Types.scala │ │ ├── ContextSensitiveDerivation.scala │ │ ├── ToScala.scala │ │ ├── Derivation.scala │ │ ├── Context.scala │ │ └── Pretty.scala │ ├── memoize │ │ ├── Syntax.scala │ │ ├── ToScala.scala │ │ └── Library.scala │ ├── abelianMaps │ │ ├── ToScala.scala │ │ ├── AbelianDerivation.scala │ │ ├── Library.scala │ │ └── Syntax.scala │ ├── products │ │ ├── ToScala.scala │ │ ├── Pretty.scala │ │ ├── Evaluation.scala │ │ ├── Types.scala │ │ └── Derivation.scala │ ├── inference │ │ ├── Reflection.scala │ │ ├── SyntaxSugar.scala │ │ └── UntypedSyntax.scala │ ├── equality │ │ └── equality.scala │ └── cbpv │ │ └── CBPVToCPSTypes.scala │ ├── UnicodeOutput.scala │ ├── language │ ├── letLanguage │ │ └── Evaluation.scala │ ├── Bacchus.scala │ ├── bacchus │ │ ├── Pretty.scala │ │ ├── Evaluation.scala │ │ ├── ToScala.scala │ │ ├── Syntax.scala │ │ ├── Prelude.scala │ │ ├── BasicDerivation.scala │ │ └── FineGrainedDifference.scala │ └── LetLanguage.scala │ ├── QuickAndDirty.scala │ ├── TypeChecking.scala │ ├── util │ ├── BooleanFlag.scala │ ├── ExtractorTrait.scala │ ├── UnionType.scala │ ├── IndentUtils.scala │ ├── EvalScala.scala │ └── process │ │ └── FunProcess.scala │ ├── examples │ ├── ExampleGenerated.scala │ ├── Dummy.scala │ └── Generator.scala │ ├── analysis │ └── FreeVariables.scala │ └── metaprogs │ └── AlphaEquiv.scala ├── bigClients ├── src │ ├── main │ │ └── scala │ │ │ └── ilc │ │ │ └── Examples.scala │ └── test │ │ └── scala │ │ ├── ilc │ │ └── examples │ │ │ ├── verification.scala │ │ │ └── HistogramGeneratedSuite.scala │ │ └── longRunning │ │ └── BenchSuite.scala └── build.sbt ├── clients ├── build.sbt └── src │ ├── test │ └── scala │ │ ├── longRunning │ │ ├── evalScala │ │ │ └── BooleanSuiteCompile.scala │ │ ├── MapSuccBaseBenchmark.scala │ │ ├── MapSuccBagsBenchmark.scala │ │ ├── MapSuccBenchmark.scala │ │ ├── SumValuesBenchmark.scala │ │ └── BagUnionBenchmark.scala │ │ └── ilc │ │ ├── util │ │ └── EvalScalaSuite.scala │ │ ├── metaprogs │ │ └── ClientsMemoizeSpec.scala │ │ └── examples │ │ ├── verification.scala │ │ ├── MapSuccGeneratedSuite.scala │ │ └── MapReduceSuite.scala │ └── main │ └── scala │ └── ilc │ ├── examples │ ├── MapSuccExample.scala │ ├── MapSuccBaseExample.scala │ ├── BagUnionExample.scala │ ├── HistogramExample.scala │ ├── SumValuesExample.scala │ ├── GroupByExample.scala │ ├── DBToasterExamples.scala │ └── MapReduce.scala │ └── Examples.scala ├── icfp2014 └── src │ ├── main │ └── scala │ │ └── ilc │ │ └── language │ │ ├── gcc │ │ ├── Pretty.scala │ │ └── Evaluation.scala │ │ └── GCC.scala │ └── test │ └── scala │ └── ilc │ └── language │ └── gcc │ ├── CollectionsSuite.scala │ └── PrimitivesSuite.scala ├── .travis.yml └── LICENSE.txt /growl.sbt: -------------------------------------------------------------------------------- 1 | growlSettings 2 | -------------------------------------------------------------------------------- /project/build.properties: -------------------------------------------------------------------------------- 1 | sbt.version=0.13.9 2 | -------------------------------------------------------------------------------- /project/growl.sbt: -------------------------------------------------------------------------------- 1 | addSbtPlugin("me.lessis" % "sbt-growl-plugin" % "0.1.3") 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | project/target 2 | target 3 | tmp 4 | .classpath 5 | .project 6 | .settings/ 7 | /.worksheet 8 | /bin/ 9 | -------------------------------------------------------------------------------- /src/test/scala/ilc/util/Util.scala: -------------------------------------------------------------------------------- 1 | package ilc.util 2 | 3 | object Util { 4 | def assertType[T](t: T) {} 5 | def assertTypeAndRet[T](t: T) = t 6 | } -------------------------------------------------------------------------------- /project/plugins.sbt: -------------------------------------------------------------------------------- 1 | addSbtPlugin("com.typesafe.sbteclipse" % "sbteclipse-plugin" % "3.0.0") 2 | 3 | scalacOptions := Seq("-deprecation", "-feature") 4 | 5 | -------------------------------------------------------------------------------- /bigClients/src/main/scala/ilc/Examples.scala: -------------------------------------------------------------------------------- 1 | package ilc 2 | 3 | import examples._ 4 | 5 | object Examples 6 | extends Generator { 7 | addExample(new HistogramExample) 8 | } 9 | -------------------------------------------------------------------------------- /src/main/scala/ilc/feature/unit/Types.scala: -------------------------------------------------------------------------------- 1 | package ilc 2 | package feature 3 | package unit 4 | 5 | trait Types extends base.Types { 6 | case object UnitType extends Type 7 | } 8 | -------------------------------------------------------------------------------- /clients/build.sbt: -------------------------------------------------------------------------------- 1 | //Imported from Build.scala 2 | generationSettings 3 | 4 | //Also include generated sources. 5 | EclipseKeys.createSrc := EclipseCreateSrc.Default + EclipseCreateSrc.Managed 6 | -------------------------------------------------------------------------------- /bigClients/build.sbt: -------------------------------------------------------------------------------- 1 | //Imported from Build.scala 2 | generationSettings 3 | 4 | //Also include generated sources. 5 | EclipseKeys.createSrc := EclipseCreateSrc.Default + EclipseCreateSrc.Managed 6 | -------------------------------------------------------------------------------- /src/main/scala/ilc/feature/base/Library.scala: -------------------------------------------------------------------------------- 1 | package ilc 2 | package feature 3 | package base 4 | 5 | /** trait of all libraries for generated code */ 6 | 7 | trait Library 8 | extends Serializable 9 | -------------------------------------------------------------------------------- /icfp2014/src/main/scala/ilc/language/gcc/Pretty.scala: -------------------------------------------------------------------------------- 1 | package ilc 2 | package language 3 | package gcc 4 | 5 | import feature._ 6 | 7 | trait Pretty 8 | extends functions.Pretty 9 | with let.Pretty 10 | -------------------------------------------------------------------------------- /src/main/scala/ilc/UnicodeOutput.scala: -------------------------------------------------------------------------------- 1 | package ilc 2 | 3 | object UnicodeOutput extends util.BooleanFlag { 4 | /** 5 | * Define this to true to enable Unicode output. 6 | */ 7 | val value = false 8 | } 9 | -------------------------------------------------------------------------------- /src/main/scala/ilc/language/letLanguage/Evaluation.scala: -------------------------------------------------------------------------------- 1 | package ilc 2 | package language 3 | package letLanguage 4 | 5 | import feature._ 6 | 7 | trait Evaluation extends integers.Evaluation with let.Evaluation 8 | -------------------------------------------------------------------------------- /src/main/scala/ilc/feature/booleans/Types.scala: -------------------------------------------------------------------------------- 1 | package ilc 2 | package feature 3 | package booleans 4 | 5 | trait Types extends base.Types with sums.Types with unit.Types { 6 | case object BooleanType extends Type 7 | } 8 | -------------------------------------------------------------------------------- /src/main/scala/ilc/feature/integers/Types.scala: -------------------------------------------------------------------------------- 1 | package ilc 2 | package feature 3 | package integers 4 | 5 | trait Types extends base.Types { 6 | case object IntType extends Type 7 | 8 | val ℤ = IntType 9 | } 10 | -------------------------------------------------------------------------------- /src/main/scala/ilc/feature/naturals/Types.scala: -------------------------------------------------------------------------------- 1 | package ilc 2 | package feature 3 | package naturals 4 | 5 | trait Types extends base.Types { 6 | case object NatType extends Type 7 | 8 | val ℕ = NatType 9 | } 10 | -------------------------------------------------------------------------------- /src/main/scala/ilc/language/Bacchus.scala: -------------------------------------------------------------------------------- 1 | package ilc 2 | package language 3 | 4 | import bacchus._ 5 | 6 | class Bacchus extends Syntax with Pretty with Evaluation with ToScala with Prelude with BasicDerivation with FineGrainedDifference 7 | -------------------------------------------------------------------------------- /src/main/scala/ilc/QuickAndDirty.scala: -------------------------------------------------------------------------------- 1 | package ilc 2 | 3 | object QuickAndDirty extends util.BooleanFlag { 4 | /** 5 | * Define this to true to speed up builds and benchmark runs, at the expense of accuracy. 6 | */ 7 | val value = false 8 | } 9 | -------------------------------------------------------------------------------- /src/main/scala/ilc/feature/base/Reification.scala: -------------------------------------------------------------------------------- 1 | package ilc 2 | package feature 3 | package base 4 | 5 | trait Reification extends Evaluation { 6 | def reify(value: Value, valueType: Type): Term = 7 | throw IDontKnow(s"how to reify $value of type $valueType") 8 | } 9 | -------------------------------------------------------------------------------- /src/main/scala/ilc/feature/bags/Types.scala: -------------------------------------------------------------------------------- 1 | package ilc 2 | package feature 3 | package bags 4 | 5 | trait Types extends base.Types { 6 | case class BagType(valType: Type) extends Type { 7 | override def traverse(f: Type => Type): Type = copy(f(valType)) 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/test/scala/ilc/howTo/GetConstraintsOutOfConstantsSuite.scala: -------------------------------------------------------------------------------- 1 | package ilc 2 | package howTo 3 | 4 | import org.scalatest.FunSuite 5 | 6 | class GetConstraintsOutOfConstantsSuite 7 | extends FunSuite 8 | with GetConstraintsOutOfConstants 9 | with feature.base.Pretty 10 | -------------------------------------------------------------------------------- /src/main/scala/ilc/feature/lists/Types.scala: -------------------------------------------------------------------------------- 1 | package ilc 2 | package feature 3 | package lists 4 | 5 | trait Types extends base.Types { 6 | case class ListType(elemType: Type) extends Type { 7 | override def traverse(f: Type => Type): Type = copy(f(elemType)) 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/main/scala/ilc/TypeChecking.scala: -------------------------------------------------------------------------------- 1 | package ilc 2 | 3 | object TypeChecking extends util.BooleanFlag { 4 | /** 5 | * Define this to false to disable some internal typechecking checks, which might be useful 6 | * for chaotic prototyping. 7 | */ 8 | val value = true 9 | } 10 | -------------------------------------------------------------------------------- /src/main/scala/ilc/feature/bintrees/Types.scala: -------------------------------------------------------------------------------- 1 | package ilc 2 | package feature 3 | package bintrees 4 | 5 | trait Types extends base.Types { 6 | case class BinTreeType(elemType: Type) extends Type { 7 | override def traverse(f: Type => Type): Type = copy(f(elemType)) 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/main/scala/ilc/feature/maybe/Types.scala: -------------------------------------------------------------------------------- 1 | package ilc 2 | package feature 3 | package maybe 4 | 5 | trait Types extends base.Types { 6 | case class MaybeType(contentType: Type) extends Type { 7 | override def traverse(f: Type => Type): Type = copy(f(contentType)) 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/main/scala/ilc/language/bacchus/Pretty.scala: -------------------------------------------------------------------------------- 1 | package ilc 2 | package language 3 | package bacchus 4 | 5 | import feature._ 6 | 7 | trait Pretty 8 | extends functions.Pretty 9 | with integers.Pretty 10 | with naturals.Pretty 11 | with sums.Pretty 12 | with products.Pretty 13 | -------------------------------------------------------------------------------- /src/main/scala/ilc/feature/unit/Syntax.scala: -------------------------------------------------------------------------------- 1 | package ilc 2 | package feature 3 | package unit 4 | 5 | trait Syntax extends base.Syntax with Types { 6 | // the inhabitant of the unit type 7 | case object UnitTerm extends Term { 8 | override lazy val getType: Type = UnitType 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/main/scala/ilc/feature/fixpoint/Library.scala: -------------------------------------------------------------------------------- 1 | package ilc 2 | package feature 3 | package fixpoint 4 | 5 | object Library extends base.Library { 6 | def fix[T]: (=> (=> T) => T) => T = 7 | fParam => { 8 | lazy val f = fParam 9 | //Or fParam? 10 | f(fix(f)) 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/main/scala/ilc/feature/maps/Types.scala: -------------------------------------------------------------------------------- 1 | package ilc 2 | package feature 3 | package maps 4 | 5 | trait Types extends base.Types { 6 | case class MapType(keyType: Type, valType: Type) extends Type { 7 | override def traverse(f: Type => Type): Type = copy(f(keyType), f(valType)) 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/main/scala/ilc/feature/sums/Types.scala: -------------------------------------------------------------------------------- 1 | package ilc 2 | package feature 3 | package sums 4 | 5 | trait Types extends base.Types { 6 | case class SumType(leftType: Type, rightType: Type) extends Type { 7 | override def traverse(f: Type => Type): Type = copy(f(leftType), f(rightType)) 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/test/scala/ilc/language/bacchus/BacchusBasicDerivationSuite.scala: -------------------------------------------------------------------------------- 1 | package ilc 2 | package language 3 | package bacchus 4 | 5 | import org.scalatest.FunSuite 6 | import ilc.feature._ 7 | 8 | class BacchusBasicDerivationSuite 9 | extends DerivativeTests 10 | with BasicDerivation 11 | with base.Pretty 12 | -------------------------------------------------------------------------------- /src/main/scala/ilc/feature/let/IsAtomic.scala: -------------------------------------------------------------------------------- 1 | package ilc 2 | package feature 3 | package let 4 | 5 | trait IsAtomic extends functions.IsAtomic { 6 | this: let.Syntax => 7 | 8 | override def isAtomic(t: Term) = 9 | t match { 10 | case _: Let => false 11 | case _ => super.isAtomic(t) 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/main/scala/ilc/util/BooleanFlag.scala: -------------------------------------------------------------------------------- 1 | package ilc 2 | package util 3 | 4 | //Using a trait makes life harder for the inliner and gives annoying warnings. 5 | abstract class BooleanFlag { 6 | def value: Boolean 7 | 8 | @inline def choose[T](ifTrue: => T, ifFalse: => T): T = 9 | if (value) ifTrue else ifFalse 10 | } 11 | -------------------------------------------------------------------------------- /src/main/scala/ilc/feature/fixpoint/Syntax.scala: -------------------------------------------------------------------------------- 1 | package ilc 2 | package feature 3 | package fixpoint 4 | 5 | trait Syntax extends base.Syntax with functions.Types { 6 | case object Fix extends ConstantWith1TypeParameter { 7 | val typeConstructor = TypeConstructor("t") { t => 8 | (t =>: t) =>: t 9 | } 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /bigClients/src/test/scala/ilc/examples/verification.scala: -------------------------------------------------------------------------------- 1 | package ilc 2 | package examples 3 | 4 | import longRunning._ 5 | 6 | class HistogramVerification extends BenchmarkVerification( 7 | new WordCountBenchData(HistogramGenerated) { 8 | override def base = 2 9 | override def last = 2 10 | override def step = 10 11 | }) 12 | -------------------------------------------------------------------------------- /clients/src/test/scala/longRunning/evalScala/BooleanSuiteCompile.scala: -------------------------------------------------------------------------------- 1 | package longRunning.evalScala 2 | 3 | import ilc.feature.booleans.BooleanSuiteBase 4 | import ilc.util.EvalScala 5 | 6 | class BooleanSuiteCompile extends BooleanSuiteBase { 7 | def expectToGet(b: Boolean)(t: => Term) { 8 | assert(evalScala(toScala(t)) === b) 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/main/scala/ilc/feature/base/Context.scala: -------------------------------------------------------------------------------- 1 | package ilc 2 | package feature 3 | package base 4 | 5 | import ilc.util.Zipper 6 | 7 | /** Term contexts: C[·] */ 8 | 9 | trait Context extends Syntax with Zipper { 10 | type Tree = Term 11 | 12 | implicit class SubtreeOps(subtree: Subtree) { 13 | def toTerm: Term = subtree.subtree 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /icfp2014/src/main/scala/ilc/language/gcc/Evaluation.scala: -------------------------------------------------------------------------------- 1 | package ilc 2 | package language 3 | package gcc 4 | 5 | import feature._ 6 | 7 | trait Evaluation 8 | extends functions.Evaluation 9 | with let.Evaluation 10 | with maybe.Evaluation 11 | with integers.Evaluation 12 | with sums.Evaluation 13 | with products.Evaluation 14 | with booleans.Evaluation 15 | -------------------------------------------------------------------------------- /src/main/scala/ilc/feature/let/Syntax.scala: -------------------------------------------------------------------------------- 1 | package ilc 2 | package feature 3 | package let 4 | 5 | trait Syntax extends functions.Syntax { 6 | case class Let(variable: Var, exp: Term, body: Term) extends Term { 7 | override lazy val getType = { 8 | assert (variable.getType == exp.getType || !TypeChecking.value) 9 | body.getType 10 | } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/test/scala/ilc/examples/FastBenchmarksFlag.scala: -------------------------------------------------------------------------------- 1 | package ilc 2 | package examples 3 | 4 | object FastBenchmarksFlag extends util.BooleanFlag { 5 | /** 6 | * Define this to true to speed up benchmarking by reducing the maximum 7 | * dataset size, and the memory allocated by the sub-JVMs set via the -Xmx 8 | * option. 9 | */ 10 | val value = true 11 | } 12 | -------------------------------------------------------------------------------- /src/main/scala/ilc/feature/fixpoint/ToScala.scala: -------------------------------------------------------------------------------- 1 | package ilc 2 | package feature 3 | package fixpoint 4 | 5 | trait ToScala extends base.ToScala with Syntax { 6 | addLibrary("fixpoint") 7 | 8 | override def toUntypedScala(t: Term): String = 9 | t match { 10 | case Fix(t) => 11 | "fix" 12 | case _ => 13 | super.toUntypedScala(t) 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/main/scala/ilc/feature/abelianGroups/Types.scala: -------------------------------------------------------------------------------- 1 | package ilc 2 | package feature 3 | package abelianGroups 4 | 5 | trait Types extends functions.Types { 6 | case class AbelianGroupType(elType: Type) extends Type { 7 | override def traverse(f: Type => Type): Type = copy(f(elType)) 8 | } 9 | 10 | def binOpType(e: Type) = e =>: e =>: e 11 | def invType(e: Type) = e =>: e 12 | } 13 | -------------------------------------------------------------------------------- /clients/src/test/scala/longRunning/MapSuccBaseBenchmark.scala: -------------------------------------------------------------------------------- 1 | package longRunning 2 | 3 | import ilc.examples.MapIntIntBenchData 4 | import ilc.examples.MapSuccBaseGenerated 5 | import ilc.examples.ReplacementChangeBenchmark 6 | 7 | /** 8 | * Benchmark generated derivative. 9 | */ 10 | object MapSuccBaseBenchmark extends ReplacementChangeBenchmark(new MapIntIntBenchData(MapSuccBaseGenerated)) 11 | -------------------------------------------------------------------------------- /src/main/scala/ilc/feature/let/Evaluation.scala: -------------------------------------------------------------------------------- 1 | package ilc 2 | package feature 3 | package let 4 | 5 | trait Evaluation extends Syntax with functions.Evaluation { 6 | override def coreEval(t: Term, env: Env): Value = 7 | t match { 8 | case Let(x, exp, body) => 9 | wrapEval(body, env.updated(x.getName, wrapEval(exp, env))) 10 | case _ => 11 | super.coreEval(t, env) 12 | } 13 | } -------------------------------------------------------------------------------- /src/main/scala/ilc/language/bacchus/Evaluation.scala: -------------------------------------------------------------------------------- 1 | package ilc 2 | package language 3 | package bacchus 4 | 5 | import feature._ 6 | 7 | trait Evaluation 8 | extends functions.Evaluation 9 | with maps.Evaluation 10 | with maybe.Evaluation 11 | with naturals.Evaluation 12 | with sums.Evaluation 13 | with abelianGroups.Evaluation 14 | with products.Evaluation 15 | with booleans.Evaluation 16 | -------------------------------------------------------------------------------- /clients/src/test/scala/ilc/util/EvalScalaSuite.scala: -------------------------------------------------------------------------------- 1 | package ilc 2 | package util 3 | 4 | import org.scalatest.FunSuite 5 | import ilc.util.process.FunProcess 6 | 7 | class EvalScalaSuite 8 | extends FunSuite 9 | with EvalScala 10 | { 11 | test("can evaluate ilc constructs") { 12 | assert(evalScala("""ilc.Examples.scalaMeterDummyCode""") === 13 | ilc.Examples.scalaMeterDummyCode) 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/test/scala/ilc/language/bacchus/ChangingTerms.scala: -------------------------------------------------------------------------------- 1 | package ilc 2 | package language 3 | package bacchus 4 | 5 | trait ChangingTerms extends feature.base.Syntax { 6 | // shorthand for changing terms 7 | case class ChangingTerms(oldTerm: Term, newTerm: Term) 8 | implicit class ChangingTermsInfixConstructor[T <% Term](oldTerm: T) { 9 | def ↦ (newTerm: Term) = ChangingTerms(oldTerm, newTerm) 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/main/scala/ilc/feature/functions/IsAtomic.scala: -------------------------------------------------------------------------------- 1 | package ilc 2 | package feature 3 | package functions 4 | 5 | trait IsAtomic { 6 | this: functions.Syntax => 7 | 8 | def isVar(t: Term) = t.isInstanceOf[Var] 9 | 10 | def isConst(t: Term) = isAtomic(t) && !isVar(t) 11 | 12 | def isAtomic(t: Term) = 13 | t match { 14 | case _: Abs => false 15 | case _: App => false 16 | case _ => true 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/main/scala/ilc/feature/unit/Pretty.scala: -------------------------------------------------------------------------------- 1 | package ilc 2 | package feature 3 | package unit 4 | 5 | import org.kiama.output._ 6 | 7 | trait Pretty extends Syntax with base.Pretty { 8 | override def toPrettyExpression(tau: Type): PrettyExpression = tau match { 9 | case UnitType => 10 | PrettyNullaryExpression(text(UnicodeOutput.choose("𝟙", "1"))) 11 | 12 | case _ => 13 | super.toPrettyExpression(tau) 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/main/scala/ilc/language/LetLanguage.scala: -------------------------------------------------------------------------------- 1 | package ilc 2 | package language 3 | 4 | import feature._ 5 | 6 | /** 7 | * A variant of Bacchus with support for Let. 8 | */ 9 | class LetLanguage extends 10 | language.Bacchus with integers.Syntax with integers.ImplicitSyntaxSugar with inference.LetInference 11 | with let.BetaReduction with let.Pretty 12 | with inference.LetSyntaxSugar 13 | with letLanguage.Evaluation 14 | -------------------------------------------------------------------------------- /src/main/scala/ilc/feature/functions/LetRecSyntax.scala: -------------------------------------------------------------------------------- 1 | package ilc 2 | package feature 3 | package functions 4 | 5 | trait LetRecSyntax extends Syntax { 6 | case class LetRec(pairs: List[(Var, Term)], bodyName: Name, body: Term) extends Term { 7 | override lazy val getType = { 8 | assert ((pairs forall { case (variable, exp) => variable.getType == exp.getType }) || !TypeChecking.value) 9 | body.getType 10 | } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/main/scala/ilc/feature/integers/Pretty.scala: -------------------------------------------------------------------------------- 1 | package ilc 2 | package feature 3 | package integers 4 | 5 | import org.kiama.output._ 6 | 7 | trait Pretty extends Syntax with base.Pretty { 8 | override def toPrettyExpression(tau: Type): PrettyExpression = tau match { 9 | case IntType => 10 | PrettyNullaryExpression(text(UnicodeOutput.choose("ℤ", "Z"))) 11 | 12 | case _ => 13 | super.toPrettyExpression(tau) 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/main/scala/ilc/feature/memoize/Syntax.scala: -------------------------------------------------------------------------------- 1 | package ilc 2 | package feature 3 | package memoize 4 | 5 | trait Syntax extends MemoizeBase { 6 | this: base.ToScala with analysis.FreeVariables => 7 | case class Memo(cacheEntry: CacheEntry, updateCache: Boolean) extends ConstantWith1TypeParameter { 8 | val typeConstructor = TypeConstructor("contentType") { 9 | case contentType => contentType =>: contentType 10 | } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/main/scala/ilc/feature/naturals/Pretty.scala: -------------------------------------------------------------------------------- 1 | package ilc 2 | package feature 3 | package naturals 4 | 5 | import org.kiama.output._ 6 | 7 | trait Pretty extends Syntax with base.Pretty { 8 | override def toPrettyExpression(tau: Type): PrettyExpression = tau match { 9 | case NatType => 10 | PrettyNullaryExpression(text(UnicodeOutput.choose("ℕ", "N"))) 11 | 12 | case _ => 13 | super.toPrettyExpression(tau) 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/main/scala/ilc/feature/base/ContextSensitiveDerivation.scala: -------------------------------------------------------------------------------- 1 | package ilc 2 | package feature 3 | package base 4 | 5 | trait ContextSensitiveDerivation 6 | extends Derivation 7 | with Context 8 | { 9 | final override def derive(t: Term): Term = 10 | deriveSubtree(Subtree.ofRoot(t)) 11 | 12 | // subclass should override this one instead 13 | def deriveSubtree(subtree: Subtree): Term = 14 | // default to 15 | super.derive(subtree.toTerm) 16 | } 17 | -------------------------------------------------------------------------------- /src/main/scala/ilc/language/bacchus/ToScala.scala: -------------------------------------------------------------------------------- 1 | package ilc 2 | package language 3 | package bacchus 4 | 5 | import feature._ 6 | 7 | trait ToScala 8 | extends functions.ToScala 9 | with bags.ToScala 10 | with maps.ToScala 11 | with maybe.ToScala 12 | with naturals.ToScala 13 | with integers.ToScala 14 | with sums.ToScala 15 | with equality.ToScala 16 | with abelianGroups.ToScala 17 | with products.ToScala 18 | with booleans.ToScala 19 | -------------------------------------------------------------------------------- /src/main/scala/ilc/feature/let/FreeVariables.scala: -------------------------------------------------------------------------------- 1 | package ilc 2 | package feature 3 | package let 4 | 5 | trait FreeVariables extends analysis.FreeVariables with Syntax { 6 | override def termFreeVariables(term: Term): Set[Var] = term match { 7 | case Let(v, exp, body) => 8 | //If v is free in exp, it is indeed free in the overall let! 9 | body.freeVariables - v ++ exp.freeVariables 10 | case _ => 11 | super.termFreeVariables(term) 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/main/scala/ilc/language/bacchus/Syntax.scala: -------------------------------------------------------------------------------- 1 | package ilc 2 | package language 3 | package bacchus 4 | 5 | import feature._ 6 | 7 | trait Syntax 8 | extends functions.Syntax 9 | with bags.SyntaxSugar 10 | with maps.SyntaxSugar 11 | with maybe.Syntax 12 | with integers.SyntaxSugar 13 | with naturals.Syntax 14 | with sums.SyntaxSugar 15 | with equality.Syntax 16 | with abelianGroups.Syntax 17 | with products.Syntax 18 | with booleans.SyntaxSugar 19 | -------------------------------------------------------------------------------- /src/main/scala/ilc/feature/lists/Pretty.scala: -------------------------------------------------------------------------------- 1 | package ilc 2 | package feature 3 | package lists 4 | 5 | import org.kiama.output._ 6 | 7 | trait Pretty extends Syntax with base.Pretty { 8 | override def toPrettyExpression(tau: Type): PrettyExpression = tau match { 9 | case ListType(elemType) => 10 | PrettyNullaryExpression { 11 | text("[") <> toDoc(elemType) <> text("]") 12 | } 13 | 14 | case _ => 15 | super.toPrettyExpression(tau) 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/test/scala/ilc/util/EvalScalaSuite.scala: -------------------------------------------------------------------------------- 1 | package ilc 2 | package util 3 | 4 | import org.scalatest.FunSuite 5 | import ilc.util.process.FunProcess 6 | 7 | class EvalScalaSuite 8 | extends FunSuite 9 | with EvalScala 10 | { 11 | test("can evaluate integer arithmetic") { 12 | assert(evalScala("(1 + 2) * (3 + 4)") === 21) 13 | } 14 | 15 | test("can evaluate Scala lambdas") { 16 | assert(evalScala("((x: Int) => 3 * x + 1997)(5)") === 3 * 5 + 1997) 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /bigClients/src/test/scala/longRunning/BenchSuite.scala: -------------------------------------------------------------------------------- 1 | package longRunning 2 | 3 | import org.scalameter.api._ 4 | import ilc.examples.BaseBenchmark 5 | 6 | class BenchSuite extends BaseBenchmark { 7 | //include[HistogramVerification] 8 | 9 | //include[BagUnionBenchmark] 10 | include[HistogramBenchmark] 11 | include[HistogramRecomputeBenchmark] 12 | //include[SumValuesBenchmark] 13 | 14 | override def reporters = super.reporters :+ 15 | ChartReporter(ChartFactory.XYLine()) 16 | } 17 | -------------------------------------------------------------------------------- /src/main/scala/ilc/feature/bintrees/Pretty.scala: -------------------------------------------------------------------------------- 1 | package ilc 2 | package feature 3 | package bintrees 4 | 5 | import org.kiama.output._ 6 | 7 | trait Pretty extends Syntax with base.Pretty { 8 | override def toPrettyExpression(tau: Type): PrettyExpression = tau match { 9 | case BinTreeType(elemType) => 10 | PrettyNullaryExpression { 11 | text("<#") <+> toDoc(elemType) <+> text("#>") 12 | } 13 | 14 | case _ => 15 | super.toPrettyExpression(tau) 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/main/scala/ilc/feature/unit/ToScala.scala: -------------------------------------------------------------------------------- 1 | package ilc 2 | package feature 3 | package unit 4 | 5 | trait ToScala extends base.ToScala with Syntax { 6 | override def toUntypedScala(t: Term): String = t match { 7 | case UnitTerm => 8 | "()" 9 | 10 | case _ => 11 | super.toUntypedScala(t) 12 | } 13 | 14 | override def toScala(tau: Type): String = tau match { 15 | case UnitType => 16 | "Unit" 17 | 18 | case _ => 19 | super.toScala(tau) 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /clients/src/test/scala/longRunning/MapSuccBagsBenchmark.scala: -------------------------------------------------------------------------------- 1 | package longRunning 2 | 3 | import ilc.examples.BagIntBenchData 4 | import ilc.examples.MapSuccBagsGenerated 5 | import ilc.examples.NonReplacementChangeBenchmark 6 | 7 | /** 8 | * Benchmark generated derivative. 9 | */ 10 | object MapSuccBagsBenchmark extends NonReplacementChangeBenchmark( 11 | new BagIntBenchData(MapSuccBagsGenerated) { 12 | override def base = 50 13 | override def last = 250 14 | override def step = 50 15 | }) 16 | -------------------------------------------------------------------------------- /src/main/scala/ilc/feature/functions/LambdaDelta.scala: -------------------------------------------------------------------------------- 1 | package ilc 2 | package feature 3 | package functions 4 | 5 | trait LambdaDelta extends base.Derivation with Syntax { 6 | def lambdaDelta(t: Term) 7 | (derivativeBody: Seq[Name] => TermBuilder): TermBuilder = 8 | { 9 | val argumentTypesOfDerivative: List[Type] = 10 | getArgumentTypes(t.getType) flatMap { theType => 11 | List(theType, deltaType(theType)) 12 | } 13 | lambda(argumentTypesOfDerivative)(derivativeBody) 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/main/scala/ilc/feature/naturals/Reification.scala: -------------------------------------------------------------------------------- 1 | package ilc 2 | package feature 3 | package naturals 4 | 5 | // Reification is a subclass of Evaluation for value declarations 6 | // alone. Should we separate those into a new trait, naturals.Values? 7 | trait Reification extends base.Reification with Evaluation { 8 | override def reify(value: Value, valueType: Type): Term = value match { 9 | case NatValue(n) => 10 | Nat(n) 11 | 12 | case _ => 13 | super.reify(value, valueType) 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/main/scala/ilc/feature/unit/Evaluation.scala: -------------------------------------------------------------------------------- 1 | package ilc 2 | package feature.unit 3 | 4 | import scala.language.implicitConversions 5 | import scala.collection.immutable 6 | 7 | trait Evaluation extends feature.base.Evaluation { 8 | this: Syntax => 9 | 10 | // the inhabitant of unit type has no computation content 11 | case object UnitValue extends Value 12 | 13 | override def coreEval(t: Term, env: Env): Value = t match { 14 | case UnitTerm => UnitValue 15 | case _ => super.coreEval(t, env) 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/main/scala/ilc/language/bacchus/Prelude.scala: -------------------------------------------------------------------------------- 1 | package ilc 2 | package language 3 | package bacchus 4 | 5 | import feature._ 6 | 7 | /** 8 | * This trait includes generally useful functions for the Bacchus object 9 | * language. When Bacchus abstraction is insufficient, we resort to meta-level 10 | * abstraction (see for instance const). 11 | * 12 | * Many function are inspired (to some extent) from Haskell ones. 13 | */ 14 | trait Prelude extends bacchus.Syntax 15 | { 16 | def succ: Term = PlusInt ! LiteralInt(1) 17 | } 18 | -------------------------------------------------------------------------------- /src/main/scala/ilc/language/bacchus/BasicDerivation.scala: -------------------------------------------------------------------------------- 1 | package ilc 2 | package language 3 | package bacchus 4 | 5 | import feature._ 6 | 7 | trait BasicDerivation 8 | extends functions.Derivation 9 | with bags.Derivation 10 | with maps.ReplacementValuesDerivation 11 | with maybe.ReplacementValuesDerivation 12 | with naturals.ReplacementValuesDerivation 13 | with sums.ReplacementValuesDerivation 14 | with abelianGroups.Derivation 15 | with products.Derivation 16 | with booleans.Derivation 17 | with integers.AbelianDerivation 18 | -------------------------------------------------------------------------------- /src/main/scala/ilc/feature/let/Derivation.scala: -------------------------------------------------------------------------------- 1 | package ilc 2 | package feature 3 | package let 4 | 5 | /** 6 | * Derivation for Let. 7 | * This is untested. That's also because Lets don't arise before 8 | * derivation in the current language pipeline. 9 | */ 10 | trait Derivation extends base.Derivation { 11 | this: Syntax => 12 | 13 | override def derive(t: Term): Term = t match { 14 | case Let(x, term, body) => 15 | Let(x, term, 16 | Let(DVar(x), derive(term), derive(body))) 17 | case _ => 18 | super.derive(t) 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/main/scala/ilc/feature/maybe/Reification.scala: -------------------------------------------------------------------------------- 1 | package ilc 2 | package feature 3 | package maybe 4 | 5 | trait Reification extends base.Reification with Evaluation { 6 | override def reify(value: Value, valueType: Type): Term = 7 | (value, valueType) match { 8 | case (MaybeValue(None), MaybeType(contentType)) => 9 | Nope.tapply(contentType) 10 | 11 | case (MaybeValue(Some(content)), MaybeType(contentType)) => 12 | Just ! reify(content, contentType) 13 | 14 | case _ => 15 | super.reify(value, valueType) 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /icfp2014/src/main/scala/ilc/language/GCC.scala: -------------------------------------------------------------------------------- 1 | package ilc 2 | package language 3 | 4 | import gcc._ 5 | import feature._ 6 | 7 | /** 8 | * A front-end language for the GCC assembler from ICFP Contest 2014. 9 | * http://icfpcontest.org/specification.html#lambda-man-cpu 10 | * 11 | * Primitive data types: integers, closures, cons cells. 12 | * This is untyped. 13 | * 14 | * We have untyped pairs, which are used to encode other types (lists and n-ary tuples). 15 | */ 16 | class GCC extends SyntaxSugar with Pretty with ToProcessorFrontend with LambdaManApi 17 | object GCC extends GCC 18 | -------------------------------------------------------------------------------- /src/main/scala/ilc/feature/maps/Reification.scala: -------------------------------------------------------------------------------- 1 | package ilc 2 | package feature 3 | package maps 4 | 5 | trait Reification extends base.Reification with Evaluation { 6 | override def reify(value: Value, valueType: Type): Term = 7 | (value, valueType) match { 8 | case (MapValue(valueMap), MapType(domain, range)) => 9 | valueMap.foldRight(EmptyMap ofType valueType) { (keyValPair, wip) => 10 | Update ! reify(keyValPair._1, domain) ! 11 | reify(keyValPair._2, range ) ! wip 12 | } 13 | 14 | case _ => 15 | super.reify(value, valueType) 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /clients/src/test/scala/longRunning/MapSuccBenchmark.scala: -------------------------------------------------------------------------------- 1 | package longRunning 2 | 3 | import ilc.examples.AbelianBagIntBenchData 4 | import ilc.examples.BenchmarkVerification 5 | import ilc.examples.MapSuccGenerated 6 | import ilc.examples.NonReplacementChangeBenchmark 7 | 8 | /** 9 | * Benchmark generated derivative. This is what we can achieve by 10 | * improving our algorithms. 11 | */ 12 | 13 | class MapSuccBenchmark extends NonReplacementChangeBenchmark( 14 | new AbelianBagIntBenchData(MapSuccGenerated) { 15 | override def base = 500 16 | override def last = 2500 17 | override def step = 500 18 | }) 19 | -------------------------------------------------------------------------------- /src/main/scala/ilc/feature/sums/Reification.scala: -------------------------------------------------------------------------------- 1 | package ilc 2 | package feature 3 | package sums 4 | 5 | trait Reification extends base.Reification with Evaluation { 6 | override def reify(value: Value, valueType: Type): Term = 7 | (value, valueType) match { 8 | case (SumValue(Left(content)), SumType(leftType, rightType)) => 9 | Inj1.tapply(rightType) ! reify(content, leftType) 10 | 11 | case (SumValue(Right(content)), SumType(leftType, rightType)) => 12 | Inj2.tapply(leftType) ! reify(content, rightType) 13 | 14 | case _ => 15 | super.reify(value, valueType) 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/test/scala/ilc/language/bacchus/TypeCheckingSuite.scala: -------------------------------------------------------------------------------- 1 | package ilc 2 | package language 3 | package bacchus 4 | 5 | import feature._ 6 | import org.scalatest.FunSuite 7 | import org.scalatest.Matchers 8 | 9 | class TypeCheckingSuite extends FunSuite with Matchers { 10 | object Lang extends Bacchus 11 | import Lang._ 12 | 13 | test("Base type inference gives accurate messages") { 14 | intercept[base.TypeError] { 15 | (FoldNat ! LiteralInt(1) ! lambda(NatType) { x => PlusInt ! x ! x }): Term 16 | }.getMessage() should be ("Type error: PlusInt has type Z -> Z -> Z but should have type N -> N -> _") 17 | } 18 | } -------------------------------------------------------------------------------- /src/test/scala/ilc/metaprogs/CachingDeriveSuite.scala: -------------------------------------------------------------------------------- 1 | package ilc 2 | package metaprogs 3 | 4 | import feature._ 5 | import org.scalatest._ 6 | import org.scalatest.matchers._ 7 | 8 | /** 9 | * @author pgiarrusso 10 | */ 11 | class CachingDeriveSuite extends FlatSpec with Matchers { 12 | val v = new CachingDerive { 13 | val syntax = new language.LetLanguage 14 | } 15 | import v._ 16 | import syntax._ 17 | 18 | "cachingDerivation" should "produce well-typed terms" in { 19 | val t = 20 | //letS('v := 1)('v) 21 | letS('v := 'x % IntType ->: 'x)('v) 22 | println(pretty(cacheDerive(t))) 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/main/scala/ilc/examples/ExampleGenerated.scala: -------------------------------------------------------------------------------- 1 | package ilc.examples 2 | 3 | /** 4 | * Interface for generated example programs. 5 | */ 6 | trait ExampleGenerated extends Serializable { 7 | val normDerivative: (=> InputType) => (=> DeltaInputType) => DeltaOutputType 8 | 9 | type InputType 10 | type DeltaInputType 11 | type OutputType 12 | type DeltaOutputType 13 | 14 | val program: (=>InputType) => OutputType 15 | val derivative: (=>InputType) => (=>DeltaInputType) => DeltaOutputType 16 | val updateInput: (=>DeltaInputType) => (=>InputType) => InputType 17 | val updateOutput: (=>DeltaOutputType) => (=>OutputType) => OutputType 18 | } 19 | -------------------------------------------------------------------------------- /src/test/scala/ilc/feature/maps/MapsPrettySuite.scala: -------------------------------------------------------------------------------- 1 | package ilc 2 | package feature 3 | package maps 4 | 5 | import org.scalatest.FunSuite 6 | 7 | class MapsPrettySuite 8 | extends FunSuite 9 | with maps.Syntax 10 | with functions.Pretty 11 | { 12 | // dummy types for testing 13 | case object A extends Type 14 | case object B extends Type 15 | case object ATerm extends Term { lazy val getType = A } 16 | 17 | test("may give type arguments to constants") { 18 | info(pretty(EmptyMap.tapply(A, B))) 19 | } 20 | 21 | test("need not give argument type of polymorphic lookup") { 22 | val t = Lookup ! ATerm ! EmptyMap.tapply(A, B) 23 | info(pretty(t)) 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: scala 2 | # Keep in sync by hand with build.sbt. 3 | jdk: 4 | - openjdk7 5 | sudo: false 6 | # Caching setup from: 7 | # http://www.scala-sbt.org/0.13/docs/Travis-CI-with-sbt.html 8 | cache: 9 | directories: 10 | - $HOME/.sbt/boot 11 | - $HOME/.sbt/launchers 12 | - $HOME/.ivy2/cache 13 | install: 14 | - travis_retry sbt update 15 | script: 16 | - sbt test "project clients" "testOnly ilc.*" "project bigClients" "testOnly ilc.*" "project icfp2014" test 17 | 18 | # Tricks to avoid unnecessary cache updates, also from 19 | # http://www.scala-sbt.org/0.13/docs/Travis-CI-with-sbt.html 20 | - find $HOME/.sbt -name "*.lock" | xargs rm 21 | - find $HOME/.ivy2 -name "ivydata-*.properties" | xargs rm 22 | -------------------------------------------------------------------------------- /clients/src/main/scala/ilc/examples/MapSuccExample.scala: -------------------------------------------------------------------------------- 1 | package ilc 2 | package examples 3 | 4 | import feature._ 5 | 6 | //Example 3: mapping over the values 7 | class MapSuccExample 8 | extends Example 9 | with bags.StdLib 10 | 11 | // context-sensitive derivations 12 | with bags.AbelianDerivation 13 | with integers.AbelianDerivation 14 | 15 | // code generation 16 | with bags.ToScala 17 | with booleans.ToScala 18 | with functions.ToScala 19 | with integers.ToScala 20 | with products.ToScala 21 | with sums.ToScala 22 | { 23 | val inc: UntypedTerm = PlusInt(LiteralInt(1)) 24 | 25 | // program : Bag ℤ → Bag ℤ 26 | // program = map (plus 1) 27 | def program: Term = untypedTermToTerm(map(inc)) 28 | } 29 | -------------------------------------------------------------------------------- /clients/src/test/scala/ilc/metaprogs/ClientsMemoizeSpec.scala: -------------------------------------------------------------------------------- 1 | package ilc 2 | package metaprogs 3 | 4 | import org.scalatest._ 5 | 6 | class ClientsMemoizeSpec extends FlatSpec { 7 | "memoizedDerive & transform" should "produce correct results for multivariate functions" in { 8 | // original source file: /clients/src/main/scala/ilc/examples/MemoizationExamples.scala 9 | val (run0, (run1, (run2, (run3, run4)))) = examples.MemoXPlusYGenerated.program(()) 10 | 11 | // assert that the results of the 5 runs are equal to the expected value 12 | assert(run0._1 == run0._2) 13 | assert(run1._1 == run1._2) 14 | assert(run2._1 == run2._2) 15 | assert(run3._1 == run3._2) 16 | assert(run4._1 == run4._2) 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/main/scala/ilc/feature/maybe/ToScala.scala: -------------------------------------------------------------------------------- 1 | package ilc 2 | package feature 3 | package maybe 4 | 5 | trait ToScala extends base.ToScala with Syntax { 6 | override def toUntypedScala(t: Term): String = t match { 7 | case Maybe(a, b) => 8 | scalaFunction("z", "f", "m")(s"m.fold[${toScala(b)}](z)(x => f(x))") 9 | 10 | case Nope(contentType) => 11 | "None" 12 | 13 | case Just(contentType) => 14 | "(Some.apply _)" 15 | 16 | case _ => 17 | super.toUntypedScala(t) 18 | } 19 | 20 | override def toScala(tau: Type): String = tau match { 21 | case MaybeType(contentType) => 22 | "Option[%s]".format(toScala(contentType)) 23 | 24 | case _ => 25 | super.toScala(tau) 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/test/scala/ilc/language/bacchus/BacchusPrettySuite.scala: -------------------------------------------------------------------------------- 1 | package ilc 2 | package language 3 | package bacchus 4 | 5 | import org.scalatest.FunSuite 6 | import org.scalatest.Matchers 7 | 8 | class BacchusPrettySuite 9 | extends FunSuite 10 | with Matchers 11 | { 12 | object Lang extends Evaluation with feature.base.Pretty 13 | import Lang._ 14 | 15 | test("values have short descriptions") { 16 | NatValue(9).toString should be("9") 17 | MapValue(1 -> 4).toString should be("Map(1 -> 4)") 18 | SumValue(Left(5)).toString should be("Inj1(5)") 19 | SumValue(Right(2)).toString should be("Inj2(2)") 20 | MaybeValue(None).toString should be("Nope") 21 | MaybeValue(Some(5)).toString should be("Just(5)") 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/main/scala/ilc/feature/fixpoint/Derivation.scala: -------------------------------------------------------------------------------- 1 | package ilc 2 | package feature 3 | package fixpoint 4 | 5 | trait Derivation extends functions.Derivation with Syntax with functions.Syntax { 6 | override def derive(t: Term): Term = t match { 7 | case App(Fix(typ), body) => 8 | //Since the metalanguage doesn't do type inference for us, 9 | //let's do it by hand. 10 | //Situation: 11 | //body: T => T 12 | //t = fix(body) 13 | //fix(body): T 14 | //derive(t): DT 15 | //derive(body) t: DT => DT 16 | //So fix(derive(body) t): DT, and DT must be the type parameter to fix. 17 | App(Fix.tapply(deltaType(typ)), App(derive(body), t)) 18 | 19 | case _ => super.derive(t) 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/main/scala/ilc/feature/integers/ToScala.scala: -------------------------------------------------------------------------------- 1 | package ilc 2 | package feature 3 | package integers 4 | 5 | trait ToScala extends base.ToScala with Syntax { 6 | private def int = "Int" 7 | 8 | override def toScala(tau: Type): String = tau match { 9 | case IntType => int 10 | case _ => super.toScala(tau) 11 | } 12 | 13 | override def isScalaPrimitive(tau: Type) = tau match { 14 | case IntType => true 15 | case _ => super.isScalaPrimitive(tau) 16 | } 17 | 18 | override def toUntypedScala(t: Term): String = t match { 19 | case LiteralInt(i) => i.toString 20 | case PlusInt => scalaFunction("i", "j")("i + j") 21 | case NegateInt => "(- _)" 22 | case _ => super.toUntypedScala(t) 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/main/scala/ilc/feature/integers/Library.scala: -------------------------------------------------------------------------------- 1 | package ilc 2 | package feature 3 | package integers 4 | 5 | object Library { 6 | import abelianGroups.Library._ 7 | 8 | 9 | // stamp for addition = big-endian encoding of "add!" 10 | val additionIndexedGroupStamp : Int = { 11 | val bytes = Array('a', 'd', 'd', '!').map(_.toInt) 12 | (0 until bytes.length).map(i => 13 | bytes(i) << 8*(bytes.length - i - 1) 14 | ).sum 15 | } 16 | 17 | assert(additionIndexedGroupStamp == 0x61646421) 18 | 19 | 20 | //This is supposed to match additiveGroupOnIntegers, but this isn't tested 21 | //(except indirectly in bags.DeltaBagSuite). 22 | val additiveGroupOnIntegers = 23 | IndexedGroup[Int](additionIndexedGroupStamp, x => x + _, -_, 0) 24 | } 25 | -------------------------------------------------------------------------------- /src/test/scala/ilc/util/process/FunProcessSuite.scala: -------------------------------------------------------------------------------- 1 | package ilc 2 | package util 3 | package process 4 | 5 | import org.scalatest.FunSuite 6 | 7 | // Testing process abstracted as (partial) functions String => String 8 | // should work on Mac and Windows 9 | // (please report platform-dependent errors) 10 | 11 | class FunProcessSuite extends FunSuite{ 12 | test("hello world") { 13 | val helloWorld = "hello world" 14 | assert(FunProcess("echo", helloWorld)("").trim === helloWorld) 15 | } 16 | 17 | test("failed process should raise error") { 18 | val unexpectedCmd = "zo129kv78j2cx" 19 | import java.io.IOException 20 | intercept[IOException] { FunProcess(unexpectedCmd)("") } 21 | intercept[IOException] { FunProcess("")("") } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /bigClients/src/test/scala/ilc/examples/HistogramGeneratedSuite.scala: -------------------------------------------------------------------------------- 1 | package ilc 2 | package examples 3 | 4 | import org.scalatest.FunSuite 5 | import org.scalatest.Matchers 6 | import ilc.examples.HistogramGenerated._ 7 | import ilc.feature.abelianMaps.Library._ 8 | import ilc.feature.bags.Library._ 9 | 10 | class HistogramGeneratedSuite 11 | extends FunSuite 12 | with Matchers 13 | { 14 | val input: InputType = 15 | AbelianMap( 16 | 1 -> Bag(1, 2, 3, 4, 5), 17 | 2 -> Bag( 2, 3, 4 ), 18 | 3 -> Bag( 3 ) 19 | ) 20 | 21 | test("Histogram is correct on a toy example") { 22 | program(input) should be(AbelianMap( 23 | 1 -> 1, 24 | 2 -> 2, 25 | 3 -> 3, 26 | 4 -> 2, 27 | 5 -> 1 28 | )) 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /clients/src/test/scala/ilc/examples/verification.scala: -------------------------------------------------------------------------------- 1 | package ilc 2 | package examples 3 | 4 | import longRunning._ 5 | 6 | object BagUnionVerification extends BenchmarkVerification( 7 | new BagPairBenchData(BagUnionGenerated) { 8 | override def base = 5 9 | override def last = 15 10 | override def step = 5 11 | }) 12 | 13 | object MapSuccVerification extends BenchmarkVerification( 14 | new AbelianBagIntBenchData(MapSuccGenerated) { 15 | override def base = 5 16 | override def last = 25 17 | override def step = 5 18 | }) 19 | 20 | object SumValuesVerification extends BenchmarkVerification( 21 | new AdditiveMapBenchData(SumValuesGenerated) { 22 | override def base = 5 23 | override def last = 25 24 | override def step = 5 25 | }) 26 | -------------------------------------------------------------------------------- /src/main/scala/ilc/feature/abelianMaps/ToScala.scala: -------------------------------------------------------------------------------- 1 | package ilc 2 | package feature 3 | package abelianMaps 4 | 5 | trait ToScala 6 | extends maps.ToScala 7 | with abelianGroups.ToScala 8 | with Syntax 9 | { 10 | addLibrary("abelianMaps") 11 | 12 | override def toScala(tau: Type): String = tau match { 13 | case MapType(keyType, valType) => 14 | s"AbelianMap[${toScala(keyType)}, ${toScala(valType)}]" 15 | 16 | case _ => 17 | super.toScala(tau) 18 | } 19 | 20 | override def toUntypedScala(t: Term): String = t match { 21 | case EmptyMap(k, v) => "emptyMap" 22 | case SingletonMap(k, v) => "singletonMap" 23 | case LiftGroup(k, v) => "liftGroup" 24 | case FoldByHom(k, a, b) => "foldByHom" 25 | 26 | case _ => 27 | super.toUntypedScala(t) 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/test/scala/ilc/feature/base/PrettySuite.scala: -------------------------------------------------------------------------------- 1 | package ilc 2 | package feature 3 | package base 4 | 5 | import org.scalatest.FunSuite 6 | 7 | class PrettySuite 8 | extends FunSuite 9 | with base.Syntax 10 | with base.Pretty 11 | { 12 | case object Unit extends Type 13 | 14 | def prettyVar(name: String): Layout = 15 | pretty(Var(LiteralName(name), Unit)) 16 | 17 | test("Variables should pretty-print to their names") { 18 | assert(prettyVar("x") == "x") 19 | assert(prettyVar("y") == "y") 20 | 21 | // beware: names must end in a lower-case letter 22 | val superLongName = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz" 23 | 24 | assert(prettyVar(superLongName) == superLongName) 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/main/scala/ilc/analysis/FreeVariables.scala: -------------------------------------------------------------------------------- 1 | package ilc 2 | package analysis 3 | 4 | import ilc.feature.functions 5 | 6 | trait FreeVariables 7 | extends functions.Context 8 | { 9 | implicit class FreeVariablesOfSubtrees(subtree: Subtree) { 10 | def freeVariables: Set[Var] = subtree.toTerm.freeVariables 11 | } 12 | 13 | def termFreeVariables(term: Term): Set[Var] = term match { 14 | case variable: Var => 15 | Set(variable) 16 | 17 | case Abs(variable, body) => 18 | body.freeVariables - variable 19 | 20 | case App(operator, operand) => 21 | operator.freeVariables ++ operand.freeVariables 22 | 23 | case _ => 24 | Set.empty 25 | } 26 | 27 | implicit class FreeVariablesOfTerms(term: Term) { 28 | def freeVariables: Set[Var] = termFreeVariables(term) 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/main/scala/ilc/util/ExtractorTrait.scala: -------------------------------------------------------------------------------- 1 | package ilc 2 | package util 3 | 4 | trait ExtractorTrait { 5 | trait Extractor[From, To] { 6 | def unapply(from: From): Option[To] 7 | } 8 | 9 | /* 10 | * Altered from https://github.com/Blaisorblade/scrap-expr-boilerplate/blob/master/src/traversal/Extractor.scala. 11 | * This collapses the cost of writing small extractors. 12 | */ 13 | def extractor[A, B](f: A => Option[B]) = new Extractor[A, B] { def unapply(x: A): Option[B] = f(x) } 14 | } 15 | 16 | //This module has no open dependencies, so there's no real need to use traits 17 | //for it. 18 | //Moreover, using a trait is part of your interface and requires clients to be 19 | //recompiled. In a project where recompilation can take 10 minutes, this can be 20 | //a huge problem. 21 | object Extractors extends ExtractorTrait 22 | -------------------------------------------------------------------------------- /src/main/scala/ilc/feature/products/ToScala.scala: -------------------------------------------------------------------------------- 1 | package ilc 2 | package feature 3 | package products 4 | 5 | trait ToScala extends base.ToScala with Syntax { 6 | private[this] def productType(a: Type, b: Type): String = 7 | toScala(ProductType(a, b)) 8 | 9 | override def toUntypedScala(t: Term): String = t match { 10 | case Pair(aType, bType) => 11 | scalaFunction("pairElem1", "pairElem2")("(pairElem1, pairElem2)") 12 | 13 | case Proj1(a, b) => 14 | "(_._1)" 15 | 16 | case Proj2(a, b) => 17 | "(_._2)" 18 | 19 | case _ => 20 | super.toUntypedScala(t) 21 | } 22 | 23 | override def toScala(tau: Type): String = tau match { 24 | case ProductType(leftType, rightType) => 25 | s"(${toScala(leftType)}, ${toScala(rightType)})" 26 | 27 | case _ => 28 | super.toScala(tau) 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/main/scala/ilc/feature/maybe/Evaluation.scala: -------------------------------------------------------------------------------- 1 | package ilc 2 | package feature 3 | package maybe 4 | 5 | trait Evaluation extends functions.Evaluation with Syntax { 6 | case class MaybeValue(lift: Option[Value]) extends Value { 7 | override def toString: String = 8 | lift.fold("Nope")(x => s"Just($x)") 9 | } 10 | 11 | override def coreEval(t: Term, env: Env): Value = t match { 12 | case Maybe(_, _) => 13 | (z: Value) => (f: Value) => (maybe: Value) => maybe match { 14 | case MaybeValue(option) => 15 | option.fold(z) { x => f(x) } 16 | 17 | case _ => 18 | maybe die " should be MaybeValue" 19 | } 20 | case Nope(_) => 21 | MaybeValue(None) 22 | case Just(_) => 23 | (value: Value) => MaybeValue(Some(value)) 24 | case _ => 25 | super.coreEval(t, env) 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /clients/src/main/scala/ilc/Examples.scala: -------------------------------------------------------------------------------- 1 | package ilc 2 | 3 | /** 4 | * Examples generator 5 | * 6 | * Interface: 7 | * 1. the first argument is the directory to generate into. 8 | * 2. for each file that's generated, print one line with its path. 9 | */ 10 | 11 | import examples._ 12 | 13 | object Examples 14 | extends Generator { 15 | // the compiled object is "MapSuccGenerated" 16 | // the benchmarking object is "MapSuccBenchmark" 17 | addExample(new MapSuccExample) 18 | 19 | //Ditto 20 | addExample(new BagUnionExample) 21 | 22 | addExample(new MapSuccBaseExample) 23 | 24 | addExample(new MapSuccBagsExample) 25 | 26 | addExample(new SumValuesExample) 27 | 28 | addExample(new GroupByExample) 29 | 30 | // add all examples about memoization 31 | addExample(new MemoXPlusY) 32 | } 33 | // idea for speeding up lookup/update: memoizing algorithms 34 | -------------------------------------------------------------------------------- /src/test/scala/ilc/feature/bags/DeltaBagSuite.scala: -------------------------------------------------------------------------------- 1 | package ilc 2 | package feature 3 | package bags 4 | 5 | import org.scalatest.FunSuite 6 | import org.scalatest.Matchers 7 | 8 | class DeltaBagSuite 9 | extends FunSuite with Matchers 10 | { 11 | import integers.Library._ 12 | import Library._ 13 | 14 | def sum(b: Bag[Int]) = 15 | bagFoldGroup[Int, Int](additiveGroupOnIntegers)(x => x)(b) 16 | val bag_1_1 = bagUnion(bagSingleton(1))(bagSingleton(1)) 17 | test("sum(Bag(1, 1)) = 2") { 18 | sum(bag_1_1) should be (2) 19 | } 20 | test("sum(Bag(1, 1, 10)) = 12") { 21 | val bag1_1_10 = bagUnion(bag_1_1)(bagSingleton(10)) 22 | sum(bag1_1_10) should be (12) 23 | } 24 | val bagNeg = bagNegate(bag_1_1) 25 | test("negate works") { 26 | bagNeg apply 1 should be (-2) 27 | } 28 | test("sum . negate works") { 29 | sum(bagNeg) should be (-2) 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/main/scala/ilc/feature/naturals/ToScala.scala: -------------------------------------------------------------------------------- 1 | package ilc 2 | package feature 3 | package naturals 4 | 5 | trait ToScala extends base.ToScala with Syntax { 6 | override def toUntypedScala(t: Term): String = t match { 7 | case Nat(n) => 8 | n.toString 9 | 10 | case PlusNat => 11 | scalaFunction("x", "y")("x + y") 12 | 13 | case FoldNat(r) => 14 | scalaFunction("z", "f", "n") { 15 | s"Range.inclusive(1, n).foldLeft[${toScala(r)}](z)((x, _) => f(x))" 16 | } 17 | 18 | case _ => 19 | super.toUntypedScala(t) 20 | } 21 | 22 | override def toScala(tau: Type): String = tau match { 23 | case NatType => 24 | "Int" 25 | 26 | case _ => 27 | super.toScala(tau) 28 | } 29 | 30 | override def isScalaPrimitive(tau: Type) = tau match { 31 | case NatType => true 32 | case _ => super.isScalaPrimitive(tau) 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/main/scala/ilc/feature/unit/Derivation.scala: -------------------------------------------------------------------------------- 1 | package ilc 2 | package feature 3 | package unit 4 | 5 | trait Derivation extends base.Derivation with Syntax { 6 | override def deltaType(tau: Type): Type = tau match { 7 | case UnitType => UnitType 8 | case _ => super.deltaType(tau) 9 | } 10 | 11 | override def updateTerm(tau: Type): Term = tau match { 12 | case UnitType => 13 | lambda(UnitType, UnitType) { case Seq(_, _) => UnitTerm } 14 | 15 | case _ => 16 | super.updateTerm(tau) 17 | } 18 | 19 | override def diffTerm(tau: Type): Term = tau match { 20 | case UnitType => 21 | lambda(UnitType, UnitType) { case Seq(_, _) => UnitTerm } 22 | 23 | case _ => 24 | super.diffTerm(tau) 25 | } 26 | 27 | override def derive(t: Term): Term = t match { 28 | case UnitTerm => UnitTerm 29 | case _ => super.derive(t) 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/main/scala/ilc/feature/naturals/Syntax.scala: -------------------------------------------------------------------------------- 1 | package ilc 2 | package feature 3 | package naturals 4 | 5 | import scala.language.implicitConversions 6 | 7 | trait Syntax extends base.Syntax with Types { 8 | // intro/elim forms of nats 9 | // 10 | // foldNat : r → (r → r) → ℕ → r 11 | 12 | case object FoldNat extends ConstantWith1TypeParameter { 13 | val typeConstructor = TypeConstructor("r") { r => 14 | r =>: (r =>: r) =>: NatType =>: r 15 | } 16 | } 17 | 18 | case class Nat(n: Int) extends Term { 19 | require(n >= 0) 20 | override lazy val getType: Type = NatType 21 | override def toString = n.toString 22 | } 23 | 24 | case object PlusNat extends Term { 25 | override lazy val getType: Type = 26 | NatType =>: NatType =>: NatType 27 | } 28 | } 29 | 30 | trait ImplicitSyntaxSugar extends Syntax { 31 | implicit def natToTerm(n: Int): Term = Nat(n) 32 | } 33 | -------------------------------------------------------------------------------- /src/main/scala/ilc/feature/sums/Pretty.scala: -------------------------------------------------------------------------------- 1 | package ilc 2 | package feature 3 | package sums 4 | 5 | import org.kiama.output._ 6 | 7 | trait Pretty extends base.Pretty with Syntax { 8 | override def operatorPrecedence(tau: Type): Int = tau match { 9 | case SumType(leftType, rightType) => 10 | 6 // like + in Haskell 11 | 12 | case _ => 13 | super.operatorPrecedence(tau) 14 | } 15 | 16 | override def toPrettyExpression(tau: Type): PrettyExpression = tau match { 17 | case SumType(leftType, rightType) => 18 | new PrettyBinaryExpression { 19 | def priority = operatorPrecedence(tau) 20 | def fixity = Infix(LeftAssoc) 21 | 22 | def left = toPrettyExpression(leftType) 23 | def right = toPrettyExpression(rightType) 24 | 25 | def op = "+" 26 | } 27 | 28 | case _ => 29 | super.toPrettyExpression(tau) 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/test/scala/ilc/feature/products/ProductToScalaSuite.scala: -------------------------------------------------------------------------------- 1 | package ilc 2 | package feature 3 | package products 4 | 5 | import org.scalatest.FunSuite 6 | import org.scalatest.Matchers 7 | import ilc.util.EvalScala 8 | 9 | class ProductToScalaSuite 10 | extends FunSuite with Matchers { 11 | object Lang extends ToScala 12 | with integers.ToScala 13 | with functions.ToScala 14 | with EvalScala 15 | with SyntaxSugar 16 | with integers.ImplicitSyntaxSugar 17 | with base.Pretty 18 | import Lang._ 19 | 20 | def run(t: Term): Any = evalScala(toScala(t)) 21 | 22 | val quintuple: Term = tuple(5) ! 1 ! 2 ! 3 ! 4 ! 5 23 | 24 | test("can construct tuples") { 25 | run(quintuple) should be((1, (2, (3, (4, 5))))) 26 | } 27 | 28 | test("can deconstruct tuples") { 29 | (1 to 5) foreach { i => 30 | run(project(i) ! quintuple) should be(i) 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/main/scala/ilc/feature/integers/AbelianDerivation.scala: -------------------------------------------------------------------------------- 1 | package ilc 2 | package feature 3 | package integers 4 | 5 | /** Δℤ = (AbelianGroup ℤ × ℤ) ⊎ ℤ 6 | * 7 | * The change to an integer is an abelian group element or a 8 | * replacement. 9 | */ 10 | trait AbelianDerivation 11 | extends abelianGroups.AbelianDerivation 12 | with Syntax 13 | with functions.ContextSensitiveDerivation 14 | { 15 | override def isAbelianType(tau: Type): Boolean = tau match { 16 | case IntType => true 17 | case _ => super.isAbelianType(tau) 18 | } 19 | 20 | override def deriveSubtree(s: Subtree): Term = s.toTerm match { 21 | case term @ LiteralInt(i) => 22 | replacementChange ! term 23 | 24 | // PlusInt and NegateInt can use the slow derivative. It operates on 25 | // integers, so calling `updateTerm` and `diffTerm` is okay. 26 | 27 | case _ => 28 | super.deriveSubtree(s) 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/main/scala/ilc/feature/sums/ToScala.scala: -------------------------------------------------------------------------------- 1 | package ilc 2 | package feature 3 | package sums 4 | 5 | trait ToScala extends base.ToScala with Syntax { 6 | private[this] def sumType(a: Type, b: Type): String = 7 | toScala(SumType(a, b)) 8 | 9 | override def toUntypedScala(t: Term): String = t match { 10 | case Either(aType, bType, cType) => { 11 | val c = toScala(cType) 12 | scalaFunction("f", "g", "s")(s"s.fold[$c](x => f(x), y => g(y))") 13 | } 14 | 15 | case Inj1(aType, bType) => 16 | s"(x => Left(x))" 17 | 18 | case Inj2(aType, bType) => 19 | s"(y => Right(y))" 20 | 21 | case _ => 22 | super.toUntypedScala(t) 23 | } 24 | 25 | override def toScala(tau: Type): String = tau match { 26 | case SumType(leftType, rightType) => 27 | "Either[%s, %s]".format(toScala(leftType), toScala(rightType)) 28 | 29 | case _ => 30 | super.toScala(tau) 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/main/scala/ilc/util/UnionType.scala: -------------------------------------------------------------------------------- 1 | package ilc 2 | package util 3 | 4 | // Union type for scala 5 | // 6 | // Source (search for Miles Sabin): 7 | // http://stackoverflow.com/a/6312508 8 | // 9 | // Usage: 10 | // 11 | // def toIntArray[T: Or[Int, String]#Type](value: T): Array[Int] = 12 | // value match { 13 | // case i: Int => Array(i) 14 | // case s: String => s.toCharArray.map(_.toInt) 15 | // } 16 | 17 | object UnionType { 18 | private type NOT[P] = P => Nothing 19 | private type OR[P, Q] = NOT[NOT[P] with NOT[Q]] 20 | private type OR3[P, Q, R] = NOT[NOT[P] with NOT[Q] with NOT[R]] 21 | private type OR4[P, Q, R, S] = 22 | NOT[NOT[P] with NOT[Q] with NOT[R] with NOT[S]] 23 | 24 | type Or[S, T] = { type Type[X] = NOT[NOT[X]] <:< OR[S, T] } 25 | type Or3[R, S, T] = { type Type[X] = NOT[NOT[X]] <:< OR3[R, S, T] } 26 | type Or4[Q, R, S, T] = { type Type[X] = NOT[NOT[X]] <:< OR4[Q, R, S, T] } 27 | } 28 | -------------------------------------------------------------------------------- /src/main/scala/ilc/feature/products/Pretty.scala: -------------------------------------------------------------------------------- 1 | package ilc 2 | package feature 3 | package products 4 | 5 | import org.kiama.output._ 6 | 7 | trait Pretty extends base.Pretty with Syntax { 8 | override def operatorPrecedence(tau: Type): Int = tau match { 9 | case ProductType(leftType, rightType) => 10 | 7 // like * in Haskell 11 | 12 | case _ => 13 | super.operatorPrecedence(tau) 14 | } 15 | 16 | override def toPrettyExpression(tau: Type): PrettyExpression = tau match { 17 | case ProductType(leftType, rightType) => 18 | new PrettyBinaryExpression { 19 | def priority = operatorPrecedence(tau) 20 | def fixity = Infix(LeftAssoc) 21 | 22 | def left = toPrettyExpression(leftType) 23 | def right = toPrettyExpression(rightType) 24 | 25 | def op = UnicodeOutput.choose("×", "*") 26 | } 27 | 28 | case _ => 29 | super.toPrettyExpression(tau) 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/main/scala/ilc/examples/Dummy.scala: -------------------------------------------------------------------------------- 1 | package ilc 2 | package examples 3 | 4 | // ScalaMeter dummy benchmark 5 | // to help experiment with code generation options 6 | // and test if everything works together smoothly 7 | 8 | trait Dummy { 9 | val scalaMeterDummyCode: String = 10 | """|package ilc 11 | |package examples 12 | | 13 | |import org.scalameter.api._ 14 | | 15 | |object DummyGenerated extends PerformanceTest.Quickbenchmark { 16 | | val sizes: Gen[Int] = Gen.range("size")(3000, 15000, 3000) 17 | | 18 | | val ranges: Gen[Range] = for { 19 | | size <- sizes 20 | | } yield 0 until size 21 | | 22 | | performance of "ilc.examples" in { 23 | | performance of "Dummy" in { 24 | | using(ranges) in { 25 | | r => r.map(_ + 1) 26 | | } 27 | | } 28 | | } 29 | |} 30 | |""".stripMargin 31 | } 32 | -------------------------------------------------------------------------------- /src/test/scala/ilc/feature/products/ProductSugarSuite.scala: -------------------------------------------------------------------------------- 1 | package ilc 2 | package feature 3 | package products 4 | 5 | import org.scalatest.FunSuite 6 | 7 | class ProductSugarSuite 8 | extends FunSuite 9 | with SyntaxSugar 10 | with Evaluation 11 | with integers.Evaluation 12 | with integers.ImplicitSyntaxSugar 13 | with functions.Evaluation 14 | with functions.Pretty 15 | { 16 | val quadruple: Term = tuple(4) ! 1 ! 2 ! 3 ! 4 17 | 18 | test("tuple(n) creates a term of tupleType(n)") { 19 | assert(quadruple.getType == tupleType(ℤ, ℤ, ℤ, ℤ)) 20 | } 21 | 22 | test("tuple(n) creates pairs nested toward the right") { 23 | val desugaredQuadruple: Term = Pair ! 1 ! (Pair ! 2 ! (Pair ! 3 ! 4)) 24 | assert(eval(quadruple) === eval(desugaredQuadruple)) 25 | } 26 | 27 | test("projections extract elements") { 28 | (1 to 4) foreach { i => 29 | assert(eval(project(i) ! quadruple).toInt === i) 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/main/scala/ilc/feature/let/ToScala.scala: -------------------------------------------------------------------------------- 1 | package ilc 2 | package feature 3 | package let 4 | 5 | trait ToScala extends Syntax with functions.ToScala { 6 | override def toUntypedScala(t: Term) = { 7 | t match { 8 | case Let(v, exp, body) => 9 | //This does not work when v shadows an existing variable that is used in exp. 10 | //This shows up in one (1!) case in Histogram - that's it. 11 | //However, normalization-by-evaluation freshens the variable, preventing this bug. 12 | //XXX: document the exact interfaces, since we have an interaction between slightly different interfaces (nominal vs. something which I hope is Barendregt), in the area of binding. 13 | s"""${openBrace()} 14 | |${indentNoNl()}lazy val ${toUntypedScala(v)} = ${toScala(exp)} 15 | |${indentNoNl()}${toScala(body)} 16 | |${closeBrace()}""".stripMargin 17 | case _ => super.toUntypedScala(t) 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/main/scala/ilc/feature/products/Evaluation.scala: -------------------------------------------------------------------------------- 1 | package ilc 2 | package feature 3 | package products 4 | 5 | trait Evaluation 6 | extends functions.Evaluation 7 | with Syntax 8 | { 9 | case class PairValue(pair: (Value, Value)) extends Value { 10 | override def toString = pair.toString 11 | } 12 | 13 | implicit class PairValueOps(value: Value) { 14 | def toPair: (Value, Value) = value match { 15 | case PairValue(pair) => pair 16 | case _ => typeErrorNotTheSame("Value.toPair", "PairValue", value) 17 | } 18 | } 19 | 20 | override def coreEval(t: Term, env: Env) = t match { 21 | case Pair(leftType, rightType) => 22 | (left: Value) => (right: Value) => PairValue((left, right)) 23 | 24 | case Proj1(leftType, rightType) => 25 | (pair: Value) => pair.toPair._1 26 | 27 | case Proj2(leftType, rightType) => 28 | (pair: Value) => pair.toPair._2 29 | 30 | case _ => 31 | super.coreEval(t, env) 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/main/scala/ilc/feature/booleans/ToScala.scala: -------------------------------------------------------------------------------- 1 | package ilc 2 | package feature 3 | package booleans 4 | 5 | trait ToScala 6 | extends functions.ToScala 7 | with unit.ToScala 8 | with Syntax 9 | { 10 | override def toScala(tau: Type): String = tau match { 11 | case BooleanType => "Boolean" 12 | case _ => super.toScala(tau) 13 | } 14 | 15 | override def isScalaPrimitive(tau: Type) = tau match { 16 | case BooleanType => true 17 | case _ => super.isScalaPrimitive(tau) 18 | } 19 | 20 | override def toUntypedScala(t: Term): String = t match { 21 | case True => "true" 22 | case False => "false" 23 | 24 | // call-by-name 25 | case IfThenElse(resultType) => { 26 | val unit = toScala(UnitTerm) 27 | val body = s"if (condition) thenBranch($unit) else elseBranch($unit)" 28 | scalaFunction("condition", "thenBranch", "elseBranch")(body) 29 | } 30 | 31 | case _ => 32 | super.toUntypedScala(t) 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/main/scala/ilc/feature/abelianGroups/Derivation.scala: -------------------------------------------------------------------------------- 1 | package ilc 2 | package feature 3 | package abelianGroups 4 | 5 | /** The change to a group is a replacement. 6 | * 7 | * Δ Abelian(e) = Abelian(e) 8 | */ 9 | 10 | trait Derivation extends base.Derivation with Syntax { 11 | override def deltaType(tau: Type): Type = tau match { 12 | case AbelianGroupType(_) => 13 | tau 14 | 15 | case _ => 16 | super.deltaType(tau) 17 | } 18 | 19 | override def updateTerm(tau: Type): Term = tau match { 20 | case _G @ AbelianGroupType(_) => 21 | lambda(_G, _G) { case Seq(dG, _G) => dG } 22 | 23 | case _ => 24 | super.updateTerm(tau) 25 | } 26 | 27 | override def diffTerm(tau: Type): Term = tau match { 28 | case _G @ AbelianGroupType(_) => 29 | lambda(_G, _G) { case Seq(_Gnew, _Gold) => _Gnew } 30 | 31 | case _ => 32 | super.diffTerm(tau) 33 | } 34 | 35 | // do not override derive, do replacement in all cases 36 | } 37 | -------------------------------------------------------------------------------- /src/main/scala/ilc/feature/let/FullErasure.scala: -------------------------------------------------------------------------------- 1 | package ilc 2 | package feature 3 | package let 4 | 5 | trait FullErasure { 6 | outer => 7 | 8 | val syntax: Syntax with IsAtomic with inference.LetUntypedSyntax 9 | import syntax._ 10 | 11 | //XXX would need a better "everywhere" to abstract the traversal. 12 | //But whatever, the types are narrow enough to ensure needed recursive calls 13 | //are done. 14 | 15 | def varName(v: Var) = v.getName 16 | def fullErasure: Term => UntypedTerm = { 17 | case v: Var => 18 | UVar(varName(v)) 19 | case App(s, t) => 20 | UApp(fullErasure(s), fullErasure(t)) 21 | case Abs(v, body) => 22 | UAbs(varName(v), None, fullErasure(body)) 23 | case Let(v, exp, body) => 24 | ULet(varName(v), fullErasure(exp), fullErasure(body)) 25 | case pc: PolymorphicConstant => 26 | UPolymorphicConstant(pc) 27 | case monoConstant if isAtomic(monoConstant) => 28 | UMonomorphicConstant(monoConstant) 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/test/scala/ilc/language/bacchus/ReificationSuite.scala: -------------------------------------------------------------------------------- 1 | package ilc 2 | package language 3 | package bacchus 4 | 5 | import org.scalatest.FunSuite 6 | import ilc.feature._ 7 | 8 | class BacchusReificationSuite 9 | extends FunSuite 10 | with Syntax // for mapLiteral 11 | with Subjects // for type shorthands 12 | with Evaluation 13 | with naturals.Reification 14 | with maps.Reification 15 | with maybe.Reification 16 | with sums.Reification 17 | with base.Pretty 18 | { 19 | test("can reify natural numbers") { 20 | val n: Value = 42 21 | assert(eval(reify(n, ℕ)) === n) 22 | } 23 | 24 | test("can reify maps") { 25 | val valueType = (ℕ ↦ ℕ) ↦ (ℕ ↦ ℕ) 26 | val t = mapLiteral( 27 | EmptyMap.tapply(ℕ, ℕ) -> mapLiteral(0 -> 100), 28 | mapLiteral(1 -> 2, 3 -> 4) -> mapLiteral(5 -> 6, 7 -> 8), 29 | mapLiteral(20 -> 30) -> EmptyMap.tapply(ℕ, ℕ)) 30 | val value = eval(t) 31 | assert(eval(reify(value, valueType)) === value) 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/main/scala/ilc/feature/integers/Evaluation.scala: -------------------------------------------------------------------------------- 1 | package ilc 2 | package feature 3 | package integers 4 | 5 | import scala.language.implicitConversions 6 | 7 | trait Evaluation extends functions.Evaluation with Syntax 8 | { 9 | case class IntValue(i: Int) extends Value { 10 | override def toString = i.toString 11 | } 12 | implicit def liftIntValue(n: Int): Value = IntValue(n) 13 | 14 | implicit class intValueOps(value: Value) { 15 | def toInt: Int = value match { 16 | case IntValue(i) => 17 | i 18 | 19 | case _ => 20 | typeErrorNotTheSame("Value.toInt", "IntValue", value) 21 | } 22 | } 23 | 24 | override def coreEval(t: Term, env: Env): Value = t match { 25 | case LiteralInt(i) => 26 | IntValue(i) 27 | 28 | case PlusInt => 29 | (i: Value) => (j: Value) => IntValue(i.toInt + j.toInt) 30 | 31 | case NegateInt => 32 | (i: Value) => IntValue(- i.toInt) 33 | 34 | case _ => 35 | super.coreEval(t, env) 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/main/scala/ilc/feature/abelianGroups/ToScala.scala: -------------------------------------------------------------------------------- 1 | package ilc 2 | package feature 3 | package abelianGroups 4 | 5 | trait ToScala extends base.ToScala with Syntax { 6 | addLibrary("abelianGroups") 7 | 8 | override def toScala(tau: Type): String = tau match { 9 | case AbelianGroupType(e) => 10 | s"AbelianGroup[${toScala(e)}]" 11 | 12 | case _ => 13 | super.toScala(tau) 14 | } 15 | 16 | override def toUntypedScala(t: Term): String = { 17 | // get abelian group type 18 | // (only works if the first argument of t is 19 | // an abelian group) 20 | t match { 21 | case AbelianGroup(e) => 22 | s"IndexedGroup.curried[${toScala(e)}]" 23 | 24 | case GetBinOp(_) => s"(_.binOp)" 25 | case GetInv(_) => s"(_.inv)" 26 | case GetNeutral(_) => s"(_.neutral)" 27 | 28 | case AreEqualGroups(_) => 29 | scalaFunction("ag1", "ag2")("ag1.isEqualGroup(ag2)") 30 | 31 | case _ => 32 | super.toUntypedScala(t) 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/main/scala/ilc/feature/booleans/Derivation.scala: -------------------------------------------------------------------------------- 1 | package ilc 2 | package feature 3 | package booleans 4 | 5 | /** change to a Boolean = replacement value */ 6 | 7 | trait Derivation extends unit.Derivation with Syntax { 8 | override def deltaType(tau: Type): Type = tau match { 9 | case BooleanType => BooleanType 10 | case _ => super.deltaType(tau) 11 | } 12 | 13 | override def updateTerm(tau: Type): Term = tau match { 14 | case BooleanType => 15 | lambda(BooleanType, BooleanType) { case Seq(db, b) => db } 16 | 17 | case _ => 18 | super.updateTerm(tau) 19 | } 20 | 21 | override def diffTerm(tau: Type): Term = tau match { 22 | case BooleanType => 23 | lambda(BooleanType, BooleanType) { case Seq(bNew, bOld) => bNew } 24 | 25 | case _ => 26 | super.diffTerm(tau) 27 | } 28 | 29 | override def derive(t: Term): Term = t match { 30 | case True => True 31 | case False => False 32 | // recompute on IfThenElse 33 | case _ => super.derive(t) 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /clients/src/main/scala/ilc/examples/MapSuccBaseExample.scala: -------------------------------------------------------------------------------- 1 | package ilc 2 | package examples 3 | 4 | import language.bacchus 5 | 6 | // Example 3 variant: mapping over the values of a map 7 | // program = map succ 8 | // input = Map(1 -> 1, 2 -> 2, 3 -> 3) 9 | // program input = Map(1 -> 2, 2 -> 3, 3 -> 4) 10 | // 11 | // The names of examples can be used elsewhere to access 12 | // a particular example. Rename wisely. 13 | class MapSuccBaseExample 14 | extends Example 15 | with bacchus.Syntax // for syntactic sugars 16 | with bacchus.Prelude 17 | with bacchus.ToScala 18 | with bacchus.BasicDerivation 19 | { 20 | def program: Term = mapWithKey ! constSucc 21 | 22 | // constSucc : ℕ → (ℕ → ℕ) 23 | // constSucc = λk : ℕ. succ 24 | def constSucc: Term = lambda(NatType) { k => succ } 25 | } 26 | 27 | class MapSuccBagsExample 28 | extends Example 29 | with bacchus.Syntax // for syntactic sugars 30 | with bacchus.Prelude 31 | with bacchus.ToScala 32 | with bacchus.BasicDerivation 33 | { 34 | def program: Term = 35 | map ! succ 36 | } 37 | -------------------------------------------------------------------------------- /src/main/scala/ilc/feature/bags/ToScala.scala: -------------------------------------------------------------------------------- 1 | package ilc 2 | package feature 3 | package bags 4 | 5 | trait ToScala 6 | extends abelianGroups.ToScala 7 | with Syntax 8 | { 9 | addLibrary("bags") 10 | 11 | private[this] def mapTypes(v: Type): (String, String) = { 12 | val vType = toScala(v) 13 | ("Bag[%s]".format(vType), vType) 14 | } 15 | 16 | override def toUntypedScala(t: Term): String = 17 | t match { 18 | case EmptyBag(v) => 19 | s"bagEmpty" 20 | case Singleton(v) => 21 | s"bagSingleton" 22 | case Union(v) => 23 | s"bagUnion" 24 | case Negate(v) => 25 | s"bagNegate" 26 | case FoldGroup(b, v) => 27 | s"bagFoldGroup" 28 | case FreeAbelianGroup(v) => 29 | s"FreeAbelianGroup()" 30 | case _ => 31 | super.toUntypedScala(t) 32 | } 33 | 34 | override def toScala(tau: Type): String = 35 | tau match { 36 | case BagType(valType) => 37 | mapTypes(valType)._1 38 | 39 | case _ => 40 | super.toScala(tau) 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/main/scala/ilc/util/IndentUtils.scala: -------------------------------------------------------------------------------- 1 | package ilc 2 | package util 3 | 4 | trait IndentUtils { 5 | protected val indentDiff: Int = 2 6 | protected def initialIndentDepth: Int = 0 7 | private var indentDepth: Int = initialIndentDepth 8 | 9 | protected def setIndentDepth(i: Int): Unit = { indentDepth = i } 10 | 11 | protected def indentMore(delta: Int = indentDiff): String = { indentDepth += delta; "" } 12 | protected def indentLess(delta: Int = indentDiff): String = { indentDepth -= delta; "" } 13 | protected def deeper(arg: => String, indentDelta: Int = indentDiff): String = { 14 | indentMore(indentDelta) 15 | val ret = arg 16 | indentLess(indentDelta) 17 | ret 18 | } 19 | 20 | protected def indentNoNl(): String = " " * indentDepth 21 | protected def indent(): String = "\n" + indentNoNl 22 | protected def openParen(delim: String) = { indentMore(); delim } 23 | protected def closeParen(delim: String) = { indentLess(); s"$indent$delim" } 24 | 25 | protected def openBrace() = openParen("{") 26 | protected def closeBrace() = closeParen("}") 27 | } 28 | -------------------------------------------------------------------------------- /src/test/scala/ilc/feature/bags/BagChanges.scala: -------------------------------------------------------------------------------- 1 | package ilc 2 | package feature 3 | package bags 4 | 5 | /** bag changes for generated bags */ 6 | 7 | trait BagChanges { 8 | import Library._ 9 | import abelianGroups.Library._ 10 | 11 | type ChangeToBags[T] = 12 | Either[(AbelianGroup[Bag[T]], Bag[T]), Bag[T]] 13 | 14 | def changesToBagsOfIntegers: Map[String, Int => ChangeToBags[Int]] = 15 | Map("no change" -> (n => mkChange(bagEmpty)), 16 | "replace 1 by n + 1" -> (n => replace(1, n + 1)), 17 | "add n + 2" -> (n => add(n + 2)), 18 | "remove 2" -> (n => remove(2)), 19 | "remove n" -> (n => remove(n))) 20 | 21 | private def mkChange[T](element: Bag[T]): ChangeToBags[T] = 22 | Left((FreeAbelianGroup.apply[T], element)) 23 | 24 | private def add[T](e: T) = 25 | mkChange(bagSingleton(e)) 26 | 27 | private def remove[T](e: T) = 28 | mkChange(bagNegate(bagSingleton(e))) 29 | 30 | private def replace[T](a: T, b: T) = 31 | mkChange(bagUnion(bagNegate(bagSingleton(a)))(bagSingleton(b))) 32 | } 33 | -------------------------------------------------------------------------------- /clients/src/main/scala/ilc/examples/BagUnionExample.scala: -------------------------------------------------------------------------------- 1 | package ilc 2 | package examples 3 | 4 | import feature._ 5 | 6 | // program = uncurry union 7 | // input = (Bag(1, 2, ..., 1997), Bag(-1, -2, ..., -1997) 8 | // output = Bag(-1997, -1996, ..., 1997) 9 | class BagUnionExample 10 | extends Example 11 | with functions.Pretty 12 | with inference.PrettySyntax 13 | 14 | with bags.StdLib 15 | with products.Syntax 16 | 17 | // context-free derivations 18 | with products.Derivation 19 | 20 | // context-sensitive derivations 21 | with bags.AbelianDerivation 22 | with integers.AbelianDerivation 23 | 24 | // code generation 25 | with bags.ToScala 26 | with booleans.ToScala 27 | with functions.ToScala 28 | with integers.ToScala 29 | with products.ToScala 30 | with sums.ToScala 31 | { 32 | val inputType = ProductType(BagType(IntType), BagType(IntType)) 33 | 34 | // λ pair : inputType. 35 | // union (proj₁ pair) (proj₂ pair) 36 | def untypedProgram: UntypedTerm = 37 | 'pair % inputType ->: Union(Proj1('pair), Proj2('pair)) 38 | 39 | def program: Term = untypedTermToTerm(untypedProgram) 40 | 41 | } 42 | -------------------------------------------------------------------------------- /src/main/scala/ilc/feature/maps/ToScala.scala: -------------------------------------------------------------------------------- 1 | package ilc 2 | package feature 3 | package maps 4 | 5 | trait ToScala extends base.ToScala with Syntax { 6 | private[this] def mapTypes(k: Type, v: Type): (String, String, String) = { 7 | val (kType, vType) = (toScala(k), toScala(v)) 8 | ("Map[%s, %s]".format(kType, vType), kType, vType) 9 | } 10 | 11 | override def toUntypedScala(t: Term): String = t match { 12 | case EmptyMap(k, v) => 13 | "Map.empty" 14 | 15 | case Update(k, v) => 16 | scalaFunction("k", "v", "m")("m.updated(k, v)") 17 | 18 | case Lookup(k, v) => 19 | scalaFunction("k", "m")("m.get(k)") 20 | 21 | case Delete(k, v) => 22 | scalaFunction("k", "m")("m - k") 23 | 24 | case Fold(k, a, b) => 25 | scalaFunction("f", "z", "m") { 26 | s"m.foldRight[${toScala(b)}](z)((p, b) => f(p._1)(p._2)(b))" 27 | } 28 | 29 | case _ => 30 | super.toUntypedScala(t) 31 | } 32 | 33 | override def toScala(tau: Type): String = tau match { 34 | case MapType(keyType, valType) => 35 | mapTypes(keyType, valType)._1 36 | 37 | case _ => 38 | super.toScala(tau) 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/test/scala/ilc/feature/base/TermBuilderSuite.scala: -------------------------------------------------------------------------------- 1 | package ilc 2 | package feature 3 | package base 4 | 5 | import org.scalatest.FunSuite 6 | 7 | class TermBuilderSuite 8 | extends FunSuite 9 | with base.Syntax 10 | with functions.Syntax // for term application operator ! 11 | with Pretty 12 | { 13 | case object Bot extends Type { override def toString = "⊥" } 14 | case object Top extends Type { override def toString = "⊤" } 15 | case object TT extends Term { lazy val getType: Type = Top } 16 | 17 | test("SpecializedTerm detects mismatched argument type") { 18 | val typeError = intercept[TypeError] { 19 | SpecializedTerm(Var("x", Top =>: Top)) specialize Bot 20 | } 21 | info(typeError.getMessage) 22 | } 23 | 24 | // id2 : ∀T. (T → T) → (T → T) 25 | object id2 extends ConstantWith1TypeParameter { 26 | val typeConstructor = TypeConstructor("T") { T => 27 | (T =>: T) =>: (T =>: T) 28 | } 29 | } 30 | 31 | test("Polymorphic constants detect mismatched argument type") { 32 | val typeError = intercept[TypeError] { 33 | val t: Term = id2 ! TT 34 | } 35 | info(typeError.getMessage) 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/main/scala/ilc/feature/let/Pretty.scala: -------------------------------------------------------------------------------- 1 | package ilc 2 | package feature 3 | package let 4 | 5 | import org.kiama.output._ 6 | 7 | trait Pretty extends functions.Pretty with Syntax { 8 | override def operatorPrecedence(t: Term): Int = t match { 9 | case Let(variable, exp, body) => 10 | // let-expressions binds as loosely as lambda expressions 11 | super.operatorPrecedence(Abs(variable, body)) 12 | 13 | case _ => 14 | super.operatorPrecedence(t) 15 | } 16 | 17 | override def toPrettyExpression(t: Term): PrettyExpression = 18 | t match { 19 | case Let(x, xdef, body) => 20 | new PrettyEnclosingExpression { 21 | def priority = operatorPrecedence(t) 22 | def fixity = Prefix 23 | 24 | def op = 25 | group( 26 | text("let") <> 27 | nest(line <> 28 | text(x.getName.toString) <+> text("=") <> 29 | nest(line <> toDoc(xdef)) 30 | ) <> line <> 31 | text("in")) 32 | 33 | def exp = toPrettyExpression(body) 34 | } 35 | 36 | case _ => 37 | super.toPrettyExpression(t) 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/main/scala/ilc/feature/booleans/Evaluation.scala: -------------------------------------------------------------------------------- 1 | package ilc 2 | package feature 3 | package booleans 4 | 5 | trait Evaluation 6 | extends functions.Evaluation 7 | with unit.Evaluation 8 | with Syntax { 9 | case class BooleanValue(toBoolean: Boolean) extends Value { 10 | override def toString = toBoolean.toString 11 | } 12 | 13 | implicit class BooleanValueOps(value: Value) { 14 | def toBoolean: Boolean = value match { 15 | case BooleanValue(boo) => 16 | boo 17 | 18 | case _ => 19 | typeErrorNotTheSame("Value.toBoolean", "BooleanValue", value) 20 | } 21 | } 22 | 23 | override def coreEval(t: Term, env: Env): Value = t match { 24 | case True => 25 | BooleanValue(true) 26 | 27 | case False => 28 | BooleanValue(false) 29 | 30 | // call-by-value. 31 | // sound by purity. 32 | case IfThenElse(_) => 33 | (condition: Value) => (thenBranch: Value) => (elseBranch: Value) => 34 | condition match { 35 | case BooleanValue(satisfied) => 36 | if (satisfied) thenBranch(UnitValue) else elseBranch(UnitValue) 37 | } 38 | 39 | case _ => 40 | super.coreEval(t, env) 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/main/scala/ilc/feature/base/FreshGen.scala: -------------------------------------------------------------------------------- 1 | package ilc 2 | package feature 3 | package base 4 | 5 | import scala.language.implicitConversions 6 | import scala.language.postfixOps 7 | 8 | /** 9 | * A reusable freshname generator. Since this contains mutable state, this module 10 | * is designed to be imported via composition, not by mixing it in. 11 | */ 12 | trait FreshGen { 13 | val syntax: Syntax 14 | import syntax._ 15 | 16 | //Have a very simple and reliable fresh variable generator. Tracking free 17 | //variables might have been the performance killer of the other normalizer. 18 | var index = 0 19 | def resetIndex() { 20 | index = 0 21 | } 22 | 23 | def freshName(varName: Name): Name = { 24 | index += 1 25 | val compress = false 26 | val baseName = 27 | if (compress) 28 | "z": NonIndexedName 29 | else 30 | //Keep names, for extra readability. 31 | decomposeName(varName)._1 32 | //IndexedName("z", index) 33 | IndexedName(baseName, index) 34 | } 35 | 36 | def fresh(varName: Name, varType: Type): Var = { 37 | Var(freshName(varName), varType) 38 | } 39 | 40 | def fresh(v: Var): Var = fresh(v.getName, v.getType) 41 | } 42 | -------------------------------------------------------------------------------- /src/test/scala/ilc/language/bacchus/CorrectnessAssertion.scala: -------------------------------------------------------------------------------- 1 | package ilc 2 | package language 3 | package bacchus 4 | 5 | import org.scalatest.FunSuite 6 | 7 | trait CorrectnessAssertion 8 | extends FunSuite 9 | with feature.base.Derivation 10 | with FineGrainedDifference 11 | with ChangingTerms 12 | { 13 | private[this] 14 | def assembleTerm(operator: Term, operands: Seq[Term]): Term = 15 | if (operands.isEmpty) 16 | operator 17 | else 18 | assembleTerm((operator ! operands.head), operands.tail) 19 | 20 | def assertCorrect(t: Term, args: ChangingTerms*) { 21 | val oldTerm = assembleTerm(t, args.map(_.oldTerm)) 22 | 23 | val newOutput = eval(assembleTerm(t, args.map(_.newTerm))) 24 | 25 | val replacement = assembleTerm(derive(t), args flatMap { terms => 26 | List(terms.oldTerm, (Diff ! terms.newTerm ! terms.oldTerm).toTerm) 27 | }) 28 | 29 | val surgery = assembleTerm(derive(t), args flatMap { terms => 30 | List(terms.oldTerm, fineGrainedDiff(terms.newTerm, terms.oldTerm)) 31 | }) 32 | 33 | List(replacement, surgery) foreach { changeTerm => 34 | assert(eval(ChangeUpdate ! changeTerm ! oldTerm) === newOutput) 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/main/scala/ilc/feature/abelianGroups/Library.scala: -------------------------------------------------------------------------------- 1 | package ilc 2 | package feature 3 | package abelianGroups 4 | 5 | object Library extends base.Library { 6 | trait AbelianGroup[T] { 7 | def binOp: (=>T) => (=>T) => T 8 | def inv: (=>T) => T 9 | def neutral: T 10 | def isEqualGroup(that: AbelianGroup[T]): Boolean 11 | 12 | override def equals(that: Any): Boolean = that match { 13 | case that: AbelianGroup[T] => 14 | this.isEqualGroup(that) 15 | 16 | case _ => 17 | false 18 | } 19 | } 20 | 21 | object IndexedGroup { 22 | def curried[T]: 23 | (=> Int) => 24 | (=> ((=>T) => (=>T) => T)) => (=>((=>T) => T)) => (=>T) => 25 | AbelianGroup[T] = 26 | id => binOp => inv => neutral => 27 | IndexedGroup(id, binOp, inv, neutral) 28 | } 29 | 30 | case class IndexedGroup[T]( 31 | id: Int, 32 | binOp: (=>T) => (=>T) => T, 33 | inv: (=>T) => T, 34 | neutral: T 35 | ) 36 | extends AbelianGroup[T] 37 | { 38 | def isEqualGroup(that: AbelianGroup[T]): Boolean = that match { 39 | case IndexedGroup(thatId, _, _, _) => 40 | this.id == thatId 41 | 42 | case _ => 43 | false 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) (2013, 2014) Yufei Cai, Paolo G. Giarrusso, Tillmann Rendel, 4 | Klaus Ostermann, Jonathan Brachthäuser 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | -------------------------------------------------------------------------------- /src/main/scala/ilc/feature/let/ToHaskell.scala: -------------------------------------------------------------------------------- 1 | package ilc 2 | package feature 3 | package let 4 | 5 | trait ToHaskell { 6 | outer => 7 | 8 | val syntax: inference.LetUntypedSyntax with base.Syntax with IsAtomic 9 | import syntax._ 10 | 11 | def untypedToHaskell: UntypedTerm => String = { 12 | case UVar(name) => 13 | name.toString 14 | case UApp(s, t) => 15 | s"${untypedToHaskell(s)} ${untypedToHaskell(t)}" 16 | case UAbs(v, None, body) => 17 | s"\\ $v -> ${untypedToHaskell(body)}" 18 | case ULet(v, exp, body) => 19 | //Consider creating top-level definitions when exp is closed or when the 20 | //let is at the top-level. 21 | s"let $v = ${untypedToHaskell(exp)} in ${untypedToHaskell(body)}" 22 | case UPolymorphicConstant(pConst) => 23 | toHaskell(pConst) 24 | case UMonomorphicConstant(mConst) => 25 | toHaskell(mConst) 26 | } 27 | 28 | def toHaskell(pConst: PolymorphicConstant): String = pConst match { 29 | case _ => 30 | sys error s"Unknown polymorphic constant $pConst" 31 | } 32 | 33 | def toHaskell(t: Term): String = { 34 | assert(isConst(t)) 35 | t match { 36 | case _ => 37 | sys error s"Unknown term $t" 38 | } 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/main/scala/ilc/feature/integers/Syntax.scala: -------------------------------------------------------------------------------- 1 | package ilc 2 | package feature 3 | package integers 4 | 5 | import scala.language.implicitConversions 6 | 7 | 8 | trait BaseSyntax 9 | extends base.Syntax 10 | with Types 11 | with functions.Types 12 | { 13 | case class LiteralInt(i: Int) extends Term { 14 | override lazy val getType: Type = IntType 15 | } 16 | } 17 | 18 | trait Syntax extends BaseSyntax { 19 | case object PlusInt extends Term { 20 | override lazy val getType: Type = IntType =>: IntType =>: IntType 21 | } 22 | 23 | case object NegateInt extends Term { 24 | override lazy val getType: Type = IntType =>: IntType 25 | } 26 | 27 | // ℤ can be folded over just like ℕ ⊎ ℕ. 28 | // foldInt isn't used yet. to add later? 29 | } 30 | 31 | trait SyntaxSugar 32 | extends Syntax 33 | with abelianGroups.SyntaxSugar 34 | { 35 | import Library._ 36 | 37 | val additiveGroupOnIntegers: Term = 38 | AbelianGroup ! LiteralInt(additionIndexedGroupStamp) ! 39 | PlusInt ! NegateInt ! LiteralInt(0) 40 | } 41 | 42 | trait ImplicitBaseSyntaxSugar extends BaseSyntax { 43 | implicit def intToTerm(n: Int): Term = LiteralInt(n) 44 | } 45 | 46 | trait ImplicitSyntaxSugar extends ImplicitBaseSyntaxSugar with SyntaxSugar 47 | -------------------------------------------------------------------------------- /clients/src/test/scala/ilc/examples/MapSuccGeneratedSuite.scala: -------------------------------------------------------------------------------- 1 | package ilc 2 | package examples 3 | 4 | import org.scalatest.FunSuite 5 | import ilc.feature.bags.BagChanges 6 | import MapSuccGenerated._ 7 | 8 | class MapSuccGeneratedSuite 9 | extends FunSuite 10 | with BagChanges 11 | { 12 | val n = 100 13 | 14 | val oldInput: InputType = (1 to n).map(i => i -> 1)(collection.breakOut) 15 | val oldOutput = program(oldInput) 16 | 17 | test("the compiled derivative increments collections") { 18 | val expected: OutputType = for { 19 | (element, multiplicity) <- oldInput 20 | } yield (element + 1, multiplicity) 21 | assert(oldOutput === expected) 22 | } 23 | 24 | test("the compiled derivative is correct") { 25 | import collection.immutable.TreeMap // for sorted print-out 26 | changes foreach { change => 27 | val outputChange = derivative(oldInput)(change) 28 | val updatedOutput = updateOutput(outputChange)(oldOutput) 29 | val newInput = updateInput(change)(oldInput) 30 | assert( 31 | TreeMap(updatedOutput.toSeq: _*) === 32 | TreeMap(program(newInput).toSeq: _*)) 33 | } 34 | } 35 | 36 | val changes: Iterable[ChangeToBags[Int]] = 37 | changesToBagsOfIntegers.map(_ _2 n) 38 | 39 | } 40 | -------------------------------------------------------------------------------- /src/main/scala/ilc/feature/naturals/ReplacementValuesDerivation.scala: -------------------------------------------------------------------------------- 1 | package ilc 2 | package feature 3 | package naturals 4 | 5 | /** ΔNat = Nat 6 | * (The change to a natural number is a replacement value) 7 | */ 8 | trait ReplacementValuesDerivation 9 | extends base.Derivation 10 | with Syntax 11 | with functions.Derivation // for syntactic sugar 12 | { 13 | override def deltaType(tau: Type): Type = tau match { 14 | case NatType => NatType 15 | case _ => super.deltaType(tau) 16 | } 17 | 18 | override def updateTerm(tau: Type): Term = tau match { 19 | case NatType => 20 | lambda(NatType, NatType) { case Seq(dx, x) => dx } 21 | 22 | case _ => 23 | super.updateTerm(tau) 24 | } 25 | 26 | override def diffTerm(tau: Type): Term = tau match { 27 | case NatType => 28 | lambda(NatType, NatType) { case Seq(m, n) => m } 29 | 30 | case _ => 31 | super.diffTerm(tau) 32 | } 33 | 34 | override def derive(t: Term): Term = t match { 35 | case Nat(n) => 36 | Nat(n) 37 | 38 | case PlusNat => 39 | lambdaDelta(t) { case Seq(x, dx, y, dy) => PlusNat ! dx ! dy } 40 | 41 | // case FoldNat => ... // we will use the default FoldNat ⊖ FoldNat 42 | case _ => 43 | super.derive(t) 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/test/scala/ilc/feature/base/TypeInversionTest.scala: -------------------------------------------------------------------------------- 1 | package ilc 2 | package feature 3 | package base 4 | 5 | import org.scalatest.FunSuite 6 | 7 | class TypeInversionTest 8 | extends FunSuite 9 | with functions.Syntax // for application 10 | with Pretty 11 | { 12 | case object Bot extends Type 13 | case class T1(inner: Type) extends Type 14 | case class T2(inner: Type) extends Type 15 | object OperatorT1 extends ConstantWith1TypeParameter { 16 | val typeConstructor = TypeConstructor("inner") { inner => 17 | T1(inner) =>: inner 18 | } 19 | } 20 | 21 | object OperandT2 extends Term { 22 | override lazy val getType: Type = T2(Bot) 23 | } 24 | 25 | test("can distinguish type constructors of the same arity") { 26 | val error = intercept[TypeError] { 27 | val illTypedTerm = (OperatorT1 ! OperandT2).toTerm 28 | } 29 | info(error.getMessage) 30 | } 31 | 32 | case object T3 extends Type { override def toString = "T3" } 33 | object OperatorT3 extends ConstantWith1TypeParameter { 34 | val typeConstructor = TypeConstructor("r") { r => 35 | T3 =>: r =>: r 36 | } 37 | } 38 | 39 | test("leaves of an abstract type tree can be non-case objects") { 40 | OperatorT3 ofType T3 =>: Bot =>: Bot 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/test/scala/ilc/feature/sums/DeltaSumSuite.scala: -------------------------------------------------------------------------------- 1 | package ilc 2 | package feature 3 | package sums 4 | 5 | import org.scalatest.FunSuite 6 | 7 | class DeltaSumSuite 8 | extends FunSuite 9 | with ReplacementValuesDerivation 10 | with Evaluation 11 | with integers.AbelianDerivation 12 | with integers.Evaluation 13 | with integers.ImplicitSyntaxSugar 14 | with base.Pretty 15 | { 16 | implicit class SumTypeOps(sigma: Type) { 17 | def ⊎ (tau: Type): Type = 18 | SumType(sigma, tau) 19 | } 20 | 21 | //XXX abstract more 22 | val oldSum = Inj1.tapply(ℤ) ! 5 23 | val swapped = Inj2.tapply(ℤ) ! 7 24 | val operated = Inj1.tapply(ℤ) ! 9 25 | 26 | val replace = Inj2.tapply(deltaType(ℤ) ⊎ deltaType(ℤ)) 27 | 28 | val replacement = replace ! swapped 29 | val surgery = Inj1.tapply(ℤ ⊎ ℤ) ! (Inj1.tapply(deltaType(ℤ)) ! (replacementChange ! 9)) 30 | 31 | test("updateTerm behaves as expected") { 32 | assert(eval(ChangeUpdate ! replacement ! oldSum) === eval(swapped)) 33 | assert(eval(ChangeUpdate ! surgery ! oldSum) === eval(operated)) 34 | } 35 | 36 | test("diffTerm produces replacement") { 37 | assert(eval(Diff ! swapped ! oldSum) === eval(replace ! swapped)) 38 | assert(eval(Diff ! operated ! oldSum) === eval(replace ! operated)) 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/main/scala/ilc/feature/functions/Evaluation.scala: -------------------------------------------------------------------------------- 1 | package ilc 2 | package feature 3 | package functions 4 | 5 | /** 6 | * Evaluating untyped lambda calculus 7 | * extensible by constants and primitives 8 | */ 9 | 10 | import scala.collection.immutable 11 | import scala.language.implicitConversions 12 | 13 | trait Evaluation extends base.Evaluation with Syntax { 14 | override def coreEval(t: Term, env: Env): Value = 15 | t match { 16 | case Abs(x, t) => 17 | (arg: Value) => wrapEval(t, env.updated(x.getName, arg)) 18 | case App(s, t) => 19 | wrapEval(s, env)(wrapEval(t, env)) 20 | case _ => 21 | super.coreEval(t, env) 22 | } 23 | 24 | case class FunctionValue(operator: Value => Value) extends Value 25 | 26 | implicit class FunctionValueOps(value: Value) { 27 | // "toFunction" 28 | def apply(arg: Value): Value = 29 | value match { 30 | case FunctionValue(f) => f(arg) 31 | case _ => value die("apply", arg) 32 | } 33 | } 34 | 35 | implicit def liftFunctionValue[T <% Value](f: Value => T): Value = 36 | FunctionValue(x => f(x)) 37 | // `f` is written pointfully so that implicit conversion 38 | // on the return value of `f` may kick in to make the 39 | // whole expression well-typed 40 | } 41 | -------------------------------------------------------------------------------- /src/main/scala/ilc/feature/products/Types.scala: -------------------------------------------------------------------------------- 1 | package ilc 2 | package feature 3 | package products 4 | 5 | import ilc.util.ExtractorTrait 6 | 7 | trait Types extends base.Types with ExtractorTrait { 8 | case class ProductType(leftType: Type, rightType: Type) extends Type { 9 | override def traverse(f: Type => Type) = copy(f(leftType), f(rightType)) 10 | } 11 | 12 | /** As in Agda, tuples are nested toward the right. 13 | * If tuples are nested toward the left, 14 | * then proj(i) is ill-defined. 15 | */ 16 | 17 | def tupleType(elementTypes: Type*): Type = 18 | if (elementTypes.size == 1) 19 | elementTypes.head 20 | else 21 | ProductType(elementTypes.head, tupleType(elementTypes.tail: _*)) 22 | 23 | def tupleTypeExtractor(n: Int): Extractor[Type, Seq[Type]] = 24 | new Extractor[Type, Seq[Type]] { 25 | def unapply(t: Type): Option[Seq[Type]] = t match { 26 | case ProductType(leftType, rightType) => 27 | if (n == 2) 28 | Some(List(leftType, rightType)) 29 | else if (n > 2) 30 | tupleTypeExtractor(n - 1).unapply(rightType). 31 | fold[Option[Seq[Type]]](None)(seq => Some(leftType +: seq)) 32 | else 33 | sys error s"${n}-tuples are not supported" 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /clients/src/main/scala/ilc/examples/HistogramExample.scala: -------------------------------------------------------------------------------- 1 | package ilc 2 | package examples 3 | 4 | import feature._ 5 | 6 | class HistogramExample 7 | extends Example 8 | with MapReduce 9 | with integers.SyntaxSugar 10 | with integers.AbelianDerivation 11 | with integers.ToScala 12 | { 13 | // userMap : ℤ → Bag ℤ → Bag (ℤ × ℤ) 14 | // userMap = λ ignoredDocumentID : ℤ. 15 | // foldGroup freeAbelianGroup 16 | // (λ number : ℤ. singleton (pair number 1)) 17 | val userMap: UntypedTerm = 'ignoredDocumentId ->: FoldGroup(FreeAbelianGroup, 18 | 'number ->: Singleton(Pair('number, LiteralInt(1)))) 19 | 20 | // userReduce : ℤ → Bag ℤ → ℤ 21 | // userReduce = λ ignoredKey : ℤ. 22 | // foldGroup additiveGroupOnIntegers (λx : ℤ. x) 23 | val userReduce = 'ignoredKey ->: FoldGroup(additiveGroupOnIntegers, 'x ->: 'x) 24 | 25 | // program : Map ℤ (Bag ℤ) → Map ℤ ℤ 26 | // program = 27 | // mapReduce 28 | // freeAbelianGroup 29 | // additiveGroupOnIntegers 30 | // userMap 31 | // userReduce 32 | val untypedProgram: UntypedTerm = 33 | mapReduce( 34 | FreeAbelianGroup, 35 | additiveGroupOnIntegers, 36 | userMap, 37 | userReduce) ofType MapType(ℤ, BagType(ℤ)) =>: MapType(ℤ, ℤ) 38 | 39 | val program: Term = untypedTermToTerm(untypedProgram) 40 | } 41 | -------------------------------------------------------------------------------- /src/test/scala/ilc/feature/integers/IntegerSuite.scala: -------------------------------------------------------------------------------- 1 | package ilc 2 | package feature 3 | package integers 4 | 5 | import org.scalatest.FunSuite 6 | import ilc.util.EvalGenerated 7 | import ilc.language.bacchus 8 | 9 | class IntegerSuite 10 | extends FunSuite 11 | with ImplicitSyntaxSugar 12 | with Evaluation 13 | with ToScala 14 | with bacchus.Evaluation 15 | with bacchus.BasicDerivation 16 | with bacchus.ToScala 17 | with EvalGenerated 18 | with functions.Pretty 19 | with AbelianDerivation 20 | { 21 | def expectToGet(i: Int)(t: => Term) { 22 | assert(eval(t) === IntValue(i)) 23 | try { assert(evalGenerated(t) === i) } 24 | catch { case e: Throwable => 25 | info(e.getMessage) 26 | info(pretty(t)) 27 | info(toScala(t)) 28 | fail 29 | } 30 | } 31 | 32 | test("2 + 2 = 4") { expectToGet( 4) { PlusInt ! 2 ! 2 } } 33 | test("2 - 4 = -2") { expectToGet(-2) { PlusInt ! 2 ! (NegateInt ! 4) } } 34 | 35 | test("i ⊕ (j ⊝ i) = j") { 36 | val (i, j) = (4, 2) 37 | expectToGet(j) { ChangeUpdate ! (Diff ! j ! i) ! i } 38 | } 39 | 40 | test("-4 ⊕ (+ 20) = 16") { 41 | val plus20: Term = Inj1.tapply(ℤ) ! 42 | (Pair ! additiveGroupOnIntegers ! 20) 43 | assert(plus20.getType === deltaType(IntType)) 44 | expectToGet(16) { ChangeUpdate ! plus20 ! -4 } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/main/scala/ilc/feature/booleans/Syntax.scala: -------------------------------------------------------------------------------- 1 | package ilc 2 | package feature 3 | package booleans 4 | 5 | import scala.language.implicitConversions 6 | 7 | trait Syntax extends unit.Syntax with Types with functions.Types { 8 | case object True extends Term { lazy val getType = BooleanType } 9 | case object False extends Term { lazy val getType = BooleanType } 10 | 11 | case object IfThenElse extends ConstantWith1TypeParameter { 12 | val typeConstructor = TypeConstructor("resultType") { r => 13 | val rCBN = UnitType =>: r 14 | BooleanType =>: rCBN =>: rCBN =>: r 15 | } 16 | } 17 | 18 | implicit def booleanToTerm(b0: Boolean): Term = 19 | if (b0) True else False 20 | } 21 | 22 | trait SyntaxSugar extends Syntax with functions.Syntax { 23 | def ifThenElse(condition : TermBuilder, 24 | thenBranch: TermBuilder, 25 | elseBranch: TermBuilder): TermBuilder = 26 | IfThenElse ! condition ! 27 | mkIfThenElseBranch(thenBranch) ! 28 | mkIfThenElseBranch(elseBranch) 29 | 30 | def mkIfThenElseBranch(t: TermBuilder): TermBuilder = 31 | lambda(Var("unit", UnitType)) { unit => t } 32 | 33 | def andTerm: Term = 34 | lambda(Var("cond1", BooleanType), Var("cond2", BooleanType)) { 35 | case Seq(cond1, cond2) => 36 | ifThenElse(cond1, cond2, False) 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/main/scala/ilc/util/EvalScala.scala: -------------------------------------------------------------------------------- 1 | package ilc 2 | package util 3 | 4 | /** 5 | * "eval" for scala 6 | * CAUTION: code is evaluated in empty context. 7 | * Local names are _not_ visible. 8 | */ 9 | 10 | import ilc.util.process.FunProcess 11 | 12 | // Using scala.reflect internal for now. 13 | // With the soon-to-be-released scala-2.11, 14 | // we can switch to JSR-223: 15 | // javax.script.ScriptEngineManager.getEngineByName("scala") 16 | // 17 | // Ref. 18 | // http://stackoverflow.com/a/12123609 19 | 20 | import scala.reflect.runtime._ 21 | import scala.tools.reflect.ToolBox 22 | 23 | trait EvalScala 24 | { 25 | /** 26 | * Evaluate scala code and load the class. 27 | * Warning: because of a ToolBox bug (https://issues.scala-lang.org/browse/SI-6393), 28 | * using imports is only partly supported. 29 | */ 30 | def evalScala(scalaCode: String): Any = { 31 | toolBox.eval(toolBox.parse(scalaCode)) 32 | } 33 | 34 | val toolBox = getToolbox(this) 35 | 36 | def getToolbox(scopeObject: Any) = 37 | universe.runtimeMirror(scopeObject.getClass.getClassLoader).mkToolBox() 38 | } 39 | 40 | trait EvalGenerated extends feature.base.ToScala with EvalScala { 41 | def evalGenerated(t: Term): Any = 42 | evalScala(s"""|{ 43 | | $imports 44 | | ${toScala(t)} 45 | |}""".stripMargin) 46 | } 47 | -------------------------------------------------------------------------------- /src/main/scala/ilc/feature/functions/Types.scala: -------------------------------------------------------------------------------- 1 | package ilc 2 | package feature 3 | package functions 4 | 5 | trait Types extends base.Types { 6 | typesTrait => 7 | /** 8 | * We will be writing function types a lot, thus the right- 9 | * associative infix notation. For example, it can be used as 10 | * 11 | * (foo =>: foo) =>: foo =>: foo 12 | * 13 | * to create the type of Church numerals for base type foo. 14 | */ 15 | case class =>: (domain: Type, range: Type) extends Type { 16 | override def traverse(f: Type => Type) = copy(f(domain), f(range)) 17 | } 18 | 19 | def getArgumentTypes(functionType: Type): List[Type] = 20 | functionType match { 21 | case domain =>: range => 22 | domain :: getArgumentTypes(range) 23 | 24 | case _ => 25 | Nil 26 | } 27 | 28 | // sugar, so that we may write `NatType =>: NatType =>: NatType` 29 | // to construct a type. Because the method `=>:` ends with colon, 30 | // it is right-associative and is invoked on the object on the 31 | // right hand side. 32 | // 33 | // feature description: 34 | // Scala Language Specification §6.12.3 35 | // 36 | // similar code in standard library: 37 | // http://goo.gl/emIQZf 38 | // 39 | implicit class FunctionsTypeOps(range: Type) { 40 | def =>: (domain: Type): Type = 41 | new typesTrait.=>:(domain, range) 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/main/scala/ilc/feature/naturals/Evaluation.scala: -------------------------------------------------------------------------------- 1 | package ilc 2 | package feature 3 | package naturals 4 | 5 | import scala.language.implicitConversions 6 | 7 | // EvaluationBase is the part of Evaluation which does not depend on functions. 8 | // See comments in feature.sum.EvaluationBase for more info. 9 | trait EvaluationBase extends feature.base.Evaluation with Syntax { 10 | 11 | case class NatValue(toNat: Int) extends Value { 12 | require(toNat >= 0) 13 | 14 | override def toString = toNat.toString 15 | } 16 | 17 | implicit def liftNatValue(n: Int): Value = NatValue(n) 18 | implicit class NatOps(value: Value) { 19 | def toNat: Int = 20 | value match { 21 | case NatValue(n) => n 22 | case _ => value die "toNat" 23 | } 24 | } 25 | } 26 | 27 | trait Evaluation 28 | extends EvaluationBase 29 | with functions.Evaluation 30 | { 31 | override def coreEval(t: Term, env: Env): Value = t match { 32 | case Nat(n) => 33 | n 34 | 35 | case FoldNat(_) => 36 | (z: Value) => (f: Value) => (n: Value) => { 37 | def loop(i: Int): Value = 38 | if (i == 0) 39 | z 40 | else 41 | f(loop(i - 1)) 42 | loop(n.toNat) 43 | } 44 | 45 | case PlusNat => 46 | (x: Value) => (y: Value) => x.toNat + y.toNat 47 | 48 | case _ => 49 | super.coreEval(t, env) 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/test/scala/ilc/feature/maybe/DeltaMaybeSuite.scala: -------------------------------------------------------------------------------- 1 | package ilc 2 | package feature 3 | package maybe 4 | 5 | import org.scalatest.FunSuite 6 | 7 | class DeltaMaybeSuite 8 | extends FunSuite 9 | with ReplacementValuesDerivation 10 | with Evaluation 11 | with integers.AbelianDerivation 12 | with integers.Evaluation 13 | with integers.ImplicitSyntaxSugar 14 | with sums.Evaluation 15 | with base.Pretty 16 | { 17 | //XXX abstract more? 18 | val toNope = Inj2.tapply(deltaType(ℤ)) ! Nope.tapply(ℤ) 19 | 20 | val to42 = Inj2.tapply(deltaType(ℤ)) ! (Just ! 42) 21 | 22 | def toJust(i: Int) = Inj1.tapply(MaybeType(ℤ)) ! (Inj2.tapply(groupBasedChangeType(ℤ)) ! i) 23 | 24 | test("updateTerm behaves as expected") { 25 | assert(eval(ChangeUpdate ! toNope ! (Just ! 5)) === 26 | MaybeValue(None)) 27 | 28 | assert(eval(ChangeUpdate ! to42 ! Nope.tapply(ℤ)) === 29 | MaybeValue(Some(42))) 30 | 31 | assert(eval(ChangeUpdate ! toJust(42) ! (Just ! 5)) === 32 | MaybeValue(Some(42))) 33 | 34 | assert(eval(ChangeUpdate ! toJust(42) ! Nope.tapply(ℤ)) === 35 | MaybeValue(None)) 36 | } 37 | 38 | test("diffTerm produces replacements") { 39 | assert(eval(Diff ! Nope.tapply(ℤ) ! (Just ! 5)) === 40 | SumValue(Right(MaybeValue(None)))) 41 | assert(eval(Diff ! (Just ! 5) ! Nope.tapply(ℤ)) === 42 | SumValue(Right(MaybeValue(Some(5))))) 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/main/scala/ilc/feature/abelianGroups/Evaluation.scala: -------------------------------------------------------------------------------- 1 | package ilc 2 | package feature 3 | package abelianGroups 4 | 5 | /** hacked evaluation to pass tests */ 6 | 7 | trait Evaluation 8 | extends products.Evaluation 9 | with functions.Evaluation 10 | with booleans.Evaluation 11 | with Syntax 12 | with products.SyntaxSugar 13 | with integers.Syntax 14 | { 15 | override def coreEval(t: Term, env: Env): Value = t match { 16 | case AbelianGroup(_) 17 | | GetBinOp(_) | GetInv(_) | GetNeutral(_) => 18 | coreEval(transform(t), env) 19 | 20 | case AreEqualGroups(e) => 21 | (g1: Value) => (g2: Value) => 22 | coreEval(g1.toPair._1 == g2.toPair._1, env) 23 | 24 | case _ => 25 | super.coreEval(t, env) 26 | } 27 | 28 | private def transform(t: Term): Term = { 29 | // annotate a term with the argument types of `t` 30 | def fixType(x: TermBuilder): Term = 31 | x % (getArgumentTypes(t.getType).map(transformType): _*) 32 | t match { 33 | case AbelianGroup(e) => fixType(tuple(4)) 34 | 35 | case GetBinOp(_) => fixType(project(2)) 36 | case GetInv(_) => fixType(project(3)) 37 | case GetNeutral(_) => fixType(project(4)) 38 | } 39 | } 40 | 41 | private def transformType(tau: Type): Type = tau match { 42 | case AbelianGroupType(e) => 43 | tupleType(IntType, binOpType(e), invType(e), e) 44 | 45 | case _ => 46 | tau 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/main/scala/ilc/feature/base/Types.scala: -------------------------------------------------------------------------------- 1 | package ilc 2 | package feature 3 | package base 4 | 5 | import ilc.feature.inference.Reflection 6 | 7 | trait Types extends PrettyTypes { 8 | // We want subclasses of Type to be case classes. 9 | // If subclass is not a case class/case object, then they 10 | // fail to instantiate at compile time (a good thing) with 11 | // an obscure error message (a necessary evil). 12 | // 13 | // In unification we use the product iterator to map over all fields and we expect those to be types themselves. 14 | // If we ever want non-type fields we need to adapt ilc.feature.inference.Inference.unification 15 | trait Type extends Product with PrettyPrintable { 16 | def traverse(f: Type => Type): Type = 17 | this 18 | } 19 | 20 | // ERROR THROWERS 21 | def typeErrorWrongType(term: String, actual: Type, expected: Type): Nothing = 22 | throw TypeError(s"$term has type ${pretty(actual)} but should have type ${pretty(expected)}") 23 | 24 | def typeErrorNotTheSame(context: String, expected: Any, actual: Any) = 25 | throw TypeError(s"expected $expected instead of $actual in $context") 26 | 27 | def typeErrorNotInContext(name : Any) = 28 | throw TypeError(s"identifier ${name} not found in typing context") 29 | } 30 | 31 | case class TypeError(msg: String) 32 | extends Exception(s"Type error: $msg") 33 | 34 | case class IDontKnow(what: String) 35 | extends Exception(s"I don't know $what") 36 | -------------------------------------------------------------------------------- /src/main/scala/ilc/feature/functions/ContextSensitiveDerivation.scala: -------------------------------------------------------------------------------- 1 | 2 | package ilc 3 | package feature 4 | package functions 5 | 6 | /** Derivation with access to parent terms to facilitate static 7 | * analysis 8 | * 9 | * CAUTION 10 | * 11 | * base.ContextSensitiveDerivation must be mixed in after all 12 | * context-free Derivation traits, because the `derive` method 13 | * of a ContextSensitiveDerivation trait is final. The final tag 14 | * is there to ensure that the `derive` called from within a 15 | * calculus with ContextSensitiveDerivation is the one defined 16 | * in base.ContextSensitiveDerivation for sure, who delegates 17 | * its duties to `deriveSubtree` immediately. 18 | * 19 | * The base deriveSubtree will then default to context-free derivation 20 | * in the superclass - ignoring code of context-free derivation traits 21 | * in subtraits. 22 | */ 23 | 24 | trait ContextSensitiveDerivation 25 | extends Derivation 26 | with functions.Context 27 | with base.ContextSensitiveDerivation 28 | { 29 | override def deriveSubtree(s: Subtree): Term = s.toTerm match { 30 | case Abs(x, _) => { 31 | val Seq(body) = s.children 32 | lambdaTerm(x, DVar(x)) { deriveSubtree(body) } 33 | } 34 | 35 | case App(_, _) => { 36 | val Seq(operator, operand) = s.children 37 | deriveSubtree(operator) ! operand.toTerm ! deriveSubtree(operand) 38 | } 39 | 40 | case _ => 41 | super.deriveSubtree(s) 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/test/scala/ilc/language/bacchus/Subjects.scala: -------------------------------------------------------------------------------- 1 | package ilc 2 | package language 3 | package bacchus 4 | 5 | import feature._ 6 | 7 | trait Subjects 8 | extends bacchus.Syntax with bacchus.Prelude with ChangingTerms 9 | with naturals.ImplicitSyntaxSugar 10 | { 11 | // shorthand for types 12 | implicit class MapTypeOps(sigma: Type) { 13 | def ↦ (tau: Type): Type = MapType(sigma, tau) 14 | } 15 | 16 | val powerOfTwo = FoldNat ! 1 ! lambda(ℕ) {x => PlusNat ! x ! x} 17 | 18 | val getSize: Term = 19 | Fold ! (lambda(ℕ, ℕ) { case Seq(_, _) => PlusNat ! 1 }) ! 0 20 | 21 | val enumerate: Term = lambda(ℕ) { n => 22 | FoldNat ! EmptyMap.tapply(ℕ, ℕ) ! 23 | lambda(ℕ ↦ ℕ) { theMap => 24 | lambda {m => Update ! m ! m ! theMap} ! (getSize ! theMap) 25 | } ! (PlusNat ! n ! 1) 26 | } 27 | 28 | val sum: Term = 29 | Fold ! lambda(ℕ, ℕ) { case Seq(k, v) => PlusNat ! v } ! 0 30 | 31 | val natPairs = { 32 | val lhs = Array(914, 649, 869, 432, 795, 761, 1, 3, 5, 7, 0) 33 | val rhs = Array(904, 772, 178, 470, 484, 889, 2, 4, 6, 7, 0) 34 | (lhs, rhs).zipped.toList 35 | } 36 | 37 | // various possibilities for keys in a changing map 38 | val oldMap = mapLiteral(1 -> 2, 2 -> 4, 3 -> 6, 4 -> 8) 39 | val newMap = mapLiteral(1 -> 200, 2 -> 4, 5 -> 10, 6 -> 12) 40 | val keyInOld = Nat(4) 41 | val keyInNew = Nat(5) 42 | val keyInBoth = Nat(1) 43 | val keyInNone = Nat(9) 44 | val keyCases = List(keyInOld, keyInNew, keyInBoth, keyInNone) 45 | } 46 | -------------------------------------------------------------------------------- /src/main/scala/ilc/examples/Generator.scala: -------------------------------------------------------------------------------- 1 | package ilc 2 | package examples 3 | 4 | import java.io.{ File, FileWriter } 5 | 6 | trait Generator 7 | extends Archive 8 | with Dummy 9 | { 10 | def main(args: Array[String]) { 11 | // base is the directory to generate in 12 | Console.err.println("Generator started") 13 | val Array(base, classOutput) = args take 2 map (new File(_)) 14 | base.mkdirs() 15 | exportDummy(base) 16 | exportExamples(base, classOutput) 17 | } 18 | 19 | // stdout is exported as paths 20 | def export(path: String): Unit = 21 | Console.out.println(path) 22 | 23 | // stderr is echoed as [info] in sbt console 24 | def info(message: String): Unit = 25 | Console.err.println(message) 26 | 27 | def exportExamples(base: File, classOutput: File) { 28 | for { 29 | example <- archive.values 30 | } { 31 | exportSource(base, classOutput, example) 32 | } 33 | } 34 | 35 | // dummy code 36 | def exportDummy(base: File) { 37 | val path = new File(base, "DummyGenerated.scala").getCanonicalPath 38 | val file = new FileWriter(path) 39 | file.write(scalaMeterDummyCode) 40 | file.close 41 | export(path) 42 | } 43 | 44 | def exportSource(base: File, classOutput: File, example: Example) { 45 | for { 46 | src <- example.toSource(base) 47 | out = src.saveIfNeeded(classOutput) 48 | } { 49 | //Ensure this file is tracked by SBT. 50 | export(out.getCanonicalPath) 51 | } 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /clients/src/main/scala/ilc/examples/SumValuesExample.scala: -------------------------------------------------------------------------------- 1 | package ilc 2 | package examples 3 | 4 | import feature._ 5 | 6 | class SumValuesExample 7 | extends Example 8 | with inference.PrettySyntax 9 | 10 | with products.Derivation 11 | with abelianMaps.AbelianDerivation 12 | with integers.AbelianDerivation 13 | 14 | with functions.Syntax 15 | with integers.SyntaxSugar 16 | with products.Syntax 17 | 18 | with abelianMaps.ToScala 19 | with booleans.ToScala 20 | with functions.ToScala 21 | with integers.ToScala 22 | with products.ToScala 23 | with sums.ToScala 24 | { 25 | // snd : ℤ → ℤ → ℤ 26 | // snd = λx : ℤ. λy : ℤ. y 27 | // 28 | // (In Scala code, we provide the argument type 29 | // annotations by the % operator. Using the % operator 30 | // to save notation overhead can cause subtle type errors 31 | // and is not recommended.) 32 | // 33 | // program : Map ℤ ℤ → (ℤ, AbelianGroup ℤ) 34 | // program = 35 | // let G+ = additiveGroupOnIntegers : AbelianGroup ℤ 36 | // in λ inputMap : Map ℤ ℤ. 37 | // pair (foldByHom G+ G+ snd inputMap) G+ 38 | val G: UntypedTerm = additiveGroupOnIntegers 39 | val foldByHom: UntypedTerm = FoldByHom 40 | // Need to annotate the first argument. It is not used, so type inference leaves it open, but code generation does not cope with type variables. 41 | val _flipConst: UntypedTerm = 'first % ℤ ->: 'second ->: 'second 42 | 43 | val program: Term = untypedTermToTerm( 44 | 'inputMap ->: Pair(FoldByHom(G, G, _flipConst, 'inputMap), G) 45 | ) 46 | } 47 | -------------------------------------------------------------------------------- /src/test/scala/ilc/feature/functions/PrettySuite.scala: -------------------------------------------------------------------------------- 1 | package ilc 2 | package feature 3 | package functions 4 | 5 | import org.scalatest.FunSuite 6 | import org.scalatest.Matchers 7 | 8 | class PrettySuite extends FunSuite with Matchers { 9 | object Lang extends Pretty { 10 | // force line breaks in all possible positions 11 | override val defaultWidth = 0 12 | } 13 | import Lang._ 14 | 15 | def showPrintout(s: String) = info("printout:\n" + s) 16 | 17 | case object Bot extends Type 18 | 19 | val id = lambda("x") { x => x } 20 | val id3 = id ! id%(Bot =>: Bot) ! id%Bot 21 | 22 | test("The identity function prints λx. x") { 23 | val printout = pretty(id%Bot) 24 | printout should be ( 25 | """|λx. 26 | | x""".stripMargin) 27 | showPrintout(printout) 28 | } 29 | 30 | test("Left-associative application print without extra parentheses") { 31 | val printout = pretty(id3) 32 | printout should be ( 33 | """|(λx. 34 | | x) 35 | | (λx. 36 | | x) 37 | | (λx. 38 | | x)""".stripMargin) 39 | showPrintout(printout) 40 | } 41 | 42 | test("Variables are disambiguated with indices.") { 43 | val shadowed = lambda("x", "x", "x", "x") { 44 | case Seq(x, x1, x2, x3) => x1 45 | } 46 | val printout = pretty(shadowed%(Bot, Bot, Bot, Bot)) 47 | val expected = 48 | """|λx. 49 | | λx_1. 50 | | λx_2. 51 | | λx_3. 52 | | x_1""".stripMargin 53 | printout should be (expected) 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /src/main/scala/ilc/feature/functions/ToScala.scala: -------------------------------------------------------------------------------- 1 | package ilc 2 | package feature 3 | package functions 4 | 5 | import scala.language.postfixOps 6 | 7 | trait ToScala extends base.ToScala with Syntax { 8 | final override def toScala(t: Term): String = 9 | t match { 10 | /* 11 | * Type annotations here are not needed. 12 | * Moreover, for a curried n-parameter function, these type annotations 13 | * consume O(n^2). The output looks essentially like this: 14 | * ((...((...)(arg_1): arg2Type => arg3Type => ... argNType => resType)(arg_2): 15 | * arg3Type => ... argNType => resType)...) (argN) :resType) 16 | * Say that all those type have size >= k in concrete syntax. 17 | * Then the space we need is at least k * (n + (n - 1) + (n - 2) + ... + 1), 18 | * that is o(k * n^2). That's why it's important to save this space. 19 | */ 20 | case App(f, x) => s"(${toUntypedScala(t)})" 21 | case v: Var => toUntypedScala(t) 22 | case _ => super.toScala(t) 23 | } 24 | 25 | 26 | override def toUntypedScala(t: Term): String = t match { 27 | case App(f, x) => 28 | "%s(%s)".format(toScala(f), toScala(x)) 29 | 30 | case Abs(variable, bodyTerm) => 31 | scalaFunction(variable.getName.toString)(toScala(bodyTerm)) 32 | 33 | case _ => 34 | super.toUntypedScala(t) 35 | } 36 | 37 | override def toScala(tau: Type): String = tau match { 38 | case sigma0 =>: sigma1 => { 39 | s"((=>${toScala(sigma0)}) => ${toScala(sigma1)})" 40 | } 41 | 42 | case _ => 43 | super.toScala(tau) 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/test/scala/ilc/feature/maps/DeltaMapSuite.scala: -------------------------------------------------------------------------------- 1 | package ilc 2 | package feature 3 | package maps 4 | 5 | import org.scalatest.FunSuite 6 | 7 | class DeltaMapSuite 8 | extends FunSuite 9 | with ReplacementValuesDerivation 10 | with SyntaxSugar 11 | with Evaluation 12 | with integers.AbelianDerivation 13 | with integers.Evaluation 14 | with integers.ImplicitSyntaxSugar 15 | with sums.Evaluation 16 | with functions.Pretty // for debugging 17 | { 18 | val oldMap = mapLiteral(1 -> 100, 2 -> 200, 3 -> 300) 19 | 20 | val newMap = mapLiteral(1 -> 100, 2 -> 400, 5 -> 500) 21 | 22 | /** surgical change from `oldMap` to `newMap` */ 23 | val surgery: Term = { 24 | import MapSurgery._ 25 | mkSurgicalMapChange(IntType, IntType)( 26 | MODIFY(2, replacementChange ! 400), 27 | DELETE(3), 28 | INSERT(5, 500) 29 | ) 30 | } 31 | 32 | /** replacement change from `oldMap` to `newMap` */ 33 | val replacement: Term = mkMapReplacement(newMap) 34 | 35 | test("updateTerm behaves as expected on replacement changes") { 36 | assert(eval(ChangeUpdate ! replacement ! oldMap) === eval(newMap)) 37 | } 38 | 39 | test("updateTerm behaves as expected on surgical changes") { 40 | assert(eval(ChangeUpdate ! surgery ! oldMap) === eval(newMap)) 41 | } 42 | 43 | test("diffTerm produces replacement changes") { 44 | assert(eval(Diff ! newMap ! oldMap) === eval(replacement)) 45 | } 46 | 47 | test("mapMinus behaves as expected") { 48 | assert(eval(mapMinus ! oldMap ! newMap) === MapValue(3 -> 300)) 49 | assert(eval(mapMinus ! newMap ! oldMap) === MapValue(5 -> 500)) 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/test/scala/ilc/feature/abelianMaps/MapChanges.scala: -------------------------------------------------------------------------------- 1 | package ilc 2 | package feature 3 | package abelianMaps 4 | 5 | /** changes for generated maps */ 6 | 7 | trait MapChanges { 8 | import Library._ 9 | import abelianGroups.Library._ 10 | 11 | type ChangeToMaps[K, V] = 12 | Either[(AbelianGroup[AbelianMap[K, V]], AbelianMap[K, V]), 13 | AbelianMap[K, V]] 14 | 15 | def changesToMapsBetweenIntegers: 16 | Map[String, 17 | AbelianGroup[Int] => AbelianMap[Int, Int] => 18 | ChangeToMaps[Int, Int]] = 19 | Map( 20 | /*"no change" -> (grp => m => 21 | mkAdditiveChange(grp)() 22 | ),*/ 23 | 24 | "replace min by max + 1" -> (grp => m => { 25 | val keySet = m.keySet 26 | val (min, max) = (keySet.min, keySet.max) 27 | val multiplicity = m(min) 28 | mkAdditiveChange(grp)( 29 | min -> (- multiplicity), 30 | (max + 1) -> multiplicity) 31 | })/*, 32 | 33 | "add max + 2" -> (grp => m => 34 | mkAdditiveChange(grp)((m.keySet.max + 2) -> 1) 35 | ), 36 | 37 | "remove second key" -> (grp => m => { 38 | val sndKey = m.keySet.tail.head 39 | mkAdditiveChange(grp)(sndKey -> (- m(sndKey))) 40 | }), 41 | 42 | "remove max" -> (grp => m => { 43 | val max = m.keySet.max 44 | mkAdditiveChange(grp)(max -> (- m(max))) 45 | })*/) 46 | 47 | private def mkAdditiveChange[K] 48 | (grp: AbelianGroup[Int]) 49 | (keyValuePairs: (K, Int)*): ChangeToMaps[K, Int] = 50 | Left((LiftedMapGroup(grp), AbelianMap(keyValuePairs: _*))) 51 | } 52 | -------------------------------------------------------------------------------- /src/test/scala/ilc/feature/cbpv/CBPVTest.sc: -------------------------------------------------------------------------------- 1 | package ilc 2 | package feature 3 | package cbpv 4 | 5 | object CBPVTest extends TypeConversions { 6 | //println("Welcome to the Scala worksheet") 7 | cbnTypeToCBPV(SumType(UnitType, UnitType)) //> res0: ilc.feature.cbpv.CBPVTest.CompType = FProducerCT(SumVT(UThunkVT(FProdu 8 | //| cerCT(UnitVT)),UThunkVT(FProducerCT(UnitVT)))) 9 | compTypeToType(cbnTypeToCBPV(BooleanType)) //> res1: ilc.feature.cbpv.CBPVTest.Type = F(( (1) + (1) )) 10 | compTypeToType(cbnTypeToCBPV(BooleanType =>: BooleanType)) 11 | //> res2: ilc.feature.cbpv.CBPVTest.Type = U(F(( (1) + (1) ))) -> F(( (1) + (1) 12 | //| )) 13 | compTypeToType(cbnTypeToCBPV(BooleanType =>: BooleanType =>: BooleanType)) 14 | //> res3: ilc.feature.cbpv.CBPVTest.Type = U(F(( (1) + (1) ))) -> U(F(( (1) + (1 15 | //| ) ))) -> F(( (1) + (1) )) 16 | valTypeToType(cbvTypeToCBPV(BooleanType =>: BooleanType =>: BooleanType)) 17 | //> res4: ilc.feature.cbpv.CBPVTest.Type = U(( (1) + (1) ) -> F(U(( (1) + (1) ) 18 | //| -> F(( (1) + (1) ))))) 19 | compTypeToType(cbnTypeToCBPV(UnitType =>: SumType(UnitType, UnitType))) 20 | //> res5: ilc.feature.cbpv.CBPVTest.Type = U(F(1)) -> F(( (U(F(1))) + (U(F(1))) 21 | //| )) 22 | } 23 | -------------------------------------------------------------------------------- /src/test/scala/ilc/metaprogs/MemoizeSpec.scala: -------------------------------------------------------------------------------- 1 | package ilc 2 | package metaprogs 3 | 4 | import org.scalatest._ 5 | import feature._ 6 | import util.EvalScala 7 | 8 | class MemoizeSpec extends FlatSpec with Memoize 9 | with memoize.Syntax 10 | with functions.Derivation //with let.Derivation 11 | 12 | with integers.ImplicitSyntaxSugar with integers.ToScala with integers.AbelianDerivation 13 | with functions.Syntax with let.Syntax 14 | with inference.PrettySyntax with inference.LetSyntaxSugar 15 | with inference.LetInference 16 | with let.Pretty with let.Traversals 17 | with analysis.FreeVariables 18 | with functions.ToScala with sums.ToScala with abelianGroups.ToScala with products.ToScala with memoize.ToScala 19 | 20 | with EvalScala 21 | { 22 | "memoizedDerive & transform" should "complete" in { 23 | val memoCtx = new MemoContext() 24 | import memoCtx._ 25 | val t = asTerm(('x % IntType) ->: ('y % IntType) ->: 'x) 26 | val tTransf = transform(t) 27 | println(tTransf) 28 | println(pretty(tTransf)) 29 | println(toScala(tTransf)) 30 | val tDeriv = memoizedDerive(t) 31 | println(tDeriv) 32 | println(pretty(tDeriv)) 33 | println(toScala(tDeriv)) 34 | 35 | { 36 | val t2 = asTerm(('x % IntType) ->: ('y % IntType) ->: ('z % IntType) ->: (PlusInt(PlusInt('x, 'y), 'z))) 37 | val t2Transf = transform(t2) 38 | println(t2Transf) 39 | println(pretty(t2Transf)) 40 | println(toScala(t2Transf)) 41 | val t2Deriv = memoizedDerive(t2) 42 | println(t2Deriv) 43 | println(pretty(t2Deriv)) 44 | println(toScala(t2Deriv)) 45 | } 46 | println(cacheMap) 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/main/scala/ilc/language/bacchus/FineGrainedDifference.scala: -------------------------------------------------------------------------------- 1 | package ilc 2 | package language 3 | package bacchus 4 | 5 | import feature._ 6 | 7 | trait FineGrainedDifference 8 | extends maps.Reification 9 | with maybe.Reification 10 | with naturals.Reification 11 | with sums.Reification 12 | with base.Derivation 13 | { 14 | private[this] def valueDiff(minuend: Value, subtrahend: Value): Value = 15 | (minuend, subtrahend) match { 16 | case (NatValue(n1), NatValue(n2)) => 17 | NatValue(n1) 18 | 19 | case (MapValue(m1), MapValue(m2)) => { 20 | val toDelete = m2.keySet -- m1.keySet 21 | val toInsert = m1 -- m2.keySet 22 | val toUpdate = (m1.keySet & m2.keySet) filter { 23 | key => m1(key) != m2(key) 24 | } 25 | SumValue(Left(MapValue( 26 | toInsert.map { 27 | case (key, value) => 28 | key -> SumValue(Left(MaybeValue(Some(value)))) 29 | } ++ toDelete.map { 30 | key => 31 | key -> SumValue(Left(MaybeValue(None))) 32 | } ++ toUpdate.map { 33 | key => 34 | key -> SumValue(Right(valueDiff(m1(key), m2(key)))) 35 | } 36 | ))) 37 | } 38 | } 39 | 40 | def fineGrainedDiff(minuend: Term, subtrahend: Term): Term = { 41 | val theType = minuend.getType 42 | require(theType == subtrahend.getType) 43 | val (mValue, sValue) = (eval(minuend), eval(subtrahend)) 44 | mValue match { 45 | case _: MapValue | _: NatValue => 46 | reify(valueDiff(mValue, sValue), deltaType(theType)) 47 | 48 | case _ => 49 | Diff ! minuend ! subtrahend 50 | } 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/main/scala/ilc/feature/inference/Reflection.scala: -------------------------------------------------------------------------------- 1 | package ilc.feature.inference 2 | 3 | import scala.language.postfixOps 4 | 5 | /* 6 | In Scala, given the definition of an AST made of case classes, how do you define local transformations without 7 | boilerplate for each case class? Here's my solution, based on reflection. 8 | -- Paolo 9 | https://gist.github.com/Blaisorblade/827e357de942a46acbdb 10 | */ 11 | 12 | object Util { 13 | def count(amount: Int, noun: String): String = { 14 | (noun, amount) match { 15 | case (_, 1) => s"${amount} ${noun}" 16 | case (_, _) => s"${amount} ${noun}s" 17 | } 18 | } 19 | } 20 | 21 | import Util._ 22 | 23 | trait Reflection { 24 | /** 25 | * Allow functional update on arbitrary case classes. Call it with an 26 | * instance of a case class and its updated children. 27 | * Only works on case classes, or classes having an appropriate copy method. 28 | * The case class 29 | * @param t an instance of a case class 30 | */ 31 | def reflectiveCopy[T <: Product](t: T, args: Any*): T = { 32 | val clazz = t.getClass 33 | if (args.length == t.productArity) { 34 | if (t.productArity == 0) { 35 | return t 36 | } else { 37 | val copyMethodOpt = clazz.getMethods filter (_.getName == "copy") headOption 38 | 39 | (copyMethodOpt getOrElse ( 40 | throw new RuntimeException(s"No 'copy' method found in reflectiveCopy for ${t} of type ${clazz}")) invoke (t, 41 | args.toArray.asInstanceOf[Array[_ <: AnyRef]]: _*)).asInstanceOf[T] 42 | } 43 | } else { 44 | throw new IllegalArgumentException(s"${count(t.productArity, "argument")} expected but ${args.length} given") 45 | } 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/main/scala/ilc/feature/functions/Derivation.scala: -------------------------------------------------------------------------------- 1 | package ilc 2 | package feature 3 | package functions 4 | 5 | /** 6 | * Symbolic derivation for first-class functions. 7 | */ 8 | trait Derivation 9 | extends LambdaDelta 10 | { 11 | override def deltaType(tau: Type): Type = tau match { 12 | case domain =>: range => 13 | domain =>: deltaType(domain) =>: deltaType(range) 14 | 15 | case _ => 16 | super.deltaType(tau) 17 | } 18 | 19 | override def updateTerm(tau: Type): Term = tau match { 20 | case functionType@(domain =>: range) => 21 | lambda( 22 | Var("df", deltaType(functionType)), 23 | Var("f", functionType), 24 | Var("x", domain) 25 | ) { 26 | case Seq(df, f, x) => 27 | updateTerm(range) ! 28 | (df ! x ! (diffTerm(domain) ! x ! x)) ! 29 | (f ! x) 30 | } 31 | 32 | case _ => 33 | super.updateTerm(tau) 34 | } 35 | 36 | override def diffTerm(tau: Type): Term = tau match { 37 | case functionType@(domain =>: range) => 38 | lambda( 39 | Var("g", functionType), 40 | Var("f", functionType), 41 | Var("x", domain), 42 | Var("dx", deltaType(domain)) 43 | ) { 44 | case Seq(g, f, x, dx) => 45 | diffTerm(range) ! 46 | (g ! (updateTerm(domain) ! dx ! x)) ! 47 | (f ! x) 48 | } 49 | 50 | case _ => 51 | super.diffTerm(tau) 52 | } 53 | 54 | override def derive(t: Term): Term = t match { 55 | case Abs(x, body) => 56 | lambdaTerm(x, DVar(x)) { derive(body) } 57 | 58 | case App(operator, operand) => 59 | derive(operator) ! operand ! derive(operand) 60 | 61 | case _ => 62 | super.derive(t) 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /src/main/scala/ilc/util/process/FunProcess.scala: -------------------------------------------------------------------------------- 1 | package ilc 2 | package util 3 | package process 4 | 5 | /** 6 | * FunProcess (functional process) is viewing a system 7 | * command as a function from String to String 8 | */ 9 | 10 | import scala.io.Source 11 | import scala.sys.process._ 12 | 13 | class ProcessFailureError(cmd: Seq[String], code: Int, msg: String) 14 | extends java.lang.Error( 15 | "Process `" ++ cmd.mkString(" ") ++ 16 | "`\n failed with exit code " ++ code.toString ++ 17 | "\n and the following error message:\n" ++ 18 | msg) 19 | 20 | case class FunProcess(cmd: String*) 21 | extends Function[String, String] 22 | // throws ProcessFailureError, java.io.IOException 23 | { 24 | val proc = Process(cmd) // process object 25 | var output: String = null // log of stdout 26 | var error: String = null // log of stderr 27 | 28 | def apply(input: String): String = { 29 | proc.run(new ProcessIO( 30 | procInput => { 31 | val writer = new java.io.PrintWriter(procInput) 32 | writer.write(input) 33 | writer.close() 34 | }, 35 | procOutput => { 36 | output = readToEnd(procOutput) 37 | procOutput.close() 38 | }, 39 | procError => { 40 | error = readToEnd(procError) 41 | procError.close() 42 | })).exitValue() match { 43 | 44 | case 0 => 45 | output 46 | 47 | case abnormalExitCode => 48 | throw new ProcessFailureError(cmd, abnormalExitCode, error) 49 | } 50 | } 51 | 52 | // convert java.io.InputStream to a String 53 | // http://stackoverflow.com/a/5221595 54 | def readToEnd(stream: java.io.InputStream): String = { 55 | Source.fromInputStream(stream).getLines().mkString("\n") 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /src/main/scala/ilc/feature/equality/equality.scala: -------------------------------------------------------------------------------- 1 | package ilc 2 | package feature 3 | package equality 4 | 5 | trait Syntax extends base.Syntax with booleans.Types { 6 | //Signature: v -> v -> Bool 7 | case object Eq extends ConstantWith1TypeParameter { 8 | override val typeConstructor = TypeConstructor("v") { v => 9 | v =>: v =>: BooleanType 10 | } 11 | } 12 | } 13 | 14 | object Library extends base.Library { 15 | //def equal[T](a: T, b: T): Boolean = 16 | def equal[T]: (=> T) => ((=> T) => Boolean) = 17 | a => b => 18 | a == b 19 | 20 | } 21 | 22 | trait ToScala extends base.ToScala with Syntax { 23 | addLibrary("equality") 24 | 25 | //This accepts arbitrary instances of Type, not just ours; see pattern match 26 | //below for rationale. 27 | private[this] def containsFunctions(t: base.Types#Type): Boolean = 28 | t match { 29 | case _ =>: _ => true 30 | case _ => 31 | t.productIterator.toSeq exists { 32 | //This pattern match cannot distinguish between "our" type and another 33 | //one - this is visible because matching against `Type` would give an 34 | //unchecked warning. However, the code is in fact safe, because it's 35 | //enough to test for base.Types#Type. 36 | case x: base.Types#Type => 37 | containsFunctions(x) 38 | case _ => 39 | false 40 | } 41 | } 42 | 43 | override def toUntypedScala(t: Term): String = 44 | t match { 45 | case Eq(v) if !containsFunctions(v) => 46 | s"equal[${toScala(v)}]" 47 | case Eq(v) => 48 | sys.error(s"Cannot implement equality on type $v containing a function type") 49 | case _ => 50 | super.toUntypedScala(t) 51 | } 52 | 53 | } 54 | -------------------------------------------------------------------------------- /src/main/scala/ilc/feature/bags/Derivation.scala: -------------------------------------------------------------------------------- 1 | package ilc 2 | package feature 3 | package bags 4 | 5 | /** {{{ 6 | * Δ(Bag a) = Bag a 7 | * }}} 8 | */ 9 | 10 | trait Derivation 11 | extends base.Derivation 12 | with Syntax 13 | with functions.Syntax 14 | { 15 | override def deltaType(tau: Type): Type = tau match { 16 | case BagType(v) => 17 | BagType(v) //Simple derivative type. 18 | 19 | case _ => 20 | super.deltaType(tau) 21 | } 22 | 23 | override def updateTerm(tau: Type): Term = tau match { 24 | case BagType(valueType) => 25 | lambda(deltaType(tau), tau) { case Seq(deltaBag, oldBag) => 26 | Union ! oldBag ! deltaBag 27 | } 28 | case _ => 29 | super.updateTerm(tau) 30 | } 31 | 32 | override def diffTerm(tau: Type): Term = tau match { 33 | case BagType(valueType) => 34 | lambda(tau, tau) { case Seq(newBag, oldBag) => 35 | Union ! newBag ! (Negate ! oldBag) 36 | } 37 | 38 | case _ => 39 | super.diffTerm(tau) 40 | } 41 | 42 | object BagSurgery { 43 | sealed trait Change 44 | case class DELETE(value: Term) extends Change 45 | case class INSERT(value: Term) extends Change 46 | case class MODIFY(value: Term, deltaValue: Term) extends Change 47 | } 48 | 49 | //Copied from ReplacementValuesDerivation.scala 50 | def mkSurgicalBagChange(valueType: Type) 51 | (changes: BagSurgery.Change*): Term = 52 | { 53 | import BagSurgery._ 54 | if (changes.isEmpty) 55 | EmptyBag.tapply(valueType) 56 | else 57 | ??? 58 | } 59 | 60 | override def derive(t: Term): Term = 61 | t match { 62 | case EmptyBag(valueType) => 63 | mkSurgicalBagChange(valueType)() 64 | case _ => 65 | super.derive(t) 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /icfp2014/src/test/scala/ilc/language/gcc/CollectionsSuite.scala: -------------------------------------------------------------------------------- 1 | package ilc 2 | package language 3 | package gcc 4 | 5 | import org.scalatest.FunSuite 6 | import org.scalatest.Matchers 7 | import GCC._ 8 | 9 | class GCCCollectionsSuite extends FunSuite with Matchers { 10 | def withLibraries(body: UT) = 11 | typecheck { letS((collectionApi ++ math ++ points):_*)(body) } 12 | 13 | test("Should find integers in list") { 14 | withLibraries { 15 | list(1, 2, 3, 4, 5) contains (3, lam('x, 'y) { 'x === 'y }) 16 | } 17 | } 18 | 19 | test("Should find points in list") { 20 | withLibraries { 21 | list((0, 3), (1, 2), (4, 6), (0, 8), (8, 2)) contains ((4, 6), 'pointEq) 22 | } 23 | } 24 | 25 | test("Should create list of given size") { 26 | withLibraries { 27 | createList(4, tuple(0, 4)).head.first === 0 28 | } 29 | } 30 | 31 | test("Should create list of given size with multiple types one after another") { 32 | withLibraries { 33 | (createList(4, tuple(0, 4)).head.first === 0) ~: (createList(4, -1).head === -1) 34 | } 35 | } 36 | 37 | test("Should create maps of given size") { 38 | withLibraries { 39 | (createMap(4, 8, tuple(-1, -1)) atPos ((2, 2))) === ((-1, -1)) 40 | } 41 | } 42 | 43 | test("Should access elements given a positional point") { 44 | withLibraries { 45 | let('a, (2, 2)) { 46 | createMap(4, 8, tuple(-1, -1)) atPos 'a 47 | } 48 | } 49 | 50 | withLibraries { 51 | let('a, (2, 2)) { 52 | (createMap(4, 8, -1) atPos 'a) ~: 53 | (createMap(4, 8, tuple(-1, -1)) atPos 'a) 54 | } 55 | } 56 | } 57 | 58 | test("Should update entry in map") { 59 | withLibraries { 60 | createMap(4, 8, (-1, -1)).update(Point)((2, 2), (42, 13)) 61 | } 62 | } 63 | 64 | } 65 | -------------------------------------------------------------------------------- /src/main/scala/ilc/feature/abelianMaps/AbelianDerivation.scala: -------------------------------------------------------------------------------- 1 | package ilc 2 | package feature 3 | package abelianMaps 4 | 5 | /** Δ(Map k v) = (AbelianGroup (Map k v) × Map k v) ⊎ Map k v 6 | * 7 | * The change to a map is an abelian group element or a 8 | * replacement. 9 | */ 10 | trait AbelianDerivation 11 | extends Syntax 12 | with abelianGroups.SyntaxSugar 13 | with booleans.SyntaxSugar 14 | with abelianGroups.Derivation 15 | with abelianGroups.AbelianDerivation 16 | with functions.ContextSensitiveDerivation 17 | with analysis.Stability 18 | { 19 | override def isAbelianType(tau: Type): Boolean = tau match { 20 | case MapType(_, _) => 21 | true 22 | 23 | case _ => 24 | super.isAbelianType(tau) 25 | } 26 | 27 | override def deriveSubtree(s: Subtree): Term = s.toTerm match { 28 | case term @ FoldByHom(k, a, b) 29 | if isAbelianType(b) && 30 | s.hasStableArgument(2) => // f : k → a → b 31 | lambdaDelta(term) { case Seq(_Ga, dGa, _Gb, dGb, f, df, m, dm) => 32 | val grp = ProductType(AbelianGroupType(MapType(k, a)), MapType(k, a)) 33 | val rep = MapType(k, a) 34 | val replacement: TermBuilder = 35 | super.deriveSubtree(s) ! _Ga ! dGa ! _Gb ! dGb ! f ! df ! m ! dm 36 | 37 | case2(dm, 38 | lambda(grp) { case grp0 => 39 | ifEqualGroups((Proj1 ! grp0, LiftGroup.tapply(k) ! _Ga), 40 | (_Ga, dGa), 41 | (_Gb, dGb)) { 42 | groupBasedChange ! _Gb ! 43 | (FoldByHom ! _Ga ! _Gb ! f ! (elementOfChange ! grp0)) 44 | } { 45 | replacement 46 | } 47 | }, 48 | lambda(rep) { _ => replacement }) 49 | } 50 | 51 | case _ => 52 | super.deriveSubtree(s) 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /clients/src/test/scala/longRunning/SumValuesBenchmark.scala: -------------------------------------------------------------------------------- 1 | package longRunning 2 | 3 | import org.scalameter.api._ 4 | import ilc.feature.abelianGroups.Library._ 5 | import ilc.feature.abelianMaps.Library._ 6 | import ilc.feature.abelianMaps.MapChanges 7 | import ilc.examples.BenchData 8 | import ilc.examples.BenchmarkVerification 9 | import ilc.examples.ExampleGenerated 10 | import ilc.examples.NonReplacementChangeBenchmark 11 | import ilc.examples.SumValuesGenerated 12 | 13 | class SumValuesBenchmark extends NonReplacementChangeBenchmark( 14 | new AdditiveMapBenchData(SumValuesGenerated) { 15 | override def base = 5000 16 | override def last = 25000 17 | override def step = 5000 18 | }) 19 | 20 | // Input: Map[Int, Int] with LiftedMapGroup(additiveIntegerGroup) 21 | class AdditiveMapBenchData(val example: ExampleGenerated { 22 | type InputType = 23 | AbelianMap[Int, Int] 24 | 25 | type DeltaInputType = 26 | Either[ 27 | (AbelianGroup[AbelianMap[Int, Int]], AbelianMap[Int, Int]), 28 | AbelianMap[Int, Int] 29 | ] 30 | 31 | type OutputType = 32 | (Int, AbelianGroup[Int]) 33 | 34 | type DeltaOutputType = 35 | (Either[(AbelianGroup[Int], Int), Int], AbelianGroup[Int]) 36 | }) extends BenchData with MapChanges 37 | { 38 | import example._ 39 | 40 | private def mkIdentityMap(n: Int) = 41 | AbelianMap((1 to n) map {i => (i, i)}: _*) 42 | 43 | def inputOfSize(n: Int): Data = mkIdentityMap(n) 44 | 45 | lazy val changeDescriptions: Gen[String] = 46 | Gen.enumeration("change")(changesToMapsBetweenIntegers.keySet.toSeq: _*) 47 | 48 | def lookupChange(description: String, 49 | inputSize: Int, 50 | input: InputType, 51 | output: OutputType): Change = 52 | changesToMapsBetweenIntegers(description)(output._2)(input) 53 | } 54 | -------------------------------------------------------------------------------- /src/main/scala/ilc/feature/maybe/ReplacementValuesDerivation.scala: -------------------------------------------------------------------------------- 1 | package ilc 2 | package feature 3 | package maybe 4 | 5 | /** Δ(Maybe a) = Δa ⊎ Maybe a */ 6 | trait ReplacementValuesDerivation 7 | extends base.Derivation 8 | with Syntax 9 | with sums.Syntax 10 | { 11 | override def deltaType(tau: Type): Type = tau match { 12 | case MaybeType(contentType) => 13 | SumType(deltaType(contentType), MaybeType(contentType)) 14 | 15 | case _ => 16 | super.deltaType(tau) 17 | } 18 | 19 | private[this] def replace(contentType: Type) = 20 | Inj2.tapply(deltaType(contentType)) 21 | 22 | override def updateTerm(tau: Type): Term = tau match { 23 | case MaybeType(contentType) => 24 | lambda(deltaType(tau), tau) { case Seq(deltaMaybe, oldMaybe) => 25 | Either ! 26 | lambda(deltaType(contentType)) { dx => 27 | oldMaybe >>= lambda { x => 28 | Just ! (updateTerm(contentType) ! dx ! x) 29 | } 30 | } ! 31 | lambda(tau) { replacement => replacement } ! 32 | deltaMaybe 33 | } 34 | 35 | case _ => 36 | super.updateTerm(tau) 37 | } 38 | 39 | override def diffTerm(tau: Type): Term = tau match { 40 | case MaybeType(contentType) => 41 | lambda(tau, tau) { case Seq(theNew, theOld) => 42 | replace(contentType) ! theNew 43 | } 44 | 45 | case _ => 46 | super.diffTerm(tau) 47 | } 48 | 49 | override def derive(t: Term): Term = t match { 50 | case Nope(contentType) => 51 | replace(contentType) ! t 52 | 53 | case Just(contentType) => 54 | lambda(contentType, deltaType(contentType)) { 55 | case Seq(x, dx) => 56 | Inj1.tapply(t.getType) ! dx 57 | } 58 | 59 | // Maybe has slow derivative 60 | case _ => 61 | super.derive(t) 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /src/test/scala/ilc/language/bacchus/BacchusEvaluationSuite.scala: -------------------------------------------------------------------------------- 1 | package ilc 2 | package language 3 | package bacchus 4 | 5 | import org.scalatest.FunSuite 6 | 7 | class BacchusEvaluationSuite 8 | extends FunSuite 9 | with Subjects 10 | with Pretty 11 | with Evaluation 12 | { 13 | // `expect` is taken and deprecated 14 | def expectToGet(result: Value)(term: => Term) { 15 | val prettyTerm = pretty(term) 16 | test(s"$result = $prettyTerm") { 17 | assert(eval(term) === result) 18 | } 19 | } 20 | 21 | // Plus 22 | expectToGet(10) { 23 | PlusNat ! (PlusNat ! 1 ! 2) ! (PlusNat ! 3 ! 4) 24 | } 25 | 26 | // Plus FoldNat 27 | expectToGet(1024) { powerOfTwo ! 10 } 28 | 29 | // Plus Inj1 Inj2 Either 30 | expectToGet(123) { 31 | def add(n: Int) = lambda(ℕ, ℕ) { 32 | case Seq(x, y) => PlusNat ! (PlusNat ! x ! y) ! n 33 | } 34 | case4(Inj2.tapply(ℕ) ! 100, Inj1.tapply(ℕ) ! 20, 35 | add(1), add(2), add(3), add(4)) 36 | } 37 | 38 | // Maybe Nope 39 | expectToGet(0) { Maybe ! 0 ! lambda(ℕ) {x => x} ! Nope.tapply(ℕ) } 40 | 41 | // Maybe Just 42 | expectToGet(5) { Maybe ! 0 ! lambda(ℕ) {x => x} ! (Just ! 5) } 43 | 44 | // Maybe Nope EmptyMap Lookup 45 | expectToGet(MaybeValue(None)) { Lookup ! 5 ! EmptyMap.tapply(ℕ, ℕ) } 46 | 47 | // Maybe Just EmptyMap Lookup Update 48 | expectToGet(MaybeValue(Some(5))) { 49 | Lookup ! 4 ! 50 | (Update ! 3 ! 6 ! (Update ! 4 ! 5 ! EmptyMap.tapply(ℕ, ℕ))) 51 | } 52 | 53 | // Plus EmptyMap Update Fold 54 | expectToGet(3) { 55 | getSize ! 56 | (Update ! 3 ! 6 ! 57 | (Update ! 4 ! 5 ! 58 | (Update ! 5 ! 4 ! EmptyMap.tapply(ℕ, ℕ)))) 59 | } 60 | 61 | // 500500 = 1 + 2 + ... + 1000 62 | // (in O(n^2) time, with n = 1000 here) 63 | expectToGet(500500) { 64 | sum ! (enumerate ! 1000) 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /clients/src/test/scala/ilc/examples/MapReduceSuite.scala: -------------------------------------------------------------------------------- 1 | package ilc 2 | package examples 3 | 4 | import org.scalatest.FunSuite 5 | import org.scalatest.Matchers 6 | 7 | /** test that map-reduce terms are well typed */ 8 | class MapReduceSuite 9 | extends FunSuite 10 | with Matchers 11 | { 12 | object Lang extends MapReduce with feature.base.Pretty 13 | import Lang._ 14 | 15 | case object K1 extends Type 16 | case object K2 extends Type 17 | case object V1 extends Type 18 | case object V2 extends Type 19 | case object V3 extends Type 20 | 21 | // TODO convert tests 22 | val mapPerKeyType = 23 | AbelianGroupType(V1) =>: 24 | (K1 =>: V1 =>: BagType(ProductType(K2, V2))) =>: 25 | MapType(K1, V1) =>: BagType(ProductType(K2, V2)) 26 | 27 | test("mapPerKey is well-typed") { 28 | untypedTermToTerm(mapPerKey ofType mapPerKeyType).getType should be(mapPerKeyType) 29 | } 30 | 31 | val groupByKeyType = 32 | BagType(ProductType(K2, V2)) =>: MapType(K2, BagType(V2)) 33 | 34 | test("groupByKey is well-typed") { 35 | untypedTermToTerm(groupByKey ofType groupByKeyType).getType should be(groupByKeyType) 36 | } 37 | 38 | val reducePerKeyType = 39 | AbelianGroupType(V3) =>: 40 | (K2 =>: BagType(V2) =>: V3) =>: 41 | MapType(K2, BagType(V2)) =>: MapType(K2, V3) 42 | 43 | test("reducePerKey is well-typed") { 44 | untypedTermToTerm(reducePerKey ofType reducePerKeyType).getType should 45 | be(reducePerKeyType) 46 | } 47 | 48 | val mapReduceType = 49 | AbelianGroupType(V1) =>: 50 | AbelianGroupType(V3) =>: 51 | (K1 =>: V1 =>: BagType(ProductType(K2, V2))) =>: 52 | (K2 =>: BagType(V2) =>: V3) =>: 53 | MapType(K1, V1) =>: MapType(K2, V3) 54 | 55 | test("mapReduce is well-typed") { 56 | untypedTermToTerm(mapReduce ofType mapReduceType).getType should be(mapReduceType) 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /src/main/scala/ilc/feature/inference/SyntaxSugar.scala: -------------------------------------------------------------------------------- 1 | package ilc 2 | package feature 3 | package inference 4 | 5 | import scala.language.implicitConversions 6 | 7 | trait SyntaxSugar extends PrettySyntax with booleans.Syntax { 8 | //Basic utils. 9 | //Force implicit conversions. 10 | def asTerm(t: Term) = t 11 | def asUntyped(t: UntypedTerm) = t 12 | 13 | def typecheck(t: UntypedTerm) = 14 | try { 15 | asTerm(t) 16 | } catch { case e: inference.Inference#UnificationFailure => 17 | println(e.details) 18 | throw e 19 | } 20 | 21 | //"Macros" 22 | /** Usage: 23 | * {{{ 24 | * let_x_= { 25 | * stuff 26 | * } { x => 27 | * blah blah x blah 28 | * } 29 | * }}} 30 | */ 31 | def let(v: Name, meaning: UntypedTerm) 32 | (body: UntypedTerm): UntypedTerm = 33 | (v ->: body)(meaning) 34 | 35 | def letS(pairs: (Name, UntypedTerm)*) 36 | (body: UntypedTerm): UntypedTerm = { 37 | pairs.foldRight(body){ (pair, body) => 38 | let(pair._1, pair._2)(body) 39 | } 40 | } 41 | 42 | //For use within letS. 43 | //Example: 44 | //letS('a := 1, 'b := 2){3} 45 | implicit class NameBindingOps(s: Name) { 46 | def :=(t: UntypedTerm): (Name, UntypedTerm) = s -> t 47 | } 48 | implicit def symToNameBindingOps(s: Symbol) = (s: Name): NameBindingOps 49 | 50 | 51 | //XXX Should use a macro version of const_. 52 | def ifThenElse_(condition: UntypedTerm, thenBranch: UntypedTerm, elseBranch: UntypedTerm) = 53 | IfThenElse(condition, const_(thenBranch), const_(elseBranch)) 54 | 55 | //"Libraries" 56 | val const_ : UntypedTerm = 'a ->: 'b ->: 'a 57 | } 58 | 59 | trait LetSyntaxSugar extends SyntaxSugar with LetUntypedSyntax { 60 | override def let(v: Name, meaning: UntypedTerm) 61 | (body: UntypedTerm): UntypedTerm = 62 | ULet(v, meaning, body) 63 | } 64 | -------------------------------------------------------------------------------- /src/main/scala/ilc/feature/lists/Syntax.scala: -------------------------------------------------------------------------------- 1 | package ilc 2 | package feature 3 | package lists 4 | 5 | trait Syntax 6 | extends base.Syntax 7 | with Types 8 | with functions.Types 9 | with booleans.Types 10 | { 11 | 12 | case object Empty extends ConstantWith1TypeParameter { 13 | val typeConstructor = TypeConstructor("elemType") { t => ListType(t) } 14 | } 15 | 16 | case object Cons extends ConstantWith1TypeParameter { 17 | val typeConstructor = TypeConstructor("elemType") { t => 18 | t =>: ListType(t) =>: ListType(t) 19 | } 20 | } 21 | 22 | case object Head extends ConstantWith1TypeParameter { 23 | val typeConstructor = TypeConstructor("elemType") { t => 24 | ListType(t) =>: t 25 | } 26 | } 27 | 28 | case object Tail extends ConstantWith1TypeParameter { 29 | val typeConstructor = TypeConstructor("elemType") { t => 30 | ListType(t) =>: ListType(t) 31 | } 32 | } 33 | 34 | case object IsEmpty extends ConstantWith1TypeParameter { 35 | val typeConstructor = TypeConstructor("elemType") { t => 36 | ListType(t) =>: BooleanType 37 | } 38 | } 39 | } 40 | 41 | trait InferenceSyntaxSugar extends Syntax with inference.SyntaxSugar { 42 | 43 | implicit class ListOps[T <% UntypedTerm](t: T) { 44 | def :::(s: UntypedTerm) = Cons(s, t) 45 | def head = Head(t) 46 | def tail = Tail(t) 47 | def isEmpty = IsEmpty(t) 48 | def get(i: Int) = project(i, t) 49 | } 50 | 51 | def emptyList: UntypedTerm = Empty 52 | 53 | def list(args: UntypedTerm*) = 54 | args.foldRight(emptyList)(_ ::: _) 55 | 56 | //i is 0-based. 57 | def project(i: Int, t: UntypedTerm): UntypedTerm = 58 | if (i < 0) 59 | sys error s"${i}-th tuple projections are not supported (tuple indexes start from 0)" 60 | else if (i == 0) 61 | t.head 62 | else 63 | project(i - 1, t.tail) 64 | } -------------------------------------------------------------------------------- /src/main/scala/ilc/feature/bintrees/Syntax.scala: -------------------------------------------------------------------------------- 1 | package ilc 2 | package feature 3 | package bintrees 4 | 5 | trait Syntax 6 | extends base.Syntax 7 | with Types 8 | with functions.Types 9 | with booleans.Types 10 | { 11 | 12 | case object EmptyTree extends ConstantWith1TypeParameter { 13 | val typeConstructor = TypeConstructor("elemType") { t => BinTreeType(t) } 14 | } 15 | 16 | case object Tree extends ConstantWith1TypeParameter { 17 | val typeConstructor = TypeConstructor("elemType") { t => 18 | BinTreeType(t) =>: t =>: BinTreeType(t) =>: BinTreeType(t) 19 | } 20 | } 21 | 22 | case object NodeValue extends ConstantWith1TypeParameter { 23 | val typeConstructor = TypeConstructor("elemType") { t => 24 | BinTreeType(t) =>: t 25 | } 26 | } 27 | 28 | case object LeftTree extends ConstantWith1TypeParameter { 29 | val typeConstructor = TypeConstructor("elemType") { t => 30 | BinTreeType(t) =>: BinTreeType(t) 31 | } 32 | } 33 | 34 | case object RightTree extends ConstantWith1TypeParameter { 35 | val typeConstructor = TypeConstructor("elemType") { t => 36 | BinTreeType(t) =>: BinTreeType(t) 37 | } 38 | } 39 | 40 | case object IsEmptyTree extends ConstantWith1TypeParameter { 41 | val typeConstructor = TypeConstructor("elemType") { t => 42 | BinTreeType(t) =>: BooleanType 43 | } 44 | } 45 | } 46 | 47 | trait InferenceSyntaxSugar extends Syntax with inference.SyntaxSugar { 48 | 49 | implicit class TreeOps[T <% UntypedTerm](t: T) { 50 | def nodeValue = NodeValue(t) 51 | def leftTree = LeftTree(t) 52 | def rightTree = RightTree(t) 53 | def isEmptyTree = IsEmptyTree(t) 54 | } 55 | 56 | def emptyTree: UntypedTerm = EmptyTree 57 | def leaf(t: UntypedTerm) = tree(emptyTree, t, emptyTree) 58 | def tree(lhs: UntypedTerm, value: UntypedTerm, rhs: UntypedTerm) 59 | = Tree(lhs, value, rhs) 60 | } -------------------------------------------------------------------------------- /src/main/scala/ilc/feature/sums/Evaluation.scala: -------------------------------------------------------------------------------- 1 | package ilc 2 | package feature.sums 3 | 4 | import scala.language.implicitConversions 5 | 6 | //EvaluationBase isolates the parts of the evaluation not depending on functions. However, 7 | //since we have higher-order constants, most of evalConst needs functions. 8 | //Hence, currently all of evalConst requires functions anyway - we could have 9 | //even more granularity, but this seems already too much. 10 | 11 | trait EvaluationBase extends feature.base.Evaluation with Syntax { 12 | type ValueSum = Either[Value, Value] 13 | 14 | implicit class SumOps(value: Value) { 15 | def toSum: ValueSum = 16 | value match { 17 | case SumValue(s) => s 18 | case _ => value die "toSum" 19 | } 20 | } 21 | 22 | case class SumValue(toSum: ValueSum) extends Value { 23 | override def toString: String = 24 | toSum.fold(x => s"Inj1($x)", y => s"Inj2($y)") 25 | } 26 | 27 | object Inj1Value { 28 | def apply(v: Value): SumValue = SumValue(Left(v)) 29 | def unapply(s: SumValue): Option[Value] = s.toSum match { 30 | case Left(v) => Some(v) 31 | case _ => None 32 | } 33 | } 34 | 35 | object Inj2Value { 36 | def apply(v: Value): SumValue = SumValue(Right(v)) 37 | def unapply(s: SumValue): Option[Value] = s.toSum match { 38 | case Right(v) => Some(v) 39 | case _ => None 40 | } 41 | } 42 | } 43 | 44 | trait Evaluation extends feature.functions.Evaluation with EvaluationBase { 45 | override def coreEval(t: Term, env: Env): Value = t match { 46 | case Inj1(_, _) => 47 | (x: Value) => Inj1Value(x) 48 | 49 | case Inj2(_, _) => 50 | (y: Value) => Inj2Value(y) 51 | 52 | case Either(_, _, _) => 53 | (f: Value) => (g: Value) => (s: Value) => 54 | s.toSum.fold(x => f(x), y => g(y)) 55 | 56 | case _ => 57 | super.coreEval(t, env) 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /src/main/scala/ilc/feature/functions/Context.scala: -------------------------------------------------------------------------------- 1 | package ilc 2 | package feature 3 | package functions 4 | 5 | trait Context extends base.Context with Syntax { 6 | override def getChildren(term: Term): Seq[Subtree] = 7 | term match { 8 | case Abs(variable, body) => 9 | Seq(Subtree(body, AbsBodyContext(Top, variable))) 10 | 11 | case App(operator, operand) => 12 | Seq(Subtree(operator, AppOperatorContext(Top, operand)), 13 | Subtree(operand, AppOperandContext(Top, operator))) 14 | 15 | case _ => 16 | super.getChildren(term) 17 | } 18 | 19 | case class AbsBodyContext(parent: Context, variable: Var) 20 | extends Context 21 | { 22 | // boilerplate clone of this.copy() 23 | def updateParent(newParent: Context): Context = 24 | this.copy(newParent, variable) 25 | 26 | def instantiate(body: Term): Term = Abs(variable, body) 27 | } 28 | 29 | case class AppOperatorContext(parent: Context, operand: Term) 30 | extends Context 31 | { 32 | def updateParent(newParent: Context): Context = 33 | this.copy(newParent, operand) 34 | 35 | def instantiate(operator: Term): Term = App(operator, operand) 36 | } 37 | 38 | case class AppOperandContext(parent: Context, operator: Term) 39 | extends Context 40 | { 41 | def updateParent(newParent: Context): Context = 42 | this.copy(newParent, operator) 43 | 44 | def instantiate(operand: Term): Term = App(operator, operand) 45 | 46 | override def holePosition: Int = 1 47 | } 48 | 49 | def goRight(subtree: Subtree) = subtree match { 50 | case Subtree(operator, AppOperatorContext(parent, operand)) => 51 | Subtree(operand, AppOperandContext(parent, operator)) 52 | } 53 | 54 | def goLeft(subtree: Subtree) = subtree match { 55 | case Subtree(operand, AppOperandContext(parent, operator)) => 56 | Subtree(operator, AppOperatorContext(parent, operand)) 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /src/main/scala/ilc/feature/maps/Evaluation.scala: -------------------------------------------------------------------------------- 1 | package ilc 2 | package feature.maps 3 | 4 | import scala.language.implicitConversions 5 | import scala.collection.immutable 6 | 7 | // Map evaluation - what only depends on base.Evaluation 8 | trait EvaluationBase extends feature.base.Evaluation with Syntax { 9 | // ValueMap is a map between values. 10 | // MapValue is the result of evaluating an expression of MapType. 11 | 12 | private[this] type ValueMap = immutable.Map[Value, Value] 13 | 14 | case class MapValue(toMap: ValueMap) extends Value { 15 | override def toString = toMap.toString 16 | } 17 | 18 | object MapValue { 19 | def apply[K <% Value, V <% Value](assoc: (K, V)*): MapValue = { 20 | val assocValues: Seq[(Value, Value)] = assoc map { 21 | case (key, value) => 22 | //The type ascription invokes implicit conversions. 23 | (key: Value, value: Value) 24 | } 25 | MapValue(immutable.Map.apply(assocValues: _*)) 26 | } 27 | } 28 | 29 | implicit class MapOps(value: Value) { 30 | def toMap: ValueMap = 31 | value match { 32 | case MapValue(m) => m 33 | case _ => value die "toMap" 34 | } 35 | } 36 | } 37 | 38 | trait Evaluation extends EvaluationBase with feature.maybe.Evaluation { 39 | override def coreEval(t: Term, env: Env): Value = t match { 40 | case EmptyMap(_, _) => 41 | MapValue() 42 | 43 | case Update(_, _) => 44 | (k: Value) => (v: Value) => (m: Value) => 45 | MapValue(m.toMap.updated(k, v)) 46 | 47 | case Lookup(_, _) => 48 | (k: Value) => (m: Value) => MaybeValue(m.toMap.get(k)) 49 | 50 | case Delete(_, _) => 51 | (k: Value) => (m: Value) => MapValue(m.toMap - k) 52 | 53 | case Fold(_, _, _) => 54 | (f: Value) => (z: Value) => (map: Value) => 55 | map.toMap.foldRight(z)((p, b) => f(p._1)(p._2)(b)) 56 | 57 | case _ => 58 | super.coreEval(t, env) 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /clients/src/main/scala/ilc/examples/GroupByExample.scala: -------------------------------------------------------------------------------- 1 | package ilc 2 | package examples 3 | 4 | import feature._ 5 | 6 | trait GroupBy 7 | extends abelianMaps.AbelianDerivation 8 | with bags.StdLib 9 | with abelianMaps.ToScala 10 | with bags.AbelianDerivation 11 | with bags.ToScala 12 | { 13 | /** `groupBy` operation on bags: 14 | * 15 | * groupByGen : ∀t k v. (t → k) → (t → v) → Bag t → Map k (Bag v) 16 | * groupByGen = 17 | * Λt k v. λf : t → k. λg : t → v. 18 | * foldGroup (liftGroup FreeAbelianGroup) 19 | * (λe : t. singletonMap (f e) (singleton (g e))) 20 | * 21 | * where 22 | * 23 | * singletonMap : k → v → Map k v 24 | * singleton : v → Bag v 25 | * 26 | * The type of primitives such as `foldGroup` can be looked up in 27 | * src/main/scala/ilc/feature/bags/Syntax.scala and 28 | * src/main/scala/ilc/feature/abelianMaps/Syntax.scala 29 | */ 30 | val groupByGen: UntypedTerm = 31 | 'f ->: 'g ->: 32 | FoldGroup( 33 | LiftGroup(FreeAbelianGroup), 34 | 'e ->: SingletonMap('f('e), Singleton('g('e)))) 35 | } 36 | 37 | /** 38 | * Exemplify using GroupBy. This primitive is important because it is useful for indexing. 39 | */ 40 | class GroupByExample 41 | extends Example 42 | with MapReduce 43 | with GroupBy 44 | with integers.SyntaxSugar 45 | with integers.AbelianDerivation 46 | with integers.ToScala 47 | { 48 | /** An ascription of `groupByGen`, instantiating it to a simply 49 | * typed term. 50 | * 51 | * program = 52 | * groupByGen : ((ℤ × ℤ) → ℤ) → ((ℤ × ℤ) → ℤ) → 53 | * Bag (ℤ × ℤ) → Map ℤ (Bag ℤ) 54 | */ 55 | val untypedProgram: UntypedTerm = 56 | groupByGen ofType 57 | ((ProductType(ℤ, ℤ) =>: ℤ) =>: (ProductType(ℤ, ℤ) =>: ℤ) 58 | =>: BagType(ProductType(ℤ, ℤ)) =>: MapType(ℤ, BagType(ℤ))) 59 | 60 | val program: Term = untypedProgram 61 | } 62 | -------------------------------------------------------------------------------- /src/test/scala/ilc/language/bacchus/BacchusToScalaSuite.scala: -------------------------------------------------------------------------------- 1 | package ilc 2 | package language.bacchus 3 | 4 | /** 5 | * Tests for Bacchus.toScala 6 | */ 7 | 8 | import org.scalatest.FunSuite 9 | import ilc.util.EvalScala 10 | 11 | class BacchusToScalaSuite 12 | extends FunSuite 13 | with ToScala 14 | with Subjects 15 | with EvalScala 16 | with feature.base.Pretty 17 | { 18 | def run(t: Term): Any = { 19 | val code = toScala(t) 20 | try { evalScala(code) } catch { 21 | // convert stack trace to string 22 | // http://stackoverflow.com/a/1149712 23 | case err: Throwable => 24 | // err.getMessage() 25 | val writer = new java.io.StringWriter() 26 | err.printStackTrace(new java.io.PrintWriter(writer)) 27 | // take first 10 lines of a stacktrace 28 | // the wasteful computation here should be acceptable 29 | // unless the thrown object is a StackOverflowError. 30 | val stacktrace = writer.toString.lines.take(10).mkString("\n") 31 | val msg = s"When evaluating\n $code\nwe encountered:\n$stacktrace" 32 | fail(msg, err) 33 | } 34 | } 35 | 36 | test("Nat, Abs, App") { 37 | val code = lambda(ℕ) { x => 1997 } ! 42 38 | assert(run(code) === 1997) 39 | } 40 | 41 | test("EmptyMap, Update, Lookup, Delete, Fold, Plus") { 42 | assert(run(oldMap) === Map(1 -> 2, 2 -> 4, 3 -> 6, 4 -> 8)) 43 | assert(run(Lookup ! 3 ! oldMap) === Some(6)) 44 | assert(run(Lookup ! 9 ! oldMap) === None) 45 | assert(run(Delete ! 1 ! oldMap) === Map(2 -> 4, 3 -> 6, 4 -> 8)) 46 | assert(run(sum ! oldMap) === 20) 47 | } 48 | 49 | test("Inj1, Inj2, Either, FoldNat") { 50 | def times: Term = lambda(ℕ, ℕ) { case Seq(m, n) => 51 | FoldNat ! 0 ! lambda(ℕ) { PlusNat ! _ ! m } ! n 52 | } 53 | assert(run(Either ! (times ! 10) ! (times ! 20) ! (Inj1.tapply(ℕ) ! 4)) === 40) 54 | assert(run(Either ! (times ! 10) ! (times ! 20) ! (Inj2.tapply(ℕ) ! 4)) === 80) 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /src/main/scala/ilc/feature/let/Traversals.scala: -------------------------------------------------------------------------------- 1 | package ilc 2 | package feature 3 | package let 4 | 5 | trait Traversals extends Syntax { 6 | type =?>:[A, B] = PartialFunction[A, B] 7 | 8 | //Switch to Scalaz? 9 | //Does not work. 10 | //def or[T, U](f: T =?>: U)(g: T => U): T => U = 11 | //Works. 12 | def or[T, U](f: PartialFunction[T, U])(g: T => U): T => U = 13 | x => f applyOrElse (x, g) 14 | 15 | def orIdentity[T](f: T =?>: T): T => T = 16 | or(f)(identity) 17 | 18 | //Switch to shapeless? 19 | //Probably yes, since I had to debug this, and it needs to be extended for Let (as lots of existing code), and so on. 20 | /** 21 | * Transform a Term by traversing it in post-order. 22 | * The transformer argument receives a tree where children have been 23 | * transformed. 24 | */ 25 | def everywhere: (Term => Term) => (Term => Term) = 26 | transf => term => 27 | transf(term match { 28 | case App(f, t) => App(everywhere(transf)(f), everywhere(transf)(t)) 29 | case Abs(v, body) => Abs(v, everywhere(transf)(body)) 30 | case Let(v, exp, body) => Let(v, everywhere(transf)(exp), everywhere(transf)(body)) 31 | case other => 32 | other 33 | }) 34 | 35 | /** 36 | * Transform a Term by traversing it in post-order. 37 | * The transformer argument receives a tree where children have been 38 | * transformed, together with the original tree (which is instead 39 | * not passed by everywhere). 40 | */ 41 | def everywherePrePost: ((Term, Term) => Term) => (Term => Term) = 42 | transf => term => 43 | transf(term, term match { 44 | case App(f, t) => App(everywherePrePost(transf)(f), everywherePrePost(transf)(t)) 45 | case Abs(v, body) => Abs(v, everywherePrePost(transf)(body)) 46 | case Let(v, exp, body) => Let(v, everywherePrePost(transf)(exp), everywherePrePost(transf)(body)) 47 | case other => 48 | other 49 | }) 50 | } 51 | -------------------------------------------------------------------------------- /src/main/scala/ilc/feature/maybe/Syntax.scala: -------------------------------------------------------------------------------- 1 | package ilc 2 | package feature 3 | package maybe 4 | 5 | trait Syntax 6 | extends base.Syntax 7 | with Types 8 | with functions.Syntax // for application 9 | { 10 | /** Mimics Haskell's Data.Maybe.mayte 11 | * {{{ 12 | * maybe :: b -> (a -> b) -> Maybe a -> b 13 | * }}} 14 | */ 15 | object Maybe extends ConstantWith2TypeParameters { 16 | val typeConstructor = TypeConstructor("a", "b") {case Seq(a, b) => 17 | b =>: (a =>: b) =>: MaybeType(a) =>: b 18 | } 19 | } 20 | 21 | object Nope extends ConstantWith1TypeParameter { 22 | val typeConstructor = TypeConstructor("contentType") { 23 | contentType => 24 | MaybeType(contentType) 25 | } 26 | } 27 | 28 | object Just extends ConstantWith1TypeParameter { 29 | val typeConstructor = TypeConstructor("contentType") { 30 | contentType => 31 | contentType =>: MaybeType(contentType) 32 | } 33 | } 34 | 35 | /** Maybe as a monad */ 36 | implicit class maybeMonad[T <% TermBuilder](t: T) { 37 | def >>= (f: TermBuilder): TermBuilder = 38 | TermBuilder(context => { 39 | val tBuilder: TermBuilder = t 40 | val tTerm: Term = t.toPolymorphicTerm(context).toTerm 41 | val inputType = tTerm.getType match { 42 | case MaybeType(contentType) => 43 | contentType 44 | 45 | case wrongType => 46 | typeErrorNotTheSame("binding a maybe monad", 47 | "Maybe a", wrongType) 48 | } 49 | val fTerm: Term = f.toPolymorphicTerm(context).specialize(inputType) 50 | val outputType = fTerm.getType match { 51 | case a =>: MaybeType(b) if a == inputType => 52 | b 53 | 54 | case wrongType => 55 | typeErrorNotTheSame("binding a maybe monad", 56 | s"$inputType =>: MaybeType(b)", wrongType) 57 | } 58 | (Maybe ! Nope.tapply(outputType) ! fTerm ! tTerm).toPolymorphicTerm(context) 59 | }) 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /src/test/scala/ilc/analysis/StabilitySuite.scala: -------------------------------------------------------------------------------- 1 | package ilc 2 | package analysis 3 | 4 | import org.scalatest.FunSuite 5 | import ilc.feature._ 6 | 7 | class StabilitySuite 8 | extends FunSuite 9 | with Stability 10 | with functions.Pretty 11 | with naturals.Syntax 12 | { 13 | val app = lambda("f", "x") { case Seq(f, x) => f ! x } 14 | 15 | val appType = (ℕ =>: ℕ) =>: ℕ =>: ℕ 16 | 17 | // app2 = (λ f x → f x) (λ f x → f x) 18 | val app2: Subtree = 19 | Subtree.ofRoot((app ofType appType =>: appType) ! (app ofType appType)) 20 | 21 | val variables = app2.children flatMap 22 | (_.children) flatMap (_.children) flatMap (_.children) 23 | 24 | val Seq(f1, x1, f2, x2) = variables 25 | 26 | //x should be an application node. 27 | def testNavigation(x: Subtree) { 28 | assert(goLeft(goRight(x.children(0))) == x.children(0)) 29 | assert(goRight(goLeft(x.children(1))) == x.children(1)) 30 | } 31 | 32 | test("goLeft and goRight are partial inverses") { 33 | testNavigation(app2) 34 | testNavigation(app2.children(0).children(0).children(0)) 35 | testNavigation(app2.children(1).children(0).children(0)) 36 | //testNavigation(app2.children(1).children(0)) 37 | } 38 | 39 | test("app2's bound variables are stable, unstable, unstable, unstable") { 40 | assert(variables.map(_.isStable) === Seq(true, false, false, false)) 41 | } 42 | 43 | test("app2's bound variables have no stable argument") { 44 | assert(variables.map(_.hasStableArgument(0)) === 45 | Seq(false, false, false, false)) 46 | } 47 | 48 | test("app2's operator has a stable argument") { 49 | val Seq(operator, operand) = app2.children 50 | assert(Range(0, 4).map(i => operator.hasStableArgument(i)) === 51 | Seq(true, false, false, false)) 52 | assert(Range(0, 4).map(i => operand.hasStableArgument(i)) === 53 | Seq(false, false, false, false)) 54 | } 55 | 56 | test("free variables are unstable") { 57 | assert(Subtree.ofRoot(Var("x", ℕ)).isStable === false) 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /src/test/scala/ilc/feature/let/Instantiations.scala: -------------------------------------------------------------------------------- 1 | package ilc 2 | package feature 3 | package let 4 | 5 | trait ShowTerms { 6 | this: Pretty => 7 | 8 | def show(t: Term) = 9 | "\n" + pretty(t) 10 | 11 | def verboseShowTerm(t: Term, qual: String) = 12 | println( 13 | s"""|Raw $qual term: ${t} 14 | |Pretty $qual term: 15 | |${pretty(t)} 16 | |Type: ${t.getType} 17 | |""".stripMargin) 18 | 19 | } 20 | 21 | trait Instantiations { 22 | def buildBacchusWithLetSystem(doCSE_ : Boolean, copyPropagation_ : Boolean, partialApplicationsAreSpecial_ : Boolean) = 23 | new language.LetLanguage with let.ShowTerms with let.ANormalFormAdapter with inference.InferenceTestHelper 24 | //XXX added to also test CPS in worksheets 25 | with let.CPS with cbpv.CBPVToCPSTypes { 26 | outer => 27 | val aNormalizer = new ANormalFormStateful { 28 | val syntax: outer.type = outer 29 | override val doCSE = doCSE_ 30 | override val copyPropagation = copyPropagation_ 31 | override val partialApplicationsAreSpecial = partialApplicationsAreSpecial_ 32 | } 33 | } 34 | 35 | def buildBaseBacchus() = new language.LetLanguage with let.ShowTerms 36 | 37 | 38 | /** 39 | * This will produce an AddCaches2 component which shares the component type with its argument. 40 | * The method type is inferred to have a dependent type: (bacchus: T): AddCaches { val syntax: bacchus.type }. 41 | * This is why you can call this method without annotating T to be a singleton type. 42 | */ 43 | def buildCacher(bacchus: Syntax with IsAtomic with products.SyntaxSugar with unit.Syntax with Traversals) = 44 | new AddCaches2 { 45 | val syntax: bacchus.type = bacchus 46 | } 47 | } 48 | 49 | object WorksheetHelpers extends Instantiations { 50 | val bacchus = buildBaseBacchus() 51 | val cacher = buildCacher(bacchus) 52 | 53 | //Assert the equality of these two types as a test. 54 | implicitly[bacchus.Term =:= cacher.syntax.Term] 55 | } 56 | -------------------------------------------------------------------------------- /src/test/scala/ilc/examples/BenchData.scala: -------------------------------------------------------------------------------- 1 | package ilc 2 | package examples 3 | 4 | import org.scalameter.api._ 5 | import examples._ 6 | 7 | // Serializability is needed for passing instances to separate JVMs for benchmarking. 8 | trait BenchData extends Serializable { 9 | /** 10 | * Subclass obligation: ExampleGenerated instance containing the generated code. 11 | */ 12 | val example: ExampleGenerated 13 | 14 | import example._ 15 | 16 | type Data = InputType 17 | type Change = DeltaInputType 18 | 19 | /** 20 | * Subclass obligation: inputs given the size. 21 | */ 22 | def inputOfSize(n: Int): Data 23 | 24 | /** 25 | * Subclass obligation: list of descriptions of changes. 26 | */ 27 | def changeDescriptions: Gen[String] 28 | 29 | /** 30 | * Subclass obligation: map input size and change description to actual change. 31 | * (XXX: should use an enum instead of a description for the key). 32 | */ 33 | def lookupChange(description: String, 34 | inputSize: Int, 35 | input: InputType, 36 | output: OutputType): Change 37 | 38 | case class Datapack( 39 | oldInput: Data, 40 | newInput: Data, 41 | change: Change, 42 | oldOutput: OutputType) 43 | 44 | // collection sizes 45 | def base = 1000 46 | def last = 5000 47 | def step = 1000 48 | def sizes: Gen[Int] = Gen.range("n")(base, last, step) 49 | 50 | lazy val inputsOutputsChanges: Gen[Datapack] = for { 51 | n <- sizes 52 | description <- changeDescriptions 53 | } yield { 54 | val oldInput = inputOfSize(n) 55 | val oldOutput = program(oldInput) 56 | val change = lookupChange(description, n, oldInput, oldOutput) 57 | val newInput = updateInput(change)(oldInput) 58 | Datapack(oldInput, newInput, change, oldOutput) 59 | } 60 | 61 | def className: String = example.getClass.getName.stripSuffix("$") 62 | 63 | val derivatives = 64 | Array( 65 | ("derivative", derivative), 66 | ("normalized derivative", normDerivative)) 67 | } 68 | -------------------------------------------------------------------------------- /src/main/scala/ilc/feature/inference/UntypedSyntax.scala: -------------------------------------------------------------------------------- 1 | package ilc 2 | package feature 3 | package inference 4 | 5 | trait UntypedSyntax { 6 | this: base.Syntax => 7 | 8 | trait UntypedTerm 9 | 10 | case class UVar(getName: Name) extends UntypedTerm 11 | case class UAbs(argumentName: Name, typeAnnotation: Option[Type], body: UntypedTerm) extends UntypedTerm 12 | case class UApp(operator: UntypedTerm, operand: UntypedTerm) extends UntypedTerm 13 | case class UMonomorphicConstant(term: Term) extends UntypedTerm 14 | case class UPolymorphicConstant(term: PolymorphicConstant) extends UntypedTerm 15 | case class TypeAscription(term: UntypedTerm, typ: Type) extends UntypedTerm 16 | 17 | def freeVars(t: UntypedTerm): Set[Name] = t match { 18 | case UVar(n) => Set(n) 19 | case UAbs(n, _, body) => freeVars(body) - n 20 | case UApp(f, arg) => freeVars(f) ++ freeVars(arg) 21 | case TypeAscription(t, _) => freeVars(t) 22 | case UMonomorphicConstant(_) => Set.empty 23 | case UPolymorphicConstant(_) => Set.empty 24 | } 25 | } 26 | 27 | trait LetUntypedSyntax extends UntypedSyntax { 28 | this: base.Syntax with functions.Syntax => 29 | 30 | case class ULet(variable: Name, exp: UntypedTerm, body: UntypedTerm) extends UntypedTerm 31 | override def freeVars(t: UntypedTerm) = t match { 32 | case ULet(v, exp, body) => 33 | // Parens are important here: if there's shadowing, v can be free on the 34 | // LHS. 35 | freeVars(exp) ++ (freeVars(body) - v) 36 | case _ => 37 | super.freeVars(t) 38 | } 39 | } 40 | 41 | trait LetRecUntypedSyntax extends UntypedSyntax { 42 | this: base.Syntax with functions.Syntax => 43 | 44 | case class ULetRec(pairs: List[(Name, UntypedTerm)], bodyName: Name, body: UntypedTerm) extends UntypedTerm 45 | override def freeVars(t: UntypedTerm) = t match { 46 | case ULetRec(pairs, _, body) => 47 | freeVars(body) ++ (pairs flatMap (x => freeVars(x._2))) -- (pairs map (_._1)) 48 | case _ => 49 | super.freeVars(t) 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/main/scala/ilc/feature/base/ToScala.scala: -------------------------------------------------------------------------------- 1 | package ilc 2 | package feature 3 | package base 4 | 5 | import util.IndentUtils 6 | 7 | trait ToScala extends Syntax with IndentUtils { 8 | //Contract for indentation: no indentation in the beginning 9 | def toScala(t: Term): String = 10 | s"(${toUntypedScala(t)} : ${toScala(t.getType)})" 11 | 12 | // subclasses should override this one without concern for types 13 | def toUntypedScala(t: Term): String = t match { 14 | case Var(name, _) => 15 | name.toString 16 | 17 | case _ => 18 | sys error s"Unknown term $t" 19 | } 20 | 21 | // types 22 | def toScala(tau: Type): String = tau match { 23 | case _ => 24 | sys error s"Unknown type $tau" 25 | } 26 | 27 | def isScalaPrimitive(tau: Type): Boolean = tau match { 28 | case _ => false 29 | } 30 | 31 | // helper to create scala functions 32 | // subclasses should always call this helper to create scala 33 | // functions. CAUTION: supplied parameter names are binding. 34 | // body: no indentation in the beginning. 35 | def scalaFunction(parameterNames: String*)(body: => String): String = { 36 | def toParam(name: String) = name + "_param" 37 | def declarations = parameterNames map { name => 38 | s"${indent}lazy val $name = ${toParam(name)}" 39 | } mkString "" 40 | def loop(names: Seq[String]): String = 41 | if (names.isEmpty) 42 | s"${openBrace()}${declarations}${indent}${body}${closeBrace()}" 43 | else 44 | s"${toParam(names.head)} => ${loop(names.tail)}" 45 | s"(${loop(parameterNames)})" 46 | } 47 | 48 | // automatic imports for generated code 49 | private[this] 50 | val features = collection.mutable.Set.empty[String] 51 | 52 | def addLibrary(featureName: String) { 53 | features += featureName 54 | } 55 | 56 | /** Imports to include in generated code. By default, the name is determined depending on this.language. 57 | */ 58 | def imports: String = features map { featureName => 59 | s"import ilc.feature.$featureName.Library._" 60 | } mkString "\n" 61 | } 62 | -------------------------------------------------------------------------------- /src/main/scala/ilc/feature/abelianGroups/Syntax.scala: -------------------------------------------------------------------------------- 1 | package ilc 2 | package feature 3 | package abelianGroups 4 | 5 | trait Syntax 6 | extends base.Syntax 7 | with Types 8 | with booleans.Types 9 | with integers.Types // for identifying abelian groups 10 | { 11 | case object AbelianGroup extends ConstantWith1TypeParameter { 12 | val typeConstructor = TypeConstructor("e") { e => 13 | IntType =>: 14 | (e =>: e =>: e) =>: (e =>: e) =>: e =>: AbelianGroupType(e) 15 | } 16 | } 17 | 18 | case object GetBinOp extends ConstantWith1TypeParameter { 19 | val typeConstructor = TypeConstructor("e") { e => 20 | AbelianGroupType(e) =>: binOpType(e) 21 | } 22 | } 23 | 24 | case object GetInv extends ConstantWith1TypeParameter { 25 | val typeConstructor = TypeConstructor("e") { e => 26 | AbelianGroupType(e) =>: invType(e) 27 | } 28 | } 29 | 30 | case object GetNeutral extends ConstantWith1TypeParameter { 31 | val typeConstructor = TypeConstructor("e") { e => 32 | AbelianGroupType(e) =>: e 33 | } 34 | } 35 | 36 | case object AreEqualGroups extends ConstantWith1TypeParameter { 37 | val typeConstructor = TypeConstructor("e") { e => 38 | AbelianGroupType(e) =>: AbelianGroupType(e) =>: BooleanType 39 | } 40 | } 41 | } 42 | 43 | trait SyntaxSugar 44 | extends Syntax 45 | with booleans.SyntaxSugar 46 | { 47 | def ifEqualGroups(firstPair: (TermBuilder, TermBuilder), 48 | groupPairs: (TermBuilder, TermBuilder)*) 49 | (thenBranch: => TermBuilder) 50 | (elseBranch: => TermBuilder): TermBuilder = 51 | ifThenElse( 52 | AreEqualGroups ! firstPair._1 ! firstPair._2, 53 | (if (groupPairs.isEmpty) 54 | thenBranch 55 | else 56 | ifEqualGroups(groupPairs.head, groupPairs.tail: _*)( 57 | thenBranch)(elseBranch)), 58 | elseBranch) 59 | 60 | //def groupUnfoldTerm: TermBuilder = GroupUnfold 61 | // stability framework can't tell higher-order arguments if 62 | // their arguments are stable. `groupUnfold` has to be a 63 | // metafunction. 64 | } 65 | -------------------------------------------------------------------------------- /clients/src/main/scala/ilc/examples/DBToasterExamples.scala: -------------------------------------------------------------------------------- 1 | package ilc 2 | package examples 3 | 4 | import feature._ 5 | 6 | /** 7 | * Experiments related to Koch's paper[1]. XXX currently ***VERY*** experimental. 8 | * 9 | * [1] Koch et al. (2014), *DBToaster: higher-order delta processing for dynamic, frequently fresh views*. 10 | */ 11 | trait DBToasterExample extends Example 12 | with MapReduce 13 | with GroupBy 14 | with integers.SyntaxSugar 15 | with integers.AbelianDerivation 16 | with integers.ToScala 17 | with equality.Syntax 18 | with inference.SyntaxSugar 19 | { 20 | 21 | } 22 | 23 | /** 24 | * This is related to example 2 in Koch's paper. 25 | */ 26 | class DBToasterExample2 27 | extends DBToasterExample 28 | { 29 | def dictFlatMap: UntypedTerm = ??? 30 | // 31 | val dictKeyFilter: UntypedTerm = 32 | 'valueGroup ->: 'userKeyFilter ->: 'map ->: 33 | FoldByHom('valueGroup, LiftGroup('valueGroup), 34 | 'k ->: 'v ->: ifThenElse_('userKeyFilter('k), SingletonMap('k, 'v), EmptyMap), 35 | 'map) 36 | 37 | //Produces a map of bags. 38 | val bagDictKeyFilter: UntypedTerm = dictKeyFilter(FreeAbelianGroup) 39 | 40 | def join: UntypedTerm = ??? 41 | //XXX better name, eq is taken! 42 | val eqq: UntypedTerm = Eq 43 | 44 | //All of this is missing aggregation. 45 | def untypedProgram0 = 'r ->: 's ->: join('r, 's, 'rEl ->: 'sEl ->: eqq(Proj1('rEl), Proj2('sEl)), Pair) 46 | def untypedProgram = 47 | 'r ->: 's ->: { 48 | letS ( 49 | ('idxR, groupByGen(Proj1, Proj2, 'r)), 50 | ('idxS, groupByGen(Proj1, Proj2, 's)) 51 | ) { 52 | 'idxR 53 | //filter (first('r) == first('s)) ... 54 | //dictFlatMap('k ->: 'v ->: bagDictKeyFilter('k2 ->: eqq('k, 'k2), 'idxS))('idxR) 55 | } 56 | //mapFlatMap apply ('k ->: 'v ->: mapFilter('k2 ->: 'v2 ->: eqq('k, 'k2), groupByGen('s, first, second))) apply groupByGen('r, first, proj2) 57 | } 58 | override val program: Term = 'x ->: 'x //untypedProgram 59 | 60 | def fooMethod() = { 61 | println(pretty(typecheck(untypedProgram))) 62 | } 63 | 64 | //XXX to call in tests: 65 | //(new DBToasterExample2).fooMethod 66 | } 67 | -------------------------------------------------------------------------------- /src/main/scala/ilc/feature/products/Derivation.scala: -------------------------------------------------------------------------------- 1 | package ilc 2 | package feature 3 | package products 4 | 5 | /** Δ(σ × τ) = Δσ × Δτ 6 | * 7 | * This change type can support products of functions, 8 | * which is necessary to express groups. 9 | */ 10 | 11 | trait Derivation extends base.Derivation with Syntax { 12 | override def deltaType(tau: Type): Type = tau match { 13 | case ProductType(leftType, rightType) => 14 | ProductType(deltaType(leftType), deltaType(rightType)) 15 | 16 | case _ => 17 | super.deltaType(tau) 18 | } 19 | 20 | override def updateTerm(tau: Type): Term = tau match { 21 | case pType@ProductType(leftType, rightType) => 22 | lambda(Var("dp", deltaType(pType)), Var("p", pType)) { 23 | case Seq(dp, p) => 24 | Pair ! (updateTerm(leftType) ! (Proj1 ! dp) ! (Proj1 ! p)) ! 25 | (updateTerm(rightType) ! (Proj2 ! dp) ! (Proj2 ! p)) 26 | } 27 | 28 | case _ => 29 | super.updateTerm(tau) 30 | } 31 | 32 | override def diffTerm(tau: Type): Term = tau match { 33 | case pType@ProductType(leftType, rightType) => 34 | lambda(Var("P", pType), Var("q", pType)) { 35 | case Seq(p, q) => 36 | Pair ! (diffTerm(leftType) ! (Proj1 ! p) ! (Proj1 ! q)) ! 37 | (diffTerm(rightType) ! (Proj2 ! p) ! (Proj2 ! q)) 38 | } 39 | 40 | case _ => 41 | super.diffTerm(tau) 42 | } 43 | 44 | override def derive(t: Term): Term = t match { 45 | case Pair(leftType, rightType) => { 46 | val (x, y) = (Var("x", leftType), Var("y", rightType)) 47 | lambda(x, DVar(x), y, DVar(y)) { case Seq(x, dx, y, dy) => 48 | Pair ! dx ! dy 49 | } 50 | } 51 | 52 | case Proj1(leftType, rightType) => { 53 | val p = Var("p", ProductType(leftType, rightType)) 54 | lambda(p, DVar(p)) { case Seq(p, dp) => 55 | Proj1 ! dp 56 | } 57 | } 58 | 59 | case Proj2(leftType, rightType) => { 60 | val p = Var("p", ProductType(leftType, rightType)) 61 | lambda(p, DVar(p)) { case Seq(p, dp) => 62 | Proj2 ! dp 63 | } 64 | } 65 | 66 | case _ => 67 | super.derive(t) 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /src/test/scala/ilc/language/testbed/FullPrettySuite.scala: -------------------------------------------------------------------------------- 1 | package ilc 2 | package language 3 | package testbed 4 | 5 | import feature._ 6 | 7 | import org.scalatest.FunSuite 8 | 9 | class FullPrettySuite 10 | extends FunSuite 11 | 12 | with bintrees.Pretty // nullary 13 | with integers.Pretty // nullary 14 | with lists.Pretty // nullary 15 | with naturals.Pretty // nullary 16 | with unit.Pretty // nullary 17 | 18 | with functions.Pretty // binary 19 | with sums.Pretty // binary 20 | with products.Pretty // binary 21 | { 22 | // dummy types 23 | case object A extends Type 24 | case object B extends Type 25 | case object C extends Type 26 | case object D extends Type 27 | 28 | val fun2 = (A =>: B) =>: (C =>: D) 29 | val sum2 = SumType(SumType(A, B), SumType(C, D)) 30 | val pro2 = ProductType(ProductType(A, B), ProductType(C, D)) 31 | 32 | val funSum = A =>: SumType(B, C) 33 | val funPro = A =>: ProductType(B, C) 34 | val sumFun = SumType(A, B =>: C) 35 | val sumPro = SumType(A, ProductType(B, C)) 36 | val proFun = ProductType(A, B =>: C) 37 | val proSum = ProductType(A, SumType(B, C)) 38 | 39 | test("Should pretty-print nullary mixfix operators") { 40 | assert(pretty(BinTreeType(BinTreeType(A))) == "<# <# A #> #>") 41 | assert(pretty(ListType(BinTreeType(A))) == "[<# A #>]") 42 | assert(pretty(BinTreeType(ListType(A))) == "<# [A] #>") 43 | assert(pretty(ListType(ListType(A))) == "[[A]]") 44 | 45 | assert(pretty(IntType) == "Z") 46 | assert(pretty(NatType) == "N") 47 | assert(pretty(UnitType) == "1") 48 | } 49 | 50 | test("Pretty printer should respect associativity") { 51 | assert(pretty(fun2) == "(A -> B) -> C -> D") 52 | assert(pretty(sum2) == "A + B + (C + D)") 53 | assert(pretty(pro2) == "A * B * (C * D)") 54 | } 55 | 56 | test("Pretty printer should respect precedence") { 57 | assert(pretty(funSum) == "A -> B + C") 58 | assert(pretty(funPro) == "A -> B * C") 59 | assert(pretty(sumFun) == "A + (B -> C)") 60 | assert(pretty(sumPro) == "A + B * C") 61 | assert(pretty(proFun) == "A * (B -> C)") 62 | assert(pretty(proSum) == "A * (B + C)") 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /src/main/scala/ilc/metaprogs/AlphaEquiv.scala: -------------------------------------------------------------------------------- 1 | package ilc 2 | package metaprogs 3 | 4 | import feature._ 5 | 6 | trait AlphaEquiv { 7 | outer: functions.Syntax => 8 | //Use custom name to avoid conflicts 9 | protected val aeFreshGen = new base.FreshGen { val syntax: outer.type = outer } 10 | import aeFreshGen.freshName 11 | 12 | def alphaEquiv(t1: Term, t2: Term, ignoreTypes: Boolean = false): Boolean = 13 | doAlphaEquiv(t1, t2, Map(), Map(), ignoreTypes) 14 | //The maps should be from Name to Name 15 | protected def doAlphaEquiv(t1: Term, t2: Term, map1: Map[Name, Name], map2: Map[Name, Name], ignoreTypes: Boolean): Boolean = 16 | (t1, t2) match { 17 | case (Abs(Var(n1, t1), body1), Abs(Var(n2, t2), body2)) => 18 | (ignoreTypes || t1 == t2) && { 19 | val nNew = freshName(n1) 20 | doAlphaEquiv(body1, body2, map1 + (n1 -> nNew), map2 + (n2 -> nNew), ignoreTypes) 21 | } 22 | case (App(f1, arg1), App(f2, arg2)) => 23 | doAlphaEquiv(f1, f2, map1, map2, ignoreTypes) && doAlphaEquiv(arg1, arg2, map1, map2, ignoreTypes) 24 | case (v1 @ Var(n1, t1), v2 @ Var(n2, t2)) => 25 | (ignoreTypes || t1 == t2) && map1(n1) == map2(n2) 26 | case (Constant(pc1, tt1), Constant(pc2, tt2)) => 27 | pc1 == pc2 && (ignoreTypes || tt1 == tt2) 28 | case _ => 29 | //Remaining values, that do *not* include type arguments. 30 | //So this *will* ignore types. 31 | t1 == t2 32 | } 33 | } 34 | 35 | trait AlphaEquivLet extends AlphaEquiv { 36 | outer: let.Syntax => 37 | import aeFreshGen.freshName 38 | 39 | override protected def doAlphaEquiv(t1: Term, t2: Term, map1: Map[Name, Name], map2: Map[Name, Name], ignoreTypes: Boolean): Boolean = 40 | (t1, t2) match { 41 | case (Let(Var(n1, t1), e1, body1), Let(Var(n2, t2), e2, body2)) => 42 | (ignoreTypes || t1 == t2) && 43 | doAlphaEquiv(e1, e2, map1, map2, ignoreTypes) && 44 | { 45 | val nNew = freshName(n1) 46 | doAlphaEquiv(body1, body2, map1 + (n1 -> nNew), map2 + (n2 -> nNew), ignoreTypes) 47 | } 48 | case _ => 49 | super.doAlphaEquiv(t1, t2, map1, map2, ignoreTypes) 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/main/scala/ilc/feature/memoize/ToScala.scala: -------------------------------------------------------------------------------- 1 | package ilc 2 | package feature 3 | package memoize 4 | 5 | trait ToScala extends Syntax with base.ToScala { 6 | this: functions.Syntax with analysis.FreeVariables => 7 | 8 | addLibrary("memoize") 9 | 10 | //This needs to create a special term that used benign side effects. 11 | /*def createLookup(cacheName: Name, memoizedT: Term, freeVars: List[Var], updateCache: Boolean): Term = 12 | freeVars.foldLeft(Summon(cacheName): TermBuilder) { case (cache, newVar) => 13 | GetOrElseUpdate ! cache ! newVar ! ??? //memoizedT 14 | }*/ 15 | 16 | 17 | /* 18 | //Some intended example output: 19 | //val v = mutable.Map[Int, mutable.Map[Int, OptCell[Term]]]() 20 | //v.getOrElseUpdate(1, mutable.Map()).getOrElseUpdate(2, OptCell(???)) 21 | { 22 | val cache_1: OptCell[Any => Any] = null 23 | val cache_2: mutable.HashMap[Any, OptCell[Any => Any]] = null 24 | 25 | (cache_1.getOrElseUpdate((x_param => { 26 | lazy val x = x_param 27 | (cache_2.getOrElseUpdate(x, OptCell())) 28 | }))) 29 | } 30 | */ 31 | 32 | def toScalaWidened(v: Var): String = { 33 | val tAsScala = v.getName.toString 34 | if (isScalaPrimitive(v.getType)) { 35 | s"widenToLong($tAsScala)" 36 | } else { 37 | tAsScala 38 | } 39 | } 40 | 41 | //Warning: No Barendregt convention around to save us! 42 | override def toUntypedScala(t: Term): String = t match { 43 | case App(Constant(Memo(ce, _), cType), subTerm) => 44 | val (lookups, _) = ce.freeVariables.foldRight((s".getOrElseUpdate(${toUntypedScala(subTerm)})", "OptCell()")) ({ 45 | case (newVar, (lookups, initExp)) => (s".getOrElseUpdate(${toScalaWidened(newVar)}, ${initExp})$lookups", 46 | varToScalaMapType(newVar, None) + "()") 47 | }) 48 | //ce.name.toString + lookups 49 | s"(${ce.name.toString}: ${ce.scalaType})${lookups}" 50 | case _ => super.toUntypedScala(t) 51 | } 52 | 53 | def declareCaches(mc: MemoContextBase): String = { 54 | val cacheDecls = for { 55 | CacheEntry(name, _, scalaType) <- mc.cacheMap.values 56 | } yield s"val ${name} = ${scalaType}()" 57 | cacheDecls.mkString(s"${indent}") 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /icfp2014/src/test/scala/ilc/language/gcc/PrimitivesSuite.scala: -------------------------------------------------------------------------------- 1 | package ilc 2 | package language 3 | package gcc 4 | 5 | import org.scalatest.FunSuite 6 | import org.scalatest.Matchers 7 | 8 | import Predef.{any2stringadd => _, _} 9 | import scala.language.reflectiveCalls 10 | 11 | class GCCPrimitivesSuite extends FunSuite with Matchers { 12 | import GCC._ 13 | 14 | test("Booleans") { 15 | 16 | typecheck { 17 | lam('a % bool, 'b % bool) { 18 | ('a or 'b) and 'a 19 | } 20 | } 21 | 22 | typecheck { 23 | switch(42)( 24 | 44 ==> 4, 25 | 43 ==> 5 26 | ) withDefault 0 27 | } 28 | 29 | 30 | } 31 | 32 | 33 | test("Tuples") { 34 | typecheck { 35 | lam('tuple % ((int, int, int))) { 36 | 'tuple.bind('a, 'b, 'c) { 37 | 'b 38 | } 39 | } 40 | } 41 | } 42 | 43 | test("Assignments") { 44 | val prog = lam('a % bool, 'b % int) { 45 | { 'a <~ true } ~: 46 | { debug('a) } ~: 47 | { 'b <~ 42 } ~: 48 | { debug('b) } 49 | } 50 | 51 | typecheck { prog } 52 | } 53 | 54 | test("Classes") { 55 | 56 | val prog = letrec( 57 | class_('Point)('x % int, 'y % int)( 58 | fun('move)('a % int) { 59 | { 'x <~ 'x + 'a } 60 | }, 61 | fun('getX)('_) { 'x } 62 | ), 63 | 64 | fun('buildPoints)('_) { 65 | letS( 66 | 'p := asUntyped('new_Point)(1, 4) 67 | )('p.call('Point, 'move)(42) ~: 'p) 68 | } 69 | )("prog", asUntyped('buildPoints)(0).call('Point, 'getX)(0)) 70 | 71 | val progSimple = letS( 72 | class_('Point)('x % int, 'y % int)( 73 | fun('move)('a % int) { 74 | { 'x <~ 'x + 'a } 75 | }, 76 | fun('getX)('_) { 'x } 77 | ))(debug(asUntyped('new_Point)(1, 4).call('Point, 'getX)(0))) 78 | 79 | typecheck { prog } 80 | 81 | } 82 | 83 | test("Should build bin trees as tuples") { 84 | val prog = tree(leaf(4), 42, tree(leaf(4), 8, leaf(6))) 85 | 86 | // should be the same code as 87 | val prog2: UT = tuple(tuple(0, 4, 0), 42, tuple(tuple(0, 4, 0), 8, tuple(0, 6, 0))) 88 | 89 | toProg(prog).code shouldEqual toProg(prog2).code 90 | 91 | } 92 | 93 | } 94 | -------------------------------------------------------------------------------- /src/main/scala/ilc/feature/cbpv/CBPVToCPSTypes.scala: -------------------------------------------------------------------------------- 1 | package ilc 2 | package feature 3 | package cbpv 4 | 5 | /** 6 | * Implement conversion from CBPV to CPS for types. 7 | * Incomplete. 8 | */ 9 | trait CBPVToCPSTypes extends TypeConversions with let.CPSTypes { 10 | def cbvTypeToCPS(t: Type) = valTypeToCPS(cbvTypeToCBPV(t)) 11 | 12 | def valTypeToCPS(vt: ValType): Type = vt match { 13 | case UThunkVT(ct) => 14 | compTypeToCPS(ct) =>: AnswerT 15 | //These are value types, so they aren't serious 16 | case UnitVT => 17 | UnitType 18 | case SumVT(a, b) => SumType(valTypeToCPS(a), valTypeToCPS(b)) 19 | case ProdVT(a, b) => ProductType(valTypeToCPS(a), valTypeToCPS(b)) 20 | case BaseVT(t) => t 21 | } 22 | 23 | def compTypeToCPS(ct: CompType): Type = ct match { 24 | case FProducerCT(vt) => 25 | valTypeToCPS(vt) =>: AnswerT 26 | case FunCT(srcVt, dstCt) => 27 | // Tempting, but wrong: 28 | //valTypeToCPS(srcVt) =>: compTypeToCPS(dstCt) 29 | 30 | // According to the literature, we need the product of the types, which 31 | // happens to be the adjoint (in fact, unsurprisingly, that's how this arises). 32 | ProductType(valTypeToCPS(srcVt), compTypeToCPS(dstCt)) 33 | // In fact, we can even flip the operands, to pass the continuation first 34 | // (and confusingly flip all other operands, but do we care?), as follows: 35 | //ProductType(compTypeToCPS(dstCt), valTypeToCPS(srcVt)) 36 | // 37 | //However, what's the matching change for the code transformation? 38 | //Finally, taking continuations first (as in Fischer's transform) or last 39 | //(as in Plotkin's transform) makes an amazing amount of difference 40 | //theoretically. (See for instance Sabry and Wadler's "A Reflection on 41 | //Call-by-Value"; see also footnote 12 of Hatcliff and Danvy's "A Generic 42 | //Account of Continuation-Passing Style"). Apparently, taking 43 | //continuations first enables doing more administrative reductions (for 44 | //some purposes, too many). 45 | case ProdCT(a, b) => 46 | ??? 47 | //Heck, ProdCT is a pair of continuations. 48 | //ProductType(compTypeToType(a), compTypeToType(b)) 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/main/scala/ilc/feature/memoize/Library.scala: -------------------------------------------------------------------------------- 1 | package ilc 2 | package feature 3 | package memoize 4 | 5 | object Library { 6 | //This should be in some standard library — it's a thread-unsafe version of scala.concurrent.SyncVar 7 | class Cell[T](initialContent: T) { 8 | var value: T = initialContent 9 | def apply() = value 10 | def update(newValue: T) { 11 | value = newValue 12 | } 13 | } 14 | 15 | class OptCell[T] { 16 | val box: Cell[Option[T]] = new Cell(None) 17 | def getOrElseUpdate(newValue: => T): T = { 18 | val curr = box() 19 | curr match { 20 | case Some(v) => v 21 | case None => 22 | val ret = newValue 23 | box() = Some(ret) 24 | ret 25 | } 26 | } 27 | } 28 | 29 | object OptCell { 30 | def apply[T](initialContent: T): OptCell[T] = { 31 | val ret = new OptCell[T] 32 | ret.box() = Some(initialContent) 33 | ret 34 | } 35 | def apply[T]() = new OptCell[T] 36 | } 37 | 38 | //Reexport relevant maps. 39 | import scala.collection.{mutable => scm} 40 | 41 | type MemoizePrimMap[V] = scm.LongMap[V] 42 | val MemoizePrimMap = scm.LongMap 43 | // XXX: should switch to http://trove4j.sourceforge.net/javadocs/gnu/trove/map/hash/TLongObjectHashMap.html? 44 | 45 | type MemoizeObjMap[K, V] = scm.Map[K, V] 46 | // XXX: should avoid the overhead of Scala wrappers? 47 | def MemoizeObjMap[Key, Value](): MemoizeObjMap[Key, Value] = { 48 | import java.util.IdentityHashMap 49 | import scala.collection.JavaConverters._ 50 | new IdentityHashMap[Key, Value]().asScala 51 | } 52 | 53 | //These widenings are used before hashtable lookup, so they must be injective 54 | //to ensure correctness. 55 | def widenToLong(v: Long): Long = v 56 | def widenToLong(v: Boolean): Long = if (v) 1 else 0 57 | 58 | def widenToLong(v: Double): Long = 59 | //I use doubleToRawLongBits over doubleToLongBits to avoid canonicalizing 60 | //different NaNs together. 61 | java.lang.Double.doubleToRawLongBits(v) 62 | 63 | //We could allow widening to Double, but this is more obviously accurate. 64 | def widenToLong(v: Float): Long = 65 | java.lang.Float.floatToRawIntBits(v).toLong 66 | 67 | def widenToLong(v: Char): Long = 68 | v.toLong 69 | } 70 | -------------------------------------------------------------------------------- /src/main/scala/ilc/feature/abelianMaps/Library.scala: -------------------------------------------------------------------------------- 1 | package ilc 2 | package feature 3 | package abelianMaps 4 | 5 | import collection.immutable.HashMap 6 | import abelianGroups.Library._ 7 | 8 | object Library { 9 | type AbelianMap[K, V] = HashMap[K, V] 10 | val AbelianMap = HashMap 11 | 12 | def emptyMap[K, V]: AbelianMap[K, V] = 13 | AbelianMap.empty 14 | 15 | def singletonMap[K, V]: (=>K) => (=>V) => AbelianMap[K, V] = 16 | key => value => AbelianMap(key -> value) 17 | 18 | def liftGroup[K, V]: 19 | (=> AbelianGroup[V]) => AbelianGroup[AbelianMap[K, V]] = 20 | valueGroup => LiftedMapGroup(valueGroup) 21 | 22 | def foldByHom[K, A, B]: 23 | (=> AbelianGroup[A]) => (=> AbelianGroup[B]) => 24 | (=> ((=> K) => (=> A) => B)) => (=> AbelianMap[K, A]) => B = 25 | _GaParam => _GbParam => fParam => mParam => { 26 | lazy val _Ga = _GaParam 27 | lazy val _Gb = _GbParam 28 | lazy val f = fParam 29 | lazy val m = mParam 30 | m.foldRight[B](_Gb.neutral) { (keyValuePair, element) => 31 | _Gb.binOp(f(keyValuePair._1)(keyValuePair._2))(element) 32 | } 33 | } 34 | 35 | case class LiftedMapGroup[K, V](valueGroup: AbelianGroup[V]) 36 | extends AbelianGroup[AbelianMap[K, V]] 37 | { 38 | private type T = AbelianMap[K, V] 39 | 40 | val binOp: (=>T) => (=>T) => T = m1Param => m2Param => { 41 | lazy val m1 = m1Param 42 | lazy val m2 = m2Param 43 | // consider using HashMap.merge instead (compare benchmarks) 44 | m1.foldRight[T](m2) { (keyValuePair, wipMap) => { 45 | val (key, value) = keyValuePair 46 | val newValue = wipMap.get(key).fold[V](value) { 47 | wipValue => valueGroup.binOp(value)(wipValue) 48 | } 49 | if (newValue == valueGroup.neutral) 50 | wipMap - key 51 | else 52 | wipMap.updated(key, newValue) 53 | } 54 | }} 55 | 56 | val inv: (=>T) => T = _.map { case (key, value) => 57 | (key, valueGroup.inv(value)) 58 | } 59 | 60 | val neutral: T = AbelianMap.empty 61 | 62 | def isEqualGroup(that: AbelianGroup[T]): Boolean = that match { 63 | case that @ LiftedMapGroup(_) => 64 | this.valueGroup isEqualGroup that.valueGroup 65 | 66 | case _ => 67 | false 68 | } 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /src/main/scala/ilc/feature/functions/Pretty.scala: -------------------------------------------------------------------------------- 1 | package ilc 2 | package feature 3 | package functions 4 | 5 | /** 6 | * Pretty printing for first-class functions. 7 | */ 8 | 9 | import org.kiama.output._ 10 | 11 | trait Pretty extends base.Pretty with Syntax { 12 | override def operatorPrecedence(tau: Type): Int = tau match { 13 | case domain =>: range => 14 | 5 15 | // like in ILC's Agda code base: Parametric.Syntax.Type: 16 | // infixr 5 _⇒_ 17 | // 18 | // Ref. 19 | // http://www.informatik.uni-marburg.de/~pgiarrusso/ILC/AEC/agda/Parametric.Syntax.Type.html 20 | 21 | case _ => 22 | super.operatorPrecedence(tau) 23 | } 24 | 25 | override def operatorPrecedence(t: Term): Int = t match { 26 | case Abs(_, _) => 27 | 0 // looser than everything 28 | 29 | case App(_, _) => 30 | 10 // tighter than Haskell's default operator precedence (9) 31 | 32 | case _ => 33 | super.operatorPrecedence(t) 34 | } 35 | 36 | override def toPrettyExpression(tau: Type): PrettyExpression = tau match { 37 | case domain =>: range => 38 | new PrettyBinaryExpression { 39 | def priority = operatorPrecedence(tau) 40 | def fixity = Infix(RightAssoc) 41 | 42 | def left = toPrettyExpression(domain) 43 | def right = toPrettyExpression(range) 44 | 45 | def op = UnicodeOutput.choose("→", "->") 46 | } 47 | 48 | case _ => 49 | super.toPrettyExpression(tau) 50 | } 51 | 52 | override def toPrettyExpression(t: Term): PrettyExpression = t match { 53 | case Abs(Var(x, xType), body) => 54 | new PrettyEnclosingExpression { 55 | def priority = operatorPrecedence(t) 56 | def fixity = Prefix 57 | 58 | def op = text("λ") <> text(x.toString) <> text(".") 59 | def exp = toPrettyExpression(body) 60 | } 61 | 62 | case App(operator, operand) => 63 | new PrettyJuxtaposedExpression { 64 | // application binds tighter than everything 65 | def priority = operatorPrecedence(t) 66 | def fixity = Infix(LeftAssoc) 67 | 68 | def left = toPrettyExpression(operator) 69 | def right = toPrettyExpression(operand) 70 | } 71 | 72 | case _ => 73 | super.toPrettyExpression(t) 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /src/main/scala/ilc/feature/abelianMaps/Syntax.scala: -------------------------------------------------------------------------------- 1 | package ilc 2 | package feature 3 | package abelianMaps 4 | 5 | trait Syntax 6 | extends maps.Syntax 7 | with abelianGroups.Types 8 | { 9 | // intro/elim forms of maps with abelian groups on values 10 | // 11 | // (new constructors) 12 | // 13 | // singleton : k → v → Map k v 14 | // 15 | // liftGroup : AbelianGroup v → AbelianGroup (Map k v) 16 | // 17 | // foldByHom : AbelianGroup a → AbelianGroup b → 18 | // (k → a → b) → Map k a → b 19 | // 20 | // (inherited from feature.maps) 21 | // 22 | // empty : Map k v 23 | // delete : k → Map k v → Map k v 24 | // lookup : k → Map k v → Maybe v 25 | 26 | object SingletonMap extends ConstantWith2TypeParameters { 27 | val typeConstructor = TypeConstructor("keyType", "valType") { 28 | case Seq(keyType, valType) => 29 | keyType =>: valType =>: MapType(keyType, valType) 30 | } 31 | } 32 | 33 | object LiftGroup extends ConstantWith2TypeParameters { 34 | val typeConstructor = TypeConstructor("keyType", "valType") { 35 | case Seq(keyType, valType) => 36 | AbelianGroupType(valType) =>: 37 | AbelianGroupType(MapType(keyType, valType)) 38 | } 39 | 40 | /** Give only the key type of LiftGroup, deduce valType from 41 | * argument 42 | * {{{ 43 | * (LiftGroup(Word) ! additiveGroup).getType = 44 | * AbelianGroupType(MapType(Word, IntType)) 45 | * }}} 46 | * Although this pattern of partially specifying type 47 | * arguments might be abstracted in traits, it's probably 48 | * more productive to simply code unification (in Haskell, 49 | * it's just a couple dozen lines). 50 | */ 51 | def tapply(keyType: Type): PolymorphicTerm = new PolymorphicTerm { 52 | def specialize(argumentTypes: Type*): Term = { 53 | val Seq(valType) = 54 | TypeConstructor("v")(AbelianGroupType.apply _). 55 | inverse(argumentTypes.head) 56 | LiftGroup.tapply(keyType, valType) 57 | } 58 | } 59 | } 60 | 61 | object FoldByHom extends ConstantWith3TypeParameters { 62 | val typeConstructor = TypeConstructor("k", "a", "b") { 63 | case Seq(k, a, b) => 64 | AbelianGroupType(a) =>: AbelianGroupType(b) =>: 65 | (k =>: a =>: b) =>: MapType(k, a) =>: b 66 | } 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /clients/src/main/scala/ilc/examples/MapReduce.scala: -------------------------------------------------------------------------------- 1 | package ilc 2 | package examples 3 | 4 | import feature._ 5 | 6 | trait MapReduce 7 | extends products.Derivation 8 | 9 | with abelianMaps.AbelianDerivation 10 | with abelianMaps.ToScala 11 | 12 | with products.Syntax 13 | with bags.AbelianDerivation 14 | with bags.ToScala 15 | 16 | with products.ToScala 17 | 18 | with booleans.ToScala 19 | with sums.ToScala 20 | with GroupBy 21 | { 22 | /** {{{ 23 | * mapPerKey : ∀ {k₁ v₁ k₂ v₂} → 24 | * AbelianGroup v₁ → 25 | * (k₁ → v₁ → Bag (k₂ × v₂)) → 26 | * Map k₁ v₁ → Bag (k₂ × v₂) 27 | * }}} 28 | * 29 | * Limitation: userMap partially applied to every key 30 | * must be a homomorphism. (Visible in fig. 5 & 6, mentioned 31 | * briefly in §4.4, the last paragraph on page 7). 32 | */ 33 | val mapPerKey: UntypedTerm = 34 | 'v1Group ->: 'userMap ->: FoldByHom('v1Group, FreeAbelianGroup, 'userMap) 35 | 36 | /** {{{ 37 | * group-by-key : ∀ {k₂ v₂} → 38 | * Bag (k₂ × v₂) → Map k₂ (Bag v₂) 39 | * }}} 40 | */ 41 | val groupByKey: UntypedTerm = 42 | FoldGroup( 43 | LiftGroup(FreeAbelianGroup), 44 | 'k2v2Pair ->: SingletonMap(Proj1('k2v2Pair), Singleton(Proj2('k2v2Pair))) 45 | ) 46 | 47 | /** Output of userReduce is v₃, but it corresponds to (Maybe v₃). 48 | * The hidden `Nothing` constructor is the neutral element 49 | * specified by the first argument, the abelian group on v₃. 50 | * 51 | * {{{ 52 | * reduce-per-key : ∀ {k₂ v₂ v₃} → 53 | * AbelianGroup v₃ → 54 | * (k₂ → Bag v₂ → v₃) → 55 | * Map k₂ (Bag v₂) → 56 | * Map k₂ v₃ 57 | * }}} 58 | */ 59 | val reducePerKey: UntypedTerm = 60 | 'v3Group ->: 'userReduce ->: 61 | FoldByHom( 62 | FreeAbelianGroup, 63 | LiftGroup('v3Group), 64 | 'key ->: 'bag ->: SingletonMap('key, 'userReduce('key, 'bag))) 65 | 66 | /** {{{ 67 | * mapReduce : ∀ {k₁ v₁ k₂ v₂} → 68 | * AbelianGroup v₁ → 69 | * AbelianGroup v₃ → 70 | * (k₁ → v₁ → Bag (k₂ × v₂)) → 71 | * (k₂ → Bag v₂ → v₃) → 72 | * Map k₁ v₁ → 73 | * Map k₂ v₃ 74 | * }}} 75 | */ 76 | val mapReduce: UntypedTerm = 77 | 'v1Group ->: 'v3Group ->: 'userMap ->: 'userReduce ->: 78 | (reducePerKey('v3Group, 'userReduce) composeWith groupByKey composeWith mapPerKey('v1Group, 'userMap)) 79 | } 80 | -------------------------------------------------------------------------------- /clients/src/test/scala/longRunning/BagUnionBenchmark.scala: -------------------------------------------------------------------------------- 1 | package longRunning 2 | 3 | import org.scalameter.api._ 4 | import ilc.feature.abelianGroups.Library._ 5 | import ilc.feature.bags.Library._ 6 | import ilc.feature.bags.BagChanges 7 | import ilc.examples.BagUnionGenerated 8 | import ilc.examples.BenchData 9 | import ilc.examples.BenchmarkVerification 10 | import ilc.examples.ExampleGenerated 11 | import ilc.examples.NonReplacementChangeBenchmark 12 | 13 | /** 14 | * Benchmark bag union. 15 | */ 16 | class BagUnionBenchmark extends NonReplacementChangeBenchmark( 17 | new BagPairBenchData(BagUnionGenerated) { 18 | override def base = 5000 19 | override def last = 25000 20 | override def step = 5000 21 | }) 22 | 23 | class BagPairBenchData(val example: ExampleGenerated { 24 | type InputType = (OutputType, OutputType) 25 | type DeltaInputType = (DeltaOutputType, DeltaOutputType) 26 | 27 | type OutputType = Bag[Int] 28 | type DeltaOutputType = 29 | Either[(AbelianGroup[Bag[Int]], Bag[Int]), Bag[Int]] 30 | }) extends BenchData with BagChanges 31 | { 32 | import example._ 33 | 34 | type BagType = OutputType 35 | type DeltaBagType = ChangeToBags[Int] 36 | 37 | // consider leaving out the output. 38 | def inputOfSize(n: Int): Data = { 39 | def mkBag(first: Int, last: Int): BagType = 40 | (first to last).map(i => (i -> 1))(collection.breakOut) 41 | (mkBag(1, n), mkBag(-n + n/2, -1 + n/2)) 42 | } 43 | 44 | def changes: Map[String, Int => DeltaBagType] = changesToBagsOfIntegers 45 | 46 | def pack(description1: String, description2: String): String = 47 | s"($description1, $description2)" 48 | 49 | def unpack(jointDescription: String): (String, String) = { 50 | val Array(x, y) = jointDescription.split("(, )|[()]").tail 51 | (x, y) 52 | } 53 | 54 | lazy val changeDescriptions: Gen[String] = { 55 | val descriptions = changes.keySet.toSeq 56 | val jointDescriptions: Seq[String] = for { 57 | change1 <- descriptions 58 | change2 <- descriptions 59 | } yield pack(change1, change2) 60 | Gen.enumeration("change")(jointDescriptions: _*) 61 | } 62 | 63 | def lookupChange(jointDescription: String, inputSize: Int, input: InputType, output: OutputType): Change = { 64 | val (description1, description2) = unpack(jointDescription) 65 | ( changes(description1)(input._1.size), 66 | changes(description2)(input._2.size) ) 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /src/main/scala/ilc/feature/sums/ReplacementValuesDerivation.scala: -------------------------------------------------------------------------------- 1 | package ilc 2 | package feature 3 | package sums 4 | 5 | /** Δ(a ⊎ b) = (Δa ⊎ Δb) ⊎ (a ⊎ b) */ 6 | trait ReplacementValuesDerivation 7 | extends base.Derivation 8 | with Syntax 9 | with SyntaxSugar 10 | { 11 | override def deltaType(tau: Type): Type = tau match { 12 | case SumType(a, b) => 13 | SumType(SumType(deltaType(a), deltaType(b)), tau) 14 | 15 | case _ => 16 | super.deltaType(tau) 17 | } 18 | 19 | override def updateTerm(tau: Type): Term = tau match { 20 | case SumType(a, b) => 21 | lambda(deltaType(tau), tau) { case Seq(deltaSum, oldSum) => 22 | case2(deltaSum, 23 | lambda(SumType(deltaType(a), deltaType(b))) { surgery => 24 | case4(surgery, oldSum, 25 | lambda(deltaType(a), a) { case Seq(dx, x) => 26 | Inj1.tapply(b) ! (updateTerm(a) ! dx ! x) 27 | }, 28 | // TODO: Introduce Error term and use it when 29 | // invalid changes are detected. 30 | lambda(deltaType(a), b) { case _ => oldSum }, // invalid 31 | lambda(deltaType(b), a) { case _ => oldSum }, // invalid 32 | lambda(deltaType(b), b) { case Seq(dy, y) => 33 | Inj2.tapply(a) ! (updateTerm(b) ! dy ! y) 34 | }) 35 | }, 36 | lambda(tau) { replacement => replacement } 37 | ) 38 | } 39 | 40 | case _ => 41 | super.updateTerm(tau) 42 | } 43 | 44 | override def diffTerm(tau: Type): Term = tau match { 45 | case SumType(a, b) => 46 | lambda(tau, tau) { case Seq(newSum, oldSum) => 47 | Inj2.tapply(SumType(deltaType(a), deltaType(b))) ! newSum 48 | } 49 | 50 | case _ => 51 | super.diffTerm(tau) 52 | } 53 | 54 | override def derive(t: Term): Term = t match { 55 | case Inj1(leftType, rightType) => 56 | lambda(leftType, deltaType(leftType)) { case Seq(x, dx) => 57 | Inj1.tapply(SumType(leftType, rightType)) ! 58 | (Inj1.tapply(deltaType(rightType)) ! dx) 59 | } 60 | 61 | case Inj2(leftType, rightType) => 62 | lambda(rightType, deltaType(rightType)) { case Seq(y, dy) => 63 | Inj1.tapply(SumType(leftType, rightType)) ! 64 | (Inj1.tapply(deltaType(leftType)) ! dy) 65 | } 66 | 67 | // Either has a slow derivative 68 | case _ => 69 | super.derive(t) 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /src/main/scala/ilc/feature/base/Evaluation.scala: -------------------------------------------------------------------------------- 1 | package ilc 2 | package feature 3 | package base 4 | 5 | import scala.collection.immutable 6 | 7 | trait Evaluation extends Syntax { 8 | type Env = immutable.Map[Name, Value] 9 | 10 | //Core of the evaluation function, to be extended by subclasses. 11 | //Recursive calls should be done only through wrapEval. 12 | def coreEval(t: Term, env: Env): Value = 13 | t match { 14 | case variable: Var => env(variable.getName) 15 | case _ => sys error s"cannot evaluate $t" 16 | } 17 | 18 | /** Evaluate a Term, returning a Value or throwing an exception (for instance, 19 | * an UnexpectedTypeException). 20 | */ 21 | def eval(t: Term): Value = 22 | try { 23 | wrapEval(t, immutable.Map.empty) 24 | } catch { case UnexpectedTypeException(info: OuterTypeExceptionInfo, cause) => 25 | throw UnexpectedTypeException(info.copy(term = Some(t)), cause) 26 | } 27 | 28 | //Record the evaluated subterm 29 | def wrapEval(t: Term, env: Env): Value = 30 | try { 31 | coreEval(t, env) 32 | } catch { case ute @ UnexpectedTypeException(InnerTypeExceptionInfo(msg), _) => 33 | throw new UnexpectedTypeException(OuterTypeExceptionInfo(msg, t), ute) 34 | } 35 | 36 | // Data is not put directly in the exception, but in nested case classes, 37 | // because exceptions seem to implement toString in a fixed way (which makes 38 | // sense) - or they just don't use toString but getMessage in the output, more 39 | // likely. 40 | case class UnexpectedTypeException(data: TypeExceptionInfo, cause: Throwable = null) 41 | extends Exception(data.toString, cause) 42 | 43 | trait TypeExceptionInfo 44 | case class InnerTypeExceptionInfo(message: String) extends TypeExceptionInfo 45 | case class OuterTypeExceptionInfo(message: String, subterm: Term, term: Option[Term] = None) extends TypeExceptionInfo { 46 | override def toString = { 47 | val subtermStr = showTerm(subterm) 48 | val optTermStr = term map { t => 49 | "\n>> Term: " + showTerm(t) 50 | } getOrElse "" 51 | s"${message}\n>> Subterm: ${subtermStr}${optTermStr}" 52 | } 53 | } 54 | 55 | trait Value { 56 | def die(from: String, arg: Any = ""): Nothing = 57 | throw UnexpectedTypeException( 58 | InnerTypeExceptionInfo(this.toString ++ 59 | "." ++ from ++ 60 | (if (arg.toString == "") 61 | "" 62 | else 63 | "(" ++ arg.toString ++ ")"))) 64 | } 65 | } 66 | --------------------------------------------------------------------------------