Coverage for kye/parser/environment.py: 0%

89 statements  

« prev     ^ index     » next       coverage.py v7.3.2, created at 2023-12-01 16:38 -0700

1from __future__ import annotations 

2import kye.parser.kye_ast as AST 

3from kye.parser.types import Expression 

4from typing import Literal, Optional, Callable 

5 

6 

7class Environment: 

8 """ Abstract class for Type Environments """ 

9 local: dict[str, ChildEnvironment] 

10 

11 def __init__(self): 

12 self.local = {} 

13 

14 @property 

15 def path(self) -> tuple[str]: 

16 raise NotImplementedError('Abstract Environment does not define `.path`') 

17 

18 @property 

19 def root(self) -> RootEnvironment: 

20 raise NotImplementedError('Abstract Environment does not define `.root`') 

21 

22 @property 

23 def global_name(self) -> str: 

24 return '.'.join(self.path) 

25 

26 def define(self, key: str, eval: Callable[[AST.AST, Environment], Expression], ast: Optional[AST.AST] = None): 

27 self.local[key] = ChildEnvironment( 

28 name=key, 

29 parent=self, 

30 eval=eval, 

31 ast=ast, 

32 ) 

33 

34 def lookup(self, key: str) -> Optional[ChildEnvironment]: 

35 raise NotImplementedError('Abstract Environment does not define `lookup()`') 

36 

37 def get_child(self, key) -> Optional[ChildEnvironment]: 

38 return self.local.get(key) 

39 

40 def apply_ast(self, ast: AST.AST, eval: Callable[[AST.AST, Environment], Expression]): 

41 env = self 

42 if isinstance(ast, AST.Definition): 

43 self.define(ast.name, eval=eval, ast=ast) 

44 env = env.get_child(ast.name) 

45 if isinstance(ast, AST.ContainedDefinitions): 

46 for child in ast.children: 

47 env.apply_ast(child, eval) 

48 

49 def __repr__(self): 

50 return self.global_name + '{' + ','.join(self.local.keys()) + '}' 

51 

52class RootEnvironment(Environment): 

53 def __init__(self): 

54 super().__init__() 

55 

56 @property 

57 def path(self) -> tuple[str]: 

58 return tuple() 

59 

60 @property 

61 def root(self) -> RootEnvironment: 

62 return self 

63 

64 def lookup(self, key: str) -> Optional[ChildEnvironment]: 

65 return self.get_child(key) 

66 

67class ChildEnvironment(Environment): 

68 name: str 

69 parent: Environment 

70 evaluator: TypeEvaluator 

71 

72 def __init__(self, name: str, parent: Environment, eval: Callable[[AST.AST, Environment], Expression], ast=Optional[AST.AST]): 

73 super().__init__() 

74 self.name = name 

75 self.parent = parent 

76 self.evaluator = TypeEvaluator( 

77 eval=eval, 

78 env=self, 

79 ast=ast, 

80 ) 

81 

82 @property 

83 def path(self) -> tuple[str]: 

84 return (*self.parent.path, self.name) 

85 

86 @property 

87 def root(self) -> RootEnvironment: 

88 return self.parent.root 

89 

90 @property 

91 def type(self) -> Expression: 

92 return self.evaluator.get_type() 

93 

94 def lookup(self, key: str) -> Optional[ChildEnvironment]: 

95 if key == self.name: 

96 return self 

97 return self.get_child(key) or self.parent.lookup(key) 

98 

99class TypeEvaluator: 

100 """ 

101 Type Evaluator houses the evaluation function 

102 caching the resulting type and also making sure 

103 that it is not circularly referenced 

104 """ 

105 eval: Callable[[AST.AST, Environment], Expression] 

106 env: Environment 

107 ast: Optional[AST.AST] 

108 status: Literal['new','processing','done'] 

109 cached_type: Optional[Expression] 

110 

111 def __init__(self, eval: Callable[[AST.AST, Environment], Expression], env: Environment, ast: Optional[AST.AST]): 

112 self.eval = eval 

113 self.env = env 

114 self.ast = ast 

115 self.status = 'new' 

116 self.cached_type = None 

117 

118 def get_type(self): 

119 if self.status == 'done': 

120 assert self.cached_type is not None 

121 return self.cached_type 

122 

123 if self.status == 'processing': 

124 raise Exception('Already has been called, possible circular reference') 

125 

126 if self.status == 'new': 

127 self.status = 'processing' 

128 self.cached_type = self.eval(self.ast, self.env) 

129 assert isinstance(self.cached_type, Expression) 

130 self.status = 'done' 

131 return self.cached_type 

132 

133 raise Exception(f'Unknown status "{self.status}"')