use pest::{self, Parser};
use multistage::MultiStage;
use std::{usize, u16, u32};
use std::fs::File;
use std::io::Read;
use std::fmt;
#[derive(Parser)]
#[grammar = "syntax.pest"]
struct TableParser;
type Nodes<'a> = pest::iterators::Pairs<'a, Rule>;
type Node<'a> = pest::iterators::Pair<'a, Rule>;
pub struct WeightEntry {
pub weights: [u16; 4],
}
impl fmt::Debug for WeightEntry {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "[")?;
for weight in &self.weights {
write!(f, ".{:04X}", weight)?;
}
write!(f, "]")?;
Ok(())
}
}
pub struct CollationTable {
weights: Vec<WeightEntry>,
table: MultiStage<PrimaryEntry>,
}
impl CollationTable {
fn new() -> Self {
Self {
weights: Vec::new(),
table: MultiStage::new(8),
}
}
pub fn from_text(data: &str) -> CollationTable {
let mut table: Nodes = TableParser::parse(Rule::table, data).unwrap();
let table = table.next().unwrap();
let entries = table
.into_inner()
.skip_while(|p| p.as_rule() != Rule::entry);
let mut max_contr = 0;
let mut table = CollationTable::new();
for entry in entries {
let mut it = entry.into_inner();
let mut codepoints = it.next().unwrap().into_inner();
let elements = it.next().unwrap();
let weights_idx = table.weights.len();
table.weights.extend(elements.into_inner().map(scan_weight));
let first = u32::from_str_radix(codepoints.next().unwrap().as_str(), 16).unwrap();
let entry = table.table.entry(first);
if codepoints.next().is_some() {
entry.contraction += 1;
max_contr = max_contr.max(entry.contraction);
} else {
debug_assert_eq!(entry.weights, usize::MAX);
entry.weights = weights_idx;
entry.len = (table.weights.len() - weights_idx) as u8;
}
}
table
}
pub fn from_text_file(path: &str) -> CollationTable {
let mut file = File::open(path).unwrap();
let mut buf = String::new();
file.read_to_string(&mut buf).unwrap();
CollationTable::from_text(&buf)
}
pub fn resolve(&self, c: char) -> &[WeightEntry] {
self.table
.get(c as u32)
.and_then(|e| {
if e.weights == usize::MAX {
None
} else {
Some(&self.weights[e.weights..e.weights + (e.len as usize)])
}
})
.unwrap_or(&[])
}
}
#[derive(Clone)]
struct PrimaryEntry {
weights: usize,
len: u8,
contraction: u16,
}
impl Default for PrimaryEntry {
fn default() -> Self {
Self {
weights: usize::MAX,
len: 0,
contraction: 0,
}
}
}
fn scan_weight(node: Node) -> WeightEntry {
debug_assert_eq!(node.as_rule(), Rule::element);
let mut it = node.into_inner();
let _alt = it.next().unwrap();
let mut weights = [0; 4];
for i in 0..4 {
weights[i] = it.next()
.map(|r| u16::from_str_radix(r.as_str(), 16).unwrap())
.unwrap_or(0);
}
WeightEntry { weights }
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
pub fn parse() {
let _table = CollationTable::from_text_file("data/allkeys.txt");
}
}