from emis_funky_funktions import * from typing import Collection, Mapping, Sequence, Tuple, TypeAlias import types_ from match_tree import LeafNode, IntNode, MatchTree, StructurePath, FAIL_NODE, EMPTY_STRUCT_PATH Pattern: TypeAlias = 'NamePattern | IntPattern | SPattern | IgnorePattern' @dataclass(frozen=True) class NamePattern: """ A pattern which always succeeds to match, and binds a whole expression to a name """ name: str def match_tree(self, location: StructurePath, success_leaf: 'MatchTree[A]') -> 'MatchTree[A]': return success_leaf def bindings(self) -> Collection[Tuple[str, StructurePath]]: return ((self.name, EMPTY_STRUCT_PATH),) def binds(self, var: str) -> bool: """ Test to see if this pattern binds a given variable """ return var == self.name def __repr__(self) -> str: return self.name @dataclass(frozen=True) class IgnorePattern: """ A pattern which always succeeds to match, but binds nothing """ def match_tree(self, location: StructurePath, success_leaf: 'MatchTree[A]') -> 'MatchTree[A]': return success_leaf def bindings(self) -> Collection[Tuple[str, StructurePath]]: return tuple() def binds(self, var: str) -> bool: """ Test to see if this pattern binds a given variable For an `IgnorePattern` this is always false """ return False def __repr__(self) -> str: return '_' @dataclass(frozen=True) class IntPattern: value: int def match_tree(self, location: StructurePath, success_leaf: 'MatchTree[A]') -> 'MatchTree[A]': return IntNode(location, {self.value: success_leaf}, LeafNode([])) def bindings(self) -> Collection[Tuple[str, StructurePath]]: return tuple() def binds(self, var: str) -> bool: """ Test to see if this pattern binds a given variable For an `IntPattern` this is always false """ return False def __repr__(self) -> str: return repr(self.value) @dataclass(frozen=True) class SPattern: pred: Pattern def match_tree(self, location: StructurePath, success_leaf: 'MatchTree[A]') -> 'MatchTree[A]': match self.pred.match_tree(StructurePath((*location, 0)), success_leaf): case IntNode(child_location, trees, fallback) as child: # If the child check is also an int node on pred of our current location: # "raise" that node to be at our current level. if child_location == (*location, 0): return IntNode(location, dict(((0, FAIL_NODE), *((1 + v, mt) for v, mt in trees.items()))), fallback) else: return IntNode(location, {0: FAIL_NODE}, child) case child: return IntNode(location, {0: FAIL_NODE}, child) def bindings(self) -> Collection[Tuple[str, StructurePath]]: # Prepend each binding path of the child pattern with 0 (i.e. subtract one from an int) return tuple((name, StructurePath((0, *path))) for (name, path) in self.pred.bindings()) def binds(self, var: str) -> bool: """ Test to see if this pattern binds a given variable """ return self.pred.binds(var) def __repr__(self) -> str: return 'S ' + repr(self.pred)