1pub(crate) mod error;
2#[cfg(test)]
3mod tests;
4
5use {
6 crate::{
7 Keysym, ModifierMask,
8 xkb::{
9 code::Code,
10 code_map::CodeMap,
11 code_slice::CodeSlice,
12 compose::{
13 parser::error::{Expected, LHS, ParserError},
14 token::Token,
15 },
16 context::Environment,
17 diagnostic::{DiagnosticKind, DiagnosticSink},
18 interner::Interner,
19 meaning::{Meaning, MeaningCache},
20 span::{SpanExt, SpanUnit, Spanned},
21 },
22 },
23 isnt::std_1::primitive::IsntSliceExt,
24 kbvm_proc::ad_hoc_display,
25 std::sync::Arc,
26};
27
28struct Parser<'a, 'b, 'c> {
29 map: &'a mut CodeMap,
30 diagnostics: &'a mut DiagnosticSink<'b, 'c>,
31 tokens: &'a [Spanned<Token>],
32 interner: &'a mut Interner,
33 meaning_cache: &'a mut MeaningCache,
34 pos: usize,
35 env: &'a Environment,
36 locale_file: Option<&'a str>,
37}
38
39#[derive(Debug)]
40pub(crate) enum Line {
41 Include(CodeSlice<'static>),
42 Production(Production),
43}
44
45#[derive(Debug)]
46pub(crate) struct Production {
47 pub(crate) steps: Vec<Step>,
48 pub(crate) string: Option<CodeSlice<'static>>,
49 pub(crate) keysym: Option<Keysym>,
50}
51
52#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
53pub(crate) struct Step {
54 pub(crate) keysym: Keysym,
55}
56
57pub(crate) fn parse_line(
58 map: &mut CodeMap,
59 diagnostics: &mut DiagnosticSink,
60 interner: &mut Interner,
61 meaning_cache: &mut MeaningCache,
62 tokens: &[Spanned<Token>],
63 env: &Environment,
64 locale_file: Option<&str>,
65) -> Result<Option<Spanned<Line>>, Spanned<ParserError>> {
66 let line = Parser {
67 map,
68 diagnostics,
69 tokens,
70 interner,
71 meaning_cache,
72 pos: 0,
73 env,
74 locale_file,
75 }
76 .parse_line()?;
77 let lo = tokens.first().map(|t| t.span.lo).unwrap_or_default();
78 let hi = tokens.last().map(|t| t.span.hi).unwrap_or_default();
79 Ok(line.map(|l| l.spanned(lo, hi)))
80}
81
82impl Parser<'_, '_, '_> {
83 fn next(
84 &mut self,
85 expected: &'static [Expected],
86 ) -> Result<Spanned<Token>, Spanned<ParserError>> {
87 if self.pos >= self.tokens.len() {
88 return Err(self.expected_but_eof(self.tokens.last().unwrap().span, expected));
89 }
90 let token = self.tokens[self.pos];
91 self.pos += 1;
92 Ok(token)
93 }
94
95 fn try_peek(&mut self) -> Option<Spanned<Token>> {
96 self.tokens.get(self.pos).copied()
97 }
98
99 fn peek(
100 &mut self,
101 expected: &'static [Expected],
102 ) -> Result<Spanned<Token>, Spanned<ParserError>> {
103 if self.pos >= self.tokens.len() {
104 return Err(self.expected_but_eof(self.tokens.last().unwrap().span, expected));
105 }
106 Ok(self.tokens[self.pos])
107 }
108
109 fn parse_line(&mut self) -> Result<Option<Line>, Spanned<ParserError>> {
110 let t = self.peek(LHS)?;
111 if let Token::Ident(i) = t.val {
112 let meaning = self.meaning_cache.get_case_insensitive(self.interner, i);
113 if meaning == Meaning::Include {
114 self.pos += 1;
115 return self.parse_include();
116 }
117 }
118 self.parse_production()
119 }
120
121 fn parse_include(&mut self) -> Result<Option<Line>, Spanned<ParserError>> {
122 let t = self.next(&[Expected::AnyString])?;
123 let Token::String(s) = t.val else {
124 return Err(self.unexpected_token(&[Expected::AnyString], t));
125 };
126 let mut bytes = self.interner.get(s).to_owned();
127 if bytes.contains(&b'%') {
128 let mut out = vec![];
129 let mut last_was_percent = false;
130 for (offset, &b) in bytes.as_bytes().iter().enumerate() {
131 if last_was_percent {
132 last_was_percent = false;
133 let lo = t.span.lo + offset as SpanUnit;
134 let hi = lo + 2;
135 match b {
136 b'%' => out.push(b'%'),
137 b'H' => {
138 if let Some(home) = &self.env.home {
139 out.extend_from_slice(home.as_bytes());
140 } else {
141 self.diagnostics.push(
142 self.map,
143 DiagnosticKind::HomeNotSet,
144 ad_hoc_display!("$HOME is not set").spanned(lo, hi),
145 );
146 return Ok(None);
147 }
148 }
149 b'L' => {
150 if let Some(dir) = &self.locale_file {
151 out.extend_from_slice(dir.as_bytes());
152 } else {
153 self.diagnostics.push(
154 self.map,
155 DiagnosticKind::LocaleComposeFileNotResolved,
156 ad_hoc_display!("%L could not be resolved").spanned(lo, hi),
157 );
158 return Ok(None);
159 }
160 }
161 b'S' => {
162 out.extend_from_slice(self.env.xlocaledir.as_bytes());
163 }
164 _ => {
165 self.diagnostics.push(
166 self.map,
167 DiagnosticKind::UnknownComposeIncludeEscape,
168 ad_hoc_display!("unknown escape sequence %{}", b as char => char)
169 .spanned(lo, hi),
170 );
171 return Ok(None);
172 }
173 }
174 } else if b == b'%' {
175 last_was_percent = true;
176 } else {
177 out.push(b);
178 }
179 }
180 bytes = Code::new(&Arc::new(out)).to_slice().to_owned();
181 }
182 if let Some(next) = self.try_peek() {
183 return Err(self.expected_eol(next));
184 }
185 Ok(Some(Line::Include(bytes)))
186 }
187
188 fn parse_production(&mut self) -> Result<Option<Line>, Spanned<ParserError>> {
189 let mut steps = vec![];
190 loop {
191 let (mask, _mods) = self.parse_modifiers()?;
192 let token = self.next(LHS)?;
193 if token.val == token![:] && mask.0 == 0 && steps.is_not_empty() {
194 break;
195 }
196 let Token::Keysym(ks) = token.val else {
197 return Err(self.unexpected_token(&[Expected::AnyKeysym], token));
198 };
199 let ks = self.interner.get(ks);
200 let Some(ks) = Keysym::from_str(ks) else {
201 return Err(self.unknown_keysym(ks, token.span));
202 };
203 steps.push(Step { keysym: ks });
204 }
205 let mut string = None;
206 let mut keysym = None;
207 let mut t = self.next(&[Expected::AnyString, Expected::AnyIdent])?;
208 'rhs: {
209 if let Token::String(s) = t.val {
210 string = Some(self.interner.get(s).to_owned());
211 match self.try_peek() {
212 Some(n) => {
213 self.pos += 1;
214 t = n
215 }
216 _ => break 'rhs,
217 }
218 }
219 let Token::Ident(ks) = t.val else {
220 return Err(self.unexpected_token(&[Expected::AnyIdent], t));
221 };
222 let ks = self.interner.get(ks);
223 let Some(ks) = Keysym::from_str(ks) else {
224 return Err(self.unknown_keysym(ks, t.span));
225 };
226 keysym = Some(ks);
227 }
228 if let Some(next) = self.try_peek() {
229 return Err(self.expected_eol(next));
230 }
231 Ok(Some(Line::Production(Production {
232 steps,
233 string,
234 keysym,
235 })))
236 }
237
238 fn parse_modifiers(&mut self) -> Result<(ModifierMask, ModifierMask), Spanned<ParserError>> {
239 let Some(t) = self.try_peek() else {
240 return Ok((ModifierMask(0), ModifierMask(0)));
241 };
242 let mut mask = ModifierMask(0);
243 if let Token::Ident(i) = t.val {
244 let meaning = self.meaning_cache.get_case_insensitive(self.interner, i);
245 if meaning == Meaning::None {
246 self.pos += 1;
247 return Ok((ModifierMask(!0), ModifierMask(0)));
248 }
249 }
250 if t.val == token![!] {
251 mask.0 = !0;
252 self.pos += 1;
253 }
254 let mut mods = ModifierMask(0);
255 while let Some(mut t) = self.try_peek() {
256 let mut tilde = false;
257 if t.val == token![~] {
258 self.pos += 1;
259 tilde = true;
260 t = self.peek(&[Expected::AnyModifier])?;
261 }
262 if let Token::Ident(i) = t.val {
263 let meaning = self.meaning_cache.get_case_insensitive(self.interner, i);
264 let mm = match meaning {
265 Meaning::Ctrl => ModifierMask::CONTROL,
266 Meaning::Caps | Meaning::Lock => ModifierMask::LOCK,
267 Meaning::Shift => ModifierMask::SHIFT,
268 Meaning::Alt | Meaning::Meta => ModifierMask::MOD1,
269 _ => return Err(self.unexpected_token(&[Expected::AnyModifier], t)),
270 };
271 mask |= mm;
272 if tilde {
273 mods &= !mm;
274 } else {
275 mods |= mm;
276 }
277 self.pos += 1;
278 } else if tilde {
279 return Err(self.unexpected_token(&[Expected::AnyModifier], t));
280 } else {
281 break;
282 }
283 }
284 Ok((mask, mods))
285 }
286}