Coverage for kye/compile.py: 16%

119 statements  

« 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 

5 

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

67 

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 ) 

78 

79 

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

93 

94 

95def compile_definitions(ast: AST.ModuleDefinitions): 

96 assert isinstance(ast, AST.ModuleDefinitions) 

97 

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) 

106 

107 for type_def in ast.children: 

108 typ = compile_type_definition(type_def, symbols) 

109 

110 if isinstance(type_def, AST.ModelDefinition): 

111 for edge_def in type_def.edges: 

112 compile_edge_definition(edge_def, typ, symbols) 

113 

114 for exp in symbols.definitions: 

115 print(exp, symbols.get_return_type(exp)) 

116 

117 return symbols 

118 

119 

120class Models: 

121 definitions: dict[str, Types.Definition] 

122 expressions: dict[str, AST.Expression] 

123 

124 def __init__(self): 

125 self.definitions = {} 

126 self.expressions = {} 

127 

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 

134 

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 

142 

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 

150 

151 def get_type(self, ref: Types.TYPE_REF): 

152 typ = self.definitions[ref] 

153 assert isinstance(typ, Types.Type) 

154 return typ 

155 

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) 

162 

163 if edge is None: 

164 raise KeyError(f"Unknown edge `{typ.ref}.{edge_ref}`") 

165 assert isinstance(edge, Types.Edge) 

166 return edge 

167 

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 

187 

188 def __contains__(self, ref: str): 

189 return ref in self.definitions