-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathtype_tree.py
More file actions
200 lines (178 loc) · 5.63 KB
/
type_tree.py
File metadata and controls
200 lines (178 loc) · 5.63 KB
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
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
from ratio import Ratio
from table import Table
from exception import throw_exception
from trigger import Trigger
import function
import strtools
class TypeTree(object):
def __init__(self, *names):
self.names = names
self.subclasses = []
def add_subclass(self, subclass):
self.subclasses.append(subclass)
def get_default_name(self):
return self.names[0]
def has_name(self, name):
return name in self.names
def has_child(self, name):
for subclass in self.subclasses:
if subclass.has_name(name) or subclass.has_child(name):
return True
return False
def categorizes(self, name):
"""
Returns True if this type categorizes name.
e.g. If this type is Number, should return True
if name is 'Int' or 'Double', and False if name
is 'String' or 'List'.
"""
return self.has_name(name) or self.has_child(name)
def __repr__(self):
return self.format()
def __str__(self):
return repr(self)
def format(self, indent_size=0):
remainder = ''
for subclass in self.subclasses:
remainder += subclass.format(indent_size + 2)
return '{0}{1}\n{2}'.format(' ' * indent_size, self.names, remainder)
def format_as_literal(self):
formatted = self.format()
formatted = formatted.replace('(', '\\x28').replace(')', '\\x29')
return '"{0}"'.format(formatted)
def merge_trees(tree, other):
"""
Combines two type trees into a single tree.
The new tree will contain all the children of both
original trees. Children that are shared will be
recursively merged.
e.g.
Take the two trees:
A A
/ \ / \
B C D B
| / \
G E F
The result should be:
A
/|\
B C D
/|\
G E F
"""
if tree.names != other.names:
throw_exception(
'CannotMergeTrees',
'Root value {0} is not the same as {1}'.format(tree.names, other.names)
)
combined_subclasses = {}
for subclass in tree.subclasses:
combined_subclasses[subclass.names] = subclass
for subclass in other.subclasses:
if subclass.names in combined_subclasses:
old_tree = combined_subclasses[subclass.names]
new_tree = merge_trees(old_tree, subclass)
combined_subclasses[subclass.names] = new_tree
else:
combined_subclasses[subclass.names] = subclass
result = TypeTree(*tree.names)
for val in combined_subclasses.values():
result.add_subclass(val)
return result
def build_type_table(tree):
"""
Creates a dictionary where all the nodes of a tree
are directly accessible by name.
e.g.
A
/ \
B C
\
D
will become:
{
'A': (tree A containing B,C),
'B': (tree B),
'C': (tree C containing D),
'D': (tree D)
}
"""
entries = {}
for name in tree.names:
entries[name] = tree
for subclass in tree.subclasses:
child_entries = build_type_table(subclass)
for key, val in child_entries.items():
if key in entries:
throw_exception('CannotBuildTypeTable', 'Duplicate node name {0}'.format(key))
else:
entries[key] = val
return entries
def get_type(value):
kind = type(value)
if kind is int or kind is long:
return 'Int'
elif kind is float:
return 'Double'
elif kind is bool:
return 'Boolean'
elif kind is str:
if value.startswith('#'):
return 'Tag'
throw_exception('UnknownValueType', 'The type of {0} cannot be determined'.format(value))
elif kind is list:
return 'List'
val_class = value.__class__
if val_class is strtools.CapString:
return 'String'
if val_class is Ratio:
return 'Ratio'
elif val_class is Table:
return 'Table'
elif value is None:
return 'Null'
elif kind is dict:
# This is a user-defined object
if '$type' in value:
if value['$type'].__class__ is strtools.CapString:
return value['$type'].contents
else:
return value['$type']
else:
return 'Instance'
elif function.is_function(value):
return 'Function'
elif val_class is Trigger:
return 'Trigger'
throw_exception('UnknownType', str(value) + ' has an unknown type.')
def generate_default_tree():
integers = TypeTree('Int', 'Integer')
doubles = TypeTree('Double')
bools = TypeTree('Boolean')
strs = TypeTree('String')
tags = TypeTree('Tag')
lists = TypeTree('List')
tables = TypeTree('Table')
ratios = TypeTree('Ratio', 'Rational')
nulls = TypeTree('Null', 'Void')
functions = TypeTree('Function')
instances = TypeTree('Instance')
triggers = TypeTree('Trigger')
numbers = TypeTree('Number')
numbers.add_subclass(integers)
numbers.add_subclass(doubles)
numbers.add_subclass(ratios)
sequences = TypeTree('Sequence', 'Iterable')
sequences.add_subclass(strs)
sequences.add_subclass(lists)
sequences.add_subclass(tables)
objects = TypeTree('Object')
objects.add_subclass(numbers)
objects.add_subclass(sequences)
objects.add_subclass(tags)
objects.add_subclass(bools)
objects.add_subclass(nulls)
objects.add_subclass(functions)
objects.add_subclass(instances)
objects.add_subclass(triggers)
return build_type_table(objects)