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
« 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
7class Environment:
8 """ Abstract class for Type Environments """
9 local: dict[str, ChildEnvironment]
11 def __init__(self):
12 self.local = {}
14 @property
15 def path(self) -> tuple[str]:
16 raise NotImplementedError('Abstract Environment does not define `.path`')
18 @property
19 def root(self) -> RootEnvironment:
20 raise NotImplementedError('Abstract Environment does not define `.root`')
22 @property
23 def global_name(self) -> str:
24 return '.'.join(self.path)
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 )
34 def lookup(self, key: str) -> Optional[ChildEnvironment]:
35 raise NotImplementedError('Abstract Environment does not define `lookup()`')
37 def get_child(self, key) -> Optional[ChildEnvironment]:
38 return self.local.get(key)
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)
49 def __repr__(self):
50 return self.global_name + '{' + ','.join(self.local.keys()) + '}'
52class RootEnvironment(Environment):
53 def __init__(self):
54 super().__init__()
56 @property
57 def path(self) -> tuple[str]:
58 return tuple()
60 @property
61 def root(self) -> RootEnvironment:
62 return self
64 def lookup(self, key: str) -> Optional[ChildEnvironment]:
65 return self.get_child(key)
67class ChildEnvironment(Environment):
68 name: str
69 parent: Environment
70 evaluator: TypeEvaluator
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 )
82 @property
83 def path(self) -> tuple[str]:
84 return (*self.parent.path, self.name)
86 @property
87 def root(self) -> RootEnvironment:
88 return self.parent.root
90 @property
91 def type(self) -> Expression:
92 return self.evaluator.get_type()
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)
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]
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
118 def get_type(self):
119 if self.status == 'done':
120 assert self.cached_type is not None
121 return self.cached_type
123 if self.status == 'processing':
124 raise Exception('Already has been called, possible circular reference')
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
133 raise Exception(f'Unknown status "{self.status}"')