initial commit
This commit is contained in:
@@ -0,0 +1,32 @@
|
||||
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
|
||||
@@ -0,0 +1,71 @@
|
||||
from dataclasses import dataclass
|
||||
from functools import cached_property
|
||||
from typing import overload
|
||||
from lsprotocol.types import Position, Range
|
||||
|
||||
from parsimonious.nodes import Node
|
||||
|
||||
|
||||
# @total_ordering
|
||||
# class Position(NamedTuple):
|
||||
# line: int
|
||||
# char: int
|
||||
#
|
||||
# def __lt__(self, other: Self) -> bool:
|
||||
# return (self.line < other.line) or (
|
||||
# (self.line == other.line) and (self.char < other.char)
|
||||
# )
|
||||
#
|
||||
# def __eq__(self, other: Self) -> bool:
|
||||
# return (self.line == other.line) and (self.char == other.char)
|
||||
#
|
||||
#
|
||||
# class Range(NamedTuple):
|
||||
# start: Position
|
||||
# end: Position
|
||||
#
|
||||
# def __add__(self, other: Self) -> Self:
|
||||
# start = min(self.start, other.start)
|
||||
# end = max(self.end, other.end)
|
||||
# return Range(start, end)
|
||||
#
|
||||
# def contained_by(self, possibly_contained_by: Self) -> bool:
|
||||
# return (self.start >= possibly_contained_by.start) and (
|
||||
# self.end <= possibly_contained_by.end
|
||||
# )
|
||||
#
|
||||
# def contains(self, possibly_contains: Self) -> bool:
|
||||
# return (self.start <= possibly_contains.start) and (
|
||||
# self.end >= possibly_contains.end
|
||||
# )
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class Locator:
|
||||
raw: str
|
||||
|
||||
@cached_property
|
||||
def newlines(self) -> tuple[int, ...]:
|
||||
t = tuple(i for i, char in enumerate(self.raw) if char == "\n")
|
||||
return t
|
||||
|
||||
def _locate_pos(self, index: int) -> Position:
|
||||
line = next(i for i, char in enumerate(self.newlines) if char >= index)
|
||||
return Position(line - 1, index - (self.newlines[line - 1] if line > 0 else 0))
|
||||
|
||||
@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)
|
||||
|
||||
start = self._locate_pos(index.start)
|
||||
end = self._locate_pos(index.end)
|
||||
|
||||
return Range(start, end)
|
||||
@@ -0,0 +1,111 @@
|
||||
from typing import Any, Sequence
|
||||
from lsprotocol.types import Range
|
||||
from parsimonious import ParseError
|
||||
from dataclasses import dataclass
|
||||
|
||||
from parsimonious.nodes import Node, NodeVisitor
|
||||
|
||||
from .location import Locator
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class BaseToken:
|
||||
range: Range
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class Literal(BaseToken):
|
||||
value: str | float | bool
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class Token(BaseToken):
|
||||
value: str
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class List(BaseToken):
|
||||
value: list[BaseToken]
|
||||
|
||||
|
||||
@dataclass
|
||||
class SkillVisitor(NodeVisitor):
|
||||
locator: Locator
|
||||
|
||||
def visit_skill(self, _: Node, visited_children: Sequence[Any]) -> list[BaseToken]:
|
||||
children = []
|
||||
for childlist in visited_children:
|
||||
for child in childlist:
|
||||
if isinstance(child, BaseToken):
|
||||
children.append(child)
|
||||
|
||||
return children
|
||||
|
||||
def visit_TOKEN(self, node: Node, _: Any) -> Token:
|
||||
return Token(self.locator.locate(node), node.text)
|
||||
|
||||
def visit_LITERAL(self, node: Node, visited_children: list[None | Node]) -> Literal:
|
||||
value, *_ = visited_children
|
||||
if value:
|
||||
match value.expr_name:
|
||||
case "L_t":
|
||||
return Literal(self.locator.locate(node), True)
|
||||
case "L_nil":
|
||||
return Literal(self.locator.locate(node), False)
|
||||
case "L_num":
|
||||
return Literal(self.locator.locate(node), float(value.text))
|
||||
case "L_string":
|
||||
return Literal(self.locator.locate(node), value.text)
|
||||
case _:
|
||||
pass
|
||||
|
||||
raise ParseError("something went wrong during literal parsing")
|
||||
|
||||
def visit_listraw(
|
||||
self, node: Node, visited_children: list[list[list[Any]]]
|
||||
) -> List:
|
||||
rest = visited_children[2]
|
||||
|
||||
children = []
|
||||
|
||||
for child in rest:
|
||||
for part in child:
|
||||
if isinstance(part, BaseToken):
|
||||
children.append(part)
|
||||
|
||||
return List(self.locator.locate(node), children)
|
||||
|
||||
def visit_listc(self, node: Node, visited_children: list[list[list[Any]]]) -> List:
|
||||
rest = ([[visited_children[0]]], visited_children[2])
|
||||
|
||||
children = []
|
||||
|
||||
for child_list in rest:
|
||||
for child in child_list:
|
||||
for part in child:
|
||||
if isinstance(part, BaseToken):
|
||||
children.append(part)
|
||||
|
||||
return List(self.locator.locate(node), children)
|
||||
|
||||
def visit_listskill(
|
||||
self, node: Node, visited_children: list[list[list[Any]]]
|
||||
) -> List:
|
||||
rest = visited_children[1]
|
||||
|
||||
children = []
|
||||
|
||||
for child in rest:
|
||||
for part in child:
|
||||
if isinstance(part, BaseToken):
|
||||
children.append(part)
|
||||
|
||||
return List(self.locator.locate(node), children)
|
||||
|
||||
def visit_inline_assign(self, node: Node, visited_children: Sequence[Any]):
|
||||
print(node)
|
||||
|
||||
def generic_visit(
|
||||
self, node: Node, visited_children: Sequence[Any]
|
||||
) -> Node | Sequence[None | Node]:
|
||||
return visited_children or node
|
||||
Reference in New Issue
Block a user