Skip to content

Commit 97f50e4

Browse files
committed
Minor tweaks
1 parent 8432f75 commit 97f50e4

File tree

9 files changed

+51
-12
lines changed

9 files changed

+51
-12
lines changed

README.md

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -375,5 +375,37 @@ This interpreter is also a little unique in that it's the only interpreter in Es
375375

376376
The reason I had to use mutable states to accomplish this is that I have to perform the evaluation in the same level as the rest of the expression in order to not blow up the call stack. If I simply made the result a `lazy val` that made a new call to `eval`, that would introduce a recursion that can't be optimized away with trampolining. The moment I figure out a way to do this without mutable state, though, I'm changing it.
377377

378+
### How the Interface Code Works
379+
You may notice odd-looking structures like this in the code for several interface handlers:
380+
```scala
381+
Trampoline.doOrElse(state){
382+
DoOrOp(args.get("s"), "Missing Source File", eio){src =>
383+
DoOrOp(getLang(args, "l", "s"), "Unrecognized Language or File Extension", eio){lang =>
384+
DoOrOp(findTranslator(state, lang, state.interpNames), "Language Not Recognized", eio){
385+
case (inam, t) =>
386+
DoOrErr(EsoFileReader.readFile(src, normLineFlag), eio){progRaw =>
387+
DoOrErr(t(progRaw), eio){prog =>
388+
TimeIt(state.interps(inam)(state.config)(prog)) match{
389+
case (i, dur) =>
390+
DoOrErr(i, eio){r =>
391+
DoOrErr(inputs, eio){inp =>
392+
TimeIt{tryAll{printer(olim(r(inp)))}} match{
393+
case (flg, rdr) => flg match{
394+
case Failure(e) =>
395+
if(timeFlg) eio.println(s"\nError: $e\nProgram failed in ${rdr}ms")
396+
else eio.println(s"\nError: $e")
397+
case Success(_) =>
398+
if(timeFlg) eio.println(s"\nProgram completed in ${rdr}ms")
399+
else eio.println()}}
400+
Done{state}}}}}}}}}}
401+
```
402+
These actually represent most of the core logic. Eso uses `Try` monads for most of its error handling, as well as `Option`s for a lot of faillable tasks, so if the code were to use a normal imperative or structural style there would be a lot of redundant code for checking whether or not something failed and breaking the flow.
403+
404+
These structures just nest each step into the previous one it's dependent on. You can see that the first layer tries to get the source file, then the second layer tries to find the language to use, and so-on. Each layer tries to do something, and either passes the result to the next layer or short-circuits with a failure. This works out to be quite an elegant way to represent multi-step IO interactions.
405+
406+
You'll also notice that the whole thing is wrapped in a `Trampoline` method. If you were to just arbitrarily nest things like this, you would be pointlessly eating up the call stack -so instead, each layer is actually a wrapper that returns the next layer. So, during execution it's actually going into a layer then coming out of that layer with the next one, only ever going down into one layer at a time. This is called trampolining, and it's a common way to optimize recursive structures that could otherwise blow up the call stack.
407+
408+
Of course, Eso wouldn't need special handling of trampolines if it had full TCE, but that's a matter for future JVM versions to address.
409+
378410
### Building
379411
I use [SBT assembly](https://github.com/sbt/sbt-assembly). This repo should give you everything you need to build it, just put it in a folder, run SBT from that directory, let it do its thing, then run assembly.

build.sbt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ lazy val root = (project in file("."))
88
name := "Eso",
99
logBuffered in Test := false,
1010
parallelExecution in Test := false,
11-
assemblyJarName in assembly := "Eso.jar",
11+
assemblyJarName in assembly := s"Eso-${version.value}.jar",
1212
scalacOptions ++= Seq("-deprecation", "-feature"),
1313
testOptions in Test += Tests.Argument(TestFrameworks.ScalaTest, "-fFW", "esoTestLog.txt", "-o"),
1414
resolvers ++= Seq(

esoTestLog.txt

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -150,6 +150,7 @@ Unlambda
150150
- should run hworld.unl correctly
151151
- should run alphatest.unl correctly
152152
- should run adventure.unl correctly
153+
- should run primes.unl correctly
153154
WSAssemblySpec:
154155
WSAssembly
155156
- should preserve the behavior of hworld.wsa
@@ -221,8 +222,8 @@ MetatapeSpec:
221222
Metatape
222223
- should run hworld.mt correctly
223224
- should run bitWidth.mt correctly
224-
Run completed in 1 minute, 51 seconds.
225-
Total number of tests run: 126
225+
Run completed in 1 minute, 37 seconds.
226+
Total number of tests run: 127
226227
Suites: completed 46, aborted 0
227-
Tests: succeeded 126, failed 0, canceled 0, ignored 0, pending 0
228+
Tests: succeeded 127, failed 0, canceled 0, ignored 0, pending 0
228229
All tests passed.

mycologyReport.txt

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -79,10 +79,10 @@ y claims all of the following:
7979
That the offset of the IP was ( 0 0 )
8080
That the least point containing a non-space cell is ( -3 -2 )
8181
That the greatest point, relative to that point, is ( 183 911 )
82-
That the day of the month is 27
82+
That the day of the month is 28
8383
That the month is 2
8484
That the year is 2020
85-
That the time is 07 : 36 : 53
85+
That the time is 04 : 39 : 47
8686
That the size of the stack stack is 1
8787
That the stack sizes are [ 0 ] from top to bottom
8888
That the command-line arguments were: [ null ]
@@ -148,8 +148,8 @@ GOOD: all of A-Z reflected
148148
Testing fingerprint HRTI... loaded.
149149
UNDEF: G gives clock granularity as 1000 microseconds
150150
GOOD: T reflected when called before M
151-
UNDEF: S pushed 530000
152-
UNDEF: T after M pushed 0 and a second T, after 675 ys, pushed 11827000
151+
UNDEF: S pushed 217000
152+
UNDEF: T after M pushed 1000 and a second T, after 675 ys, pushed 12146000
153153
GOOD: ET reflected
154154

155155
Testing fingerprint MODE... not loaded.

src/main/scala/ui/EsoExecutor.scala

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,8 @@ case class EsoExecutor(cmds: Vector[InterfaceHandler]) extends EsoObj{
3030
def showHelp(): Unit = {
3131
val cStr = cmds.map(h => s"- ${h.nam} ${h.helpStr}").sorted.mkString("\n")
3232
val hStr =
33-
s"""|Commands:
33+
s"""|Version: ${getClass.getPackage.getImplementationVersion}
34+
|Commands:
3435
|$cStr
3536
|
3637
|Syntax:

src/main/scala/ui/EsoIOInterface.scala

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ import scala.io.StdIn
77
trait EsoIOInterface extends EsoObj{
88
def readLine: String
99
def readLine(str: String): String
10+
def linesIterator: Iterator[String] = Iterator.continually(readLine)
11+
def charsIterator: Iterator[Char] = linesIterator.flatMap(ln => ln + '\n')
1012

1113
def print(x: Any): Unit
1214
def println(x: Any): Unit

src/main/scala/ui/InterfaceHandlers.scala

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -79,9 +79,9 @@ case class RunProgHandler(eio: EsoIOInterface = EsoConsoleInterface) extends Int
7979
def inputs: Try[LazyList[Char]] = args.get("i") match{
8080
case Some(fnam) =>
8181
val fStr = EsoFileReader.readFile(fnam, normLineFlag) map (s => (s + state.nums("fileEOF").toChar).to(LazyList).map{c => if(echoFInp) eio.print(c); c})
82-
if(appFlg) fStr map (s => s :++ LazyList.continually(eio.readLine + '\n').flatten)
82+
if(appFlg) fStr map (s => s :++ eio.charsIterator)
8383
else fStr
84-
case None => Success(LazyList.continually(eio.readLine + '\n').flatten)}
84+
case None => Success(eio.charsIterator.to(LazyList))}
8585

8686
def printer(out: Seq[Char]): Unit = args.get("o") match{
8787
case Some(onam) =>
Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
11
package unlambda
22

3+
import common.PrimeNumTools
34
import common_test.EsoSpec
45

56
class UnlambdaSpec extends EsoSpec{
67
testAllAgainstOutputAutoLimit(Unlambda, defaultConfig.set("echoFileInp", b=false))(
78
("hworld.unl", "", "Hello world\n", false),
89
("alphatest.unl", "", "abcdefghijklmnopqrstuvwxyz0123456789\n", false),
9-
("adventure.unl", grabFile("winAdventure.txt"), grabFile("adventureRef.txt"), true))
10+
("adventure.unl", grabFile("winAdventure.txt"), grabFile("adventureRef.txt"), true),
11+
("primes.unl", "", PrimeNumTools.primesLazy.take(100).mkString("\n"), true))
1012
}

0 commit comments

Comments
 (0)