Coverage for kye/compile.py: 16%
119 statements
« prev ^ index » next coverage.py v7.3.2, created at 2023-12-06 14:19 -0700
« prev ^ index » next coverage.py v7.3.2, created at 2023-12-06 14:19 -0700
1from __future__ import annotations
2from typing import Optional, Literal, Union
3import kye.parser.kye_ast as AST
4import kye.types as Types
6def compile_expression(ast: AST.Expression, typ: Optional[Types.Type], symbols: Models) -> Types.Expression:
7 assert isinstance(ast, AST.Expression)
8 if isinstance(ast, AST.Identifier):
9 # TODO: Maybe somehow push this location onto a call stack?
10 if ast.kind == 'type':
11 # Maybe this could also be a call where the type is `Object` and it is bound to the type
12 return Types.Expression(
13 returns=symbols.get_return_type(ast.name),
14 loc=ast.meta,
15 )
16 if ast.kind == 'edge':
17 edge = symbols.get_edge(typ, ast.name)
18 return Types.CallExpression(
19 bound=None,
20 args=[],
21 returns=symbols.get_return_type(edge.ref),
22 edge=edge,
23 loc=ast.meta,
24 )
25 elif isinstance(ast, AST.LiteralExpression):
26 if type(ast.value) is str:
27 typ = symbols.get_type('String')
28 elif type(ast.value) is bool:
29 typ = symbols.get_type('Boolean')
30 elif isinstance(ast.value, (int, float)):
31 typ = symbols.get_type('Number')
32 else:
33 raise Exception()
34 return Types.LiteralExpression(returns=typ, value=ast.value, loc=ast.meta)
35 elif isinstance(ast, AST.Operation):
36 assert len(ast.children) >= 1
37 expr = compile_expression(ast.children[0], typ, symbols)
38 if ast.name == 'filter':
39 assert len(ast.children) <= 2
40 if len(ast.children) == 2:
41 filter = compile_expression(ast.children[1], expr.returns, symbols)
42 expr = Types.CallExpression(
43 bound=expr,
44 args=[filter],
45 returns=expr.returns,
46 edge=symbols.get_edge(expr.returns, '$filter'),
47 loc=ast.meta,
48 )
49 elif ast.name == 'dot':
50 assert len(ast.children) >= 2
51 for child in ast.children[1:]:
52 expr = compile_expression(child, expr.returns, symbols)
53 else:
54 for child in ast.children[1:]:
55 expr = Types.CallExpression(
56 bound=expr,
57 args=[
58 compile_expression(child, typ, symbols)
59 ],
60 returns=expr.returns,
61 edge=symbols.get_edge(expr.returns, '$' + ast.name),
62 loc=ast.meta,
63 )
64 return expr
65 else:
66 raise Exception('Unknown Expression')
68def compile_edge_definition(ast: AST.EdgeDefinition, model: Types.Type, symbols: Models) -> tuple[Types.Edge, Types.Expression]:
69 assert isinstance(ast, AST.EdgeDefinition)
70 return symbols.define_edge(
71 name=ast.name,
72 model=model,
73 nullable=ast.cardinality in ('?','*'),
74 multiple=ast.cardinality in ('+','*'),
75 loc=ast.meta,
76 expr=ast.type,
77 )
80def compile_type_definition(ast: AST.TypeDefinition, symbols: Models) -> tuple[Types.Type, Types.Expression]:
81 assert isinstance(ast, AST.TypeDefinition)
82 if isinstance(ast, AST.AliasDefinition):
83 return symbols.define_type(ref=ast.name, loc=ast.meta, expr=ast.type)
84 elif isinstance(ast, AST.ModelDefinition):
85 return symbols.define_type(
86 ref=ast.name,
87 indexes=ast.indexes,
88 loc=ast.meta,
89 extends=symbols.get_type('Object')
90 )
91 else:
92 raise Exception('Unknown TypeDefinition')
95def compile_definitions(ast: AST.ModuleDefinitions):
96 assert isinstance(ast, AST.ModuleDefinitions)
98 symbols = Models()
99 Object = symbols.define_type(ref='Object')
100 String = symbols.define_type(ref='String', extends=Object)
101 Number = symbols.define_type(ref='Number', extends=Object)
102 Boolean = symbols.define_type(ref='Boolean', extends=Object)
103 symbols.define_edge(model=Object, name='$filter', args=[Boolean], returns=Object)
104 symbols.define_edge(model=String, name='length', returns=Number)
105 symbols.define_edge(model=Number, name='$gt', args=[Number], returns=Boolean)
107 for type_def in ast.children:
108 typ = compile_type_definition(type_def, symbols)
110 if isinstance(type_def, AST.ModelDefinition):
111 for edge_def in type_def.edges:
112 compile_edge_definition(edge_def, typ, symbols)
114 for exp in symbols.definitions:
115 print(exp, symbols.get_return_type(exp))
117 return symbols
120class Models:
121 definitions: dict[str, Types.Definition]
122 expressions: dict[str, AST.Expression]
124 def __init__(self):
125 self.definitions = {}
126 self.expressions = {}
128 def _define(self, definition: Types.Definition, expr: Optional[AST.Expression] = None):
129 assert definition.ref not in self.definitions
130 self.definitions[definition.ref] = definition
131 assert definition.returns is not None or expr is not None, 'Must either have returns defined or expression defined'
132 if expr is not None:
133 self.expressions[definition.ref] = expr
135 def define_type(self, expr: Optional[AST.Expression] = None, **kwargs) -> Types.Type:
136 typ = Types.Type(**kwargs)
137 # If the type does not define an expression, then return itself
138 if expr is None:
139 typ.returns = typ
140 self._define(typ, expr)
141 return typ
143 def define_edge(self, expr: Optional[AST.Expression] = None, **kwargs) -> Types.Edge:
144 edge = Types.Edge(**kwargs)
145 self._define(edge, expr)
146 if edge.name not in edge.model.edges:
147 edge.model.edges[edge.name] = edge
148 assert edge.model.edges[edge.name] == edge
149 return edge
151 def get_type(self, ref: Types.TYPE_REF):
152 typ = self.definitions[ref]
153 assert isinstance(typ, Types.Type)
154 return typ
156 def get_edge(self, typ: Types.Type, edge_ref: Types.EDGE_REF):
157 extended_type = typ
158 edge = self.definitions.get(extended_type.ref + '.' + edge_ref)
159 while edge is None and extended_type.extends is not None:
160 extended_type = extended_type.extends
161 edge = self.definitions.get(extended_type.ref + '.' + edge_ref)
163 if edge is None:
164 raise KeyError(f"Unknown edge `{typ.ref}.{edge_ref}`")
165 assert isinstance(edge, Types.Edge)
166 return edge
168 def get_return_type(self, ref: str) -> Types.Type:
169 if ref not in self.definitions:
170 raise KeyError(f'Unknown symbol `{ref}`')
171 definition = self.definitions[ref]
172 if definition is None:
173 raise Exception(f'Possible circular reference for `{ref}`')
174 if getattr(definition, 'returns') is None:
175 # Clear the table first, so that if the function calls itself
176 # it will get a circular reference error
177 self.definitions[ref] = None
178 definition.expr = compile_expression(
179 ast=self.expressions[ref],
180 typ=definition.model if isinstance(definition, Types.Edge) else None,
181 symbols=self
182 )
183 assert isinstance(definition.expr, Types.Expression)
184 definition.returns = definition.expr.returns
185 self.definitions[ref] = definition
186 return definition.returns
188 def __contains__(self, ref: str):
189 return ref in self.definitions