Coverage for kye/parser/types.py: 36%
149 statements
« prev ^ index » next coverage.py v7.3.2, created at 2023-12-01 16:38 -0700
« prev ^ index » next coverage.py v7.3.2, created at 2023-12-01 16:38 -0700
1from __future__ import annotations
2from typing import Optional, Literal, Union, TYPE_CHECKING
4if TYPE_CHECKING:
5 from kye.parser.environment import Environment
7class Expression:
8 """ Abstract interface for Types, Edges & Values """
9 name: Optional[str]
10 env: Environment
12 def extends(self, other: Expression) -> bool:
13 raise NotImplementedError()
15 def __getitem__(self, key: str) -> Edge:
16 raise NotImplementedError()
18 def __contains__(self, key: str) -> bool:
19 raise NotImplementedError()
21class Value(Expression):
22 value: str | int | float | bool
23 type: Type
25 def __init__(self, type: Type, value: str | int | float | bool):
26 super().__init__()
27 self.name = None
28 self.type = type
29 self.value = value
31 def extends(self, other: Expression) -> bool:
32 if not self.type.extends(other):
33 return False
34 if isinstance(other, Value):
35 return self.value == other.value
36 return True
38 def __getitem__(self, key: str) -> Edge:
39 return self.type[key].bind(self)
41 def __contains__(self, key: str) -> bool:
42 return key in self.type
44 def __repr__(self):
45 return "{}({})".format(
46 self.type.name,
47 repr(self.value),
48 )
51class Type(Expression):
52 name: str
53 edges: dict[str, Edge]
54 filter: Optional[Edge]
56 def __init__(self,
57 name: str,
58 edges: Optional[dict[str, Edge]] = None,
59 filter: Optional[Edge] = None,
60 env: Optional[Environment] = None,
61 ):
62 self.name = name
63 self.edges = edges or {}
64 self.filter = filter
65 self.env = env
66 assert self.env is None or self.env.name == name
68 def _extend_filter(self, filter: Optional[Edge]):
69 if self.filter:
70 if filter is not None:
71 filter = filter['__and__'].apply(self.filter)
72 else:
73 filter = self.filter
74 return filter
76 def _extend_edges(self, edges: dict[str, Edge]):
77 edges = {**edges}
78 for key, edge in self.edges.items():
79 # If over writing the edge, ensure
80 # that it can extend the parent's edge
81 if key in edges:
82 assert edges[key].extends(edge)
83 else:
84 edges[key] = edge
85 return edges
87 def extend(self,
88 name: Optional[str] = None,
89 edges: dict[str, Edge] = {},
90 filter: Optional[Edge] = None,
91 env: Optional[Environment] = None,
92 ):
93 return Type(
94 name=name or self.name,
95 edges=self._extend_edges(edges),
96 filter=self._extend_filter(filter),
97 env=env or self.env,
98 )
100 def extends(self, other: Expression) -> bool:
101 if isinstance(other, Edge):
102 return self.extends(other.returns)
103 if isinstance(other, Value):
104 return self.extends(other.type)
105 if isinstance(other, Type):
106 for other_key, other_edge in other.edges.items():
107 if other_key not in self:
108 return False
109 if not self[other_key].extends(other_edge):
110 return False
111 # TODO: Also check if our filter is a subset of the other's filter?
112 return True
113 raise Exception('How did you get here?')
115 def select(self, value: str | float | int | bool):
116 return Value(type=self, value=value)
118 def __getitem__(self, key: str) -> Edge:
119 return self.edges[key]
121 def __contains__(self, key: str):
122 return key in self.edges
124 def __repr__(self):
125 return '{}{}'.format(
126 self.name,
127 '{' + ','.join(repr(edge) for edge in self.edges.values()) + '}' if len(self.edges) else '',
128 )
130class Model(Type):
131 indexes: list[list[str]]
133 def __init__(self,
134 name: str,
135 indexes: list[list[str]],
136 edges: dict[str, Edge] = {},
137 filter: Optional[Edge] = None,
138 env: Optional[Environment] = None,
139 ):
140 super().__init__(name, edges, filter, env)
141 assert len(indexes) > 0
142 self.indexes = indexes
144 def extend(self,
145 name: Optional[str] = None,
146 indexes: Optional[list[list[str]]] = None,
147 edges: dict[str, Edge] = {},
148 filter: Optional[Edge] = None,
149 env: Optional[Environment] = None,
150 ):
151 return Model(
152 name=name or self.name,
153 indexes=indexes or self.indexes,
154 edges=self._extend_edges(edges),
155 filter=self._extend_filter(filter),
156 env=env or self.env,
157 )
159 def extends(self, other: Expression) -> bool:
160 if not super().extends(other):
161 return False
162 if isinstance(other, Model):
163 indexes = {tuple(idx) for idx in self.indexes}
164 for other_idx in other.indexes:
165 if tuple(other_idx) not in indexes:
166 return False
167 return True
169 @property
170 def index(self) -> set[str]:
171 """ Flatten the 2d list of indexes into a set """
172 return {idx for idxs in self.indexes for idx in idxs}
174 def __repr__(self):
175 non_index_edges = [
176 repr(self.edges[edge])
177 for edge in self.edges.keys()
178 if edge not in self.index
179 ]
180 return '{}{}{}'.format(
181 self.name,
182 ''.join('(' + ','.join(repr(self.edges[edge]) for edge in idx) + ')' for idx in self.indexes),
183 '{' + ','.join(non_index_edges) + '}' if len(non_index_edges) else '',
184 )
186class Edge(Expression):
187 owner: Type
188 name: Optional[str]
189 returns: Type
190 parameters: list[Type]
191 bound: Optional[Expression]
192 values: list[Optional[Edge]]
193 nullable: bool
194 multiple: bool
196 def __init__(self,
197 owner: Type,
198 name: Optional[str],
199 returns: Type,
200 parameters: list[Type] = [],
201 values: list[Optional[Edge]] = [],
202 bound: Optional[Expression] = None,
203 nullable: bool = False,
204 multiple: bool = False,
205 ):
206 self.owner = owner
207 self.name = name
208 self.returns = returns
209 self.parameters = parameters
210 assert bound is None or bound.extends(self.owner)
211 self.bound = bound
212 self.values = self._normalize_values(values)
213 self.nullable = nullable
214 self.multiple = multiple
216 def _copy(self, **kwargs):
217 return Edge(**{**self.__dict__, **kwargs})
219 def _normalize_values(self, values: list[Edge]):
220 assert len(values) <= len(self.parameters)
221 for i, val in enumerate(values):
222 assert val is None or val.extends(self.parameters[i])
223 # fill the undefined values with null
224 values = values + [None] * (len(self.parameters) - len(values))
225 return values
227 def bind(self, exp: Optional[Expression]) -> Edge:
228 return self._copy(bound=exp)
230 def apply(self, values: list[Optional[Edge]]):
231 return self._copy(values=values)
233 def extends(self, other: Expression) -> bool:
234 if not self.returns.extends(other):
235 return False
236 if isinstance(other, Edge):
237 if not self.nullable and other.nullable:
238 return False
239 if not self.multiple and other.multiple:
240 return False
241 # TODO: Might also want to check parameters and values?
242 return True
244 def __getitem__(self, key: str) -> Edge:
245 return self.returns[key].bind(self)
247 def __contains__(self, key: str) -> bool:
248 return key in self.returns
250 def __repr__(self):
251 return "{}{}".format(
252 self.name,
253 ([['' ,'+'],
254 ['?','*']])[int(self.nullable)][int(self.multiple)]
255 )
257if __name__ == '__main__':
258 boolean = Type('Boolean')
259 number = Type('Number')
260 number.edges['__gt__'] = Edge(owner=number, name='__gt__', parameters=[number], returns=boolean)
261 number.edges['__lt__'] = Edge(owner=number, name='__lt__', parameters=[number], returns=boolean)
262 string = Type('String')
263 string.edges['length'] = Edge(owner=string, name='length', returns=number)
264 big_string = string.extend(name='BigString')
265 big_string.filter = big_string['length']['__gt__'].apply([number.select(5)])
266 print('hi')