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
117
118
119
120
121
122
123
124
|
package moe.nea.lisp
object CoreBindings {
val nil = LispData.LispNil
val def = LispData.externalRawCall { context, callsite, stackFrame, args ->
if (args.size != 2) {
return@externalRawCall context.reportError("Function define expects exactly two arguments", callsite)
}
val (name, value) = args
if (name !is LispAst.Reference) {
return@externalRawCall context.reportError("Define expects a name as first argument", name)
}
if (name.label in stackFrame.variables) {
return@externalRawCall context.reportError("Cannot redefine value in local context", name)
}
return@externalRawCall stackFrame.setValueLocal(name.label, context.resolveValue(stackFrame, value))
}
val pure = LispData.externalCall { args, reportError ->
return@externalCall args.singleOrNull()?.let { value ->
LispData.externalCall { args, reportError ->
if (args.isNotEmpty())
reportError("Pure function does not expect arguments")
else
value
}
} ?: reportError("Function pure expects exactly one argument")
}
val lambda = LispData.externalRawCall { context, callsite, stackFrame, args ->
if (args.size != 2) {
return@externalRawCall context.reportError("Lambda needs exactly 2 arguments", callsite)
}
val (argumentNames, body) = args
if (argumentNames !is LispAst.Parenthesis) {
return@externalRawCall context.reportError("Lambda has invalid argument declaration", argumentNames)
}
val argumentNamesString = argumentNames.items.map {
val ref = it as? LispAst.Reference
if (ref == null) {
return@externalRawCall context.reportError("Lambda has invalid argument declaration", it)
}
ref.label
}
if (body !is LispAst.Parenthesis) {
return@externalRawCall context.reportError("Lambda has invalid body declaration", body)
}
LispData.createLambda(stackFrame, argumentNamesString, body)
}
val defun = LispData.externalRawCall { context, callSite, stackFrame, lispAsts ->
if (lispAsts.size != 3) {
return@externalRawCall context.reportError("Invalid function definition", callSite)
}
val (name, args, body) = lispAsts
if (name !is LispAst.Reference) {
return@externalRawCall context.reportError("Invalid function definition name", name)
}
if (name.label in stackFrame.variables) {
return@externalRawCall context.reportError("Cannot redefine function in local context", name)
}
if (args !is LispAst.Parenthesis) {
return@externalRawCall context.reportError("Invalid function definition arguments", args)
}
val argumentNames = args.items.map {
val ref = it as? LispAst.Reference
?: return@externalRawCall context.reportError("Invalid function definition argument name", it)
ref.label
}
if (body !is LispAst.Parenthesis) {
return@externalRawCall context.reportError("Invalid function definition body", body)
}
return@externalRawCall stackFrame.setValueLocal(
name.label,
LispData.createLambda(stackFrame, argumentNames, body, name.label)
)
}
val seq = LispData.externalRawCall { context, callsite, stackFrame, args ->
var lastResult: LispData? = null
for (arg in args) {
lastResult = context.executeLisp(stackFrame, arg)
}
lastResult ?: context.reportError("Seq cannot be invoked with 0 argumens", callsite)
}
val debuglog = LispData.externalRawCall { context, callsite, stackFrame, args ->
println(args.joinToString(" ") { arg ->
when (val resolved = context.resolveValue(stackFrame, arg)) {
is LispData.Atom -> ":${resolved.label}"
is LispData.JavaExecutable -> "<native code>"
LispData.LispNil -> "nil"
is LispData.LispNode -> resolved.node.toSource()
is LispData.LispString -> resolved.string
is LispData.LispNumber -> resolved.value.toString()
is LispData.LispInterpretedCallable -> "<function ${resolved.name ?: ""} ${resolved.argNames} ${resolved.body.toSource()}>"
}
})
LispData.LispNil
}
val add = LispData.externalCall { args, reportError ->
if (args.size == 0) {
return@externalCall reportError("Cannot call add without at least 1 argument")
}
LispData.LispNumber(args.fold(0.0) { a, b ->
a + (b as? LispData.LispNumber
?: return@externalCall reportError("Unexpected argument $b, expected number")).value
})
}
fun offerArithmeticTo(bindings: StackFrame) {
bindings.setValueLocal("+", add)
}
fun offerAllTo(bindings: StackFrame) {
bindings.setValueLocal("nil", nil)
bindings.setValueLocal("def", def)
bindings.setValueLocal("pure", pure)
bindings.setValueLocal("lambda", lambda)
bindings.setValueLocal("defun", defun)
bindings.setValueLocal("seq", seq)
bindings.setValueLocal("debuglog", debuglog)
offerArithmeticTo(bindings)
}
}
|