Skip to content

Instantly share code, notes, and snippets.

@yaraki
Created February 14, 2015 08:48
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save yaraki/bbecffc8da412ca86291 to your computer and use it in GitHub Desktop.
Save yaraki/bbecffc8da412ca86291 to your computer and use it in GitHub Desktop.
package com.arakitech.kotlisp
import java.util.HashMap
fun StringBuilder.clear() {
this.setLength(0)
}
class Tokenizer(val input: String) {
private var position = 0
private var preserved: String? = null
fun next(): String? {
val sb = StringBuilder()
val length = input.length()
while (position < length) {
val c = input[position]
when (c) {
' ', '\t', '\r', '\n' -> {
if (0 < sb.length()) {
position++
return sb.toString()
}
}
'(', ')', '[', ']', '\'', '`' -> {
if (0 < sb.length()) {
return sb.toString()
}
position++
return c.toString()
}
',' -> {
if (0 < sb.length()) {
return sb.toString()
}
position++
if ('@' == input[position]) {
position++
return ",@"
} else {
return ","
}
}
else -> sb.append(c)
}
position++
}
if (0 < sb.length()) {
return sb.toString()
}
return null
}
fun hasNext() = position < input.length()
}
abstract class LExpr {
abstract fun show(): String
}
abstract class LLiteral : LExpr() {
}
class LSymbol(val name: String) : LExpr() {
override fun show(): String {
return name
}
}
class LInteger(val value: Int) : LLiteral() {
override fun show(): String {
return value.toString()
}
}
abstract class LList : LExpr() {
abstract fun nil(): Boolean
abstract fun showWithoutParen(): String
override fun show(): String {
return "(${showWithoutParen()})"
}
}
class LNil : LList() {
class object {
val NIL = LNil()
}
override fun showWithoutParen(): String {
return "nil"
}
override fun nil(): Boolean {
return true
}
}
class LPair(val head: LExpr, val tail: LList) : LList() {
override fun nil(): Boolean {
return false;
}
override fun showWithoutParen(): String {
return if (tail.nil()) head.show() else "${head.show()} ${tail.showWithoutParen()}"
}
override fun show(): String {
return "(${showWithoutParen()})"
}
}
fun read(tokenizer: Tokenizer, asList: Boolean): LExpr? {
val token = tokenizer.next()
if (token == null) {
return null
}
var expr: LExpr? = null
if ("(" == token) {
expr = read(tokenizer, true)
} else if (")" == token) {
expr = LNil.NIL
} else if (token.matches("[0-9.]+")) {
expr = LInteger(Integer.parseInt(token))
} else {
expr = LSymbol(token)
}
return if (asList) {
if (expr == LNil.NIL) {
LNil.NIL
} else {
LPair(expr as LExpr, read(tokenizer, true) as LList)
}
} else {
expr
}
}
fun read(s: String): LExpr? {
val tokenizer = Tokenizer(s)
return read(tokenizer, false)
}
class LFunction(val f: (LList) -> LExpr) : LExpr() {
override fun show(): String {
return "#function"
}
fun apply(args: LList): LExpr {
return f(args)
}
}
class Env {
private val table = HashMap<String, LExpr>()
fun intern(s: String, expr: LExpr) {
table.put(s, expr)
}
fun evalEach(list: LList): LList {
return when (list) {
is LPair -> LPair(eval(list.head), evalEach(list.tail))
else -> LNil.NIL
}
}
fun eval(expr: LExpr): LExpr =
when (expr ) {
is LLiteral -> expr
is LSymbol -> {
table[expr.name]
}
is LPair -> {
val head = eval(expr.head)
when (head) {
is LFunction -> head.apply(evalEach(expr.tail))
else -> LNil.NIL
}
}
else -> LNil.NIL
}
}
fun main(args: Array<String>) {
val s = LSymbol("+")
println(s.show())
val i1 = LInteger(1)
val i2 = LInteger(2)
println(i1.show())
val p1 = LPair(i1, LNil.NIL)
val p2 = LPair(i2, p1)
val p3 = LPair(s, p2)
println(p3.show())
//
println("==================================")
val t = Tokenizer("(`(cons ,@a ,b) [+ 123 abc])")
while (t.hasNext()) {
val token = t.next()
if (token == null) break
println(token)
}
println("==================================")
println(read("(+ 1 2 3)")?.show())
println(read("(+ 1 (* 2 3))")?.show())
val env = Env()
env.intern("+", LFunction({ args ->
var l = args
var i = 0
while (l !is LNil) {
val v = ((l as LPair).head as LInteger)
i += v.value
l = (l as LPair).tail
}
LInteger(i)
}))
val ll = read("(+ 1 2 3 (+ 4 5 (+ 6 7)))")!!
println("# ${ll.show()}")
val result = env.eval(ll)
if (result != null) {
println(result.show())
}
}
@yaraki
Copy link
Author

yaraki commented Feb 14, 2015

This is my very first Kotlin program, so there should definitely be a plenty of room for improvements.

@abreslav
Copy link

This is just a joke, but it's working nevertheless. Have a look here: http://kotlin-demo.jetbrains.com/?publicLink=104074971561017308771-1697121195

The April fool's blog post about it: http://blog.jetbrains.com/kotlin/2014/04/kotlin-gets-support-for-s-expressions/

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment