Skip to content

Commit

Permalink
feat(Rust parser): Initial iteration of a parser for Rust
Browse files Browse the repository at this point in the history
  • Loading branch information
nokome committed Dec 7, 2021
1 parent d4c744c commit abcf8f3
Show file tree
Hide file tree
Showing 11 changed files with 386 additions and 0 deletions.
22 changes: 22 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

10 changes: 10 additions & 0 deletions fixtures/fragments/rust/assigns.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
const var1: u16 = 1;
static var2: &str = "2";

let var3 = true;
let var4 = false;
let var5 = 42;
let var6 = 3.14;
let var7 = "string";
let var8 = vec![1, 2];
let var9: Vec<u16> = Vec::new();
2 changes: 2 additions & 0 deletions fixtures/fragments/rust/uses.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
var1;
var2 + var3;
13 changes: 13 additions & 0 deletions rust/parser-rust/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
[package]
name = "parser-rust"
version = "0.0.0"
edition = "2021"

[dependencies]
once_cell = "1.8.0"
parser-treesitter = { path = "../parser-treesitter", version = "0.0.0" }
tree-sitter-rust = { version = "0.20.0" }

[dev-dependencies]
test-snaps = { path = "../test-snaps", version = "0.0.0" }
test-utils = { path = "../test-utils", version = "0.0.0" }
95 changes: 95 additions & 0 deletions rust/parser-rust/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
use once_cell::sync::Lazy;
use parser_treesitter::{
apply_tags,
eyre::Result,
graph_triples::{relations, resources, Pairs},
Parser, ParserTrait, TreesitterParser,
};
use std::path::Path;

/// Tree-sitter based parser for Rust
static PARSER: Lazy<TreesitterParser> =
Lazy::new(|| TreesitterParser::new(tree_sitter_rust::language(), QUERY));

/// Tree-sitter AST query
const QUERY: &str = include_str!("query.txt");

/// A parser for Rust
pub struct RustParser {}

impl ParserTrait for RustParser {
fn spec() -> Parser {
Parser {
language: "rust".to_string(),
}
}

fn parse(path: &Path, code: &str) -> Result<Pairs> {
let code = code.as_bytes();
let tree = PARSER.parse(code);
let matches = PARSER.query(code, &tree);

let relations = matches
.iter()
.filter_map(|(pattern, captures)| match pattern {
0 | 1 | 2 => {
// Assigns a variable
let range = captures[0].range;
let name = captures[0].text.clone();
Some((
relations::assigns(range),
resources::symbol(path, &name, ""),
))
}
3 => {
// Uses a variable
let node = captures[0].node;
let range = captures[0].range;
let symbol = captures[0].text.clone();

if let Some(parent_node) = node.parent() {
let kind = parent_node.kind();
if
// Skip names of `static` and `const`s
((kind == "const_item" || kind == "static_item")
&& Some(node) == parent_node.child_by_field_name("name"))
// Skip names of `let`s
|| (kind == "let_declaration"
&& Some(node) == parent_node.child_by_field_name("pattern"))
// Skip any part of a scoped identifier e.g. Vec::new
|| kind == "scoped_identifier"
// Skip names of macros
|| kind == "macro_invocation"
&& Some(node) == parent_node.child_by_field_name("macro")
{
return None;
}
}

Some((relations::uses(range), resources::symbol(path, &symbol, "")))
}
_ => None,
})
.collect();

let pairs = apply_tags(path, &Self::spec().language, matches, 0, relations);
Ok(pairs)
}
}

#[cfg(test)]
mod tests {
use super::*;
use test_snaps::{insta::assert_json_snapshot, snapshot_fixtures};
use test_utils::fixtures;

#[test]
fn parse_rust_fragments() {
snapshot_fixtures("fragments/rust/*.rs", |path| {
let code = std::fs::read_to_string(path).expect("Unable to read");
let path = path.strip_prefix(fixtures()).expect("Unable to strip");
let pairs = RustParser::parse(path, &code).expect("Unable to parse");
assert_json_snapshot!(pairs);
})
}
}
18 changes: 18 additions & 0 deletions rust/parser-rust/src/query.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
(source_file
(const_item
name: (identifier) @name
)
)
(source_file
(static_item
name: (identifier) @name
)
)
(source_file
(let_declaration
pattern: (identifier) @name
)
)


((identifier) @name)
161 changes: 161 additions & 0 deletions rust/parser-rust/src/snapshots/parse_rust_fragments@assigns.rs.snap
Original file line number Diff line number Diff line change
@@ -0,0 +1,161 @@
---
source: rust/parser-rust/src/lib.rs
expression: pairs
input_file: fixtures/fragments/rust/assigns.rs

---
[
[
{
"type": "Assign",
"range": [
0,
6,
0,
10
]
},
{
"type": "Symbol",
"path": "fragments/rust/assigns.rs",
"name": "var1",
"kind": ""
}
],
[
{
"type": "Assign",
"range": [
1,
7,
1,
11
]
},
{
"type": "Symbol",
"path": "fragments/rust/assigns.rs",
"name": "var2",
"kind": ""
}
],
[
{
"type": "Assign",
"range": [
3,
4,
3,
8
]
},
{
"type": "Symbol",
"path": "fragments/rust/assigns.rs",
"name": "var3",
"kind": ""
}
],
[
{
"type": "Assign",
"range": [
4,
4,
4,
8
]
},
{
"type": "Symbol",
"path": "fragments/rust/assigns.rs",
"name": "var4",
"kind": ""
}
],
[
{
"type": "Assign",
"range": [
5,
4,
5,
8
]
},
{
"type": "Symbol",
"path": "fragments/rust/assigns.rs",
"name": "var5",
"kind": ""
}
],
[
{
"type": "Assign",
"range": [
6,
4,
6,
8
]
},
{
"type": "Symbol",
"path": "fragments/rust/assigns.rs",
"name": "var6",
"kind": ""
}
],
[
{
"type": "Assign",
"range": [
7,
4,
7,
8
]
},
{
"type": "Symbol",
"path": "fragments/rust/assigns.rs",
"name": "var7",
"kind": ""
}
],
[
{
"type": "Assign",
"range": [
8,
4,
8,
8
]
},
{
"type": "Symbol",
"path": "fragments/rust/assigns.rs",
"name": "var8",
"kind": ""
}
],
[
{
"type": "Assign",
"range": [
9,
4,
9,
8
]
},
{
"type": "Symbol",
"path": "fragments/rust/assigns.rs",
"name": "var9",
"kind": ""
}
]
]

0 comments on commit abcf8f3

Please sign in to comment.