marktwin 0.5.0

Marktwin format support for Eternaltwin.
Documentation
use crate::syntax::SyntaxKind;
use std::convert::TryFrom;

#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct TokenSet(pub(crate) u64);

impl TokenSet {
  pub const EMPTY: TokenSet = TokenSet(0);

  pub const fn insert_end(self) -> Self {
    Self(self.0 | TokenSet::end_to_bit())
  }

  pub const fn insert_token(self, kind: SyntaxKind) -> Self {
    Self(self.0 | TokenSet::token_to_bit(kind))
  }

  pub const fn insert(self, kind: Option<SyntaxKind>) -> Self {
    match kind {
      Some(kind) => self.insert_token(kind),
      None => self.insert_end(),
    }
  }

  pub const fn contains(self, kind: Option<SyntaxKind>) -> bool {
    let bit = match kind {
      Some(kind) => TokenSet::token_to_bit(kind),
      None => TokenSet::end_to_bit(),
    };
    self.0 & bit != 0
  }

  pub const fn intersection(self, rhs: Self) -> Self {
    Self(self.0 & rhs.0)
  }

  pub const fn is_empty(self) -> bool {
    self.0 == 0
  }

  pub const fn union(self, rhs: Self) -> Self {
    Self(self.0 | rhs.0)
  }

  pub const fn iter(self) -> TokenSetIter {
    TokenSetIter {
      token_set: self,
      bit_index: 0,
    }
  }

  pub(crate) const fn end_to_bit() -> u64 {
    1
  }

  pub(crate) const fn token_to_bit(kind: SyntaxKind) -> u64 {
    let bit_id = kind.into_u16() + 1;
    // TODO: Use const_assert once available
    // assert!(bit_id < 64);
    // TODO: Use `u64::from` once available in const context
    1 << (bit_id as u64)
  }

  pub(crate) fn bit_index_to_token_or_end(bit_index: u16) -> Option<SyntaxKind> {
    match bit_index {
      0 => None,
      x => Some(SyntaxKind::try_from(x - 1).unwrap()),
    }
  }
}

impl std::fmt::Debug for TokenSet {
  fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
    write!(fmt, "TokenSet {{")?;
    let mut is_first = true;
    for token in self.iter() {
      if is_first {
        is_first = false
      } else {
        write!(fmt, ", ")?;
      }
      write!(fmt, "{:?}", token)?;
    }
    write!(fmt, "}}")
  }
}

impl core::ops::BitOr for TokenSet {
  type Output = TokenSet;

  fn bitor(self, rhs: Self) -> Self::Output {
    self.union(rhs)
  }
}

pub struct TokenSetIter {
  token_set: TokenSet,
  bit_index: u16,
}

impl Iterator for TokenSetIter {
  type Item = Option<SyntaxKind>;

  fn next(&mut self) -> Option<Self::Item> {
    while self.bit_index < 64 {
      let old_bit_index = self.bit_index;
      let has_bit = self.token_set.0 & (1u64 << u64::from(old_bit_index)) != 0;
      self.bit_index += 1;
      if has_bit {
        return Some(TokenSet::bit_index_to_token_or_end(old_bit_index));
      }
    }
    None
  }
}

/// Defines a token set.
///
/// The first argument is the name, the remaining arguments are the tokens in the set.
///
/// It is defined as `const` if constant tokens are supported (requires the `const_if_match`
/// feature) otherwise as `static`.
macro_rules! token_set {
  (None) => {
    crate::token_set::TokenSet::EMPTY.insert_end()
  };
  (None, $($token:expr),+) => {
    crate::token_set::TokenSet::EMPTY.insert_end() $(.insert_token($token))*
  };
  ($($token:expr),*) => {
    crate::token_set::TokenSet::EMPTY $(.insert_token($token))*
  };
}

#[test]
fn test_empty() {
  let set = token_set!();
  assert!(!set.contains(None));
  assert!(!set.contains(Some(SyntaxKind::TokenText)));
  assert!(!set.contains(Some(SyntaxKind::TokenSlash)));
  assert!(!set.contains(Some(SyntaxKind::TokenColon)));
}

#[test]
fn test_only_end() {
  let set = token_set!(None);
  assert!(set.contains(None));
  assert!(!set.contains(Some(SyntaxKind::TokenText)));
  assert!(!set.contains(Some(SyntaxKind::TokenSlash)));
  assert!(!set.contains(Some(SyntaxKind::TokenColon)));
}

#[test]
fn test_only_ident() {
  let set = token_set!(SyntaxKind::TokenText);
  assert!(!set.contains(None));
  assert!(set.contains(Some(SyntaxKind::TokenText)));
  assert!(!set.contains(Some(SyntaxKind::TokenSlash)));
  assert!(!set.contains(Some(SyntaxKind::TokenColon)));
}

#[test]
fn test_end_and_ident() {
  let set = token_set!(None, SyntaxKind::TokenText);
  assert!(set.contains(None));
  assert!(set.contains(Some(SyntaxKind::TokenText)));
  assert!(!set.contains(Some(SyntaxKind::TokenSlash)));
  assert!(!set.contains(Some(SyntaxKind::TokenColon)));
}

#[test]
fn test_ident_and_semicolon() {
  let set = token_set!(SyntaxKind::TokenText, SyntaxKind::TokenColon);
  assert!(!set.contains(None));
  assert!(set.contains(Some(SyntaxKind::TokenText)));
  assert!(!set.contains(Some(SyntaxKind::TokenSlash)));
  assert!(set.contains(Some(SyntaxKind::TokenColon)));
}

#[test]
fn test_iter_empty() {
  let set = token_set!();
  let mut it = set.iter();
  assert_eq!(it.next(), None);
}

#[test]
fn test_iter_only_end() {
  let set = token_set!(None);
  let mut it = set.iter();
  assert_eq!(it.next(), Some(None));
  assert_eq!(it.next(), None);
}

#[test]
fn test_iter_ident_and_semicolon() {
  let set = token_set!(SyntaxKind::TokenText, SyntaxKind::TokenColon);
  let mut it = set.iter();
  assert_eq!(it.next(), Some(Some(SyntaxKind::TokenText)));
  assert_eq!(it.next(), Some(Some(SyntaxKind::TokenColon)));
  assert_eq!(it.next(), None);
}