redo
This commit is contained in:
parent
51984e297b
commit
82b165dd21
|
|
@ -1,145 +0,0 @@
|
|||
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,
|
||||
)
|
||||
|
|
@ -1,6 +0,0 @@
|
|||
;; append
|
||||
append(
|
||||
l_list1
|
||||
l_list2
|
||||
) => l_result
|
||||
|
||||
|
|
@ -1,21 +0,0 @@
|
|||
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"]
|
||||
|
|
@ -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)
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
@ -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"
|
||||
123
skillls/main.py
123
skillls/main.py
|
|
@ -123,26 +123,7 @@ def offset_range(range: Range, lines: int, cols: int = 0) -> Range:
|
|||
)
|
||||
|
||||
|
||||
#
|
||||
# @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):
|
||||
contents: dict[str, TextDocument]
|
||||
trees: dict[str, Tree]
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
|
|
@ -165,111 +146,7 @@ class SkillLanguageServer(LanguageServer):
|
|||
notebook_document_sync,
|
||||
max_workers,
|
||||
)
|
||||
self.trees = {}
|
||||
self.contents = {}
|
||||
|
||||
def parse(self, doc: TextDocument) -> None:
|
||||
parsed = SKILL_PARSER.parse(doc.source.encode("utf8"), encoding="utf8")
|
||||
self.trees[doc.uri] = parsed
|
||||
self.contents[doc.uri] = doc
|
||||
|
||||
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 "UNEXPECTED" in str(node):
|
||||
msg = f"unexpected '{content}'"
|
||||
else:
|
||||
msg = str()
|
||||
|
||||
diags.append(
|
||||
Diagnostic(
|
||||
range,
|
||||
msg,
|
||||
severity=DiagnosticSeverity.Error,
|
||||
),
|
||||
)
|
||||
|
||||
return diags
|
||||
|
||||
def diagnose(self, uri: str) -> list[Diagnostic]:
|
||||
diags: list[Diagnostic] = []
|
||||
|
||||
diags.extend(self._diagnose_errors(uri))
|
||||
|
||||
return diags
|
||||
|
||||
|
||||
server = SkillLanguageServer("skillls", "v0.3")
|
||||
|
||||
|
||||
# @server.feature(TEXT_DOCUMENT_DID_SAVE)
|
||||
@server.feature(TEXT_DOCUMENT_DID_OPEN)
|
||||
def on_open(ls: SkillLanguageServer, params: DidSaveTextDocumentParams) -> None:
|
||||
doc = server.workspace.get_text_document(params.text_document.uri)
|
||||
ls.parse(doc)
|
||||
|
||||
diags = ls.diagnose(doc.uri)
|
||||
ls.publish_diagnostics(doc.uri, diags)
|
||||
|
||||
|
||||
@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)
|
||||
def doc_symbols(
|
||||
ls: SkillLanguageServer,
|
||||
params: DocumentSymbolParams,
|
||||
) -> list[DocumentSymbol]:
|
||||
# return ls.procs + ls.lets + ls.defs + ls.globals
|
||||
return []
|
||||
|
||||
|
||||
def main():
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -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)
|
||||
|
|
@ -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)
|
||||
|
|
@ -1 +0,0 @@
|
|||
|
||||
Loading…
Reference in New Issue