try rust parser

This commit is contained in:
2025-08-25 22:03:21 +02:00
parent a8bf55882e
commit 5a61161992
5 changed files with 425 additions and 0 deletions
+108
View File
@@ -0,0 +1,108 @@
# tools/tcl_parser_client.py
import asyncio
import json
import uuid
from typing import Optional, Dict, Any
class ParserProcess:
"""
Async wrapper for the NDJSON stdin/stdout parser process.
Start once at server startup; call parse() concurrently.
"""
def __init__(self, cmd):
self.cmd = cmd
self.proc: Optional[asyncio.Process] = None
self._write_lock = asyncio.Lock()
self._pending: Dict[str, asyncio.Future] = {}
self._reader_task: Optional[asyncio.Task] = None
async def start(self):
if self.proc:
return
self.proc = await asyncio.create_subprocess_exec(
*self.cmd,
stdin=asyncio.subprocess.PIPE,
stdout=asyncio.subprocess.PIPE,
stderr=asyncio.subprocess.PIPE,
)
self._reader_task = asyncio.create_task(self._reader_loop())
asyncio.create_task(self._read_stderr())
async def stop(self):
if not self.proc:
return
try:
self.proc.terminate()
except Exception:
pass
await self.proc.wait()
if self._reader_task:
self._reader_task.cancel()
self.proc = None
async def parse(self, tcl: str, timeout: float = 5.0) -> str:
"""
Send `tcl` to the parser and await the S-expression string result.
Raises on parse error or if the parser dies.
"""
if not self.proc:
await self.start()
req_id = uuid.uuid4().hex
payload = json.dumps({"id": req_id, "tcl": tcl}, separators=(",", ":"))
fut = asyncio.get_event_loop().create_future()
self._pending[req_id] = fut
async with self._write_lock:
assert self.proc and self.proc.stdin
self.proc.stdin.write((payload + "\n").encode("utf-8"))
await self.proc.stdin.drain()
try:
result = await asyncio.wait_for(fut, timeout)
return result
finally:
self._pending.pop(req_id, None)
async def _reader_loop(self):
assert self.proc and self.proc.stdout
reader = self.proc.stdout
try:
while True:
line = await reader.readline()
if not line:
# parser process closed
for f in list(self._pending.values()):
if not f.done():
f.set_exception(RuntimeError("parser process terminated"))
break
try:
obj = json.loads(line.decode("utf-8", "replace"))
except Exception as e:
# ignore malformed lines or log
continue
rid = obj.get("id")
fut = self._pending.get(rid)
if fut and not fut.done():
if obj.get("ok"):
fut.set_result(obj.get("result"))
else:
fut.set_exception(RuntimeError(obj.get("error") or "parse error"))
except asyncio.CancelledError:
return
async def _read_stderr(self):
# optional: log parser stderr
assert self.proc and self.proc.stderr
try:
while True:
line = await self.proc.stderr.readline()
if not line:
break
# Replace this with your LSP server logging
print("[tcl_parser stderr]", line.decode("utf-8", "replace").rstrip())
except asyncio.CancelledError:
return