use std::sync::Arc;
use crate::error::NewickError;
use crate::node::Node;
use crate::prelude::*;
fn is_delimiter(c: char) -> bool {
c.is_whitespace() || matches!(c, '(' | ')' | '[' | ']' | ',' | ':' | ';' | '\'')
}
struct Scanner<'a> {
src: &'a str,
pos: usize,
}
impl<'a> Scanner<'a> {
fn new(src: &'a str) -> Self {
Scanner { src, pos: 0 }
}
fn rest(&self) -> &'a str {
&self.src[self.pos..]
}
fn peek(&self) -> Option<char> {
self.rest().chars().next()
}
fn bump(&mut self) -> Option<char> {
let c = self.peek()?;
self.pos += c.len_utf8();
Some(c)
}
fn skip_trivia(&mut self, sink: &mut String) -> Result<(), NewickError> {
loop {
let trimmed = self.rest().trim_start();
self.pos = self.src.len() - trimmed.len();
if self.rest().starts_with('[') {
let start = self.pos;
match self.rest().find(']') {
Some(rel) => {
let end = self.pos + rel + ']'.len_utf8();
sink.push_str(&self.src[start..end]);
self.pos = end;
}
None => return Err(NewickError::UnterminatedComment { idx: start }),
}
} else {
return Ok(());
}
}
}
fn read_token(&mut self) -> &'a str {
let start = self.pos;
while let Some(c) = self.peek() {
if is_delimiter(c) {
break;
}
self.pos += c.len_utf8();
}
&self.src[start..self.pos]
}
fn read_quoted(&mut self) -> Result<String, NewickError> {
let start = self.pos;
self.bump(); let mut out = String::new();
loop {
match self.bump() {
None => return Err(NewickError::UnterminatedQuote { idx: start }),
Some('\'') => {
if self.peek() == Some('\'') {
self.bump();
out.push('\'');
} else {
return Ok(out);
}
}
Some(c) => out.push(c),
}
}
}
}
fn commit_annotation<T, W, Z, H>(
tree: &mut SimpleRootedTree<T, W, Z>,
id: TreeNodeID<SimpleRootedTree<T, W, Z>>,
raw: &mut String,
handler: &H,
) where
T: NodeTaxa,
W: EdgeWeight,
Z: NodeWeight,
H: AnnotationHandler,
{
if raw.is_empty() {
return;
}
if let Some(stored) = handler.handle(raw) {
if let Some(node) = tree.get_node_mut(id) {
let combined = match node.get_annotation() {
Some(existing) => Arc::from(format!("{existing}{stored}")),
None => stored,
};
node.set_annotation(Some(combined));
}
}
raw.clear();
}
pub(crate) fn parse_newick<T, W, Z, H>(
src: &str,
handler: &H,
) -> Result<SimpleRootedTree<T, W, Z>, NewickError>
where
T: NodeTaxa,
W: EdgeWeight,
Z: NodeWeight,
H: AnnotationHandler,
{
type Id<T, W, Z> = TreeNodeID<SimpleRootedTree<T, W, Z>>;
let mut sc = Scanner::new(src);
let mut tree = SimpleRootedTree::new(0);
let mut current: Id<T, W, Z> = tree.get_root_id();
let mut stack: Vec<Id<T, W, Z>> = Vec::new();
let mut saw_content = false;
let mut raw = String::new();
loop {
sc.skip_trivia(&mut raw)?;
let c = match sc.peek() {
None => break,
Some(';') => break,
Some(c) => c,
};
match c {
'(' => {
sc.bump();
commit_annotation(&mut tree, current, &mut raw, handler);
stack.push(current);
let child = tree.next_id();
tree.set_node(Node::new(child));
tree.set_child(current, child);
current = child;
saw_content = true;
}
',' => {
sc.bump();
commit_annotation(&mut tree, current, &mut raw, handler);
let parent = *stack
.last()
.ok_or(NewickError::UnbalancedParens { idx: sc.pos })?;
let sibling = tree.next_id();
tree.set_node(Node::new(sibling));
tree.set_child(parent, sibling);
current = sibling;
}
')' => {
sc.bump();
commit_annotation(&mut tree, current, &mut raw, handler);
current = stack
.pop()
.ok_or(NewickError::UnbalancedParens { idx: sc.pos })?;
}
':' => {
sc.bump();
sc.skip_trivia(&mut raw)?;
let weight = sc
.read_token()
.parse::<TreeNodeWeight<SimpleRootedTree<T, W, Z>>>();
if let Some(node) = tree.get_node_mut(current) {
node.set_weight(weight.ok());
}
}
'\'' => {
let label = sc.read_quoted()?;
tree.set_node_taxa(current, T::from_str(&label).ok());
saw_content = true;
}
_ => {
let label = sc.read_token();
if label.is_empty() {
return Err(NewickError::InvalidCharacter { idx: sc.pos });
}
tree.set_node_taxa(current, T::from_str(label).ok());
saw_content = true;
}
}
}
commit_annotation(&mut tree, current, &mut raw, handler);
if !stack.is_empty() {
return Err(NewickError::UnbalancedParens { idx: sc.pos });
}
if !saw_content {
return Err(NewickError::Empty);
}
Ok(tree)
}