""" Colorize matching parenthesis in a string. The "primary" entry point is ``colorize``, and ``Colored`` the "primary" data structure. """ import io from typing import Literal, Generator from dataclasses import dataclass __version__ = "0.2" @dataclass class Stackpointer(): """A "fancy" integer which implements pre-and post decrement.""" depth: int = 0 def __call__(self, c: int): """ Update and return current state. :param c: An integer. Positive integers increment the value after returning it, negative integers decrement the value before returning it, 0 returns the value as is. """ ret: int if c > 0: ret = self.depth self.depth += 1 elif c < 0: self.depth -= 1 ret = self.depth else: ret = self.depth return ret @dataclass class Colored: """ Tag an item with a color "depth". Depth is an arbitarary (positive) integer, from 0 and incrementing by up. Should map to colors. """ depth: int item: str def color(depth: int, c: str) -> Colored: """Write a highlighted string.""" return Colored(depth, c) def colorize(strm: io.TextIOBase) -> Generator[str | Colored, None, None]: """ Colorize a given string. :param strm: Text stream to get contents from. Use ``io.StringIO`` if you want to pass a string. :returns: Yields a stream of tokens, where each token is either a literal string, laternatively, it's a ``Colored`` object, which attaches a color number to the string. """ in_string: Literal[False] | Literal['"'] | Literal["'"] = False paren = 0 brace = 0 brack = 0 depth = Stackpointer() while c := strm.read(1): match [in_string, c]: case ['"', '"']: # The string is ending in_string = False yield c case ["'", "'"]: in_string = False # The string is ending yield c case [False, _]: # We are not in a string, emit the next character # colored if it's a parenthesis (or similar), and # plainly otherwise. # Can also ether strings match c: case '(': yield color(depth(1), c) paren += 1 case ')': yield color(depth(-1), c) paren -= 1 case '[': yield color(depth(1), c) brack += 1 case ']': yield color(depth(-1), c) brack -= 1 case '{': yield color(depth(1), c) brace += 1 case '}': yield color(depth(-1), c) brace -= 1 case "'" | '"': in_string = c yield c case '\\': yield c yield strm.read(1) case c: yield c case [_, _]: yield c