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 is
-thruthy
= ({type
, value
}) ->
11 type
!= \const or value not in
[\nil \false
]
14 fmap
-ast
= (fn
, {type
, value
}: ast
) -->
15 {type
: type
, value
: fn value
}
18 eval_simple
= (env
, {type
, value
}: ast
) ->
20 | \symbol
=> env.get value
21 | \list
, \vector
=> ast |
> fmap
-ast map eval_ast env
22 | \map
=> ast |
> fmap
-ast Obj.map eval_ast env
26 eval_ast
= (env
, {type
, value
}: ast
) -->
27 if type
!= \list then eval_simple env
, ast
28 else if value.length
== 0 then ast
29 else if value
[0].type
== \symbol
32 |
'def!' => eval_def env, params
33 |
'let*' => eval_let env, params
34 |
'do' => eval_do env, params
35 |
'if' => eval_if env, params
36 |
'fn*' => eval_fn env, params
37 | otherwise
=> eval_apply env
, value
42 check_params
= (name
, params
, expected
) ->
43 if params.length
!= expected
44 runtime
-error "
'#{name}' expected #{expected} parameters,
48 eval_def
= (env
, params
) ->
49 check_params
'def!', params, 2
51 #
Name is in the first parameter
, and is not evaluated.
53 if name.type
!= \symbol
54 runtime
-error "expected a symbol for the first parameter
55 of def
!, got a #
{name.type
}"
57 #
Evaluate the second parameter and store
58 # it under name in the env.
59 env.set name.value
, (eval_ast env
, params
[1])
62 eval_let
= (env
, params
) ->
63 check_params
'let*', params, 2
65 binding_list
= params
[0]
66 if binding_list.type not in
[\list \vector
]
67 runtime
-error "expected
1st parameter of
'let*' to
68 be a binding list
(or vector
),
69 got a #
{binding_list.type
}"
70 else if binding_list.value.length %
2 != 0
71 runtime
-error "binding list of
'let*' must have an even
74 # Make a new environment with the
75 # current environment as outer.
78 #
Evaluate all binding values in the
82 |
> each
([binding_name
, binding_value
]) ->
83 if binding_name.type
!= \symbol
84 runtime
-error "expected a symbol as binding name
,
85 got a #
{binding_name.type
}"
87 let_env.set binding_name.value
, (eval_ast let_env
, binding_value
)
89 #
Evaluate the
'body' of let* with the new environment.
90 eval_ast let_env
, params
[1]
93 eval_do
= (env
, params
) ->
95 runtime
-error "
'do' expected at least one parameter"
97 params |
> map eval_ast env |
> last
100 eval_if
= (env
, params
) ->
102 runtime
-error "
'if' expected at least 2 parameters"
103 else if params.length
> 3
104 runtime
-error "
'if' expected at most 3 parameters"
106 cond
= eval_ast env
, params
[0]
108 eval_ast env
, params
[1]
109 else if params.length
> 2
110 eval_ast env
, params
[2]
112 {type
: \const
, value
: \nil
}
115 eval_fn
= (env
, params
) ->
116 check_params
'fn*', params, 2
118 if params
[0].type not in
[\list \vector
]
119 runtime
-error "
'fn*' expected first parameter to be a list or vector."
121 if not all
(.type
== \symbol
), params
[0].value
122 runtime
-error "
'fn*' expected only symbols in the parameters list."
124 binds
= params
[0].value |
> map
(.value
)
127 # Parse variadic bind.
129 [...rest
, amper
, name
] = binds
130 if amper
== '&' and name != '&'
134 if elem
-index
'&', binds
135 runtime
-error "
'fn*' invalid usage of variadic parameters."
137 if
(unique binds
).length
!= binds.length
138 runtime
-error "
'fn*' duplicate symbols in parameters list."
142 fn_instance
= (...values
) ->
143 if not vargs and values.length
!= binds.length
144 runtime
-error "function expected #
{binds.length
} parameters
,
145 got #
{values.length
}"
146 else if vargs and values.length
< binds.length
147 runtime
-error "function expected at least
148 #
{binds.length
} parameters
,
149 got #
{values.length
}"
151 #
Set binds to values in the new env.
154 for
[name
, value
] in
(zip binds
, values
)
155 fn_env.set name
, value
160 value
: values.slice binds.length
162 #
Evaluate the function body with the new environment.
163 eval_ast fn_env
, body
165 {type
: \function
, value
: fn_instance
}
168 eval_apply
= (env
, list
) ->
169 [fn
, ...args
] = list |
> map eval_ast env
170 if fn.type
!= \function
171 runtime
-error "#
{fn.value
} is not a function"
173 fn.value.apply env
, args
177 for symbol
, value of ns
178 repl_env.set symbol
, value
185 |
> (ast
) -> pr_str ast
, print_readably
=true
189 line
= readline.readline
'user> '
190 break if not line? or line
== ''
194 console.error message