use std::collections::{HashMap, HashSet, VecDeque};
use crate::analysis::AnalyzedGrammar;
use crate::lowering::build::{Block, BlockId, Op, Program, Target};
use crate::lowering::lexer_dfa;
use crate::lowering::{build_dispatch_tree, FirstSet, Op as StateOp, State, StateId, StateTable};
pub fn layout(prog: Program, ag: &AnalyzedGrammar) -> StateTable {
let mut order: Vec<BlockId> = Vec::new();
let mut seen: HashSet<BlockId> = HashSet::new();
for name in &prog.rule_order {
let root = prog.rule_entry[name];
bfs(root, &prog.blocks, &mut order, &mut seen);
}
let mut entry: HashMap<BlockId, StateId> = HashMap::new();
let mut cursor: StateId = 1;
for bid in &order {
entry.insert(*bid, cursor);
cursor += prog.blocks[bid.0 as usize].ops.len() as StateId + 1;
}
let mut states: std::collections::BTreeMap<StateId, State> = std::collections::BTreeMap::new();
let mut next_id: StateId = 1;
for bid in &order {
let block = &prog.blocks[bid.0 as usize];
for (i, op) in block.ops.iter().enumerate() {
let here = next_id;
let fall = here + 1;
states.insert(
here,
State {
id: here,
label: block.op_labels[i].clone(),
ops: lower_op(op, fall, &entry, &prog.rule_entry, &prog.first_sets),
},
);
next_id += 1;
}
states.insert(
next_id,
State {
id: next_id,
label: format!("{}:ret", block.label_prefix),
ops: vec![StateOp::Ret],
},
);
next_id += 1;
}
let entry_states: Vec<(String, StateId)> = prog
.rule_order
.iter()
.filter(|n| prog.public_rule_names.contains(n.as_str()))
.map(|n| (n.clone(), entry[&prog.rule_entry[n]]))
.collect();
let lexer_dfa = lexer_dfa::compile(&prog.tokens);
StateTable {
grammar_name: ag.grammar.name.clone(),
tokens: prog.tokens,
rule_kinds: prog.rules,
first_sets: prog.first_sets,
sync_sets: prog.sync_sets,
states,
entry_states,
eof_id: prog.eof_id,
error_id: prog.error_id,
k: ag.k,
lexer_dfa,
}
}
fn bfs(start: BlockId, blocks: &[Block], order: &mut Vec<BlockId>, seen: &mut HashSet<BlockId>) {
let mut queue: VecDeque<BlockId> = VecDeque::new();
queue.push_back(start);
while let Some(bid) = queue.pop_front() {
if !seen.insert(bid) {
continue;
}
order.push(bid);
for op in &blocks[bid.0 as usize].ops {
visit_block_targets(op, &mut |child| {
if !seen.contains(&child) {
queue.push_back(child);
}
});
}
}
}
fn visit_block_targets(op: &Op, f: &mut dyn FnMut(BlockId)) {
let mut visit = |t: &Target| {
if let Target::Block(b) = t {
f(*b);
}
};
match op {
Op::Enter { .. } | Op::Exit { .. } | Op::Expect { .. } => {}
Op::Call { target } => visit(target),
Op::Opt { body, .. } | Op::Star { body, .. } => visit(body),
Op::Dispatch { arms, .. } => {
for (_, t) in arms {
visit(t);
}
}
}
}
fn lower_op(
op: &Op,
fall: StateId,
entry: &HashMap<BlockId, StateId>,
rules: &HashMap<String, BlockId>,
first_sets: &[FirstSet],
) -> Vec<StateOp> {
match op {
Op::Enter { kind } => vec![StateOp::Enter(*kind), StateOp::Jump(fall)],
Op::Exit { kind } => vec![StateOp::Exit(*kind), StateOp::Jump(fall)],
Op::Expect {
kind,
token_name,
sync,
} => vec![
StateOp::Expect {
kind: *kind,
token_name: token_name.clone(),
sync: *sync,
},
StateOp::Jump(fall),
],
Op::Call { target } => vec![
StateOp::PushRet(fall),
StateOp::Jump(resolve(target, entry, rules)),
],
Op::Opt { first, body } => vec![StateOp::Opt {
first: *first,
body: resolve(body, entry, rules),
next: fall,
}],
Op::Star { first, body } => vec![StateOp::Star {
first: *first,
body: resolve(body, entry, rules),
next: fall,
}],
Op::Dispatch {
arms,
has_eps,
sync,
} => {
let resolved: Vec<(u32, StateId)> = arms
.iter()
.map(|(f, t)| (*f, resolve(t, entry, rules)))
.collect();
let tree = build_dispatch_tree(&resolved, first_sets, *has_eps);
vec![StateOp::Dispatch {
tree,
sync: *sync,
next: fall,
}]
}
}
}
fn resolve(
t: &Target,
entry: &HashMap<BlockId, StateId>,
rules: &HashMap<String, BlockId>,
) -> StateId {
match t {
Target::Block(b) => entry[b],
Target::Rule(n) => entry[&rules[n]],
}
}