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
) ->
16 is
-thruthy
= ({type
, value
}) ->
17 type
!= \const or value not in
[\nil \false
]
20 fmap
-ast
= (fn
, {type
, value
}: ast
) -->
21 {type
: type
, value
: fn value
}
24 eval_simple
= (env
, {type
, value
}: ast
) ->
26 | \symbol
=> env.get value
27 | \list
, \vector
=> ast |
> fmap
-ast map eval_ast env
28 | \map
=> ast |
> fmap
-ast Obj.map eval_ast env
32 eval_ast
= (env
, {type
, value
}: ast
) -->
35 return eval_simple env
, ast
36 else if value.length
== 0
39 result
= if value
[0].type
== \symbol
42 |
'def!' => eval_def env, params
43 |
'let*' => eval_let env, params
44 |
'do' => eval_do env, params
45 |
'if' => eval_if env, params
46 |
'fn*' => eval_fn env, params
47 | otherwise
=> eval_apply env
, value
51 if result.type
== \tco
53 {type
, value
}: ast
= result.ast
58 check_params
= (name
, params
, expected
) ->
59 if params.length
!= expected
60 runtime
-error "
'#{name}' expected #{expected} parameters,
64 eval_def
= (env
, params
) ->
65 check_params
'def!', params, 2
67 #
Name is in the first parameter
, and is not evaluated.
69 if name.type
!= \symbol
70 runtime
-error "expected a symbol for the first parameter
71 of def
!, got a #
{name.type
}"
73 #
Evaluate the second parameter and store
74 # it under name in the env.
75 env.set name.value
, (eval_ast env
, params
[1])
78 eval_let
= (env
, params
) ->
79 check_params
'let*', params, 2
81 binding_list
= params
[0]
82 if binding_list.type not in
[\list \vector
]
83 runtime
-error "expected
1st parameter of
'let*' to
84 be a binding list
(or vector
),
85 got a #
{binding_list.type
}"
86 else if binding_list.value.length %
2 != 0
87 runtime
-error "binding list of
'let*' must have an even
90 # Make a new environment with the
91 # current environment as outer.
94 #
Evaluate all binding values in the
98 |
> each
([binding_name
, binding_value
]) ->
99 if binding_name.type
!= \symbol
100 runtime
-error "expected a symbol as binding name
,
101 got a #
{binding_name.type
}"
103 let_env.set binding_name.value
, (eval_ast let_env
, binding_value
)
105 # Defer evaluation of let
* body with TCO.
106 defer
-tco let_env
, params
[1]
109 eval_do
= (env
, params
) ->
110 if params.length
== 0
111 runtime
-error "
'do' expected at least one parameter"
113 [...rest
, last
-param
] = params
114 rest |
> each eval_ast env
118 eval_if
= (env
, params
) ->
120 runtime
-error "
'if' expected at least 2 parameters"
121 else if params.length
> 3
122 runtime
-error "
'if' expected at most 3 parameters"
124 cond
= eval_ast env
, params
[0]
126 defer
-tco env
, params
[1]
127 else if params.length
> 2
128 defer
-tco env
, params
[2]
130 {type
: \const
, value
: \nil
}
133 eval_fn
= (env
, params
) ->
134 check_params
'fn*', params, 2
136 if params
[0].type not in
[\list \vector
]
137 runtime
-error "
'fn*' expected first parameter to be a list or vector."
139 if not all
(.type
== \symbol
), params
[0].value
140 runtime
-error "
'fn*' expected only symbols in the parameters list."
142 binds
= params
[0].value |
> map
(.value
)
145 # Parse variadic bind.
147 [...rest
, amper
, name
] = binds
148 if amper
== '&' and name != '&'
152 if elem
-index
'&', binds
153 runtime
-error "
'fn*' invalid usage of variadic parameters."
155 if
(unique binds
).length
!= binds.length
156 runtime
-error "
'fn*' duplicate symbols in parameters list."
160 fn_instance
= (...values
) ->
161 if not vargs and values.length
!= binds.length
162 runtime
-error "function expected #
{binds.length
} parameters
,
163 got #
{values.length
}"
164 else if vargs and values.length
< binds.length
165 runtime
-error "function expected at least
166 #
{binds.length
} parameters
,
167 got #
{values.length
}"
169 #
Set binds to values in the new env.
172 for
[name
, value
] in
(zip binds
, values
)
173 fn_env.set name
, value
178 value
: values.slice binds.length
180 # Defer evaluation of the function body to TCO.
181 defer
-tco fn_env
, body
183 {type
: \function
, value
: fn_instance
}
186 eval_apply
= (env
, list
) ->
187 [fn
, ...args
] = list |
> map eval_ast env
188 if fn.type
!= \function
189 runtime
-error "#
{fn.value
} is not a function
, got a #
{fn.type
}"
191 fn.value.apply env
, args
195 for symbol
, value of ns
196 repl_env.set symbol
, value
203 |
> (ast
) -> pr_str ast
, print_readably
=true
207 rep
'(def! not (fn* (x) (if x false true)))'
210 line
= readline.readline
'user> '
211 break if not line? or line
== ''
216 then console.error error.message
217 else console.error "
Error:"
, pr_str error
, print_readably
=true