try rust parser
This commit is contained in:
@@ -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
|
||||
Reference in New Issue
Block a user