Compare commits
2
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d60d3ec942 | ||
|
|
e52722e353 |
@@ -19,8 +19,9 @@ A comprehensive VS Code extension providing language support for NX CAM postproc
|
|||||||
## Installation
|
## Installation
|
||||||
|
|
||||||
1. Install from the VS Code Marketplace
|
1. Install from the VS Code Marketplace
|
||||||
2. Open any `.cdl`, `.tcl`, or `.def` file
|
2. Install Python 3.8 or higher
|
||||||
3. The extension will automatically activate and provide language support
|
3. Open any `.cdl`, `.tcl`, or `.def` file
|
||||||
|
4. The extension will automatically activate and provide language support
|
||||||
|
|
||||||
## Configuration
|
## Configuration
|
||||||
|
|
||||||
|
|||||||
@@ -5,6 +5,7 @@
|
|||||||
"version": "2025.9.100",
|
"version": "2025.9.100",
|
||||||
"publisher": "Christoph",
|
"publisher": "Christoph",
|
||||||
"icon": "images/nx-1.png",
|
"icon": "images/nx-1.png",
|
||||||
|
"extensionDependencies": ["ms-python.python"],
|
||||||
"serverInfo": {
|
"serverInfo": {
|
||||||
"name": "NX Postprocessor Support",
|
"name": "NX Postprocessor Support",
|
||||||
"module": "nx-post-support"
|
"module": "nx-post-support"
|
||||||
|
|||||||
@@ -1,108 +0,0 @@
|
|||||||
# 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
|
|
||||||
Generated
-216
@@ -1,216 +0,0 @@
|
|||||||
# This file is automatically @generated by Cargo.
|
|
||||||
# It is not intended for manual editing.
|
|
||||||
version = 4
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "aho-corasick"
|
|
||||||
version = "1.1.3"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916"
|
|
||||||
dependencies = [
|
|
||||||
"memchr",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "anyhow"
|
|
||||||
version = "1.0.99"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "b0674a1ddeecb70197781e945de4b3b8ffb61fa939a5597bcf48503737663100"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "cc"
|
|
||||||
version = "1.2.34"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "42bc4aea80032b7bf409b0bc7ccad88853858911b7713a8062fdc0623867bedc"
|
|
||||||
dependencies = [
|
|
||||||
"shlex",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "equivalent"
|
|
||||||
version = "1.0.2"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "hashbrown"
|
|
||||||
version = "0.15.5"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "indexmap"
|
|
||||||
version = "2.11.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "f2481980430f9f78649238835720ddccc57e52df14ffce1c6f37391d61b563e9"
|
|
||||||
dependencies = [
|
|
||||||
"equivalent",
|
|
||||||
"hashbrown",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "itoa"
|
|
||||||
version = "1.0.15"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "memchr"
|
|
||||||
version = "2.7.5"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "32a282da65faaf38286cf3be983213fcf1d2e2a58700e808f83f4ea9a4804bc0"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "proc-macro2"
|
|
||||||
version = "1.0.101"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "89ae43fd86e4158d6db51ad8e2b80f313af9cc74f5c0e03ccb87de09998732de"
|
|
||||||
dependencies = [
|
|
||||||
"unicode-ident",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "quote"
|
|
||||||
version = "1.0.40"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d"
|
|
||||||
dependencies = [
|
|
||||||
"proc-macro2",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "regex"
|
|
||||||
version = "1.11.2"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "23d7fd106d8c02486a8d64e778353d1cffe08ce79ac2e82f540c86d0facf6912"
|
|
||||||
dependencies = [
|
|
||||||
"aho-corasick",
|
|
||||||
"memchr",
|
|
||||||
"regex-automata",
|
|
||||||
"regex-syntax",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "regex-automata"
|
|
||||||
version = "0.4.10"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "6b9458fa0bfeeac22b5ca447c63aaf45f28439a709ccd244698632f9aa6394d6"
|
|
||||||
dependencies = [
|
|
||||||
"aho-corasick",
|
|
||||||
"memchr",
|
|
||||||
"regex-syntax",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "regex-syntax"
|
|
||||||
version = "0.8.6"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "caf4aa5b0f434c91fe5c7f1ecb6a5ece2130b02ad2a590589dda5146df959001"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "ryu"
|
|
||||||
version = "1.0.20"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "serde"
|
|
||||||
version = "1.0.219"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "5f0e2c6ed6606019b4e29e69dbaba95b11854410e5347d525002456dbbb786b6"
|
|
||||||
dependencies = [
|
|
||||||
"serde_derive",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "serde_derive"
|
|
||||||
version = "1.0.219"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00"
|
|
||||||
dependencies = [
|
|
||||||
"proc-macro2",
|
|
||||||
"quote",
|
|
||||||
"syn",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "serde_json"
|
|
||||||
version = "1.0.143"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "d401abef1d108fbd9cbaebc3e46611f4b1021f714a0597a71f41ee463f5f4a5a"
|
|
||||||
dependencies = [
|
|
||||||
"indexmap",
|
|
||||||
"itoa",
|
|
||||||
"memchr",
|
|
||||||
"ryu",
|
|
||||||
"serde",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "shlex"
|
|
||||||
version = "1.3.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "streaming-iterator"
|
|
||||||
version = "0.1.9"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "2b2231b7c3057d5e4ad0156fb3dc807d900806020c5ffa3ee6ff2c8c76fb8520"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "syn"
|
|
||||||
version = "2.0.106"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "ede7c438028d4436d71104916910f5bb611972c5cfd7f89b8300a8186e6fada6"
|
|
||||||
dependencies = [
|
|
||||||
"proc-macro2",
|
|
||||||
"quote",
|
|
||||||
"unicode-ident",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "tcl_parser"
|
|
||||||
version = "0.1.0"
|
|
||||||
dependencies = [
|
|
||||||
"anyhow",
|
|
||||||
"serde",
|
|
||||||
"serde_json",
|
|
||||||
"tree-sitter",
|
|
||||||
"tree-sitter-tcl",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "tree-sitter"
|
|
||||||
version = "0.25.8"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "6d7b8994f367f16e6fa14b5aebbcb350de5d7cbea82dc5b00ae997dd71680dd2"
|
|
||||||
dependencies = [
|
|
||||||
"cc",
|
|
||||||
"regex",
|
|
||||||
"regex-syntax",
|
|
||||||
"serde_json",
|
|
||||||
"streaming-iterator",
|
|
||||||
"tree-sitter-language",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "tree-sitter-language"
|
|
||||||
version = "0.1.5"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "c4013970217383f67b18aef68f6fb2e8d409bc5755227092d32efb0422ba24b8"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "tree-sitter-tcl"
|
|
||||||
version = "1.1.0"
|
|
||||||
source = "git+https://github.com/tree-sitter-grammars/tree-sitter-tcl#8f11ac7206a54ed11210491cee1e0657e2962c47"
|
|
||||||
dependencies = [
|
|
||||||
"cc",
|
|
||||||
"tree-sitter-language",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "unicode-ident"
|
|
||||||
version = "1.0.18"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512"
|
|
||||||
@@ -1,16 +0,0 @@
|
|||||||
[package]
|
|
||||||
name = "tcl_parser"
|
|
||||||
version = "0.1.0"
|
|
||||||
edition = "2024"
|
|
||||||
|
|
||||||
[dependencies]
|
|
||||||
tree-sitter = "0.25.8"
|
|
||||||
tree-sitter-tcl = { git = "https://github.com/tree-sitter-grammars/tree-sitter-tcl" }
|
|
||||||
serde = { version = "1.0", features = ["derive"] }
|
|
||||||
serde_json = "1.0"
|
|
||||||
anyhow = "1.0"
|
|
||||||
|
|
||||||
[lib]
|
|
||||||
name = "tcl_parser"
|
|
||||||
path = "src/lib.rs"
|
|
||||||
|
|
||||||
@@ -1,26 +0,0 @@
|
|||||||
use tree_sitter::Parser;
|
|
||||||
|
|
||||||
pub fn parse_to_sexp(src: &str) -> Result<String, anyhow::Error> {
|
|
||||||
let lang = tree_sitter_tcl::LANGUAGE;
|
|
||||||
let mut parser = Parser::new();
|
|
||||||
parser.set_language(&lang.into())?;
|
|
||||||
let tree = parser
|
|
||||||
.parse(src.as_bytes(), None)
|
|
||||||
.ok_or_else(|| anyhow::anyhow!("failed to parse input"))?;
|
|
||||||
Ok(tree.root_node().to_sexp())
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod tests {
|
|
||||||
use super::*;
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn parses_sample() {
|
|
||||||
let src = r#"proc ProcName {arg1 arg2 {arg3 3}} {
|
|
||||||
# Comment
|
|
||||||
set var1 100
|
|
||||||
}"#;
|
|
||||||
let sexp = parse_to_sexp(src).expect("parse");
|
|
||||||
assert!(sexp.contains("proc"));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,59 +0,0 @@
|
|||||||
use std::io::{ self, BufRead };
|
|
||||||
use serde::{ Deserialize, Serialize };
|
|
||||||
|
|
||||||
fn main() -> anyhow::Result<()> {
|
|
||||||
println!(
|
|
||||||
"tcl_parser server starting (NDJSON stdin). Send JSON lines with {{'id':..., 'tcl': '...'}}"
|
|
||||||
);
|
|
||||||
let stdin = io::stdin();
|
|
||||||
for line in stdin.lock().lines() {
|
|
||||||
let line = line?;
|
|
||||||
if line.trim().is_empty() {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
let req: Request = match serde_json::from_str(&line) {
|
|
||||||
Ok(r) => r,
|
|
||||||
Err(e) => {
|
|
||||||
let resp = Response::error(None, format!("invalid request json: {}", e));
|
|
||||||
println!("{}", serde_json::to_string(&resp)?);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
let id = req.id.clone();
|
|
||||||
match tcl_parser::parse_to_sexp(&req.tcl) {
|
|
||||||
Ok(sexp) => {
|
|
||||||
let resp = Response::ok(id, sexp);
|
|
||||||
println!("{}", serde_json::to_string(&resp)?);
|
|
||||||
}
|
|
||||||
Err(e) => {
|
|
||||||
let resp = Response::error(id, format!("parse error: {}", e));
|
|
||||||
println!("{}", serde_json::to_string(&resp)?);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Deserialize)]
|
|
||||||
struct Request {
|
|
||||||
id: Option<String>,
|
|
||||||
tcl: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Serialize)]
|
|
||||||
struct Response {
|
|
||||||
id: Option<String>,
|
|
||||||
ok: bool,
|
|
||||||
result: Option<String>,
|
|
||||||
error: Option<String>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Response {
|
|
||||||
fn ok(id: Option<String>, result: String) -> Self {
|
|
||||||
Self { id, ok: true, result: Some(result), error: None }
|
|
||||||
}
|
|
||||||
fn error(id: Option<String>, err: String) -> Self {
|
|
||||||
Self { id, ok: false, result: None, error: Some(err) }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Reference in New Issue
Block a user