Step 4 of Make-a-Lisp for Erlang
[jackhill/mal.git] / erlang / src / core.erl
CommitLineData
7e823504
NF
1%%%
2%%% Core functions
3%%%
4
5-module(core).
6-compile(export_all).
7
a61ea75a
NF
8count([Args]) ->
9 case Args of
10 {list, List} -> {integer, length(List)};
11 {vector, List} -> {integer, length(List)};
12 nil -> {integer, 0};
13 _ -> {error, "count called on non-sequence"}
14 end;
15count([]) ->
16 {error, "count called with no arguments"};
17count(_) ->
18 {error, "count expects one list argument"}.
19
20empty_q([Args]) ->
21 case Args of
22 {list, List} -> length(List) == 0;
23 {vector, List} -> length(List) == 0;
24 _ -> {error, "empty? called on non-sequence"}
25 end;
26empty_q([]) ->
27 {error, "empty? called with no arguments"};
28empty_q(_) ->
29 {error, "empty? expects one list argument"}.
30
31equal_q(Args) ->
32 case Args of
33 [nil, nil] -> true;
34 [true, true] -> true;
35 [false, false] -> true;
36 [{integer, I}, {integer, J}] -> I == J;
37 [{string, S}, {string, T}] -> S == T;
38 [{keyword, K}, {keyword, J}] -> K == J;
39 [{symbol, S}, {symbol, T}] -> S == T;
40 [{list, L1}, {list, L2}] -> L1 == L2;
41 [{vector, L1}, {vector, L2}] -> L1 == L2;
42 [{list, L1}, {vector, L2}] -> L1 == L2;
43 [{vector, L1}, {list, L2}] -> L1 == L2;
44 [{map, M1}, {map, M2}] -> M1 == M2;
45 [{closure, _C1}, {closure, _C2}] -> false;
46 [{function, _F1}, {function, _F2}] -> false;
47 [_A, _B] -> false;
48 _ -> {error, "equal? expects two arguments"}
49 end.
7e823504 50
583a62df 51int_op(F, [A0,A1]) ->
7e823504
NF
52 case A0 of
53 {integer, I0} ->
54 case A1 of
55 {integer, I1} ->
a61ea75a 56 {integer, F(I0, I1)};
7e823504
NF
57 _ -> {error, "second argument must be an integer"}
58 end;
59 _ -> {error, "first argument must be an integer"}
60 end;
61int_op(_F, _L) ->
62 {error, "must have two arguments"}.
63
64int_add(Args) ->
a61ea75a 65 int_op(fun(I, J) -> I + J end, Args).
7e823504
NF
66
67int_sub(Args) ->
a61ea75a 68 int_op(fun(I, J) -> I - J end, Args).
7e823504
NF
69
70int_mul(Args) ->
a61ea75a 71 int_op(fun(I, J) -> I * J end, Args).
7e823504
NF
72
73int_div(Args) ->
a61ea75a
NF
74 int_op(fun(I, J) -> I div J end, Args).
75
76bool_op(F, [A0,A1]) ->
77 case A0 of
78 {integer, I0} ->
79 case A1 of
80 {integer, I1} ->
81 % the true or false is our return value
82 F(I0, I1);
83 _ -> {error, "second argument must be an integer"}
84 end;
85 _ -> {error, "first argument must be an integer"}
86 end;
87bool_op(_F, _L) ->
88 {error, "must have two arguments"}.
89
90bool_lt(Args) ->
91 bool_op(fun(I, J) -> I < J end, Args).
92
93bool_lte(Args) ->
94 bool_op(fun(I, J) -> I =< J end, Args).
95
96bool_gt(Args) ->
97 bool_op(fun(I, J) -> I > J end, Args).
98
99bool_gte(Args) ->
100 bool_op(fun(I, J) -> I >= J end, Args).
101
102pr_str(Args) ->
103 {string, printer:pr_list(Args, "", "", " ", true)}.
104
105str(Args) ->
106 {string, printer:pr_list(Args, "", "", "", false)}.
107
108prn(Args) ->
109 io:format("~s~n", [printer:pr_list(Args, "", "", " ", true)]),
110 nil.
111
112println(Args) ->
113 io:format("~s~n", [printer:pr_list(Args, "", "", " ", false)]),
114 nil.
7e823504
NF
115
116ns() ->
a61ea75a
NF
117 Builtins = #{
118 "*" => fun int_mul/1,
119 "+" => fun int_add/1,
120 "-" => fun int_sub/1,
121 "/" => fun int_div/1,
122 "<" => fun bool_lt/1,
123 "<=" => fun bool_lte/1,
124 "=" => fun equal_q/1,
125 ">" => fun bool_gt/1,
126 ">=" => fun bool_gte/1,
127 "count" => fun count/1,
128 "empty?" => fun empty_q/1,
129 "list" => fun types:list/1,
130 "list?" => fun types:list_p/1,
131 "pr-str" => fun pr_str/1,
132 "println" => fun println/1,
133 "prn" => fun prn/1,
134 "str" => fun str/1
135 },
136 SetEnv = fun(K, V, AccIn) ->
137 env:set(AccIn, {symbol, K}, types:func(V))
138 end,
139 maps:fold(SetEnv, env:new(undefined), Builtins).