Livescript: update Dockerfile to support Travis.
[jackhill/mal.git] / livescript / step3_env.ls
CommitLineData
a9c0e8ba
JB
1readline = require './node_readline'
2{id, map, each} = require 'prelude-ls'
3{read_str} = require './reader'
4{pr_str} = require './printer'
5{Env} = require './env'
6
7repl_env = new Env null, do
8 '+':
9 type: \function
10 value: (a, b) -> {type: \int, value: a.value + b.value}
11 '-':
12 type: \function
13 value: (a, b) -> {type: \int, value: a.value - b.value}
14 '*':
15 type: \function
16 value: (a, b) -> {type: \int, value: a.value * b.value}
17 '/':
18 type: \function
19 value: (a, b) -> {type: \int, value: parseInt(a.value / b.value)}
20
21
22is-symbol = ({type, value}: ast, name) ->
23 type == \symbol and value == name
24
25
26list-to-pairs = (list) ->
27 [0 to (list.length - 2) by 2] \
28 |> map (idx) -> [list[idx], list[idx+1]]
29
30
3181c695 31eval_simple = (env, {type, value}: ast) ->
a9c0e8ba
JB
32 switch type
33 | \symbol => env.get value
3181c695
JB
34 | \list, \vector => do
35 type: type
a9c0e8ba 36 value: value |> map eval_ast env
3181c695
JB
37 | otherwise => ast
38
a9c0e8ba 39
3181c695
JB
40eval_ast = (env, {type, value}: ast) -->
41 if type != \list then eval_simple env, ast
42 else if value.length == 0 then ast
43 else if value[0].type == \symbol
44 params = value[1 to]
45 switch value[0].value
46 | 'def!' => eval_def env, params
47 | 'let*' => eval_let env, params
48 | otherwise => eval_apply env, value
49 else
50 eval_apply env, value
51
52
53check_params = (name, params, expected) ->
54 if params.length != expected
55 throw new Error "#{name} expected #{expected} parameters,
56 got #{params.length}"
57
58
59eval_def = (env, params) ->
60 check_params 'def!', params, 2
61
62 # Name is in the first parameter, and is not evaluated.
63 name = params[0]
64 if name.type != \symbol
65 throw new Error "expected a symbol for the first parameter
66 of def!, got a #{name.type}"
67
68 # Evaluate the second parameter and store
69 # it under name in the env.
70 env.set name.value, (eval_ast env, params[1])
71
72
73eval_let = (env, params) ->
74 check_params 'let*', params, 2
75
76 binding_list = params[0]
77 if binding_list.type not in [\list \vector]
78 throw new Error "expected 1st parameter of let* to
79 be a binding list (or vector),
80 got a #{binding_list.type}"
81 else if binding_list.value.length % 2 != 0
82 throw new Error "binding list of let* must have an even
83 number of parameters"
84
85 # Make a new environment with the
86 # current environment as outer.
87 let_env = new Env env
88
89 # Evaluate all binding values in the
90 # new environment.
91 binding_list.value
92 |> list-to-pairs
93 |> each ([binding_name, binding_value]) ->
94 if binding_name.type != \symbol
95 throw new Error "expected a symbol as binding name,
96 got a #{binding_name.type}"
97
98 let_env.set binding_name.value, (eval_ast let_env, binding_value)
99
100 # Evaluate the 'body' of let* with the new environment.
101 eval_ast let_env, params[1]
102
103
104eval_apply = (env, list) ->
105 [fn, ...args] = list |> map eval_ast env
106 if fn.type != \function
107 throw new Error fn.value, ' is not a function'
108 fn.value.apply env, args
a9c0e8ba
JB
109
110
111rep = (line) ->
112 line
113 |> read_str
114 |> eval_ast repl_env
115 |> pr_str
116
117
118loop
119 line = readline.readline 'user> '
120 break if not line? or line == ''
121 try
122 console.log rep line
123 catch {message}
124 console.error message