Merge pull request #378 from asarhaddon/test-macro-not-changing-function
[jackhill/mal.git] / cs / types.cs
1 using System;
2 using System.Collections.Generic;
3 using Mal;
4
5 namespace Mal {
6 public class types {
7 //
8 // Exceptions/Errors
9 //
10 public class MalThrowable : Exception {
11 public MalThrowable() : base() { }
12 public MalThrowable(string msg) : base(msg) { }
13 }
14 public class MalError : MalThrowable {
15 public MalError(string msg) :base(msg) { }
16 }
17 public class MalContinue : MalThrowable { }
18
19 // Thrown by throw function
20 public class MalException : MalThrowable {
21 MalVal value;
22 //string Message;
23 public MalException(MalVal value) {
24 this.value = value;
25 }
26 public MalException(string value) :base(value) {
27 this.value = new MalString(value);
28 }
29 public MalVal getValue() { return value; }
30 }
31
32 //
33 // General functions
34 //
35
36 public static bool _equal_Q(MalVal a, MalVal b) {
37 Type ota = a.GetType(), otb = b.GetType();
38 if (!((ota == otb) ||
39 (a is MalList && b is MalList))) {
40 return false;
41 } else {
42 if (a is MalInt) {
43 return ((MalInt)a).getValue() ==
44 ((MalInt)b).getValue();
45 } else if (a is MalSymbol) {
46 return ((MalSymbol)a).getName() ==
47 ((MalSymbol)b).getName();
48 } else if (a is MalString) {
49 return ((MalString)a).getValue() ==
50 ((MalString)b).getValue();
51 } else if (a is MalList) {
52 if (((MalList)a).size() != ((MalList)b).size()) {
53 return false;
54 }
55 for (int i=0; i<((MalList)a).size(); i++) {
56 if (! _equal_Q(((MalList)a)[i], ((MalList)b)[i])) {
57 return false;
58 }
59 }
60 return true;
61 } else if (a is MalHashMap) {
62 var akeys = ((MalHashMap)a).getValue().Keys;
63 var bkeys = ((MalHashMap)b).getValue().Keys;
64 if (akeys.Count != bkeys.Count) {
65 return false;
66 }
67 foreach (var k in akeys) {
68 if (!_equal_Q(((MalHashMap)a).getValue()[k],
69 ((MalHashMap)b).getValue()[k])) {
70 return false;
71 }
72 }
73 return true;
74 } else {
75 return a == b;
76 }
77 }
78 }
79
80
81 public abstract class MalVal {
82 MalVal meta = Nil;
83 public virtual MalVal copy() {
84 return (MalVal)this.MemberwiseClone();
85 }
86
87 // Default is just to call regular toString()
88 public virtual string ToString(bool print_readably) {
89 return this.ToString();
90 }
91 public MalVal getMeta() { return meta; }
92 public MalVal setMeta(MalVal m) { meta = m; return this; }
93 public virtual bool list_Q() { return false; }
94 }
95
96 public class MalConstant : MalVal {
97 string value;
98 public MalConstant(string name) { value = name; }
99 public new MalConstant copy() { return this; }
100
101 public override string ToString() {
102 return value;
103 }
104 public override string ToString(bool print_readably) {
105 return value;
106 }
107 }
108
109 static public MalConstant Nil = new MalConstant("nil");
110 static public MalConstant True = new MalConstant("true");
111 static public MalConstant False = new MalConstant("false");
112
113 public class MalInt : MalVal {
114 Int64 value;
115 public MalInt(Int64 v) { value = v; }
116 public new MalInt copy() { return this; }
117
118 public Int64 getValue() { return value; }
119 public override string ToString() {
120 return value.ToString();
121 }
122 public override string ToString(bool print_readably) {
123 return value.ToString();
124 }
125 public static MalConstant operator <(MalInt a, MalInt b) {
126 return a.getValue() < b.getValue() ? True : False;
127 }
128 public static MalConstant operator <=(MalInt a, MalInt b) {
129 return a.getValue() <= b.getValue() ? True : False;
130 }
131 public static MalConstant operator >(MalInt a, MalInt b) {
132 return a.getValue() > b.getValue() ? True : False;
133 }
134 public static MalConstant operator >=(MalInt a, MalInt b) {
135 return a.getValue() >= b.getValue() ? True : False;
136 }
137 public static MalInt operator +(MalInt a, MalInt b) {
138 return new MalInt(a.getValue() + b.getValue());
139 }
140 public static MalInt operator -(MalInt a, MalInt b) {
141 return new MalInt(a.getValue() - b.getValue());
142 }
143 public static MalInt operator *(MalInt a, MalInt b) {
144 return new MalInt(a.getValue() * b.getValue());
145 }
146 public static MalInt operator /(MalInt a, MalInt b) {
147 return new MalInt(a.getValue() / b.getValue());
148 }
149 }
150
151 public class MalSymbol : MalVal {
152 string value;
153 public MalSymbol(string v) { value = v; }
154 public MalSymbol(MalString v) { value = v.getValue(); }
155 public new MalSymbol copy() { return this; }
156
157 public string getName() { return value; }
158 public override string ToString() {
159 return value;
160 }
161 public override string ToString(bool print_readably) {
162 return value;
163 }
164 }
165
166 public class MalString : MalVal {
167 string value;
168 public MalString(string v) { value = v; }
169 public new MalString copy() { return this; }
170
171 public string getValue() { return value; }
172 public override string ToString() {
173 return "\"" + value + "\"";
174 }
175 public override string ToString(bool print_readably) {
176 if (value.Length > 0 && value[0] == '\u029e') {
177 return ":" + value.Substring(1);
178 } else if (print_readably) {
179 return "\"" + value.Replace("\\", "\\\\")
180 .Replace("\"", "\\\"")
181 .Replace("\n", "\\n") + "\"";
182 } else {
183 return value;
184 }
185 }
186 }
187
188
189
190 public class MalList : MalVal {
191 public string start = "(", end = ")";
192 List<MalVal> value;
193 public MalList() {
194 value = new List<MalVal>();
195 }
196 public MalList(List<MalVal> val) {
197 value = val;
198 }
199 public MalList(params MalVal[] mvs) {
200 value = new List<MalVal>();
201 conj_BANG(mvs);
202 }
203
204 public List<MalVal> getValue() { return value; }
205 public override bool list_Q() { return true; }
206
207 public override string ToString() {
208 return start + printer.join(value, " ", true) + end;
209 }
210 public override string ToString(bool print_readably) {
211 return start + printer.join(value, " ", print_readably) + end;
212 }
213
214 public MalList conj_BANG(params MalVal[] mvs) {
215 for (int i = 0; i < mvs.Length; i++) {
216 value.Add(mvs[i]);
217 }
218 return this;
219 }
220
221 public int size() { return value.Count; }
222 public MalVal nth(int idx) {
223 return value.Count > idx ? value[idx] : Nil;
224 }
225 public MalVal this[int idx] {
226 get { return value.Count > idx ? value[idx] : Nil; }
227 }
228 public MalList rest() {
229 if (size() > 0) {
230 return new MalList(value.GetRange(1, value.Count-1));
231 } else {
232 return new MalList();
233 }
234 }
235 public virtual MalList slice(int start) {
236 return new MalList(value.GetRange(start, value.Count-start));
237 }
238 public virtual MalList slice(int start, int end) {
239 return new MalList(value.GetRange(start, end-start));
240 }
241
242 }
243
244 public class MalVector : MalList {
245 // Same implementation except for instantiation methods
246 public MalVector() :base() {
247 start = "[";
248 end = "]";
249 }
250 public MalVector(List<MalVal> val)
251 :base(val) {
252 start = "[";
253 end = "]";
254 }
255
256 public override bool list_Q() { return false; }
257
258 public override MalList slice(int start, int end) {
259 var val = this.getValue();
260 return new MalVector(val.GetRange(start, val.Count-start));
261 }
262 }
263
264 public class MalHashMap : MalVal {
265 Dictionary<string, MalVal> value;
266 public MalHashMap(Dictionary<string, MalVal> val) {
267 value = val;
268 }
269 public MalHashMap(MalList lst) {
270 value = new Dictionary<String, MalVal>();
271 assoc_BANG(lst);
272 }
273 public new MalHashMap copy() {
274 var new_self = (MalHashMap)this.MemberwiseClone();
275 new_self.value = new Dictionary<string, MalVal>(value);
276 return new_self;
277 }
278
279 public Dictionary<string, MalVal> getValue() { return value; }
280
281 public override string ToString() {
282 return "{" + printer.join(value, " ", true) + "}";
283 }
284 public override string ToString(bool print_readably) {
285 return "{" + printer.join(value, " ", print_readably) + "}";
286 }
287
288 public MalHashMap assoc_BANG(MalList lst) {
289 for (int i=0; i<lst.size(); i+=2) {
290 value[((MalString)lst[i]).getValue()] = lst[i+1];
291 }
292 return this;
293 }
294
295 public MalHashMap dissoc_BANG(MalList lst) {
296 for (int i=0; i<lst.size(); i++) {
297 value.Remove(((MalString)lst[i]).getValue());
298 }
299 return this;
300 }
301 }
302
303 public class MalAtom : MalVal {
304 MalVal value;
305 public MalAtom(MalVal value) { this.value = value; }
306 //public MalAtom copy() { return new MalAtom(value); }
307 public MalVal getValue() { return value; }
308 public MalVal setValue(MalVal value) { return this.value = value; }
309 public override string ToString() {
310 return "(atom " + printer._pr_str(value, true) + ")";
311 }
312 public override string ToString(Boolean print_readably) {
313 return "(atom " + printer._pr_str(value, print_readably) + ")";
314 }
315 }
316
317 public class MalFunc : MalVal {
318 Func<MalList, MalVal> fn = null;
319 MalVal ast = null;
320 Mal.env.Env env = null;
321 MalList fparams;
322 bool macro = false;
323 public MalFunc(Func<MalList, MalVal> fn) {
324 this.fn = fn;
325 }
326 public MalFunc(MalVal ast, Mal.env.Env env, MalList fparams,
327 Func<MalList, MalVal> fn) {
328 this.fn = fn;
329 this.ast = ast;
330 this.env = env;
331 this.fparams = fparams;
332 }
333
334 public override string ToString() {
335 if (ast != null) {
336 return "<fn* " + Mal.printer._pr_str(fparams,true) +
337 " " + Mal.printer._pr_str(ast, true) + ">";
338 } else {
339 return "<builtin_function " + fn.ToString() + ">";
340 }
341 }
342
343 public MalVal apply(MalList args) {
344 return fn(args);
345 }
346
347 public MalVal getAst() { return ast; }
348 public Mal.env.Env getEnv() { return env; }
349 public MalList getFParams() { return fparams; }
350 public Mal.env.Env genEnv(MalList args) {
351 return new Mal.env.Env(env, fparams, args);
352 }
353 public bool isMacro() { return macro; }
354 public void setMacro() { macro = true; }
355
356 }
357 }
358 }