add tcl parser
This commit is contained in:
+15
-12
@@ -13,6 +13,7 @@ import sys
|
|||||||
import sysconfig
|
import sysconfig
|
||||||
import traceback
|
import traceback
|
||||||
from typing import Any, Optional, Sequence
|
from typing import Any, Optional, Sequence
|
||||||
|
from parser.parser import parse_tcl, format_tree
|
||||||
|
|
||||||
|
|
||||||
# **********************************************************
|
# **********************************************************
|
||||||
@@ -179,18 +180,20 @@ def _get_severity(*_codes: list[str]) -> lsp.DiagnosticSeverity:
|
|||||||
@LSP_SERVER.feature(lsp.TEXT_DOCUMENT_FORMATTING)
|
@LSP_SERVER.feature(lsp.TEXT_DOCUMENT_FORMATTING)
|
||||||
def formatting(params: lsp.DocumentFormattingParams) -> list[lsp.TextEdit] | None:
|
def formatting(params: lsp.DocumentFormattingParams) -> list[lsp.TextEdit] | None:
|
||||||
"""LSP handler for textDocument/formatting request."""
|
"""LSP handler for textDocument/formatting request."""
|
||||||
# If your tool is a formatter you can use this handler to provide
|
text = LSP_SERVER.workspace.get_text_document(params.text_document.uri)
|
||||||
# formatting support on save. You have to return an array of lsp.TextEdit
|
tree, _ = parse_tcl(text)
|
||||||
# objects, to provide your formatted results.
|
if not tree:
|
||||||
|
return []
|
||||||
document = LSP_SERVER.workspace.get_document(params.text_document.uri)
|
new_text = format_tree(tree)
|
||||||
edits = _formatting_helper(document)
|
return [
|
||||||
if edits:
|
{
|
||||||
return edits
|
"range": {
|
||||||
|
"start": {"line": 0, "character": 0},
|
||||||
# NOTE: If you provide [] array, VS Code will clear the file of all contents.
|
"end": {"line": len(text.splitlines()), "character": 0},
|
||||||
# To indicate no changes to file return None.
|
},
|
||||||
return None
|
"newText": new_text,
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
def _formatting_helper(document: workspace.Document) -> list[lsp.TextEdit] | None:
|
def _formatting_helper(document: workspace.Document) -> list[lsp.TextEdit] | None:
|
||||||
|
|||||||
+32
-25
@@ -1,32 +1,39 @@
|
|||||||
from lark import Lark, Transformer, Token, v_args
|
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
from lark import Lark, Tree, Token, UnexpectedInput
|
||||||
|
|
||||||
GRAMMAR_FILE = Path(__file__).parent.joinpath("tcl.lark")
|
tcl_parser = Lark.open(
|
||||||
TCL_GRAMMAR = GRAMMAR_FILE.read_text(encoding="utf-8")
|
Path(__file__).parent.joinpath("tcl.lark"), parser="lalr", propagate_positions=True
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
@v_args(inline=True)
|
def parse_tcl(source: str):
|
||||||
class TCLTransformer(Transformer):
|
try:
|
||||||
def start(self, *stmts):
|
tree = tcl_parser.parse(source)
|
||||||
return list(stmts)
|
return tree, []
|
||||||
|
except UnexpectedInput as e:
|
||||||
|
# e.line, e.column enthalten Position
|
||||||
|
diagnostic = {
|
||||||
|
"range": {
|
||||||
|
"start": {"line": e.line - 1, "character": e.column - 1},
|
||||||
|
"end": {"line": e.line - 1, "character": e.column},
|
||||||
|
},
|
||||||
|
"message": f"Syntaxfehler: {e}",
|
||||||
|
"severity": 1, # Error
|
||||||
|
}
|
||||||
|
return None, [diagnostic]
|
||||||
|
|
||||||
def command(self, *elements):
|
|
||||||
return list(elements)
|
|
||||||
|
|
||||||
def braced(self, *content):
|
def format_tree(tree):
|
||||||
return "{" + "".join(c for c in content) + "}"
|
def fmt(node, indent=0):
|
||||||
|
if isinstance(node, Token):
|
||||||
|
return node.value
|
||||||
|
elif isinstance(node, Tree):
|
||||||
|
if node.data == "cmd":
|
||||||
|
parts = []
|
||||||
|
for child in node.children:
|
||||||
|
parts.append(fmt(child, indent))
|
||||||
|
return " ".join(parts) + ";\n"
|
||||||
|
# weitere Node-Typen …
|
||||||
|
return ""
|
||||||
|
|
||||||
def quoted(self, *content):
|
return "".join(fmt(tree))
|
||||||
return '"' + "".join(c for c in content) + '"'
|
|
||||||
|
|
||||||
def variable(self, token):
|
|
||||||
return token.value
|
|
||||||
|
|
||||||
def cmdsubst(self, *inner):
|
|
||||||
return "[" + " ".join(inner) + "]"
|
|
||||||
|
|
||||||
def bare(self, token):
|
|
||||||
return token.value
|
|
||||||
|
|
||||||
def COMMENT(self, token):
|
|
||||||
return token.value
|
|
||||||
|
|||||||
+16
-22
@@ -1,27 +1,21 @@
|
|||||||
start: statement*
|
// tcl.lark
|
||||||
|
// sehr vereinfachtes Beispiel – für volle Abdeckung musst du die offizielle TCL-Spec implementieren
|
||||||
|
|
||||||
?statement: command | COMMENT
|
?start: script
|
||||||
|
|
||||||
COMMENT: /#[^\n]*/
|
script: stmt*
|
||||||
|
stmt: command ";"? -> cmd
|
||||||
|
| newline -> empty
|
||||||
|
|
||||||
command: element+
|
command: WORD arg*
|
||||||
|
arg: WORD -> bareword
|
||||||
|
| STRING -> string
|
||||||
|
| BRACED -> braced
|
||||||
|
|
||||||
?element: braced
|
WORD: /[^\s\{\}";]+/
|
||||||
| quoted
|
STRING: "\"" /([^"\\]|\\.)*/ "\"" // "…"
|
||||||
| variable
|
BRACED: "{" /([^{}\\]|\\.)*/ "}" // {…}
|
||||||
| cmdsubst
|
|
||||||
| bare
|
|
||||||
|
|
||||||
braced: "{" inner_braced* "}"
|
%import common.NEWLINE -> newline
|
||||||
inner_braced: /[^{}]+/ | braced
|
%import common.WS
|
||||||
|
%ignore WS
|
||||||
quoted: '"' inner_quoted* '"'
|
|
||||||
inner_quoted: /[^"{}]+/ | braced
|
|
||||||
|
|
||||||
variable: "$" /[A-Za-z_][A-Za-z0-9_]*/
|
|
||||||
cmdsubst: "[" start "]"
|
|
||||||
|
|
||||||
bare: /[^\s{}$"]+/
|
|
||||||
|
|
||||||
%import common.WS_INLINE
|
|
||||||
%ignore WS_INLINE
|
|
||||||
|
|||||||
Reference in New Issue
Block a user