use crate::ops::AlgebraicLaw;
use crate::ir::transform::compiler::{U32X2_OUTPUTS, U32X5_INPUTS};
use crate::lower::wgsl::compiler::wgsl_backend;
use crate::ops::{IntrinsicDescriptor, OpSpec};
use thiserror::Error;
#[must_use]
pub const fn source() -> &'static str {
include_str!("../../../lower/wgsl/compiler/recursive_descent.wgsl")
}
impl RecursiveDescentOp {
pub const SPEC: OpSpec = OpSpec::intrinsic(
"compiler_primitives.recursive_descent",
U32X5_INPUTS,
U32X2_OUTPUTS,
LAWS,
wgsl_backend,
IntrinsicDescriptor::new("compiler_primitives_recursive_descent", "workgroup_stack", crate::ops::cpu_op::structured_intrinsic_cpu),
);
}
pub const LAWS: &[AlgebraicLaw] = &[AlgebraicLaw::Bounded {
lo: 0,
hi: u32::MAX,
}];
pub fn parse(
tokens: &[u32],
transitions: &[Transition],
start_state: u32,
accept_state: u32,
max_stack: usize,
max_callbacks: usize,
) -> Result<ParseResult, RecursiveDescentError> {
let mut state = start_state;
let mut stack = Vec::with_capacity(max_stack);
let mut callbacks = Vec::new();
let mut consumed = 0usize;
while consumed < tokens.len() {
let token = tokens[consumed];
let transition = transitions
.iter()
.find(|candidate| candidate.state == state && candidate.token_kind == token)
.copied()
.ok_or(RecursiveDescentError::NoTransition { state, token })?;
if transition.push_state != u32::MAX {
if stack.len() == max_stack {
return Err(RecursiveDescentError::StackOverflow { max_stack });
}
stack.push(transition.push_state);
}
if transition.callback != 0 {
if callbacks.len() == max_callbacks {
return Err(RecursiveDescentError::CallbackOverflow { max_callbacks });
}
callbacks.push(transition.callback);
}
state = if transition.next_state == u32::MAX {
stack.pop().ok_or(RecursiveDescentError::StackUnderflow)?
} else {
transition.next_state
};
consumed += 1;
}
if state != accept_state {
return Err(RecursiveDescentError::NotAccepted {
state,
accept_state,
});
}
Ok(ParseResult {
callbacks,
consumed: u32::try_from(consumed).map_err(|_| RecursiveDescentError::TokenOverflow)?,
final_state: state,
})
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct ParseResult {
pub callbacks: Vec<u32>,
pub consumed: u32,
pub final_state: u32,
}
#[derive(Debug, Clone, PartialEq, Eq, Error)]
pub enum RecursiveDescentError {
#[error("RecursiveDescentNoTransition: no transition for state {state} token {token}. Fix: add a grammar table edge or reject this token stream before dispatch.")]
NoTransition {
state: u32,
token: u32,
},
#[error("RecursiveDescentStackOverflow: stack exceeded {max_stack} entries. Fix: increase workgroup.stack depth or split the grammar production.")]
StackOverflow {
max_stack: usize,
},
#[error("RecursiveDescentStackUnderflow: return transition found an empty stack. Fix: validate push/return grammar balance.")]
StackUnderflow,
#[error("RecursiveDescentCallbackOverflow: callback output exceeded {max_callbacks}. Fix: increase callback output capacity.")]
CallbackOverflow {
max_callbacks: usize,
},
#[error("RecursiveDescentNotAccepted: final state {state} does not equal accept state {accept_state}. Fix: add a completion transition or reject incomplete input.")]
NotAccepted {
state: u32,
accept_state: u32,
},
#[error("RecursiveDescentTokenOverflow: consumed token count cannot fit u32. Fix: split the token stream.")]
TokenOverflow,
}
#[derive(Debug, Default, Clone, Copy)]
pub struct RecursiveDescentOp;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Transition {
pub state: u32,
pub token_kind: u32,
pub next_state: u32,
pub callback: u32,
pub push_state: u32,
}
pub const WORKGROUP_SIZE: [u32; 3] = [64, 1, 1];