Skip to content

Commit 9e62ac3

Browse files
Catherine Gasniermeta-codesync[bot]
authored andcommitted
Add CLI tool for parsing SCIP symbols to JSON
Summary: Add a new command-line tool `scip-symbol-parse` to parse SCIP symbol strings and output structured JSON. This enables easier debugging and inspection of SCIP symbols by providing a simple interface to the existing `scip_symbol` parsing library. The tool supports: - Parsing symbols from command-line arguments or stdin - Optional pretty-printing for human-readable output - Batch processing via stdin mode The existing `scip_symbol` library was enhanced with serde serialization support to enable JSON output, and the `Method` descriptor variant was converted from tuple-style to named fields for clearer serialization. Differential Revision: D87650280 fbshipit-source-id: 5b0b91b31ef5f3861cc1a4ffa42bcaf83b33b2ec
1 parent 6160c18 commit 9e62ac3

5 files changed

Lines changed: 344 additions & 16 deletions

File tree

glean/lang/scip/indexer/scip_to_glean/cli/src/lsif.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ impl SymbolKind {
4949
Namespace => SkPackage,
5050
Type => SkClass,
5151
Term => SkVariable,
52-
Method(_) => SkMethod,
52+
Method { .. } => SkMethod,
5353
TypeParameter => SkTypeParameter,
5454
Parameter => SkField,
5555
Meta => SkUnknown,

glean/lang/scip/indexer/scip_to_glean/scip_symbol/src/lib.rs

Lines changed: 38 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,10 @@
1717
//! - Parses all descriptor types (namespace, type, term, method, parameter, etc.)
1818
//! - Supports the "." placeholder for empty package fields
1919
20+
use serde::Serialize;
21+
22+
#[derive(Debug, Clone, PartialEq, Serialize)]
23+
#[serde(tag = "type", rename_all = "lowercase")]
2024
#[allow(dead_code)]
2125
pub enum ScipSymbol {
2226
Local {
@@ -29,29 +33,31 @@ pub enum ScipSymbol {
2933
},
3034
}
3135

36+
#[derive(Debug, Clone, PartialEq, Serialize)]
3237
#[allow(dead_code)]
3338
pub struct Package {
3439
pub manager: Option<String>,
3540
pub name: Option<String>,
3641
pub version: Option<String>,
3742
}
3843

39-
#[derive(Debug, Clone, PartialEq)]
44+
#[derive(Debug, Clone, PartialEq, Serialize)]
4045
pub struct Descriptor {
4146
pub name: String,
4247
pub kind: DescriptorKind,
4348
}
4449

45-
#[derive(Debug, Clone, PartialEq)]
50+
#[derive(Debug, Clone, PartialEq, Serialize)]
51+
#[serde(tag = "kind", rename_all = "lowercase")]
4652
pub enum DescriptorKind {
47-
Namespace, // name/
48-
Type, // name#
49-
Term, // name.
50-
Method(Option<String>), // name(<disambiguator>?).
51-
TypeParameter, // [name]
52-
Parameter, // (name)
53-
Meta, // name:
54-
Macro, // name!
53+
Namespace,
54+
Type,
55+
Term,
56+
Method { disambiguator: Option<String> },
57+
TypeParameter,
58+
Parameter,
59+
Meta,
60+
Macro,
5561
}
5662

5763
/// Parses a SCIP symbol string according to the SCIP specification.
@@ -296,7 +302,7 @@ fn parse_method(descriptors_str: &str, name: String, start_pos: usize) -> (Descr
296302
(
297303
Descriptor {
298304
name,
299-
kind: DescriptorKind::Method(disambiguator),
305+
kind: DescriptorKind::Method { disambiguator },
300306
},
301307
i,
302308
)
@@ -468,7 +474,12 @@ mod tests {
468474
assert_eq!(descriptors[0].name, "MyClass");
469475
assert_eq!(descriptors[0].kind, DescriptorKind::Type);
470476
assert_eq!(descriptors[1].name, "myMethod");
471-
assert_eq!(descriptors[1].kind, DescriptorKind::Method(None));
477+
assert_eq!(
478+
descriptors[1].kind,
479+
DescriptorKind::Method {
480+
disambiguator: None
481+
}
482+
);
472483
}
473484

474485
#[test]
@@ -496,7 +507,12 @@ mod tests {
496507

497508
assert_eq!(descriptors.len(), 2);
498509
assert_eq!(descriptors[0].name, "func");
499-
assert_eq!(descriptors[0].kind, DescriptorKind::Method(None));
510+
assert_eq!(
511+
descriptors[0].kind,
512+
DescriptorKind::Method {
513+
disambiguator: None
514+
}
515+
);
500516
assert_eq!(descriptors[1].name, "param");
501517
assert_eq!(descriptors[1].kind, DescriptorKind::Parameter);
502518
}
@@ -597,7 +613,12 @@ mod tests {
597613
assert_eq!(descriptors[0].name, "MyClass");
598614
assert_eq!(descriptors[0].kind, DescriptorKind::Type);
599615
assert_eq!(descriptors[1].name, "special method");
600-
assert_eq!(descriptors[1].kind, DescriptorKind::Method(None));
616+
assert_eq!(
617+
descriptors[1].kind,
618+
DescriptorKind::Method {
619+
disambiguator: None
620+
}
621+
);
601622
}
602623

603624
#[test]
@@ -615,7 +636,9 @@ mod tests {
615636
assert_eq!(descriptors[1].name, "myMethod");
616637
assert_eq!(
617638
descriptors[1].kind,
618-
DescriptorKind::Method(Some("disambig".to_string()))
639+
DescriptorKind::Method {
640+
disambiguator: Some("disambig".to_string())
641+
}
619642
);
620643
}
621644

Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
# scip-symbol-parse
2+
3+
A CLI tool for parsing SCIP symbols and outputting them as JSON.
4+
5+
## Overview
6+
7+
This tool parses SCIP (SCIP Code Intelligence Protocol) symbols according to the specification and outputs the parsed structure as JSON. It uses the `scip_symbol` library for parsing logic.
8+
9+
## Usage
10+
11+
### Parse a single symbol
12+
13+
```bash
14+
scip-symbol-parse "rust-analyzer cargo std v1.0 io/IsTerminal#"
15+
```
16+
17+
### Parse from stdin
18+
19+
```bash
20+
echo "local myVar" | scip-symbol-parse --stdin
21+
```
22+
23+
### Pretty-print JSON output
24+
25+
```bash
26+
scip-symbol-parse --pretty "rust-analyzer cargo std v1.0 io/IsTerminal#"
27+
```
28+
29+
### Process multiple symbols from a file
30+
31+
```bash
32+
cat symbols.txt | scip-symbol-parse --stdin
33+
```
34+
35+
## Options
36+
37+
- `<SYMBOL>` - The SCIP symbol to parse (positional argument)
38+
- `--stdin, -s` - Read symbols from stdin (one per line)
39+
- `--pretty, -p` - Pretty-print JSON output
40+
41+
## Examples
42+
43+
### Global symbol
44+
45+
```bash
46+
$ scip-symbol-parse "rust-analyzer cargo std v1.0 io/IsTerminal#"
47+
{"type":"global","scheme":"rust-analyzer","package":{"manager":"cargo","name":"std","version":"v1.0"},"descriptors":[{"name":"io","kind":"namespace"},{"name":"IsTerminal","kind":"type"}]}
48+
```
49+
50+
### Local symbol
51+
52+
```bash
53+
$ scip-symbol-parse "local myVar"
54+
{"type":"local","id":"myVar"}
55+
```
56+
57+
### Pretty-printed output
58+
59+
```bash
60+
$ scip-symbol-parse --pretty "scip . . . MyClass#myMethod()."
61+
{
62+
"type": "global",
63+
"scheme": "scip",
64+
"package": {
65+
"manager": null,
66+
"name": null,
67+
"version": null
68+
},
69+
"descriptors": [
70+
{
71+
"name": "MyClass",
72+
"kind": "type"
73+
},
74+
{
75+
"name": "myMethod",
76+
"kind": {
77+
"kind": "method",
78+
"disambiguator": null
79+
}
80+
}
81+
]
82+
}
83+
```
84+
85+
## Building
86+
87+
```bash
88+
buck2 build //fbcode/glean/lang/scip/indexer/scip_to_glean/scip_symbol_parse:scip-symbol-parse
89+
```
90+
91+
## Testing
92+
93+
Run the test suite directly:
94+
95+
```bash
96+
./test.sh
97+
```
98+
99+
Or run via Buck:
100+
101+
```bash
102+
buck2 test //fbcode/glean/lang/scip/indexer/scip_to_glean/scip_symbol_parse:test
103+
```
104+
105+
Each test validates the complete JSON output to ensure correctness.
106+
107+
Requirements:
108+
- `buck2` (for building)
109+
110+
## SCIP Symbol Format
111+
112+
This is documented in scip.proto: https://github.com/sourcegraph/scip/blob/main/scip.proto#L147-L188
Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
/*
2+
* Copyright (c) Meta Platforms, Inc. and affiliates.
3+
* All rights reserved.
4+
*
5+
* This source code is licensed under the BSD-style license found in the
6+
* LICENSE file in the root directory of this source tree.
7+
*/
8+
9+
//! CLI tool for parsing SCIP symbols and outputting them as JSON.
10+
//!
11+
//! This tool takes SCIP symbol strings as input and parses them using the
12+
//! scip_symbol library, then outputs the parsed structure as JSON.
13+
//!
14+
//! Usage:
15+
//! scip-symbol-parse <symbol>
16+
//! scip-symbol-parse --stdin
17+
//!
18+
//! Examples:
19+
//! scip-symbol-parse "rust-analyzer cargo std v1.0 io/IsTerminal#"
20+
//! echo "local myVar" | scip-symbol-parse --stdin
21+
22+
use std::io::BufRead;
23+
use std::io::{self};
24+
25+
use anyhow::Result;
26+
use clap::Parser;
27+
use scip_symbol::parse_scip_symbol;
28+
29+
#[derive(Parser, Debug)]
30+
#[clap(name = "scip-symbol-parse")]
31+
#[clap(about = "Parse SCIP symbols and output as JSON", long_about = None)]
32+
struct Args {
33+
/// SCIP symbol to parse
34+
#[clap(value_name = "SYMBOL")]
35+
symbol: Option<String>,
36+
37+
/// Read symbols from stdin (one per line)
38+
#[clap(short, long)]
39+
stdin: bool,
40+
41+
/// Pretty-print JSON output
42+
#[clap(short, long)]
43+
pretty: bool,
44+
}
45+
46+
fn main() -> Result<()> {
47+
let args = Args::parse();
48+
49+
if args.stdin {
50+
// Read from stdin, one symbol per line
51+
let stdin = io::stdin();
52+
for line in stdin.lock().lines() {
53+
let line = line?;
54+
let line = line.trim();
55+
if line.is_empty() {
56+
continue;
57+
}
58+
parse_and_output(line, args.pretty)?;
59+
}
60+
} else if let Some(symbol) = &args.symbol {
61+
// Parse single symbol from command line argument
62+
parse_and_output(symbol, args.pretty)?;
63+
} else {
64+
eprintln!("Error: Either provide a symbol as an argument or use --stdin");
65+
std::process::exit(1);
66+
}
67+
68+
Ok(())
69+
}
70+
71+
fn parse_and_output(symbol: &str, pretty: bool) -> Result<()> {
72+
let parsed = parse_scip_symbol(symbol);
73+
74+
let json = if pretty {
75+
serde_json::to_string_pretty(&parsed)?
76+
} else {
77+
serde_json::to_string(&parsed)?
78+
};
79+
80+
println!("{}", json);
81+
Ok(())
82+
}

0 commit comments

Comments
 (0)