3 import kotlin.text.Regex
5 val TOKEN_REGEX = Regex("[\\s,]*(~@|[\\[\\]{}()'`~^@]|\"(?:\\\\.|[^\\\\\"])*\"|;.*|[^\\s\\[\\]{}('\"`,;)]*)")
6 val ATOM_REGEX = Regex("(^-?[0-9]+$)|(^nil$)|(^true$)|(^false$)|^\"(.*)\"$|:(.*)|(^[^\"]*$)")
8 class Reader(sequence: Sequence<String>) {
9 val tokens = sequence.iterator()
10 var current = advance()
18 fun peek(): String? = current
20 private fun advance(): String? = if (tokens.hasNext()) tokens.next() else null
23 fun read_str(input: String?): MalType {
24 val tokens = tokenizer(input) ?: return NIL
25 return read_form(Reader(tokens))
28 fun tokenizer(input: String?): Sequence<String>? {
29 if (input == null) return null
31 return TOKEN_REGEX.findAll(input)
32 .map({ it -> it.groups[1]?.value as String })
33 .filter({ it != "" && !it.startsWith(";")})
36 fun read_form(reader: Reader): MalType =
37 when (reader.peek()) {
38 null -> throw MalContinue()
39 "(" -> read_list(reader)
40 ")" -> throw MalReaderException("expected form, got ')'")
41 "[" -> read_vector(reader)
42 "]" -> throw MalReaderException("expected form, got ']'")
43 "{" -> read_hashmap(reader)
44 "}" -> throw MalReaderException("expected form, got '}'")
45 "'" -> read_shorthand(reader, "quote")
46 "`" -> read_shorthand(reader, "quasiquote")
47 "~" -> read_shorthand(reader, "unquote")
48 "~@" -> read_shorthand(reader, "splice-unquote")
52 val meta = read_form(reader)
53 val obj = read_form(reader)
56 list.conj_BANG(MalSymbol("with-meta"))
62 else -> read_atom(reader)
65 fun read_list(reader: Reader): MalType = read_sequence(reader, MalList(), ")")
66 fun read_vector(reader: Reader): MalType = read_sequence(reader, MalVector(), "]")
68 private fun read_sequence(reader: Reader, sequence: IMutableSeq, end: String): MalType {
72 val form = when (reader.peek()) {
73 null -> throw MalReaderException("expected '$end', got EOF")
74 end -> { reader.next(); null }
75 else -> read_form(reader)
79 sequence.conj_BANG(form)
81 } while (form != null)
86 fun read_hashmap(reader: Reader): MalType {
88 val hashMap = MalHashMap()
91 var value : MalType? = null;
92 val key = when (reader.peek()) {
93 null -> throw MalReaderException("expected '}', got EOF")
94 "}" -> { reader.next(); null }
96 var key = read_form(reader)
97 if (key !is MalString) {
98 throw MalReaderException("hash-map keys must be strings or keywords")
100 value = when (reader.peek()) {
101 null -> throw MalReaderException("expected form, got EOF")
102 else -> read_form(reader)
109 hashMap.assoc_BANG(key as MalString, value as MalType)
111 } while (key != null)
116 fun read_shorthand(reader: Reader, symbol: String): MalType {
120 list.conj_BANG(MalSymbol(symbol))
121 list.conj_BANG(read_form(reader))
126 fun read_atom(reader: Reader): MalType {
127 val next = reader.next() ?: throw MalReaderException("Unexpected null token")
128 val groups = ATOM_REGEX.find(next)?.groups ?: throw MalReaderException("Unrecognized token: " + next)
130 return if (groups[1]?.value != null) {
131 MalInteger(Integer.valueOf(groups[1]?.value))
132 } else if (groups[2]?.value != null) {
134 } else if (groups[3]?.value != null) {
136 } else if (groups[4]?.value != null) {
138 } else if (groups[5]?.value != null) {
139 MalString((groups[5]?.value as String).replace("\\n", "\n").replace("\\\"", "\""))
140 } else if (groups[6]?.value != null) {
141 MalKeyword(groups[6]?.value as String)
142 } else if (groups[7]?.value != null) {
143 MalSymbol(groups[7]?.value as String)
145 throw MalReaderException("Unrecognized token: " + next)