Commit | Line | Data |
---|---|---|
a9c0e8ba JB |
1 | readline = 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 | ||
7 | repl_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 | ||
22 | is-symbol = ({type, value}: ast, name) -> | |
23 | type == \symbol and value == name | |
24 | ||
25 | ||
26 | list-to-pairs = (list) -> | |
27 | [0 to (list.length - 2) by 2] \ | |
28 | |> map (idx) -> [list[idx], list[idx+1]] | |
29 | ||
30 | ||
3181c695 | 31 | eval_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 |
40 | eval_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 | ||
53 | check_params = (name, params, expected) -> | |
54 | if params.length != expected | |
55 | throw new Error "#{name} expected #{expected} parameters, | |
56 | got #{params.length}" | |
57 | ||
58 | ||
59 | eval_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 | ||
73 | eval_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 | ||
104 | eval_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 | ||
111 | rep = (line) -> | |
112 | line | |
113 | |> read_str | |
114 | |> eval_ast repl_env | |
115 | |> pr_str | |
116 | ||
117 | ||
118 | loop | |
119 | line = readline.readline 'user> ' | |
120 | break if not line? or line == '' | |
121 | try | |
122 | console.log rep line | |
dd7a4f55 JM |
123 | catch error |
124 | if error.message | |
125 | then console.error error.message | |
126 | else console.error "Error:", pr_str error, print_readably=true |