use super::{Kind, Node, NodeOrToken, Token, stack::Stack};
pub(crate) struct Cursor<'a> {
pos: usize,
current: NodeRef<'a>,
parents: Stack<NodeRef<'a>, 4>,
}
struct NodeRef<'a> {
node: &'a Node,
fresh: bool,
idx: usize,
}
impl<'a> Cursor<'a> {
pub fn new(root: &'a Node) -> Self {
Cursor {
pos: root.abs_pos as usize,
current: NodeRef {
node: root,
fresh: true,
idx: 0,
},
parents: Stack::default(),
}
}
#[allow(dead_code)] pub fn depth(&self) -> usize {
self.parents.len()
}
pub fn pos(&self) -> usize {
self.pos
}
pub fn parent_kind(&self) -> Kind {
self.current.node.kind
}
pub fn next_token(&mut self) -> Option<&'a Token> {
loop {
let current = self.current();
self.advance();
match current {
Some(NodeOrToken::Node(_)) => (),
Some(NodeOrToken::Token(t)) => return Some(t),
None => break None,
}
}
}
pub fn step_over(&mut self) {
let len = self.current().map(NodeOrToken::text_len).unwrap_or(0);
self.current.advance();
self.pos += len;
}
pub fn advance(&mut self) {
self.pos += self.text_len_if_at_token().unwrap_or(0);
match self.current() {
Some(NodeOrToken::Token(_)) => {
self.current.advance();
}
Some(NodeOrToken::Node(node)) => {
if self.current.fresh {
self.descend(node);
} else {
self.current.advance();
}
}
None => (),
}
if self.current().is_none() {
assert!(self.current.is_done());
while self.current.is_done() {
match self.parents.pop() {
Some(parent) => self.current = parent,
None => return,
}
self.current.advance();
}
}
}
fn text_len_if_at_token(&self) -> Option<usize> {
match self.current()? {
NodeOrToken::Token(t) => Some(t.text.len()),
_ => None,
}
}
pub fn current(&self) -> Option<&'a NodeOrToken> {
self.current.current()
}
fn descend(&mut self, node: &'a Node) {
let new_current = NodeRef {
node,
fresh: true,
idx: 0,
};
let mut prev = std::mem::replace(&mut self.current, new_current);
prev.fresh = false;
self.parents.push(prev);
}
pub fn descend_current(&mut self) {
let new_current = self
.current()
.and_then(NodeOrToken::as_node)
.expect("descend_current expects current to be Node");
self.descend(new_current)
}
pub fn ascend(&mut self) {
let len = self.current.prev_items_len();
if let Some(parent) = self.parents.pop() {
self.current = parent;
}
self.pos -= len;
}
}
impl<'a> NodeRef<'a> {
fn current(&self) -> Option<&'a NodeOrToken> {
self.node.children.get(self.idx)
}
fn children(&self) -> &[NodeOrToken] {
self.node.children.as_ref()
}
fn advance(&mut self) -> Option<&'a NodeOrToken> {
self.fresh = true;
let idx = self.idx;
self.idx += 1;
self.node.children.get(idx)
}
fn prev_items_len(&self) -> usize {
self.children()[..self.idx]
.iter()
.map(NodeOrToken::text_len)
.sum()
}
fn is_done(&self) -> bool {
self.idx >= self.node.children.len()
}
}
impl std::fmt::Debug for NodeRef<'_> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
std::fmt::Debug::fmt(self.node, f)
}
}
#[cfg(test)]
mod tests {
use super::*;
static SAMPLE_FEA: &str = include_str!("../../test-data/fonttools-tests/mini.fea");
#[test]
fn abs_positions() {
let (ast, errs) = crate::parse::parse_string(SAMPLE_FEA);
assert!(errs.is_empty());
let mut last_end = 0;
for token in ast.root().iter_tokens() {
assert_eq!(
token.range().start,
last_end,
"{:?}: '{}'",
token.range(),
token.text
);
last_end = token.range().end;
}
}
#[test]
fn ascend_jump() {
let (ast, _errs) = crate::parse::parse_string(SAMPLE_FEA);
let mut cursor = ast.root().cursor();
cursor.advance();
cursor.advance();
cursor.advance();
cursor.advance();
cursor.advance();
cursor.advance();
let pre_descent = cursor.pos();
cursor.descend_current();
cursor.ascend();
assert_eq!(cursor.pos(), pre_descent);
cursor.descend_current();
cursor.advance();
cursor.ascend();
assert_eq!(cursor.pos(), pre_descent);
}
fn at_node(cursor: &Cursor, kind: Kind) -> bool {
cursor
.current()
.and_then(NodeOrToken::as_node)
.map(|n| n.kind == kind)
.unwrap_or(false)
}
fn at_token(cursor: &Cursor, kind: Kind) -> bool {
cursor
.current()
.and_then(NodeOrToken::as_token)
.map(|n| n.kind == kind)
.unwrap_or(false)
}
#[test]
fn advance() {
let (ast, errs) = crate::parse::parse_string("feature kern { pos a b -20; }kern;");
assert!(errs.is_empty());
let mut cursor = ast.root().cursor();
assert!(
at_node(&cursor, Kind::FeatureNode),
"{:?}",
cursor.current()
);
cursor.advance();
assert!(at_token(&cursor, Kind::FeatureKw), "{:?}", cursor.current());
cursor.advance();
assert!(at_token(&cursor, Kind::Whitespace));
cursor.advance();
assert!(at_token(&cursor, Kind::Tag));
cursor.advance();
cursor.advance();
assert!(at_token(&cursor, Kind::LBrace), "{:?}", cursor.current());
cursor.advance();
assert!(
at_token(&cursor, Kind::Whitespace),
"{:?}",
cursor.current()
);
cursor.advance();
assert!(at_node(&cursor, Kind::GposType2), "{:?}", cursor.current());
cursor.advance();
cursor.advance();
cursor.advance();
cursor.advance();
cursor.advance();
assert!(at_token(&cursor, Kind::GlyphName), "{:?}", cursor.current());
cursor.advance();
cursor.advance();
assert!(
at_node(&cursor, Kind::ValueRecordNode),
"{:?}",
cursor.current()
);
cursor.advance();
assert!(at_token(&cursor, Kind::Number), "{:?}", cursor.current());
cursor.advance();
assert!(at_token(&cursor, Kind::Semi), "{:?}", cursor.current());
}
}