Skip to content

Commit 359d7f8

Browse files
authored
feat: Engine (#120)
1 parent af1cb96 commit 359d7f8

39 files changed

Lines changed: 3976 additions & 1263 deletions

AGENTS.md

Lines changed: 50 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -157,7 +157,7 @@ Boolean = [
157157
```
158158
crates/
159159
plotnik-cli/ # CLI tool
160-
src/commands/ # Subcommands (debug, docs, langs)
160+
src/commands/ # Subcommands (debug, docs, exec, langs, types)
161161
plotnik-core/ # Common code
162162
plotnik-lib/ # Plotnik as library
163163
src/
@@ -177,20 +177,68 @@ Run: `cargo run -p plotnik-cli -- <command>`
177177

178178
- `debug` — Inspect queries and source file ASTs
179179
- Example: `cargo run -p plotnik-cli -- debug -q '(foo) @bar'`
180+
- `exec` — Execute query against source, output JSON
181+
- Example: `cargo run -p plotnik-cli -- exec -q '(identifier) @id' -s app.js`
182+
- `types` — Generate TypeScript type definitions from query
183+
- Example: `cargo run -p plotnik-cli -- types -q '(identifier) @id' -l javascript`
180184
- `langs` — List supported languages
181185

182186
Inputs: `-q/--query <Q>`, `--query-file <F>`, `--source <S>`, `-s/--source-file <F>`, `-l/--lang <L>`
183187

184-
Output (inferred from input): `--only-symbols`, `--cst`, `--raw`, `--spans`, `--cardinalities`
188+
### `debug` output flags
189+
190+
- `--only-symbols` — Show only symbol table (requires query)
191+
- `--cst` — Show query CST instead of AST
192+
- `--raw` — Include trivia tokens (whitespace, comments)
193+
- `--spans` — Show source spans
194+
- `--cardinalities` — Show inferred cardinalities
195+
- `--graph` — Show compiled transition graph
196+
- `--graph-raw` — Show unoptimized graph (before epsilon elimination)
197+
- `--types` — Show inferred types
185198

186199
```sh
187200
cargo run -p plotnik-cli -- debug -q '(identifier) @id'
188201
cargo run -p plotnik-cli -- debug -q '(identifier) @id' --only-symbols
202+
cargo run -p plotnik-cli -- debug -q '(identifier) @id' --graph -l javascript
203+
cargo run -p plotnik-cli -- debug -q '(identifier) @id' --types -l javascript
189204
cargo run -p plotnik-cli -- debug -s app.ts
190205
cargo run -p plotnik-cli -- debug -s app.ts --raw
191206
cargo run -p plotnik-cli -- debug -q '(function_declaration) @fn' -s app.ts -l typescript
192207
```
193208

209+
### `exec` output flags
210+
211+
- `--pretty` — Pretty-print JSON output
212+
- `--verbose-nodes` — Include line/column positions in nodes
213+
- `--check` — Validate output against inferred types
214+
- `--entry <NAME>` — Entry point name (definition to match from)
215+
216+
```sh
217+
cargo run -p plotnik-cli -- exec -q '(program (expression_statement (identifier) @name))' --source 'x' -l javascript
218+
cargo run -p plotnik-cli -- exec -q '(identifier) @id' -s app.js --pretty
219+
cargo run -p plotnik-cli -- exec -q '(function_declaration) @fn' -s app.ts -l typescript --verbose-nodes
220+
cargo run -p plotnik-cli -- exec -q '(identifier) @id' -s app.js --check
221+
cargo run -p plotnik-cli -- exec -q '(identifier) @id' -s app.js --verbose-nodes --pretty
222+
cargo run -p plotnik-cli -- exec -q 'A = (identifier) @id B = (string) @str' -s app.js --entry B
223+
```
224+
225+
### `types` output flags
226+
227+
- `--format <FORMAT>` — Output format: `typescript` or `ts` (default: typescript)
228+
- `--root-type <NAME>` — Name for root type of anonymous expressions (default: Query)
229+
- `--verbose-nodes` — Use verbose Node shape (matches `exec --verbose-nodes`)
230+
- `--no-node-type` — Don't emit Node/Point type definitions
231+
- `--no-export` — Don't add `export` keyword to types
232+
- `-o/--output <FILE>` — Write output to file instead of stdout
233+
234+
```sh
235+
cargo run -p plotnik-cli -- types -q '(identifier) @id' -l javascript
236+
cargo run -p plotnik-cli -- types -q 'Func = (function_declaration name: (identifier) @name body: (statement_block) @body)' -l js
237+
cargo run -p plotnik-cli -- types -q '(identifier) @id' -l javascript --verbose-nodes
238+
cargo run -p plotnik-cli -- types -q '(identifier) @id' -l javascript --no-node-type
239+
cargo run -p plotnik-cli -- types -q '(identifier) @id' -l javascript -o types.d.ts
240+
```
241+
194242
# Coding rules
195243

196244
- Avoid nesting logic: prefer early exit in functions (return) and loops (continue/break)

Cargo.lock

Lines changed: 2 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

crates/plotnik-cli/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,4 +69,5 @@ yaml = ["plotnik-langs/yaml"]
6969
clap = { version = "4.5", features = ["derive"] }
7070
plotnik-langs = { version = "0.1.0", path = "../plotnik-langs", default-features = false }
7171
plotnik-lib = { version = "0.1.0", path = "../plotnik-lib" }
72+
serde_json = "1.0"
7273
tree-sitter = "0.26"

crates/plotnik-cli/src/cli.rs

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,95 @@ pub enum Command {
6060

6161
/// List supported languages
6262
Langs,
63+
64+
/// Execute a query against source code and output JSON
65+
#[command(after_help = r#"EXAMPLES:
66+
plotnik exec -q '(identifier) @id' -s app.js
67+
plotnik exec -q '(identifier) @id' -s app.js --pretty
68+
plotnik exec -q '(function_declaration) @fn' -s app.ts -l typescript --verbose-nodes
69+
plotnik exec -q '(identifier) @id' -s app.js --check
70+
plotnik exec --query-file query.plnk -s app.js --entry FunctionDef"#)]
71+
Exec {
72+
#[command(flatten)]
73+
query: QueryArgs,
74+
75+
#[command(flatten)]
76+
source: SourceArgs,
77+
78+
/// Language for source (required for inline text, inferred from extension otherwise)
79+
#[arg(long, short = 'l', value_name = "LANG")]
80+
lang: Option<String>,
81+
82+
#[command(flatten)]
83+
output: ExecOutputArgs,
84+
},
85+
86+
/// Generate type definitions from a query
87+
#[command(after_help = r#"EXAMPLES:
88+
plotnik types -q '(identifier) @id' -l javascript
89+
plotnik types --query-file query.plnk -l typescript
90+
plotnik types -q '(function_declaration) @fn' -l js --format ts
91+
plotnik types -q '(identifier) @id' -l js --verbose-nodes
92+
plotnik types -q '(identifier) @id' -l js -o types.d.ts
93+
94+
NOTE: Use --verbose-nodes to match `exec --verbose-nodes` output shape."#)]
95+
Types {
96+
#[command(flatten)]
97+
query: QueryArgs,
98+
99+
/// Target language (required)
100+
#[arg(long, short = 'l', value_name = "LANG")]
101+
lang: Option<String>,
102+
103+
#[command(flatten)]
104+
output: TypesOutputArgs,
105+
},
106+
}
107+
108+
#[derive(Args)]
109+
pub struct ExecOutputArgs {
110+
/// Pretty-print JSON output
111+
#[arg(long)]
112+
pub pretty: bool,
113+
114+
/// Include verbose node information (line/column positions)
115+
#[arg(long)]
116+
pub verbose_nodes: bool,
117+
118+
/// Validate output against inferred types
119+
#[arg(long)]
120+
pub check: bool,
121+
122+
/// Entry point name (definition to match from)
123+
#[arg(long, value_name = "NAME")]
124+
pub entry: Option<String>,
125+
}
126+
127+
#[derive(Args)]
128+
pub struct TypesOutputArgs {
129+
/// Output format (typescript, ts)
130+
#[arg(long, default_value = "typescript", value_name = "FORMAT")]
131+
pub format: String,
132+
133+
/// Name for the root type (for anonymous expressions)
134+
#[arg(long, default_value = "Query", value_name = "NAME")]
135+
pub root_type: String,
136+
137+
/// Use verbose node shape (matches exec --verbose-nodes)
138+
#[arg(long)]
139+
pub verbose_nodes: bool,
140+
141+
/// Don't emit Node/Point type definitions
142+
#[arg(long)]
143+
pub no_node_type: bool,
144+
145+
/// Don't export types
146+
#[arg(long)]
147+
pub no_export: bool,
148+
149+
/// Write output to file
150+
#[arg(short = 'o', long, value_name = "FILE")]
151+
pub output: Option<PathBuf>,
63152
}
64153

65154
#[derive(Args)]

crates/plotnik-cli/src/commands/debug/mod.rs

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
mod source;
1+
pub mod source;
22

33
use std::fs;
44
use std::io::{self, Read};
@@ -86,7 +86,13 @@ pub fn run(args: DebugArgs) {
8686
if (args.graph || args.graph_raw || args.types)
8787
&& let Some(q) = query.take()
8888
{
89-
let (q, pre_opt_dump) = q.build_graph_with_pre_opt_dump();
89+
// Determine root kind for auto-wrapping
90+
let root_kind = args.lang.as_ref().and_then(|lang_name| {
91+
let lang = resolve_lang_for_link(&Some(lang_name.clone()));
92+
lang.root().and_then(|root_id| lang.node_type_name(root_id))
93+
});
94+
95+
let (q, pre_opt_dump) = q.build_graph_with_pre_opt_dump(root_kind);
9096
let mut needs_separator = false;
9197
if args.graph_raw {
9298
if show_both_graphs {

crates/plotnik-cli/src/commands/debug/source.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,10 @@ pub fn load_source(text: &Option<String>, file: &Option<PathBuf>) -> String {
1616
.expect("failed to read stdin");
1717
return buf;
1818
}
19-
return fs::read_to_string(path).expect("failed to read source file");
19+
return fs::read_to_string(path).unwrap_or_else(|_| {
20+
eprintln!("error: file not found: {}", path.display());
21+
std::process::exit(1);
22+
});
2023
}
2124
unreachable!()
2225
}

0 commit comments

Comments
 (0)