1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
|
package moe.nea.lisp
import java.io.File
class LispParser private constructor(filename: String, string: String) {
val racer = StringRacer(filename, string)
val program = parseProgram()
companion object {
fun parse(filename: String, string: String): LispAst.Program {
return LispParser(filename, string).program
}
fun parse(file: File): LispAst.Program {
return parse(file.absolutePath, file.readText())
}
val digits = "1234567890"
val alphabet = "abcdefghijklmnopqrstuvwxyz"
val validStartingIdentifiers = "-.#+*'!$%&/=?_~|^" + alphabet + alphabet.uppercase()
val validIdentifiers = validStartingIdentifiers + digits
val parenthesisMatches = mapOf(
"(" to ")",
"[" to "]",
"{" to "}",
"<" to ">",
)
}
fun parseProgram(): LispAst.Program {
val start = racer.idx
val nodes = mutableListOf<LispAst.LispNode>()
while (true) {
racer.skipWhitespace()
if (racer.finished())
break
nodes.add(parseNode())
}
return LispAst.Program(racer.span(start), nodes)
}
private fun parseNode(): LispAst.LispNode {
val start = racer.idx
val paren = racer.peekReq(1) ?: racer.error("Expected start of expression")
val matchingParen = parenthesisMatches[paren]
if (matchingParen != null) {
val paren = parseParenthesis(paren, matchingParen)
return LispAst.Parenthesis(racer.span(start), paren)
}
if (paren == "\"") {
return parseString()
}
if (paren == ":") {
return parseAtom()
}
val ident = parseIdentifier()
return LispAst.Reference(racer.span(start), ident)
}
fun parseAtom(): LispAst.Atom {
val start = racer.idx
racer.expect(":", "Expected : at start of atom")
val ident = parseIdentifier()
return LispAst.Atom(racer.span(start), ident)
}
fun parseIdentifier(): String {
return racer.consumeWhile { it.first() in validStartingIdentifiers && it.last() in validIdentifiers }.also {
if (it.isEmpty()) racer.error("Expected identifier")
}
}
fun parseString(): LispAst.StringLiteral {
val start = racer.idx
val quoted = parseQuotedString()
return LispAst.StringLiteral(racer.span(start), quoted)
}
fun parseQuotedString(): String {
racer.expect("\"", "Expected '\"' at string start")
val sb = StringBuilder()
while (true) {
when (val peek = racer.consumeCountReq(1)) {
"\"" -> break
"\\" -> {
val escaped = racer.consumeCountReq(1) ?: racer.error("Unfinished backslash escape")
if (escaped != "\"" && escaped != "\\") {
// Surprisingly i couldn't find unicode escapes to be generated by the original minecraft 1.8.9 implementation
racer.idx--
racer.error("Invalid backslash escape '$escaped'")
}
sb.append(escaped)
}
null -> racer.error("Unfinished string")
else -> {
sb.append(peek)
}
}
}
return sb.toString()
}
private fun parseParenthesis(opening: String, closing: String): List<LispAst.LispNode> {
val l = mutableListOf<LispAst.LispNode>()
racer.expect(opening, "Expected $opening")
while (true) {
racer.skipWhitespace()
if (racer.tryConsume(closing)) {
return l
}
l.add(parseNode())
}
}
}
|