Coverage for kye/parser/expressions.py: 37%
134 statements
« prev ^ index » next coverage.py v7.3.2, created at 2023-11-30 16:02 -0700
« prev ^ index » next coverage.py v7.3.2, created at 2023-11-30 16:02 -0700
1from __future__ import annotations
2from typing import Optional, Literal, Union, TYPE_CHECKING
3import kye.parser.kye_ast as AST
5if TYPE_CHECKING:
6 from kye.parser.environment import Environment
8class Expression:
9 env: Environment
10 ast: Optional[AST.Expression]
12 def get_type(self) -> Environment:
13 raise NotImplementedError()
16class Value(Expression):
17 value: str | int | float | bool
19 def __init__(self, env: Type, value: str | int | float | bool):
20 super().__init__()
21 self.env = env
22 self.value = value
24 def get_type(self) -> Environment:
25 return self.env
27 def __repr__(self):
28 return "{}({})".format(
29 self.env.global_name,
30 repr(self.value),
31 )
34class Type(Expression):
35 name: str
36 edges: dict[str, Edge]
37 filter: Optional[Edge]
39 def __init__(self,
40 name: str,
41 edges: Optional[dict[str, Edge]] = None,
42 filter: Optional[Edge] = None
43 ):
44 self.name = name
45 self.edges = edges or {}
46 self.filter = filter
48 def _extend_filter(self, filter: Optional[Edge]):
49 if self.filter:
50 if filter is not None:
51 filter = filter['__and__'].apply(self.filter)
52 else:
53 filter = self.filter
54 return filter
56 def _extend_edges(self, edges: dict[str, Edge]):
57 edges = {**edges}
58 for key, edge in self.edges.items():
59 # If over writing the edge, ensure
60 # that it can extend the parent's edge
61 if key in edges:
62 assert edges[key].extends(edge)
63 else:
64 edges[key] = edge
65 return edges
67 def extend(self,
68 name: Optional[str] = None,
69 edges: dict[str, Edge] = {},
70 filter: Optional[Edge] = None):
71 return Type(
72 name=name or self.name,
73 edges=self._extend_edges(edges),
74 filter=self._extend_filter(filter),
75 )
77 def extends(self, other: Expression) -> bool:
78 if isinstance(other, Edge):
79 return self.extends(other.returns)
80 if isinstance(other, Value):
81 return self.extends(other.type)
82 if isinstance(other, Type):
83 for other_key, other_edge in other.edges.items():
84 if other_key not in self:
85 return False
86 if not self[other_key].extends(other_edge):
87 return False
88 # TODO: Also check if our filter is a subset of the other's filter?
89 return True
90 raise Exception('How did you get here?')
92 def select(self, value: str | float | int | bool):
93 return Value(env=self, value=value)
95 def __getitem__(self, key: str) -> Edge:
96 return self.edges[key]
98 def __contains__(self, key: str):
99 return key in self.edges
101 def __repr__(self):
102 return '{}{}'.format(
103 self.name,
104 '{' + ','.join(repr(edge) for edge in self.edges.values()) + '}' if len(self.edges) else '',
105 )
107class Model(Type):
108 indexes: list[list[str]]
110 def __init__(self,
111 name: str,
112 indexes: list[list[str]],
113 edges: dict[str, Edge] = {},
114 filter: Optional[Edge] = None,
115 ):
116 super().__init__(name, edges, filter)
117 assert len(indexes) > 0
118 self.indexes = indexes
120 def extend(self,
121 name: Optional[str] = None,
122 indexes: Optional[list[list[str]]] = None,
123 edges: dict[str, Edge] = {},
124 filter: Optional[Edge] = None,
125 ):
126 return Model(
127 name=name or self.name,
128 indexes=indexes or self.indexes,
129 edges=self._extend_edges(edges),
130 filter=self._extend_filter(filter),
131 )
133 def extends(self, other: Expression) -> bool:
134 if not super().extends(other):
135 return False
136 if isinstance(other, Model):
137 indexes = {tuple(idx) for idx in self.indexes}
138 for other_idx in other.indexes:
139 if tuple(other_idx) not in indexes:
140 return False
141 return True
143 @property
144 def index(self) -> set[str]:
145 """ Flatten the 2d list of indexes into a set """
146 return {idx for idxs in self.indexes for idx in idxs}
148 def __repr__(self):
149 non_index_edges = [
150 repr(self.edges[edge])
151 for edge in self.edges.keys()
152 if edge not in self.index
153 ]
154 return '{}{}{}'.format(
155 self.name,
156 ''.join('(' + ','.join(repr(self.edges[edge]) for edge in idx) + ')' for idx in self.indexes),
157 '{' + ','.join(non_index_edges) + '}' if len(non_index_edges) else '',
158 )
160class Edge(Expression):
161 owner: Type
162 name: Optional[str]
163 returns: Type
164 parameters: list[Type]
165 bound: Optional[Expression]
166 values: list[Optional[Edge]]
167 nullable: bool
168 multiple: bool
170 def __init__(self,
171 owner: Type,
172 name: Optional[str],
173 returns: Type,
174 parameters: list[Type] = [],
175 values: list[Optional[Edge]] = [],
176 bound: Optional[Expression] = None,
177 nullable: bool = False,
178 multiple: bool = False,
179 ):
180 self.owner = owner
181 self.name = name
182 self.returns = returns
183 self.parameters = parameters
184 assert bound is None or bound.extends(self.owner)
185 self.bound = bound
186 self.values = self._normalize_values(values)
187 self.nullable = nullable
188 self.multiple = multiple
190 def _copy(self, **kwargs):
191 return Edge(**{**self.__dict__, **kwargs})
193 def _normalize_values(self, values: list[Edge]):
194 assert len(values) <= len(self.parameters)
195 for i, val in enumerate(values):
196 assert val is None or val.extends(self.parameters[i])
197 # fill the undefined values with null
198 values = values + [None] * (len(self.parameters) - len(values))
199 return values
201 def bind(self, exp: Optional[Expression]) -> Edge:
202 return self._copy(bound=exp)
204 def apply(self, values: list[Optional[Edge]]):
205 return self._copy(values=values)
207 def extends(self, other: Expression) -> bool:
208 if not self.returns.extends(other):
209 return False
210 if isinstance(other, Edge):
211 if not self.nullable and other.nullable:
212 return False
213 if not self.multiple and other.multiple:
214 return False
215 # TODO: Might also want to check parameters and values?
216 return True
218 def __getitem__(self, key: str) -> Edge:
219 return self.returns[key].bind(self)
221 def __contains__(self, key: str) -> bool:
222 return key in self.returns
224 def __repr__(self):
225 return "{}{}".format(
226 self.name,
227 ([['' ,'+'],
228 ['?','*']])[int(self.nullable)][int(self.multiple)]
229 )
231if __name__ == '__main__':
232 boolean = Type('Boolean')
233 number = Type('Number')
234 number.edges['__gt__'] = Edge(owner=number, name='__gt__', parameters=[number], returns=boolean)
235 number.edges['__lt__'] = Edge(owner=number, name='__lt__', parameters=[number], returns=boolean)
236 string = Type('String')
237 string.edges['length'] = Edge(owner=string, name='length', returns=number)
238 big_string = string.extend(name='BigString')
239 big_string.filter = big_string['length']['__gt__'].apply([number.select(5)])
240 print('hi')