From 51984e297bf431a2114c91b9cf23d092dfb2e73c Mon Sep 17 00:00:00 2001 From: AcerecA Date: Sun, 16 Nov 2025 14:56:14 +0100 Subject: [PATCH] update --- examples/example.il | 44 +-- pyproject.toml | 7 +- skillls/builtins/__init__.py | 0 skillls/builtins/common.py | 145 ++++++++++ skillls/builtins/functions.defs | 6 + skillls/builtins/functions.py | 21 ++ skillls/main.py | 474 +++++++++----------------------- uv.lock | 36 +++ 8 files changed, 376 insertions(+), 357 deletions(-) create mode 100644 skillls/builtins/__init__.py create mode 100644 skillls/builtins/common.py create mode 100644 skillls/builtins/functions.defs create mode 100644 skillls/builtins/functions.py diff --git a/examples/example.il b/examples/example.il index e49a736..b457fa0 100644 --- a/examples/example.il +++ b/examples/example.il @@ -1,22 +1,34 @@ example = nil example2 = example -; func2(g_arg1 g_arg2 ?g_args1 1 ?g_argw 2) => nil -(procedure func2(arg1 arg2 @key (args 1) (argw 2) "ggng") - ; some stuff to do - a = some_obj->field1 - some_obj->field2 = 2 - args = 2 -) +(call qdwdq) -( - (let (some vars (default 0)) - ; ... some wall of text - "))\"" - wqdqwf = '(doqwf) - var = 1.3 - vars = 231 - qqvwv - cfunc() +;; func2(g_arg1 g_arg2 ?g_args1 1 ?g_argw 2) => nil +(procedure Func2(arg1 arg2 @keys (args "ss") (argw 2) "ggng") + (let () + ; some stuff to do + a = some_obj->field1 + some_obj->field2 = 2 + db_obj->help() + args = 2 + args + + (procedure Wqrqw(a1 a2 @keys (a "12") "sqd") + ) ) ) + +(let (some vars (default "sd")) + ; ... some wall of text + "))\"" + wqdqwf = '(doqwf) + 'wq + var = 1.3 + vars = 231 + qqvwv + if(expr then expr else expr) + cfunc() +) + + +somfunccall("somecalcfunc()") diff --git a/pyproject.toml b/pyproject.toml index f5b4e7f..cccf925 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -5,7 +5,9 @@ version = "0.1.0" dependencies = [ "parsimonious~=0.10.0", "pygls", - "rich" + "rich", + "tree-sitter>=0.24.0", + "tree-sitter-skill>=0.1.5", ] [project.optional-dependencies] @@ -35,3 +37,6 @@ include = "skillls" [tools.ruff] line-length = 100 include = ['ALL'] + +[tool.uv.sources] +tree-sitter-skill = { git = "ssh://git@git.acereca.net/acereca/tree-sitter-skill.git" } diff --git a/skillls/builtins/__init__.py b/skillls/builtins/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/skillls/builtins/common.py b/skillls/builtins/common.py new file mode 100644 index 0000000..b328624 --- /dev/null +++ b/skillls/builtins/common.py @@ -0,0 +1,145 @@ +from abc import ABC +from collections.abc import Mapping +from dataclasses import dataclass, field +from enum import Enum +from typing import ClassVar + +from lsprotocol.types import SymbolKind + + +class SkillDataType(Enum): + array = "a" + """array""" + + ddUserType = "b" + """Boolean""" + + opfcontext = "C" + """OPF Context""" + + dbobject = "d" + """Cadence database object (CDBA)""" + + envobj = "e" + """environment""" + + flonum = "f" + """floating-point number""" + + opffile = "F" + """OPF file ID""" + + general = "g" + """any data type""" + + nil = "g" + """""" + + dgbSpecIlUserType = "G" + """gdm spec""" + + hdbobject = "h" + """hierarchical database configuration object""" + + list = "l" + """linked list""" + + nmpIlUserType = "m" + """nmpll user type""" + + cdsEvalObject = "M" + """Cadence evaluation object""" + + number = "n" + """integere of floating point number""" + + userType = "o" + """user defined type (other)""" + + port = "p" + """I/O port""" + + gdmspecListIlUSerType = "q" + """ gdm spec list""" + + defstruct = "r" + """defstruct""" + + rodObj = "R" + """relative object design (ROD) object""" + + symbol = "s" + """symbol""" + + stringSymbol = "S" + """symbol or character string""" + + string = "t" + """character string (text)""" + + function = "u" + """function object, either the name if a function (symbol) or a lambda function body (list)""" + + funobj = "U" + """function object""" + + hdbpath = "v" + """""" + + wtype = "w" + """window type""" + + integer = "x" + """integer type""" + + binary = "y" + """binary function""" + + pointer = "&" + """pointer type""" + + +@dataclass(frozen=True) +class Builtin(ABC): + token: str + kind: ClassVar[SymbolKind] + + +@dataclass(frozen=True) +class Variable(Builtin): + kind: ClassVar[SymbolKind] = SymbolKind.Variable + typ: SkillDataType + default: str | None = None + + +@dataclass(frozen=True) +class AnonymousVariable(Builtin): + kind: ClassVar[SymbolKind] = SymbolKind.Variable + typ: SkillDataType + default: str | None = None + + +@dataclass(frozen=True) +class Procedure(Builtin): + kind: ClassVar[SymbolKind] = SymbolKind.Function + args: Mapping[str, Variable] = field(default_factory=dict) + rest: Variable | None = None + kwargs: Mapping[str, Variable] | Mapping[str, AnonymousVariable] = field( + default_factory=dict + ) + """list of ``Variable` if ``@key`` was used, and ``AnonymousVariable`` if ``@option`` was used""" + ret: SkillDataType = SkillDataType.nil + + @property + def has_options(self) -> bool: + return bool(self.kwargs) and isinstance( + next(iter(self.kwargs.values())), + AnonymousVariable, + ) + + @property + def has_keys(self) -> bool: + return bool(self.kwargs) and isinstance( + next(iter(self.kwargs.values())), + Variable, + ) diff --git a/skillls/builtins/functions.defs b/skillls/builtins/functions.defs new file mode 100644 index 0000000..b399802 --- /dev/null +++ b/skillls/builtins/functions.defs @@ -0,0 +1,6 @@ +;; append +append( + l_list1 + l_list2 +) => l_result + diff --git a/skillls/builtins/functions.py b/skillls/builtins/functions.py new file mode 100644 index 0000000..13c33a7 --- /dev/null +++ b/skillls/builtins/functions.py @@ -0,0 +1,21 @@ +from collections.abc import Mapping + +from .common import Procedure, SkillDataType, Variable + +NUM = SkillDataType.number +ANY = SkillDataType.general + + +FUNCTIONS: Mapping[str, Procedure] = { + "plus": Procedure( + "plus", + ret=NUM, + args={ + "op1": Variable("op1", NUM), + "op2": Variable("op2", NUM), + }, + rest=Variable("op3", NUM), + ), +} + +FUNCTIONS["plus"] diff --git a/skillls/main.py b/skillls/main.py index 39da720..1347206 100644 --- a/skillls/main.py +++ b/skillls/main.py @@ -1,9 +1,12 @@ -from collections.abc import Generator +from collections.abc import Callable, Generator, Sequence from dataclasses import dataclass, field +from difflib import Differ from itertools import chain -from logging import INFO, basicConfig, debug, error, getLogger, info, warning +from logging import DEBUG, INFO, basicConfig, debug, error, getLogger, info, warning from re import findall, finditer, fullmatch, match as rematch +import re from time import time +from cattrs import Converter from lsprotocol.types import ( INLAY_HINT_RESOLVE, TEXT_DOCUMENT_DID_CHANGE, @@ -28,23 +31,37 @@ from lsprotocol.types import ( InlayHintKind, InlayHintParams, MessageType, + NotebookDocumentSyncOptions, Position, Range, SymbolKind, + TextDocumentContentChangeEvent, + TextDocumentContentChangeEvent_Type1, + TextDocumentSyncKind, ) +from pygls.protocol import LanguageServerProtocol, default_converter +from tree_sitter_skill import language as skill_lang +from tree_sitter import Language, Node, Parser, Query, Tree + from pygls.server import LanguageServer from pygls.workspace import TextDocument -from skillls.builtins.common import SkillDataType -from skillls.parsing.iterative import IterativeParser, TokenParser - from .cache import Cache +SKILL_LANG = Language(skill_lang()) +SKILL_PARSER = Parser(SKILL_LANG) + URI = str -basicConfig(filename="skillls.log", filemode="w", level=INFO) +basicConfig( + filename="skillls.log", + filemode="w", + level=DEBUG, + format="%(asctime)s [%(levelname)s]: %(message)s", +) +logger = getLogger() cache: Cache[str, CompletionItem] = Cache() @@ -93,6 +110,19 @@ class LetEnvironment(Environment): locals: set[str] = field(default_factory=set) +def offset_range(range: Range, lines: int, cols: int = 0) -> Range: + return Range( + Position( + range.start.line + lines, + range.start.character + cols, + ), + Position( + range.end.line + lines, + range.end.character + cols, + ), + ) + + # # @dataclass(frozen=True) # class ProcEnvironment(Environment): @@ -111,363 +141,126 @@ class LetEnvironment(Environment): class SkillLanguageServer(LanguageServer): - lets: list[DocumentSymbol] = [] - procs: list[DocumentSymbol] = [] - defs: list[DocumentSymbol] = [] - globals: list[DocumentSymbol] = [] + contents: dict[str, TextDocument] + trees: dict[str, Tree] - @property - def envs(self) -> tuple[DocumentSymbol, ...]: - return ( - *self.procs, - *self.lets, + def __init__( + self, + name: str, + version: str, + loop=None, + protocol_cls: type[LanguageServerProtocol] = LanguageServerProtocol, + converter_factory: Callable[[], Converter] = default_converter, + text_document_sync_kind: TextDocumentSyncKind = TextDocumentSyncKind.Incremental, + notebook_document_sync: NotebookDocumentSyncOptions | None = None, + max_workers: int = 2, + ): + super().__init__( + name, + version, + loop, + protocol_cls, + converter_factory, + text_document_sync_kind, + notebook_document_sync, + max_workers, ) - - def _diagnose_parens(self, doc: TextDocument) -> Generator[Diagnostic, None, None]: - open: list[tuple[int, int]] = [] - in_str: bool = False - last = "" - for row, line in enumerate(doc.lines): - for col, char in enumerate(line): - match char: - case "(": - if not in_str: - open.append((row, col)) - case ")": - if not in_str: - if len(open) > 0: - open.pop() - else: - yield ( - Diagnostic( - Range( - Position(row, col), - Position(row, col), - ), - "unopened ) encountered", - ) - ) - case '"': - if not (in_str and last == "\\"): - in_str = not in_str - case _: - last = char - - last = char - - if len(open) > 0: - for row, col in open: - yield ( - Diagnostic( - Range(Position(row, col), Position(row, col)), - "unclosed ) encountered", - ) - ) - - def _diagnose_cisms(self, doc: TextDocument) -> Generator[Diagnostic, None, None]: - for row, line in enumerate(doc.lines): - for m in finditer( - r"(?Pprocedure\s+|;.*)?([a-zA-Z_][a-zA-Z_0-9]+)\(", line - ): - if not m.group("proc"): - yield Diagnostic( - Range(Position(row, m.start()), Position(row, m.end())), - f"change `{m.group(2)}(` to `( {m.group(2)}`", - DiagnosticSeverity.Hint, - ) - - def diagnose(self, doc: TextDocument) -> None: - diags: list[Diagnostic] = [] - - diags.extend(self._diagnose_parens(doc)) - diags.extend(self._diagnose_cisms(doc)) - - self.publish_diagnostics(doc.uri, diags) + self.trees = {} + self.contents = {} def parse(self, doc: TextDocument) -> None: - self.lets = [] - self._parse_let(doc.lines) - self.procs = [] - self._parse_proc(doc.lines, doc.uri) - self.globals = [] - self._parse_assigns(doc.lines) + parsed = SKILL_PARSER.parse(doc.source.encode("utf8"), encoding="utf8") + self.trees[doc.uri] = parsed + self.contents[doc.uri] = doc - def _parse_assigns(self, lines: list[str]) -> None: - for row, line in enumerate(lines): - for found in finditer( - r"\b([a-zA-Z_][a-zA-Z0-9_]*)((-|~)>[a-zA-Z_][a-zA-Z0-9_]*)?\s*=\s+", - line, - ): - token = found.group(1) - token_range = Range( - Position(row, found.start()), - Position(row, found.start() + len(token)), + def update(self, uri: str, changes: list[TextDocumentContentChangeEvent]) -> None: + for change in changes: + if isinstance(change, TextDocumentContentChangeEvent_Type1): + logger.debug(f"updating {change.range}") + change_fixed = TextDocumentContentChangeEvent_Type1( + offset_range(change.range, -1), + change.text, + change.range_length, + ) + old = self.contents[uri].lines + self.contents[uri].apply_change(change) + d = Differ() + logger.debug("".join(d.compare(old, self.contents[uri].lines))) + + else: + pass + + self.trees[uri] = SKILL_PARSER.parse( + self.contents[uri].source.encode("utf8"), + old_tree=self.trees[uri], + ) + + def _get_leaves(self, node: Node) -> list[Node]: + if node.children: + return [l for child in node.children for l in self._get_leaves(child)] + + return [node] + + def _diagnose_errors(self, uri: str) -> list[Diagnostic]: + diags: list[Diagnostic] = [] + q = SKILL_LANG.query("(ERROR) @error") + nodes = ( + q.captures(self.trees[uri].root_node)["error"] + if self.trees.get(uri) + else [] + ) + + for node in nodes: + if node.type == "ERROR": + logger.error(node) + logger.error(node.range) + content = node.text.decode("utf8") if node.text else "" + range = Range( + Position(*node.range.start_point), Position(*node.range.end_point) ) - if any( - in_range(token_range.start, ns.range) - and (token in (child.name for child in (ns.children or []))) - for ns in chain(self.lets, self.procs) - ): - pass + if "UNEXPECTED" in str(node): + msg = f"unexpected '{content}'" else: - self.globals.append( - DocumentSymbol( - token, SymbolKind.Variable, token_range, token_range - ) - ) + msg = str() - def _parse_let(self, lines: list[str]) -> None: - active_let: DocumentSymbol - for row, line in enumerate(lines): - for found in finditer(r"(\(\s*let\s+|\blet\(\s+)\((.*)\)", line): - start = Position(row, found.start()) - end = find_end(start, lines) - children: list[DocumentSymbol] = [] - active_let = DocumentSymbol( - "let", - SymbolKind.Namespace, - Range(start, end), - Range(start, end), - children=children, - ) - self.lets.append(active_let) - - offset = len(found.group(1)) + 3 - for local_var in finditer( - r"([a-zA-Z_][a-zA-Z0-9_]*|\([a-zA-Z_][a-zA-Z0-9_]*\s+.+\))", - found.group(2), - ): - if local_var.group(1).startswith("("): - if m := fullmatch( - r"\(([a-zA-Z_][a-zA-Z0-9_]*)\s+.+\)", - local_var.group(1), - ): - children.append( - DocumentSymbol( - m.group(1), - SymbolKind.Variable, - Range( - Position(row, offset + local_var.start() + 1), - Position( - row, - offset - + local_var.start() - + 1 - + len(m.string), - ), - ), - Range( - Position(row, offset + local_var.start() + 1), - Position( - row, - offset - + local_var.start() - + 1 - + len(m.group(1)), - ), - ), - ) - ) - else: - assert isinstance(active_let.children, list) - active_let.children.append( - DocumentSymbol( - local_var.group(1), - SymbolKind.Variable, - Range( - Position(row, offset + local_var.start()), - Position(row, offset + local_var.end()), - ), - Range( - Position(row, offset + local_var.start()), - Position(row, offset + local_var.end()), - ), - ) - ) - - def _parse_proc(self, lines: list[str], uri: str) -> None: - for row, line in enumerate(lines): - for found in finditer( - r"(\(\s*procedure|\bprocedure\()(\s+)([a-zA-Z_][a-zA-Z0-9_]*)\((.*)\)", - line, - ): - start = Position(row, found.start()) - end = find_end(start, lines) - if "@option" in found.group(4) and "@key" in found.group(4): - self.publish_diagnostics( - uri, - [ - Diagnostic( - Range(start, Position(row, len(line))), - "`@key` and `@option` used in same definition", - severity=DiagnosticSeverity.Error, - ) - ], - ) - return - - args: list[DocumentSymbol] = [] - kwargs: list[DocumentSymbol] = [] - rest: list[DocumentSymbol] = [] - params_start = found.end() - len(found.group(4)) - warning(found.group(4)) - - for part in finditer( - rf"(@(option|key)(\s\(\w+\s+.+\))+|@rest \w+|\"[{''.join(dt.value for dt in SkillDataType)}]+\"|(\w+\s*))", - found.group(4), - ): - info(part.group(1)) - if part.group(1).startswith("@rest"): - rest_var_name = part.group(1).split()[1] - rest_var_range = Range( - Position( - row, - params_start + part.end() - len(rest_var_name), - ), - Position(row, params_start + part.end()), - ) - rest.append( - DocumentSymbol( - rest_var_name, - kind=SymbolKind.Variable, - range=rest_var_range, - selection_range=rest_var_range, - ) - ) - elif part.group(1).startswith("@"): - for kwarg in finditer(r"(\((\w+)\s+[^\)]+\))", part.group(1)): - kwargs.append( - DocumentSymbol( - kwarg.group(2), - kind=SymbolKind.Variable, - range=Range( - Position( - row, - params_start + part.start() + kwarg.start(), - ), - Position( - row, - params_start + part.start() + kwarg.end(), - ), - ), - selection_range=Range( - Position( - row, - params_start + part.start() + kwarg.start(), - ), - Position( - row, - params_start - + part.start() - + kwarg.start() - + len(kwarg.group(2)), - ), - ), - ) - ) - elif fullmatch( - rf'"[{"".join(dt.value for dt in SkillDataType)}]+"', - part.group(1), - ): - if not ( - len(args) + len(kwargs) + len(rest) - == len(part.group(1)) - 2 - ): - self.publish_diagnostics( - uri, - [ - Diagnostic( - Range(start, Position(row, len(line))), - "type info length mismatches number of arguments", - severity=DiagnosticSeverity.Error, - ) - ], - ) - return - for char, arg in zip( - part.group(1)[1:-1], chain(args, rest, kwargs) - ): - typ = SkillDataType(char) - arg.detail = f"{typ.value}_" - break - else: - for arg in finditer(r"(\w+)", part.group(1)): - arg_range = Range( - Position( - row, - params_start + part.start() + arg.start() - 1, - ), - Position( - row, - params_start + part.start() + arg.end() - 1, - ), - ) - args.append( - DocumentSymbol( - arg.group(1), - kind=SymbolKind.Variable, - range=arg_range, - selection_range=arg_range, - ) - ) - - self.procs.append( - DocumentSymbol( - found.group(3), - kind=SymbolKind.Function, - range=Range(start, end), - selection_range=Range(start, Position(row, len(line))), - children=args + rest + kwargs, - ) + diags.append( + Diagnostic( + range, + msg, + severity=DiagnosticSeverity.Error, + ), ) - def _hint_let(self) -> Generator[InlayHint, None, None]: - for let in self.lets: - if let.children: - for child in let.children: - yield InlayHint(child.selection_range.end, "|l") + return diags - def _hint_proc(self) -> Generator[InlayHint, None, None]: - for proc in self.procs: - warning(proc) - if proc.children: - for child in proc.children: - yield InlayHint(child.selection_range.end, "|l") + def diagnose(self, uri: str) -> list[Diagnostic]: + diags: list[Diagnostic] = [] - if child.detail: - yield InlayHint(child.selection_range.start, child.detail) + diags.extend(self._diagnose_errors(uri)) - def _hint_globals(self) -> Generator[InlayHint, None, None]: - for glbl in self.globals: - yield InlayHint(glbl.selection_range.end, "|g") - - def hint(self, doc: TextDocument, area: Range) -> list[InlayHint]: - hints: list[InlayHint] = [] - hints.extend(self._hint_proc()) - hints.extend(self._hint_let()) - hints.extend(self._hint_globals()) - - return hints + return diags server = SkillLanguageServer("skillls", "v0.3") -@server.feature(TEXT_DOCUMENT_DID_SAVE) +# @server.feature(TEXT_DOCUMENT_DID_SAVE) @server.feature(TEXT_DOCUMENT_DID_OPEN) -@server.feature(TEXT_DOCUMENT_DID_CHANGE) def on_open(ls: SkillLanguageServer, params: DidSaveTextDocumentParams) -> None: doc = server.workspace.get_text_document(params.text_document.uri) - if not ls.diagnose(doc): - ls.parse(doc) - ls.lsp.send_request_async(WORKSPACE_INLAY_HINT_REFRESH) + ls.parse(doc) + + diags = ls.diagnose(doc.uri) + ls.publish_diagnostics(doc.uri, diags) -@server.feature(TEXT_DOCUMENT_INLAY_HINT) -def inlay_hints(ls: SkillLanguageServer, params: InlayHintParams) -> list[InlayHint]: - doc = server.workspace.get_text_document(params.text_document.uri) - return ls.hint(doc, params.range) +@server.feature(TEXT_DOCUMENT_DID_CHANGE) +def on_change(ls: SkillLanguageServer, params: DidChangeTextDocumentParams) -> None: + ls.update(params.text_document.uri, changes=params.content_changes) + + diags = ls.diagnose(params.text_document.uri) + ls.publish_diagnostics(params.text_document.uri, diags) @server.feature(TEXT_DOCUMENT_DOCUMENT_SYMBOL) @@ -475,7 +268,8 @@ def doc_symbols( ls: SkillLanguageServer, params: DocumentSymbolParams, ) -> list[DocumentSymbol]: - return ls.procs + ls.lets + ls.defs + ls.globals + # return ls.procs + ls.lets + ls.defs + ls.globals + return [] def main(): diff --git a/uv.lock b/uv.lock index 995653b..fef80b1 100644 --- a/uv.lock +++ b/uv.lock @@ -1,4 +1,5 @@ version = 1 +revision = 1 requires-python = ">=3.12" [[package]] @@ -313,6 +314,8 @@ dependencies = [ { name = "parsimonious" }, { name = "pygls" }, { name = "rich" }, + { name = "tree-sitter" }, + { name = "tree-sitter-skill" }, ] [package.optional-dependencies] @@ -333,8 +336,41 @@ requires-dist = [ { name = "pytest", marker = "extra == 'dev'" }, { name = "rich" }, { name = "ruff", marker = "extra == 'dev'" }, + { name = "tree-sitter", specifier = ">=0.24.0" }, + { name = "tree-sitter-skill", git = "ssh://git@git.acereca.net/acereca/tree-sitter-skill.git" }, { name = "types-parsimonious", marker = "extra == 'dev'" }, ] +provides-extras = ["dev"] + +[[package]] +name = "tree-sitter" +version = "0.24.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/a7/a2/698b9d31d08ad5558f8bfbfe3a0781bd4b1f284e89bde3ad18e05101a892/tree-sitter-0.24.0.tar.gz", hash = "sha256:abd95af65ca2f4f7eca356343391ed669e764f37748b5352946f00f7fc78e734", size = 168304 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e9/57/3a590f287b5aa60c07d5545953912be3d252481bf5e178f750db75572bff/tree_sitter-0.24.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:14beeff5f11e223c37be7d5d119819880601a80d0399abe8c738ae2288804afc", size = 140788 }, + { url = "https://files.pythonhosted.org/packages/61/0b/fc289e0cba7dbe77c6655a4dd949cd23c663fd62a8b4d8f02f97e28d7fe5/tree_sitter-0.24.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:26a5b130f70d5925d67b47db314da209063664585a2fd36fa69e0717738efaf4", size = 133945 }, + { url = "https://files.pythonhosted.org/packages/86/d7/80767238308a137e0b5b5c947aa243e3c1e3e430e6d0d5ae94b9a9ffd1a2/tree_sitter-0.24.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5fc5c3c26d83c9d0ecb4fc4304fba35f034b7761d35286b936c1db1217558b4e", size = 564819 }, + { url = "https://files.pythonhosted.org/packages/bf/b3/6c5574f4b937b836601f5fb556b24804b0a6341f2eb42f40c0e6464339f4/tree_sitter-0.24.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:772e1bd8c0931c866b848d0369b32218ac97c24b04790ec4b0e409901945dd8e", size = 579303 }, + { url = "https://files.pythonhosted.org/packages/0a/f4/bd0ddf9abe242ea67cca18a64810f8af230fc1ea74b28bb702e838ccd874/tree_sitter-0.24.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:24a8dd03b0d6b8812425f3b84d2f4763322684e38baf74e5bb766128b5633dc7", size = 581054 }, + { url = "https://files.pythonhosted.org/packages/8c/1c/ff23fa4931b6ef1bbeac461b904ca7e49eaec7e7e5398584e3eef836ec96/tree_sitter-0.24.0-cp312-cp312-win_amd64.whl", hash = "sha256:f9e8b1605ab60ed43803100f067eed71b0b0e6c1fb9860a262727dbfbbb74751", size = 120221 }, + { url = "https://files.pythonhosted.org/packages/b2/2a/9979c626f303177b7612a802237d0533155bf1e425ff6f73cc40f25453e2/tree_sitter-0.24.0-cp312-cp312-win_arm64.whl", hash = "sha256:f733a83d8355fc95561582b66bbea92ffd365c5d7a665bc9ebd25e049c2b2abb", size = 108234 }, + { url = "https://files.pythonhosted.org/packages/61/cd/2348339c85803330ce38cee1c6cbbfa78a656b34ff58606ebaf5c9e83bd0/tree_sitter-0.24.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:0d4a6416ed421c4210f0ca405a4834d5ccfbb8ad6692d4d74f7773ef68f92071", size = 140781 }, + { url = "https://files.pythonhosted.org/packages/8b/a3/1ea9d8b64e8dcfcc0051028a9c84a630301290995cd6e947bf88267ef7b1/tree_sitter-0.24.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:e0992d483677e71d5c5d37f30dfb2e3afec2f932a9c53eec4fca13869b788c6c", size = 133928 }, + { url = "https://files.pythonhosted.org/packages/fe/ae/55c1055609c9428a4aedf4b164400ab9adb0b1bf1538b51f4b3748a6c983/tree_sitter-0.24.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:57277a12fbcefb1c8b206186068d456c600dbfbc3fd6c76968ee22614c5cd5ad", size = 564497 }, + { url = "https://files.pythonhosted.org/packages/ce/d0/f2ffcd04882c5aa28d205a787353130cbf84b2b8a977fd211bdc3b399ae3/tree_sitter-0.24.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d25fa22766d63f73716c6fec1a31ee5cf904aa429484256bd5fdf5259051ed74", size = 578917 }, + { url = "https://files.pythonhosted.org/packages/af/82/aebe78ea23a2b3a79324993d4915f3093ad1af43d7c2208ee90be9273273/tree_sitter-0.24.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:7d5d9537507e1c8c5fa9935b34f320bfec4114d675e028f3ad94f11cf9db37b9", size = 581148 }, + { url = "https://files.pythonhosted.org/packages/a1/b4/6b0291a590c2b0417cfdb64ccb8ea242f270a46ed429c641fbc2bfab77e0/tree_sitter-0.24.0-cp313-cp313-win_amd64.whl", hash = "sha256:f58bb4956917715ec4d5a28681829a8dad5c342cafd4aea269f9132a83ca9b34", size = 120207 }, + { url = "https://files.pythonhosted.org/packages/a8/18/542fd844b75272630229c9939b03f7db232c71a9d82aadc59c596319ea6a/tree_sitter-0.24.0-cp313-cp313-win_arm64.whl", hash = "sha256:23641bd25dcd4bb0b6fa91b8fb3f46cc9f1c9f475efe4d536d3f1f688d1b84c8", size = 108232 }, +] + +[[package]] +name = "tree-sitter-skill" +version = "0.1.1" +source = { git = "ssh://git@git.acereca.net/acereca/tree-sitter-skill.git#ce8634713b13f1787837fd9a7c515383ecedac07" } +dependencies = [ + { name = "tree-sitter" }, +] [[package]] name = "types-parsimonious"