From 73168d3c0b51b4e4bfaebd04437e471075cfc541 Mon Sep 17 00:00:00 2001 From: Matthew Gordon Date: Thu, 17 Oct 2024 19:32:57 -0300 Subject: [PATCH] In-progress implementation of rope Just the beginnings of a rope implementation but I want to refactor already so saving what I've got so far. --- Cargo.toml | 6 ++ src/core/mod.rs | 1 + src/core/rope.rs | 198 +++++++++++++++++++++++++++++++++++++++++++++++ src/main.rs | 5 ++ 4 files changed, 210 insertions(+) create mode 100644 Cargo.toml create mode 100644 src/core/mod.rs create mode 100644 src/core/rope.rs create mode 100644 src/main.rs diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..5ef76fc --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,6 @@ +[package] +name = "ged" +version = "0.1.0" +edition = "2021" + +[dependencies] diff --git a/src/core/mod.rs b/src/core/mod.rs new file mode 100644 index 0000000..f67f4f0 --- /dev/null +++ b/src/core/mod.rs @@ -0,0 +1 @@ +pub mod rope; diff --git a/src/core/rope.rs b/src/core/rope.rs new file mode 100644 index 0000000..4a83b8c --- /dev/null +++ b/src/core/rope.rs @@ -0,0 +1,198 @@ +use std::rc::Rc; + +/// [Rope](https://en.wikipedia.org/wiki/Rope_(data_structure)) data structure +/// implementation. +/// +/// This is the main data structure used to store text file contents while they +/// are being edited. +#[derive(Debug)] +pub struct Rope { + /// Total number of bytes in the string contained in the left subtree, or + /// the number of bytes in the string if this is a leaf. + bytes_weight: usize, + + /// Total number of characters in the string contained in the left subtree, + /// or the number of characters in the string if this is a leaf. + chars_weight: usize, + + /// Total number of line endings in the string contained in the left + /// subtree, or the number of line endings in the string if this is a leaf. + lines_weight: usize, + + /// The children (subtrees) if this is a branch node, or the string fragment + /// if this is a leaf. + contents: NodeContents, +} + +#[derive(Debug)] +pub enum NodeContents { + /// The children of a branch Rope node + Children { + /// The root of the left subtree + left: Rc, + /// The root of the right subtree + right: Rc, + }, + /// The string fragment contained in a Rope leaf node + String(String), +} + +impl Rope { + pub fn new(contents: impl Into) -> Rc { + let string = contents.into(); + let bytes_weight = string.len(); + let chars_weight = string.chars().count(); + let lines_weight = string.chars().filter(|&c| c == '\n').count(); + Rc::new(Rope { + bytes_weight, + chars_weight, + lines_weight, + contents: NodeContents::String(string), + }) + } + + pub fn join(left: Rc, right: Rc) -> Rc { + Rc::new(Rope { + bytes_weight: left.total_bytes(), + chars_weight: left.total_chars(), + lines_weight: left.total_lines(), + contents: NodeContents::Children { left, right }, + }) + } + + pub fn total_bytes(&self) -> usize { + if let NodeContents::Children { left, right } = &self.contents { + left.bytes_weight + right.bytes_weight + } else { + self.bytes_weight + } + } + + pub fn total_chars(&self) -> usize { + if let NodeContents::Children { left, right } = &self.contents { + left.chars_weight + right.chars_weight + } else { + self.chars_weight + } + } + + pub fn total_lines(&self) -> usize { + if let NodeContents::Children { left, right } = &self.contents { + left.lines_weight + right.lines_weight + } else { + self.lines_weight + } + } + + pub fn is_leaf(&self) -> bool { + match self.contents { + NodeContents::Children { .. } => false, + NodeContents::String(_) => true, + } + } + + fn iter_nodes(self: Rc) -> NodeIterator { + NodeIterator::new(self) + } + + pub fn iter_chars(self: Rc) -> CharIterator { + CharIterator::new(self) + } +} + +struct NodeIterator { + stack: Vec>, +} + +impl NodeIterator { + fn new(rope: Rc) -> Self { + let mut result = NodeIterator { stack: vec![] }; + result.push_tree(rope); + result + } + + fn push_tree(&mut self, node: Rc) { + let mut curr = node; + self.stack.push(curr.clone()); + while let NodeContents::Children { left, .. } = &curr.contents { + self.stack.push(left.clone()); + curr = left.clone(); + } + } +} + +impl Iterator for NodeIterator { + type Item = Rc; + + fn next(&mut self) -> Option { + let curr = self.stack.pop()?; + if let NodeContents::Children { right, .. } = &curr.contents { + self.push_tree(right.clone()); + self.next() + } else { + Some(curr) + } + } +} + +pub struct CharIterator<'a> { + node_iterator: NodeIterator, + current_node: Rc, + str_iterator: std::str::Chars<'a> +} + +impl<'a> CharIterator<'a> { + fn new(rope: Rc) -> Self{ + let mut node_iterator = NodeIterator::new(rope); + let str_iterator = node_iterator.next().contents + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn node_iterator_for_single_node_returns_node_and_only_node() { + let target = Rope::new("The"); + + let result: Vec<_> = target.iter_nodes().collect(); + assert_eq!(result.len(), 1); + assert_eq!(result[0].bytes_weight, 3); + assert_eq!(result[0].chars_weight, 3); + assert_eq!(result[0].lines_weight, 0); + match &result[0].contents { + NodeContents::String(text) => assert_eq!(text, "The"), + _ => panic!(), + } + } + + #[test] + fn node_iterator_returns_nodes_in_correct_order() { + let strings = vec![ + "abc", "def", "g", "hi", "jklm", "n", "op", "qr", "stuv", "xyz", + ]; + let target = Rope::join( + Rope::join( + Rope::join(Rope::new("abc"), Rope::new("def")), + Rope::join(Rope::new("g"), Rope::new("hi")), + ), + Rope::join( + Rope::join( + Rope::join(Rope::new("jklm"), Rope::new("n")), + Rope::join(Rope::new("op"), Rope::new("qr")), + ), + Rope::join(Rope::new("stuv"), Rope::new("xyz")), + ), + ); + + let result: Vec<_> = target.iter_nodes().collect(); + assert_eq!(result.len(), 10); + for (node, string) in result.iter().zip(strings) { + match &node.contents { + NodeContents::String(text) => assert_eq!(text, string), + _ => panic!(), + } + } + } +} diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..4b419a6 --- /dev/null +++ b/src/main.rs @@ -0,0 +1,5 @@ +mod core; + +fn main() { + println!("Hello, world!"); +}