-
Notifications
You must be signed in to change notification settings - Fork 0
/
scheme_utils.py
120 lines (83 loc) · 3.24 KB
/
scheme_utils.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
import numbers
from scheme_classes import *
#################
# Type Checking #
#################
def scheme_procedurep(x):
return isinstance(x, Procedure)
def scheme_listp(x):
"""Return whether x is a well-formed list. Assumes no cycles."""
while x is not nil:
if not isinstance(x, Pair):
return False
x = x.rest
return True
def scheme_booleanp(x):
return x is True or x is False
def scheme_numberp(x):
return isinstance(x, numbers.Real) and not scheme_booleanp(x)
def is_scheme_true(val):
"""All values in Scheme are true except False."""
return val is not False
def is_scheme_false(val):
"""Only False is false in scheme_reader."""
return val is False
def scheme_stringp(x):
return isinstance(x, str) and x.startswith('"')
def scheme_symbolp(x):
return isinstance(x, str) and not scheme_stringp(x)
def scheme_nullp(x):
return type(x).__name__ == 'nil'
def scheme_atomp(x):
return (scheme_booleanp(x) or scheme_numberp(x) or scheme_symbolp(x) or
scheme_nullp(x) or scheme_stringp(x))
def self_evaluating(expr):
"""Return whether EXPR evaluates to itself."""
return (scheme_atomp(expr) and not scheme_symbolp(expr)) or expr is None
#######################
# Argument Validation #
#######################
def validate_type(val, predicate, k, name):
"""Returns VAL. Raises a SchemeError if not PREDICATE(VAL)
using "argument K of NAME" to describe the offending value."""
if not predicate(val):
msg = "argument {0} of {1} has wrong type ({2})"
type_name = type(val).__name__
if scheme_symbolp(val):
type_name = "symbol"
raise SchemeError(msg.format(k, name, type_name))
return val
def validate_procedure(procedure):
"""Check that PROCEDURE is a valid Scheme procedure."""
if not scheme_procedurep(procedure):
raise SchemeError('{0} is not callable: {1}'.format(
type(procedure).__name__.lower(), repl_str(procedure)))
def validate_form(expr, min, max=float('inf')):
"""Check EXPR is a proper list whose length is at least MIN and no more
than MAX (default: no maximum). Raises a SchemeError if this is not the
case.
>>> validate_form(read_line('(a b)'), 2)
"""
if not scheme_listp(expr):
raise SchemeError('badly formed expression: ' + repl_str(expr))
length = len(expr)
if length < min:
raise SchemeError('too few operands in form')
elif length > max:
raise SchemeError('too many operands in form')
def validate_formals(formals):
"""Check that FORMALS is a valid parameter list, a Scheme list of symbols
in which each symbol is distinct. Raise a SchemeError if the list of
formals is not a list of symbols or if any symbol is repeated.
>>> validate_formals(read_line('(a b c)'))
"""
symbols = set()
def validate_and_add(symbol, is_last):
if not scheme_symbolp(symbol):
raise SchemeError('non-symbol: {0}'.format(symbol))
if symbol in symbols:
raise SchemeError('duplicate symbol: {0}'.format(symbol))
symbols.add(symbol)
while isinstance(formals, Pair):
validate_and_add(formals.first, formals.rest is nil)
formals = formals.rest