fffde7d7 |
1 | defmodule Mal.Core do |
2 | def namespace do |
3 | %{ |
d34837dd |
4 | "+" => fn [a, b] -> a + b end, |
5 | "-" => fn [a, b] -> a - b end, |
6 | "*" => fn [a, b] -> a * b end, |
7 | "/" => fn [a, b] -> div(a, b) end, |
d34837dd |
8 | ">" => fn [a, b] -> a > b end, |
9 | "<" => fn [a, b] -> a < b end, |
10 | "<=" => fn [a, b] -> a <= b end, |
11 | ">=" => fn [a, b] -> a >= b end, |
1bc4ac2b |
12 | "concat" => &concat/1, |
13 | "=" => &equal/1, |
d34837dd |
14 | "list?" => &list?/1, |
15 | "empty?" => &empty?/1, |
a355b219 |
16 | "count" => &count/1, |
17 | "pr-str" => &pr_str/1, |
18 | "str" => &str/1, |
19 | "prn" => &prn/1, |
68ce2eea |
20 | "println" => &println/1, |
4115c430 |
21 | "slurp" => &slurp/1, |
0887f470 |
22 | "nth" => &nth/1, |
23 | "first" => &first/1, |
24 | "rest" => &rest/1, |
0a272a6b |
25 | "map" => &map/1, |
ed5a059b |
26 | "apply" => &apply/1, |
2b8ec8ad |
27 | "keyword" => &keyword/1, |
ed5a059b |
28 | "symbol?" => &symbol?/1, |
1bc4ac2b |
29 | "cons" => &cons/1, |
30 | "vector?" => &vector?/1, |
87e39e76 |
31 | "assoc" => &assoc/1, |
32 | "dissoc" => &dissoc/1, |
33 | "get" => &get/1, |
34 | "hash-map" => &Mal.Types.hash_map/1, |
1bc4ac2b |
35 | "sequential?" => fn arg -> vector?(arg) or list?(arg) end, |
36 | "vector" => fn list -> {:vector, list} end, |
2b8ec8ad |
37 | "keyword?" => fn [type] -> is_atom(type) end, |
87e39e76 |
38 | "map?" => fn [type] -> is_map(type) end, |
ed5a059b |
39 | "nil?" => fn [type] -> type == nil end, |
40 | "true?" => fn [type] -> type == true end, |
41 | "false?" => fn [type] -> type == false end, |
2b8ec8ad |
42 | "symbol" => fn [name] -> {:symbol, name} end, |
43 | "list" => fn args -> args end, |
0887f470 |
44 | "read-string" => fn [input] -> Mal.Reader.read_str(input) end, |
ed5a059b |
45 | "throw" => fn [arg] -> throw({:error, arg}) end, |
87e39e76 |
46 | "contains?" => fn [map, key] -> Map.has_key?(map, key) end, |
47 | "keys" => fn [map] -> Map.keys(map) end, |
48 | "vals" => fn [map] -> Map.values(map) end |
fffde7d7 |
49 | } |
50 | end |
51 | |
1bc4ac2b |
52 | defp convert_vector({:vector, list}), do: list |
53 | defp convert_vector(other), do: other |
54 | |
55 | def equal([a, b]) do |
56 | convert_vector(a) == convert_vector(b) |
57 | end |
58 | |
d34837dd |
59 | def list?([arg]) when is_list(arg), do: true |
60 | def list?([_arg]), do: false |
61 | |
62 | def empty?([[]]), do: true |
1bc4ac2b |
63 | def empty?([{:vector, []}]), do: true |
d34837dd |
64 | def empty?(_), do: false |
65 | |
66 | def count([arg]) when is_list(arg), do: length(arg) |
1bc4ac2b |
67 | def count([{:vector, arg}]), do: length(arg) |
d34837dd |
68 | def count(_), do: 0 |
a355b219 |
69 | |
70 | def pr_str(args) do |
71 | args |
72 | |> Enum.map(&Mal.Printer.print_str/1) |
73 | |> Enum.join(" ") |
74 | end |
75 | |
76 | def str(args) do |
77 | args |
78 | |> Enum.map(&(Mal.Printer.print_str(&1, false))) |
79 | |> Enum.join("") |
80 | end |
81 | |
82 | def prn(args) do |
83 | args |
84 | |> pr_str |
85 | |> IO.puts |
86 | nil |
87 | end |
88 | |
89 | def println(args) do |
90 | args |
91 | |> Enum.map(&(Mal.Printer.print_str(&1, false))) |
92 | |> Enum.join(" ") |
93 | |> IO.puts |
94 | nil |
95 | end |
68ce2eea |
96 | |
97 | def slurp([file_name]) do |
98 | case File.read(file_name) do |
99 | {:ok, content} -> content |
100 | {:error, :enoent} -> throw({:error, "can't find file #{file_name}"}) |
101 | {:error, :eisdir} -> throw({:error, "can't read directory #{file_name}"}) |
102 | {:error, :eaccess} -> throw({:error, "missing permissions #{file_name}"}) |
103 | {:error, reason} -> throw({:error, "can't read file #{file_name}, #{reason}"}) |
104 | end |
105 | end |
0887f470 |
106 | |
107 | def nth([list, index]) do |
1bc4ac2b |
108 | case Enum.at(convert_vector(list), index, :error) do |
0887f470 |
109 | :error -> throw({:error, "index out of bounds"}) |
110 | any -> any |
111 | end |
112 | end |
113 | |
1bc4ac2b |
114 | def first([{:vector, [head | tail]}]), do: head |
0887f470 |
115 | def first([[head | tail]]), do: head |
116 | def first(_), do: nil |
117 | |
1bc4ac2b |
118 | def rest([{:vector, list}]), do: do_rest(list) |
119 | def rest([list]), do: do_rest(list) |
120 | |
121 | defp do_rest([head | tail]), do: tail |
122 | defp do_rest([]), do: [] |
0a272a6b |
123 | |
ed5a059b |
124 | def map([{_function_type, function}, list]), do: do_map(function, list) |
0a272a6b |
125 | def map([function, list]), do: do_map(function, list) |
126 | |
127 | defp do_map(function, list) do |
1bc4ac2b |
128 | convert_vector(list) |
129 | |> Enum.map(fn arg -> function.([arg]) end) |
0a272a6b |
130 | end |
ed5a059b |
131 | |
132 | def apply([{_function_type, function} | tail]), do: do_apply(function, tail) |
133 | def apply([function | tail]), do: do_apply(function, tail) |
134 | |
135 | def do_apply(function, tail) do |
1bc4ac2b |
136 | [list | reversed_args] = Enum.reverse(tail) |
137 | args = Enum.reverse(reversed_args) |
138 | func_args = Enum.concat(args, convert_vector(list)) |
ed5a059b |
139 | function.(func_args) |
140 | end |
141 | |
142 | def symbol?([{:symbol, _}]), do: true |
143 | def symbol?(_), do: false |
2b8ec8ad |
144 | |
1bc4ac2b |
145 | def vector?([{:vector, _}]), do: true |
146 | def vector?(_), do: false |
147 | |
2b8ec8ad |
148 | def keyword([atom]) when is_atom(atom), do: atom |
149 | def keyword([atom]), do: String.to_atom(atom) |
1bc4ac2b |
150 | |
151 | def cons([prepend, {:vector, list}]), do: [prepend | list] |
152 | def cons([prepend, list]), do: [prepend | list] |
153 | |
154 | def concat(args) do |
155 | Enum.map(args, &convert_vector/1) |
156 | |> Enum.concat |
157 | end |
87e39e76 |
158 | |
159 | def assoc([hash_map | pairs]) do |
160 | Map.merge(hash_map, Mal.Types.hash_map(pairs)) |
161 | end |
162 | |
163 | def dissoc([hash_map | keys]) do |
164 | Map.drop(hash_map, keys) |
165 | end |
166 | |
167 | def get([map, key]) when is_map(map), do: Map.get(map, key, nil) |
168 | def get([_map, _key]), do: nil |
fffde7d7 |
169 | end |