add tcl parser

This commit is contained in:
2025-07-18 22:49:49 +02:00
parent e2ba07c8b4
commit e045e1533c
3 changed files with 63 additions and 59 deletions
+15 -12
View File
@@ -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
View File
@@ -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
View File
@@ -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