use super::Expression;
use crate::TokenKind;
pub(super) const HALT: u8 = 0;
pub(super) const CALL: u8 = 1;
pub(super) const RETURN: u8 = 2;
pub(super) const ENTER: u8 = 3;
pub(super) const LEAVE: u8 = 4;
pub(super) const KEYWORD: u8 = 5;
pub(super) const TOKEN: u8 = 6;
pub(super) const PARAMETER: u8 = 7;
pub(super) const IF_FAILED: u8 = 8;
pub(super) const CHOICE_NEXT: u8 = 9;
pub(super) const OPTIONAL_END: u8 = 10;
pub(super) const REPEAT_NEXT: u8 = 11;
pub(super) const TIGHT_END: u8 = 12;
pub(super) const LOOK_END: u8 = 13;
pub(super) const CALL_EXPRESSION: u8 = 14;
pub(super) const KEYWORD_CHOICE: u8 = 15;
pub(super) const ROOT_CALL: usize = super::MAX_RULES;
pub(super) const CHOICE_SCOPE: usize = 1;
pub(super) const LOOK_SCOPE: usize = 2;
const SHIFT: u32 = 60;
const ARGUMENT: u64 = (1_u64 << SHIFT) - 1;
pub(super) struct Program {
pub instructions: Vec<u64>,
pub entries: Vec<usize>,
pub keywords: Vec<&'static str>,
pub keyword_choices: Vec<std::ops::Range<usize>>,
pub tokens: Vec<(TokenKind, &'static str)>,
pub looks: Vec<(&'static str, bool)>,
}
impl Program {
pub fn compile(rules: Vec<(&'static str, Expression<usize>)>) -> (Self, Vec<&'static str>) {
let mut program = Self {
instructions: Vec::new(),
entries: Vec::with_capacity(rules.len()),
keywords: Vec::new(),
keyword_choices: Vec::new(),
tokens: Vec::new(),
looks: Vec::new(),
};
program.emit(CALL, ROOT_CALL);
program.emit(HALT, 0);
let mut names = Vec::with_capacity(rules.len());
for (name, body) in rules {
names.push(name);
program.entries.push(program.instructions.len());
program.expression(&body);
program.emit(RETURN, 0);
}
program.instructions.shrink_to_fit();
program.keywords.shrink_to_fit();
program.keyword_choices.shrink_to_fit();
program.tokens.shrink_to_fit();
program.looks.shrink_to_fit();
(program, names)
}
pub fn decode(&self, pc: usize) -> (u8, usize) {
let instruction = self.instructions[pc];
(
(instruction >> SHIFT) as u8,
(instruction & ARGUMENT) as usize,
)
}
fn emit(&mut self, opcode: u8, argument: usize) -> usize {
let argument = argument as u64;
assert!(
argument <= ARGUMENT,
"program operand fits allocation bounds"
);
let pc = self.instructions.len();
self.instructions
.push((u64::from(opcode) << SHIFT) | argument);
pc
}
fn patch(&mut self, pc: usize, target: usize) {
let (opcode, _) = self.decode(pc);
assert!(
(target as u64) <= ARGUMENT,
"program target fits allocation bounds"
);
self.instructions[pc] = (u64::from(opcode) << SHIFT) | target as u64;
}
fn link_jump(&mut self, opcode: u8, jumps: &mut Option<usize>) {
*jumps = Some(self.emit(opcode, jumps.map_or(0, |pc| pc + 1)));
}
fn patch_jumps(&mut self, mut jumps: Option<usize>, target: usize) {
while let Some(pc) = jumps {
let (_, next) = self.decode(pc);
self.patch(pc, target);
jumps = next.checked_sub(1);
}
}
fn expression(&mut self, expression: &Expression<usize>) {
match expression {
Expression::Keyword(keyword) => {
let id = self.keywords.len();
self.keywords.push(keyword);
self.emit(KEYWORD, id);
}
Expression::Token(kind, expected) => {
let id = self.tokens.len();
self.tokens.push((*kind, expected));
self.emit(TOKEN, id);
}
Expression::Parameter => {
self.emit(PARAMETER, 0);
}
Expression::Rule(rule) => {
self.emit(CALL_EXPRESSION, *rule);
}
Expression::Sequence(items) => {
self.emit(ENTER, 0);
let mut jumps = None;
for (index, item) in items.iter().enumerate() {
self.expression(item);
if index + 1 < items.len() {
self.link_jump(IF_FAILED, &mut jumps);
}
}
let exit = self.emit(LEAVE, 0);
self.patch_jumps(jumps, exit);
}
Expression::Choice(items)
if !items.is_empty()
&& items
.iter()
.all(|item| matches!(item, Expression::Keyword(_))) =>
{
let start = self.keywords.len();
self.keywords.extend(items.iter().map(|item| {
let Expression::Keyword(keyword) = item else {
unreachable!()
};
*keyword
}));
let id = self.keyword_choices.len();
self.keyword_choices.push(start..self.keywords.len());
self.emit(KEYWORD_CHOICE, id);
}
Expression::Choice(items) => {
self.emit(ENTER, CHOICE_SCOPE);
let mut jumps = None;
for (index, item) in items.iter().enumerate() {
self.expression(item);
if index + 1 < items.len() {
self.link_jump(CHOICE_NEXT, &mut jumps);
}
}
let exit = self.emit(LEAVE, 0);
self.patch_jumps(jumps, exit);
}
Expression::Optional(inner) => {
self.emit(ENTER, 0);
self.expression(inner);
self.emit(OPTIONAL_END, 0);
self.emit(LEAVE, 0);
}
Expression::Repeat(inner) => {
self.emit(ENTER, 0);
let begin = self.instructions.len();
self.expression(inner);
self.emit(REPEAT_NEXT, begin);
self.emit(LEAVE, 0);
}
Expression::Tight(inner) => {
self.emit(ENTER, 0);
self.expression(inner);
self.emit(TIGHT_END, 0);
self.emit(LEAVE, 0);
}
Expression::Ahead(inner, expected) | Expression::Not(inner, expected) => {
self.emit(ENTER, LOOK_SCOPE);
self.expression(inner);
let id = self.looks.len();
self.looks
.push((expected, matches!(expression, Expression::Ahead(..))));
self.emit(LOOK_END, id);
self.emit(LEAVE, 0);
}
}
}
}