From 072e8c781b505c2a7ac021380fb1c1b72c53f74e Mon Sep 17 00:00:00 2001 From: Joao Duarte Date: Mon, 6 Nov 2023 19:30:33 -0300 Subject: [PATCH 01/10] feat: return matchings --- matching/src/matchings.rs | 20 ++++++++++++++++++-- matching/src/ordered_tree_matching.rs | 10 +++++----- 2 files changed, 23 insertions(+), 7 deletions(-) diff --git a/matching/src/matchings.rs b/matching/src/matchings.rs index 43043aa..db98739 100644 --- a/matching/src/matchings.rs +++ b/matching/src/matchings.rs @@ -8,7 +8,7 @@ use crate::matching_entry::MatchingEntry; #[derive(Debug, Clone)] pub struct Matchings<'a> { - matching_entries: HashMap>, MatchingEntry>, + pub matching_entries: HashMap>, MatchingEntry>, } impl<'a> Matchings<'a> { @@ -41,7 +41,7 @@ impl<'a> Matchings<'a> { left: &'a CSTNode<'a>, right: &'a CSTNode<'a>, ) -> Option<&MatchingEntry> { - self.matching_entries.get(&UnorderedPair(left, right)) + self.matching_entries.get(&UnorderedPair::new(left, right)) } pub fn has_bidirectional_matching( @@ -53,6 +53,22 @@ impl<'a> Matchings<'a> { } } +impl Default for Matchings<'_> { + fn default() -> Self { + Self { matching_entries: Default::default() } + } +} + +impl<'a> IntoIterator for Matchings<'a> { + type Item = (UnorderedPair<&'a CSTNode<'a>>, MatchingEntry); + + type IntoIter = std::collections::hash_map::IntoIter>, MatchingEntry>; + + fn into_iter(self) -> Self::IntoIter { + self.matching_entries.into_iter() + } +} + #[cfg(test)] mod tests { use super::*; diff --git a/matching/src/ordered_tree_matching.rs b/matching/src/ordered_tree_matching.rs index dedc667..2bfbbbf 100644 --- a/matching/src/ordered_tree_matching.rs +++ b/matching/src/ordered_tree_matching.rs @@ -13,7 +13,7 @@ enum Direction { #[derive(Clone)] struct Entry<'a>( pub Direction, - pub HashMap>, MatchingEntry>, + pub Matchings<'a>, ); impl<'a> Default for Entry<'a> { @@ -54,8 +54,8 @@ fn ordered_tree_matching_helper<'a>( let left_child = children_left.get(i - 1).unwrap(); let right_child = children_right.get(j - 1).unwrap(); - let w = ordered_tree_matching_helper(left_child, right_child); - let matching = w.get(&UnorderedPair::new(left_child, right_child)).unwrap(); + let w = ordered_tree_matching(left_child, right_child); + let matching = w.get_matching_entry(left_child, right_child).unwrap(); if matrix_m[i][j - 1] > matrix_m[i - 1][j] { if matrix_m[i][j - 1] > matrix_m[i - 1][j - 1] + matching.score { @@ -79,7 +79,7 @@ fn ordered_tree_matching_helper<'a>( let mut i = m; let mut j = n; - let mut children = Vec::<&HashMap, MatchingEntry>>::new(); + let mut children = Vec::<&'a Matchings<'a>>::new(); while i >= 1 && j >= 1 { match matrix_t.get(i).unwrap().get(j).unwrap().0 { @@ -99,7 +99,7 @@ fn ordered_tree_matching_helper<'a>( let mut result = HashMap::new(); result.insert(UnorderedPair::new(left, right), matching); children.into_iter().for_each(|child_matchings| { - child_matchings.iter().for_each(|(key, matching)| { + child_matchings.clone().into_iter().for_each(|(key, matching)| { result.insert(key.to_owned(), matching.to_owned()); }) }); From 4590dbbddc1589b88af715817aca104b694a8909 Mon Sep 17 00:00:00 2001 From: Joao Duarte Date: Mon, 6 Nov 2023 19:32:40 -0300 Subject: [PATCH 02/10] refactor: Remove helper function --- matching/src/ordered_tree_matching.rs | 19 ++++++------------- 1 file changed, 6 insertions(+), 13 deletions(-) diff --git a/matching/src/ordered_tree_matching.rs b/matching/src/ordered_tree_matching.rs index 2bfbbbf..d7dbb5d 100644 --- a/matching/src/ordered_tree_matching.rs +++ b/matching/src/ordered_tree_matching.rs @@ -23,13 +23,6 @@ impl<'a> Default for Entry<'a> { } pub fn ordered_tree_matching<'a>(left: &'a CSTNode, right: &'a CSTNode) -> Matchings<'a> { - return Matchings::new(ordered_tree_matching_helper(left, right)); -} - -fn ordered_tree_matching_helper<'a>( - left: &'a CSTNode, - right: &'a CSTNode, -) -> HashMap>, MatchingEntry> { match (left, right) { ( CSTNode::NonTerminal { @@ -79,7 +72,7 @@ fn ordered_tree_matching_helper<'a>( let mut i = m; let mut j = n; - let mut children = Vec::<&'a Matchings<'a>>::new(); + let mut children = Vec::>::new(); while i >= 1 && j >= 1 { match matrix_t.get(i).unwrap().get(j).unwrap().0 { @@ -87,7 +80,7 @@ fn ordered_tree_matching_helper<'a>( Direction::LEFT => j = j - 1, Direction::DIAG => { if matrix_m[i][j] > matrix_m[i - 1][j - 1] { - children.push(&matrix_t[i][j].1); + children.push(matrix_t[i][j].1.clone()); } i = i - 1; j = j - 1; @@ -99,11 +92,11 @@ fn ordered_tree_matching_helper<'a>( let mut result = HashMap::new(); result.insert(UnorderedPair::new(left, right), matching); children.into_iter().for_each(|child_matchings| { - child_matchings.clone().into_iter().for_each(|(key, matching)| { + child_matchings.into_iter().for_each(|(key, matching)| { result.insert(key.to_owned(), matching.to_owned()); }) }); - result + Matchings::new(result) } ( CSTNode::Terminal { @@ -121,7 +114,7 @@ fn ordered_tree_matching_helper<'a>( UnorderedPair::new(left, right), MatchingEntry::new(is_perfetch_match.into(), is_perfetch_match), ); - result + Matchings::new(result) } (_, _) => { let mut result = HashMap::new(); @@ -129,7 +122,7 @@ fn ordered_tree_matching_helper<'a>( UnorderedPair::new(left, right), MatchingEntry::new(0, false), ); - result + Matchings::new(result) } } } From fe21a3e82dd6eb18fb1d663e653a6d58cdbda91d Mon Sep 17 00:00:00 2001 From: Joao Duarte Date: Mon, 6 Nov 2023 19:33:00 -0300 Subject: [PATCH 03/10] refactor: call top level --- matching/src/ordered_tree_matching.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/matching/src/ordered_tree_matching.rs b/matching/src/ordered_tree_matching.rs index d7dbb5d..8eaf4ce 100644 --- a/matching/src/ordered_tree_matching.rs +++ b/matching/src/ordered_tree_matching.rs @@ -1,4 +1,4 @@ -use crate::{matching_entry::MatchingEntry, Matchings}; +use crate::{matching_entry::MatchingEntry, Matchings, calculate_matchings}; use model::CSTNode; use std::collections::HashMap; use utils::unordered_pair::UnorderedPair; @@ -47,7 +47,7 @@ pub fn ordered_tree_matching<'a>(left: &'a CSTNode, right: &'a CSTNode) -> Match let left_child = children_left.get(i - 1).unwrap(); let right_child = children_right.get(j - 1).unwrap(); - let w = ordered_tree_matching(left_child, right_child); + let w = calculate_matchings(left_child, right_child); let matching = w.get_matching_entry(left_child, right_child).unwrap(); if matrix_m[i][j - 1] > matrix_m[i - 1][j] { From abb629974f130a82f05e7fd7b37475054df2f20a Mon Sep 17 00:00:00 2001 From: Joao Duarte Date: Mon, 6 Nov 2023 19:48:55 -0300 Subject: [PATCH 04/10] refactor: Simplify matching code --- matching/src/matchings.rs | 19 +++++++++-- matching/src/ordered_tree_matching.rs | 46 ++++++++++++--------------- 2 files changed, 37 insertions(+), 28 deletions(-) diff --git a/matching/src/matchings.rs b/matching/src/matchings.rs index db98739..669d466 100644 --- a/matching/src/matchings.rs +++ b/matching/src/matchings.rs @@ -18,6 +18,12 @@ impl<'a> Matchings<'a> { } } + pub fn from_single(key: UnorderedPair<&'a CSTNode>, value: MatchingEntry) -> Self { + let mut matching_entries = HashMap::new(); + matching_entries.insert(key, value); + Matchings { matching_entries } + } + pub fn new(matching_entries: HashMap>, MatchingEntry>) -> Self { Matchings { matching_entries } } @@ -51,18 +57,27 @@ impl<'a> Matchings<'a> { ) -> bool { self.find_matching_for(left).is_some() && self.find_matching_for(right).is_some() } + + pub fn push(&mut self, key: UnorderedPair<&'a CSTNode>, value: MatchingEntry) { + self.matching_entries.insert(key, value); + } + + pub fn extend(&mut self, matchings: HashMap>, MatchingEntry>) { + self.matching_entries.extend(matchings); + } } impl Default for Matchings<'_> { fn default() -> Self { - Self { matching_entries: Default::default() } + Self::empty() } } impl<'a> IntoIterator for Matchings<'a> { type Item = (UnorderedPair<&'a CSTNode<'a>>, MatchingEntry); - type IntoIter = std::collections::hash_map::IntoIter>, MatchingEntry>; + type IntoIter = + std::collections::hash_map::IntoIter>, MatchingEntry>; fn into_iter(self) -> Self::IntoIter { self.matching_entries.into_iter() diff --git a/matching/src/ordered_tree_matching.rs b/matching/src/ordered_tree_matching.rs index 8eaf4ce..b454065 100644 --- a/matching/src/ordered_tree_matching.rs +++ b/matching/src/ordered_tree_matching.rs @@ -1,4 +1,4 @@ -use crate::{matching_entry::MatchingEntry, Matchings, calculate_matchings}; +use crate::{calculate_matchings, matching_entry::MatchingEntry, Matchings}; use model::CSTNode; use std::collections::HashMap; use utils::unordered_pair::UnorderedPair; @@ -11,10 +11,7 @@ enum Direction { } #[derive(Clone)] -struct Entry<'a>( - pub Direction, - pub Matchings<'a>, -); +struct Entry<'a>(pub Direction, pub Matchings<'a>); impl<'a> Default for Entry<'a> { fn default() -> Self { @@ -88,15 +85,18 @@ pub fn ordered_tree_matching<'a>(left: &'a CSTNode, right: &'a CSTNode) -> Match } } - let matching = MatchingEntry::new(matrix_m[m][n] + root_matching, left == right); - let mut result = HashMap::new(); - result.insert(UnorderedPair::new(left, right), matching); - children.into_iter().for_each(|child_matchings| { - child_matchings.into_iter().for_each(|(key, matching)| { - result.insert(key.to_owned(), matching.to_owned()); - }) - }); - Matchings::new(result) + let mut matchings = Matchings::from_single( + UnorderedPair::new(left, right), + MatchingEntry::new(matrix_m[m][n] + root_matching, left == right), + ); + matchings.extend(children.into_iter().fold( + HashMap::new(), + |mut acc, child_matchings| { + acc.extend(child_matchings); + acc + }, + )); + matchings } ( CSTNode::Terminal { @@ -108,22 +108,16 @@ pub fn ordered_tree_matching<'a>(left: &'a CSTNode, right: &'a CSTNode) -> Match value: value_right, }, ) => { - let mut result = HashMap::new(); let is_perfetch_match = kind_left == kind_right && value_left == value_right; - result.insert( + Matchings::from_single( UnorderedPair::new(left, right), MatchingEntry::new(is_perfetch_match.into(), is_perfetch_match), - ); - Matchings::new(result) - } - (_, _) => { - let mut result = HashMap::new(); - result.insert( - UnorderedPair::new(left, right), - MatchingEntry::new(0, false), - ); - Matchings::new(result) + ) } + (_, _) => Matchings::from_single( + UnorderedPair::new(left, right), + MatchingEntry::new(0, false), + ), } } From fd3c522cb3a020251ffe9199943c1ad1b292f5ab Mon Sep 17 00:00:00 2001 From: Joao Duarte Date: Mon, 6 Nov 2023 20:04:44 -0300 Subject: [PATCH 05/10] refactor: simplify extends code --- matching/src/matchings.rs | 2 +- matching/src/ordered_tree_matching.rs | 20 ++++++-------------- 2 files changed, 7 insertions(+), 15 deletions(-) diff --git a/matching/src/matchings.rs b/matching/src/matchings.rs index 669d466..2aad909 100644 --- a/matching/src/matchings.rs +++ b/matching/src/matchings.rs @@ -62,7 +62,7 @@ impl<'a> Matchings<'a> { self.matching_entries.insert(key, value); } - pub fn extend(&mut self, matchings: HashMap>, MatchingEntry>) { + pub fn extend(&mut self, matchings: Matchings<'a>) { self.matching_entries.extend(matchings); } } diff --git a/matching/src/ordered_tree_matching.rs b/matching/src/ordered_tree_matching.rs index b454065..6fc4ed5 100644 --- a/matching/src/ordered_tree_matching.rs +++ b/matching/src/ordered_tree_matching.rs @@ -1,6 +1,5 @@ use crate::{calculate_matchings, matching_entry::MatchingEntry, Matchings}; use model::CSTNode; -use std::collections::HashMap; use utils::unordered_pair::UnorderedPair; #[derive(PartialEq, Eq, Debug, Clone)] @@ -69,7 +68,11 @@ pub fn ordered_tree_matching<'a>(left: &'a CSTNode, right: &'a CSTNode) -> Match let mut i = m; let mut j = n; - let mut children = Vec::>::new(); + + let mut matchings = Matchings::from_single( + UnorderedPair::new(left, right), + MatchingEntry::new(matrix_m[m][n] + root_matching, left == right), + ); while i >= 1 && j >= 1 { match matrix_t.get(i).unwrap().get(j).unwrap().0 { @@ -77,7 +80,7 @@ pub fn ordered_tree_matching<'a>(left: &'a CSTNode, right: &'a CSTNode) -> Match Direction::LEFT => j = j - 1, Direction::DIAG => { if matrix_m[i][j] > matrix_m[i - 1][j - 1] { - children.push(matrix_t[i][j].1.clone()); + matchings.extend(matrix_t[i][j].1.clone()); } i = i - 1; j = j - 1; @@ -85,17 +88,6 @@ pub fn ordered_tree_matching<'a>(left: &'a CSTNode, right: &'a CSTNode) -> Match } } - let mut matchings = Matchings::from_single( - UnorderedPair::new(left, right), - MatchingEntry::new(matrix_m[m][n] + root_matching, left == right), - ); - matchings.extend(children.into_iter().fold( - HashMap::new(), - |mut acc, child_matchings| { - acc.extend(child_matchings); - acc - }, - )); matchings } ( From cb1fadbc203bd6e7d9943c55294bf2f4dd158168 Mon Sep 17 00:00:00 2001 From: Joao Duarte Date: Mon, 6 Nov 2023 20:06:40 -0300 Subject: [PATCH 06/10] refactor: remove unused method --- matching/src/matchings.rs | 4 ---- 1 file changed, 4 deletions(-) diff --git a/matching/src/matchings.rs b/matching/src/matchings.rs index 2aad909..aeb8954 100644 --- a/matching/src/matchings.rs +++ b/matching/src/matchings.rs @@ -58,10 +58,6 @@ impl<'a> Matchings<'a> { self.find_matching_for(left).is_some() && self.find_matching_for(right).is_some() } - pub fn push(&mut self, key: UnorderedPair<&'a CSTNode>, value: MatchingEntry) { - self.matching_entries.insert(key, value); - } - pub fn extend(&mut self, matchings: Matchings<'a>) { self.matching_entries.extend(matchings); } From 80c257f7606d9682da274cddaa4c969708e7a328 Mon Sep 17 00:00:00 2001 From: Joao Duarte Date: Mon, 6 Nov 2023 20:53:32 -0300 Subject: [PATCH 07/10] feat: Draft implementation for unordered matching --- bin/src/main.rs | 58 ++++++++----- matching/src/lib.rs | 7 +- matching/src/matching_entry.rs | 9 ++ matching/src/ordered_tree_matching.rs | 4 +- matching/src/unordered_tree_matching.rs | 108 ++++++++++++++++++++++++ model/src/cst_node.rs | 10 +++ 6 files changed, 175 insertions(+), 21 deletions(-) create mode 100644 matching/src/unordered_tree_matching.rs diff --git a/bin/src/main.rs b/bin/src/main.rs index dcfa77f..bee4691 100644 --- a/bin/src/main.rs +++ b/bin/src/main.rs @@ -1,32 +1,52 @@ fn main() { let base = r#" - public class Main { - public static void main(String[] args) { - System.out.println("Hello, world!"); - int y = 4; - int j = 0; - } + public interface Repository { } "#; - let left = r#" - public class Main { - public static void main(String[] args) { - int x = 0; - System.out.println("Hello, João!"); - int y = 3; - int j = 0; - } + let left: &str = r#" + public interface Repository { + void create(Pessoa pessoa); + void delete(Pessoa pessoa); } "#; + let right = r#" - public class Main { - public static void main(String[] args) { - System.out.println("Hello, Paulo!"); - int y = 3; - } + public interface Repository { + void delete(Pessoa pessoa); + void remove(Pessoa pessoa); + void insert(Pessoa pessoa); + void create(Pessoa pessoa); } "#; + // let base = r#" + // public class Main { + // public static void main(String[] args) { + // System.out.println("Hello, world!"); + // int y = 4; + // int j = 0; + // } + // } + // "#; + // let left = r#" + // public class Main { + // public static void main(String[] args) { + // int x = 0; + // System.out.println("Hello, João!"); + // int y = 3; + // int j = 0; + // } + // } + // "#; + // let right = r#" + // public class Main { + // public static void main(String[] args) { + // System.out.println("Hello, Paulo!"); + // int y = 3; + // } + // } + // "#; + let parser_configuration = parsing::ParserConfiguration::from_language(model::Language::Java); let base_tree = parsing::parse_string(base, &parser_configuration).unwrap(); diff --git a/matching/src/lib.rs b/matching/src/lib.rs index 9d7b851..53468d4 100644 --- a/matching/src/lib.rs +++ b/matching/src/lib.rs @@ -2,6 +2,7 @@ mod matching; mod matching_entry; mod matchings; mod ordered_tree_matching; +mod unordered_tree_matching; pub use matching_entry::MatchingEntry; pub use matchings::Matchings; @@ -11,5 +12,9 @@ pub fn calculate_matchings<'a>( left: &'a model::CSTNode, right: &'a model::CSTNode, ) -> Matchings<'a> { - return ordered_tree_matching::ordered_tree_matching(left, right); + if left.can_be_matching_unordered() && right.can_be_matching_unordered() { + unordered_tree_matching::unordered_tree_matching(left, right) + } else { + ordered_tree_matching::ordered_tree_matching(left, right) + } } diff --git a/matching/src/matching_entry.rs b/matching/src/matching_entry.rs index d3424c8..e1fdc34 100644 --- a/matching/src/matching_entry.rs +++ b/matching/src/matching_entry.rs @@ -12,3 +12,12 @@ impl MatchingEntry { }; } } + +impl Default for &MatchingEntry { + fn default() -> Self { + &MatchingEntry { + score: 0, + is_perfect_match: false, + } + } +} diff --git a/matching/src/ordered_tree_matching.rs b/matching/src/ordered_tree_matching.rs index 6fc4ed5..f213b79 100644 --- a/matching/src/ordered_tree_matching.rs +++ b/matching/src/ordered_tree_matching.rs @@ -44,7 +44,9 @@ pub fn ordered_tree_matching<'a>(left: &'a CSTNode, right: &'a CSTNode) -> Match let right_child = children_right.get(j - 1).unwrap(); let w = calculate_matchings(left_child, right_child); - let matching = w.get_matching_entry(left_child, right_child).unwrap(); + let matching = w + .get_matching_entry(left_child, right_child) + .unwrap_or_default(); if matrix_m[i][j - 1] > matrix_m[i - 1][j] { if matrix_m[i][j - 1] > matrix_m[i - 1][j - 1] + matching.score { diff --git a/matching/src/unordered_tree_matching.rs b/matching/src/unordered_tree_matching.rs new file mode 100644 index 0000000..a9fb371 --- /dev/null +++ b/matching/src/unordered_tree_matching.rs @@ -0,0 +1,108 @@ +use model::CSTNode; +use utils::unordered_pair::UnorderedPair; + +use crate::{MatchingEntry, Matchings, calculate_matchings}; + +pub fn unordered_tree_matching<'a>(left: &'a CSTNode, right: &'a CSTNode) -> crate::Matchings<'a> { + match (left, right) { + ( + CSTNode::Terminal { + kind: kind_left, + value: value_left, + }, + CSTNode::Terminal { + kind: kind_right, + value: value_right, + }, + ) => { + let is_perfetch_match = kind_left == kind_right && value_left == value_right; + Matchings::from_single( + UnorderedPair::new(left, right), + MatchingEntry::new(is_perfetch_match.into(), is_perfetch_match), + ) + } + ( + CSTNode::NonTerminal { + kind: kind_left, + children: children_left, + }, + CSTNode::NonTerminal { + kind: kind_right, + children: children_right, + }, + ) => { + let root_matching: usize = (left == right).into(); + + let mut children_matchings: Vec> = vec![]; + let mut sum = 0; + + for child_left in children_left { + for child_right in children_right { + let matching_score = compute_matching_score(&child_left, &child_right); + + if matching_score == 1 { + let child_matching = calculate_matchings(child_left, child_right); + if let Some(matching) = child_matching.get_matching_entry(child_left, child_right) { + sum += matching.score; + } + children_matchings.push(child_matching); + } + } + } + + let mut result = Matchings::from_single( + UnorderedPair(left, right), + MatchingEntry { + score: sum + root_matching, + is_perfect_match: false, + }, + ); + + children_matchings.iter().for_each(|matchings| { + result.extend(matchings.to_owned()) + }); + + result + } + (_, _) => panic!("Invalid configuration reached"), + } +} + +fn compute_matching_score<'a>(left: &'a CSTNode, right: &'a CSTNode) -> usize { + match (left, right) { + ( + CSTNode::Terminal { + kind: kind_left, + value: value_left, + }, + CSTNode::Terminal { + kind: kind_right, + value: value_right, + }, + ) => (kind_left == kind_right && value_left == value_right).into(), + ( + CSTNode::NonTerminal { + children: children_left, + .. + }, + CSTNode::NonTerminal { + children: children_right, + .. + }, + ) => { + // Try to find an identifier on children, and compare them + let identifier_left = children_left.iter().find(|node| match node { + CSTNode::Terminal { kind, .. } => kind == &"identifier", + _ => false, + }); + + let identifier_right = children_right.iter().find(|node| match node { + CSTNode::Terminal { kind, .. } => kind == &"identifier", + _ => false, + }); + + (identifier_left == identifier_right).into() + } + (_, _) => 0, + } +} diff --git a/model/src/cst_node.rs b/model/src/cst_node.rs index 9aadebe..b051564 100644 --- a/model/src/cst_node.rs +++ b/model/src/cst_node.rs @@ -14,6 +14,16 @@ pub enum CSTNode<'a> { }, } +impl CSTNode<'_> { + pub fn can_be_matching_unordered(&self) -> bool { + match self { + CSTNode::Terminal { .. } => false, + CSTNode::NonTerminal { kind, .. } => vec!["interface_body"].contains(kind), + CSTNode::Conflict { .. } => false, + } + } +} + impl ToString for CSTNode<'_> { fn to_string(&self) -> String { match self { From ac57fa1b6b1ced12759df05c6bc75aac980750e4 Mon Sep 17 00:00:00 2001 From: Joao Duarte Date: Mon, 6 Nov 2023 22:52:56 -0300 Subject: [PATCH 08/10] refactor: Simplify code --- matching/src/unordered_tree_matching.rs | 24 +++++++++++------------- 1 file changed, 11 insertions(+), 13 deletions(-) diff --git a/matching/src/unordered_tree_matching.rs b/matching/src/unordered_tree_matching.rs index a9fb371..0aa6d88 100644 --- a/matching/src/unordered_tree_matching.rs +++ b/matching/src/unordered_tree_matching.rs @@ -1,7 +1,7 @@ use model::CSTNode; use utils::unordered_pair::UnorderedPair; -use crate::{MatchingEntry, Matchings, calculate_matchings}; +use crate::{calculate_matchings, MatchingEntry, Matchings}; pub fn unordered_tree_matching<'a>(left: &'a CSTNode, right: &'a CSTNode) -> crate::Matchings<'a> { match (left, right) { @@ -23,44 +23,42 @@ pub fn unordered_tree_matching<'a>(left: &'a CSTNode, right: &'a CSTNode) -> cra } ( CSTNode::NonTerminal { - kind: kind_left, children: children_left, + .. }, CSTNode::NonTerminal { - kind: kind_right, children: children_right, + .. }, ) => { let root_matching: usize = (left == right).into(); - let mut children_matchings: Vec> = vec![]; let mut sum = 0; + let mut result = Matchings::empty(); for child_left in children_left { for child_right in children_right { - let matching_score = compute_matching_score(&child_left, &child_right); + let matching_score = compute_matching_score(&child_left, &child_right); if matching_score == 1 { let child_matching = calculate_matchings(child_left, child_right); - if let Some(matching) = child_matching.get_matching_entry(child_left, child_right) { + if let Some(matching) = + child_matching.get_matching_entry(child_left, child_right) + { sum += matching.score; } - children_matchings.push(child_matching); + result.extend(child_matching); } } } - let mut result = Matchings::from_single( + result.extend(Matchings::from_single( UnorderedPair(left, right), MatchingEntry { score: sum + root_matching, is_perfect_match: false, }, - ); - - children_matchings.iter().for_each(|matchings| { - result.extend(matchings.to_owned()) - }); + )); result } From f0809ee3878267d5fd0c9d8fc7bb7611d1b2a3b0 Mon Sep 17 00:00:00 2001 From: Joao Duarte Date: Tue, 7 Nov 2023 08:41:31 -0300 Subject: [PATCH 09/10] refactor: simplify code --- matching/src/unordered_tree_matching.rs | 21 +++++---------------- 1 file changed, 5 insertions(+), 16 deletions(-) diff --git a/matching/src/unordered_tree_matching.rs b/matching/src/unordered_tree_matching.rs index 0aa6d88..e721b30 100644 --- a/matching/src/unordered_tree_matching.rs +++ b/matching/src/unordered_tree_matching.rs @@ -42,11 +42,9 @@ pub fn unordered_tree_matching<'a>(left: &'a CSTNode, right: &'a CSTNode) -> cra if matching_score == 1 { let child_matching = calculate_matchings(child_left, child_right); - if let Some(matching) = - child_matching.get_matching_entry(child_left, child_right) - { - sum += matching.score; - } + sum += child_matching + .get_matching_entry(child_left, child_right) + .map_or(0, |matching| matching.score); result.extend(child_matching); } } @@ -56,7 +54,7 @@ pub fn unordered_tree_matching<'a>(left: &'a CSTNode, right: &'a CSTNode) -> cra UnorderedPair(left, right), MatchingEntry { score: sum + root_matching, - is_perfect_match: false, + is_perfect_match: left == right, }, )); @@ -68,16 +66,7 @@ pub fn unordered_tree_matching<'a>(left: &'a CSTNode, right: &'a CSTNode) -> cra fn compute_matching_score<'a>(left: &'a CSTNode, right: &'a CSTNode) -> usize { match (left, right) { - ( - CSTNode::Terminal { - kind: kind_left, - value: value_left, - }, - CSTNode::Terminal { - kind: kind_right, - value: value_right, - }, - ) => (kind_left == kind_right && value_left == value_right).into(), + (CSTNode::Terminal { .. }, CSTNode::Terminal { .. }) => (left == right).into(), ( CSTNode::NonTerminal { children: children_left, From 411671dd4ddac00f0940b1446ba961c8b0ca5077 Mon Sep 17 00:00:00 2001 From: Joao Duarte Date: Wed, 8 Nov 2023 22:45:48 -0300 Subject: [PATCH 10/10] feat: Consider node position in Tree Matching --- Cargo.lock | 3 + bin/src/main.rs | 2 +- matching/src/matchings.rs | 6 + matching/src/ordered_tree_matching.rs | 58 +++- matching/src/unordered_tree_matching.rs | 2 + merge/src/odered_merge.rs | 373 +++++++++++++++++++++++- model/Cargo.toml | 1 + model/src/cst_node.rs | 4 + model/src/lib.rs | 2 + parsing/src/parse.rs | 181 ++++++++---- 10 files changed, 570 insertions(+), 62 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 4d4565f..3fb050a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -64,6 +64,9 @@ dependencies = [ [[package]] name = "model" version = "0.1.0" +dependencies = [ + "tree-sitter", +] [[package]] name = "nu-ansi-term" diff --git a/bin/src/main.rs b/bin/src/main.rs index bee4691..1428b6c 100644 --- a/bin/src/main.rs +++ b/bin/src/main.rs @@ -12,10 +12,10 @@ fn main() { let right = r#" public interface Repository { + void create(Pessoa pessoa); void delete(Pessoa pessoa); void remove(Pessoa pessoa); void insert(Pessoa pessoa); - void create(Pessoa pessoa); } "#; diff --git a/matching/src/matchings.rs b/matching/src/matchings.rs index aeb8954..6b7b363 100644 --- a/matching/src/matchings.rs +++ b/matching/src/matchings.rs @@ -82,6 +82,8 @@ impl<'a> IntoIterator for Matchings<'a> { #[cfg(test)] mod tests { + use model::Point; + use super::*; #[test] @@ -89,6 +91,8 @@ mod tests { let a_node = CSTNode::Terminal { kind: "kind", value: "value".into(), + start_position: Point { row: 0, column: 0 }, + end_position: Point { row: 0, column: 5 }, }; assert_eq!(None, Matchings::empty().find_matching_for(&a_node)) @@ -99,6 +103,8 @@ mod tests { let a_node = CSTNode::Terminal { kind: "kind", value: "value".into(), + start_position: Point { row: 0, column: 0 }, + end_position: Point { row: 0, column: 5 }, }; let mut matchings = HashMap::new(); diff --git a/matching/src/ordered_tree_matching.rs b/matching/src/ordered_tree_matching.rs index f213b79..3f0247e 100644 --- a/matching/src/ordered_tree_matching.rs +++ b/matching/src/ordered_tree_matching.rs @@ -24,10 +24,12 @@ pub fn ordered_tree_matching<'a>(left: &'a CSTNode, right: &'a CSTNode) -> Match CSTNode::NonTerminal { kind: kind_left, children: children_left, + .. }, CSTNode::NonTerminal { kind: kind_right, children: children_right, + .. }, ) => { let root_matching: usize = (kind_left == kind_right).into(); @@ -96,10 +98,12 @@ pub fn ordered_tree_matching<'a>(left: &'a CSTNode, right: &'a CSTNode) -> Match CSTNode::Terminal { kind: kind_left, value: value_left, + .. }, CSTNode::Terminal { kind: kind_right, value: value_right, + .. }, ) => { let is_perfetch_match = kind_left == kind_right && value_left == value_right; @@ -118,17 +122,21 @@ pub fn ordered_tree_matching<'a>(left: &'a CSTNode, right: &'a CSTNode) -> Match #[cfg(test)] mod tests { use crate::{matching_entry::MatchingEntry, *}; - use model::CSTNode; + use model::{CSTNode, Point}; #[test] fn two_terminal_nodes_matches_with_a_score_of_one_if_they_have_the_same_kind_and_value() { let left = CSTNode::Terminal { kind: "kind", value: "value".to_owned(), + start_position: Point { row: 0, column: 0 }, + end_position: Point { row: 0, column: 5 }, }; let right = CSTNode::Terminal { kind: "kind", value: "value".to_owned(), + start_position: Point { row: 0, column: 0 }, + end_position: Point { row: 0, column: 5 }, }; let matchings = ordered_tree_matching(&left, &right); @@ -144,10 +152,14 @@ mod tests { let left = CSTNode::Terminal { kind: "kind", value: "value_a".to_owned(), + start_position: Point { row: 0, column: 0 }, + end_position: Point { row: 0, column: 7 }, }; let right = CSTNode::Terminal { kind: "kind", value: "value_b".to_owned(), + start_position: Point { row: 0, column: 0 }, + end_position: Point { row: 0, column: 7 }, }; let matchings = ordered_tree_matching(&left, &right); @@ -163,10 +175,14 @@ mod tests { let left = CSTNode::Terminal { kind: "kind_a", value: "value".to_owned(), + start_position: Point { row: 0, column: 0 }, + end_position: Point { row: 0, column: 5 }, }; let right = CSTNode::Terminal { kind: "kind_b", value: "value".to_owned(), + start_position: Point { row: 0, column: 0 }, + end_position: Point { row: 0, column: 5 }, }; let matchings = ordered_tree_matching(&left, &right); @@ -182,10 +198,14 @@ mod tests { let left = CSTNode::Terminal { kind: "kind_a", value: "value_a".to_owned(), + start_position: Point { row: 0, column: 0 }, + end_position: Point { row: 0, column: 7 }, }; let right = CSTNode::Terminal { kind: "kind_b", value: "value_a".to_owned(), + start_position: Point { row: 0, column: 0 }, + end_position: Point { row: 0, column: 7 }, }; let matchings = ordered_tree_matching(&left, &right); @@ -201,13 +221,19 @@ mod tests { let child = CSTNode::Terminal { kind: "kind_b", value: "value_b".into(), + start_position: Point { row: 1, column: 0 }, + end_position: Point { row: 1, column: 7 }, }; let left = CSTNode::NonTerminal { kind: "kind_a", + start_position: Point { row: 0, column: 0 }, + end_position: Point { row: 1, column: 7 }, children: vec![child.clone()], }; let right = CSTNode::NonTerminal { kind: "kind_a", + start_position: Point { row: 0, column: 0 }, + end_position: Point { row: 1, column: 7 }, children: vec![child.clone()], }; @@ -224,19 +250,27 @@ mod tests { let left_child = CSTNode::Terminal { kind: "kind_b", value: "value_b".into(), + start_position: Point { row: 0, column: 0 }, + end_position: Point { row: 0, column: 7 }, }; let right_child = CSTNode::Terminal { kind: "kind_c".into(), value: "value_c".into(), + start_position: Point { row: 1, column: 0 }, + end_position: Point { row: 1, column: 7 }, }; let left = CSTNode::NonTerminal { kind: "kind_a", children: vec![left_child.clone()], + start_position: Point { row: 1, column: 0 }, + end_position: Point { row: 0, column: 7 }, }; let right = CSTNode::NonTerminal { kind: "kind_a", children: vec![right_child.clone()], + start_position: Point { row: 1, column: 0 }, + end_position: Point { row: 0, column: 7 }, }; let matchings = ordered_tree_matching(&left, &right); @@ -252,18 +286,26 @@ mod tests { let common_child = CSTNode::Terminal { kind: "kind_b", value: "value_b".into(), + start_position: Point { row: 0, column: 0 }, + end_position: Point { row: 0, column: 7 }, }; let unique_right_child = CSTNode::Terminal { kind: "kind_c".into(), value: "value_c".into(), + start_position: Point { row: 0, column: 0 }, + end_position: Point { row: 0, column: 7 }, }; let left = CSTNode::NonTerminal { kind: "kind_a", + start_position: Point { row: 0, column: 0 }, + end_position: Point { row: 0, column: 7 }, children: vec![common_child.clone()], }; let right = CSTNode::NonTerminal { kind: "kind_a", + start_position: Point { row: 0, column: 0 }, + end_position: Point { row: 0, column: 7 }, children: vec![common_child.clone(), unique_right_child], }; @@ -279,15 +321,21 @@ mod tests { fn perfect_matching_deep_nodes() { let common_child = CSTNode::Terminal { kind: "kind_b", + start_position: Point { row: 0, column: 0 }, + end_position: Point { row: 0, column: 7 }, value: "value_b".into(), }; let left = CSTNode::NonTerminal { kind: "kind_a", + start_position: Point { row: 0, column: 0 }, + end_position: Point { row: 0, column: 7 }, children: vec![common_child.clone()], }; let right = CSTNode::NonTerminal { kind: "kind_a", + start_position: Point { row: 0, column: 0 }, + end_position: Point { row: 0, column: 7 }, children: vec![common_child.clone()], }; @@ -303,20 +351,28 @@ mod tests { fn perfect_matching_deeper_nodes() { let leaf = CSTNode::Terminal { kind: "kind_b", + start_position: Point { row: 0, column: 0 }, + end_position: Point { row: 0, column: 7 }, value: "value_b".into(), }; let intermediate = CSTNode::NonTerminal { kind: "intermediate".into(), + start_position: Point { row: 0, column: 0 }, + end_position: Point { row: 0, column: 7 }, children: vec![leaf], }; let left = CSTNode::NonTerminal { kind: "kind_a", + start_position: Point { row: 0, column: 0 }, + end_position: Point { row: 0, column: 7 }, children: vec![intermediate.clone()], }; let right = CSTNode::NonTerminal { kind: "kind_a", + start_position: Point { row: 0, column: 0 }, + end_position: Point { row: 0, column: 7 }, children: vec![intermediate.clone()], }; diff --git a/matching/src/unordered_tree_matching.rs b/matching/src/unordered_tree_matching.rs index e721b30..d6f41ec 100644 --- a/matching/src/unordered_tree_matching.rs +++ b/matching/src/unordered_tree_matching.rs @@ -9,10 +9,12 @@ pub fn unordered_tree_matching<'a>(left: &'a CSTNode, right: &'a CSTNode) -> cra CSTNode::Terminal { kind: kind_left, value: value_left, + .. }, CSTNode::Terminal { kind: kind_right, value: value_right, + .. }, ) => { let is_perfetch_match = kind_left == kind_right && value_left == value_right; diff --git a/merge/src/odered_merge.rs b/merge/src/odered_merge.rs index 532e377..a6ca282 100644 --- a/merge/src/odered_merge.rs +++ b/merge/src/odered_merge.rs @@ -20,7 +20,10 @@ pub fn ordered_merge<'a>( value: value_left, .. }, CSTNode::Terminal { - value: value_right, .. + value: value_right, + start_position, + end_position, + .. }, ) => { // Unchanged @@ -29,8 +32,18 @@ pub fn ordered_merge<'a>( // Changed in both } else if value_left != value_base && value_right != value_base { match diffy::merge(&value_base, &value_left, &value_right) { - Ok(value) => CSTNode::Terminal { kind, value: value }, - Err(value) => CSTNode::Terminal { kind, value: value }, + Ok(value) => CSTNode::Terminal { + kind, + value, + start_position: *start_position, + end_position: *end_position, + }, + Err(value) => CSTNode::Terminal { + kind, + value, + start_position: *start_position, + end_position: *end_position, + }, } // Only left changed } else if value_left != value_base { @@ -48,6 +61,8 @@ pub fn ordered_merge<'a>( }, CSTNode::NonTerminal { children: children_right, + start_position, + end_position, .. }, ) => { @@ -239,6 +254,8 @@ pub fn ordered_merge<'a>( CSTNode::NonTerminal { kind, children: result_children, + start_position: *start_position, + end_position: *end_position, } } (_, _, _) => panic!("Can not merge Terminal with Non-Terminal"), @@ -250,7 +267,7 @@ mod tests { use std::vec; use matching::{ordered_tree_matching, Matchings}; - use model::CSTNode; + use model::{CSTNode, Point}; use super::ordered_merge; @@ -311,6 +328,8 @@ mod tests { fn if_i_am_merging_three_unchanged_nodes_it_is_a_success() { let node = CSTNode::Terminal { kind: "kind".into(), + start_position: Point { row: 0, column: 0 }, + end_position: Point { row: 0, column: 7 }, value: "value".into(), }; @@ -326,14 +345,20 @@ mod tests { fn returns_success_if_there_are_changes_in_both_parents_and_they_are_not_conflicting() { let base = CSTNode::Terminal { kind: "kind".into(), + start_position: Point { row: 0, column: 0 }, + end_position: Point { row: 0, column: 7 }, value: "\nvalue\n".into(), }; let left = CSTNode::Terminal { kind: "kind".into(), + start_position: Point { row: 0, column: 0 }, + end_position: Point { row: 0, column: 7 }, value: "left\nvalue\n".into(), }; let right = CSTNode::Terminal { kind: "kind".into(), + start_position: Point { row: 0, column: 0 }, + end_position: Point { row: 0, column: 7 }, value: "\nvalue\nright".into(), }; @@ -343,6 +368,8 @@ mod tests { right, CSTNode::Terminal { kind: "kind".into(), + start_position: Point { row: 0, column: 0 }, + end_position: Point { row: 0, column: 7 }, value: "left\nvalue\nright".into(), }, ) @@ -352,14 +379,20 @@ mod tests { fn returns_conflict_if_there_are_changes_in_both_parents_and_they_are_conflicting() { let base = CSTNode::Terminal { kind: "kind".into(), + start_position: Point { row: 0, column: 0 }, + end_position: Point { row: 0, column: 7 }, value: "value".into(), }; let left = CSTNode::Terminal { kind: "kind".into(), + start_position: Point { row: 0, column: 0 }, + end_position: Point { row: 0, column: 7 }, value: "left_value".into(), }; let right = CSTNode::Terminal { kind: "kind".into(), + start_position: Point { row: 0, column: 0 }, + end_position: Point { row: 0, column: 7 }, value: "right_value".into(), }; @@ -368,6 +401,8 @@ mod tests { &Matchings::empty()), CSTNode::Terminal { kind: "kind".into(), + start_position: Point { row: 0, column: 0 }, + end_position: Point { row: 0, column: 7 }, value: "<<<<<<< ours\nleft_value||||||| original\nvalue=======\nright_value>>>>>>> theirs\n".into() } ) @@ -377,10 +412,14 @@ mod tests { fn if_there_is_a_change_only_in_one_parent_it_returns_the_changes_from_this_parent() { let base_and_left = CSTNode::Terminal { kind: "kind".into(), + start_position: Point { row: 0, column: 0 }, + end_position: Point { row: 0, column: 7 }, value: "value".into(), }; let changed_parent = CSTNode::Terminal { kind: "kind".into(), + start_position: Point { row: 0, column: 0 }, + end_position: Point { row: 0, column: 7 }, value: "value_right".into(), }; @@ -398,14 +437,20 @@ mod tests { ordered_merge( &CSTNode::Terminal { kind: "kind".into(), + start_position: Point { row: 0, column: 0 }, + end_position: Point { row: 0, column: 7 }, value: "value".into(), }, &CSTNode::Terminal { kind: "kind".into(), + start_position: Point { row: 0, column: 0 }, + end_position: Point { row: 0, column: 7 }, value: "value".into(), }, &CSTNode::NonTerminal { kind: "kind".into(), + start_position: Point { row: 0, column: 0 }, + end_position: Point { row: 0, column: 7 }, children: vec![], }, &Matchings::empty(), @@ -418,13 +463,19 @@ mod tests { fn it_merges_non_terminals_if_there_are_non_changes() { let tree = CSTNode::NonTerminal { kind: "kind".into(), + start_position: Point { row: 0, column: 0 }, + end_position: Point { row: 0, column: 7 }, children: vec![ CSTNode::Terminal { kind: "kind_a".into(), + start_position: Point { row: 0, column: 0 }, + end_position: Point { row: 0, column: 7 }, value: "value_a".into(), }, CSTNode::Terminal { kind: "kind_b".into(), + start_position: Point { row: 0, column: 0 }, + end_position: Point { row: 0, column: 7 }, value: "value_b".into(), }, ], @@ -442,17 +493,25 @@ mod tests { fn it_merges_non_terminals_if_both_left_and_right_add_the_same_things() { let base = CSTNode::NonTerminal { kind: "kind".into(), + start_position: Point { row: 0, column: 0 }, + end_position: Point { row: 0, column: 7 }, children: vec![], }; let parent = CSTNode::NonTerminal { kind: "kind".into(), + start_position: Point { row: 0, column: 0 }, + end_position: Point { row: 0, column: 7 }, children: vec![ CSTNode::Terminal { kind: "kind_a".into(), + start_position: Point { row: 0, column: 0 }, + end_position: Point { row: 0, column: 7 }, value: "value_a".into(), }, CSTNode::Terminal { kind: "kind_b".into(), + start_position: Point { row: 0, column: 0 }, + end_position: Point { row: 0, column: 7 }, value: "value_b".into(), }, ], @@ -471,26 +530,38 @@ mod tests { { let base = CSTNode::NonTerminal { kind: "kind".into(), + start_position: Point { row: 0, column: 0 }, + end_position: Point { row: 0, column: 7 }, children: vec![], }; let initially_empty_parent = CSTNode::NonTerminal { kind: "kind".into(), + start_position: Point { row: 0, column: 0 }, + end_position: Point { row: 0, column: 7 }, children: vec![], }; let parent_that_added = CSTNode::NonTerminal { kind: "kind".into(), + start_position: Point { row: 0, column: 0 }, + end_position: Point { row: 0, column: 7 }, children: vec![CSTNode::Terminal { kind: "kind_a".into(), + start_position: Point { row: 0, column: 0 }, + end_position: Point { row: 0, column: 7 }, value: "value_a".into(), }], }; let merge = CSTNode::NonTerminal { kind: "kind".into(), + start_position: Point { row: 0, column: 0 }, + end_position: Point { row: 0, column: 7 }, children: vec![CSTNode::Terminal { kind: "kind_a".into(), + start_position: Point { row: 0, column: 0 }, + end_position: Point { row: 0, column: 7 }, value: "value_a".into(), }], }; @@ -507,29 +578,43 @@ mod tests { fn it_merges_non_terminals_if_only_one_parent_adds_a_node_in_non_empty_children_list() { let base = CSTNode::NonTerminal { kind: "kind".into(), + start_position: Point { row: 0, column: 0 }, + end_position: Point { row: 0, column: 7 }, children: vec![CSTNode::Terminal { kind: "kind_a".into(), + start_position: Point { row: 0, column: 0 }, + end_position: Point { row: 0, column: 7 }, value: "value_a".into(), }], }; let unchanged_parent = CSTNode::NonTerminal { kind: "kind".into(), + start_position: Point { row: 0, column: 0 }, + end_position: Point { row: 0, column: 7 }, children: vec![CSTNode::Terminal { kind: "kind_a".into(), + start_position: Point { row: 0, column: 0 }, + end_position: Point { row: 0, column: 7 }, value: "value_a".into(), }], }; let parent_that_added = CSTNode::NonTerminal { kind: "kind".into(), + start_position: Point { row: 0, column: 0 }, + end_position: Point { row: 0, column: 7 }, children: vec![ CSTNode::Terminal { kind: "kind_a".into(), + start_position: Point { row: 0, column: 0 }, + end_position: Point { row: 0, column: 7 }, value: "value_a".into(), }, CSTNode::Terminal { kind: "kind_b".into(), + start_position: Point { row: 0, column: 0 }, + end_position: Point { row: 0, column: 7 }, value: "value_b".into(), }, ], @@ -537,13 +622,19 @@ mod tests { let merge = CSTNode::NonTerminal { kind: "kind".into(), + start_position: Point { row: 0, column: 0 }, + end_position: Point { row: 0, column: 7 }, children: vec![ CSTNode::Terminal { kind: "kind_a".into(), + start_position: Point { row: 0, column: 0 }, + end_position: Point { row: 0, column: 7 }, value: "value_a".into(), }, CSTNode::Terminal { kind: "kind_b".into(), + start_position: Point { row: 0, column: 0 }, + end_position: Point { row: 0, column: 7 }, value: "value_b".into(), }, ], @@ -561,32 +652,48 @@ mod tests { fn it_merges_when_one_parent_adds_a_node_and_removes_one_that_was_not_edited_in_the_other() { let base = CSTNode::NonTerminal { kind: "kind".into(), + start_position: Point { row: 0, column: 0 }, + end_position: Point { row: 0, column: 7 }, children: vec![CSTNode::Terminal { kind: "kind_a".into(), + start_position: Point { row: 0, column: 0 }, + end_position: Point { row: 0, column: 7 }, value: "value_a".into(), }], }; let changed_parent = CSTNode::NonTerminal { kind: "kind".into(), + start_position: Point { row: 0, column: 0 }, + end_position: Point { row: 0, column: 7 }, children: vec![CSTNode::Terminal { kind: "kind_b".into(), + start_position: Point { row: 0, column: 0 }, + end_position: Point { row: 0, column: 7 }, value: "value_b".into(), }], }; let unchanged_parent = CSTNode::NonTerminal { kind: "kind".into(), + start_position: Point { row: 0, column: 0 }, + end_position: Point { row: 0, column: 7 }, children: vec![CSTNode::Terminal { kind: "kind_a".into(), + start_position: Point { row: 0, column: 0 }, + end_position: Point { row: 0, column: 7 }, value: "value_a".into(), }], }; let expected_merge = CSTNode::NonTerminal { kind: "kind".into(), + start_position: Point { row: 0, column: 0 }, + end_position: Point { row: 0, column: 7 }, children: vec![CSTNode::Terminal { kind: "kind_b".into(), + start_position: Point { row: 0, column: 0 }, + end_position: Point { row: 0, column: 7 }, value: "value_b".into(), }], }; @@ -603,10 +710,16 @@ mod tests { fn it_merges_when_one_parent_adds_a_node_and_removes_from_another_that_was_changed() { let base = CSTNode::NonTerminal { kind: "kind".into(), + start_position: Point { row: 0, column: 0 }, + end_position: Point { row: 0, column: 7 }, children: vec![CSTNode::NonTerminal { kind: "subtree".into(), + start_position: Point { row: 0, column: 0 }, + end_position: Point { row: 0, column: 7 }, children: vec![CSTNode::Terminal { kind: "kind_a".into(), + start_position: Point { row: 0, column: 0 }, + end_position: Point { row: 0, column: 7 }, value: "value_a".into(), }], }], @@ -614,10 +727,16 @@ mod tests { let parent_a = CSTNode::NonTerminal { kind: "kind".into(), + start_position: Point { row: 0, column: 0 }, + end_position: Point { row: 0, column: 7 }, children: vec![CSTNode::NonTerminal { kind: "another_subtree".into(), + start_position: Point { row: 0, column: 0 }, + end_position: Point { row: 0, column: 7 }, children: vec![CSTNode::Terminal { kind: "kind_b".into(), + start_position: Point { row: 0, column: 0 }, + end_position: Point { row: 0, column: 7 }, value: "value_b".into(), }], }], @@ -625,10 +744,16 @@ mod tests { let parent_b = CSTNode::NonTerminal { kind: "kind".into(), + start_position: Point { row: 0, column: 0 }, + end_position: Point { row: 0, column: 7 }, children: vec![CSTNode::NonTerminal { kind: "subtree".into(), + start_position: Point { row: 0, column: 0 }, + end_position: Point { row: 0, column: 7 }, children: vec![CSTNode::Terminal { kind: "kind_c".into(), + start_position: Point { row: 0, column: 0 }, + end_position: Point { row: 0, column: 7 }, value: "value_c".into(), }], }], @@ -658,11 +783,17 @@ mod tests { assert_eq!( CSTNode::NonTerminal { kind: "kind".into(), + start_position: Point { row: 0, column: 0 }, + end_position: Point { row: 0, column: 7 }, children: vec![ CSTNode::NonTerminal { kind: "another_subtree".into(), + start_position: Point { row: 0, column: 0 }, + end_position: Point { row: 0, column: 7 }, children: vec![CSTNode::Terminal { kind: "kind_b".into(), + start_position: Point { row: 0, column: 0 }, + end_position: Point { row: 0, column: 7 }, value: "value_b".into(), }], }, @@ -670,8 +801,12 @@ mod tests { left: None, right: Some(&CSTNode::NonTerminal { kind: "subtree".into(), + start_position: Point { row: 0, column: 0 }, + end_position: Point { row: 0, column: 7 }, children: vec![CSTNode::Terminal { kind: "kind_c".into(), + start_position: Point { row: 0, column: 0 }, + end_position: Point { row: 0, column: 7 }, value: "value_c".into(), }], }), @@ -684,19 +819,29 @@ mod tests { assert_eq!( CSTNode::NonTerminal { kind: "kind".into(), + start_position: Point { row: 0, column: 0 }, + end_position: Point { row: 0, column: 7 }, children: vec![ CSTNode::NonTerminal { kind: "another_subtree".into(), + start_position: Point { row: 0, column: 0 }, + end_position: Point { row: 0, column: 7 }, children: vec![CSTNode::Terminal { kind: "kind_b".into(), + start_position: Point { row: 0, column: 0 }, + end_position: Point { row: 0, column: 7 }, value: "value_b".into(), }], }, CSTNode::Conflict { left: Some(&CSTNode::NonTerminal { kind: "subtree".into(), + start_position: Point { row: 0, column: 0 }, + end_position: Point { row: 0, column: 7 }, children: vec![CSTNode::Terminal { kind: "kind_c".into(), + start_position: Point { row: 0, column: 0 }, + end_position: Point { row: 0, column: 7 }, value: "value_c".into(), }], }), @@ -712,21 +857,31 @@ mod tests { fn if_both_parents_add_different_nodes_then_we_have_a_conflict() { let base = CSTNode::NonTerminal { kind: "kind".into(), + start_position: Point { row: 0, column: 0 }, + end_position: Point { row: 0, column: 7 }, children: vec![], }; let left = CSTNode::NonTerminal { kind: "kind".into(), + start_position: Point { row: 0, column: 0 }, + end_position: Point { row: 0, column: 7 }, children: vec![CSTNode::Terminal { kind: "kind_a".into(), + start_position: Point { row: 0, column: 0 }, + end_position: Point { row: 0, column: 7 }, value: "value_a".into(), }], }; let right = CSTNode::NonTerminal { kind: "kind".into(), + start_position: Point { row: 0, column: 0 }, + end_position: Point { row: 0, column: 7 }, children: vec![CSTNode::Terminal { kind: "kind_b".into(), + start_position: Point { row: 0, column: 0 }, + end_position: Point { row: 0, column: 7 }, value: "value_b".into(), }], }; @@ -737,13 +892,19 @@ mod tests { &right, &CSTNode::NonTerminal { kind: "kind".into(), + start_position: Point { row: 0, column: 0 }, + end_position: Point { row: 0, column: 7 }, children: vec![CSTNode::Conflict { left: Some(&CSTNode::Terminal { kind: "kind_a".into(), + start_position: Point { row: 0, column: 0 }, + end_position: Point { row: 0, column: 7 }, value: "value_a".into(), }), right: Some(&CSTNode::Terminal { kind: "kind_b".into(), + start_position: Point { row: 0, column: 0 }, + end_position: Point { row: 0, column: 7 }, value: "value_b".into(), }), }], @@ -755,13 +916,19 @@ mod tests { fn it_merges_when_one_parent_removes_a_node_that_was_not_changed_in_another_parent() { let base = CSTNode::NonTerminal { kind: "kind".into(), + start_position: Point { row: 0, column: 0 }, + end_position: Point { row: 0, column: 7 }, children: vec![ CSTNode::Terminal { kind: "kind_a".into(), + start_position: Point { row: 0, column: 0 }, + end_position: Point { row: 0, column: 7 }, value: "value_a".into(), }, CSTNode::Terminal { kind: "kind_b".into(), + start_position: Point { row: 0, column: 0 }, + end_position: Point { row: 0, column: 7 }, value: "value_b".into(), }, ], @@ -769,13 +936,19 @@ mod tests { let left = CSTNode::NonTerminal { kind: "kind".into(), + start_position: Point { row: 0, column: 0 }, + end_position: Point { row: 0, column: 7 }, children: vec![ CSTNode::Terminal { kind: "kind_a".into(), + start_position: Point { row: 0, column: 0 }, + end_position: Point { row: 0, column: 7 }, value: "value_a".into(), }, CSTNode::Terminal { kind: "kind_b".into(), + start_position: Point { row: 0, column: 0 }, + end_position: Point { row: 0, column: 7 }, value: "value_b".into(), }, ], @@ -783,8 +956,12 @@ mod tests { let right = CSTNode::NonTerminal { kind: "kind".into(), + start_position: Point { row: 0, column: 0 }, + end_position: Point { row: 0, column: 7 }, children: vec![CSTNode::Terminal { kind: "kind_b".into(), + start_position: Point { row: 0, column: 0 }, + end_position: Point { row: 0, column: 7 }, value: "value_b".into(), }], }; @@ -795,8 +972,12 @@ mod tests { &right, &CSTNode::NonTerminal { kind: "kind".into(), + start_position: Point { row: 0, column: 0 }, + end_position: Point { row: 0, column: 7 }, children: vec![CSTNode::Terminal { kind: "kind_b".into(), + start_position: Point { row: 0, column: 0 }, + end_position: Point { row: 0, column: 7 }, value: "value_b".into(), }], }, @@ -807,16 +988,24 @@ mod tests { fn it_detects_a_conflit_when_one_parent_removes_a_node_that_was_changed_in_another_parent() { let base = CSTNode::NonTerminal { kind: "kind".into(), + start_position: Point { row: 0, column: 0 }, + end_position: Point { row: 0, column: 7 }, children: vec![ CSTNode::NonTerminal { kind: "subtree".into(), + start_position: Point { row: 0, column: 0 }, + end_position: Point { row: 0, column: 7 }, children: vec![CSTNode::Terminal { kind: "kind_b".into(), + start_position: Point { row: 0, column: 0 }, + end_position: Point { row: 0, column: 7 }, value: "value_b".into(), }], }, CSTNode::Terminal { kind: "kind_a".into(), + start_position: Point { row: 0, column: 0 }, + end_position: Point { row: 0, column: 7 }, value: "value_a".into(), }, ], @@ -824,16 +1013,24 @@ mod tests { let left = CSTNode::NonTerminal { kind: "kind".into(), + start_position: Point { row: 0, column: 0 }, + end_position: Point { row: 0, column: 7 }, children: vec![ CSTNode::NonTerminal { kind: "subtree".into(), + start_position: Point { row: 0, column: 0 }, + end_position: Point { row: 0, column: 7 }, children: vec![CSTNode::Terminal { kind: "kind_c".into(), + start_position: Point { row: 0, column: 0 }, + end_position: Point { row: 0, column: 7 }, value: "value_c".into(), }], }, CSTNode::Terminal { kind: "kind_a".into(), + start_position: Point { row: 0, column: 0 }, + end_position: Point { row: 0, column: 7 }, value: "value_a".into(), }, ], @@ -841,8 +1038,12 @@ mod tests { let right = CSTNode::NonTerminal { kind: "kind".into(), + start_position: Point { row: 0, column: 0 }, + end_position: Point { row: 0, column: 7 }, children: vec![CSTNode::Terminal { kind: "kind_a".into(), + start_position: Point { row: 0, column: 0 }, + end_position: Point { row: 0, column: 7 }, value: "value_a".into(), }], }; @@ -853,12 +1054,18 @@ mod tests { &right, &CSTNode::NonTerminal { kind: "kind".into(), + start_position: Point { row: 0, column: 0 }, + end_position: Point { row: 0, column: 7 }, children: vec![ CSTNode::Conflict { left: Some(&CSTNode::NonTerminal { kind: "subtree".into(), + start_position: Point { row: 0, column: 0 }, + end_position: Point { row: 0, column: 7 }, children: vec![CSTNode::Terminal { kind: "kind_c".into(), + start_position: Point { row: 0, column: 0 }, + end_position: Point { row: 0, column: 7 }, value: "value_c".into(), }], }) @@ -867,6 +1074,8 @@ mod tests { }, CSTNode::Terminal { kind: "kind_a".into(), + start_position: Point { row: 0, column: 0 }, + end_position: Point { row: 0, column: 7 }, value: "value_a".into(), }, ], @@ -879,13 +1088,19 @@ mod tests { &left, &CSTNode::NonTerminal { kind: "kind".into(), + start_position: Point { row: 0, column: 0 }, + end_position: Point { row: 0, column: 7 }, children: vec![ CSTNode::Conflict { left: None.into(), right: Some(&CSTNode::NonTerminal { kind: "subtree".into(), + start_position: Point { row: 0, column: 0 }, + end_position: Point { row: 0, column: 7 }, children: vec![CSTNode::Terminal { kind: "kind_c".into(), + start_position: Point { row: 0, column: 0 }, + end_position: Point { row: 0, column: 7 }, value: "value_c".into(), }], }) @@ -893,6 +1108,8 @@ mod tests { }, CSTNode::Terminal { kind: "kind_a".into(), + start_position: Point { row: 0, column: 0 }, + end_position: Point { row: 0, column: 7 }, value: "value_a".into(), }, ], @@ -904,13 +1121,19 @@ mod tests { fn it_merges_when_a_parent_adds_a_node() { let base = CSTNode::NonTerminal { kind: "kind".into(), + start_position: Point { row: 0, column: 0 }, + end_position: Point { row: 0, column: 7 }, children: vec![ CSTNode::Terminal { kind: "kind_a".into(), + start_position: Point { row: 0, column: 0 }, + end_position: Point { row: 0, column: 7 }, value: "value_a".into(), }, CSTNode::Terminal { kind: "kind_c".into(), + start_position: Point { row: 0, column: 0 }, + end_position: Point { row: 0, column: 7 }, value: "value_c".into(), }, ], @@ -918,13 +1141,19 @@ mod tests { let unchanged_parent = CSTNode::NonTerminal { kind: "kind".into(), + start_position: Point { row: 0, column: 0 }, + end_position: Point { row: 0, column: 7 }, children: vec![ CSTNode::Terminal { kind: "kind_a".into(), + start_position: Point { row: 0, column: 0 }, + end_position: Point { row: 0, column: 7 }, value: "value_a".into(), }, CSTNode::Terminal { kind: "kind_c".into(), + start_position: Point { row: 0, column: 0 }, + end_position: Point { row: 0, column: 7 }, value: "value_c".into(), }, ], @@ -932,17 +1161,25 @@ mod tests { let changed_parent = CSTNode::NonTerminal { kind: "kind".into(), + start_position: Point { row: 0, column: 0 }, + end_position: Point { row: 0, column: 7 }, children: vec![ CSTNode::Terminal { kind: "kind_a".into(), + start_position: Point { row: 0, column: 0 }, + end_position: Point { row: 0, column: 7 }, value: "value_a".into(), }, CSTNode::Terminal { kind: "kind_b".into(), + start_position: Point { row: 0, column: 0 }, + end_position: Point { row: 0, column: 7 }, value: "value_b".into(), }, CSTNode::Terminal { kind: "kind_c".into(), + start_position: Point { row: 0, column: 0 }, + end_position: Point { row: 0, column: 7 }, value: "value_c".into(), }, ], @@ -950,17 +1187,25 @@ mod tests { let expected_merge = CSTNode::NonTerminal { kind: "kind".into(), + start_position: Point { row: 0, column: 0 }, + end_position: Point { row: 0, column: 7 }, children: vec![ CSTNode::Terminal { kind: "kind_a".into(), + start_position: Point { row: 0, column: 0 }, + end_position: Point { row: 0, column: 7 }, value: "value_a".into(), }, CSTNode::Terminal { kind: "kind_b".into(), + start_position: Point { row: 0, column: 0 }, + end_position: Point { row: 0, column: 7 }, value: "value_b".into(), }, CSTNode::Terminal { kind: "kind_c".into(), + start_position: Point { row: 0, column: 0 }, + end_position: Point { row: 0, column: 7 }, value: "value_c".into(), }, ], @@ -978,29 +1223,43 @@ mod tests { fn it_merges_when_one_parent_removes_and_add_a_node() { let base = CSTNode::NonTerminal { kind: "kind".into(), + start_position: Point { row: 0, column: 0 }, + end_position: Point { row: 0, column: 7 }, children: vec![CSTNode::Terminal { kind: "kind_b".into(), + start_position: Point { row: 0, column: 0 }, + end_position: Point { row: 0, column: 7 }, value: "value_b".into(), }], }; let parent_a = CSTNode::NonTerminal { kind: "kind".into(), + start_position: Point { row: 0, column: 0 }, + end_position: Point { row: 0, column: 7 }, children: vec![CSTNode::Terminal { kind: "kind_a", + start_position: Point { row: 0, column: 0 }, + end_position: Point { row: 0, column: 7 }, value: "value_a".into(), }], }; let parent_b = CSTNode::NonTerminal { kind: "kind".into(), + start_position: Point { row: 0, column: 0 }, + end_position: Point { row: 0, column: 7 }, children: vec![ CSTNode::Terminal { kind: "kind_b".into(), + start_position: Point { row: 0, column: 0 }, + end_position: Point { row: 0, column: 7 }, value: "value_b".into(), }, CSTNode::Terminal { kind: "kind_a", + start_position: Point { row: 0, column: 0 }, + end_position: Point { row: 0, column: 7 }, value: "value_a".into(), }, ], @@ -1008,8 +1267,12 @@ mod tests { let expected_merge = CSTNode::NonTerminal { kind: "kind".into(), + start_position: Point { row: 0, column: 0 }, + end_position: Point { row: 0, column: 7 }, children: vec![CSTNode::Terminal { kind: "kind_a", + start_position: Point { row: 0, column: 0 }, + end_position: Point { row: 0, column: 7 }, value: "value_a".into(), }], }; @@ -1026,10 +1289,16 @@ mod tests { fn it_conflicts_when_one_parent_removes_and_add_a_node() { let base = CSTNode::NonTerminal { kind: "kind", + start_position: Point { row: 0, column: 0 }, + end_position: Point { row: 0, column: 7 }, children: vec![CSTNode::NonTerminal { kind: "subtree", + start_position: Point { row: 0, column: 0 }, + end_position: Point { row: 0, column: 7 }, children: vec![CSTNode::Terminal { kind: "kind_b", + start_position: Point { row: 0, column: 0 }, + end_position: Point { row: 0, column: 7 }, value: "value_b".into(), }], }], @@ -1037,24 +1306,36 @@ mod tests { let parent_a = CSTNode::NonTerminal { kind: "kind".into(), + start_position: Point { row: 0, column: 0 }, + end_position: Point { row: 0, column: 7 }, children: vec![CSTNode::Terminal { kind: "kind_a", + start_position: Point { row: 0, column: 0 }, + end_position: Point { row: 0, column: 7 }, value: "value_a".into(), }], }; let parent_b = CSTNode::NonTerminal { kind: "kind".into(), + start_position: Point { row: 0, column: 0 }, + end_position: Point { row: 0, column: 7 }, children: vec![ CSTNode::NonTerminal { kind: "subtree", + start_position: Point { row: 0, column: 0 }, + end_position: Point { row: 0, column: 7 }, children: vec![CSTNode::Terminal { kind: "kind_b", + start_position: Point { row: 0, column: 0 }, + end_position: Point { row: 0, column: 7 }, value: "value_c".into(), }], }, CSTNode::Terminal { kind: "kind_a", + start_position: Point { row: 0, column: 0 }, + end_position: Point { row: 0, column: 7 }, value: "value_a".into(), }, ], @@ -1066,13 +1347,19 @@ mod tests { &parent_b, &CSTNode::NonTerminal { kind: "kind".into(), + start_position: Point { row: 0, column: 0 }, + end_position: Point { row: 0, column: 7 }, children: vec![ CSTNode::Conflict { left: None.into(), right: Some(&CSTNode::NonTerminal { kind: "subtree", + start_position: Point { row: 0, column: 0 }, + end_position: Point { row: 0, column: 7 }, children: vec![CSTNode::Terminal { kind: "kind_b", + start_position: Point { row: 0, column: 0 }, + end_position: Point { row: 0, column: 7 }, value: "value_c".into(), }], }) @@ -1080,6 +1367,8 @@ mod tests { }, CSTNode::Terminal { kind: "kind_a", + start_position: Point { row: 0, column: 0 }, + end_position: Point { row: 0, column: 7 }, value: "value_a".into(), }, ], @@ -1091,12 +1380,18 @@ mod tests { &parent_a, &CSTNode::NonTerminal { kind: "kind".into(), + start_position: Point { row: 0, column: 0 }, + end_position: Point { row: 0, column: 7 }, children: vec![ CSTNode::Conflict { left: Some(&CSTNode::NonTerminal { kind: "subtree", + start_position: Point { row: 0, column: 0 }, + end_position: Point { row: 0, column: 7 }, children: vec![CSTNode::Terminal { kind: "kind_b", + start_position: Point { row: 0, column: 0 }, + end_position: Point { row: 0, column: 7 }, value: "value_c".into(), }], }) @@ -1105,6 +1400,8 @@ mod tests { }, CSTNode::Terminal { kind: "kind_a", + start_position: Point { row: 0, column: 0 }, + end_position: Point { row: 0, column: 7 }, value: "value_a".into(), }, ], @@ -1116,26 +1413,38 @@ mod tests { fn it_merges_when_a_parent_adds_one_node() { let base = CSTNode::NonTerminal { kind: "kind", + start_position: Point { row: 0, column: 0 }, + end_position: Point { row: 0, column: 7 }, children: vec![], }; let parent_a = CSTNode::NonTerminal { kind: "kind", + start_position: Point { row: 0, column: 0 }, + end_position: Point { row: 0, column: 7 }, children: vec![CSTNode::Terminal { kind: "kind_a", + start_position: Point { row: 0, column: 0 }, + end_position: Point { row: 0, column: 7 }, value: "value_a".into(), }], }; let parent_b = CSTNode::NonTerminal { kind: "kind".into(), + start_position: Point { row: 0, column: 0 }, + end_position: Point { row: 0, column: 7 }, children: vec![ CSTNode::Terminal { kind: "kind_c", + start_position: Point { row: 0, column: 0 }, + end_position: Point { row: 0, column: 7 }, value: "value_c".into(), }, CSTNode::Terminal { kind: "kind_a", + start_position: Point { row: 0, column: 0 }, + end_position: Point { row: 0, column: 7 }, value: "value_a".into(), }, ], @@ -1143,13 +1452,19 @@ mod tests { let expected_merge = CSTNode::NonTerminal { kind: "kind", + start_position: Point { row: 0, column: 0 }, + end_position: Point { row: 0, column: 7 }, children: vec![ CSTNode::Terminal { kind: "kind_c", + start_position: Point { row: 0, column: 0 }, + end_position: Point { row: 0, column: 7 }, value: "value_c".into(), }, CSTNode::Terminal { kind: "kind_a", + start_position: Point { row: 0, column: 0 }, + end_position: Point { row: 0, column: 7 }, value: "value_a".into(), }, ], @@ -1167,13 +1482,19 @@ mod tests { fn it_does_not_detect_a_conflict_if_am_merging_two_subtrees_that_have_not_changed_mutually() { let base = CSTNode::NonTerminal { kind: "kind", + start_position: Point { row: 0, column: 0 }, + end_position: Point { row: 0, column: 7 }, children: vec![ CSTNode::Terminal { kind: "kind_b", + start_position: Point { row: 0, column: 0 }, + end_position: Point { row: 0, column: 7 }, value: "value_b".into(), }, CSTNode::Terminal { kind: "kind_c", + start_position: Point { row: 0, column: 0 }, + end_position: Point { row: 0, column: 7 }, value: "value_c".into(), }, ], @@ -1181,22 +1502,32 @@ mod tests { let parent_a = CSTNode::NonTerminal { kind: "kind", + start_position: Point { row: 0, column: 0 }, + end_position: Point { row: 0, column: 7 }, children: vec![CSTNode::Terminal { kind: "kind_b", + start_position: Point { row: 0, column: 0 }, + end_position: Point { row: 0, column: 7 }, value: "value_b".into(), }], }; let parent_b = CSTNode::NonTerminal { kind: "kind".into(), + start_position: Point { row: 0, column: 0 }, + end_position: Point { row: 0, column: 7 }, children: vec![CSTNode::Terminal { kind: "kind_c", + start_position: Point { row: 0, column: 0 }, + end_position: Point { row: 0, column: 7 }, value: "value_c".into(), }], }; let expected_merge = CSTNode::NonTerminal { kind: "kind", + start_position: Point { row: 0, column: 0 }, + end_position: Point { row: 0, column: 7 }, children: vec![], }; @@ -1213,18 +1544,28 @@ mod tests { ) { let base = CSTNode::NonTerminal { kind: "kind", + start_position: Point { row: 0, column: 0 }, + end_position: Point { row: 0, column: 7 }, children: vec![ CSTNode::NonTerminal { kind: "subtree_a", + start_position: Point { row: 0, column: 0 }, + end_position: Point { row: 0, column: 7 }, children: vec![CSTNode::Terminal { kind: "kind_b", + start_position: Point { row: 0, column: 0 }, + end_position: Point { row: 0, column: 7 }, value: "value_b".into(), }], }, CSTNode::NonTerminal { kind: "subtree_b", + start_position: Point { row: 0, column: 0 }, + end_position: Point { row: 0, column: 7 }, children: vec![CSTNode::Terminal { kind: "kind_c", + start_position: Point { row: 0, column: 0 }, + end_position: Point { row: 0, column: 7 }, value: "value_c".into(), }], }, @@ -1233,10 +1574,16 @@ mod tests { let parent_a = CSTNode::NonTerminal { kind: "kind", + start_position: Point { row: 0, column: 0 }, + end_position: Point { row: 0, column: 7 }, children: vec![CSTNode::NonTerminal { kind: "subtree_b", + start_position: Point { row: 0, column: 0 }, + end_position: Point { row: 0, column: 7 }, children: vec![CSTNode::Terminal { kind: "kind_c", + start_position: Point { row: 0, column: 0 }, + end_position: Point { row: 0, column: 7 }, value: "value_c".into(), }], }], @@ -1244,10 +1591,16 @@ mod tests { let parent_b = CSTNode::NonTerminal { kind: "kind", + start_position: Point { row: 0, column: 0 }, + end_position: Point { row: 0, column: 7 }, children: vec![CSTNode::NonTerminal { kind: "subtree_a", + start_position: Point { row: 0, column: 0 }, + end_position: Point { row: 0, column: 7 }, children: vec![CSTNode::Terminal { kind: "kind_a", + start_position: Point { row: 0, column: 0 }, + end_position: Point { row: 0, column: 7 }, value: "value_c".into(), }], }], @@ -1259,11 +1612,17 @@ mod tests { &parent_b, &CSTNode::NonTerminal { kind: "kind", + start_position: Point { row: 0, column: 0 }, + end_position: Point { row: 0, column: 7 }, children: vec![CSTNode::Conflict { left: Some(&CSTNode::NonTerminal { kind: "subtree_b", + start_position: Point { row: 0, column: 0 }, + end_position: Point { row: 0, column: 7 }, children: vec![CSTNode::Terminal { kind: "kind_c", + start_position: Point { row: 0, column: 0 }, + end_position: Point { row: 0, column: 7 }, value: "value_c".into(), }], }) @@ -1278,12 +1637,18 @@ mod tests { &parent_a, &CSTNode::NonTerminal { kind: "kind", + start_position: Point { row: 0, column: 0 }, + end_position: Point { row: 0, column: 7 }, children: vec![CSTNode::Conflict { left: None.into(), right: Some(&CSTNode::NonTerminal { kind: "subtree_b", + start_position: Point { row: 0, column: 0 }, + end_position: Point { row: 0, column: 7 }, children: vec![CSTNode::Terminal { kind: "kind_c", + start_position: Point { row: 0, column: 0 }, + end_position: Point { row: 0, column: 7 }, value: "value_c".into(), }], }) diff --git a/model/Cargo.toml b/model/Cargo.toml index 1d77457..31444d5 100644 --- a/model/Cargo.toml +++ b/model/Cargo.toml @@ -6,3 +6,4 @@ edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] +tree-sitter = "0.20.9" diff --git a/model/src/cst_node.rs b/model/src/cst_node.rs index b051564..222b406 100644 --- a/model/src/cst_node.rs +++ b/model/src/cst_node.rs @@ -3,10 +3,14 @@ pub enum CSTNode<'a> { Terminal { kind: &'a str, value: String, + start_position: tree_sitter::Point, + end_position: tree_sitter::Point, }, NonTerminal { kind: &'a str, children: Vec>, + start_position: tree_sitter::Point, + end_position: tree_sitter::Point, }, Conflict { left: Option<&'a CSTNode<'a>>, diff --git a/model/src/lib.rs b/model/src/lib.rs index c58b4dc..6b7d048 100644 --- a/model/src/lib.rs +++ b/model/src/lib.rs @@ -3,3 +3,5 @@ pub mod language; pub use cst_node::CSTNode; pub use language::Language; + +pub type Point = tree_sitter::Point; diff --git a/parsing/src/parse.rs b/parsing/src/parse.rs index 1e060e1..e690a59 100644 --- a/parsing/src/parse.rs +++ b/parsing/src/parse.rs @@ -6,12 +6,16 @@ fn explore_node<'a>(node: Node, src: &'a str, config: &'a ParserConfiguration) - if node.child_count() == 0 || config.stop_compilation_at.contains(node.kind()) { CSTNode::Terminal { kind: node.kind().into(), + start_position: node.start_position(), + end_position: node.end_position(), value: src[node.byte_range()].into(), } } else { let mut cursor = node.walk(); CSTNode::NonTerminal { kind: node.kind().into(), + start_position: node.start_position(), + end_position: node.end_position(), children: node .children(&mut cursor) .map(|child| explore_node(child, src, config)) @@ -38,6 +42,9 @@ pub fn parse_string<'a>( #[cfg(test)] mod tests { + use model::CSTNode::{NonTerminal, Terminal}; + use model::Point; + use super::*; #[test] @@ -52,90 +59,136 @@ mod tests { stop_compilation_at: [].into_iter().collect(), }; let result = parse_string(code, &parser_configuration); - let expected = CSTNode::NonTerminal { - kind: "program".into(), - children: vec![CSTNode::NonTerminal { - kind: "interface_declaration".into(), + let expected = NonTerminal { + kind: "program", + children: vec![NonTerminal { + kind: "interface_declaration", children: vec![ - CSTNode::NonTerminal { - kind: "modifiers".into(), + NonTerminal { + kind: "modifiers", children: vec![ - CSTNode::Terminal { - kind: "public".into(), + Terminal { + kind: "public", value: "public".into(), + start_position: Point { row: 1, column: 12 }, + end_position: Point { row: 1, column: 18 }, }, - CSTNode::Terminal { - kind: "static".into(), + Terminal { + kind: "static", value: "static".into(), + start_position: Point { row: 1, column: 19 }, + end_position: Point { row: 1, column: 25 }, }, ], + start_position: Point { row: 1, column: 12 }, + end_position: Point { row: 1, column: 25 }, }, - CSTNode::Terminal { - kind: "interface".into(), + Terminal { + kind: "interface", value: "interface".into(), + start_position: Point { row: 1, column: 26 }, + end_position: Point { row: 1, column: 35 }, }, - CSTNode::Terminal { - kind: "identifier".into(), + Terminal { + kind: "identifier", value: "HelloWorld".into(), + start_position: Point { row: 1, column: 36 }, + end_position: Point { row: 1, column: 46 }, }, - CSTNode::NonTerminal { - kind: "interface_body".into(), + NonTerminal { + kind: "interface_body", children: vec![ - CSTNode::Terminal { - kind: "{".into(), + Terminal { + kind: "{", value: "{".into(), + start_position: Point { row: 1, column: 47 }, + end_position: Point { row: 1, column: 48 }, }, - CSTNode::NonTerminal { - kind: "method_declaration".into(), + NonTerminal { + kind: "method_declaration", children: vec![ - CSTNode::Terminal { - kind: "void_type".into(), + Terminal { + kind: "void_type", value: "void".into(), + start_position: Point { row: 2, column: 16 }, + end_position: Point { row: 2, column: 20 }, }, - CSTNode::Terminal { - kind: "identifier".into(), + Terminal { + kind: "identifier", value: "sayHello".into(), + start_position: Point { row: 2, column: 21 }, + end_position: Point { row: 2, column: 29 }, }, - CSTNode::NonTerminal { - kind: "formal_parameters".into(), + NonTerminal { + kind: "formal_parameters", children: vec![ - CSTNode::Terminal { - kind: "(".into(), + Terminal { + kind: "(", value: "(".into(), + start_position: Point { row: 2, column: 29 }, + end_position: Point { row: 2, column: 30 }, }, - CSTNode::NonTerminal { - kind: "formal_parameter".into(), + NonTerminal { + kind: "formal_parameter", children: vec![ - CSTNode::Terminal { - kind: "type_identifier".into(), + Terminal { + kind: "type_identifier", value: "String".into(), + start_position: Point { + row: 2, + column: 30, + }, + end_position: Point { row: 2, column: 36 }, }, - CSTNode::Terminal { - kind: "identifier".into(), + Terminal { + kind: "identifier", value: "name".into(), + start_position: Point { + row: 2, + column: 37, + }, + end_position: Point { row: 2, column: 41 }, }, ], + start_position: Point { row: 2, column: 30 }, + end_position: Point { row: 2, column: 41 }, }, - CSTNode::Terminal { - kind: ")".into(), + Terminal { + kind: ")", value: ")".into(), + start_position: Point { row: 2, column: 41 }, + end_position: Point { row: 2, column: 42 }, }, ], + start_position: Point { row: 2, column: 29 }, + end_position: Point { row: 2, column: 42 }, }, - CSTNode::Terminal { - kind: ";".into(), + Terminal { + kind: ";", value: ";".into(), + start_position: Point { row: 2, column: 42 }, + end_position: Point { row: 2, column: 43 }, }, ], + start_position: Point { row: 2, column: 16 }, + end_position: Point { row: 2, column: 43 }, }, - CSTNode::Terminal { - kind: "}".into(), + Terminal { + kind: "}", value: "}".into(), + start_position: Point { row: 3, column: 12 }, + end_position: Point { row: 3, column: 13 }, }, ], + start_position: Point { row: 1, column: 47 }, + end_position: Point { row: 3, column: 13 }, }, ], + start_position: Point { row: 1, column: 12 }, + end_position: Point { row: 3, column: 13 }, }], + start_position: Point { row: 1, column: 12 }, + end_position: Point { row: 4, column: 8 }, }; assert_eq!(expected, result.unwrap()) } @@ -149,38 +202,54 @@ mod tests { }; let result = parse_string(code, &parser_configuration); - let expected = CSTNode::NonTerminal { - kind: "program".into(), - children: vec![CSTNode::NonTerminal { - kind: "interface_declaration".into(), + let expected = NonTerminal { + kind: "program", + children: vec![NonTerminal { + kind: "interface_declaration", children: vec![ - CSTNode::NonTerminal { - kind: "modifiers".into(), + NonTerminal { + kind: "modifiers", children: vec![ - CSTNode::Terminal { - kind: "public".into(), + Terminal { + kind: "public", value: "public".into(), + start_position: Point { row: 0, column: 0 }, + end_position: Point { row: 0, column: 6 }, }, - CSTNode::Terminal { - kind: "static".into(), + Terminal { + kind: "static", value: "static".into(), + start_position: Point { row: 0, column: 7 }, + end_position: Point { row: 0, column: 13 }, }, ], + start_position: Point { row: 0, column: 0 }, + end_position: Point { row: 0, column: 13 }, }, - CSTNode::Terminal { - kind: "interface".into(), + Terminal { + kind: "interface", value: "interface".into(), + start_position: Point { row: 0, column: 14 }, + end_position: Point { row: 0, column: 23 }, }, - CSTNode::Terminal { - kind: "identifier".into(), + Terminal { + kind: "identifier", value: "HelloWorld".into(), + start_position: Point { row: 0, column: 24 }, + end_position: Point { row: 0, column: 34 }, }, - CSTNode::Terminal { - kind: "interface_body".into(), + Terminal { + kind: "interface_body", value: "{void sayHello(String name);}".into(), + start_position: Point { row: 0, column: 35 }, + end_position: Point { row: 0, column: 64 }, }, ], + start_position: Point { row: 0, column: 0 }, + end_position: Point { row: 0, column: 64 }, }], + start_position: Point { row: 0, column: 0 }, + end_position: Point { row: 0, column: 64 }, }; assert_eq!(expected, result.unwrap()) }