Skip to content

Commit ebdb5ef

Browse files
Petr LaštovičkaLastaapps
Petr Laštovička
authored andcommitted
build: ShadowJar and CLI parsing added
1 parent b630f86 commit ebdb5ef

16 files changed

+263
-52
lines changed

build.sh

+9
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
#!/bin/bash
2+
3+
cd lispik
4+
5+
chmod +x gradlew
6+
./gradlew shadowJar
7+
8+
cp build/libs/lispik-1.0.0.jar ../lispik.jar
9+

lispik/build.gradle.kts

+16-1
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,11 @@
1+
import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar
12
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
23

34
plugins {
45
kotlin("jvm") version "1.8.0-RC2"
6+
id("com.github.johnrengelman.shadow") version "7.1.2"
57
application
8+
id("java")
69
}
710

811
group = "cz.lastaapps"
@@ -21,6 +24,7 @@ dependencies {
2124
// Kotlin
2225
implementation("org.jetbrains.kotlinx:kotlinx-datetime:0.4.0")
2326
implementation("org.jetbrains.kotlinx:kotlinx-collections-immutable:0.3.5")
27+
implementation("org.jetbrains.kotlinx:kotlinx-cli:0.3.5")
2428

2529
// Kotest
2630
testImplementation("io.kotest:kotest-runner-junit5:5.5.4")
@@ -38,6 +42,17 @@ tasks.withType<KotlinCompile> {
3842
kotlinOptions.languageVersion = "1.9"
3943
}
4044

45+
java {
46+
sourceCompatibility = JavaVersion.VERSION_11
47+
targetCompatibility = JavaVersion.VERSION_11
48+
}
49+
4150
application {
4251
mainClass.set("MainKt")
43-
}
52+
}
53+
54+
tasks.withType<ShadowJar> {
55+
archiveBaseName.set("lispik")
56+
archiveClassifier.set("")
57+
archiveVersion.set("1.0.0")
58+
}

lispik/examples/merge_sort.lsp

+6
Original file line numberDiff line numberDiff line change
@@ -22,3 +22,9 @@
2222
(if (null? (cdr lst)) lst
2323
(let (pair (my-split lst))
2424
(merge-list (merge-sort (car pair)) (merge-sort (cdr pair)))))))
25+
26+
(merge-sort '())
27+
(merge-sort '(1 2 3 4 5))
28+
(merge-sort '(5 4 3 2 1))
29+
(merge-sort '(69 420 42))
30+

lispik/gradle.properties

+7
Original file line numberDiff line numberDiff line change
@@ -1 +1,8 @@
11
kotlin.code.style=official
2+
org.gradle.caching=true
3+
org.gradle.unsafe.configuration-cache-problems=warn
4+
org.gradle.daemon=true
5+
org.gradle.parallel=true
6+
org.gradle.jvmargs=-Xmx2048M -Dkotlin.daemon.jvm.options\="-Xmx2048M" -Dfile.encoding\=UTF-8 -XX\:+UseParallelGC
7+
org.gradle.configureondemand=true
8+
org.gradle.unsafe.configuration-cache=true

lispik/src/main/kotlin/Main.kt

+131-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,135 @@
1+
import arrow.core.Either
2+
import arrow.core.andThen
3+
import data.compiler.unwrap
4+
import data.getMessage
5+
import domain.Compiler
6+
import domain.Parser
17
import domain.Repl
8+
import domain.Tokenizer
9+
import domain.VirtualMachine
10+
import kotlinx.cli.ArgParser
11+
import kotlinx.cli.ArgType
12+
import kotlinx.cli.default
13+
import kotlinx.cli.optional
14+
import util.loadFile
15+
import kotlin.system.exitProcess
216

317
fun main(args: Array<String>) {
4-
Repl.createInstance().run(args.getOrNull(0), true)
18+
19+
val parser = ArgParser("lispik")
20+
21+
val noGlobalEnv by parser.option(
22+
type = ArgType.Boolean,
23+
shortName = "ng",
24+
fullName = "no-global",
25+
description = "Disables usage of the global environment for calling functions. Define keyword is not available in this mode and will throw an error",
26+
).default(false)
27+
28+
val evalOnly by parser.option(
29+
type = ArgType.Boolean,
30+
shortName = "e",
31+
fullName = "eval-only",
32+
description = "Evaluates the file on input, print results and exit. Disables REPL.",
33+
).default(false)
34+
35+
val compileOnly by parser.option(
36+
type = ArgType.Boolean,
37+
shortName = "c",
38+
fullName = "compile-only",
39+
description = "Just prints out compiled bytecode in human-readable form. Disables REPL.",
40+
).default(false)
41+
42+
val showByteCode by parser.option(
43+
type = ArgType.Boolean,
44+
shortName = "b",
45+
fullName = "show-bytecode",
46+
description = "Show compiled bytecode in the REPL mode",
47+
).default(false)
48+
49+
val debug by parser.option(
50+
type = ArgType.Boolean,
51+
shortName = "d",
52+
fullName = "debug",
53+
description = "Shows debug logs, lots of them",
54+
).default(false)
55+
56+
val filename by parser.argument(
57+
type = ArgType.String,
58+
description = "Load a library at startup and evaluates it.",
59+
).optional()
60+
61+
parser.parse(args)
62+
63+
when {
64+
compileOnly -> compileOnly(filename, !noGlobalEnv)
65+
evalOnly -> evalOnly(filename, !noGlobalEnv)
66+
else ->
67+
Repl.createInstance().run(
68+
filename = filename,
69+
showByteCode = showByteCode,
70+
debug = debug,
71+
globalEnv = !noGlobalEnv,
72+
)
73+
}
74+
}
75+
76+
private fun compileOnly(filename: String?, globalEnabled: Boolean) {
77+
val source = filename?.let {
78+
when (val res = loadFile(filename)) {
79+
is Either.Left -> {
80+
println("Failed to read file '$filename'")
81+
exitProcess(1)
82+
}
83+
84+
is Either.Right -> {
85+
res.value
86+
}
87+
}
88+
} ?: run {
89+
println("No file given, exiting")
90+
exitProcess(2)
91+
}
92+
93+
val tokenizer = Tokenizer.from(source)
94+
val parser = Parser.from(tokenizer)
95+
parser.parseToAST().andThen { globalScope ->
96+
Compiler.from().compile(globalScope, globalEnabled)
97+
}.tap {
98+
println(it)
99+
}.tapInvalid {
100+
println(it.getMessage())
101+
}
102+
}
103+
104+
private fun evalOnly(filename: String?, globalEnabled: Boolean) {
105+
val source = filename?.let {
106+
when (val res = loadFile(filename)) {
107+
is Either.Left -> {
108+
println("Failed to read file '$filename'")
109+
exitProcess(1)
110+
}
111+
112+
is Either.Right -> {
113+
res.value
114+
}
115+
}
116+
} ?: run {
117+
println("No file given, exiting")
118+
exitProcess(2)
119+
}
120+
121+
val tokenizer = Tokenizer.from(source)
122+
val parser = Parser.from(tokenizer)
123+
parser.parseToAST().andThen { globalScope ->
124+
Compiler.from().compile(globalScope, globalEnabled)
125+
}.andThen {
126+
VirtualMachine.from().runCode(it, globalEnv = globalEnabled)
127+
}
128+
.tap { stack ->
129+
stack.forEach {
130+
println(it.unwrap())
131+
}
132+
}.tapInvalid {
133+
println(it.getMessage())
134+
}
5135
}

lispik/src/main/kotlin/data/compiler/CompilerImpl.kt

+12-3
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,13 @@ import kotlinx.collections.immutable.toPersistentList
2020
* Holds function/variable/parameter names
2121
* The root one is always the global scope
2222
*/
23-
typealias CompilationContext = PersistentList<ImmutableList<String>>
23+
data class CompilationContext(
24+
val env: PersistentList<ImmutableList<String>>,
25+
val globalEnabled: Boolean,
26+
)
27+
28+
fun CompilationContext.push(env: ImmutableList<String>) =
29+
copy(env = this.env.add(0, env))
2430

2531
fun Node.compileDispatcher(context: CompilationContext): Validated<Error, ByteCode.CodeBlock> =
2632
when (this) {
@@ -44,8 +50,11 @@ fun Node.compileDispatcher(context: CompilationContext): Validated<Error, ByteCo
4450
class CompilerImpl : Compiler {
4551

4652
override fun compile(scope: GlobalScope, createGlobalEnv: Boolean): Validated<Error, ByteCode.CodeBlock> {
47-
val rootContext: CompilationContext = persistentListOf(
48-
scope.functions.map { it.name }.toPersistentList()
53+
val rootContext = CompilationContext(
54+
persistentListOf(
55+
scope.functions.map { it.name }.toPersistentList(),
56+
),
57+
createGlobalEnv,
4958
)
5059

5160
if (!createGlobalEnv && scope.functions.isNotEmpty()) {

lispik/src/main/kotlin/data/compiler/DefineLambdaCompiler.kt

+2-2
Original file line numberDiff line numberDiff line change
@@ -8,15 +8,15 @@ import domain.model.Node
88
import domain.model.protect
99

1010
fun Node.Closures.DeFun.compile(context: CompilationContext): Validated<Error, ByteCode.CodeBlock> {
11-
val childContext = context.add(0, this.params)
11+
val childContext = context.push(params)
1212

1313
return body.compileDispatcher(childContext).map { child ->
1414
listOf(child, ByteInstructions.Rtn).flatten()
1515
}
1616
}
1717

1818
fun Node.Closures.Lambda.compile(context: CompilationContext): Validated<Error, ByteCode.CodeBlock> {
19-
val childContext = context.add(0, this.params)
19+
val childContext = context.push(params)
2020

2121
return body.compileDispatcher(childContext).map { child ->
2222
listOf(

lispik/src/main/kotlin/data/compiler/LetCompiler.kt

+2-2
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ import kotlinx.collections.immutable.persistentListOf
1515
* NIL LDC 1 CONS LDF ( LD (0.0) ) AP
1616
*/
1717
fun Node.Closures.Let.compile(context: CompilationContext): Validated<Error, ByteCode.CodeBlock> {
18-
val childContext = context.add(0, persistentListOf(name))
18+
val childContext = context.push(persistentListOf(name))
1919

2020
val valueCode = value.compileDispatcher(context).valueOr { return it.invalid() } // child context in letrec
2121
val bodyCode = body.compileDispatcher(childContext).valueOr { return it.invalid() }
@@ -31,7 +31,7 @@ fun Node.Closures.Let.compile(context: CompilationContext): Validated<Error, Byt
3131
}
3232

3333
fun Node.Closures.LetRec.compile(context: CompilationContext): Validated<Error, ByteCode.CodeBlock> {
34-
val childContext = context.add(0, persistentListOf(name))
34+
val childContext = context.push(persistentListOf(name))
3535

3636
val valueCode = value.compileDispatcher(childContext).valueOr { return it.invalid() }
3737
val bodyCode = this.body.compileDispatcher(childContext).valueOr { return it.invalid() }

lispik/src/main/kotlin/data/compiler/SubstitutionCompiler.kt

+2-2
Original file line numberDiff line numberDiff line change
@@ -10,13 +10,13 @@ import domain.model.Error
1010
import domain.model.Node
1111

1212
fun Node.Named.findCoordinates(context: CompilationContext): Validated<Error, ByteCode.Literal.LPair> {
13-
context.forEachIndexed { envIndex, env ->
13+
context.env.forEachIndexed { envIndex, env ->
1414
when (val index = env.indexOf(name)) {
1515
-1 -> return@forEachIndexed
1616
else -> {
1717
return ByteCode.Literal.LPair(
1818
ByteCode.Literal.Integer(
19-
if (envIndex == context.lastIndex) {
19+
if (envIndex == context.env.lastIndex && context.globalEnabled) {
2020
ByteCode.Literal.GlobalContext.value
2121
} else {
2222
envIndex

0 commit comments

Comments
 (0)