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

93 statements  

« prev     ^ index     » next       coverage.py v7.3.2, created at 2023-11-13 15:17 -0700

1from __future__ import annotations 

2from pydantic import BaseModel, model_validator, constr 

3from typing import Optional, Literal, Union 

4from lark import tree 

5 

6TAB = ' ' 

7 

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

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

10 

11class TokenPosition(BaseModel): 

12 line: int 

13 column: int 

14 end_line: int 

15 end_column: int 

16 start_pos: int 

17 end_pos: int 

18 

19 @model_validator(mode='before') 

20 @classmethod 

21 def from_meta(cls, meta): 

22 if isinstance(meta, tree.Meta): 

23 return { 

24 'line': meta.line, 

25 'column': meta.column, 

26 'end_line': meta.end_line, 

27 'end_column': meta.end_column, 

28 'start_pos': meta.start_pos, 

29 'end_pos': meta.end_pos, 

30 } 

31 return meta 

32 

33 def __repr__(self): 

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

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

36 

37class AST(BaseModel): 

38 name: Optional[str] = None 

39 children: list[AST] = [] 

40 meta: TokenPosition 

41 scope: Optional[dict] = None 

42 type_ref: Optional[str] = None 

43 

44 def __str__(self): 

45 return self.name or super().__str__() 

46 

47 def traverse(self, path=tuple()): 

48 path = path + (self,) 

49 for child in self.children: 

50 yield path, child 

51 yield from child.traverse(path=path) 

52 

53class Script(AST): 

54 children: list[Union[TypeAlias, Model]] 

55 

56 @model_validator(mode='after') 

57 def validate_definitions(self): 

58 type_names = set() 

59 for child in self.children: 

60 # raise error if definition name is duplicated 

61 if child.name in type_names: 

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

63 type_names.add(child.name) 

64 return self 

65 

66 def __repr__(self): 

67 return f"Script<{','.join(child.name for child in self.children)}>" 

68 

69class Model(AST): 

70 name: TYPE 

71 indexes: list[Index] 

72 edges: list[Edge] 

73 

74 @model_validator(mode='after') 

75 def validate_indexes(self): 

76 # self.children.extend(self.indexes) 

77 self.children = self.edges 

78 edge_names = set() 

79 for edge in self.edges: 

80 # raise error if edge name is duplicated 

81 if edge.name in edge_names: 

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

83 edge_names.add(edge.name) 

84 

85 idx_names = set() 

86 for idx in self.indexes: 

87 for name in idx.edges: 

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

89 if name not in edge_names: 

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

91 if name in idx_names: 

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

93 idx_names.add(name) 

94 return self 

95 

96 def __repr__(self): 

97 return self.name + \ 

98 ''.join(repr(idx) for idx in self.indexes) + \ 

99 "{" + ','.join(edge.name for edge in self.edges) + "}" 

100 

101class Index(AST): 

102 edges: list[EDGE] 

103 

104 def __str__(self): 

105 return f"({','.join(self.edges)})" 

106 

107 def __repr__(self): 

108 return str(self) 

109 

110class Edge(AST): 

111 name: EDGE 

112 typ: Optional[Type] 

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

114 

115 @model_validator(mode='after') 

116 def set_children(self): 

117 if self.typ: 

118 self.children = [self.typ] 

119 return self 

120 

121 def __repr__(self): 

122 return f"{self.name}:{self.typ or ''}{self.cardinality or ''}" 

123 

124class TypeRef(AST): 

125 name: TYPE 

126 index: Optional[Index] = None 

127 

128 @model_validator(mode='after') 

129 def set_children(self): 

130 if self.index: 

131 self.children = [self.index] 

132 return self 

133 

134 def __repr__(self): 

135 return self.name + \ 

136 (repr(self.index) if self.index else '') 

137 

138class TypeAlias(AST): 

139 name: TYPE 

140 typ: Type 

141 

142 @model_validator(mode='after') 

143 def set_children(self): 

144 self.children = [self.typ] 

145 return self 

146 

147 def __repr__(self): 

148 return f"{self.name}:{self.typ}" 

149 

150Type = Union[TypeAlias, Model, TypeRef]