From bf4e1304f87e5769b9fefa5bfd6c47bf851cd4fc Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Tue, 22 Jan 2019 12:02:17 -0800 Subject: [PATCH] Start work on new `ref` API, for giving names to nodes' children Co-Authored-By: Ayman Nadeem --- cli/src/generate/parse_grammar.rs | 5 +++ cli/src/tests/mod.rs | 1 + cli/src/tests/node_refs.rs | 62 +++++++++++++++++++++++++++++++ lib/binding/bindings.rs | 3 ++ lib/binding/lib.rs | 10 ++++- lib/include/tree_sitter/api.h | 1 + lib/src/node.c | 4 ++ 7 files changed, 85 insertions(+), 1 deletion(-) create mode 100644 cli/src/tests/node_refs.rs diff --git a/cli/src/generate/parse_grammar.rs b/cli/src/generate/parse_grammar.rs index cf2005ad..4f049572 100644 --- a/cli/src/generate/parse_grammar.rs +++ b/cli/src/generate/parse_grammar.rs @@ -26,6 +26,10 @@ enum RuleJSON { CHOICE { members: Vec, }, + REF { + value: String, + content: Box, + }, SEQ { members: Vec, }, @@ -120,6 +124,7 @@ fn parse_rule(json: RuleJSON) -> Rule { RuleJSON::PATTERN { value } => Rule::Pattern(value), RuleJSON::SYMBOL { name } => Rule::NamedSymbol(name), RuleJSON::CHOICE { members } => Rule::choice(members.into_iter().map(parse_rule).collect()), + RuleJSON::REF { content, value } => parse_rule(*content), RuleJSON::SEQ { members } => Rule::seq(members.into_iter().map(parse_rule).collect()), RuleJSON::REPEAT1 { content } => Rule::repeat(parse_rule(*content)), RuleJSON::REPEAT { content } => { diff --git a/cli/src/tests/mod.rs b/cli/src/tests/mod.rs index af2b4582..157f09a8 100644 --- a/cli/src/tests/mod.rs +++ b/cli/src/tests/mod.rs @@ -1,5 +1,6 @@ mod corpus_test; mod helpers; +mod node_refs; mod node_test; mod parser_test; mod properties_test; diff --git a/cli/src/tests/node_refs.rs b/cli/src/tests/node_refs.rs new file mode 100644 index 00000000..143ae7f6 --- /dev/null +++ b/cli/src/tests/node_refs.rs @@ -0,0 +1,62 @@ +use super::helpers::fixtures::get_test_language; +use crate::generate::generate_parser_for_grammar; +use tree_sitter::Parser; + +#[test] +fn test_basic_node_refs() { + let (parser_name, parser_code) = generate_parser_for_grammar( + r#" + { + "name": "test_grammar_with_refs", + "extras": [ + {"type": "PATTERN", "value": "\\s+"} + ], + "rules": { + "rule_a": { + "type": "SEQ", + "members": [ + { + "type": "REF", + "value": "ref_1", + "content": { + "type": "STRING", + "value": "child-1" + } + }, + { + "type": "CHOICE", + "members": [ + { + "type": "STRING", + "value": "child-2" + }, + { + "type": "BLANK" + } + ] + }, + { + "type": "REF", + "value": "ref_2", + "content": { + "type": "STRING", + "value": "child-3" + } + } + ] + } + } + } + "#, + ) + .unwrap(); + + let mut parser = Parser::new(); + let language = get_test_language(&parser_name, &parser_code, None); + parser.set_language(language).unwrap(); + + let tree = parser.parse("child-1 child-2 child-3", None).unwrap(); + let root_node = tree.root_node(); + assert_eq!(root_node.child_by_ref("ref_1"), root_node.child(0)); + assert_eq!(root_node.child_by_ref("ref_2"), root_node.child(2)); +} diff --git a/lib/binding/bindings.rs b/lib/binding/bindings.rs index 9d1f3490..3d71f804 100644 --- a/lib/binding/bindings.rs +++ b/lib/binding/bindings.rs @@ -227,6 +227,9 @@ extern "C" { extern "C" { pub fn ts_node_child(arg1: TSNode, arg2: u32) -> TSNode; } +extern "C" { + pub fn ts_node_child_by_ref(arg1: TSNode, arg2: *const ::std::os::raw::c_char) -> TSNode; +} extern "C" { pub fn ts_node_named_child(arg1: TSNode, arg2: u32) -> TSNode; } diff --git a/lib/binding/lib.rs b/lib/binding/lib.rs index 66444a55..841f5895 100644 --- a/lib/binding/lib.rs +++ b/lib/binding/lib.rs @@ -12,7 +12,7 @@ use std::os::unix::io::AsRawFd; use regex::Regex; use serde::de::DeserializeOwned; use std::collections::HashMap; -use std::ffi::CStr; +use std::ffi::{CStr, CString}; use std::fmt; use std::marker::PhantomData; use std::os::raw::{c_char, c_void}; @@ -463,6 +463,14 @@ impl<'tree> Node<'tree> { Self::new(unsafe { ffi::ts_node_child(self.0, i as u32) }) } + pub fn child_by_ref(&self, ref_name: &str) -> Option { + if let Ok(c_ref_name) = CString::new(ref_name) { + Self::new(unsafe { ffi::ts_node_child_by_ref(self.0, c_ref_name.as_ptr()) }) + } else { + None + } + } + pub fn child_count(&self) -> usize { unsafe { ffi::ts_node_child_count(self.0) as usize } } diff --git a/lib/include/tree_sitter/api.h b/lib/include/tree_sitter/api.h index 16841c8e..1fa105cd 100644 --- a/lib/include/tree_sitter/api.h +++ b/lib/include/tree_sitter/api.h @@ -119,6 +119,7 @@ bool ts_node_has_changes(TSNode); bool ts_node_has_error(TSNode); TSNode ts_node_parent(TSNode); TSNode ts_node_child(TSNode, uint32_t); +TSNode ts_node_child_by_ref(TSNode, const char *); TSNode ts_node_named_child(TSNode, uint32_t); uint32_t ts_node_child_count(TSNode); uint32_t ts_node_named_child_count(TSNode); diff --git a/lib/src/node.c b/lib/src/node.c index eb4a3121..081ac803 100644 --- a/lib/src/node.c +++ b/lib/src/node.c @@ -453,6 +453,10 @@ TSNode ts_node_named_child(TSNode self, uint32_t child_index) { return ts_node__child(self, child_index, false); } +TSNode ts_node_child_by_ref(TSNode self, const char *ref_name) { + return ts_node__null(); +} + uint32_t ts_node_child_count(TSNode self) { Subtree tree = ts_node__subtree(self); if (ts_subtree_child_count(tree) > 0) {