from dataclasses import dataclass from enum import Enum, auto from lsprotocol.types import Location, Position, Range from skillls.types import URI class SyntaxError(Exception): pass class ParenMismatchErrorKind(Enum): TooManyClosed = "Found too many closing parens" TooManyOpened = "Found too many open parens" @dataclass class ParenMismatchError(SyntaxError): kind: ParenMismatchErrorKind loc: Range def _check_for_matching_parens(content: str) -> list[Exception]: excs: list[Exception] = [] opened = 0 line = 0 col = 0 last_open: Position = Position(0, 0) last_close: Position = Position(0, 0) for char in content: match char: case "(": opened += 1 last_open = Position(line, col) case ")": opened -= 1 if opened < 0: excs.append( ParenMismatchError( ParenMismatchErrorKind.TooManyClosed, Range(Position(line, col), Position(line, col + 1)), ) ) opened = 0 last_close = Position(line, col) case "\n": line += 1 col = -1 case _: pass col += 1 if opened > 0: excs.append( ParenMismatchError( ParenMismatchErrorKind.TooManyOpened, Range(last_open, Position(last_open.line, last_open.character + 1)), ) ) return excs def check_content_for_errors(clean_content: str) -> None: excs: list[Exception] = [] excs.extend(_check_for_matching_parens(clean_content)) if excs: raise ExceptionGroup("", excs)