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
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
|
-- builtin special forms
import Action, FnDef from require 'core.base'
import Result, Value from require 'core.value'
import Cell from require 'core.cell'
import Scope from require 'core.scope'
class doc extends Action
@doc: "(doc sym) - print documentation in console
prints the docstring for sym in the console"
eval: (scope, tail) =>
assert #tail == 1, "'doc' takes exactly one parameter"
result = L\push tail[1]\eval, scope
with Result children: { def }
def = result\const!\unwrap!
L\print "(doc #{tail[1]\stringify!}):\n#{def.doc}\n"
class def extends Action
@doc: "(def sym1 val-expr1
[sym2 val-expr2]...) - declare symbols in parent scope
defines the symbols sym1, sym2, ... to resolve to the values of val-expr1, val-expr2, ...
updates all val-exprs."
eval: (scope, tail) =>
L\trace "evaling #{@}"
assert #tail > 1, "'def' requires at least 2 arguments"
assert #tail % 2 == 0, "'def' requires an even number of arguments"
children = L\push ->
return for i=1,#tail,2
name, val_expr = tail[i], tail[i+1]
name = (name\quote scope)\unwrap 'sym'
with val_expr\eval scope
scope\set name, \make_ref!
Result :children
class use extends Action
@doc: "(use scope1 [scope2]...) - merge scopes into parent scope
adds all symbols from scope1, scope2, ... to the parent scope.
all scopes have to be eval-time constants."
eval: (scope, tail) =>
L\trace "evaling #{@}"
for child in *tail
result = L\push child\eval, scope
value = result\const!
scope\use value\unwrap 'scope', "'use' only works on scopes"
Result!
class require_ extends Action
@doc: "(require name-str) - require a module
returns the module's scope
name-str has to be an eval-time constant."
eval: (scope, tail) =>
L\trace "evaling #{@}"
assert #tail == 1, "'require' takes exactly one parameter"
result = L\push tail[1]\eval, scope
name = result\const!
L\trace @, "loading module #{name}"
Result value: Value.wrap require "lib.#{name\unwrap 'str'}"
class import_ extends Action
@doc: "(import sym1 [sym2]...) - require and define modules
requires modules sym1, sym2, ... and defines them as sym1, sym2, ... in the current scope"
eval: (scope, tail) =>
L\trace "evaling #{@}"
assert #tail > 0, "'import' requires at least one arguments"
for child in *tail
name = (child\quote scope)\unwrap 'sym'
scope\set name, Result value: Value.wrap require "lib.#{name}"
Result!
class import_star extends Action
@doc: "(import* sym1 [sym2]...) - require and use modules
requires modules sym1, sym2, ... and merges them into the current scope"
eval: (scope, tail) =>
L\trace "evaling #{@}"
assert #tail > 0, "'import' requires at least one arguments"
for child in *tail
name = (child\quote scope)\unwrap 'sym'
scope\use (Value.wrap require "lib.#{name}")\unwrap 'scope'
Result!
class fn extends Action
@doc: "(fn (p1 [p2]...) body-expr) - declare a (lambda) function
the symbols p1, p2, ... will resolve to the arguments passed to the function."
eval: (scope, tail) =>
L\trace "evaling #{@}"
assert #tail == 2, "'fn' takes exactly two arguments"
{ params, body } = tail
assert params.__class == Cell, "'fn's first argument has to be an expression"
param_symbols = for param in *params.children
assert param.type == 'sym', "function parameter declaration has to be a symbol"
param\quote scope
body = body\quote scope
Result value: Value.wrap FnDef param_symbols, body, scope
class defn extends Action
@doc: "(defn name-sym (p1 [p2]...) body-expr) - define a function
declares a lambda (see (doc fn)) and defines it in the current scope"
eval: (scope, tail) =>
L\trace "evaling #{@}"
assert #tail == 3, "'defn' takes exactly three arguments"
{ name, params, body } = tail
name = (name\quote scope)\unwrap 'sym'
assert params.__class == Cell, "'defn's second argument has to be an expression"
param_symbols = for param in *params.children
assert param.type == 'sym', "function parameter declaration has to be a symbol"
param\quote scope
body = body\quote scope
fn = FnDef param_symbols, body, scope
scope\set name, Result value: Value.wrap fn
Result!
class do_expr extends Action
@doc: "(do expr1 [expr2]...) - update multiple expressions
evaluates and continously updates expr1, expr2, ...
the last expression's value is returned."
eval: (scope, tail) =>
scope = Scope scope
Result children: [expr\eval scope for expr in *tail]
class if_ extends Action
@doc: "(if bool then-expr [else-xpr]) - make an eval-time const choice
bool has to be an eval-time constant. If it is truthy, this expression is equivalent
to then-expr, otherwise it is equivalent to else-xpr if given, or nil otherwise."
eval: (scope, tail) =>
L\trace "evaling #{@}"
assert #tail >= 2, "'if' needs at least two parameters"
assert #tail <= 3, "'if' needs at most three parameters"
{ xif, xthen, xelse } = tail
xif = L\push xif\eval, scope
xif = xif\const!\unwrap!
if xif
xthen\eval scope
elseif xelse
xelse\eval scope
class trace extends Action
@doc: "(trace expr) - print an eval-time constant to the console"
eval: (scope, tail) =>
L\trace "evaling #{@}"
assert #tail == 1, "'trace' takes exactly one parameter"
with result = L\push tail[1]\eval, scope
L\print "trace #{tail[1]\stringify!}: #{result.value}"
{
:doc, :trace
:def, :use
require: require_
import: import_
'import*': import_star
true: Value.bool true
false: Value.bool false
:fn, :defn
'do': do_expr
if: if_
}
|