Coverage for kye/parser/kye_ast.py: 61%

94 statements  

« prev     ^ index     » next       coverage.py v7.3.2, created at 2024-01-04 16:29 -0700

1from __future__ import annotations 

2from pydantic import BaseModel, model_validator, constr 

3from typing import Optional, Literal, Union, Any 

4 

5TYPE = constr(pattern=r'[A-Z][a-z][a-zA-Z]*') 

6EDGE = constr(pattern=r'[a-z][a-z_]*') 

7 

8class TokenPosition(BaseModel): 

9 line: int 

10 column: int 

11 end_line: int 

12 end_column: int 

13 start_pos: int 

14 end_pos: int 

15 text: str 

16 

17 def __repr__(self): 

18 end_line = f"{self.end_line}:" if self.end_line != self.line else '' 

19 return f"{self.line}:{self.column}-{end_line}{self.end_column}" 

20 

21class AST(BaseModel): 

22 children: list[AST] = [] 

23 meta: TokenPosition 

24 

25 def __repr__(self): 

26 end_line = f"-{self.meta.end_line}" if self.meta.end_line != self.meta.line else '' 

27 return f"{self.__class__.__name__}<{self.__repr_value__()}>:{self.meta.line}{end_line}" 

28 

29 def __repr_value__(self): 

30 raise Exception('Not implemented __repr_value__') 

31 

32class Definition(AST): 

33 """ Abstract class for all AST nodes that define a name """ 

34 name: Union[TYPE, EDGE] 

35 

36class TypeDefinition(Definition): 

37 """ Abstract class for all AST nodes that define a type """ 

38 name: TYPE 

39 

40class ExpressionDefinition(Definition): 

41 """ Abstract class for all AST nodes who's type is an expression """ 

42 type: Expression 

43 

44class ContainedDefinitions(AST): 

45 """ Abstract class for all AST nodes that have child definitions """ 

46 children: list[Definition] 

47 

48class ModuleDefinitions(ContainedDefinitions): 

49 children: list[TypeDefinition] 

50 

51 @model_validator(mode='after') 

52 def validate_definitions(self): 

53 type_names = set() 

54 for child in self.children: 

55 # raise error if definition name is duplicated 

56 if child.name in type_names: 

57 raise ValueError(f'Type name {child.name} is duplicated in model {self.name}') 

58 type_names.add(child.name) 

59 return self 

60 

61 def __repr_value__(self): 

62 return f"{','.join(child.name for child in self.children)}" 

63 

64class AliasDefinition(TypeDefinition, ExpressionDefinition): 

65 

66 @model_validator(mode='after') 

67 def set_children(self): 

68 self.children = [self.type] 

69 return self 

70 

71 def __repr_value__(self): 

72 return f"{self.name}" 

73 

74class ModelDefinition(TypeDefinition, ContainedDefinitions): 

75 indexes: list[list[EDGE]] 

76 subtypes: list[TypeDefinition] 

77 edges: list[EdgeDefinition] 

78 

79 @model_validator(mode='after') 

80 def validate_indexes(self): 

81 self.children = self.subtypes + self.edges 

82 

83 subtype_names = set() 

84 for subtype in self.subtypes: 

85 # raise error if subtype name is duplicated 

86 if subtype.name in subtype_names: 

87 raise ValueError(f'Subtype {subtype.name} is duplicated in model {self.name}') 

88 subtype_names.add(subtype.name) 

89 

90 edge_names = set() 

91 for edge in self.edges: 

92 # raise error if edge name is duplicated 

93 if edge.name in edge_names: 

94 raise ValueError(f'Edge name {edge.name} is duplicated in model {self.name}') 

95 edge_names.add(edge.name) 

96 

97 idx_names = set() 

98 for idx in self.indexes: 

99 for name in idx: 

100 # raise error if index name is not an edge name 

101 if name not in edge_names: 

102 raise ValueError(f'Index {name} is not an edge name in model {self.name}') 

103 if name in idx_names: 

104 raise ValueError(f'Index Edge {name} is in multiple indexes in model {self.name}') 

105 idx_names.add(name) 

106 return self 

107 

108 def __repr_value__(self): 

109 def format_index(idx): 

110 return "(" + ','.join(idx) + ")" 

111 

112 return self.name + \ 

113 ''.join(format_index(idx) for idx in self.indexes) + \ 

114 "{" + ','.join(edge.name for edge in self.children) + "}" 

115 

116class EdgeDefinition(ExpressionDefinition): 

117 name: EDGE 

118 cardinality: Optional[Literal['*','?','+','!']] 

119 _ref: Optional[str] 

120 

121 @model_validator(mode='after') 

122 def set_children(self): 

123 self.children = [self.type] 

124 return self 

125 

126 def __repr_value__(self): 

127 return f"{self.name}{self.cardinality or ''}" 

128 

129class Expression(AST): 

130 """ Abstract class for all AST nodes that are expressions """ 

131 pass 

132 

133class Identifier(Expression): 

134 name: str 

135 

136 @property 

137 def kind(self): 

138 if self.name[0].isupper(): 

139 if any(letter.islower() for letter in self.name): 

140 return 'type' 

141 return 'const' 

142 if self.name[0].islower(): 

143 return 'edge' 

144 

145 def __repr_value__(self): 

146 return self.name 

147 

148class LiteralExpression(Expression): 

149 value: Union[str, float, bool] 

150 

151 def __repr_value__(self): 

152 return repr(self.value) 

153 

154class Operation(Expression): 

155 _OP_NAMES = { 

156 '!': 'not', '~': 'invert', 

157 '!=': 'ne', '==': 'eq', 

158 '>=': 'gte', '<=': 'lte', 

159 '>': 'gt', '<': 'lt', 

160 '+': 'add', '-': 'sub', 

161 '*': 'mul', '/': 'div', '%': 'mod', 

162 '|': 'or', '&': 'and', '^': 'xor', 

163 '[]': 'filter', '.': 'dot', 'is': 'is', 

164 } 

165 

166 op: Literal[ 

167 '!','~', 

168 '!=','==','>=','<=','>','<', 

169 '+','-','*','/','%', 

170 '|','&','^', 

171 '[]','.','is', 

172 ] 

173 children: list[Expression] 

174 

175 @property 

176 def name(self): 

177 return self._OP_NAMES[self.op] 

178 

179 def __repr_value__(self): 

180 return self.name