1 readline
= require
'./node_readline'
2 {id
, map
, each
, last
, all
, unique
, zip
, Obj
, elem
-index
} = require
'prelude-ls'
3 {read_str
} = require
'./reader'
4 {pr_str
} = require
'./printer'
5 {Env
} = require
'./env'
6 {runtime
-error
, ns
} = require
'./core'
7 {list
-to
-pairs
} = require
'./utils'
10 defer
-tco
= (env
, ast
) ->
14 eval
: -> eval_ast env
, ast
17 is
-thruthy
= ({type
, value
}) ->
18 type
!= \const or value not in
[\nil \false
]
21 fmap
-ast
= (fn
, {type
, value
}: ast
) -->
22 {type
: type
, value
: fn value
}
25 eval_simple
= (env
, {type
, value
}: ast
) ->
27 | \symbol
=> env.get value
28 | \list
, \vector
=> ast |
> fmap
-ast map eval_ast env
29 | \map
=> ast |
> fmap
-ast Obj.map eval_ast env
33 eval_ast
= (env
, {type
, value
}: ast
) -->
36 return eval_simple env
, ast
37 else if value.length
== 0
40 result
= if value
[0].type
== \symbol
43 |
'def!' => eval_def env, params
44 |
'let*' => eval_let env, params
45 |
'do' => eval_do env, params
46 |
'if' => eval_if env, params
47 |
'fn*' => eval_fn env, params
48 | otherwise
=> eval_apply env
, value
52 if result.type
== \tco
54 {type
, value
}: ast
= result.ast
59 check_params
= (name
, params
, expected
) ->
60 if params.length
!= expected
61 runtime
-error "
'#{name}' expected #{expected} parameters,
65 eval_def
= (env
, params
) ->
66 check_params
'def!', params, 2
68 #
Name is in the first parameter
, and is not evaluated.
70 if name.type
!= \symbol
71 runtime
-error "expected a symbol for the first parameter
72 of def
!, got a #
{name.type
}"
74 #
Evaluate the second parameter and store
75 # it under name in the env.
76 env.set name.value
, (eval_ast env
, params
[1])
79 eval_let
= (env
, params
) ->
80 check_params
'let*', params, 2
82 binding_list
= params
[0]
83 if binding_list.type not in
[\list \vector
]
84 runtime
-error "expected
1st parameter of
'let*' to
85 be a binding list
(or vector
),
86 got a #
{binding_list.type
}"
87 else if binding_list.value.length %
2 != 0
88 runtime
-error "binding list of
'let*' must have an even
91 # Make a new environment with the
92 # current environment as outer.
95 #
Evaluate all binding values in the
99 |
> each
([binding_name
, binding_value
]) ->
100 if binding_name.type
!= \symbol
101 runtime
-error "expected a symbol as binding name
,
102 got a #
{binding_name.type
}"
104 let_env.set binding_name.value
, (eval_ast let_env
, binding_value
)
106 # Defer evaluation of let
* body with TCO.
107 defer
-tco let_env
, params
[1]
110 eval_do
= (env
, params
) ->
111 if params.length
== 0
112 runtime
-error "
'do' expected at least one parameter"
114 [...rest
, last
-param
] = params
115 rest |
> each eval_ast env
116 defer
-tco env
, last
-param
119 eval_if
= (env
, params
) ->
121 runtime
-error "
'if' expected at least 2 parameters"
122 else if params.length
> 3
123 runtime
-error "
'if' expected at most 3 parameters"
125 cond
= eval_ast env
, params
[0]
127 defer
-tco env
, params
[1]
128 else if params.length
> 2
129 defer
-tco env
, params
[2]
131 {type
: \const
, value
: \nil
}
134 eval_fn
= (env
, params
) ->
135 check_params
'fn*', params, 2
137 if params
[0].type not in
[\list \vector
]
138 runtime
-error "
'fn*' expected first parameter to be a list or vector."
140 if not all
(.type
== \symbol
), params
[0].value
141 runtime
-error "
'fn*' expected only symbols in the parameters list."
143 binds
= params
[0].value |
> map
(.value
)
146 # Parse variadic bind.
148 [...rest
, amper
, name
] = binds
149 if amper
== '&' and name != '&'
153 if elem
-index
'&', binds
154 runtime
-error "
'fn*' invalid usage of variadic parameters."
156 if
(unique binds
).length
!= binds.length
157 runtime
-error "
'fn*' duplicate symbols in parameters list."
161 fn_instance
= (...values
) ->
162 if not vargs and values.length
!= binds.length
163 runtime
-error "function expected #
{binds.length
} parameters
,
164 got #
{values.length
}"
165 else if vargs and values.length
< binds.length
166 runtime
-error "function expected at least
167 #
{binds.length
} parameters
,
168 got #
{values.length
}"
170 #
Set binds to values in the new env.
173 for
[name
, value
] in
(zip binds
, values
)
174 fn_env.set name
, value
179 value
: values.slice binds.length
181 # Defer evaluation of the function body to TCO.
182 defer
-tco fn_env
, body
184 {type
: \function
, value
: fn_instance
}
187 eval_apply
= (env
, list
) ->
188 [fn
, ...args
] = list |
> map eval_ast env
189 if fn.type
!= \function
190 runtime
-error "#
{fn.value
} is not a function"
192 fn.value.apply env
, args
196 for symbol
, value of ns
197 repl_env.set symbol
, value
200 repl_env.set
'eval', do
202 value
: (ast
) -> eval_ast repl_env
, ast # or use current env?
(@
= this
).
209 |
> (ast
) -> pr_str ast
, print_readably
=true
217 (str "
(do "
(slurp f
) "
)"
)))))'
219 # Parse program arguments.
220 # The first two
(exe and core
-file
) are
, respectively
,
221 # the interpreter executable
(nodejs or lsc
) and the
222 # source file being executed
(stepX_
*.
(ls|js
)).
223 [exe
, core
-file
, mal
-file
, ...argv
] = process.argv
225 repl_env.set
'*ARGV*', do
227 value
: argv |
> map
(arg
) ->
232 rep "
(load
-file \"#
{mal
-file
}\"
)"
236 line
= readline.readline
'user> '
237 break if not line? or line
== ''
241 console.error message