mod build;
pub mod fuse;
mod layout;
pub mod lexer_dfa;
use std::collections::BTreeMap;
use crate::analysis::AnalyzedGrammar;
use crate::grammar::ir::TokenPattern;
pub use lexer_dfa::DfaTable;
pub type StateId = u32;
pub type FirstSetId = u32;
pub type SyncSetId = u32;
pub type LookaheadSeq = Vec<i16>;
pub type FirstSet = Vec<LookaheadSeq>;
pub type FirstSetPool = Vec<FirstSet>;
pub type SyncSet = Vec<i16>;
pub type SyncSetPool = Vec<SyncSet>;
pub const TERMINATED: StateId = u32::MAX;
#[derive(Clone, Debug)]
pub struct StateTable {
pub grammar_name: String,
pub tokens: Vec<TokenInfo>,
pub rule_kinds: Vec<String>,
pub first_sets: FirstSetPool,
pub sync_sets: SyncSetPool,
pub states: BTreeMap<StateId, State>,
pub entry_states: Vec<(String, StateId)>,
pub eof_id: i16,
pub error_id: i16,
pub k: usize,
pub lexer_dfa: DfaTable,
}
#[derive(Clone, Debug)]
pub struct TokenInfo {
pub name: String,
pub pattern: TokenPattern,
pub skip: bool,
pub kind: i16,
}
#[derive(Clone, Debug)]
pub enum Op {
Enter(u16),
Exit(u16),
Expect {
kind: i16,
token_name: String,
sync: SyncSetId,
},
PushRet(StateId),
Jump(StateId),
Ret,
Star {
first: FirstSetId,
body: StateId,
next: StateId,
},
Opt {
first: FirstSetId,
body: StateId,
next: StateId,
},
Dispatch {
tree: DispatchTree,
sync: SyncSetId,
next: StateId,
},
}
#[derive(Clone, Copy, Debug)]
pub enum DispatchLeaf {
Arm(StateId),
Fallthrough,
Error,
}
#[derive(Clone, Debug)]
pub enum DispatchTree {
Leaf(DispatchLeaf),
Switch {
depth: u8,
arms: Vec<(i16, DispatchTree)>,
default: DispatchLeaf,
},
}
pub fn build_dispatch_tree(
arms: &[(FirstSetId, StateId)],
first_sets: &[FirstSet],
has_eps: bool,
) -> DispatchTree {
let outer_default = if has_eps {
DispatchLeaf::Fallthrough
} else {
DispatchLeaf::Error
};
let mut entries: Vec<(Vec<i16>, StateId)> = Vec::new();
for (fid, target) in arms {
for seq in &first_sets[*fid as usize] {
entries.push((seq.clone(), *target));
}
}
build_trie(&entries, 0, outer_default)
}
fn build_trie(
entries: &[(Vec<i16>, StateId)],
depth: usize,
outer_default: DispatchLeaf,
) -> DispatchTree {
let mut surviving: Vec<(Vec<i16>, StateId)> = Vec::new();
let mut terminal: Option<DispatchLeaf> = None;
for entry in entries {
if entry.0.len() == depth {
terminal = Some(DispatchLeaf::Arm(entry.1));
break;
}
surviving.push(entry.clone());
}
let node_default = terminal.unwrap_or(outer_default);
if surviving.is_empty() {
return DispatchTree::Leaf(node_default);
}
use std::collections::BTreeMap;
let mut by_kind: BTreeMap<i16, Vec<(Vec<i16>, StateId)>> = BTreeMap::new();
for entry in &surviving {
by_kind
.entry(entry.0[depth])
.or_default()
.push(entry.clone());
}
let arms: Vec<(i16, DispatchTree)> = by_kind
.into_iter()
.map(|(k, subentries)| (k, build_trie(&subentries, depth + 1, node_default)))
.collect();
DispatchTree::Switch {
depth: depth as u8,
arms,
default: node_default,
}
}
#[derive(Clone, Debug, Default)]
pub struct State {
pub id: StateId,
pub label: String,
pub ops: Vec<Op>,
}
pub fn lower(ag: &AnalyzedGrammar) -> StateTable {
let program = build::build(ag);
let mut table = layout::layout(program, ag);
fuse::fuse(&mut table);
table
}
impl State {
pub fn comment(&self) -> String {
let body = if self.ops.is_empty() {
"<empty>".to_string()
} else {
self.ops
.iter()
.map(format_op)
.collect::<Vec<_>>()
.join(" ; ")
};
format!("{} {}", self.label, body)
}
}
fn format_op(op: &Op) -> String {
match op {
Op::Enter(k) => format!("Enter({})", k),
Op::Exit(k) => format!("Exit({})", k),
Op::Expect {
kind, token_name, ..
} => format!("Expect({} /*{}*/)", kind, token_name),
Op::PushRet(r) => format!("PushRet({})", r),
Op::Jump(n) => format!("Jump({})", n),
Op::Ret => "Ret".to_string(),
Op::Star { body, .. } => format!("Star -> {}", body),
Op::Opt { body, .. } => format!("Opt -> {}", body),
Op::Dispatch { tree, .. } => format!("Dispatch[{}]", dispatch_tree_shape(tree)),
}
}
fn dispatch_tree_shape(tree: &DispatchTree) -> String {
let (leaves, depth) = tree_metrics(tree);
format!("{} leaves, depth {}", leaves, depth)
}
fn tree_metrics(tree: &DispatchTree) -> (usize, usize) {
match tree {
DispatchTree::Leaf(_) => (1, 0),
DispatchTree::Switch { arms, .. } => {
let (mut leaf_count, mut max_depth) = (1, 1);
for (_, sub) in arms {
let (l, d) = tree_metrics(sub);
leaf_count += l;
if d + 1 > max_depth {
max_depth = d + 1;
}
}
(leaf_count, max_depth)
}
}
}