Commit my random junk
[sandbox] / mini / mini.py
1 from __future__ import print_function
2
3 import re
4 import traceback
5
6 class MiniObject(object):
7     def __init__(self, py_object, **meta):
8         (   "The following python types map to the following mini types:\n"
9             "   bool -> boolean\n"
10             "   str -> string\n"
11             "   int -> integer\n"
12             "   float -> float\n"
13             "   tuple -> list (may contain different types)\n"
14             "   list -> vector (may only contain one type)\n"
15             "   dict -> map\n"
16             "   MiniSymbol -> symbol\n"
17             "   Pair -> pair"
18             "mini vectors and maps should be treated as though immutable"
19             "s-expressions should be parsed as tuples"
20         )
21         self.py_object = py_object
22         self.meta = meta
23
24     def __repr__(self):
25         if self.py_object == None:
26             return 'nil'
27
28         if isinstance(self.py_object,bool):
29             return 'true' if self.py_object else 'false'
30
31         return repr(self.py_object)
32
33     def __str__(self):
34         if isinstance(self.py_object,str):
35             return self.py_object
36
37         return repr(self)
38
39 class Identifier(object):
40     def __init__(self,symbol,**kwargs):
41         assert isinstance(symbol,str)
42
43         self.symbol = symbol
44
45         self.start = kwargs.get('start')
46         self.end = kwargs.get('end')
47
48     def __repr__(self):
49         return '<identifier {}>'.format(self.symbol)
50
51 def is_identifier(mini_object):
52     assert isinstance(mini_object, MiniObject)
53     if isinstance(mini_object.py_object, Identifier):
54         return TRUE
55     return FALSE
56
57 SYMBOLS = {}
58
59 class MiniSymbol(object):
60     def __init__(self,string):
61         self.string = string
62
63     def __eq__(self,other):
64         return self is other
65
66     def __repr__(self):
67         return '<symbol :{}>'.format(self.string)
68
69 class MiniPair(object):
70     def __init__(self, car, cdr):
71         assert isinstance(car, MiniObject)
72         assert isinstance(cdr, MiniObject)
73
74         self.car = car
75         self.cdr = cdr
76
77     def __repr__(self):
78         return '<pair {}, {}>'.format(self.car, self.cdr)
79
80 def evaluate_arguments(arguments_cons_list, environment):
81     if arguments_cons_list == NIL:
82         return NIL
83
84     return cons(
85         evaluate(car(arguments_cons_list), environment),
86         evaluate_arguments(cdr(arguments_cons_list), environment))
87
88 class MiniEnvironment(MiniObject):
89     'This acts like a dict in Python code and a cons-dict in mini code'
90     def __init__(self):
91         super(self.__class__, self).__init__(None)
92
93     def __getitem__(self,key):
94         assert isinstance(key,str)
95         key_symbol = create_symbol(key)
96
97         return cons_dict_get(self,key_symbol)
98
99     def __setitem__(self,key,value):
100         assert isinstance(key,str)
101         key_symbol = create_symbol(key)
102
103         assert isinstance(value, MiniObject)
104         self.py_object = cons_dict_set(self,key_symbol,value).py_object
105
106     def __contains__(self,key):
107         assert isinstance(key,str)
108         key_symbol = create_symbol(key)
109
110         return cons_dict_has_key(self,key_symbol) == TRUE
111
112     def get(self,key):
113         assert isinstance(key,str)
114
115         if key in self:
116             return self[key]
117
118         return None
119
120 def dict_to_environment(dictionary):
121     result = MiniEnvironment()
122
123     for key,value in dictionary.iteritems():
124         result[key] = value
125
126     return result
127
128 class MiniApplicative(object):
129     def __init__(self, operative):
130         assert callable(operative)
131         self.operative = operative
132         
133     def __call__(self, pattern, environment):
134         assert isinstance(pattern, MiniObject)
135
136         return self.operative(pattern, environment)
137
138 class MiniWrapper(object):
139     def __init__(self, operative):
140         assert isinstance(operative,MiniObject)
141         assert isinstance(operative.py_object, MiniApplicative) or isinstance(operative.py_object, MiniWrapper)
142
143         self.operative = operative
144
145     def __call__(self, pattern, environment):
146         assert isinstance(pattern, MiniObject)
147
148         return self.operative.py_object(evaluate_arguments(pattern, environment), environment)
149
150     def __repr__(self):
151         return "<wrapper {}>".format(repr(self.operative))
152
153 def wrap(thing):
154     return MiniObject(MiniWrapper(thing))
155
156 def unwrap(thing):
157     if isinstance(thing.py_object, MiniWrapper):
158         return thing.py_object.operative
159
160     raise Exception('UnwrapError')
161
162 def create_symbol(string,**kwargs):
163     if string in SYMBOLS:
164         return SYMBOLS[string]
165
166     k = MiniObject(MiniSymbol(string), **kwargs)
167     SYMBOLS[string] = k
168     return k
169
170 def create_cons_collection(py_collection):
171     result = NIL
172
173     for item in reversed(py_collection):
174         result = MiniObject(MiniPair(item, result))
175
176     return result
177
178 def cons_collection_to_py_collection(cons_collection):
179     while cons_collection != NIL:
180         yield car(cons_collection)
181         cons_collection = cdr(cons_collection)
182
183 token_regex = re.compile(r'''(?mx)
184     (\s*|\#.*?\n)*(?:
185     (?P<open_parenthese>\()|
186     (?P<close_parenthese>\))|
187     (?P<number>\-?\d+\.\d+|\-?\d+)|
188     (?P<string>"[^"]*")|
189     (?P<identifier>[_A-Za-z\?\-\+\*/=\>\<]+)|
190     (?P<symbol>\:[_A-Za-z\?\-\+\*/=\>\<]*)
191     )''')
192
193 def parse_all(source):
194     def parse(matches, index_holder):
195         match = matches[index_holder[0]]
196         index_holder[0] += 1
197
198         if match.group('open_parenthese'):
199             r = []
200
201             while index_holder[0] < len(matches) and not matches[index_holder[0]].group('close_parenthese'):
202                 r.append(parse(matches, index_holder))
203
204             if index_holder[0] == len(matches):
205                 raise Exception('Unmatched parenthese (')
206
207             index_holder[0] += 1
208             return create_cons_collection(r)
209
210         if match.group('close_parenthese'):
211             raise Exception("Unmatched parenthese )")
212
213         if match.group('number'):
214             v = float(match.group('number'))
215             if v.is_integer(): v = int(v)
216
217             return MiniObject(
218                 v,
219                 start = match.start('number'),
220                 end = match.end('number'))
221
222         if match.group('string'):
223             return MiniObject(
224                 match.group('string')[1:-1],
225                 start = match.start('string'),
226                 end = match.end('string'))
227
228         if match.group('identifier'):
229             return MiniObject(Identifier(
230                 match.group('identifier'),
231                 start = match.start('identifier'),
232                 end = match.end('identifier')))
233
234         if match.group('symbol'):
235             return create_symbol(
236                 match.group('symbol')[1:],
237                 start = match.start('symbol'),
238                 end = match.end('symbol'))
239
240         assert False, "I'm not sure how this happened"
241
242     def parse_all_internal(matches, index_holder):
243         if index_holder[0] == len(matches):
244             return NIL
245
246         parsed_atom = parse(matches, index_holder)
247
248         return cons(parsed_atom, parse_all_internal(matches, index_holder))
249
250     matches = list(token_regex.finditer(source))
251     match_index_wrapped = [0]
252
253     return parse_all_internal(matches, match_index_wrapped)
254
255 NIL = MiniObject(None)
256
257 class Boolean(MiniObject):
258     def __init__(self, py_object, **kwargs):
259         super(Boolean,self).__init__(py_object, **kwargs)
260
261 TRUE = Boolean(True)
262 FALSE = Boolean(False)
263
264 def is_number(arg):
265     if isinstance(arg, float):
266         return True
267
268     # isinstance(True, int) returns True
269     return isinstance(arg, int) and not isinstance(arg, bool)
270
271 def py_to_mini(py_object):
272     assert callable(py_object)
273     
274     def wrapped(pattern, environment):
275         result = py_object(*cons_collection_to_py_collection(pattern))
276
277         if is_number(result) or isinstance(result,MiniPair):
278             return MiniObject(result)
279
280         if isinstance(result,str):
281             return MiniObject(result)
282
283         return {
284             True    : TRUE,
285             False   : FALSE,
286             None    : NIL,
287         }.get(result, result)
288
289     return MiniObject(MiniWrapper(MiniObject(MiniApplicative(wrapped))))
290
291 def apply(applicative, pattern, environment):
292     assert isinstance(applicative, MiniObject)
293
294     return applicative.py_object(pattern, environment)
295
296 def evaluate(expression, environment):
297     assert isinstance(expression, MiniObject)
298
299     if isinstance(expression.py_object, str) or is_number(expression.py_object):
300         return expression
301
302     if isinstance(expression.py_object, MiniSymbol):
303         return expression
304
305     if isinstance(expression.py_object, MiniPair):
306         applicative = evaluate(car(expression), environment)
307         arguments = cdr(expression)
308
309         assert isinstance(applicative, MiniObject)
310         assert isinstance(arguments, MiniObject)
311
312         if isinstance(applicative.py_object, MiniApplicative) or isinstance(applicative.py_object, MiniWrapper):
313             return apply(applicative, arguments, environment)
314
315         raise Exception("Expected applicative, got {}".format(applicative.py_object))
316
317     if isinstance(expression.py_object, Identifier):
318         parent_symbol = create_symbol('__parent__')
319
320         while environment != None:
321             if cons_dict_has_key(environment, create_symbol(expression.py_object.symbol)) == TRUE:
322                 return cons_dict_get(environment, create_symbol(expression.py_object.symbol))
323     
324             if cons_dict_has_key(environment, parent_symbol) == TRUE:
325                 environment = cons_dict_get(environment, create_symbol('__parent__'))
326             else: 
327                 raise Exception('UndefinedIdentifierError: Undefined identifier {}'.format(expression.py_object.symbol))
328
329 def length(string):
330     assert isinstance(string, MiniObject)
331
332     if isinstance(string.py_object, str):
333         return len(string.py_object)
334
335     raise Exception("TypeError")
336
337 def concatenate(l,r):
338     # TODO Implement ropes: http://citeseer.ist.psu.edu/viewdoc/download?doi=10.1.1.14.9450&rep=rep1&type=pdf
339     # TODO Apply this to other collection types
340     if isinstance(l.py_object,str) and isinstance(r.py_object, str):
341         return MiniObject(l.py_object + r.py_object)
342
343     raise Exception('TypeError')
344
345 def is_integer(arg):
346     return isinstance(arg, int) and not isinstance(arg, bool)
347
348 def slice(string, start, end):
349     if not isinstance(string.py_object, str):
350         raise Exception('TypeError')
351
352     py_string = string.py_object
353
354     if is_integer(start.py_object):
355         py_start = start.py_object
356
357     elif start.py_object == None:
358         py_start = 0
359
360     else:
361         raise Exception('TypeError')
362
363     if is_integer(end.py_object):
364         py_end = end.py_object
365
366     elif end.py_object == None:
367         py_end = len(py_string)
368
369     else:
370         raise Exception('TypeError')
371
372     return MiniObject(py_string[py_start:py_end])
373
374 def _assert(pattern, environment):
375     def assert_internal(*arguments):
376         if len(arguments) == 0:
377             raise Exception("ArgumentError: assert expects 1 or more arguments, received none")
378         
379         if len(arguments) == 1:
380             description = 'assertion failed'
381             assertion = arguments
382         
383         else:
384             description = arguments[0].py_object
385             assertion = arguments[1:]
386         
387         if not isinstance(assertion[-1].py_object, bool):
388             raise Exception("TypeError: `assert` expected Boolean assertion but received {} {}".format(type(assertion[-1].py_object), assertion[-1]))
389         
390         if assertion[-1] is TRUE:
391             return None
392         
393         if assertion[-1] is FALSE:
394             raise Exception("AssertionError: {}".format(description))
395         
396         assert False
397
398     # Execute in nested scope
399     return py_to_mini(assert_internal).py_object(pattern, nest(environment))
400
401 def throws(pattern, environment):
402     if cons_collection_len(pattern) != 2:
403         raise Exception("throws? expects 2 argument, received {}".format(len(pattern)))
404
405     expression = car(pattern)
406     exception = evaluate(car(cdr(pattern)), environment)
407
408     if not isinstance(exception.py_object, str):
409         raise Exception('throws? expects a string as the second argument')
410
411     try:
412         evaluate(expression, environment)
413         return FALSE
414
415     except Exception as e:
416         if ':' in e.message:
417             exception_type, message = e.message.split(':',1)
418         else:
419             exception_type = e.message
420
421         if exception_type == exception.py_object:
422             return TRUE
423
424         raise
425
426 def _not(argument):
427     if not isinstance(argument, Boolean):
428         raise Exception('TypeError: Expected Boolean but received {}'.format(type(argument)))
429
430     if argument == TRUE:
431         return FALSE
432
433     if argument == FALSE:
434         return TRUE
435
436     assert False
437
438 def evaluate_expressions(expressions, environment):
439     result = NIL
440
441     while expressions != NIL:
442         result = evaluate(car(expressions), environment)
443         expressions = cdr(expressions)
444
445     return result
446
447 def cons_collection_len(cons_collection):
448     result = 0
449
450     while cons_collection != NIL:
451         result += 1
452         cons_collection = cdr(cons_collection)
453
454     return result
455
456 def define(pattern, environment):
457     if cons_collection_len(pattern) < 2:
458         raise Exception('DefineError: `define` expected two arguments, received {}'.format(cons_collection_len(pattern)))
459
460     head = car(pattern)
461     body = cdr(pattern)
462
463     if isinstance(head.py_object, Identifier):
464         identifier = head.py_object.symbol
465
466         if is_defined(head, environment) == TRUE:
467             raise Exception('AlreadyDefinedError: the identifier {} is already defined'.format(identifier))
468     
469         environment[identifier] = evaluate_expressions(body, environment)
470     
471         return NIL
472     
473     elif isinstance(head.py_object, MiniPair):
474         raise Exception('NotImplementedError: Defining patterns is not yet implemented')
475
476     else:
477         raise Exception("DefineError")
478
479 def defined_p(pattern, environment):
480     if cons_collection_len(pattern) != 1:
481         raise Exception("ArgumentError: `defined?` expects 1 argument, received {}".format(len(pattern)))
482
483     if not isinstance(car(pattern).py_object, Identifier):
484         raise Exception("TypeError: Expected Identifier but got {}".format(type(car(pattern).py_object)))
485
486     return is_defined(car(pattern), environment)
487
488 def is_defined(identifier, environment):
489     assert isinstance(identifier, MiniObject)
490     assert isinstance(environment, MiniObject)
491
492     identifier_symbol = identifier_to_symbol(identifier)
493     parent_symbol = create_symbol('__parent__')
494
495     while True:
496         if cons_dict_has_key(environment, identifier_symbol) == TRUE:
497             return TRUE
498
499         elif cons_dict_has_key(environment, parent_symbol) == TRUE:
500             environment = cons_dict_get(environment, parent_symbol)
501
502         else:
503             return FALSE
504
505 def _if(pattern, environment):
506     if not cons_collection_len(pattern) in [2,3]:
507         raise Exception("ArgumentError")
508
509     condition = car(pattern)
510     if_result_true = car(cdr(pattern))
511     if_result_false = car(cdr(cdr(pattern)))
512
513     result = evaluate(condition, environment)
514
515     if result is TRUE:
516         return evaluate(if_result_true, environment)
517     if result is FALSE:
518         return evaluate(if_result_false, environment)
519
520     raise Exception("TypeError: `if` expects boolean, received {}".format(type(result)))
521
522 def nest(environment):
523     isinstance(environment,MiniEnvironment)
524
525     result = MiniEnvironment()
526     result['__parent__'] = environment
527     return result
528
529 # This is vau from John N. Shutt's seminal paper
530 # https://www.wpi.edu/Pubs/ETD/Available/etd-090110-124904/unrestricted/jshutt.pdf
531 # While Greek letters are appropriate for an academic, theoretical context, they make for
532 # poor variable names, so this is tentatively named `operative`
533 def operative(pattern, defining_environment):
534     argument_list_identifier = None
535     argument_identifiers = None
536
537     calling_environment_identifier = car(cdr(pattern)).py_object.symbol
538
539     if isinstance(car(pattern).py_object, Identifier):
540         argument_list_identifier = car(pattern).py_object.symbol
541
542         if calling_environment_identifier == argument_list_identifier:
543             raise Exception("ArgumentError: Argument list identifier `{}` may not be the same as calling environment identifier".format(ai))
544
545     elif car(pattern).py_object == None or isinstance(car(pattern).py_object, MiniPair):
546         if not all([isinstance(arg.py_object, Identifier) for arg in cons_collection_to_py_collection(car(pattern))]):
547             raise Exception("ArgumentError: Unexpected {} {}".format(type(arg),arg))
548
549         argument_identifiers = [ai.py_object.symbol for ai in cons_collection_to_py_collection(car(pattern))]
550         
551         existing = set()
552         for ai in argument_identifiers:
553             if ai in existing:
554                 raise Exception("ArgumentError: Argument `{}` already defined".format(ai))
555
556             if calling_environment_identifier == ai:
557                 raise Exception("ArgumentError: Argument `{}` may not be the same as calling environment identifier".format(ai))
558
559             existing.add(ai)
560
561     else:
562         raise Exception("ArgumentError: `operative` expected identifier or cons-list as first argument, received {}".format(type(car(pattern).py_object)))
563
564     if not isinstance(car(cdr(pattern)).py_object,Identifier):
565         raise Exception("ArgumentError: The second argument to `operative` should be an identifer")
566
567     def result(calling_pattern, calling_environment):
568         local_environment = nest(defining_environment)
569
570         assert (argument_list_identifier == None) != (argument_identifiers == None)
571         if argument_list_identifier != None:
572             local_environment[argument_list_identifier] = calling_pattern
573
574         if argument_identifiers != None:
575             if not cons_collection_len(calling_pattern) == len(argument_identifiers):
576                 raise Exception("ArgumentError: operative expected {} arguments, received {}".format(len(argument_identifiers),len(calling_pattern)))
577
578             calling_pattern = list(cons_collection_to_py_collection(calling_pattern))
579
580             for i in range(len(argument_identifiers)):
581                 local_environment[argument_identifiers[i]] = calling_pattern[i]
582
583         local_environment[calling_environment_identifier] = calling_environment
584
585         return evaluate_expressions(cdr(cdr(pattern)), local_environment)
586
587     return MiniObject(MiniApplicative(result))
588
589 def read_file(filename):
590     assert isinstance(filename, MiniObject)
591
592     with open(filename.py_object, 'r') as f:
593         return f.read()
594
595 def write_file(filename, string):
596     assert isinstance(filename, MiniObject)
597     assert isinstance(string, MiniObject)
598
599     with open(filename.py_object, 'w') as f:
600         f.write(string.py_object)
601
602 def add(l,r):
603     if isinstance(l, MiniObject) and isinstance(r, MiniObject):
604         l = l.py_object
605         r = r.py_object
606
607         if is_number(l) and is_number(r):
608             return l + r
609
610     raise Excepion('TypeError')
611
612 def subtract(l,r):
613     if isinstance(l, MiniObject) and isinstance(r, MiniObject):
614         l = l.py_object
615         r = r.py_object
616
617         if is_number(l) and is_number(r):
618             return l - r
619
620     raise Excepion('TypeError')
621
622 def multiply(l,r):
623     if isinstance(l, MiniObject) and isinstance(r, MiniObject):
624         l = l.py_object
625         r = r.py_object
626
627         if is_number(l) and is_number(r):
628             return l * r
629
630     raise Excepion('TypeError')
631
632 def divide(l,r):
633     if isinstance(l, MiniObject) and isinstance(r, MiniObject):
634         l = l.py_object
635         r = r.py_object
636
637         if is_number(l) and is_number(r):
638             if isinstance(l,int) and isinstance(r,int) and l % r != 0:
639                 l = float(l)
640
641             return l / r
642
643     raise Excepion('TypeError')
644
645 def idivide(l,r):
646     if isinstance(l, MiniObject) and isinstance(r, MiniObject):
647         l = l.py_object
648         r = r.py_object
649
650         if is_number(l) and is_number(r):
651             return l // r
652
653     raise Excepion('TypeError')
654
655 def mod(l,r):
656     if isinstance(l, MiniObject) and isinstance(r, MiniObject):
657         l = l.py_object
658         r = r.py_object
659
660         if is_number(l) and is_number(r):
661             return l % r
662
663     raise Excepion('TypeError')
664
665 def eq(l,r):
666     assert isinstance(l,MiniObject)
667     assert isinstance(r,MiniObject)
668
669     return l.py_object == r.py_object
670
671 def lt(l,r):
672     assert isinstance(l,MiniObject)
673     assert isinstance(r,MiniObject)
674
675     if is_number(l.py_object) and is_number(r.py_object):
676         return l.py_object < r.py_object
677
678     if isinstance(l.py_object,str) and isinstance(r.py_object,str):
679         return l.py_object < r.py_object
680
681     if isinstance(l.py_object,MiniSymbol) and isinstance(r.py_object,MiniSymbol):
682         return l.py_object.string < r.py_object.string
683
684     raise TypeError('`<` expected number or string, received {} and {}'.format(l.py_object, r.py_object))
685
686 def gt(l,r):
687     assert isinstance(l,MiniObject)
688     assert isinstance(r,MiniObject)
689
690     if is_number(l.py_object) and is_number(r.py_object):
691         return l.py_object > r.py_object
692
693     if isinstance(l.py_object,str) and isinstance(r.py_object,str):
694         return l.py_object > r.py_object
695
696     if isinstance(l.py_object,MiniSymbol) and isinstance(r.py_object,MiniSymbol):
697         return l.py_object.string > r.py_object.string
698
699     raise TypeError('`>` expected number or string, received {} and {}'.format(l.py_object, r.py_object))
700
701 def le(l,r):
702     return lt(l,r) or eq(l,r)
703
704 def ge(l,r):
705     return gt(l,r) or eq(l,r)
706
707 def cons(l,r):
708     return MiniObject(MiniPair(l,r))
709
710 def car(p):
711     return p.py_object.car
712
713 def cdr(p):
714     return p.py_object.cdr
715
716 def is_cons_list(mini_object):
717     assert isinstance(mini_object,MiniObject)
718
719     if eq(mini_object,NIL) or isinstance(mini_object.py_object,MiniPair):
720         return TRUE
721
722     return FALSE
723
724 def cons_dict_set(dictionary,key,value):
725     assert isinstance(dictionary,MiniObject)
726     assert isinstance(key,MiniObject)
727     assert isinstance(value,MiniObject)
728
729     if eq(dictionary,NIL):
730         return cons(cons(key,value),cons(NIL,NIL))
731
732     current_node_key = car(car(dictionary))
733
734     if lt(key,current_node_key):
735         return cons(
736             car(dictionary),
737             cons(
738                 cons_dict_set(car(cdr(dictionary)), key, value),
739                 cdr(cdr(dictionary))))
740
741     if gt(key,current_node_key):
742         return cons(
743             car(dictionary),
744             cons(
745                 car(cdr(dictionary)),
746                 cons_dict_set(cdr(cdr(dictionary)), key, value)))
747
748     if eq(key,current_node_key):
749         return cons(cons(key,value), cdr(dictionary))
750
751     assert False
752
753 def cons_dict_get(dictionary,key):
754     assert isinstance(dictionary, MiniObject)
755     assert isinstance(key, MiniObject)
756
757     if eq(dictionary,NIL):
758         raise Exception('KeyError: Dictionary does not contain key "{}"'.format(key))
759
760     current_node_key = car(car(dictionary))
761
762     if lt(key, current_node_key):
763         return cons_dict_get(car(cdr(dictionary)), key)
764
765     if gt(key, current_node_key):
766         return cons_dict_get(cdr(cdr(dictionary)), key)
767
768     if eq(key, current_node_key):
769         return cdr(car(dictionary))
770
771 def cons_dict_has_key(dictionary,key):
772     assert isinstance(dictionary, MiniObject)
773     assert isinstance(key, MiniObject)
774
775     if eq(dictionary,NIL):
776         return FALSE
777
778     current_node_key = car(car(dictionary))
779
780     if lt(key, current_node_key):
781         return cons_dict_has_key(car(cdr(dictionary)), key)
782
783     if gt(key, current_node_key):
784         return cons_dict_has_key(cdr(cdr(dictionary)), key)
785
786     if eq(key, current_node_key):
787         return TRUE
788
789 def identifier_to_symbol(identifier):
790     assert isinstance(identifier, MiniObject)
791
792     if not isinstance(identifier.py_object, Identifier):
793         raise Exception('`identifier->symbol` expected identifier, received {}'.format(type(identifier.py_object)))
794
795     return create_symbol(identifier.py_object.symbol)
796
797 def read(string):
798     assert isinstance(string,MiniObject)
799
800     if not isinstance(string.py_object,str):
801         raise Exception("TypeError: `read` expected string, got {}".format(type(strin.py_object)))
802
803     result =  parse_all(string.py_object)
804
805     assert cdr(result) == NIL
806
807     return car(result)
808
809 builtins = {
810     # Builtin constants
811     'true'      : TRUE,
812     'false'     : FALSE,
813     'nil'       : NIL,
814
815     # Builtin comparison functions
816     '='             : py_to_mini(eq),
817     '<'             : py_to_mini(lt),
818     '>'             : py_to_mini(gt),
819     '<='            : py_to_mini(le),
820     '>='            : py_to_mini(ge),
821
822     # Builtin conversion functions
823     'identifier->symbol'    : py_to_mini(identifier_to_symbol),
824
825     # Builtin type test functions
826     'cons-list?'    : py_to_mini(is_cons_list),
827     'identifier?'   : py_to_mini(is_identifier),
828
829     # Builtin general functions
830     'evaluate'      : py_to_mini(evaluate),
831     'evaluate-expressions'  : py_to_mini(evaluate_expressions),
832     'print'         : py_to_mini(print),
833     'prompt'        : py_to_mini(raw_input),
834     'read-file'     : py_to_mini(read_file),
835     'write-file'    : py_to_mini(write_file),
836     'read'          : py_to_mini(read),
837     'wrap'          : py_to_mini(wrap),
838     'unwrap'        : py_to_mini(unwrap),
839
840     # Builtin number functions
841     '+'             : py_to_mini(add),
842     '-'             : py_to_mini(subtract),
843     '*'             : py_to_mini(multiply),
844     '/'             : py_to_mini(divide),
845     '//'            : py_to_mini(idivide),
846     'mod'           : py_to_mini(mod),
847
848     # Builtin pair functions
849     'cons'          : py_to_mini(cons),
850     'car'           : py_to_mini(car),
851     'cdr'           : py_to_mini(cdr),
852
853     # Builtin cons dictionary functions
854     'cons-dict-set' : py_to_mini(cons_dict_set),
855     'cons-dict-get' : py_to_mini(cons_dict_get),
856
857     # Builtin string functions
858     'concatenate'   : py_to_mini(concatenate),
859     'length'        : py_to_mini(length),
860     'slice'         : py_to_mini(slice),
861
862     # Builtin boolean functions
863     'not'           : py_to_mini(_not),
864
865     # Builtin special forms
866     'assert'    : MiniObject(MiniApplicative(_assert)),
867     'define'    : MiniObject(MiniApplicative(define)),
868     'defined?'  : MiniObject(MiniApplicative(defined_p)),
869     'if'        : MiniObject(MiniApplicative(_if)),
870     'operative' : MiniObject(MiniApplicative(operative)),
871     'throws?'   : MiniObject(MiniApplicative(throws)),
872 }
873
874 builtins = dict_to_environment(builtins)
875
876 if __name__ == '__main__':
877     import os.path
878     import sys
879
880     arguments = sys.argv[1:]
881
882     predefineds = nest(builtins)
883     predefineds_filename = os.path.join(os.path.dirname(os.path.realpath(__file__)), 'predefineds.mini')
884
885     with open(predefineds_filename, 'r') as predefineds_file:
886         predefineds_source = predefineds_file.read()
887
888         try:
889             evaluate_expressions(parse_all(predefineds_source), predefineds)
890
891         except:
892             traceback.print_exc()
893
894     if len(arguments) == 0:
895         environment = nest(predefineds)
896         
897         while True:
898             source = raw_input('>>> ')
899             
900             try:
901                 print(evaluate_expressions(parse_all(source), environment))
902         
903             except:
904                 traceback.print_exc()
905
906     else:
907         filename = arguments[0]
908         arguments = arguments[1:]
909
910         environment = nest(predefineds)
911         environment['__file__'] = MiniObject(os.path.join(os.path.realpath(filename)))
912         environment['__arguments__'] = create_cons_collection(map(MiniObject,arguments))
913         
914         with open(filename,'r') as f:
915             source = f.read()
916
917         try:
918             print(evaluate_expressions(parse_all(source), environment))
919
920         except:
921             traceback.print_exc()