Skip to content

Commit

Permalink
Merge pull request #5 from karlll/dev
Browse files Browse the repository at this point in the history
Split in subproject & new REPL
  • Loading branch information
karlll authored Dec 6, 2021
2 parents 490c2bb + 3b3570d commit 3af1b17
Show file tree
Hide file tree
Showing 53 changed files with 274 additions and 14 deletions.
2 changes: 1 addition & 1 deletion bin/krisp-repl.sh
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
#!/usr/bin/env bash

bin_dir="$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )"
java -jar "${bin_dir}/../build/libs/krisp-0.0.1-SNAPSHOT.jar"
java -jar "${bin_dir}/../core/build/libs/krisp-core-all.jar"
37 changes: 25 additions & 12 deletions build.gradle.kts → core/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,6 @@ plugins {
kotlin("jvm") version "1.5.10"
}

group = "com.ninjacontrol"
version = "0.0.1-SNAPSHOT"

repositories {
mavenCentral()
}
Expand All @@ -17,22 +14,38 @@ dependencies {
tasks.withType<KotlinCompile>() {
kotlinOptions.jvmTarget = "11"
}
tasks.withType<Jar> {
manifest {
attributes["Main-Class"] = "com.ninjacontrol.krisp.MainKt"
}
configurations["compileClasspath"].forEach { file: File ->
from(zipTree(file.absoluteFile)).duplicatesStrategy = DuplicatesStrategy.EXCLUDE
}
}

task<JavaExec>("run-internal-tests") {
dependsOn("testClasses")
mainClass.set("com.ninjacontrol.krisp.TestRunnerKt")
classpath = sourceSets["test"].runtimeClasspath
}
task<Exec>("run-mal-regression-tests") {
dependsOn("jar")
dependsOn("copy-fat-jar")
workingDir = File("./src/test/mal")
commandLine = listOf("./regression-test.sh")
}
tasks.withType<Jar> {
manifest {
attributes["Main-Class"] = "com.ninjacontrol.krisp.MainKt"
}
archiveBaseName.set("krisp-core")
}
task<Jar>("fat-jar") {
dependsOn("jar")
configurations["compileClasspath"].forEach { file: File ->
from(zipTree(file.absoluteFile)).duplicatesStrategy = DuplicatesStrategy.EXCLUDE
}
archiveBaseName.set("krisp-core-all")
with(tasks["jar"] as CopySpec)
}
task("copy-fat-jar") {
dependsOn("fat-jar")
doLast {
copy {
from("$buildDir/libs/krisp-core-all-${project.version}.jar")
into("$buildDir/libs/")
rename("(.+)-${project.version}(.+)", "$1$2")
}
}
}
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,5 @@ steps=(step2_eval.mal step3_env.mal step4_if_fn_do.mal step5_tco.mal step6_file.

for i in "${steps[@]}"
do
"${test_dir}"/runtest.py --rundir "${test_dir}" --hard --deferrable --optional "$i" -- bash "${test_dir}"/../../../bin/krisp-repl.sh
"${test_dir}"/runtest.py --rundir "${test_dir}" --hard --deferrable --optional "$i" -- bash "${test_dir}"/../../../../bin/krisp-repl.sh
done
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
2 changes: 2 additions & 0 deletions gradle.properties
Original file line number Diff line number Diff line change
@@ -1 +1,3 @@
kotlin.code.style=official
group=com.ninjacontrol
version=0.0.3
30 changes: 30 additions & 0 deletions repl/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile

plugins {
kotlin("jvm") version "1.5.10"
}

repositories {
mavenCentral()
}

dependencies {
implementation(project(":core"))
implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8")
implementation("org.aesh:readline:2.2")
}
tasks.withType<KotlinCompile>() {
kotlinOptions.jvmTarget = "11"
}
task<Jar>("fat-jar") {
dependsOn("jar")
manifest {
attributes["Main-Class"] = "com.ninjacontrol.krisp.ReplKt"
attributes["Implementation-Version"] = project.version
}
configurations["compileClasspath"].forEach { file: File ->
from(zipTree(file.absoluteFile)).duplicatesStrategy = DuplicatesStrategy.EXCLUDE
}
archiveBaseName.set("krisp-repl")
with(tasks["jar"] as CopySpec)
}
213 changes: 213 additions & 0 deletions repl/src/main/kotlin/com/ninjacontrol/krisp/Repl.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,213 @@
package com.ninjacontrol.krisp

import org.aesh.readline.Readline
import org.aesh.readline.ReadlineBuilder
import org.aesh.readline.editing.EditMode
import org.aesh.readline.editing.EditModeBuilder
import org.aesh.readline.terminal.TerminalBuilder
import org.aesh.readline.tty.terminal.TerminalConnection
import org.aesh.terminal.Terminal
import org.aesh.terminal.tty.Signal
import kotlin.system.exitProcess
import org.aesh.terminal.utils.Config as TerminalConfig

val replExecutionEnv = Environment().apply {
add(namespace)
set(
Symbols.eval,
func { args ->
eval(args[0], this)
}
)
set(symbol("*host-language*"), string("kotlin"))
}
val init = listOf(
"""(def! load-file (fn* (f) (eval (read-string (str "(do " (slurp f) "\nnil)")))))""",
"""(defmacro! cond (fn* (& xs) (if (> (count xs) 0) (list 'if (first xs) (if (> (count xs) 1) (nth xs 1) (throw "odd number of forms to cond")) (cons 'cond (rest (rest xs)))))))""",
)

tailrec fun mainLoop() {
readLine()?.let { input ->
try {
rep(input, replExecutionEnv)
} catch (e: MalException) {
out("*** ${e.message}")
} catch (e2: Throwable) {
out("*** Error: $e2")
out("*** Aborting")
exitProcess(1)
}
} ?: run {
out("** Exiting.")
exitProcess(0)
}
mainLoop()
}

fun re(input: String, env: Environment) = eval(read(input), env = env)
fun rep(input: String, env: Environment) = print(re(input, env = env))

fun evaluateFileAndExit(file: String) {
val expression = "(load-file \"$file\")"
try {
val result = re(expression, replExecutionEnv)
printString(result)
exitProcess(0)
} catch (e: Throwable) {
out("*** Error: ${e.message ?: "unknown error"}")
exitProcess(1)
}
}

fun start(
options: Options,
args: Array<String>?
) {

args?.let {
replExecutionEnv.set(
symbol("*ARGV*"),
// remove file argument from ARGV (args[0]) if we're going to evaluate a file
if (options.file == null) it.toMalList() else it.sliceArray(1 until it.size).toMalList()
)
}
if (options.withInit) {
init.forEach { expression ->
try {
re(expression, replExecutionEnv)
} catch (e: Throwable) {
out("*** Init failed (${e.message})")
exitProcess(1)
}
}
}
when {
options.file != null -> evaluateFileAndExit(options.file)
else -> {
banner()
mainLoop()
}
}
}

fun banner() {

out(
"""
❄️ krisp v${BuildVersion.version}
""".trimIndent()
)
}

fun printHelp() {
val msg = """
Usage: krisp <file> <options>
options:
--debug|-d Debug mode
--version|-v Print version and quit
--skipInit Do not run init
--help|-h Print help and quit
--input-mode=[emacs|vi] Set input mode, 'emacs' or 'vi' (default)
"""
out(msg)
}

fun printVersion() {
out(BuildVersion.version)
}

fun main(args: Array<String>) {
val options = Options(
withInit = !args.contains("--skipInit"),
debug = args.contains("--debug") || args.contains("-d"),
help = args.contains("--help") || args.contains("-h"),
file = args.getOrNull(0)?.let { if (it.startsWith("-")) null else it },
inputMode = if (args.contains("--input-mode=emacs")) InputMode.Emacs else InputMode.Vi,
version = args.contains("--version") || args.contains("-v")
)
val terminalConnection = getTerminalConnection()
terminalConnection.setSignalHandler { signal ->
when (signal) {
Signal.INT -> {
out("bye!"); exitProcess(0)
}
else -> terminalConnection.write(signal.name)
}
}

Output.target = TerminalOutput(terminalConnection)
Input.source = TerminalInput(terminalConnection, history = true, inputMode = options.inputMode)
when {
options.help -> {
printHelp()
}
options.version -> {
printVersion()
}
else -> start(options, args)
}
exitProcess(0)
}

enum class InputMode {
Vi, Emacs
}

data class Options(
val withInit: Boolean = true,
val debug: Boolean = false,
val inputMode: InputMode = InputMode.Vi,
val help: Boolean = false,
val file: String? = null,
val version: Boolean = false
)

fun getTerminalConnection() = TerminalConnection(getDefaultTerminal())
fun getDefaultTerminal(): Terminal = TerminalBuilder.builder()
.name("Krisp REPL")
.input(System.`in`)
.output(System.out)
.nativeSignals(true)
.build()

class TerminalOutput(private val terminalConnection: TerminalConnection) : Out {
override fun put(string: String) {
terminalConnection.write(string)
}

override fun putLine(string: String) {
terminalConnection.write(string + TerminalConfig.getLineSeparator())
}
}

class TerminalInput(
private val terminalConnection: TerminalConnection,
history: Boolean,
inputMode: InputMode
) : In {
private val mode = EditModeBuilder.builder(
if (inputMode == InputMode.Vi) EditMode.Mode.VI else EditMode.Mode.EMACS
).create()
private val readline: Readline =
ReadlineBuilder.builder()
.enableHistory(history)
.editMode(mode)
.build()

override fun readLine(): String? {
var result: String? = null
readline.readline(terminalConnection, prompt()) { input ->
result = input

terminalConnection.stopReading()
}
terminalConnection.openBlocking()
return result
}
}

object BuildVersion {
val version: String
get() = BuildVersion::class.java.getPackage().implementationVersion ?: "???"
}
2 changes: 2 additions & 0 deletions settings.gradle.kts
Original file line number Diff line number Diff line change
@@ -1 +1,3 @@
rootProject.name = "krisp"
include("core")
include("repl")

0 comments on commit 3af1b17

Please sign in to comment.