Compare commits

..

No commits in common. "main" and "9e5d32a42095427200c5260dd155a250fb739872" have entirely different histories.

28 changed files with 1022 additions and 1403 deletions

7
.gitignore vendored
View File

@ -1,5 +1,2 @@
.venv/* zig-cache
.idea/* zig-out
*.egg-info/*
**/__pycache__/*
*.log

20
.vscode/launch.json vendored
View File

@ -1,20 +0,0 @@
{
"version": "0.2.0",
"configurations": [
{
"name": "main",
"type": "python",
"request": "launch",
"program": "/home/patrick/git/skill-ls/.venv/bin/skillls",
"python": "/home/patrick/git/skill-ls/.venv/bin/python"
},
{
"name": "main",
"type": "python",
"request": "launch",
"module": "skillls.parsing.iterative",
"python": "/home/patrick/git/skill-ls/.venv/bin/python"
}
]
}

91
build.zig Normal file
View File

@ -0,0 +1,91 @@
const std = @import("std");
// Although this function looks imperative, note that its job is to
// declaratively construct a build graph that will be executed by an external
// runner.
pub fn build(b: *std.Build) void {
// Standard target options allows the person running `zig build` to choose
// what target to build for. Here we do not override the defaults, which
// means any target is allowed, and the default is native. Other options
// for restricting supported target set are available.
const target = b.standardTargetOptions(.{});
// Standard optimization options allow the person running `zig build` to select
// between Debug, ReleaseSafe, ReleaseFast, and ReleaseSmall. Here we do not
// set a preferred release mode, allowing the user to decide how to optimize.
const optimize = b.standardOptimizeOption(.{});
const lib = b.addStaticLibrary(.{
.name = "skill-ls",
// In this case the main source file is merely a path, however, in more
// complicated build scripts, this could be a generated file.
.root_source_file = b.path("src/root.zig"),
.target = target,
.optimize = optimize,
});
// This declares intent for the library to be installed into the standard
// location when the user invokes the "install" step (the default step when
// running `zig build`).
b.installArtifact(lib);
const exe = b.addExecutable(.{
.name = "skill-ls",
.root_source_file = b.path("src/main.zig"),
.target = target,
.optimize = optimize,
});
// This declares intent for the executable to be installed into the
// standard location when the user invokes the "install" step (the default
// step when running `zig build`).
b.installArtifact(exe);
// This *creates* a Run step in the build graph, to be executed when another
// step is evaluated that depends on it. The next line below will establish
// such a dependency.
const run_cmd = b.addRunArtifact(exe);
// By making the run step depend on the install step, it will be run from the
// installation directory rather than directly from within the cache directory.
// This is not necessary, however, if the application depends on other installed
// files, this ensures they will be present and in the expected location.
run_cmd.step.dependOn(b.getInstallStep());
// This allows the user to pass arguments to the application in the build
// command itself, like this: `zig build run -- arg1 arg2 etc`
if (b.args) |args| {
run_cmd.addArgs(args);
}
// This creates a build step. It will be visible in the `zig build --help` menu,
// and can be selected like this: `zig build run`
// This will evaluate the `run` step rather than the default, which is "install".
const run_step = b.step("run", "Run the app");
run_step.dependOn(&run_cmd.step);
// Creates a step for unit testing. This only builds the test executable
// but does not run it.
const lib_unit_tests = b.addTest(.{
.root_source_file = b.path("src/root.zig"),
.target = target,
.optimize = optimize,
});
const run_lib_unit_tests = b.addRunArtifact(lib_unit_tests);
const exe_unit_tests = b.addTest(.{
.root_source_file = b.path("src/main.zig"),
.target = target,
.optimize = optimize,
});
const run_exe_unit_tests = b.addRunArtifact(exe_unit_tests);
// Similar to creating the run step earlier, this exposes a `test` step to
// the `zig build --help` menu, providing a way for the user to request
// running the unit tests.
const test_step = b.step("test", "Run unit tests");
test_step.dependOn(&run_lib_unit_tests.step);
test_step.dependOn(&run_exe_unit_tests.step);
}

70
build.zig.zon Normal file
View File

@ -0,0 +1,70 @@
.{
.name = "skill-ls",
// This is a [Semantic Version](https://semver.org/).
// In a future version of Zig it will be used for package deduplication.
.version = "0.0.0",
// This field is optional.
// This is currently advisory only; Zig does not yet do anything
// with this value.
//.minimum_zig_version = "0.11.0",
// This field is optional.
// Each dependency must either provide a `url` and `hash`, or a `path`.
// `zig build --fetch` can be used to fetch all dependencies of a package, recursively.
// Once all dependencies are fetched, `zig build` no longer requires
// internet connectivity.
.dependencies = .{
.lsfw = .{
.path = "lib/lsfw",
}
// See `zig fetch --save <url>` for a command-line interface for adding dependencies.
//.example = .{
// // When updating this field to a new URL, be sure to delete the corresponding
// // `hash`, otherwise you are communicating that you expect to find the old hash at
// // the new URL.
// .url = "https://example.com/foo.tar.gz",
//
// // This is computed from the file contents of the directory of files that is
// // obtained after fetching `url` and applying the inclusion rules given by
// // `paths`.
// //
// // This field is the source of truth; packages do not come from a `url`; they
// // come from a `hash`. `url` is just one of many possible mirrors for how to
// // obtain a package matching this `hash`.
// //
// // Uses the [multihash](https://multiformats.io/multihash/) format.
// .hash = "...",
//
// // When this is provided, the package is found in a directory relative to the
// // build root. In this case the package's hash is irrelevant and therefore not
// // computed. This field and `url` are mutually exclusive.
// .path = "foo",
// // When this is set to `true`, a package is declared to be lazily
// // fetched. This makes the dependency only get fetched if it is
// // actually used.
// .lazy = false,
//},
},
// Specifies the set of files and directories that are included in this package.
// Only files and directories listed here are included in the `hash` that
// is computed for this package.
// Paths are relative to the build root. Use the empty string (`""`) to refer to
// the build root itself.
// A directory listed here means that all files within, recursively, are included.
.paths = .{
// This makes *all* files, recursively, included in this package. It is generally
// better to explicitly list the files and directories instead, to insure that
// fetching from tarballs, file system paths, and version control all result
// in the same contents hash.
"",
// For example...
//"build.zig",
//"build.zig.zon",
//"src",
//"LICENSE",
//"README.md",
},
}

19
data/example.il Normal file
View File

@ -0,0 +1,19 @@
;;; this is some example module docstring
; random comment
a=1+2.0*3e2-4/ 5
b_var->a
(list 1 2 34)
'(1 2 3 4)
(procedure func_name(param1 param2 @keys (a nil))
; some struff to do
)
"srting"
"wqdwd\"qwesfwf"

View File

@ -1,22 +0,0 @@
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
)
(
(let (some vars (default 0))
; ... some wall of text
"))\""
wqdqwf = '(doqwf)
var = 1.3
vars = 231
qqvwv
cfunc()
)
)

View File

@ -1,37 +0,0 @@
[project]
name = "skillls"
version = "0.1.0"
dependencies = [
"parsimonious~=0.10.0",
"pygls",
"rich"
]
[project.optional-dependencies]
dev = [
"black",
"mypy",
"ruff",
"pytest",
"types-parsimonious",
]
[build-system]
build-backend = 'setuptools.build_meta'
requires = [
'setuptools',
]
[project.scripts]
skillls = "skillls.main:main"
[tools.black]
line-length = 100
target-version = "py311"
include = "skillls"
[tools.ruff]
line-length = 100
include = ['ALL']

View File

View File

@ -1,37 +0,0 @@
from dataclasses import dataclass, field
from typing import Any, Generic, TypeVar, TypeVarTuple, Union, Unpack
T = TypeVar("T")
L = TypeVarTuple("L")
ID = int
@dataclass
class Cache(Generic[*L, T]):
cached: list[T] = field(default_factory=list)
lookups: dict[type[Union[*L]], dict[Union[*L], ID]] = field(default_factory=dict)
def __getitem__(self, key: Union[*L]) -> T:
id = self.lookups[type(key)][key]
return self.cached[id]
def __setitem__(self, keys: tuple[Unpack[L]], value: T) -> None:
print(type(keys), keys)
id = len(self.cached)
self.cached.append(value)
for key in keys:
self.lookups.setdefault(type(key), {})
self.lookups[type(key)][key] = id
if __name__ == "__main__":
c = Cache[int, str, str]()
print(c)
c[0, None] = "a"
print(c)

View File

@ -1,34 +0,0 @@
skill = inline_expr+
expr = (inline_expr / nl)
inline_expr = (listraw / listc / listskill / inline_get / inline_op / inline_assign / ws / nl)
inline_assign = TOKEN ws* "=" ws* (inline_expr / LITERAL / TOKEN)
inline_op = TOKEN ws* inline_op_symbol ws* (inline_expr / TOKEN / LITERAL)
inline_op_symbol = ~"[*-+/]"
inline_get = TOKEN inline_get_symbol (inline_expr / TOKEN / LITERAL)
inline_get_symbol = ~"(~>|->)"
listraw = "'" list_start expr* list_end
listc = TOKEN list_start expr* list_end
listskill = list_start expr* list_end
list_start = "("
list_end = ")"
TOKEN = ~"[a-zA-Z_][_a-zA-Z0-9]+"
LITERAL = L_num / L_t / L_nil / L_str
L_num = ~"[0-9]+(\.[0-9]+)?"
L_t = "t"
L_nil = "nil"
L_str = delim_str any_str delim_str
delim_str = "\""
any_str = ~"[^\"]*"
ws = ~"\\h"
nl = ~"\\n"

View File

@ -1,482 +0,0 @@
from collections.abc import Generator
from dataclasses import dataclass, field
from itertools import chain
from logging import INFO, basicConfig, debug, error, getLogger, info, warning
from re import findall, finditer, fullmatch, match as rematch
from time import time
from lsprotocol.types import (
INLAY_HINT_RESOLVE,
TEXT_DOCUMENT_DID_CHANGE,
TEXT_DOCUMENT_DID_OPEN,
TEXT_DOCUMENT_DID_SAVE,
TEXT_DOCUMENT_DOCUMENT_SYMBOL,
TEXT_DOCUMENT_HOVER,
TEXT_DOCUMENT_INLAY_HINT,
WORKSPACE_INLAY_HINT_REFRESH,
WORKSPACE_SEMANTIC_TOKENS_REFRESH,
CompletionItem,
Diagnostic,
DiagnosticSeverity,
DidChangeTextDocumentParams,
DidOpenTextDocumentParams,
DidSaveTextDocumentParams,
DocumentSymbol,
DocumentSymbolParams,
Hover,
HoverParams,
InlayHint,
InlayHintKind,
InlayHintParams,
MessageType,
Position,
Range,
SymbolKind,
)
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
URI = str
basicConfig(filename="skillls.log", filemode="w", level=INFO)
cache: Cache[str, CompletionItem] = Cache()
def in_range(what: Position, area: Range) -> bool:
return (what >= area.start) and (what <= area.end)
def find_end(start: Position, lines: list[str]) -> Position:
count = 0
in_str: bool = False
last = ""
for row, line in enumerate(lines[start.line :]):
if row == 0:
line = line[start.character :]
row += start.character
for col, char in enumerate(line[start.character :] if row == 0 else line):
match char:
case "(":
if not in_str:
count += 1
case ")":
if not in_str:
if count > 0:
count -= 1
if count == 0:
return Position(start.line + row, col)
case '"':
if not (in_str and last == "\\"):
in_str = not in_str
case _:
last = char
last = char
error(f"did not fin end for start at {start}")
return Position(len(lines), len(lines[-1]))
@dataclass(frozen=True)
class Environment:
range: Range
@dataclass(frozen=True)
class LetEnvironment(Environment):
locals: set[str] = field(default_factory=set)
#
# @dataclass(frozen=True)
# class ProcEnvironment(Environment):
# name: str
# args: tuple[DocumentSymbol, ...]
# kwargs: tuple[DocumentSymbol, ...]
# rest: DocumentSymbol | None = None
#
# @property
# def locals(self) -> tuple[DocumentSymbol, ...]:
# ret = [*self.args, *self.kwargs]
# if self.rest:
# ret.append(self.rest)
#
# return tuple(ret)
class SkillLanguageServer(LanguageServer):
lets: list[DocumentSymbol] = []
procs: list[DocumentSymbol] = []
defs: list[DocumentSymbol] = []
globals: list[DocumentSymbol] = []
@property
def envs(self) -> tuple[DocumentSymbol, ...]:
return (
*self.procs,
*self.lets,
)
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"(?P<proc>procedure\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)
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)
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)),
)
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
else:
self.globals.append(
DocumentSymbol(
token, SymbolKind.Variable, token_range, token_range
)
)
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,
)
)
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")
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")
if child.detail:
yield InlayHint(child.selection_range.start, child.detail)
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
server = SkillLanguageServer("skillls", "v0.3")
@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)
@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_DOCUMENT_SYMBOL)
def doc_symbols(
ls: SkillLanguageServer,
params: DocumentSymbolParams,
) -> list[DocumentSymbol]:
return ls.procs + ls.lets + ls.defs + ls.globals
def main():
server.start_io()

View File

@ -1,32 +0,0 @@
from dataclasses import dataclass
from enum import Enum, auto
from typing import ClassVar, DefaultDict
from .location import Range
from .tokenize import BaseToken
class ContextType(Enum):
Use = auto()
Assign = auto()
Declare = auto()
Unbind = auto()
@dataclass(frozen=True)
class Context:
lookup: ClassVar[dict[ContextType, list["Context"]]] = {}
typ: ContextType
token: list[BaseToken]
def __post_init__(self):
type(self).lookup.setdefault(self.typ, [])
type(self).lookup[self.typ].append(self)
@property
def range(self) -> Range:
new_range = self.token[0].range
for token in self.token[1:]:
new_range += token.range
return new_range

View File

@ -1,296 +0,0 @@
from abc import ABC
from dataclasses import dataclass, field
from enum import Enum
from logging import getLogger
import re
from pathlib import Path
from typing import NamedTuple, Self
from lsprotocol.types import (
Diagnostic,
DiagnosticSeverity,
DocumentSymbol,
Position,
Range,
SymbolKind,
)
logger = getLogger(__name__)
class Pair(NamedTuple):
start: str
end: str
class SyntaxPair(Enum):
Paren = Pair("(", ")")
Square = Pair("[", "]")
@classmethod
def by_start_elem(cls, start: str) -> Self:
for option in cls:
if option.value[0] == start:
return option
raise ValueError(f"`{start}` not a valid start character")
@classmethod
def by_end_elem(cls, end: str) -> Self:
for option in cls:
if option.value[1] == end:
return option
raise ValueError(f"`{end}` not a valid end character")
def char_range(line: int, char: int) -> Range:
return Range(Position(line, char), Position(line, char + 1))
def pair_mismatch(line: int, char: int, msg: str) -> Diagnostic:
return Diagnostic(
char_range(line, char),
msg,
severity=DiagnosticSeverity.Error,
)
class StackElement(NamedTuple):
range: Range
elem: SyntaxPair
WHITESPACE_OR_PAREN = re.compile(r"(\s|\(|\)|\[|\]|\'\()+")
TOKEN_REGEX = re.compile(r"\w[a-zA-Z0-9_]*")
NUMBER_REGEX = re.compile(r"\d+(\.\d+)?")
OPERATORS = re.compile(r"(->|~>|\+|\-|\*|\/|\=|\|\||\&\&)")
@dataclass
class TreeToken(ABC):
content: str
range: Range
def String(content: str, range: Range) -> DocumentSymbol:
return DocumentSymbol(
name=content,
range=range,
kind=SymbolKind.String,
selection_range=range,
)
def Operator(content: str, range: Range) -> DocumentSymbol:
return DocumentSymbol(
name=content,
range=range,
kind=SymbolKind.Operator,
selection_range=range,
)
def Number(content: str, range: Range) -> DocumentSymbol:
return DocumentSymbol(
name=content,
range=range,
kind=SymbolKind.Number,
selection_range=range,
)
def Token(content: str, range: Range) -> DocumentSymbol:
return DocumentSymbol(
name=content,
range=range,
kind=SymbolKind.Variable,
selection_range=range,
)
RawIndex = int
ColIndex = int
LineIndex = int
@dataclass
class TokenParser:
_in_string: bool = False
_in_comment: bool = False
_token_tree: list[DocumentSymbol] = field(default_factory=list)
_current: str = ""
_line_indices: list[RawIndex] = field(default_factory=list)
def _get_line(self, index: RawIndex) -> tuple[LineIndex, RawIndex]:
for line, newline_pos in enumerate(self._line_indices):
if index < newline_pos:
return line, self._line_indices[line - 1] if line > 0 else 0
return len(self._line_indices), self._line_indices[-1]
def _get_range(self, start: RawIndex, end: RawIndex) -> Range:
start_line, start_line_index = self._get_line(start)
start_col = start - start_line_index - 1
end_line, end_line_index = self._get_line(end)
end_col = end - end_line_index - 1
return Range(Position(start_line, start_col), Position(end_line, end_col))
def _parse_string(self, raw: str, index: int) -> int:
stop = raw.index('"', index + 1)
self._token_tree.append(
String(raw[index : stop + 1], self._get_range(index, stop))
)
return stop + 1
def _parse_comment(self, raw: str, index: int) -> int:
stop = raw.index("\n", index)
# self._token_tree.append(Comment(raw[index:stop], self._get_range(index, stop)))
return stop + 1
def _parse_whitespace(self, raw: str, index: int) -> int:
if m := WHITESPACE_OR_PAREN.search(raw, index):
stop = m.end()
else:
stop = index + 1
# self._token_tree.append(Whitespace(raw[index:stop]))
return stop
def _parse_operator(self, raw: str, index: int) -> int:
if m := OPERATORS.search(raw, index):
stop = m.end()
else:
stop = index + 1
self._token_tree.append(
Operator(raw[index:stop], self._get_range(index, stop - 1))
)
return stop + 1
def _parse_token(self, raw: str, index: int) -> int:
if m := TOKEN_REGEX.search(raw, index):
stop = m.end()
else:
stop = index + 1
self._token_tree.append(
Token(raw[index:stop], self._get_range(index, stop - 1))
)
return stop
def _parse_number(self, raw: str, index: int) -> int:
if m := NUMBER_REGEX.search(raw, index):
stop = m.end()
else:
stop = index + 1
self._token_tree.append(
Number(raw[index:stop], self._get_range(index, stop - 1))
)
return stop
def prepare_content(self, raw: str) -> None:
self._line_indices = [i for i, char in enumerate(raw) if char == "\n"]
max_index = len(raw)
index = 0
while index < max_index:
if raw[index] == '"':
index = self._parse_string(raw, index)
elif raw[index] == ";":
index = self._parse_comment(raw, index)
elif WHITESPACE_OR_PAREN.match(raw[index : index + 2]):
index = self._parse_whitespace(raw, index)
elif OPERATORS.match(raw[index]):
index = self._parse_operator(raw, index)
elif NUMBER_REGEX.match(raw[index]):
index = self._parse_number(raw, index)
else:
index = self._parse_token(raw, index)
@dataclass()
class IterativeParser:
_stack: list[StackElement] = field(default_factory=list)
def peek(self) -> StackElement:
return self._stack[-1]
def pop(self) -> StackElement:
return self._stack.pop()
def push(self, pair: StackElement) -> None:
return self._stack.append(pair)
def __call__(self, raw: list[str]) -> list[Diagnostic]:
in_string = False
errs = []
for line, raw_line in enumerate(raw):
for char, raw_char in enumerate(raw_line):
match raw_char:
case ";":
if not in_string:
break
case '"':
in_string = not in_string
case "(" | "[":
if not in_string:
self.push(
StackElement(
char_range(line, char),
SyntaxPair.by_start_elem(raw_char),
)
)
case "]" | ")":
if not in_string:
if not self._stack:
errs.append(
pair_mismatch(
line, char, f"one {raw_char} too much"
)
)
continue
expected = SyntaxPair.by_end_elem(raw_char)
elem = self._stack.pop()
if elem.elem == expected:
continue
if self._stack and self._stack[-1].elem == expected:
errs.append(
pair_mismatch(
line, char, f"unclosed {elem.elem.value.start}"
)
)
self._stack.pop()
self._stack.append(elem)
else:
errs.append(
pair_mismatch(
line, char, f"one {raw_char} too much"
)
)
self._stack.append(elem)
for rest in self._stack:
errs.append(
Diagnostic(
rest.range,
f"unclosed {rest.elem.value.start}",
severity=DiagnosticSeverity.Error,
)
)
self._stack = []
return errs
if __name__ == "__main__":
example = Path(__file__).parent.parent.parent / "examples" / "example.il"
t = TokenParser()
t.prepare_content(example.read_text())
print(t._token_tree)

View File

@ -1,42 +0,0 @@
from dataclasses import dataclass
from functools import cached_property
from typing import overload
from lsprotocol.types import Position, Range
from parsimonious.nodes import Node
@dataclass(frozen=True)
class Locator:
raw: list[str]
def _locate_pos(self, index: int) -> Position:
counter = 0
line = 0
for ix, raw_line in enumerate(self.raw):
if counter + len(raw_line) + 1 > index:
line = ix
break
else:
counter += len(raw_line) + 1
print(counter, line)
return Position(line + 1, index - counter + 1)
@overload
def locate(self, index: int) -> Position:
...
@overload
def locate(self, index: Node) -> Range:
...
def locate(self, index: int | Node) -> Position | Range:
if isinstance(index, int):
return self._locate_pos(index)
print(index.start, index.end)
start = self._locate_pos(index.start)
end = self._locate_pos(index.end)
return Range(start, end)

View File

@ -1 +0,0 @@

91
src/classifier.zig Normal file
View File

@ -0,0 +1,91 @@
const tkz = @import("tokenize.zig");
const hlp = @import("helpers.zig");
const std = @import("std");
pub const TokenClass = enum {
symbol,
string,
comment,
docstring,
number,
nil,
t,
list_start,
list_lazy_start,
list_end,
operator,
};
pub const ClassifiedToken = struct {
tok: tkz.Token,
cls: TokenClass,
};
const operators = std.ComptimeStringMap(void, .{
.{"->"},
.{"~>"},
.{"/="},
.{"*="},
.{"-="},
.{"+="},
.{"||"},
.{"&&"},
.{"="},
.{"+"},
.{"-"},
.{"*"},
.{"/"},
.{"~"},
.{"%"},
.{"@keys"},
.{"@rest"},
});
const numbers = std.ComptimeStringMap(void, .{
.{"0"},
.{"1"},
.{"2"},
.{"3"},
.{"4"},
.{"5"},
.{"6"},
.{"7"},
.{"8"},
.{"9"},
});
fn classify(tok: tkz.Token) ClassifiedToken {
return ClassifiedToken{
.tok = tok,
.cls = if (operators.has(tok.value))
TokenClass.operator
else if (std.mem.eql(u8, "'(", tok.value))
TokenClass.list_lazy_start
else if (std.mem.eql(u8, "(", tok.value))
TokenClass.list_start
else if (std.mem.eql(u8, ")", tok.value))
TokenClass.list_end
else if (std.mem.eql(u8, "\"", tok.value[0..1]))
TokenClass.string
else if (std.mem.eql(u8, "nil", tok.value))
TokenClass.nil
else if (std.mem.eql(u8, "t", tok.value))
TokenClass.t
else if (numbers.has(tok.value[0..1]))
TokenClass.number
else if (std.mem.eql(u8, ";", tok.value[0..1]))
if (tok.value.len >= 3 and std.mem.eql(u8, ";;;", tok.value[0..3])) TokenClass.docstring else TokenClass.comment
else
TokenClass.symbol,
};
}
pub fn classifyTokens(toks: []const tkz.Token, allocator: std.mem.Allocator) !std.ArrayList(ClassifiedToken) {
var ctoks = std.ArrayList(ClassifiedToken).init(allocator);
for (toks) |tok| {
try ctoks.append(classify(tok));
}
return ctoks;
}

9
src/helpers.zig Normal file
View File

@ -0,0 +1,9 @@
const std = @import("std");
pub fn isPartOf(comptime T: type, haystack: [][]const T, needle: []const T) bool {
for (haystack) |straw| {
if (std.mem.eql(u8, straw, needle[0..straw.len])) {
return true;
}
}
return false;
}

1
src/lsfw Submodule

@ -0,0 +1 @@
Subproject commit 5077a6cc6d6e0cf8ed95db234146aa14c42767f0

90
src/lsp.zig Normal file
View File

@ -0,0 +1,90 @@
const std = @import("std");
const lsp_types = @import("lsfw/src/types.zig");
const lsp = @import("lsfw/src/lsp.zig");
const lsp_doc = @import("lsfw/src/document.zig");
const lsp_log = @import("lsfw/src/logger.zig");
const tkz = @import("tokenize.zig");
const cls = @import("classifier.zig");
const State = struct { symbols: std.ArrayList(cls.ClassifiedToken) };
const Lsp = lsp.Lsp(State);
const Scope = enum { hi };
fn handleHover(allocator: std.mem.Allocator, ctx: *Lsp.Context, pos: lsp_types.Position) ?[]const u8 {
if (null == ctx.state) {
lsp_log.notify(.info, "could not find token under cursor (at {})", .{pos});
return null;
} else if (0 == ctx.state.?.symbols.items.len) {
handleDocOpen(allocator, ctx);
}
lsp_log.notify(.err, "{}", .{ctx.state.?.symbols});
// for (ctx.state.?.symbols.items) |tok| {
// if (tok.tok.line == pos.line and tok.tok.char <= pos.character and (tok.tok.char + tok.tok.value.len) >= pos.character) {
// lsp_log.notify(.info, "{}", .{tok});
// break;
// }
// }
return null;
}
fn handleCompletion(allocator: std.mem.Allocator, context: *Lsp.Context, position: lsp_types.Position) ?lsp_types.CompletionList {
_ = context;
_ = position;
var completions = std.ArrayList(lsp_types.CompletionItem).init(allocator);
if (std.mem.Allocator.Error.OutOfMemory == completions.append(.{
.label = "(procedure)",
.insertText = "(procedure ${1:func_name}($2)\n\n)",
.insertTextFormat = .Snippet,
.kind = .Function,
})) {
return null;
}
return .{ .items = completions.items };
}
fn handleDocOpen(allocator: std.mem.Allocator, context: *Lsp.Context) void {
lsp_log.notify(.err, "opened doc {s}", .{context.document.uri});
const content = context.document.text;
const toks = tkz.tokenizeContent(content, allocator) catch unreachable;
// const toks = std.ArrayList(tkz.Token).init(allocator);
lsp_log.notify(.err, "toks {}", .{toks});
// defer toks.deinit();
const ctoks = cls.classifyTokens(toks.items, allocator) catch unreachable;
lsp_log.notify(.err, "ctoks {}", .{ctoks});
// defer ctoks.deinit();
// const ast = try stx.generateSyntaxTree(ctoks);
lsp_log.notify(.info, "opened {s}, found {d} tokens", .{ context.document.uri, ctoks.items.len });
if (context.state != null) {
context.state.?.symbols.deinit();
}
context.state = .{
.symbols = std.ArrayList(cls.ClassifiedToken).init(allocator),
};
}
fn handleDocChanged(allocator: std.mem.Allocator, context: *Lsp.Context, _: []lsp_types.ChangeEvent) void {
handleDocOpen(allocator, context);
}
fn handleDocClose(_: std.mem.Allocator, _: *Lsp.Context) void {}
pub fn start() !u8 {
const descr = lsp_types.ServerData{
.serverInfo = .{
.name = "skill lsp",
.version = "0.1.0",
},
};
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
var server = Lsp.init(gpa.allocator(), descr);
server.registerHoverCallback(handleHover);
server.registerCompletionCallback(handleCompletion);
server.registerDocOpenCallback(handleDocOpen);
server.registerDocChangeCallback(handleDocChanged);
server.registerDocCloseCallback(handleDocClose);
return server.start();
}

35
src/main.zig Normal file
View File

@ -0,0 +1,35 @@
const std = @import("std");
const tkz = @import("tokenizer.zig");
// const cls = @import("classifier.zig");
// const stx = @import("syntax.zig");
const lsp = @import("lsp.zig");
pub fn main() !void {
// var file = try std.fs.cwd().openFile("data/example.il", .{});
// defer file.close();
//
// const content = try file.readToEndAlloc(std.heap.page_allocator, 4096 * ((1 << 10) << 10));
//
// const toks = try tkz.tokenizeContent(content);
// // for (toks.items) |tok| {
// // std.debug.print("{}:{} `{s}`\n", .{
// // tok.line,
// // tok.char,
// // tok.value,
// // });
// // }
//
// const ctoks = try cls.classifyTokens(toks);
// // for (ctoks.items) |ctok| {
// // std.debug.print("{}:{}\t`{s:<40}`({})\n", .{
// // ctok.tok.line,
// // ctok.tok.char,
// // ctok.tok.value,
// // ctok.cls,
// // });
// // }
// const ast = try stx.generateSyntaxTree(ctoks);
// std.debug.print("{}\n", .{ast});
//
_ = try lsp.start();
}

178
src/parser.zig Normal file
View File

@ -0,0 +1,178 @@
const std = @import("std");
const toks = @import("tokenizer.zig");
pub const ParseError = error{ no_fn_name, no_fn_params };
pub const Tag = enum {
///expression
///`<rhs...>`
///
///lhs ignored
expr,
///variable assignment
///`<lhs> = <rhs...>`
///
///lhs is overwritten to be variable
var_assign,
///lazy evaluated list
///`'(<rhs...>)`
///
///lhs ignored
llist,
///list (evaluated)
///`(<lhs> <rhs...>)`
///
///lhs needs to be a callable
list_eval,
///fn_def (procedure)
///`;;; <lhs>
///(procedure <main_token>(<lhs>) <rhs...>)`
fn_def,
};
pub const Node = struct {
tag: Tag,
main_token: Index,
data: Data,
pub const Data = struct {
lhs: Index,
rhs: Index,
};
pub const Index = u32;
};
pub const AstError = error{};
pub const Parser = struct {
gpa: std.mem.Allocator,
source: [:0]const u8,
token_tags: []const toks.Token.Tag,
token_locs: []const toks.Token.Loc,
tok_i: Node.Index,
errs: std.ArrayList(AstError),
nodes: std.MultiArrayList(Node),
extra_data: std.ArrayList(Node.Index),
scratch: std.ArrayList(Node.Index),
pub fn init(buffer: [:0]const u8, mal: std.MultiArrayList(toks.Token), allocator: std.mem.Allocator) !Parser {
return .{
.gpa = allocator,
.source = buffer,
.token_tags = mal.items(.tag),
.token_locs = mal.items(.loc),
.tok_i = 0,
.errs = std.ArrayList(AstError).init(allocator),
.nodes = std.MultiArrayList(Node){},
.extra_data = std.ArrayList(Node.Index).init(allocator),
.scratch = std.ArrayList(Node.Index).init(allocator),
};
}
fn hasToken(self: *Parser, expected: toks.Token.Tag, offset: isize) ?toks.Token {
if (self.token_tags[@intCast(self.tok_i + offset)] == expected) {
return .{ .loc = self.token_locs[@intCast(self.tok_i + offset)], .tag = self.token_tags[@intCast(self.tok_i + offset)] };
}
return null;
}
fn eatToken(self: *Parser, expected: toks.Token.Tag) ?Node.Index {
const tok = self.hasToken(expected, 0);
if (tok != null) {
self.tok_i += 1;
return self.tok_i - 1;
}
return null;
}
fn parse_fn_proc(self: *Parser) ?Node {
_ = self.eatToken(.sym);
if (self.hasToken(.list_l, -2) != null) {
// lisp style
} else if (self.eatToken(.list_l) != null) {
// c style
} else {
// not a procedure call or invalid syntax?
}
const name = self.eatToken(.sym) orelse return null;
std.debug.print("found procedure def for `{s}`", .{self.source[self.token_locs[name].start..self.token_locs[name].end]});
_ = self.eatToken(.list_l) orelse return null;
var open_lists: usize = 0;
while (true) : (self.tok_i += 1) {
switch (self.token_tags[self.tok_i]) {
.list_l, .list_lz => {
open_lists += 1;
},
.list_r => {
if (open_lists > 0) {
open_lists -= 1;
} else {
break;
}
},
else => {},
}
}
while (true) : (self.tok_i += 1) {
switch (self.token_tags[self.tok_i]) {
.list_l, .list_lz => {
open_lists += 1;
},
.list_r => {
if (open_lists > 0) {
open_lists -= 1;
} else {
break;
}
},
else => {},
}
}
self.tok_i += 1;
return Node{ .tag = .fn_def, .main_token = name, .data = .{ .lhs = 0, .rhs = 0 } };
}
pub fn next(self: *Parser) ?Node {
while (self.tok_i < self.token_tags.len) : (self.tok_i += 1) {
switch (self.token_tags[self.tok_i]) {
toks.Token.Tag.sym => {
if (std.mem.eql(u8, "procedure", self.source[self.token_locs[self.tok_i].start..self.token_locs[self.tok_i].end])) {
return self.parse_fn_proc();
}
},
else => {},
}
}
return null;
}
};
test "parsing of simple example" {
const example =
\\t
\\nil
\\a = b
\\"some string w/ escaped\""
\\(procedure a() )
;
var tokz = toks.Tokenizer.init(example);
var tokens = std.MultiArrayList(toks.Token){};
defer tokens.deinit(std.testing.allocator);
while (tokz.next()) |tok| {
try tokens.append(std.testing.allocator, tok);
std.debug.print("{}\n", .{tok});
}
var parse = try Parser.init(example, tokens, std.testing.allocator);
while (parse.next()) |ast_node| {
std.debug.print("{}\n", .{ast_node});
}
}

10
src/root.zig Normal file
View File

@ -0,0 +1,10 @@
const std = @import("std");
const testing = std.testing;
export fn add(a: i32, b: i32) i32 {
return a + b;
}
test "basic add functionality" {
try testing.expect(add(3, 7) == 10);
}

55
src/syntax.zig Normal file
View File

@ -0,0 +1,55 @@
const std = @import("std");
const cls = @import("classifier.zig");
pub const SyntaxNode = struct {
ctok: cls.ClassifiedToken,
nodes: ?std.ArrayList(SyntaxNode),
};
pub fn generateSyntaxTree(ctoks: std.ArrayList(cls.ClassifiedToken)) !std.ArrayList(SyntaxNode) {
var nodes = std.ArrayList(SyntaxNode).init(std.heap.page_allocator);
var actives = std.ArrayList(SyntaxNode).init(std.heap.page_allocator);
for (ctoks.items) |ctok| {
switch (ctok.cls) {
cls.TokenClass.comment, cls.TokenClass.docstring => {
try nodes.append(.{
.ctok = ctok,
.nodes = null,
});
},
cls.TokenClass.list_start, cls.TokenClass.list_lazy_start => {
try actives.append(.{
.ctok = ctok,
.nodes = std.ArrayList(SyntaxNode).init(std.heap.page_allocator),
});
},
cls.TokenClass.list_end => {
if (actives.items.len > 0) {
try nodes.append(actives.pop());
} else {
std.debug.print("{}\n", .{actives});
}
},
else => {
const active_top = actives.popOrNull();
if (active_top != null) {
var active = active_top.?;
var actives_nodes: std.ArrayList(SyntaxNode) = undefined;
if (active.nodes != null) {
actives_nodes = active.nodes.?;
} else {
active.nodes = std.ArrayList(SyntaxNode).init(std.heap.page_allocator);
actives_nodes = active.nodes.?;
}
try actives_nodes.append(.{
.ctok = ctok,
.nodes = null,
});
} else {}
},
}
}
return nodes;
}

99
src/tokenize.zig Normal file
View File

@ -0,0 +1,99 @@
const std = @import("std");
const lsp = @import("lsfw/src/lsp.zig");
pub const Token = struct {
/// 0-based index of token start in whole file
start: usize,
/// 1-based line numbert token starts at
line: usize,
/// 1-based char numbert token starts at in line
char: usize,
value: []const u8,
};
const TokenizationError = error{InvalidKeyword};
pub fn tokenizeContent(content: []u8, allocator: std.mem.Allocator) !std.ArrayList(Token) {
var toks = std.ArrayList(Token).init(allocator);
var lines = std.ArrayList(usize).init(allocator);
defer lines.deinit();
var index: usize = 0;
while (index < content.len) {
var l: usize = 1;
const char = content[index];
_ = switch (char) {
'\n' => {
try lines.append(index);
index += l;
continue;
},
';' => {
while (switch (content[index + l]) {
'\n' => false,
else => true,
}) : (l += 1) {}
},
'"' => {
while (switch (content[index + l]) {
'"' => (content[index + l - 1] == '\\'),
else => true,
}) : (l += 1) {}
l += 1;
},
'a'...'z', 'A'...'Z', '_' => {
while (switch (content[index + l]) {
'a'...'z', 'A'...'Z', '0'...'9', '_' => true,
else => false,
}) : (l += 1) {}
},
'0'...'9' => {
while (switch (content[index + l]) {
'0'...'9', '.', 'e' => true,
else => false,
}) : (l += 1) {}
},
'+', '-', '~', '*', '/', '%', '<', '>', '=', '?', '|', '&', '(', ')', '\'' => {
for ([_]*const [2]u8{ "->", "~>", "||", "&&", "/=", "*=", "+=", "-=", "'(" }) |op| {
if (std.mem.eql(u8, op, content[index .. index + 2])) {
l = 2;
break;
}
}
},
'@' => {
while (switch (content[index + l]) {
'a'...'z', 'A'...'Z', '_', '0'...'9' => true,
else => false,
}) : (l += 1) {}
if (std.mem.eql(u8, "@keys", content[index .. index + l])) {} else if (std.mem.eql(u8, "@rest", content[index .. index + l])) {} else {
std.debug.print("line={d}, char={d}\n", .{
.line = lines.items.len,
.char = switch (lines.items.len) {
0 => index,
else => index - lines.items[lines.items.len - 1],
},
});
}
},
else => {
index += l;
continue;
},
};
try toks.append(.{
.start = index,
.value = try allocator.dupe(u8, content[index .. index + l]),
.line = lines.items.len,
.char = switch (lines.items.len) {
0 => index,
else => index - lines.items[lines.items.len - 1],
},
});
index += l;
}
lsp.logger.notify(.err, "done with initial tokenization, generated {d} tokens", .{toks.items.len});
return toks;
}

272
src/tokenizer.zig Normal file
View File

@ -0,0 +1,272 @@
const std = @import("std");
pub const Token = struct {
tag: Tag,
loc: Loc,
pub const Loc = struct {
start: usize,
end: usize,
};
pub const Tag = enum {
sym,
num,
str,
/// t
t,
/// nil
nil,
/// =
assign,
/// -=
assign_sub,
/// /=
assign_div,
/// *=
assign_mul,
/// +=
assign_add,
/// ==
op_eq,
/// >
op_gt,
/// >=
op_geq,
/// <
op_lt,
/// <=
op_leq,
/// /
op_div,
/// *
op_mul,
/// +
op_add,
/// -
op_sub,
/// ->
op_acc,
/// ~>
op_derefacc,
/// %
op_mod,
/// !
op_not,
/// !=
op_neq,
/// ||
op_or,
/// &&
op_and,
/// (
list_l,
/// '(
list_lz,
/// )
list_r,
/// @keys
kw_keys,
/// @rest
kw_rest,
};
pub fn format(self: *const Token, comptime _: []const u8, _: std.fmt.FormatOptions, writer: anytype) !void {
try writer.print("{d}:{d} .{s}", .{ self.loc.start, self.loc.end, @tagName(self.tag) });
}
};
pub const Tokenizer = struct {
buffer: [:0]const u8,
index: usize,
start: usize,
const State = enum {
start,
alphanum_identifier,
number_or_float,
decimals,
signed_exponent,
unsigned_exponent,
string,
op_plus,
op_minus,
op_star,
op_fslash,
op_pipe,
op_amp,
op_excl,
op_deref,
op_eq,
list_l,
list_lz,
list_r,
};
pub fn init(buf: [:0]const u8) Tokenizer {
return .{
.buffer = buf,
.index = 0,
.start = 0,
};
}
pub fn next(self: *Tokenizer) ?Token {
var state: State = .start;
while (self.index < self.buffer.len) : (self.index += 1) {
const c = self.buffer[self.index];
const loc = Token.Loc{ .start = self.start, .end = self.index };
state = switch (state) {
.start => blk: {
self.start = self.index;
break :blk switch (c) {
'a'...'z', 'A'...'Z', '_' => .alphanum_identifier,
'0'...'9' => .number_or_float,
'.' => .decimals,
'"' => .string,
'+' => .op_plus,
'-' => .op_minus,
'*' => .op_star,
'/' => .op_fslash,
'|' => .op_pipe,
'&' => .op_amp,
'!' => .op_excl,
'~' => .op_deref,
'=' => .op_eq,
'(' => .list_l,
')' => .list_r,
'\'' => .list_lz,
else => .start,
};
},
.alphanum_identifier => switch (c) {
'a'...'z', 'A'...'Z', '0'...'9', '_' => .alphanum_identifier,
else => {
inline for (.{ Token.Tag.t, Token.Tag.nil }) |alphanum_tag| {
if (std.mem.eql(u8, self.buffer[self.start..self.index], @tagName(alphanum_tag))) {
return Token{ .tag = alphanum_tag, .loc = loc };
}
}
return Token{ .tag = .sym, .loc = loc };
},
},
.number_or_float => switch (c) {
'0'...'9' => .number_or_float,
'.' => .decimals,
'e' => .signed_exponent,
' ', '\n' => {
return Token{ .tag = .num, .loc = loc };
},
else => unreachable,
},
.decimals => switch (c) {
'0'...'9' => .decimals,
' ', '\n' => {
return Token{ .tag = .num, .loc = loc };
},
else => unreachable,
},
.signed_exponent => switch (c) {
'0'...'9', '+', '-' => .unsigned_exponent,
else => unreachable,
},
.unsigned_exponent => switch (c) {
'0'...'9' => .unsigned_exponent,
' ', '\n' => {
return Token{ .tag = .num, .loc = loc };
},
else => unreachable,
},
.string => switch (c) {
'"' => {
return Token{ .tag = .str, .loc = loc };
},
'\\' => blk: {
self.index += 1;
break :blk .string;
},
else => .string,
},
.op_plus, .op_minus, .op_fslash, .op_star, .op_excl, .op_eq => switch (c) {
'=' => {
return Token{ .tag = switch (state) {
.op_plus => .assign_add,
.op_minus => .assign_sub,
.op_star => .assign_mul,
.op_fslash => .assign_div,
.op_excl => .op_neq,
.op_eq => .op_eq,
else => unreachable,
}, .loc = loc };
},
' ', '\n' => {
return Token{ .tag = switch (state) {
.op_plus => .op_add,
.op_minus => .op_sub,
.op_star => .op_mul,
.op_fslash => .op_div,
.op_excl => .op_not,
.op_eq => .assign,
else => unreachable,
}, .loc = loc };
},
'>' => {
return Token{ .tag = switch (state) {
.op_minus => .op_acc,
else => unreachable,
}, .loc = loc };
},
else => unreachable,
},
.op_pipe => switch (c) {
'|' => {
return Token{ .tag = .op_or, .loc = loc };
},
else => unreachable,
},
.op_amp => switch (c) {
'&' => {
return Token{ .tag = .op_and, .loc = loc };
},
else => unreachable,
},
.op_deref => switch (c) {
'>' => {
return Token{ .tag = .op_derefacc, .loc = loc };
},
else => unreachable,
},
.list_l => {
return Token{ .tag = .list_l, .loc = loc };
},
.list_r => {
return Token{ .tag = .list_r, .loc = loc };
},
.list_lz => switch (c) {
'(' => {
return Token{ .tag = .op_derefacc, .loc = loc };
},
else => unreachable,
},
};
}
return null;
}
};
test "simple tokenization" {
const example =
\\t
\\nil
\\a = b
\\"some string w/ escaped\""
;
var tokz = Tokenizer.init(example);
try std.testing.expectEqual(Token{ .loc = .{ .start = 0, .end = 1 }, .tag = .t }, tokz.next());
try std.testing.expectEqual(Token{ .loc = .{ .start = 2, .end = 5 }, .tag = .nil }, tokz.next());
try std.testing.expectEqual(Token{ .loc = .{ .start = 6, .end = 7 }, .tag = .sym }, tokz.next());
try std.testing.expectEqual(Token{ .loc = .{ .start = 8, .end = 9 }, .tag = .assign }, tokz.next());
try std.testing.expectEqual(Token{ .loc = .{ .start = 10, .end = 11 }, .tag = .sym }, tokz.next());
try std.testing.expectEqual(Token{ .loc = .{ .start = 12, .end = 37 }, .tag = .str }, tokz.next());
}

40
todo.md
View File

@ -1,40 +0,0 @@
# TODOs
- [x] Paren pair parsing
- iterative parsing and matching of paren/bracket pairs
- [ ] tokenizer
- identify "tokens"
- everythin is a token with exception of:
- operators
- parens/brackets
- numbers
- t / nil
- comments (maybe already handled)
- [ ] namespaces / scopes
- namespaces are started with:
- let / letseq / let...
```skill
; let[T]( locals: list[tuple[symbol, Any] | symbol] | nil, *exprs: Any, last_expr: T) -> T
```
- prog
```skill
; prog( locals: list[symbol] | nil, *exprs: Any) -> Any
```
- procedure
```skill
; function_name(req_param: Any, key_param1: any = value_param2) => Any
procedure( function_name(req_param @keys (key_param1 value_param2))
...
)
function_name(<req_arg> ?key_param1 <value_param2>)
```
- [ ] token contextualization
- looks for declaration / definition of symbol

355
uv.lock
View File

@ -1,355 +0,0 @@
version = 1
requires-python = ">=3.12"
[[package]]
name = "attrs"
version = "24.3.0"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/48/c8/6260f8ccc11f0917360fc0da435c5c9c7504e3db174d5a12a1494887b045/attrs-24.3.0.tar.gz", hash = "sha256:8f5c07333d543103541ba7be0e2ce16eeee8130cb0b3f9238ab904ce1e85baff", size = 805984 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/89/aa/ab0f7891a01eeb2d2e338ae8fecbe57fcebea1a24dbb64d45801bfab481d/attrs-24.3.0-py3-none-any.whl", hash = "sha256:ac96cd038792094f438ad1f6ff80837353805ac950cd2aa0e0625ef19850c308", size = 63397 },
]
[[package]]
name = "black"
version = "24.10.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "click" },
{ name = "mypy-extensions" },
{ name = "packaging" },
{ name = "pathspec" },
{ name = "platformdirs" },
]
sdist = { url = "https://files.pythonhosted.org/packages/d8/0d/cc2fb42b8c50d80143221515dd7e4766995bd07c56c9a3ed30baf080b6dc/black-24.10.0.tar.gz", hash = "sha256:846ea64c97afe3bc677b761787993be4991810ecc7a4a937816dd6bddedc4875", size = 645813 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/90/04/bf74c71f592bcd761610bbf67e23e6a3cff824780761f536512437f1e655/black-24.10.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:b5e39e0fae001df40f95bd8cc36b9165c5e2ea88900167bddf258bacef9bbdc3", size = 1644256 },
{ url = "https://files.pythonhosted.org/packages/4c/ea/a77bab4cf1887f4b2e0bce5516ea0b3ff7d04ba96af21d65024629afedb6/black-24.10.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:d37d422772111794b26757c5b55a3eade028aa3fde43121ab7b673d050949d65", size = 1448534 },
{ url = "https://files.pythonhosted.org/packages/4e/3e/443ef8bc1fbda78e61f79157f303893f3fddf19ca3c8989b163eb3469a12/black-24.10.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:14b3502784f09ce2443830e3133dacf2c0110d45191ed470ecb04d0f5f6fcb0f", size = 1761892 },
{ url = "https://files.pythonhosted.org/packages/52/93/eac95ff229049a6901bc84fec6908a5124b8a0b7c26ea766b3b8a5debd22/black-24.10.0-cp312-cp312-win_amd64.whl", hash = "sha256:30d2c30dc5139211dda799758559d1b049f7f14c580c409d6ad925b74a4208a8", size = 1434796 },
{ url = "https://files.pythonhosted.org/packages/d0/a0/a993f58d4ecfba035e61fca4e9f64a2ecae838fc9f33ab798c62173ed75c/black-24.10.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:1cbacacb19e922a1d75ef2b6ccaefcd6e93a2c05ede32f06a21386a04cedb981", size = 1643986 },
{ url = "https://files.pythonhosted.org/packages/37/d5/602d0ef5dfcace3fb4f79c436762f130abd9ee8d950fa2abdbf8bbc555e0/black-24.10.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:1f93102e0c5bb3907451063e08b9876dbeac810e7da5a8bfb7aeb5a9ef89066b", size = 1448085 },
{ url = "https://files.pythonhosted.org/packages/47/6d/a3a239e938960df1a662b93d6230d4f3e9b4a22982d060fc38c42f45a56b/black-24.10.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ddacb691cdcdf77b96f549cf9591701d8db36b2f19519373d60d31746068dbf2", size = 1760928 },
{ url = "https://files.pythonhosted.org/packages/dd/cf/af018e13b0eddfb434df4d9cd1b2b7892bab119f7a20123e93f6910982e8/black-24.10.0-cp313-cp313-win_amd64.whl", hash = "sha256:680359d932801c76d2e9c9068d05c6b107f2584b2a5b88831c83962eb9984c1b", size = 1436875 },
{ url = "https://files.pythonhosted.org/packages/8d/a7/4b27c50537ebca8bec139b872861f9d2bf501c5ec51fcf897cb924d9e264/black-24.10.0-py3-none-any.whl", hash = "sha256:3bb2b7a1f7b685f85b11fed1ef10f8a9148bceb49853e47a294a3dd963c1dd7d", size = 206898 },
]
[[package]]
name = "cattrs"
version = "24.1.2"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "attrs" },
]
sdist = { url = "https://files.pythonhosted.org/packages/64/65/af6d57da2cb32c076319b7489ae0958f746949d407109e3ccf4d115f147c/cattrs-24.1.2.tar.gz", hash = "sha256:8028cfe1ff5382df59dd36474a86e02d817b06eaf8af84555441bac915d2ef85", size = 426462 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/c8/d5/867e75361fc45f6de75fe277dd085627a9db5ebb511a87f27dc1396b5351/cattrs-24.1.2-py3-none-any.whl", hash = "sha256:67c7495b760168d931a10233f979b28dc04daf853b30752246f4f8471c6d68d0", size = 66446 },
]
[[package]]
name = "click"
version = "8.1.8"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "colorama", marker = "sys_platform == 'win32'" },
]
sdist = { url = "https://files.pythonhosted.org/packages/b9/2e/0090cbf739cee7d23781ad4b89a9894a41538e4fcf4c31dcdd705b78eb8b/click-8.1.8.tar.gz", hash = "sha256:ed53c9d8990d83c2a27deae68e4ee337473f6330c040a31d4225c9574d16096a", size = 226593 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/7e/d4/7ebdbd03970677812aac39c869717059dbb71a4cfc033ca6e5221787892c/click-8.1.8-py3-none-any.whl", hash = "sha256:63c132bbbed01578a06712a2d1f497bb62d9c1c0d329b7903a866228027263b2", size = 98188 },
]
[[package]]
name = "colorama"
version = "0.4.6"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/d8/53/6f443c9a4a8358a93a6792e2acffb9d9d5cb0a5cfd8802644b7b1c9a02e4/colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44", size = 27697 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335 },
]
[[package]]
name = "iniconfig"
version = "2.0.0"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/d7/4b/cbd8e699e64a6f16ca3a8220661b5f83792b3017d0f79807cb8708d33913/iniconfig-2.0.0.tar.gz", hash = "sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3", size = 4646 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/ef/a6/62565a6e1cf69e10f5727360368e451d4b7f58beeac6173dc9db836a5b46/iniconfig-2.0.0-py3-none-any.whl", hash = "sha256:b6a85871a79d2e3b22d2d1b94ac2824226a63c6b741c88f7ae975f18b6778374", size = 5892 },
]
[[package]]
name = "lsprotocol"
version = "2023.0.1"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "attrs" },
{ name = "cattrs" },
]
sdist = { url = "https://files.pythonhosted.org/packages/9d/f6/6e80484ec078d0b50699ceb1833597b792a6c695f90c645fbaf54b947e6f/lsprotocol-2023.0.1.tar.gz", hash = "sha256:cc5c15130d2403c18b734304339e51242d3018a05c4f7d0f198ad6e0cd21861d", size = 69434 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/8d/37/2351e48cb3309673492d3a8c59d407b75fb6630e560eb27ecd4da03adc9a/lsprotocol-2023.0.1-py3-none-any.whl", hash = "sha256:c75223c9e4af2f24272b14c6375787438279369236cd568f596d4951052a60f2", size = 70826 },
]
[[package]]
name = "markdown-it-py"
version = "3.0.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "mdurl" },
]
sdist = { url = "https://files.pythonhosted.org/packages/38/71/3b932df36c1a044d397a1f92d1cf91ee0a503d91e470cbd670aa66b07ed0/markdown-it-py-3.0.0.tar.gz", hash = "sha256:e3f60a94fa066dc52ec76661e37c851cb232d92f9886b15cb560aaada2df8feb", size = 74596 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/42/d7/1ec15b46af6af88f19b8e5ffea08fa375d433c998b8a7639e76935c14f1f/markdown_it_py-3.0.0-py3-none-any.whl", hash = "sha256:355216845c60bd96232cd8d8c40e8f9765cc86f46880e43a8fd22dc1a1a8cab1", size = 87528 },
]
[[package]]
name = "mdurl"
version = "0.1.2"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/d6/54/cfe61301667036ec958cb99bd3efefba235e65cdeb9c84d24a8293ba1d90/mdurl-0.1.2.tar.gz", hash = "sha256:bb413d29f5eea38f31dd4754dd7377d4465116fb207585f97bf925588687c1ba", size = 8729 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/b3/38/89ba8ad64ae25be8de66a6d463314cf1eb366222074cfda9ee839c56a4b4/mdurl-0.1.2-py3-none-any.whl", hash = "sha256:84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8", size = 9979 },
]
[[package]]
name = "mypy"
version = "1.14.1"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "mypy-extensions" },
{ name = "typing-extensions" },
]
sdist = { url = "https://files.pythonhosted.org/packages/b9/eb/2c92d8ea1e684440f54fa49ac5d9a5f19967b7b472a281f419e69a8d228e/mypy-1.14.1.tar.gz", hash = "sha256:7ec88144fe9b510e8475ec2f5f251992690fcf89ccb4500b214b4226abcd32d6", size = 3216051 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/43/1b/b38c079609bb4627905b74fc6a49849835acf68547ac33d8ceb707de5f52/mypy-1.14.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:30ff5ef8519bbc2e18b3b54521ec319513a26f1bba19a7582e7b1f58a6e69f14", size = 11266668 },
{ url = "https://files.pythonhosted.org/packages/6b/75/2ed0d2964c1ffc9971c729f7a544e9cd34b2cdabbe2d11afd148d7838aa2/mypy-1.14.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:cb9f255c18052343c70234907e2e532bc7e55a62565d64536dbc7706a20b78b9", size = 10254060 },
{ url = "https://files.pythonhosted.org/packages/a1/5f/7b8051552d4da3c51bbe8fcafffd76a6823779101a2b198d80886cd8f08e/mypy-1.14.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8b4e3413e0bddea671012b063e27591b953d653209e7a4fa5e48759cda77ca11", size = 11933167 },
{ url = "https://files.pythonhosted.org/packages/04/90/f53971d3ac39d8b68bbaab9a4c6c58c8caa4d5fd3d587d16f5927eeeabe1/mypy-1.14.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:553c293b1fbdebb6c3c4030589dab9fafb6dfa768995a453d8a5d3b23784af2e", size = 12864341 },
{ url = "https://files.pythonhosted.org/packages/03/d2/8bc0aeaaf2e88c977db41583559319f1821c069e943ada2701e86d0430b7/mypy-1.14.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:fad79bfe3b65fe6a1efaed97b445c3d37f7be9fdc348bdb2d7cac75579607c89", size = 12972991 },
{ url = "https://files.pythonhosted.org/packages/6f/17/07815114b903b49b0f2cf7499f1c130e5aa459411596668267535fe9243c/mypy-1.14.1-cp312-cp312-win_amd64.whl", hash = "sha256:8fa2220e54d2946e94ab6dbb3ba0a992795bd68b16dc852db33028df2b00191b", size = 9879016 },
{ url = "https://files.pythonhosted.org/packages/9e/15/bb6a686901f59222275ab228453de741185f9d54fecbaacec041679496c6/mypy-1.14.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:92c3ed5afb06c3a8e188cb5da4984cab9ec9a77ba956ee419c68a388b4595255", size = 11252097 },
{ url = "https://files.pythonhosted.org/packages/f8/b3/8b0f74dfd072c802b7fa368829defdf3ee1566ba74c32a2cb2403f68024c/mypy-1.14.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:dbec574648b3e25f43d23577309b16534431db4ddc09fda50841f1e34e64ed34", size = 10239728 },
{ url = "https://files.pythonhosted.org/packages/c5/9b/4fd95ab20c52bb5b8c03cc49169be5905d931de17edfe4d9d2986800b52e/mypy-1.14.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8c6d94b16d62eb3e947281aa7347d78236688e21081f11de976376cf010eb31a", size = 11924965 },
{ url = "https://files.pythonhosted.org/packages/56/9d/4a236b9c57f5d8f08ed346914b3f091a62dd7e19336b2b2a0d85485f82ff/mypy-1.14.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d4b19b03fdf54f3c5b2fa474c56b4c13c9dbfb9a2db4370ede7ec11a2c5927d9", size = 12867660 },
{ url = "https://files.pythonhosted.org/packages/40/88/a61a5497e2f68d9027de2bb139c7bb9abaeb1be1584649fa9d807f80a338/mypy-1.14.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:0c911fde686394753fff899c409fd4e16e9b294c24bfd5e1ea4675deae1ac6fd", size = 12969198 },
{ url = "https://files.pythonhosted.org/packages/54/da/3d6fc5d92d324701b0c23fb413c853892bfe0e1dbe06c9138037d459756b/mypy-1.14.1-cp313-cp313-win_amd64.whl", hash = "sha256:8b21525cb51671219f5307be85f7e646a153e5acc656e5cebf64bfa076c50107", size = 9885276 },
{ url = "https://files.pythonhosted.org/packages/a0/b5/32dd67b69a16d088e533962e5044e51004176a9952419de0370cdaead0f8/mypy-1.14.1-py3-none-any.whl", hash = "sha256:b66a60cc4073aeb8ae00057f9c1f64d49e90f918fbcef9a977eb121da8b8f1d1", size = 2752905 },
]
[[package]]
name = "mypy-extensions"
version = "1.0.0"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/98/a4/1ab47638b92648243faf97a5aeb6ea83059cc3624972ab6b8d2316078d3f/mypy_extensions-1.0.0.tar.gz", hash = "sha256:75dbf8955dc00442a438fc4d0666508a9a97b6bd41aa2f0ffe9d2f2725af0782", size = 4433 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/2a/e2/5d3f6ada4297caebe1a2add3b126fe800c96f56dbe5d1988a2cbe0b267aa/mypy_extensions-1.0.0-py3-none-any.whl", hash = "sha256:4392f6c0eb8a5668a69e23d168ffa70f0be9ccfd32b5cc2d26a34ae5b844552d", size = 4695 },
]
[[package]]
name = "packaging"
version = "24.2"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/d0/63/68dbb6eb2de9cb10ee4c9c14a0148804425e13c4fb20d61cce69f53106da/packaging-24.2.tar.gz", hash = "sha256:c228a6dc5e932d346bc5739379109d49e8853dd8223571c7c5b55260edc0b97f", size = 163950 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/88/ef/eb23f262cca3c0c4eb7ab1933c3b1f03d021f2c48f54763065b6f0e321be/packaging-24.2-py3-none-any.whl", hash = "sha256:09abb1bccd265c01f4a3aa3f7a7db064b36514d2cba19a2f694fe6150451a759", size = 65451 },
]
[[package]]
name = "parsimonious"
version = "0.10.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "regex" },
]
sdist = { url = "https://files.pythonhosted.org/packages/7b/91/abdc50c4ef06fdf8d047f60ee777ca9b2a7885e1a9cea81343fbecda52d7/parsimonious-0.10.0.tar.gz", hash = "sha256:8281600da180ec8ae35427a4ab4f7b82bfec1e3d1e52f80cb60ea82b9512501c", size = 52172 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/aa/0f/c8b64d9b54ea631fcad4e9e3c8dbe8c11bb32a623be94f22974c88e71eaf/parsimonious-0.10.0-py3-none-any.whl", hash = "sha256:982ab435fabe86519b57f6b35610aa4e4e977e9f02a14353edf4bbc75369fc0f", size = 48427 },
]
[[package]]
name = "pathspec"
version = "0.12.1"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/ca/bc/f35b8446f4531a7cb215605d100cd88b7ac6f44ab3fc94870c120ab3adbf/pathspec-0.12.1.tar.gz", hash = "sha256:a482d51503a1ab33b1c67a6c3813a26953dbdc71c31dacaef9a838c4e29f5712", size = 51043 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/cc/20/ff623b09d963f88bfde16306a54e12ee5ea43e9b597108672ff3a408aad6/pathspec-0.12.1-py3-none-any.whl", hash = "sha256:a0d503e138a4c123b27490a4f7beda6a01c6f288df0e4a8b79c7eb0dc7b4cc08", size = 31191 },
]
[[package]]
name = "platformdirs"
version = "4.3.6"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/13/fc/128cc9cb8f03208bdbf93d3aa862e16d376844a14f9a0ce5cf4507372de4/platformdirs-4.3.6.tar.gz", hash = "sha256:357fb2acbc885b0419afd3ce3ed34564c13c9b95c89360cd9563f73aa5e2b907", size = 21302 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/3c/a6/bc1012356d8ece4d66dd75c4b9fc6c1f6650ddd5991e421177d9f8f671be/platformdirs-4.3.6-py3-none-any.whl", hash = "sha256:73e575e1408ab8103900836b97580d5307456908a03e92031bab39e4554cc3fb", size = 18439 },
]
[[package]]
name = "pluggy"
version = "1.5.0"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/96/2d/02d4312c973c6050a18b314a5ad0b3210edb65a906f868e31c111dede4a6/pluggy-1.5.0.tar.gz", hash = "sha256:2cffa88e94fdc978c4c574f15f9e59b7f4201d439195c3715ca9e2486f1d0cf1", size = 67955 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/88/5f/e351af9a41f866ac3f1fac4ca0613908d9a41741cfcf2228f4ad853b697d/pluggy-1.5.0-py3-none-any.whl", hash = "sha256:44e1ad92c8ca002de6377e165f3e0f1be63266ab4d554740532335b9d75ea669", size = 20556 },
]
[[package]]
name = "pygls"
version = "1.3.1"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "cattrs" },
{ name = "lsprotocol" },
]
sdist = { url = "https://files.pythonhosted.org/packages/86/b9/41d173dad9eaa9db9c785a85671fc3d68961f08d67706dc2e79011e10b5c/pygls-1.3.1.tar.gz", hash = "sha256:140edceefa0da0e9b3c533547c892a42a7d2fd9217ae848c330c53d266a55018", size = 45527 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/11/19/b74a10dd24548e96e8c80226cbacb28b021bc3a168a7d2709fb0d0185348/pygls-1.3.1-py3-none-any.whl", hash = "sha256:6e00f11efc56321bdeb6eac04f6d86131f654c7d49124344a9ebb968da3dd91e", size = 56031 },
]
[[package]]
name = "pygments"
version = "2.19.1"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/7c/2d/c3338d48ea6cc0feb8446d8e6937e1408088a72a39937982cc6111d17f84/pygments-2.19.1.tar.gz", hash = "sha256:61c16d2a8576dc0649d9f39e089b5f02bcd27fba10d8fb4dcc28173f7a45151f", size = 4968581 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/8a/0b/9fcc47d19c48b59121088dd6da2488a49d5f72dacf8262e2790a1d2c7d15/pygments-2.19.1-py3-none-any.whl", hash = "sha256:9ea1544ad55cecf4b8242fab6dd35a93bbce657034b0611ee383099054ab6d8c", size = 1225293 },
]
[[package]]
name = "pytest"
version = "8.3.4"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "colorama", marker = "sys_platform == 'win32'" },
{ name = "iniconfig" },
{ name = "packaging" },
{ name = "pluggy" },
]
sdist = { url = "https://files.pythonhosted.org/packages/05/35/30e0d83068951d90a01852cb1cef56e5d8a09d20c7f511634cc2f7e0372a/pytest-8.3.4.tar.gz", hash = "sha256:965370d062bce11e73868e0335abac31b4d3de0e82f4007408d242b4f8610761", size = 1445919 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/11/92/76a1c94d3afee238333bc0a42b82935dd8f9cf8ce9e336ff87ee14d9e1cf/pytest-8.3.4-py3-none-any.whl", hash = "sha256:50e16d954148559c9a74109af1eaf0c945ba2d8f30f0a3d3335edde19788b6f6", size = 343083 },
]
[[package]]
name = "regex"
version = "2024.11.6"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/8e/5f/bd69653fbfb76cf8604468d3b4ec4c403197144c7bfe0e6a5fc9e02a07cb/regex-2024.11.6.tar.gz", hash = "sha256:7ab159b063c52a0333c884e4679f8d7a85112ee3078fe3d9004b2dd875585519", size = 399494 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/ba/30/9a87ce8336b172cc232a0db89a3af97929d06c11ceaa19d97d84fa90a8f8/regex-2024.11.6-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:52fb28f528778f184f870b7cf8f225f5eef0a8f6e3778529bdd40c7b3920796a", size = 483781 },
{ url = "https://files.pythonhosted.org/packages/01/e8/00008ad4ff4be8b1844786ba6636035f7ef926db5686e4c0f98093612add/regex-2024.11.6-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:fdd6028445d2460f33136c55eeb1f601ab06d74cb3347132e1c24250187500d9", size = 288455 },
{ url = "https://files.pythonhosted.org/packages/60/85/cebcc0aff603ea0a201667b203f13ba75d9fc8668fab917ac5b2de3967bc/regex-2024.11.6-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:805e6b60c54bf766b251e94526ebad60b7de0c70f70a4e6210ee2891acb70bf2", size = 284759 },
{ url = "https://files.pythonhosted.org/packages/94/2b/701a4b0585cb05472a4da28ee28fdfe155f3638f5e1ec92306d924e5faf0/regex-2024.11.6-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b85c2530be953a890eaffde05485238f07029600e8f098cdf1848d414a8b45e4", size = 794976 },
{ url = "https://files.pythonhosted.org/packages/4b/bf/fa87e563bf5fee75db8915f7352e1887b1249126a1be4813837f5dbec965/regex-2024.11.6-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:bb26437975da7dc36b7efad18aa9dd4ea569d2357ae6b783bf1118dabd9ea577", size = 833077 },
{ url = "https://files.pythonhosted.org/packages/a1/56/7295e6bad94b047f4d0834e4779491b81216583c00c288252ef625c01d23/regex-2024.11.6-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:abfa5080c374a76a251ba60683242bc17eeb2c9818d0d30117b4486be10c59d3", size = 823160 },
{ url = "https://files.pythonhosted.org/packages/fb/13/e3b075031a738c9598c51cfbc4c7879e26729c53aa9cca59211c44235314/regex-2024.11.6-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:70b7fa6606c2881c1db9479b0eaa11ed5dfa11c8d60a474ff0e095099f39d98e", size = 796896 },
{ url = "https://files.pythonhosted.org/packages/24/56/0b3f1b66d592be6efec23a795b37732682520b47c53da5a32c33ed7d84e3/regex-2024.11.6-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0c32f75920cf99fe6b6c539c399a4a128452eaf1af27f39bce8909c9a3fd8cbe", size = 783997 },
{ url = "https://files.pythonhosted.org/packages/f9/a1/eb378dada8b91c0e4c5f08ffb56f25fcae47bf52ad18f9b2f33b83e6d498/regex-2024.11.6-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:982e6d21414e78e1f51cf595d7f321dcd14de1f2881c5dc6a6e23bbbbd68435e", size = 781725 },
{ url = "https://files.pythonhosted.org/packages/83/f2/033e7dec0cfd6dda93390089864732a3409246ffe8b042e9554afa9bff4e/regex-2024.11.6-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:a7c2155f790e2fb448faed6dd241386719802296ec588a8b9051c1f5c481bc29", size = 789481 },
{ url = "https://files.pythonhosted.org/packages/83/23/15d4552ea28990a74e7696780c438aadd73a20318c47e527b47a4a5a596d/regex-2024.11.6-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:149f5008d286636e48cd0b1dd65018548944e495b0265b45e1bffecce1ef7f39", size = 852896 },
{ url = "https://files.pythonhosted.org/packages/e3/39/ed4416bc90deedbfdada2568b2cb0bc1fdb98efe11f5378d9892b2a88f8f/regex-2024.11.6-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:e5364a4502efca094731680e80009632ad6624084aff9a23ce8c8c6820de3e51", size = 860138 },
{ url = "https://files.pythonhosted.org/packages/93/2d/dd56bb76bd8e95bbce684326302f287455b56242a4f9c61f1bc76e28360e/regex-2024.11.6-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:0a86e7eeca091c09e021db8eb72d54751e527fa47b8d5787caf96d9831bd02ad", size = 787692 },
{ url = "https://files.pythonhosted.org/packages/0b/55/31877a249ab7a5156758246b9c59539abbeba22461b7d8adc9e8475ff73e/regex-2024.11.6-cp312-cp312-win32.whl", hash = "sha256:32f9a4c643baad4efa81d549c2aadefaeba12249b2adc5af541759237eee1c54", size = 262135 },
{ url = "https://files.pythonhosted.org/packages/38/ec/ad2d7de49a600cdb8dd78434a1aeffe28b9d6fc42eb36afab4a27ad23384/regex-2024.11.6-cp312-cp312-win_amd64.whl", hash = "sha256:a93c194e2df18f7d264092dc8539b8ffb86b45b899ab976aa15d48214138e81b", size = 273567 },
{ url = "https://files.pythonhosted.org/packages/90/73/bcb0e36614601016552fa9344544a3a2ae1809dc1401b100eab02e772e1f/regex-2024.11.6-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:a6ba92c0bcdf96cbf43a12c717eae4bc98325ca3730f6b130ffa2e3c3c723d84", size = 483525 },
{ url = "https://files.pythonhosted.org/packages/0f/3f/f1a082a46b31e25291d830b369b6b0c5576a6f7fb89d3053a354c24b8a83/regex-2024.11.6-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:525eab0b789891ac3be914d36893bdf972d483fe66551f79d3e27146191a37d4", size = 288324 },
{ url = "https://files.pythonhosted.org/packages/09/c9/4e68181a4a652fb3ef5099e077faf4fd2a694ea6e0f806a7737aff9e758a/regex-2024.11.6-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:086a27a0b4ca227941700e0b31425e7a28ef1ae8e5e05a33826e17e47fbfdba0", size = 284617 },
{ url = "https://files.pythonhosted.org/packages/fc/fd/37868b75eaf63843165f1d2122ca6cb94bfc0271e4428cf58c0616786dce/regex-2024.11.6-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bde01f35767c4a7899b7eb6e823b125a64de314a8ee9791367c9a34d56af18d0", size = 795023 },
{ url = "https://files.pythonhosted.org/packages/c4/7c/d4cd9c528502a3dedb5c13c146e7a7a539a3853dc20209c8e75d9ba9d1b2/regex-2024.11.6-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b583904576650166b3d920d2bcce13971f6f9e9a396c673187f49811b2769dc7", size = 833072 },
{ url = "https://files.pythonhosted.org/packages/4f/db/46f563a08f969159c5a0f0e722260568425363bea43bb7ae370becb66a67/regex-2024.11.6-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1c4de13f06a0d54fa0d5ab1b7138bfa0d883220965a29616e3ea61b35d5f5fc7", size = 823130 },
{ url = "https://files.pythonhosted.org/packages/db/60/1eeca2074f5b87df394fccaa432ae3fc06c9c9bfa97c5051aed70e6e00c2/regex-2024.11.6-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3cde6e9f2580eb1665965ce9bf17ff4952f34f5b126beb509fee8f4e994f143c", size = 796857 },
{ url = "https://files.pythonhosted.org/packages/10/db/ac718a08fcee981554d2f7bb8402f1faa7e868c1345c16ab1ebec54b0d7b/regex-2024.11.6-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0d7f453dca13f40a02b79636a339c5b62b670141e63efd511d3f8f73fba162b3", size = 784006 },
{ url = "https://files.pythonhosted.org/packages/c2/41/7da3fe70216cea93144bf12da2b87367590bcf07db97604edeea55dac9ad/regex-2024.11.6-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:59dfe1ed21aea057a65c6b586afd2a945de04fc7db3de0a6e3ed5397ad491b07", size = 781650 },
{ url = "https://files.pythonhosted.org/packages/a7/d5/880921ee4eec393a4752e6ab9f0fe28009435417c3102fc413f3fe81c4e5/regex-2024.11.6-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:b97c1e0bd37c5cd7902e65f410779d39eeda155800b65fc4d04cc432efa9bc6e", size = 789545 },
{ url = "https://files.pythonhosted.org/packages/dc/96/53770115e507081122beca8899ab7f5ae28ae790bfcc82b5e38976df6a77/regex-2024.11.6-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:f9d1e379028e0fc2ae3654bac3cbbef81bf3fd571272a42d56c24007979bafb6", size = 853045 },
{ url = "https://files.pythonhosted.org/packages/31/d3/1372add5251cc2d44b451bd94f43b2ec78e15a6e82bff6a290ef9fd8f00a/regex-2024.11.6-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:13291b39131e2d002a7940fb176e120bec5145f3aeb7621be6534e46251912c4", size = 860182 },
{ url = "https://files.pythonhosted.org/packages/ed/e3/c446a64984ea9f69982ba1a69d4658d5014bc7a0ea468a07e1a1265db6e2/regex-2024.11.6-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:4f51f88c126370dcec4908576c5a627220da6c09d0bff31cfa89f2523843316d", size = 787733 },
{ url = "https://files.pythonhosted.org/packages/2b/f1/e40c8373e3480e4f29f2692bd21b3e05f296d3afebc7e5dcf21b9756ca1c/regex-2024.11.6-cp313-cp313-win32.whl", hash = "sha256:63b13cfd72e9601125027202cad74995ab26921d8cd935c25f09c630436348ff", size = 262122 },
{ url = "https://files.pythonhosted.org/packages/45/94/bc295babb3062a731f52621cdc992d123111282e291abaf23faa413443ea/regex-2024.11.6-cp313-cp313-win_amd64.whl", hash = "sha256:2b3361af3198667e99927da8b84c1b010752fa4b1115ee30beaa332cabc3ef1a", size = 273545 },
]
[[package]]
name = "rich"
version = "13.9.4"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "markdown-it-py" },
{ name = "pygments" },
]
sdist = { url = "https://files.pythonhosted.org/packages/ab/3a/0316b28d0761c6734d6bc14e770d85506c986c85ffb239e688eeaab2c2bc/rich-13.9.4.tar.gz", hash = "sha256:439594978a49a09530cff7ebc4b5c7103ef57baf48d5ea3184f21d9a2befa098", size = 223149 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/19/71/39c7c0d87f8d4e6c020a393182060eaefeeae6c01dab6a84ec346f2567df/rich-13.9.4-py3-none-any.whl", hash = "sha256:6049d5e6ec054bf2779ab3358186963bac2ea89175919d699e378b99738c2a90", size = 242424 },
]
[[package]]
name = "ruff"
version = "0.9.2"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/80/63/77ecca9d21177600f551d1c58ab0e5a0b260940ea7312195bd2a4798f8a8/ruff-0.9.2.tar.gz", hash = "sha256:b5eceb334d55fae5f316f783437392642ae18e16dcf4f1858d55d3c2a0f8f5d0", size = 3553799 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/af/b9/0e168e4e7fb3af851f739e8f07889b91d1a33a30fca8c29fa3149d6b03ec/ruff-0.9.2-py3-none-linux_armv6l.whl", hash = "sha256:80605a039ba1454d002b32139e4970becf84b5fee3a3c3bf1c2af6f61a784347", size = 11652408 },
{ url = "https://files.pythonhosted.org/packages/2c/22/08ede5db17cf701372a461d1cb8fdde037da1d4fa622b69ac21960e6237e/ruff-0.9.2-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:b9aab82bb20afd5f596527045c01e6ae25a718ff1784cb92947bff1f83068b00", size = 11587553 },
{ url = "https://files.pythonhosted.org/packages/42/05/dedfc70f0bf010230229e33dec6e7b2235b2a1b8cbb2a991c710743e343f/ruff-0.9.2-py3-none-macosx_11_0_arm64.whl", hash = "sha256:fbd337bac1cfa96be615f6efcd4bc4d077edbc127ef30e2b8ba2a27e18c054d4", size = 11020755 },
{ url = "https://files.pythonhosted.org/packages/df/9b/65d87ad9b2e3def67342830bd1af98803af731243da1255537ddb8f22209/ruff-0.9.2-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:82b35259b0cbf8daa22a498018e300b9bb0174c2bbb7bcba593935158a78054d", size = 11826502 },
{ url = "https://files.pythonhosted.org/packages/93/02/f2239f56786479e1a89c3da9bc9391120057fc6f4a8266a5b091314e72ce/ruff-0.9.2-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:8b6a9701d1e371bf41dca22015c3f89769da7576884d2add7317ec1ec8cb9c3c", size = 11390562 },
{ url = "https://files.pythonhosted.org/packages/c9/37/d3a854dba9931f8cb1b2a19509bfe59e00875f48ade632e95aefcb7a0aee/ruff-0.9.2-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9cc53e68b3c5ae41e8faf83a3b89f4a5d7b2cb666dff4b366bb86ed2a85b481f", size = 12548968 },
{ url = "https://files.pythonhosted.org/packages/fa/c3/c7b812bb256c7a1d5553433e95980934ffa85396d332401f6b391d3c4569/ruff-0.9.2-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:8efd9da7a1ee314b910da155ca7e8953094a7c10d0c0a39bfde3fcfd2a015684", size = 13187155 },
{ url = "https://files.pythonhosted.org/packages/bd/5a/3c7f9696a7875522b66aa9bba9e326e4e5894b4366bd1dc32aa6791cb1ff/ruff-0.9.2-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3292c5a22ea9a5f9a185e2d131dc7f98f8534a32fb6d2ee7b9944569239c648d", size = 12704674 },
{ url = "https://files.pythonhosted.org/packages/be/d6/d908762257a96ce5912187ae9ae86792e677ca4f3dc973b71e7508ff6282/ruff-0.9.2-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1a605fdcf6e8b2d39f9436d343d1f0ff70c365a1e681546de0104bef81ce88df", size = 14529328 },
{ url = "https://files.pythonhosted.org/packages/2d/c2/049f1e6755d12d9cd8823242fa105968f34ee4c669d04cac8cea51a50407/ruff-0.9.2-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c547f7f256aa366834829a08375c297fa63386cbe5f1459efaf174086b564247", size = 12385955 },
{ url = "https://files.pythonhosted.org/packages/91/5a/a9bdb50e39810bd9627074e42743b00e6dc4009d42ae9f9351bc3dbc28e7/ruff-0.9.2-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:d18bba3d3353ed916e882521bc3e0af403949dbada344c20c16ea78f47af965e", size = 11810149 },
{ url = "https://files.pythonhosted.org/packages/e5/fd/57df1a0543182f79a1236e82a79c68ce210efb00e97c30657d5bdb12b478/ruff-0.9.2-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:b338edc4610142355ccf6b87bd356729b62bf1bc152a2fad5b0c7dc04af77bfe", size = 11479141 },
{ url = "https://files.pythonhosted.org/packages/dc/16/bc3fd1d38974f6775fc152a0554f8c210ff80f2764b43777163c3c45d61b/ruff-0.9.2-py3-none-musllinux_1_2_i686.whl", hash = "sha256:492a5e44ad9b22a0ea98cf72e40305cbdaf27fac0d927f8bc9e1df316dcc96eb", size = 12014073 },
{ url = "https://files.pythonhosted.org/packages/47/6b/e4ca048a8f2047eb652e1e8c755f384d1b7944f69ed69066a37acd4118b0/ruff-0.9.2-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:af1e9e9fe7b1f767264d26b1075ac4ad831c7db976911fa362d09b2d0356426a", size = 12435758 },
{ url = "https://files.pythonhosted.org/packages/c2/40/4d3d6c979c67ba24cf183d29f706051a53c36d78358036a9cd21421582ab/ruff-0.9.2-py3-none-win32.whl", hash = "sha256:71cbe22e178c5da20e1514e1e01029c73dc09288a8028a5d3446e6bba87a5145", size = 9796916 },
{ url = "https://files.pythonhosted.org/packages/c3/ef/7f548752bdb6867e6939489c87fe4da489ab36191525fadc5cede2a6e8e2/ruff-0.9.2-py3-none-win_amd64.whl", hash = "sha256:c5e1d6abc798419cf46eed03f54f2e0c3adb1ad4b801119dedf23fcaf69b55b5", size = 10773080 },
{ url = "https://files.pythonhosted.org/packages/0e/4e/33df635528292bd2d18404e4daabcd74ca8a9853b2e1df85ed3d32d24362/ruff-0.9.2-py3-none-win_arm64.whl", hash = "sha256:a1b63fa24149918f8b37cef2ee6fff81f24f0d74b6f0bdc37bc3e1f2143e41c6", size = 10001738 },
]
[[package]]
name = "skillls"
version = "0.1.0"
source = { editable = "." }
dependencies = [
{ name = "parsimonious" },
{ name = "pygls" },
{ name = "rich" },
]
[package.optional-dependencies]
dev = [
{ name = "black" },
{ name = "mypy" },
{ name = "pytest" },
{ name = "ruff" },
{ name = "types-parsimonious" },
]
[package.metadata]
requires-dist = [
{ name = "black", marker = "extra == 'dev'" },
{ name = "mypy", marker = "extra == 'dev'" },
{ name = "parsimonious", specifier = "~=0.10.0" },
{ name = "pygls" },
{ name = "pytest", marker = "extra == 'dev'" },
{ name = "rich" },
{ name = "ruff", marker = "extra == 'dev'" },
{ name = "types-parsimonious", marker = "extra == 'dev'" },
]
[[package]]
name = "types-parsimonious"
version = "0.10.0.20240331"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/11/8a/3e0f5d72ea35bc5fba64899fca45124a4398c78e28c40a3e119f15882d35/types-parsimonious-0.10.0.20240331.tar.gz", hash = "sha256:c9ca50c968b83203a285ee8fbe4a50c5aa6d8ca903d92802ee5398cb95608ceb", size = 5482 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/b5/fe/f6f9b4ec9c4cd0c567bcd581d65795bb07b0613fe3d15b249af348efa42a/types_parsimonious-0.10.0.20240331-py3-none-any.whl", hash = "sha256:6828faa2b74c03d229d2ea5b661a9de47589a837fec48424804c42b9113a28cd", size = 6308 },
]
[[package]]
name = "typing-extensions"
version = "4.12.2"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/df/db/f35a00659bc03fec321ba8bce9420de607a1d37f8342eee1863174c69557/typing_extensions-4.12.2.tar.gz", hash = "sha256:1a7ead55c7e559dd4dee8856e3a88b41225abfe1ce8df57b7c13915fe121ffb8", size = 85321 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/26/9f/ad63fc0248c5379346306f8668cda6e2e2e9c95e01216d2b8ffd9ff037d0/typing_extensions-4.12.2-py3-none-any.whl", hash = "sha256:04e5ca0351e0f3f85c6853954072df659d0d13fac324d0072316b67d7794700d", size = 37438 },
]