Special.repr()   A
last analyzed

Complexity

Conditions 1

Size

Total Lines 3

Duplication

Lines 0
Ratio 0 %
Metric Value
cc 1
dl 0
loc 3
rs 10
1
import os
2
from rpython.rlib.rbigint import rbigint as RBigInt
3
from rpython.rlib.objectmodel import r_dict
4
5
"""Note that 'types' is part of the python standard library, so we're
6
forced to name this file trifle_types.
7
8
RPython ignores magic methods (except __init__) but we implement them
9
for convenience when testing.
10
11
"""
12
13
def is_equal(x, y):
14
    """Return True if x and y are equal.
15
16
    TODO: fix the potential stack overflow here for deeply nested
17
    lists and hashmaps.
18
19
    """
20
    # TODO: Once we have proper interning, we should be able to use `x
21
    # is y` for symbols and keywords.
22
    if isinstance(x, Symbol):
23
        if isinstance(y, Symbol):
24
            return x.symbol_name == y.symbol_name
25
26
        return False
27
28
    elif isinstance(x, Keyword):
29
        if isinstance(y, Keyword):
30
            return x.symbol_name == y.symbol_name
31
32
        return False
33
34
    elif isinstance(x, Float):
35
        if isinstance(y, Float):
36
            return x.float_value == y.float_value
37
38
        elif isinstance(y, Integer):
39
            # TODO: potentially y could be too big for floats.
40
            return x.float_value == y.bigint_value.tofloat()
41
42
        return False
43
44
    elif isinstance(x, Integer):
45
        if isinstance(y, Integer):
46
            return x.bigint_value.eq(y.bigint_value)
47
48
        elif isinstance(y, Float):
49
            return x.bigint_value.tofloat() == y.float_value
50
51
        return False
52
53
    elif isinstance(x, Fraction):
54
        if isinstance(y, Fraction):
55
            return (x.numerator == y.numerator and
56
                    x.denominator == y.denominator)
57
58
        elif isinstance(y, Float):
59
            # TODO: Document the corner cases here when our fractions
60
            # are bigger than the largest legal float, or can't be represented exactly.
61
            return is_equal(Float(x.numerator.tofloat() / x.denominator.toint()), y)
62
63
        return False
64
65
    elif isinstance(x, Character):
66
        if isinstance(y, Character):
67
            return x.character == y.character
68
69
        return False
70
71
    elif isinstance(x, String):
72
        if isinstance(y, String):
73
            return x.string == y.string
74
75
        return False
76
77
    elif isinstance(x, Bytestring):
78
        if isinstance(y, Bytestring):
79
            return x.byte_value == y.byte_value
80
81
        return False
82
83
    elif isinstance(x, List):
84
        if isinstance(y, List):
85
            if len(x.values) != len(y.values):
86
                return False
87
88
            for i in range(len(x.values)):
89
                if not is_equal(x.values[i], y.values[i]):
90
                    return False
91
            return True
92
93
        return False
94
95
    elif isinstance(x, Hashmap):
96
        if isinstance(y, Hashmap):
97
            if len(x.dict.keys()) != len(y.dict.keys()):
98
                return False
99
100
            for key, x_value in x.dict.iteritems():
101
                y_value = y.dict.get(key, None)
102
103
                if y_value is None:
104
                    return False
105
                elif not is_equal(x_value, y_value):
106
                    return False
107
108
            return True
109
110
        return False
111
112
    # In principle, we should only have one instance of #true and of
113
    # #false. This ensures boolean equality still works even if
114
    # built-in functions instantiate fresh booleans.
115
    elif isinstance(x, Boolean):
116
        if isinstance(y, Boolean):
117
            return x.value == y.value
118
119
        return False
120
121
    return x is y
122
123
124
class TrifleType(object):
125
    def __eq__(self, other):
126
        return is_equal(self, other)
127
    
128
    def __repr__(self):
129
        """We can't override __repr__ in rpython, so this is only useful when
130
        debugging with CPython.
131
132
        """
133
        return "<%s: %s>" % (self.__class__.__name__, self.repr())
134
135
136
# TODO: move to a consistent naming scheme: .boolean_value, .list_value etc.
137
class Boolean(TrifleType):
138
    def repr(self):
139
        if self.value:
140
            return u"#true"
141
        else:
142
            return u"#false"
143
144
    def __init__(self, value):
145
        self.value = value
146
147
148
TRUE = Boolean(True)
149
150
151
FALSE = Boolean(False)
152
153
154
class Null(TrifleType):
155
    def repr(self):
156
        return u"#null"
157
158
159
NULL = Null()
160
161
162
class Integer(TrifleType):
163
    def repr(self):
164
        return unicode(self.bigint_value.str())
165
166
    def __eq__(self, other):
167
        """We deliberately treat Integer(1) as different to Float(1.0) since
168
        this magic method is only used in tests and it avoids confusion.
169
170
        """
171
        if self.__class__ != other.__class__:
172
            return False
173
174
        return self.bigint_value.eq(other.bigint_value)
175
176
    def __init__(self, value):
177
        assert isinstance(value, RBigInt)
178
        self.bigint_value = value
179
180
    @staticmethod
181
    def fromstr(s):
182
        return Integer(RBigInt.fromdecimalstr(s))
183
184
    @staticmethod
185
    def fromint(num):
186
        assert isinstance(num, int), "Expected Python int but got: %s" % num
187
        return Integer(RBigInt.fromint(num))
188
189
190
def greatest_common_divisor(a, b):
191
    """Find the largest number that divides both a and b.
192
    We use the Euclidean algorithm for simplicity:
193
    http://en.wikipedia.org/wiki/Euclidean_algorithm
194
195
    """
196
    while not b.eq(RBigInt.fromint(0)):
197
        temp = b
198
        b = a.mod(b)
199
        a = temp
200
201
    return a
202
203
204
class Fraction(TrifleType):
205
    def repr(self):
206
        return u"%s/%s" % (unicode(self.numerator.str()),
207
                           unicode(self.denominator.str()))
208
209
    def __init__(self, numerator, denominator):
210
        assert isinstance(numerator, RBigInt)
211
        assert isinstance(denominator, RBigInt)
212
213
        assert denominator.gt(RBigInt.fromint(0))
214
215
        common_factor = greatest_common_divisor(
216
            numerator.abs(), denominator.abs())
217
        if common_factor.ne(RBigInt.fromint(1)):
218
            numerator = numerator.div(common_factor)
219
            denominator = denominator.div(common_factor)
220
        
221
        self.numerator = numerator
222
        self.denominator = denominator
223
224
225
class Float(TrifleType):
226
    def repr(self):
227
        return u"%f" % self.float_value
228
229
    def __eq__(self, other):
230
        """We deliberately treat Integer(1) as different to Float(1.0) since
231
        this magic method is only used in tests and it avoids confusion.
232
233
        """
234
        if self.__class__ != other.__class__:
235
            return False
236
237
        return self.float_value == other.float_value
238
239
    def __init__(self, value):
240
        assert isinstance(value, float)
241
        self.float_value = value
242
243
244
class Symbol(TrifleType):
245
    def repr(self):
246
        return self.symbol_name
247
248
    def __repr__(self):
249
        return "<%s: %s>" % (self.__class__.__name__, self.symbol_name)
250
251
    def __init__(self, symbol_name):
252
        assert isinstance(symbol_name, unicode)
253
        self.symbol_name = symbol_name
254
255
256
# TODOC
257
class Keyword(TrifleType):
258
    def repr(self):
259
        return u":%s" % self.symbol_name
260
261
    def __repr__(self):
262
        return "<%s: %s>" % (self.__class__.__name__, self.symbol_name)
263
264
    def __init__(self, symbol_name):
265
        assert isinstance(symbol_name, unicode)
266
        self.symbol_name = symbol_name
267
268
269
class Character(TrifleType):
270
    def repr(self):
271
        if self.character == '\n':
272
            return u"'\\n'"
273
        elif self.character == "'":
274
            return u"'\\''"
275
        elif self.character == "\\":
276
            return u"'\\\\'"
277
        else:
278
            return u"'%s'" % self.character
279
280
    def __repr__(self):
281
        return "<%s: %s>" % (self.__class__.__name__, self.character)
282
283
    def __init__(self, character):
284
        assert isinstance(character, unicode)
285
        assert len(character) == 1
286
287
        self.character = character
288
289
290
def hash_trifle_type(trifle_value):
291
    if isinstance(trifle_value, Integer):
292
        return trifle_value.bigint_value.hash()
293
    else:
294
        assert False, "TODO: hash more Trifle types."
295
296
297
class Hashmap(TrifleType):
298
    def __init__(self):
299
        self.dict = r_dict(is_equal, hash_trifle_type)
300
301
    def __eq__(self, other):
302
        return is_equal(self, other)
303
304
    def __repr__(self):
305
        return self.repr()
306
307
    # TODO: fix infinite loop for hashmaps that contain themselves.
308
    def repr(self):
309
        element_reprs = [key.repr() + u" " + value.repr() for key, value in self.dict.iteritems()]
310
        return u"{%s}" % u", ".join(element_reprs)
311
312
313
class String(TrifleType):
314
    def repr(self):
315
        printable_chars = []
316
        for char in self.string:
317
            if char == u'\n':
318
                printable_chars.append(u"\\n")
319
            elif char == u'"':
320
                printable_chars.append(u'\\"')
321
            elif char == u"\\":
322
                printable_chars.append(u"\\\\")
323
            else:
324
                printable_chars.append(char)
325
            
326
        return u'"%s"' % u"".join(printable_chars)
327
328
    def __repr__(self):
329
        return '<String: %s>' % self.repr()
330
331
    def as_unicode(self):
332
        return u"".join(self.string)
333
334
    def __init__(self, string):
335
        """We expect a list of unicode chars."""
336
        assert isinstance(string, list)
337
        if string:
338
            assert isinstance(string[0], unicode)
339
340
        self.string = string
341
342
343
class List(TrifleType):
344
    def __init__(self, values=None):
345
        if values is None:
346
            self.values = []
347
        else:
348
            assert isinstance(values, list)
349
            self.values = values
350
351
    def append(self, value):
352
        self.values.append(value)
353
354
    # TODO: fix infinite loop for lists that contain themselves
355
    def repr(self):
356
        element_reprs = [element.repr() for element in self.values]
357
        return u"(%s)" % u" ".join(element_reprs)
358
359
360
class Bytestring(TrifleType):
361
    def __init__(self, byte_value):
362
        assert isinstance(byte_value, list), "Expected a list, but got: %s" % byte_value
363
        if byte_value:
364
            assert isinstance(byte_value[0], int)
365
        self.byte_value = byte_value
366
367
    def repr(self):
368
        SMALLEST_PRINTABLE_CHAR = ' '
369
        LARGEST_PRINTABLE_CHAR = '~'
370
371
        printable_chars = []
372
373
        for char_code in self.byte_value:
374
            char = chr(char_code)
375
            if SMALLEST_PRINTABLE_CHAR <= char <= LARGEST_PRINTABLE_CHAR:
376
                if char == "\\":
377
                    printable_chars.append("\\\\")
378
                else:
379
                    printable_chars.append(char)
380
            else:
381
                # e.g. "0x21"
382
                hexadecimal = hex(ord(char))
383
                printable_chars.append("\\x%s" % hexadecimal[2:])
384
385
        return u'#bytes("%s")' % ("".join(printable_chars)).decode('utf-8')
386
387
388
class FileHandle(TrifleType):
389
    def __init__(self, file_name, file_handle, file_mode):
390
        self.is_closed = False
391
392
        assert isinstance(file_name, str), "File name is %r" % file_name
393
        self.file_name = file_name
394
395
        self.file_handle = file_handle
396
        self.mode = file_mode
397
398
    def close(self):
399
        self.is_closed = True
400
        self.file_handle.close()
401
402
    def flush(self):
403
        self.file_handle.flush()
404
405
    def write(self, string):
406
        self.file_handle.write(string)
407
408
    def repr(self):
409
        return u'#file-handle("%s")' % self.file_name.decode('utf-8')
410
411
412
STDOUT_FILE_DESCRIPTOR = 1
413
414
415
class Stdout(FileHandle):
416
    
417
    def __init__(self):
418
        self.file_name = ""
419
        self.is_closed = False
420
        self.mode = Keyword(u'write')
421
422
    def close(self):
423
        self.is_closed = True
424
        os.close(STDOUT_FILE_DESCRIPTOR)
425
426
    def flush(self):
427
        """This is a Python-specific detail, I believe and we don't need to do
428
        anything for stdout. Note that Python doesn't provide
429
        os.flush(file_descriptor).
430
        
431
        TODO: consider removing `flush!` entirely.
432
433
        """
434
        pass
435
436
    def write(self, string):
437
        os.write(STDOUT_FILE_DESCRIPTOR, string)
438
439
    def repr(self):
440
        return u'#file-handle(stdout)'
441
442
443
444
class Function(TrifleType):
445
    """A function provided by the interpreter. Subclasses must provide a
446
    call method. Arguments are passed in after being evaluated.
447
448
    """
449
    def repr(self):
450
        # todo: we can be more helpful than this
451
        return u"<built-in function>"
452
453
454
# TODO: rename this, since it also takes the stack as an argument.
455
class FunctionWithEnv(TrifleType):
456
    """A function provided by the interpreter. Subclasses must provide a
457
    call method that takes arguments and the environment. Arguments
458
    are passed in after being evaluated.
459
460
    """
461
    def repr(self):
462
        # todo: we can be more helpful than this
463
        return u"<built-in function>"
464
465
466
# todo: could we define interpreter Function classes in terms of Lambda?
467
class Lambda(TrifleType):
468
    """A user defined function. Holds a reference to the current lexical
469
    environment, so we support closures.
470
471
    """
472
    def __init__(self, arguments, body, env):
473
        self.arguments = arguments
474
        self.body = body
475
        self.env = env
476
477
    def repr(self):
478
        # todo: we can be more helpful than this
479
        return u"<lambda>"
480
481
482
# TODO: decide on whether we want to use the term 'error' or
483
# 'exception', and use it consistently.
484
class TrifleExceptionType(TrifleType):
485
    """We catch exceptions by type. An exception type may declare a parent
486
    (i.e. single inheritance).
487
488
    Catching the parent exception will also catch any exception that
489
    inherits from it.
490
491
    """
492
    def __init__(self, parent, name):
493
        assert isinstance(name, unicode), \
494
            "Exception type names must be unicode strings, but got %r" % name
495
        self.name = name
496
497
        if parent is None:
498
            self.parent = None
499
        else:
500
            assert isinstance(parent, TrifleExceptionType)
501
            self.parent = parent
502
503
    def repr(self):
504
        return u'#error-type("%s")' % self.name
505
506
507
class TrifleExceptionInstance(TrifleType):
508
    def __init__(self, exception_type, message):
509
        assert isinstance(exception_type, TrifleExceptionType)
510
        self.exception_type = exception_type
511
        
512
        assert isinstance(message, unicode)
513
        self.message = message
514
515
        self.caught = False
516
517
    def repr(self):
518
        # TODO: show the message too.
519
        return u'#error(%s)' % self.exception_type.name
520
521
    def __repr__(self):
522
        return '<%s: %s>' % (self.exception_type.name, self.message)
523
524
525
class Macro(TrifleType):
526
    """As with Function, subclasses must provide a call method. Macros are
527
    evaluated at compile time, and should return an expression for the
528
    intepreter to evaluate at run time.
529
530
    """
531
    def __init__(self, name, arguments, body):
532
        self.name = name
533
        self.arguments = arguments
534
        self.body = body
535
536
    def repr(self):
537
        # todo: we can be more helpful than this
538
        return u"<macro>"
539
540
541
class Special(TrifleType):
542
    """A special expression is an expression whose arguments are passed
543
    unevaluated, but at run time.
544
545
    """
546
    def repr(self):
547
        # todo: we can be more helpful than this
548
        return u"<special expression>"
549
550
551
"""Our parenthesis classes aren't exposed to the user, but we add them
552
for consistency when boxing values from the lexer.
553
554
"""
555
556
class OpenParen(TrifleType):
557
    pass
558
559
560
class CloseParen(TrifleType):
561
    pass
562
563
564
class OpenCurlyParen(TrifleType):
565
    pass
566
567
568
class CloseCurlyParen(TrifleType):
569
    pass
570