Step 4 of Make-a-Lisp for Erlang
[jackhill/mal.git] / erlang / src / env.erl
CommitLineData
2cc3804b
NF
1%%%
2%%% Environement
3%%%
4
5-module(env).
6
a61ea75a 7-export([new/0, new/1, bind/3, set/3, get/2, fallback/2]).
583a62df 8
a61ea75a 9-record(env, {outer, data, fallback=undefined}).
583a62df
NF
10
11%%
12%% Public API
13%%
14
a61ea75a
NF
15-spec new() -> Env
16 when Env :: #env{}.
17new() ->
18 new(undefined).
19
583a62df
NF
20-spec new(Outer) -> Env
21 when Outer :: #env{},
22 Env :: #env{}.
583a62df
NF
23new(Outer) ->
24 #env{outer=Outer, data=#{}}.
25
a61ea75a
NF
26-spec bind(Env1, Names, Values) -> Env2
27 when Env1 :: #env{},
28 Names :: [term()],
29 Values :: [term()],
30 Env2 :: #env{}.
31bind(Env, [], []) ->
32 Env;
33bind(Env, [{symbol, "&"},Name], Values) ->
34 set(Env, Name, {list, Values});
35bind(Env, [Name|Ntail], [Value|Vtail]) ->
36 bind(set(Env, Name, Value), Ntail, Vtail).
37
583a62df
NF
38-spec set(Env1, Key, Value) -> Env2
39 when Env1 :: #env{},
40 Key :: {symbol, term()},
41 Value :: term(),
42 Env2 :: #env{}.
43set(Env, Key, Value) ->
44 case Key of
45 {symbol, Name} ->
46 Map = maps:put(Name, Value, Env#env.data),
47 #env{outer=Env#env.outer, data=Map};
48 _ -> throw("env:set/3 called with non-symbol key")
49 end.
50
51-spec get(Env, Key) -> Value
52 when Env :: #env{},
53 Key :: {symbol, term()},
54 Value :: term().
55get(Env, Key) ->
56 case Key of
57 {symbol, Name} ->
58 case find(Env, Name) of
59 nil -> throw(io_lib:format("'~s' not found", [Name]));
60 E -> maps:get(Name, E#env.data)
61 end;
62 _ -> throw("env:get/2 called with non-symbol key")
63 end.
64
a61ea75a
NF
65-spec fallback(Env1, Fallback) -> Env2
66 when Env1 :: #env{},
67 Fallback :: #env{},
68 Env2 :: #env{}.
69fallback(Env, Fallback) ->
70 #env{outer=Env#env.outer, data=Env#env.data, fallback=Fallback}.
71
583a62df
NF
72%%
73%% Internal functions
74%%
75
76find(Env, Name) ->
77 case maps:is_key(Name, Env#env.data) of
78 true -> Env;
79 false ->
80 case Env#env.outer of
a61ea75a
NF
81 undefined ->
82 case Env#env.fallback of
83 undefined -> nil;
84 Fallback -> find(Fallback, Name)
85 end;
583a62df
NF
86 Outer -> find(Outer, Name)
87 end
88 end.