// Dotstring Parse โ 99 Problems #40
// Parse a dotstring into a binary tree and verify round-trips.
// This focuses on the parsing direction and error handling.
#[derive(Debug, PartialEq, Clone)]
enum Tree {
Leaf,
Node(Box<Tree>, char, Box<Tree>),
}
impl Tree {
fn leaf() -> Self { Tree::Leaf }
fn node(l: Tree, v: char, r: Tree) -> Self {
Tree::Node(Box::new(l), v, Box::new(r))
}
}
#[derive(Debug, PartialEq)]
enum ParseError {
UnexpectedEnd,
TrailingChars(usize),
}
/// Parse a dot-string with proper error handling.
fn parse_dotstring(s: &str) -> Result<Tree, ParseError> {
let chars: Vec<char> = s.chars().collect();
let (tree, consumed) = parse_inner(&chars, 0)?;
if consumed != chars.len() {
Err(ParseError::TrailingChars(consumed))
} else {
Ok(tree)
}
}
fn parse_inner(chars: &[char], pos: usize) -> Result<(Tree, usize), ParseError> {
if pos >= chars.len() {
return Err(ParseError::UnexpectedEnd);
}
let c = chars[pos];
if c == '.' {
Ok((Tree::leaf(), pos + 1))
} else {
let (left, pos2) = parse_inner(chars, pos + 1)?;
let (right, pos3) = parse_inner(chars, pos2)?;
Ok((Tree::node(left, c, right), pos3))
}
}
fn tree_to_dotstring(tree: &Tree) -> String {
match tree {
Tree::Leaf => ".".to_string(),
Tree::Node(l, v, r) => format!("{}{}{}", v, tree_to_dotstring(l), tree_to_dotstring(r)),
}
}
fn sample_tree() -> Tree {
Tree::node(
Tree::node(
Tree::node(Tree::leaf(), 'd', Tree::leaf()),
'b',
Tree::node(Tree::leaf(), 'e', Tree::leaf()),
),
'a',
Tree::node(Tree::leaf(), 'c', Tree::node(Tree::leaf(), 'f', Tree::leaf())),
)
}
fn main() {
let t = sample_tree();
let s = tree_to_dotstring(&t);
println!("Dotstring: {}", s);
match parse_dotstring(&s) {
Ok(parsed) => println!("Parsed OK: {}", parsed == t),
Err(e) => println!("Error: {:?}", e),
}
// Error cases
println!("Parse '': {:?}", parse_dotstring(""));
println!("Parse 'x..extra': {:?}", parse_dotstring("x..extra"));
println!("Parse '.': {:?}", parse_dotstring("."));
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_parse_leaf() {
assert_eq!(parse_dotstring("."), Ok(Tree::leaf()));
}
#[test]
fn test_parse_single_node() {
assert_eq!(
parse_dotstring("x.."),
Ok(Tree::node(Tree::leaf(), 'x', Tree::leaf()))
);
}
#[test]
fn test_round_trip() {
let t = sample_tree();
let s = tree_to_dotstring(&t);
assert_eq!(parse_dotstring(&s), Ok(t));
}
#[test]
fn test_parse_empty_error() {
assert_eq!(parse_dotstring(""), Err(ParseError::UnexpectedEnd));
}
#[test]
fn test_parse_trailing_chars() {
// "x.." parses a single node, then "xx" is trailing
assert_eq!(
parse_dotstring("x..extra"),
Err(ParseError::TrailingChars(3))
);
}
}
(* Dotstring Parse *)
(* OCaml 99 Problems #40 *)
(* Implementation for example 40 *)
(* Tests *)
let () =
(* Add tests *)
print_endline "โ OCaml tests passed"