├── 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 |
--------------------------------------------------------------------------------