use core::fmt;
use core::hash::{Hash, Hasher};
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct Span {
pub start: u32,
pub end: u32,
}
impl Span {
#[must_use]
pub const fn new(start: u32, end: u32) -> Self {
Self { start, end }
}
#[must_use]
pub const fn range(self) -> core::ops::Range<usize> {
self.start as usize..self.end as usize
}
}
#[derive(Clone, Copy, Debug, Default)]
pub struct Ignored<T>(
pub T,
);
impl<T> PartialEq for Ignored<T> {
fn eq(&self, _other: &Self) -> bool {
true
}
}
impl<T> Eq for Ignored<T> {}
impl<T> Hash for Ignored<T> {
fn hash<H: Hasher>(&self, _state: &mut H) {}
}
#[derive(Clone, Debug)]
pub struct RuleName(String);
impl RuleName {
#[must_use]
pub fn new(name: impl Into<String>) -> Self {
Self(name.into())
}
#[must_use]
pub fn as_str(&self) -> &str {
&self.0
}
#[must_use]
pub fn key(&self) -> String {
self.0.to_ascii_lowercase()
}
}
impl PartialEq for RuleName {
fn eq(&self, other: &Self) -> bool {
self.0.eq_ignore_ascii_case(&other.0)
}
}
impl Eq for RuleName {}
impl Hash for RuleName {
fn hash<H: Hasher>(&self, state: &mut H) {
for byte in self.0.as_bytes() {
state.write_u8(byte.to_ascii_lowercase());
}
state.write_u8(0xff);
}
}
impl fmt::Display for RuleName {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(&self.0)
}
}
#[derive(Clone, Debug, Default, PartialEq, Eq)]
pub struct ParseOptions {
pub strict_crlf: bool,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub enum DefinedAs {
Base,
Incremental,
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct Definition {
pub name: RuleName,
pub defined_as: DefinedAs,
pub body: Element,
pub span: Ignored<Span>,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub struct Repeat {
pub min: u64,
pub max: Option<u64>,
}
impl Repeat {
#[must_use]
pub const fn exactly(n: u64) -> Self {
Self {
min: n,
max: Some(n),
}
}
#[must_use]
pub const fn at_least(n: u64) -> Self {
Self { min: n, max: None }
}
#[must_use]
pub const fn bounded(min: u64, max: u64) -> Self {
Self {
min,
max: Some(max),
}
}
#[must_use]
pub const fn is_unbounded(&self) -> bool {
self.max.is_none()
}
#[must_use]
pub const fn is_valid(&self) -> bool {
match self.max {
Some(max) => self.min <= max,
None => true,
}
}
#[must_use]
pub const fn is_never(&self) -> bool {
matches!(self.max, Some(0))
}
}
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub struct CharVal {
pub value: String,
pub case_sensitive: bool,
}
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub enum NumVal {
Scalar(u64),
Range {
lo: u64,
hi: u64,
},
Concat(Vec<u64>),
}
const MAX_SCALAR: u64 = 0x10_FFFF;
const SURROGATES: (u64, u64) = (0xD800, 0xDFFF);
const fn overlap(lo: u64, hi: u64, a: u64, b: u64) -> u64 {
let start = if lo > a { lo } else { a };
let end = if hi < b { hi } else { b };
if start > end { 0 } else { end - start + 1 }
}
impl NumVal {
#[must_use]
pub fn is_representable(&self) -> bool {
match self {
Self::Scalar(value) => is_scalar(*value),
Self::Range { lo, hi } => lo <= hi && Self::scalars_in(*lo, *hi) > 0,
Self::Concat(values) => values.iter().copied().all(is_scalar),
}
}
#[must_use]
pub const fn scalars_in(lo: u64, hi: u64) -> u64 {
if lo > hi {
return 0;
}
overlap(lo, hi, 0, MAX_SCALAR) - overlap(lo, hi, SURROGATES.0, SURROGATES.1)
}
#[must_use]
pub fn nth_scalar(lo: u64, hi: u64, index: u64) -> Option<char> {
if index >= Self::scalars_in(lo, hi) {
return None;
}
let below = overlap(lo, hi, 0, SURROGATES.0 - 1);
let value = if index < below {
lo + index
} else {
SURROGATES.1 + 1 + (index - below)
};
char::from_u32(u32::try_from(value).ok()?)
}
}
const fn is_scalar(value: u64) -> bool {
value <= MAX_SCALAR && (value < SURROGATES.0 || value > SURROGATES.1)
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum Element {
Alt(Vec<Element>),
Concat(Vec<Element>),
Repeat {
repeat: Repeat,
body: Box<Element>,
span: Ignored<Span>,
},
Optional(Box<Element>),
RuleRef {
name: RuleName,
span: Ignored<Span>,
},
CharVal(CharVal),
NumVal {
value: NumVal,
span: Ignored<Span>,
},
ProseVal {
text: String,
span: Ignored<Span>,
},
}
#[derive(Clone, Debug)]
pub struct Grammar {
defs: Vec<Definition>,
opts: ParseOptions,
}
impl Grammar {
#[must_use]
pub fn new(defs: Vec<Definition>, opts: ParseOptions) -> Self {
Self { defs, opts }
}
#[must_use]
pub fn definitions(&self) -> &[Definition] {
&self.defs
}
#[must_use]
pub fn into_definitions(self) -> Vec<Definition> {
self.defs
}
#[must_use]
pub fn parse_options(&self) -> &ParseOptions {
&self.opts
}
}
impl PartialEq for Grammar {
fn eq(&self, other: &Self) -> bool {
self.defs == other.defs
}
}
impl Eq for Grammar {}
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct NodeId(u32);
impl NodeId {
#[must_use]
pub const fn from_index(index: u32) -> Self {
Self(index)
}
#[must_use]
pub const fn index(self) -> usize {
self.0 as usize
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum RuleId {
User(u32),
Core(u32),
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct Rule {
pub name: RuleName,
pub body: NodeId,
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum Node {
Alt {
branches: Vec<NodeId>,
},
Concat {
items: Vec<NodeId>,
},
Repeat {
repeat: Repeat,
body: NodeId,
span: Ignored<Span>,
},
Optional {
body: NodeId,
},
RuleRef {
name: RuleName,
span: Ignored<Span>,
},
CharVal(CharVal),
NumVal {
value: NumVal,
span: Ignored<Span>,
},
ProseVal(String),
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum MinLen {
Finite(u64),
Infinite,
}
impl MinLen {
pub const ZERO: Self = Self::Finite(0);
#[must_use]
pub const fn is_finite(self) -> bool {
matches!(self, Self::Finite(_))
}
#[must_use]
pub const fn saturating_add(self, other: Self) -> Self {
match (self, other) {
(Self::Finite(a), Self::Finite(b)) => Self::Finite(a.saturating_add(b)),
_ => Self::Infinite,
}
}
#[must_use]
pub const fn saturating_mul(self, count: u64) -> Self {
if count == 0 {
return Self::ZERO;
}
match self {
Self::Finite(a) => Self::Finite(a.saturating_mul(count)),
Self::Infinite => Self::Infinite,
}
}
#[must_use]
pub const fn min(self, other: Self) -> Self {
match (self, other) {
(Self::Finite(a), Self::Finite(b)) => Self::Finite(if a < b { a } else { b }),
(Self::Finite(a), Self::Infinite) => Self::Finite(a),
(Self::Infinite, other) => other,
}
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub enum Witness {
Branch(u32),
Count(u64),
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn rule_names_compare_case_insensitively() {
assert_eq!(RuleName::new("Foo"), RuleName::new("fOO"));
assert_ne!(RuleName::new("foo"), RuleName::new("foobar"));
assert_eq!(RuleName::new("Foo").as_str(), "Foo");
assert_eq!(RuleName::new("Foo").key(), "foo");
}
#[test]
fn rule_name_hash_agrees_with_eq() {
use std::collections::HashSet;
let mut set = HashSet::new();
set.insert(RuleName::new("DIGIT"));
assert!(set.contains(&RuleName::new("digit")));
}
#[test]
fn spans_never_affect_equality() {
let a = Element::RuleRef {
name: RuleName::new("x"),
span: Ignored(Span::new(0, 1)),
};
let b = Element::RuleRef {
name: RuleName::new("X"),
span: Ignored(Span::new(40, 41)),
};
assert_eq!(a, b);
}
#[test]
fn parse_options_do_not_affect_grammar_equality() {
let def = Definition {
name: RuleName::new("a"),
defined_as: DefinedAs::Base,
body: Element::CharVal(CharVal {
value: "x".into(),
case_sensitive: false,
}),
span: Ignored(Span::default()),
};
let lenient = Grammar::new(vec![def.clone()], ParseOptions::default());
let strict = Grammar::new(vec![def], ParseOptions { strict_crlf: true });
assert_eq!(lenient, strict);
}
#[test]
fn repeat_bounds() {
assert!(Repeat::exactly(3).is_valid());
assert!(Repeat::at_least(3).is_valid());
assert!(Repeat::at_least(3).is_unbounded());
assert!(!Repeat::bounded(5, 2).is_valid());
assert!(Repeat::bounded(0, 0).is_never());
assert!(!Repeat::bounded(0, 1).is_never());
}
#[test]
fn scalars_are_representable_and_surrogates_are_not() {
assert!(NumVal::Scalar(0x41).is_representable());
assert!(NumVal::Scalar(0).is_representable());
assert!(NumVal::Scalar(0x10_FFFF).is_representable());
assert!(
!NumVal::Scalar(0x11_0000).is_representable(),
"above the last scalar"
);
assert!(
!NumVal::Scalar(0xD800).is_representable(),
"a surrogate is not a scalar"
);
assert!(!NumVal::Scalar(0xDFFF).is_representable());
assert!(
NumVal::Scalar(0xE000).is_representable(),
"just past the surrogates"
);
}
#[test]
fn a_range_need_only_contain_one_scalar() {
assert!(NumVal::Range { lo: 0x41, hi: 0x5A }.is_representable());
assert!(
NumVal::Range {
lo: 0xD7FF,
hi: 0xE000
}
.is_representable()
);
assert!(
NumVal::Range {
lo: 0,
hi: 0x10_FFFF
}
.is_representable()
);
assert!(
!NumVal::Range {
lo: 0xD800,
hi: 0xDFFF
}
.is_representable()
);
assert!(
!NumVal::Range {
lo: 0xDC00,
hi: 0xDD00
}
.is_representable()
);
assert!(
!NumVal::Range {
lo: 0x11_0000,
hi: 0x20_0000
}
.is_representable()
);
}
#[test]
fn octet_ranges_stay_representable() {
assert!(NumVal::Range { lo: 0x80, hi: 0xFF }.is_representable());
assert!(NumVal::Range { lo: 0, hi: 0xFF }.is_representable());
}
#[test]
fn a_concatenation_is_representable_only_if_every_element_is() {
assert!(NumVal::Concat(vec![0x41, 0x42, 0x43]).is_representable());
assert!(!NumVal::Concat(vec![0x41, 0xD800]).is_representable());
assert!(!NumVal::Concat(vec![0x11_0000]).is_representable());
}
#[test]
fn counting_scalars_excludes_the_surrogate_block() {
assert_eq!(NumVal::scalars_in(0x41, 0x41), 1);
assert_eq!(NumVal::scalars_in(0x41, 0x5A), 26);
assert_eq!(NumVal::scalars_in(0xD800, 0xDFFF), 0);
assert_eq!(
NumVal::scalars_in(0xD7FF, 0xE000),
2,
"the ends, not the 2048 between"
);
assert_eq!(NumVal::scalars_in(0, 0x10_FFFF), 0x11_0000 - 2048);
assert_eq!(
NumVal::scalars_in(0x10_FFFF, 0x20_0000),
1,
"clamped to the last scalar"
);
assert_eq!(
NumVal::scalars_in(5, 4),
0,
"an inverted range holds nothing"
);
}
#[test]
fn indexing_a_range_steps_over_the_surrogates() {
let at = |lo, hi, i| NumVal::nth_scalar(lo, hi, i).map(u32::from);
assert_eq!(at(0x41, 0x5A, 0), Some(0x41));
assert_eq!(at(0x41, 0x5A, 25), Some(0x5A));
assert_eq!(at(0x41, 0x5A, 26), None, "past the end");
assert_eq!(at(0xD7FF, 0xE000, 0), Some(0xD7FF));
assert_eq!(at(0xD7FF, 0xE000, 1), Some(0xE000));
assert_eq!(at(0xD7FF, 0xE000, 2), None);
assert_eq!(at(0xDC00, 0xE001, 0), Some(0xE000));
assert_eq!(at(0xD800, 0xDFFF, 0), None);
}
#[test]
fn every_index_of_a_range_yields_a_distinct_scalar() {
let (lo, hi) = (0xD7FD, 0xE002);
let count = NumVal::scalars_in(lo, hi);
let scalars: Vec<u32> = (0..count)
.map(|i| u32::from(NumVal::nth_scalar(lo, hi, i).expect("in range")))
.collect();
assert_eq!(scalars, [0xD7FD, 0xD7FE, 0xD7FF, 0xE000, 0xE001, 0xE002]);
}
#[test]
fn min_len_orders_finite_below_infinite() {
assert!(MinLen::Finite(u64::MAX) < MinLen::Infinite);
assert!(MinLen::Finite(1) < MinLen::Finite(2));
assert!(MinLen::ZERO.is_finite());
assert!(!MinLen::Infinite.is_finite());
}
#[test]
fn min_len_arithmetic_saturates() {
assert_eq!(
MinLen::Finite(2).saturating_add(MinLen::Finite(3)),
MinLen::Finite(5)
);
assert_eq!(
MinLen::Finite(u64::MAX).saturating_add(MinLen::Finite(1)),
MinLen::Finite(u64::MAX)
);
assert_eq!(
MinLen::Finite(1).saturating_add(MinLen::Infinite),
MinLen::Infinite
);
assert_eq!(
MinLen::Finite(u64::MAX).saturating_mul(2),
MinLen::Finite(u64::MAX),
"a saturated value is still finite, hence still productive"
);
}
#[test]
fn zero_repetitions_of_an_unproductive_body_match_the_empty_string() {
assert_eq!(MinLen::Infinite.saturating_mul(0), MinLen::ZERO);
assert_eq!(MinLen::Infinite.saturating_mul(1), MinLen::Infinite);
}
#[test]
fn min_picks_the_shorter_alternative() {
assert_eq!(MinLen::Finite(3).min(MinLen::Finite(1)), MinLen::Finite(1));
assert_eq!(MinLen::Infinite.min(MinLen::Finite(7)), MinLen::Finite(7));
assert_eq!(MinLen::Finite(7).min(MinLen::Infinite), MinLen::Finite(7));
assert_eq!(MinLen::Infinite.min(MinLen::Infinite), MinLen::Infinite);
}
}