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
« 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
6TAB = ' '
8TYPE = constr(pattern=r'[A-Z][a-z][a-zA-Z]*')
9EDGE = constr(pattern=r'[a-z][a-z_]*')
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
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
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}"
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
44 def __str__(self):
45 return self.name or super().__str__()
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)
53class Script(AST):
54 children: list[Union[TypeAlias, Model]]
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
66 def __repr__(self):
67 return f"Script<{','.join(child.name for child in self.children)}>"
69class Model(AST):
70 name: TYPE
71 indexes: list[Index]
72 edges: list[Edge]
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)
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
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) + "}"
101class Index(AST):
102 edges: list[EDGE]
104 def __str__(self):
105 return f"({','.join(self.edges)})"
107 def __repr__(self):
108 return str(self)
110class Edge(AST):
111 name: EDGE
112 typ: Optional[Type]
113 cardinality: Optional[Literal['*','?','+','!']]
115 @model_validator(mode='after')
116 def set_children(self):
117 if self.typ:
118 self.children = [self.typ]
119 return self
121 def __repr__(self):
122 return f"{self.name}:{self.typ or ''}{self.cardinality or ''}"
124class TypeRef(AST):
125 name: TYPE
126 index: Optional[Index] = None
128 @model_validator(mode='after')
129 def set_children(self):
130 if self.index:
131 self.children = [self.index]
132 return self
134 def __repr__(self):
135 return self.name + \
136 (repr(self.index) if self.index else '')
138class TypeAlias(AST):
139 name: TYPE
140 typ: Type
142 @model_validator(mode='after')
143 def set_children(self):
144 self.children = [self.typ]
145 return self
147 def __repr__(self):
148 return f"{self.name}:{self.typ}"
150Type = Union[TypeAlias, Model, TypeRef]