#[cfg(any(test, feature = "cpu-parity"))]
use crate::parsing::c::lex::tokens::TOK_PREPROC;
#[cfg(any(test, feature = "cpu-parity"))]
use crate::parsing::c::preprocess::c_logical_directive_len;
use crate::parsing::c::preprocess::{
c_directive_payload, c_translation_phase_line_splice, classify_phase2_preprocessor_directive,
CLineSplicedSource, CPreprocessorDirective, CPreprocessorError,
};
#[cfg(any(test, feature = "cpu-parity"))]
pub(super) struct DirectiveRow<'a> {
pub index: usize,
pub start: usize,
pub bytes: &'a [u8],
}
#[cfg(any(test, feature = "cpu-parity"))]
pub(super) fn for_each_directive_row<F>(
tok_types: &[u32],
tok_starts: &[u32],
tok_lens: &[u32],
source: &[u8],
mut visit: F,
) -> Result<(), CPreprocessorError>
where
F: FnMut(DirectiveRow<'_>) -> Result<(), CPreprocessorError>,
{
if tok_types.len() != tok_starts.len() || tok_types.len() != tok_lens.len() {
return Err(CPreprocessorError {
offset: tok_types.len().min(tok_starts.len()).min(tok_lens.len()),
message: "Fix: token type/start/length streams must have identical lengths",
});
}
for (index, ((tok_type, start), len)) in
tok_types.iter().zip(tok_starts).zip(tok_lens).enumerate()
{
if *tok_type != TOK_PREPROC {
continue;
}
let start = usize::try_from(*start).map_err(|_| CPreprocessorError {
offset: index,
message: "Fix: token start does not fit host usize",
})?;
let len = usize::try_from(*len).map_err(|_| CPreprocessorError {
offset: index,
message: "Fix: token length does not fit host usize",
})?;
let token_end = start.checked_add(len).ok_or(CPreprocessorError {
offset: start,
message: "Fix: token span overflows source address space",
})?;
let logical_len = c_logical_directive_len(source, start);
if logical_len > len {
return Err(CPreprocessorError {
offset: start + len,
message: "Fix: TOK_PREPROC span must include the full phase-2 spliced directive row",
});
}
if token_end > source.len() {
return Err(CPreprocessorError {
offset: start,
message: "Fix: preprocessor token span must be inside the source buffer",
});
}
let logical_end = start.checked_add(logical_len).ok_or(CPreprocessorError {
offset: start,
message: "Fix: directive logical span overflows source address space",
})?;
let bytes = source.get(start..logical_end).ok_or(CPreprocessorError {
offset: start,
message: "Fix: preprocessor token span must be inside the source buffer",
})?;
visit(DirectiveRow { index, start, bytes })?;
}
Ok(())
}
pub(super) struct ScannedDirective {
pub spliced: CLineSplicedSource,
pub directive: CPreprocessorDirective,
directive_offset: usize,
}
impl ScannedDirective {
pub(super) fn classify(
row: &[u8],
directive_offset: usize,
) -> Result<Self, CPreprocessorError> {
let spliced = c_translation_phase_line_splice(row);
let directive =
classify_phase2_preprocessor_directive(&spliced.bytes).map_err(|mut err| {
err.offset = directive_offset + spliced.original_offset(err.offset);
err
})?;
Ok(Self {
spliced,
directive,
directive_offset,
})
}
pub(super) fn source_offset(&self, phase2_offset: usize) -> usize {
self.directive_offset + self.spliced.original_offset(phase2_offset)
}
pub(super) fn remap(&self, mut err: CPreprocessorError) -> CPreprocessorError {
err.offset = self.source_offset(err.offset);
err
}
pub(super) fn payload(&self) -> Result<&[u8], CPreprocessorError> {
c_directive_payload(&self.spliced.bytes, self.directive).map_err(|err| self.remap(err))
}
}
pub(super) fn skip_horizontal_ws(bytes: &[u8], mut index: usize) -> usize {
while matches!(bytes.get(index), Some(b' ' | b'\t' | b'\x0b' | b'\x0c')) {
index += 1;
}
index
}