Skip to content

Commit

Permalink
feat(matching): Add initial implementation for Unordered Tree Matchin…
Browse files Browse the repository at this point in the history
…g algorithm (#16)
  • Loading branch information
jpedroh authored Nov 9, 2023
1 parent 44d7159 commit c56ed8c
Show file tree
Hide file tree
Showing 12 changed files with 731 additions and 82 deletions.
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

0 comments on commit c56ed8c

Please sign in to comment.