#![cfg(feature = "c-parser")]
use vyre_libs::parsing::c::lex::tokens::{
TOK_IDENTIFIER, TOK_LBRACE, TOK_LBRACKET, TOK_LPAREN, TOK_PLUS, TOK_RBRACE, TOK_RBRACKET,
TOK_RPAREN, TOK_SEMICOLON,
};
use vyre_libs::parsing::c::parse::structure_statement::{
c11_statement_bounds, c11_statement_bounds_scratch_words,
};
use vyre_primitives::wire::{decode_u32_le_bytes_all, pack_u32_slice};
use vyre_reference::value::Value;
use vyre_foundation::ir::Expr;
const OLD_SCAN_WINDOW_TOKENS: u32 = 256;
fn filler(n: u32) -> Vec<u32> {
(0..n)
.map(|i| if i % 2 == 0 { TOK_IDENTIFIER } else { TOK_PLUS })
.collect()
}
fn long_statement(n: u32) -> Vec<u32> {
let mut tokens = filler(n);
tokens.push(TOK_SEMICOLON);
tokens
}
fn spans(tokens: &[u32]) -> Vec<(u32, u32)> {
let token_count = u32::try_from(tokens.len()).expect("token count must fit u32");
let program = c11_statement_bounds(
"tok_types",
Expr::u32(token_count),
"out_statements",
"out_counts",
);
let scratch_words = c11_statement_bounds_scratch_words(token_count) as usize;
let outputs = vyre_reference::reference_eval(
&program,
&[
Value::from(pack_u32_slice(tokens)),
Value::from(vec![0_u8; tokens.len() * 2 * std::mem::size_of::<u32>()]),
Value::from(vec![0_u8; std::mem::size_of::<u32>()]),
Value::from(vec![0_u8; scratch_words * std::mem::size_of::<u32>()]),
],
)
.expect("reference evaluation of c11_statement_bounds must succeed");
let statements = decode_u32_le_bytes_all(&outputs[0].to_bytes());
let counts = decode_u32_le_bytes_all(&outputs[1].to_bytes());
let used = counts[0] as usize;
statements[..used]
.chunks_exact(2)
.map(|pair| (pair[0], pair[1]))
.collect()
}
fn span_from_zero(tokens: &[u32]) -> (u32, u32) {
*spans(tokens)
.iter()
.find(|(start, _)| *start == 0)
.expect("a candidate span starting at token 0 must be recorded")
}
fn span_from(tokens: &[u32], start: u32) -> (u32, u32) {
*spans(tokens)
.iter()
.find(|(candidate, _)| *candidate == start)
.unwrap_or_else(|| panic!("a candidate span starting at token {start} must be recorded"))
}
fn sorted_spans(mut recorded: Vec<(u32, u32)>) -> Vec<(u32, u32)> {
recorded.sort_unstable();
recorded
}
fn oracle_spans(tokens: &[u32]) -> Vec<(u32, u32)> {
let mut is_boundary = vec![false; tokens.len()];
let mut paren_depth: u32 = 0;
let mut bracket_depth: u32 = 0;
for (index, &token) in tokens.iter().enumerate() {
match token {
TOK_LPAREN => paren_depth += 1,
TOK_RPAREN => paren_depth = paren_depth.saturating_sub(1),
TOK_LBRACKET => bracket_depth += 1,
TOK_RBRACKET => bracket_depth = bracket_depth.saturating_sub(1),
_ => {}
}
let at_top_level = paren_depth == 0 && bracket_depth == 0;
is_boundary[index] = token == TOK_SEMICOLON
|| (at_top_level && (token == TOK_LBRACE || token == TOK_RBRACE));
}
let mut next_boundary: Vec<Option<usize>> = vec![None; tokens.len()];
let mut nearest: Option<usize> = None;
for index in (0..tokens.len()).rev() {
if is_boundary[index] {
nearest = Some(index);
}
next_boundary[index] = nearest;
}
(0..tokens.len())
.map(|start| {
let start_u32 = u32::try_from(start).expect("token index must fit u32");
match next_boundary[start] {
Some(boundary) => (
start_u32,
u32::try_from(boundary + 1).expect("token index must fit u32"),
),
None => (start_u32, start_u32),
}
})
.collect()
}
#[test]
fn statement_shorter_than_scan_window_ends_at_its_semicolon() {
let tokens = long_statement(8);
let (start, end) = span_from_zero(&tokens);
assert_eq!(start, 0, "candidate must start at token 0");
assert_eq!(
end, 9,
"8 tokens then a semicolon at index 8 must yield end = 9, got {end}"
);
}
#[test]
fn statement_terminated_on_last_scanned_token_is_bounded_correctly() {
let tokens = long_statement(OLD_SCAN_WINDOW_TOKENS - 1);
assert_eq!(
tokens[(OLD_SCAN_WINDOW_TOKENS - 1) as usize],
TOK_SEMICOLON,
"fixture must place the semicolon on the last token thread 0 scans"
);
let (_, end) = span_from_zero(&tokens);
assert_eq!(
end,
OLD_SCAN_WINDOW_TOKENS,
"a semicolon at index {} must yield end = {OLD_SCAN_WINDOW_TOKENS}, got {end}",
OLD_SCAN_WINDOW_TOKENS - 1
);
}
#[test]
fn statement_longer_than_scan_window_is_not_silently_truncated() {
let tokens = long_statement(OLD_SCAN_WINDOW_TOKENS);
let semicolon_index = OLD_SCAN_WINDOW_TOKENS;
assert_eq!(
tokens[semicolon_index as usize], TOK_SEMICOLON,
"fixture must place the semicolon one token past the scan window"
);
let (_, end) = span_from_zero(&tokens);
assert_ne!(
end, 1,
"end collapsed to 1, the truncation signature: a {OLD_SCAN_WINDOW_TOKENS} token statement \
was recorded as a single token span"
);
assert_eq!(
end,
semicolon_index + 1,
"a semicolon at index {semicolon_index} must yield end = {}, got {end}",
semicolon_index + 1
);
}
#[test]
fn long_statement_well_past_scan_window_is_not_silently_truncated() {
let tokens = long_statement(OLD_SCAN_WINDOW_TOKENS * 3);
let semicolon_index = OLD_SCAN_WINDOW_TOKENS * 3;
let (_, end) = span_from_zero(&tokens);
assert_eq!(
end,
semicolon_index + 1,
"a semicolon at index {semicolon_index} must yield end = {}, got {end}",
semicolon_index + 1
);
}
#[test]
fn long_statement_terminated_by_brace_is_not_silently_truncated() {
let mut tokens: Vec<u32> = (0..OLD_SCAN_WINDOW_TOKENS + 10)
.map(|i| if i % 2 == 0 { TOK_IDENTIFIER } else { TOK_PLUS })
.collect();
let brace_index = tokens.len() as u32;
tokens.push(TOK_LBRACE);
let (_, end) = span_from_zero(&tokens);
assert_eq!(
end,
brace_index + 1,
"a brace at index {brace_index} must yield end = {}, got {end}",
brace_index + 1
);
}
#[test]
fn semicolon_immediately_past_old_window_edge_is_bounded_at_257() {
let tokens = long_statement(OLD_SCAN_WINDOW_TOKENS);
assert_eq!(
tokens.len(),
(OLD_SCAN_WINDOW_TOKENS + 1) as usize,
"fixture must be 256 filler tokens plus the semicolon"
);
assert_eq!(
tokens[OLD_SCAN_WINDOW_TOKENS as usize], TOK_SEMICOLON,
"fixture must place the semicolon at index {OLD_SCAN_WINDOW_TOKENS}"
);
let (start, end) = span_from_zero(&tokens);
assert_eq!(start, 0, "candidate must start at token 0");
assert_eq!(
end, 257,
"a semicolon at index 256 must yield end = 257, got {end}"
);
}
#[test]
fn semicolon_two_tokens_past_old_window_edge_is_bounded_at_258() {
let tokens = long_statement(OLD_SCAN_WINDOW_TOKENS + 1);
assert_eq!(
tokens.len(),
(OLD_SCAN_WINDOW_TOKENS + 2) as usize,
"fixture must be 257 filler tokens plus the semicolon"
);
assert_eq!(
tokens[(OLD_SCAN_WINDOW_TOKENS + 1) as usize],
TOK_SEMICOLON,
"fixture must place the semicolon at index {}",
OLD_SCAN_WINDOW_TOKENS + 1
);
let recorded = sorted_spans(spans(&tokens));
assert_eq!(
recorded.len(),
tokens.len(),
"one candidate span per token position: expected {}, got {}",
tokens.len(),
recorded.len()
);
assert_eq!(
recorded[0],
(0, 258),
"position 0 must end just past the semicolon at index 257"
);
assert_eq!(
recorded[257],
(257, 258),
"position 257 is the semicolon itself and must end at 258"
);
assert_eq!(
recorded,
oracle_spans(&tokens),
"the full span set must match the contract oracle exactly"
);
}
#[test]
fn unterminated_stream_reports_empty_spans_distinguishable_from_one_token_statement() {
let tokens = filler(300);
assert!(
!tokens
.iter()
.any(|&token| token == TOK_SEMICOLON || token == TOK_LBRACE || token == TOK_RBRACE),
"fixture must contain no boundary token at all"
);
let recorded = sorted_spans(spans(&tokens));
assert_eq!(
recorded.len(),
tokens.len(),
"one candidate span per token position: expected {}, got {}",
tokens.len(),
recorded.len()
);
for &(start, end) in &recorded {
assert_eq!(
end, start,
"no boundary exists at or after {start}, so the span must be empty, got ({start}, {end})"
);
}
let unterminated_at_zero = span_from_zero(&tokens);
assert_eq!(
unterminated_at_zero,
(0, 0),
"position 0 of an unterminated stream must be the empty span (0, 0)"
);
let single_statement = vec![TOK_SEMICOLON];
let terminated_at_zero = span_from_zero(&single_statement);
assert_eq!(
terminated_at_zero,
(0, 1),
"a genuine one token statement must be (0, 1), never (0, 0)"
);
assert_ne!(
unterminated_at_zero, terminated_at_zero,
"the unterminated signal must be distinguishable from a real one token \
statement, got {unterminated_at_zero:?} for both"
);
}
#[test]
fn consecutive_long_statements_each_end_at_their_own_semicolon() {
let lengths = [300_u32, 400, 500];
let mut tokens: Vec<u32> = Vec::new();
let mut statement_starts: Vec<u32> = Vec::new();
let mut semicolon_indices: Vec<u32> = Vec::new();
for &length in &lengths {
statement_starts.push(u32::try_from(tokens.len()).expect("index must fit u32"));
tokens.extend(filler(length));
semicolon_indices.push(u32::try_from(tokens.len()).expect("index must fit u32"));
tokens.push(TOK_SEMICOLON);
}
assert_eq!(
statement_starts,
vec![0, 301, 702],
"fixture layout drifted"
);
assert_eq!(
semicolon_indices,
vec![300, 701, 1202],
"fixture semicolon indices drifted"
);
assert_eq!(tokens.len(), 1203, "fixture length drifted");
for (&start, &semicolon) in statement_starts.iter().zip(semicolon_indices.iter()) {
let (_, end) = span_from(&tokens, start);
assert_eq!(
end,
semicolon + 1,
"the statement starting at {start} is terminated by the semicolon at \
{semicolon} and must yield end = {}, got {end}",
semicolon + 1
);
}
let (_, interior_end) = span_from(&tokens, 500);
assert_eq!(
interior_end, 702,
"position 500 sits inside the second statement and must end at its \
semicolon index 701 plus one, got {interior_end}"
);
}
#[test]
fn statement_longer_than_pipeline_token_scan_cap_is_bounded_at_its_semicolon() {
let length = 65_600_u32;
assert!(
length > vyre_libs::parsing::c::pipeline::stages::C11_AST_MAX_TOK_SCAN,
"fixture must exceed the pipeline token scan cap of {}",
vyre_libs::parsing::c::pipeline::stages::C11_AST_MAX_TOK_SCAN
);
let tokens = long_statement(length);
assert_eq!(
tokens[length as usize], TOK_SEMICOLON,
"fixture must place the semicolon at index {length}"
);
let (_, end) = span_from_zero(&tokens);
assert_eq!(
end,
length + 1,
"a semicolon at index {length} must yield end = {}, got {end}",
length + 1
);
}
#[test]
fn span_set_matches_cpu_oracle_over_mixed_corpus() {
let mut corpus: Vec<(&str, Vec<u32>)> = Vec::new();
let mut short_statements = Vec::new();
for length in [3_u32, 1, 5, 2] {
short_statements.extend(filler(length));
short_statements.push(TOK_SEMICOLON);
}
corpus.push(("four short statements", short_statements));
corpus.push((
"statement of 255 filler tokens",
long_statement(OLD_SCAN_WINDOW_TOKENS - 1),
));
corpus.push((
"statement of 256 filler tokens",
long_statement(OLD_SCAN_WINDOW_TOKENS),
));
corpus.push((
"statement of 257 filler tokens",
long_statement(OLD_SCAN_WINDOW_TOKENS + 1),
));
let mut brace_terminated = filler(OLD_SCAN_WINDOW_TOKENS + 4);
brace_terminated.push(TOK_LBRACE);
brace_terminated.extend(filler(3));
brace_terminated.push(TOK_RBRACE);
corpus.push(("brace terminated statement", brace_terminated));
let mut balanced = filler(3);
balanced.push(TOK_LPAREN);
balanced.extend(filler(4));
balanced.push(TOK_LBRACKET);
balanced.extend(filler(2));
balanced.push(TOK_RBRACKET);
balanced.extend(filler(1));
balanced.push(TOK_RPAREN);
balanced.extend(filler(2));
balanced.push(TOK_SEMICOLON);
corpus.push(("balanced parens and brackets", balanced));
let mut brace_in_parens = filler(2);
brace_in_parens.push(TOK_LPAREN);
brace_in_parens.extend(filler(2));
brace_in_parens.push(TOK_LBRACE);
brace_in_parens.extend(filler(2));
brace_in_parens.push(TOK_RBRACE);
brace_in_parens.extend(filler(1));
brace_in_parens.push(TOK_RPAREN);
brace_in_parens.push(TOK_SEMICOLON);
corpus.push(("brace nested inside parens", brace_in_parens));
let mut brace_in_brackets = vec![TOK_LBRACKET];
brace_in_brackets.extend(filler(2));
brace_in_brackets.push(TOK_LBRACE);
brace_in_brackets.push(TOK_RBRACKET);
brace_in_brackets.push(TOK_LBRACE);
brace_in_brackets.push(TOK_SEMICOLON);
corpus.push(("brace nested inside brackets", brace_in_brackets));
let mut unmatched_closers = vec![TOK_RPAREN, TOK_RPAREN, TOK_RBRACKET];
unmatched_closers.extend(filler(3));
unmatched_closers.push(TOK_LBRACE);
unmatched_closers.extend(filler(2));
unmatched_closers.push(TOK_SEMICOLON);
corpus.push(("unmatched closing parens and brackets", unmatched_closers));
let mut unclosed_paren = vec![TOK_LPAREN];
unclosed_paren.extend(filler(2));
unclosed_paren.push(TOK_LBRACE);
unclosed_paren.extend(filler(2));
unclosed_paren.push(TOK_SEMICOLON);
unclosed_paren.push(TOK_LBRACE);
unclosed_paren.extend(filler(2));
corpus.push(("semicolon inside an unclosed paren", unclosed_paren));
let mut unterminated_tail = long_statement(10);
unterminated_tail.extend(filler(20));
corpus.push(("terminated head then unterminated tail", unterminated_tail));
assert!(
corpus.len() >= 8,
"the corpus must cover at least 8 streams, got {}",
corpus.len()
);
for (name, tokens) in &corpus {
let expected = oracle_spans(tokens);
let recorded = sorted_spans(spans(tokens));
assert_eq!(
recorded,
expected,
"kernel span set diverged from the contract oracle on the {name} stream \
({} tokens)",
tokens.len()
);
}
}