use super::*;
impl core::fmt::Debug for Prepared<'_> {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_struct("Prepared")
.field("required_words", &self.size)
.field("remaining_work", &self.budget.remaining())
.finish_non_exhaustive()
}
}
impl Prepared<'_> {
pub fn required_words(&self) -> usize {
self.size
}
pub fn remaining_work(&self) -> usize {
self.budget.remaining()
}
pub(super) fn step(&mut self) -> Result<(), CompileError> {
self.budget.charge(1).map_err(|_| CompileError::WorkLimit)
}
pub fn emit<'p>(self, output: &'p mut [u32]) -> Result<Program<'p>, CompileError> {
if let Some(first) = output.first_mut() {
*first = 0;
}
let size = self.size;
if output.len() < size {
return Err(CompileError::ProgramStorage {
required_words: size,
});
}
let output = &mut output[..size];
if let Err(error) = self.emit_into(output) {
output[0] = 0;
return Err(error);
}
Ok(Program { words: output })
}
fn emit_into(mut self, output: &mut [u32]) -> Result<(), CompileError> {
let (root, count, bits, base_size) = (self.root, self.count, self.flags, self.base_size);
let parser = &mut self;
parser.write_names(output, base_size)?;
parser.nodes[root as usize].start = 1;
let mut repeat_id = 0;
let write = |out: &mut [u32], pc: u32, op: u32, a: u32, b: u32| {
let at = HEADER + pc as usize * 3;
out[at..at + 3].copy_from_slice(&[op, a, b]);
};
for i in (0..parser.used).rev() {
parser.step()?;
let n = parser.nodes[i];
let pc = n.start;
let mut child = |id: u32, start: u32, reverse: bool| {
parser.nodes[id as usize].start = start;
parser.nodes[id as usize].reverse = reverse;
};
match n.kind {
EMPTY | NAME_META | NAME_DECL => {}
WRAP => child(n.a, pc, n.reverse),
SEQ => {
let (a, b) = if n.reverse { (n.b, n.a) } else { (n.a, n.b) };
let len = parser.nodes[a as usize].len;
parser.nodes[a as usize].start = pc;
parser.nodes[a as usize].reverse = n.reverse;
parser.nodes[b as usize].start = pc + len;
parser.nodes[b as usize].reverse = n.reverse;
}
ALT => {
let len = parser.nodes[n.a as usize].len;
let right = pc + len + 2;
parser.nodes[n.a as usize].start = pc + 1;
parser.nodes[n.a as usize].reverse = n.reverse;
parser.nodes[n.b as usize].start = right;
parser.nodes[n.b as usize].reverse = n.reverse;
write(output, pc, SPLIT, pc + 1, right);
write(output, right - 1, JUMP, pc + n.len, 0);
}
GROUP => {
child(n.a, pc + 1, n.reverse);
write(output, pc, SAVE, n.b * 2 + u32::from(n.reverse), 0);
write(
output,
pc + n.len - 1,
SAVE,
n.b * 2 + u32::from(!n.reverse),
0,
);
}
ASSERT => {
child(n.a, pc + 1, n.flags & 2 != 0);
write(output, pc, ASSERT, pc + n.len, n.flags);
write(output, pc + n.len - 1, ASSERT_END, 0, 0);
}
REPEAT => {
child(n.a, pc + 3, n.reverse);
for (delta, op) in [
(0, REPEAT_INIT),
(1, REPEAT_CHOICE),
(2, REPEAT_BODY),
(n.len - 1, REPEAT_NEXT),
] {
write(output, pc + delta, op, repeat_id, 0);
}
let at = HEADER
+ count as usize * 3
+ parser.range_used * 2
+ repeat_id as usize * 8;
output[at..at + 8].copy_from_slice(&[
n.b,
n.c,
n.flags,
pc + 2,
pc + n.len,
n.first,
n.end,
0,
]);
repeat_id += 1;
}
op => write(output, pc, modified(op, n.flags), n.a, n.b),
}
}
write(output, 0, SAVE, 0, 0);
write(output, count - 2, SAVE, 1, 0);
write(output, count - 1, MATCH, 0, 0);
let start = HEADER + count as usize * 3;
for (i, range) in parser.ranges[..parser.range_used].iter().enumerate() {
parser
.budget
.charge(1)
.map_err(|_| CompileError::WorkLimit)?;
output[start + i * 2..start + i * 2 + 2].copy_from_slice(&[range.lo, range.hi]);
}
output[..HEADER].copy_from_slice(&[
MAGIC,
VERSION,
bits | if parser.name_count != 0 { NAMES } else { 0 },
parser.captures,
count,
parser.range_used as u32,
parser.repeats,
0,
0,
0,
]);
for i in 0..parser.repeats as usize {
parser.step()?;
let p = Program { words: output };
let r = p.repeat(i);
let entry = r[3] as usize - 2;
if r[4] as usize == entry + 5 && r[5] == r[6] && consuming(p.instruction(entry + 3)[0])
{
output[HEADER + entry * 3] = ATOM_REPEAT;
let at = HEADER + count as usize * 3 + parser.range_used * 2 + i * 8;
output[at + 7] = 1;
}
}
if parser.repeats != 0 {
let hint = parser.admission(Program { words: output }, root)?;
output[2] |= hint;
}
parser.candidate()?;
output[7] = parser.candidate_descriptor(root);
output[8] = parser.end_candidate_descriptor(root);
output[9] = crate::program::derive_run_skip(output, Program { words: output })
| crate::program::derive_leading(output, count as usize)
| if parser.forward {
crate::program::ADMISSION_FORWARD
} else {
0
};
Program::from_words(output, parser.budget)
.map(|_| ())
.map_err(|e| match e {
ProgramError::WorkLimit => CompileError::WorkLimit,
ProgramError::Invalid => CompileError::InvalidProgram,
})
}
}