vyre_libs/parsing/c/preprocess/
source.rs1#[cfg(any(test, feature = "cpu-parity"))]
4use crate::parsing::c::preprocess::directive_scan::for_each_directive_row;
5use crate::parsing::c::preprocess::directive_scan::{skip_horizontal_ws, ScannedDirective};
6use crate::parsing::c::preprocess::{CPreprocessorDirectiveKind, CPreprocessorError};
7
8#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
10pub enum CIncludeStyle {
11 Quote,
13 Angle,
15}
16
17#[derive(Debug, Clone, PartialEq, Eq)]
19pub struct CIncludeRequest {
20 pub directive: CPreprocessorDirectiveKind,
22 pub style: CIncludeStyle,
24 pub spelling: Vec<u8>,
26 pub directive_offset: usize,
28 pub payload_offset: usize,
30}
31
32impl CIncludeRequest {
33 #[must_use]
35 pub const fn is_include_next(&self) -> bool {
36 matches!(self.directive, CPreprocessorDirectiveKind::IncludeNext)
37 }
38}
39
40#[derive(Debug, Clone, PartialEq, Eq)]
42pub struct CSourceFile {
43 pub source_id: u32,
45 pub display_name: String,
47 pub bytes: Vec<u8>,
49}
50
51pub trait CPreprocessorSourceManager {
57 fn load_include(&self, request: &CIncludeRequest) -> Result<CSourceFile, CPreprocessorError>;
64}
65
66#[derive(Debug, Clone, PartialEq, Eq)]
68pub struct CResolvedInclude {
69 pub token_index: usize,
71 pub request: CIncludeRequest,
73 pub source: CSourceFile,
75}
76
77pub fn parse_c_include_request(
87 row: &[u8],
88 directive_offset: usize,
89) -> Result<Option<CIncludeRequest>, CPreprocessorError> {
90 let scan = ScannedDirective::classify(row, directive_offset)?;
91 let directive = scan.directive;
92 if !matches!(
93 directive.kind,
94 CPreprocessorDirectiveKind::Include | CPreprocessorDirectiveKind::IncludeNext
95 ) {
96 return Ok(None);
97 }
98
99 let payload = scan.payload()?;
100 let (style, spelling, payload_rel) =
101 parse_header_name_payload(payload).map_err(|mut err| {
102 err.offset += directive.payload_start;
103 scan.remap(err)
104 })?;
105 Ok(Some(CIncludeRequest {
106 directive: directive.kind,
107 style,
108 spelling,
109 directive_offset,
110 payload_offset: scan.source_offset(directive.payload_start + payload_rel),
111 }))
112}
113
114#[cfg(any(test, feature = "cpu-parity"))]
122pub fn reference_c_preprocessor_load_includes<M: CPreprocessorSourceManager>(
123 tok_types: &[u32],
124 tok_starts: &[u32],
125 tok_lens: &[u32],
126 source: &[u8],
127 manager: &M,
128) -> Result<Vec<CResolvedInclude>, CPreprocessorError> {
129 let mut resolved = Vec::new();
130 for_each_directive_row(tok_types, tok_starts, tok_lens, source, |row| {
131 if let Some(request) = parse_c_include_request(row.bytes, row.start)? {
132 let loaded = manager.load_include(&request)?;
133 resolved.push(CResolvedInclude {
134 token_index: row.index,
135 request,
136 source: loaded,
137 });
138 }
139 Ok(())
140 })?;
141 Ok(resolved)
142}
143
144fn parse_header_name_payload(
145 payload: &[u8],
146) -> Result<(CIncludeStyle, Vec<u8>, usize), CPreprocessorError> {
147 let start = skip_horizontal_ws(payload, 0);
148 let Some(open) = payload.get(start).copied() else {
149 return Err(CPreprocessorError {
150 offset: start,
151 message: "Fix: #include needs a header name payload",
152 });
153 };
154 match open {
155 b'"' => parse_delimited_header(payload, start, b'"', CIncludeStyle::Quote),
156 b'<' => parse_delimited_header(payload, start, b'>', CIncludeStyle::Angle),
157 _ => Err(CPreprocessorError {
158 offset: start,
159 message:
160 "Fix: #include payload must be a quoted or angle-bracket header name after macro expansion",
161 }),
162 }
163}
164
165fn parse_delimited_header(
166 payload: &[u8],
167 start: usize,
168 close: u8,
169 style: CIncludeStyle,
170) -> Result<(CIncludeStyle, Vec<u8>, usize), CPreprocessorError> {
171 let mut index = start + 1;
172 while let Some(byte) = payload.get(index).copied() {
173 if matches!(byte, b'\n' | b'\r') {
174 return Err(CPreprocessorError {
175 offset: index,
176 message: "Fix: #include header name must close before newline",
177 });
178 }
179 if byte == close {
180 let trailing = skip_horizontal_ws(payload, index + 1);
181 if trailing != payload.len() {
182 return Err(CPreprocessorError {
183 offset: trailing,
184 message: "Fix: unexpected tokens after #include header name",
185 });
186 }
187 return Ok((style, payload[start + 1..index].to_vec(), start + 1));
188 }
189 index += 1;
190 }
191 Err(CPreprocessorError {
192 offset: start,
193 message: "Fix: terminate #include header name",
194 })
195}