1use std::path::{Path, PathBuf};
4
5use lang_util::position::LexerPosition;
6
7use glsl_lang_pp::{
8 exts::{Registry, DEFAULT_REGISTRY},
9 last::{self, Event},
10 processor::{
11 fs::{ExpandStack, ParsedFile, Processor},
12 ProcessorState,
13 },
14};
15
16use crate::{HasLexerError, LangLexer, LangLexerIterator, ParseContext, ParseOptions, Token};
17
18use super::{
19 core::{self, HandleTokenResult, LexerCore},
20 Directives, LexicalError,
21};
22
23pub use glsl_lang_pp::processor::fs::FileSystem;
24
25pub struct Lexer<'r, 'p, F: FileSystem> {
27 inner: last::Tokenizer<'r, ExpandStack<'p, F>>,
28 current_file: PathBuf,
29 handle_token: HandleTokenResult<F::Error>,
30 opts: ParseOptions,
31}
32
33impl<'r, 'p, F: FileSystem> Lexer<'r, 'p, F> {
34 fn new(inner: ExpandStack<'p, F>, registry: &'r Registry, opts: &ParseOptions) -> Self {
35 Self {
36 inner: inner.tokenize(opts.default_version, opts.target_vulkan, registry),
37 current_file: Default::default(),
38 handle_token: Default::default(),
39 opts: *opts,
40 }
41 }
42
43 fn with_context(self, ctx: ParseContext) -> LexerIterator<'r, 'p, F> {
44 LexerIterator {
45 inner: self.inner,
46 core: LexerCore::new(&self.opts, ctx),
47 current_file: self.current_file,
48 handle_token: self.handle_token,
49 }
50 }
51}
52
53pub struct LexerIterator<'r, 'p, F: FileSystem> {
55 inner: last::Tokenizer<'r, ExpandStack<'p, F>>,
56 core: LexerCore,
57 current_file: PathBuf,
58 handle_token: HandleTokenResult<F::Error>,
59}
60
61impl<F: FileSystem> LexerIterator<'_, '_, F> {
62 pub fn into_directives(self) -> Directives {
63 self.core.into_directives()
64 }
65}
66
67impl<'r, 'p, F: FileSystem> Iterator for LexerIterator<'r, 'p, F> {
68 type Item = core::Item<F::Error>;
69
70 fn next(&mut self) -> Option<Self::Item> {
71 loop {
72 if let Some(item) = self.handle_token.pop_item() {
74 return Some(item);
75 }
76
77 if let Some(result) = self.handle_token.pop_event().or_else(|| self.inner.next()) {
78 match result {
79 Ok(event) => match event {
80 Event::Error { error, masked } => {
81 if !masked {
82 return Some(Err(error.into()));
83 }
84 }
85
86 Event::Token {
87 source_token,
88 token_kind,
89 state,
90 } => {
91 self.core.handle_token(
92 source_token,
93 token_kind,
94 state,
95 &mut self.inner,
96 &mut self.handle_token,
97 );
98 }
99
100 Event::Directive { directive, masked } => {
101 if let Err(errors) = self.core.handle_directive(directive, masked) {
102 self.handle_token.push_errors(errors);
103 }
104 }
105
106 Event::EnterFile {
107 file_id,
108 path,
109 canonical_path: _,
110 } => {
111 self.current_file = path;
112 self.core.handle_file_id(file_id);
113 }
114 },
115
116 Err(err) => {
117 return Some(Err(LexicalError::Io(err)));
118 }
119 }
120 } else {
121 return None;
122 }
123 }
124 }
125}
126
127impl<F: FileSystem> HasLexerError for Lexer<'_, '_, F> {
128 type Error = LexicalError<F::Error>;
129}
130
131impl<'r, 'p, F: FileSystem> LangLexer<'p> for Lexer<'r, 'p, F>
132where
133 File<'r, 'p, F>: 'p,
134{
135 type Input = File<'r, 'p, F>;
136 type Iter = LexerIterator<'r, 'p, F>;
137
138 fn new(source: Self::Input, opts: &ParseOptions) -> Self {
139 Lexer::new(
140 source.inner.process(source.state.unwrap_or_default()),
141 source.registry.unwrap_or(&DEFAULT_REGISTRY),
142 opts,
143 )
144 }
145
146 fn run(self, ctx: ParseContext) -> Self::Iter {
147 self.with_context(ctx)
148 }
149}
150
151impl<F: FileSystem> HasLexerError for LexerIterator<'_, '_, F> {
152 type Error = LexicalError<F::Error>;
153}
154
155impl<'r, 'p, F: FileSystem> LangLexerIterator for LexerIterator<'r, 'p, F> {
156 fn resolve_err(
157 &self,
158 err: lalrpop_util::ParseError<LexerPosition, Token, Self::Error>,
159 ) -> lang_util::error::ParseError<Self::Error> {
160 let location = self.inner.location();
161 let (file_id, lexer) = lang_util::error::error_location(&err);
162
163 lang_util::error::ParseError::<Self::Error>::builder()
164 .pos(lexer)
165 .current_file(file_id)
166 .resolve(location)
167 .resolve_path(&self.inner)
168 .finish(err.into())
169 }
170}
171
172pub trait PreprocessorExt<F: FileSystem> {
174 fn open(&mut self, path: impl AsRef<Path>) -> Result<File<'_, '_, F>, F::Error>;
180
181 fn open_source(&mut self, source: &str, path: impl AsRef<Path>) -> File<'_, '_, F>;
188}
189
190impl<F: FileSystem> PreprocessorExt<F> for Processor<F> {
191 fn open(&mut self, path: impl AsRef<Path>) -> Result<File<'_, '_, F>, F::Error> {
192 self.parse(path.as_ref()).map(|parsed_file| File {
193 inner: parsed_file,
194 state: None,
195 registry: None,
196 })
197 }
198
199 fn open_source(&mut self, source: &str, path: impl AsRef<Path>) -> File<'_, '_, F> {
200 File {
201 inner: self.parse_source(source, path.as_ref()),
202 state: None,
203 registry: None,
204 }
205 }
206}
207
208pub struct File<'r, 'p, F: FileSystem> {
210 inner: ParsedFile<'p, F>,
211 state: Option<ProcessorState>,
212 registry: Option<&'r Registry>,
213}
214
215impl<'r, 'p, F: FileSystem> File<'r, 'p, F> {
216 pub fn with_state(self, state: impl Into<ProcessorState>) -> Self {
218 Self {
219 state: Some(state.into()),
220 ..self
221 }
222 }
223
224 pub fn with_registry(self, registry: impl Into<&'r Registry>) -> Self {
226 Self {
227 registry: Some(registry.into()),
228 ..self
229 }
230 }
231}