Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(matching): Add initial implementation for Unordered Tree Matching algorithm #16

Merged
merged 12 commits into from
Nov 9, 2023
3 changes: 3 additions & 0 deletions Cargo.lock

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

58 changes: 39 additions & 19 deletions bin/src/main.rs
Original file line number Diff line number Diff line change
@@ -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 create(Pessoa pessoa);
void delete(Pessoa pessoa);
void remove(Pessoa pessoa);
void insert(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();
Expand Down
7 changes: 6 additions & 1 deletion matching/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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)
}
}
9 changes: 9 additions & 0 deletions matching/src/matching_entry.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,12 @@ impl MatchingEntry {
};
}
}

impl Default for &MatchingEntry {
fn default() -> Self {
&MatchingEntry {
score: 0,
is_perfect_match: false,
}
}
}
6 changes: 6 additions & 0 deletions matching/src/matchings.rs
Original file line number Diff line number Diff line change
Expand Up @@ -82,13 +82,17 @@ impl<'a> IntoIterator for Matchings<'a> {

#[cfg(test)]
mod tests {
use model::Point;

use super::*;

#[test]
fn returns_none_if_a_matching_for_the_node_is_not_found() {
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))
Expand All @@ -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();
Expand Down
62 changes: 60 additions & 2 deletions matching/src/ordered_tree_matching.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand All @@ -44,7 +46,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 {
Expand Down Expand Up @@ -94,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;
Expand All @@ -116,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);
Expand All @@ -142,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);
Expand All @@ -161,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);
Expand All @@ -180,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);
Expand All @@ -199,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()],
};

Expand All @@ -222,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);
Expand All @@ -250,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],
};

Expand All @@ -277,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()],
};

Expand All @@ -301,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()],
};

Expand Down
Loading
Loading