Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
56 commits
Select commit Hold shift + click to select a range
1ad550a
Update dependencies (remove some breakages)
JanBessai Apr 23, 2025
82396e6
Cleanup toward Scala 3
JanBessai May 14, 2025
4453c4a
Cleanup until Java Main
JanBessai May 21, 2025
fd84c74
Most Scala 3 translation fixed up to language-java.
JanBessai May 21, 2025
b64ee69
Solve some lazy val inheritance issues in Scala3 with case classes
JanBessai May 28, 2025
decc921
Fix java code generator for Scala 3
JanBessai Jun 18, 2025
360c55b
Fix Scala 3 for the scala generator
JanBessai Jun 18, 2025
39729fa
Refactor core to extract cogen
JanBessai Jun 25, 2025
a5006c7
Factor out approaches into subproject and refactor domains
JanBessai Jul 9, 2025
5246ee0
Refactored/fixed language-java for cogen
JanBessai Jul 9, 2025
e2a2c3c
Refactored/fixed language inbetween
JanBessai Jul 9, 2025
899c9b1
Refactor/fix language newScala for cogen
JanBessai Jul 9, 2025
7347c6d
Fix coco and java lists after refactor
JanBessai Jul 16, 2025
4bb2a4c
Updated helloWorld so it compiles properly and cleaned up lots of Sca…
heineman Jul 17, 2025
36a3c2a
Continuing clean-up of code
heineman Jul 21, 2025
8bff401
Updated documentation
heineman Jul 30, 2025
b059e50
Toward moving Trees into builder:
JanBessai Aug 27, 2025
4b41711
Toward refactoring for seperate domain type extensions (e.g., here: T…
JanBessai Sep 26, 2025
531d5e4
Refactored Inbetween up to generics and ffi
JanBessai Sep 26, 2025
3b8d392
Refactor inbetween Generics
JanBessai Sep 26, 2025
3d6e0ad
inbetween FFI refactored
JanBessai Oct 15, 2025
c90bc6e
Initial refactor of newScala to improved CoCo pattern
JanBessai Oct 15, 2025
c414e49
Initial work on finalized layer for ScalaBaseAST
JanBessai Oct 22, 2025
fda705f
Initial work on FFIASTs for scala
JanBessai Oct 22, 2025
6a84b2d
Some more finalized new Scala factories, including a NameProvider
JanBessai Oct 29, 2025
4dc49b0
Completed rest, but two compilation errors remain.
heineman Oct 31, 2025
6190417
Some progress on scalaBase FinalFactories
JanBessai Nov 5, 2025
136992c
Patched up as many as I could, with a few remaining.
heineman Nov 6, 2025
d0b4873
Minor fixes
heineman Nov 6, 2025
ccb6335
Most scala FFIs in new style
JanBessai Nov 12, 2025
2046448
starting process of upgrading code
heineman Nov 13, 2025
b1b7f77
Finish scala ASTs
JanBessai Nov 19, 2025
0272eec
Scala code generator overhaul done
JanBessai Nov 19, 2025
f6100d2
Merge branch 'inbetween_cleanup' of https://github.com/combinators/ex…
heineman Nov 25, 2025
ce8a346
I was able to generate HelloWorld and Fibonacci Scala code using the …
heineman Nov 25, 2025
380f109
Got EpCoGen to run (without Trees in Scala).
heineman Nov 28, 2025
e7b6bb6
Add missing genericsFactory in newScala BaseAST
JanBessai Dec 3, 2025
05f47b0
Add missing FinalNameProviderAST in newScala
JanBessai Dec 3, 2025
989fd58
Remove treeLibrary from CodeGenerator2 in newScala
JanBessai Dec 3, 2025
23cfaf9
Some progress on Trees in builder.
JanBessai Dec 3, 2025
38825ea
Removing un-matched trailing */
heineman Dec 8, 2025
89ca200
Add capability to include custom files in Project and fix Scala build…
JanBessai Dec 10, 2025
cb198fe
Moved a fibonacci Main to the builder.
heineman Dec 11, 2025
6ba199c
Fix missing typeLookupMap copy and override vals
JanBessai Dec 17, 2025
2f5bc3f
Merge branch 'inbetween_cleanup' of github.com:combinators/expression…
JanBessai Dec 17, 2025
75e88da
Bump scala version
JanBessai Dec 17, 2025
7eb5dec
Fixup trait logic
JanBessai Dec 17, 2025
0e4cf94
Fix missing type registration in classes and constructors
JanBessai Jan 14, 2026
4a9eb77
Updated and prepared for removing 2s from classes
heineman Jan 14, 2026
df4844f
Remove old-style inbetween/newScala files
JanBessai Jan 14, 2026
14521cd
Rename XXX2 created in newstyle refactor to XXX
JanBessai Jan 14, 2026
6e79165
Fixup helloworld examples, especially fix missing FinalTypes in newScala
JanBessai Jan 14, 2026
12cda7d
Add ci-pipeline publishing for cogen
JanBessai Feb 4, 2026
8b162a4
Merge branch 'main' into inbetween_cleanup
JanBessai Feb 4, 2026
3ae1bd4
Fix sbt version and publish only for cogen
JanBessai Feb 4, 2026
14ebe5b
Merge branch 'inbetween_cleanup' of github.com:combinators/expression…
JanBessai Feb 4, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
46 changes: 46 additions & 0 deletions .github/workflows/test_and_release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
name: Test code, update coverage, and release main branch

on: [ push, pull_request ]

jobs:
release:
runs-on: ubuntu-latest
needs: [ ]
steps:
- uses: actions/checkout@v4.2.2
- name: Check if we are head of main
id: check_head_of_main
run: |
git fetch origin main &&
MAIN=`git rev-parse origin/main` &&
echo "::set-output name=head_of_main::$MAIN" &&
CURRENT=`git rev-list -n 1 ${{ github.ref }} || echo "NOT_MAIN"` &&
echo "::set-output name=current_job_ref::$CURRENT"
- name: Set up JDK
if: steps.check_head_of_main.outputs.head_of_main == steps.check_head_of_main.outputs.current_job_ref
uses: actions/setup-java@v4
with:
distribution: temurin
java-version: 21
- name: Set up SBT
if: steps.check_head_of_master.outputs.head_of_main == steps.check_head_of_main.outputs.current_job_ref
uses: sbt/setup-sbt@v1.1.16
with:
sbt-runner-version: 1.10.11
- name: Build and release
if: steps.check_head_of_master.outputs.head_of_main == steps.check_head_of_main.outputs.current_job_ref
env:
SONATYPE_USERNAME: ${{ secrets.SONATYPE_USERNAME }}
SONATYPE_PASSWORD: ${{ secrets.SONATYPE_PASSWORD }}
SONA_USER: ${{ secrets.SONATYPE_USERNAME }}
SONA_PASS: ${{ secrets.SONATYPE_PASSWORD }}
PGP_SECRET: ${{ secrets.PGP_SECRET }}
PGP_PASSPHRASE: ${{ secrets.PGP_PASSPHRASE }}
PGP_KEY_HEX: ${{ secrets.PGP_KEY_HEX }}
CI: github
run: |
git fetch --prune --unshallow --tags &&
export GPG_TTY=$(tty) &&
echo $PGP_SECRET | base64 -d | gpg --passphrase=$PGP_PASSPHRASE --yes --batch --pinentry-mode loopback --import &&
export PATH=`pwd`/.github/bin:$PATH &&
sbt cogen/ciReleaseTagNextVersion cogen/ciReleaseSonatype
2 changes: 2 additions & 0 deletions January-2026-ToDo.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
1. Visitor / M5 yields "key not found: Tree"

8 changes: 4 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -313,20 +313,20 @@ family and are created using this same initial execution.

# Dependencies

EPCoGen is compiled with Scala version 2.12.17.
EPCoGen is compiled with Scala 3.6.4 version.

All examples validated on Linux and Windows, using [sbt 1.7.2](https://www.scala-sbt.org/download#previous-releases)
All examples validated on Linux and Windows, using [sbt 1.10.11](https://www.scala-sbt.org/download#previous-releases)
, Scala version 2.12.17 and [Java 11 (OpenJDK)](https://openjdk.org/projects/jdk/11/).

Java code is generated using [JavaParser](https://github.com/javaparser/javaparser) version 3.19.0.
Java code is generated using [JavaParser](https://github.com/javaparser/javaparser) version 3.26.4.

All JUnit tests are compiled using [JUnit 4.12](https://junit.org/junit4/) and Java compiler is directed to
compile only assuming Java 1.8 compatibility.

The images above were generated using [GraphViz online](https://dreampuf.github.io/GraphvizOnline) based on
output generated by our [GraphViz](core/src/main/scala/org/combinators/ep/domain/GraphViz.scala) code.

All generated Scala code is compiled using Scala version 3.3.3 and version
All generated Scala code is compiled using Scala version 3.6.4 and version
3.2.19 of the [Scalactic testing framework](https://www.scalatest.org/)

Python scripts require Python 3. Only the `scipy` package needs to be installed.
Expand Down
125 changes: 98 additions & 27 deletions TECHNICAL.txt
Original file line number Diff line number Diff line change
@@ -1,64 +1,135 @@
Codebase validated to work with

scalaVersion := "3.6.4"

When you have a fresh clone of the git repository, enter the command `sbt compile` in the
top-level directory to compile EpCoGen. sbt will properly install all necessary software
libraries.

We have tested with sbt versions of 1.7.1, 1.9.8, and 1.10.7.
We have tested with sbt versions of 1.7.1, 1.9.8, 1.10.7, and 1.10.11.

We have tested with Java JDK 18.

If your Java version is too advanced for the sbt, you may need to add the
following to the build.st file to lower the associated Java version.

javacOptions ++= Seq("-source", "11", "-target", "11"),
javacOptions ++= Seq("-source", "11", "-target", "11")

And then you can install an openJDK version of Java 11 and use it by setting JAVA_HOME
properly and/or configuring PATH variable appropriately.
properly and/or configuring PATH variable appropriately. Grab the appropriate JDK 11
from https://www.openlogic.com/openjdk-downloads

===================================
Examples to run
===================================

Sample commands to run. The first column shows the time it was launched, so to generate all
evolution stages for all approaches on a Linux machine took just under an hour. And the
compiling process for all code took just about three hours.

15:17 sbt language-java/run
16:08 cd target/
16:09 cd ep-java
16:11 python3 ../../scripts/compare.py ../../scripts/systems/system-main.json >> REPORT
16:12 python3 ../../scripts/compile.py
19:08 python3 ../../scripts/process.py > STATISTICS
19:09 cd ..
19:11 sbt "helloWorld/runMain org.combinators.fibonacci.FibonacciWithLucasScalaDirectToDiskMain"
19:13 cd target/fib
19:13 cd scala/
19:13 sbt test
Sample commands to run. The first column shows the time to execute, so to generate all
evolution stages for all approaches on a Linux machine took 22 minutes. The greatest
cost is compiling everything. To compile the main example took about 2 hours since there
are 27 evolutionary stages in the example.

Time Command
00:02 sbt compile
00:22 sbt language-java/run

cd target/ep-java
00:01 python3 ../../scripts/compare.py ../../scripts/systems/system-main.json >> REPORT
02:10 python3 ../../scripts/compile.py

00:01 python3 ../../scripts/process.py > STATISTICS
cd ../..

---------------------------------
Java Generation
---------------------------------

The above generates all subdirectories that can now be compiled:

* D1D2 (to just generated this, execute `sbt "language-java/runMain org.combinators.ep.language.java.GenerateAllD1D2"`)

cd target/ep-java-d1d2
python3 ../../scripts/compare.py ../../scripts/systems/system-d1d2.json >> REPORT
python3 ../../scripts/compile.py
python3 ../../scripts/process.py > STATISTICS

* Main (to just generated this, execute `sbt "language-java/runMain org.combinators.ep.language.java.GenerateAllMain"`)

cd target/ep-java
python3 ../../scripts/compare.py ../../scripts/systems/system-main.json >> REPORT
python3 ../../scripts/compile.py
python3 ../../scripts/process.py > STATISTICS

* Merging (to just generated this, execute `sbt "language-java/runMain org.combinators.ep.language.java.GenerateAllMerging"`)

cd target/ep-java-merging
python3 ../../scripts/compare.py ../../scripts/systems/system-merging.json >> REPORT
python3 ../../scripts/compile.py
python3 ../../scripts/process.py > STATISTICS

* Extended (to just generated this, execute `sbt "language-java/runMain org.combinators.ep.language.java.GenerateAllExtended"`)

cd target/ep-java-extended
python3 ../../scripts/compare.py ../../scripts/systems/system-extended.json >> REPORT
python3 ../../scripts/compile.py
python3 ../../scripts/process.py > STATISTICS

* Third Alternate (to just generated this, execute `sbt "language-java/runMain org.combinators.ep.language.java.GenerateAllThirdAlternate"`)

cd target/ep-java-third-alternate
python3 ../../scripts/compare.py ../../scripts/systems/system-third-alternate.json >> REPORT
python3 ../../scripts/compile.py
python3 ../../scripts/process.py > STATISTICS

* System J (to just generated this, execute `sbt "language-java/runMain org.combinators.ep.language.java.GenerateAllJ"`)

cd target/ep-java-j
python3 ../../scripts/compare.py ../../scripts/systems/system-j.json >> REPORT
python3 ../../scripts/compile.py
python3 ../../scripts/process.py > STATISTICS


When generating the full Java implementations for all approaches and all evolution stages,
there are 19,842 Java files containing 513,094 total lines of code.
there are 17,469 Java files containing 432,215 total lines of code.

Using the CLOC utility, a more accurate picture appears. Of these only 3890 are truly
unique files. The reason? Because the Expression Problem shows how to extend a system without
modifying already created code, so it makes perfect sense that 80% of the code is duplicated!

% $CLOC --csv `find . -name "*.java"`

19842 text files.
3890 unique files.
15952 files ignored.
17429 text files.
3279 unique files.
14150 files ignored.

files,language,blank,comment,code,"github.com/AlDanial/cloc v 1.86 T=17.31 s (224.7 files/s, 7917.7 lines/s)"
3890,Java,27755,4,109292
3890,SUM,27755,4,109292
files,language,blank,comment,code,"github.com/AlDanial/cloc v 1.86 T=21.67 s (151.3 files/s, 4895.7 lines/s)"
3279,Java,22368,4,83739
3279,SUM,22368,4,83739

cloc reports 109,292 lines of code.
cloc reports 83,739 lines of code.

Now run and turn off the uniqueness check:

% $CLOC --csv `find . -name "*.java"` --skip-uniqueness

19842,Java,117328,516,395250
17429,Java,101632,420,327068

This reveals that 395,250 lines of Java code were generated.

---------------------------------
ScalaGeneration
---------------------------------
sbt "language-newScala/runMain org.combinators.ep.language.scala.codegen.GenerateAllJ"
sbt "language-newScala/runMain org.combinators.ep.language.scala.codegen.GenerateAllD1D2"
sbt "language-newScala/runMain org.combinators.ep.language.scala.codegen.GenerateAllMain"
sbt "language-newScala/runMain org.combinators.ep.language.scala.codegen.GenerateAllMerging"
sbt "language-newScala/runMain org.combinators.ep.language.scala.codegen.GenerateAllExtended"

% $CLOC --csv `find ep-scala -name "*.scala"` `find ep-scala-d1d2 -name "*.scala"` `find ep-scala-extended -name "*.scala"` `find ep-scala-j -name "*.scala"` `find ep-scala-merging -name "*.scala"`

16770 text files.
12217 unique files.
4553 files ignored.

files,language,blank,comment,code,"github.com/AlDanial/cloc v 1.86 T=31.21 s (391.5 files/s, 14591.1 lines/s)"
12217,Scala,242004,0,213356
12217,SUM,242004,0,213356
27 changes: 2 additions & 25 deletions TODO.txt
Original file line number Diff line number Diff line change
@@ -1,40 +1,17 @@
1. Generated test cases often exceed length allowed to method declarations.
Generated test cases often exceed length allowed to method declarations.

a. As a stop-gap measure, these methods are arbitrarily split up, which means that the
visitorSideEffect has test cases that do not compile because of variables defined for
the visitor in one method but used in the other method.

2. Functional -- continue progress from before

3.
Functional -- continue progress from before

Scala-InBetween could add 'trait' capability and that could then
be exposed as an option. This is high-level change and not
object-oriented per se.

Add exceptions to newScala

Make CoGen stand alone

Note that Java equality in methods uses the .equals() which is a problem with primitive types. Thoughts?

Trees that grow

============================

Notes: A1M3 for coco needs a full Exp and Factory. Why? Because otherwise merge accessing finalized instances from A1 are incompatible with
the finalized instances from M3. Similar argument for M3W1 and even A1M3I2

ifEqExpr <- ffiEquals.equalityCapabilities.areEqual(expType, expValue, zero)
where expType is expType <- toTargetLanguageType(TypeRep.Double)

It wasn't working because I had mistakenly used a type for "ep.Exp[FT]"

ALgebra had to go back to its own "dataTypeCasesWithNewOperations" for some reason.

So Coco, Trivially and Algebra all have slightly different results and it REALLY makes me curious
as to why

============================

Runtime Dispatch FAILS whenever pass in a model. if we can fix this, then I can better integrate with OO code.
Original file line number Diff line number Diff line change
@@ -1,15 +1,17 @@
package org.combinators.ep.approach.functional /*DI:LI:AD*/

import org.combinators.ep.domain.GenericModel
import org.combinators.ep.generator.{AbstractSyntax, ApproachImplementationProvider, Command, EvolutionImplementationProvider, NameProvider, Understands}
import org.combinators.ep.generator.paradigm.control.{ConstructorPattern, Functional => FunControl}
import Command.{Generator, _}
import org.combinators.ep.generator.{ApproachImplementationProvider, EvolutionImplementationProvider}
import org.combinators.cogen.{AbstractSyntax, Command, NameProvider, TypeRep, Understands}
import Command.Generator
import cats.implicits._
import org.combinators.ep.domain.abstractions.{DataType, DataTypeCase, Operation, Parameter, TypeRep}
import org.combinators.cogen.paradigm.{AnyParadigm, FindType, Functional}
import org.combinators.ep.domain.abstractions.{DataType, DataTypeCase, DomainTpeRep, Operation, Parameter}
import org.combinators.ep.generator.communication.{ReceivedRequest, Request, SendRequest}
import org.combinators.ep.generator.paradigm.{AnyParadigm, FindType, Functional}
import AnyParadigm.syntax._
import org.combinators.ep.generator.paradigm.control.Functional.WithBase
import org.combinators.cogen.paradigm.control.{ConstructorPattern, Functional as FunControl}
import org.combinators.cogen.paradigm.control.Functional.WithBase
import org.combinators.ep.domain.extensions._

trait Traditional extends ApproachImplementationProvider {
val functional: Functional.WithBase[paradigm.type]
Expand All @@ -34,7 +36,7 @@ trait Traditional extends ApproachImplementationProvider {
import paradigm.methodBodyCapabilities._
import functional.methodBodyCapabilities._
for {
rt <- toTargetLanguageType(TypeRep.DataType(baseTpe))
rt <- toTargetLanguageType(DomainTpeRep.DataType(baseTpe))
_ <- resolveAndAddImport(rt)
res <- instantiateType(rt, names.mangle(names.conceptNameOf(tpeCase)), args)
} yield res
Expand Down Expand Up @@ -104,7 +106,7 @@ trait Traditional extends ApproachImplementationProvider {
}

for {
params <- forEach (Parameter(names.instanceNameOf(tpe), TypeRep.DataType(tpe)) +: op.parameters) { param: Parameter =>
params <- forEach (Parameter(names.instanceNameOf(tpe), DomainTpeRep.DataType(tpe)) +: op.parameters) { (param: Parameter) =>
for {
pt <- toTargetLanguageType(param.tpe)
_ <- resolveAndAddImport(pt)
Expand All @@ -116,7 +118,7 @@ trait Traditional extends ApproachImplementationProvider {
_ <- resolveAndAddImport(returnType)
_ <- setReturnType(returnType)
args <- getArguments()
onTpe <- toTargetLanguageType(TypeRep.DataType(tpe))
onTpe <- toTargetLanguageType(DomainTpeRep.DataType(tpe))
result <- {
val matchGen = makeCases(tpe, cases, op, args.head._3, args.tail, domainSpecific)(_, _)
patternMatch(
Expand Down Expand Up @@ -153,7 +155,7 @@ trait Traditional extends ApproachImplementationProvider {
import functional.projectCapabilities._
import functional.methodBodyCapabilities._ // Needed below
import functional.typeCapabilities._ // Needed below
val dtpeRep = TypeRep.DataType(domain.baseDataType)
val dtpeRep = DomainTpeRep.DataType(domain.baseDataType)
for {
_ <- addTypeLookupForMethods(dtpeRep, domainTypeLookup(domain.baseDataType))
_ <- addTypeLookupForTypes(dtpeRep, domainTypeLookup(domain.baseDataType))
Expand Down Expand Up @@ -182,11 +184,13 @@ object Traditional {
(base: P)
(nameProvider: NameProvider[base.syntax.Name],
fun: Functional.WithBase[base.type],
funControl: FunControl.WithBase[base.MethodBodyContext, base.type]): Traditional.WithParadigm[base.type] =
new Traditional {
override val paradigm: base.type = base
override val names: NameProvider[base.syntax.Name] = nameProvider
override val functional: Functional.WithBase[paradigm.type] = fun
override val functionalControl: WithBase[paradigm.MethodBodyContext, paradigm.type] = funControl
}
funControl: FunControl.WithBase[base.MethodBodyContext, base.type]): Traditional.WithParadigm[base.type] = {
case class Trad(
override val paradigm: base.type,
override val names: NameProvider[base.syntax.Name])(
override val functional: Functional.WithBase[paradigm.type],
override val functionalControl: WithBase[paradigm.MethodBodyContext, paradigm.type]
) extends Traditional
Trad(base, nameProvider)(fun, funControl)
}
}
Loading
Loading