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

1from __future__ import annotations 

2from typing import Optional, Literal, Union, TYPE_CHECKING 

3import kye.parser.kye_ast as AST 

4 

5if TYPE_CHECKING: 

6 from kye.parser.environment import Environment 

7 

8class Expression: 

9 env: Environment 

10 ast: Optional[AST.Expression] 

11 

12 def get_type(self) -> Environment: 

13 raise NotImplementedError() 

14 

15 

16class Value(Expression): 

17 value: str | int | float | bool 

18 

19 def __init__(self, env: Type, value: str | int | float | bool): 

20 super().__init__() 

21 self.env = env 

22 self.value = value 

23 

24 def get_type(self) -> Environment: 

25 return self.env 

26 

27 def __repr__(self): 

28 return "{}({})".format( 

29 self.env.global_name, 

30 repr(self.value), 

31 ) 

32 

33 

34class Type(Expression): 

35 name: str 

36 edges: dict[str, Edge] 

37 filter: Optional[Edge] 

38 

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 

47 

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 

55 

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 

66 

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 ) 

76 

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?') 

91 

92 def select(self, value: str | float | int | bool): 

93 return Value(env=self, value=value) 

94 

95 def __getitem__(self, key: str) -> Edge: 

96 return self.edges[key] 

97 

98 def __contains__(self, key: str): 

99 return key in self.edges 

100 

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 ) 

106 

107class Model(Type): 

108 indexes: list[list[str]] 

109 

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 

119 

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 ) 

132 

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 

142 

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} 

147 

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 ) 

159 

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 

169 

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 

189 

190 def _copy(self, **kwargs): 

191 return Edge(**{**self.__dict__, **kwargs}) 

192 

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 

200 

201 def bind(self, exp: Optional[Expression]) -> Edge: 

202 return self._copy(bound=exp) 

203 

204 def apply(self, values: list[Optional[Edge]]): 

205 return self._copy(values=values) 

206 

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 

217 

218 def __getitem__(self, key: str) -> Edge: 

219 return self.returns[key].bind(self) 

220 

221 def __contains__(self, key: str) -> bool: 

222 return key in self.returns 

223 

224 def __repr__(self): 

225 return "{}{}".format( 

226 self.name, 

227 ([['' ,'+'], 

228 ['?','*']])[int(self.nullable)][int(self.multiple)] 

229 ) 

230 

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')