1use std::vec::Drain;
6
7use crate::actions::Parser;
8use crate::error::{Error, Notice};
9use crate::filter::Tokens;
10use crate::generated::keywords::{self, Keyword};
11use crate::generated::tables::{
12 ACTION_BASE, CHARACTER, CHECK, DEFAULT_GOTO, DEFAULT_REDUCTION, ENTRIES, ERROR, FINAL,
13 GOTO_BASE, NO_BASE, RULE_LENGTH, RULE_LHS, RULE_NAME, SYMBOL_NAME,
14};
15use crate::lexer::Lexer;
16
17pub fn character(c: u8) -> Option<u16> {
19 let token = *CHARACTER.get(usize::from(c))?;
20 (token != 0).then_some(token)
21}
22
23pub fn keyword(word: &str) -> Option<&'static Keyword> {
25 keywords::lookup(word.as_bytes())
26}
27
28pub fn keywords() -> &'static [Keyword] {
30 &keywords::KEYWORDS
31}
32
33#[derive(Clone, Copy, PartialEq, Eq, Debug)]
35pub struct SyntaxError {
36 pub token: usize,
38}
39
40pub fn rule_name(rule: usize) -> Option<&'static str> {
42 RULE_NAME.get(rule).copied()
43}
44
45pub fn symbol_name(symbol: usize) -> Option<&'static str> {
47 SYMBOL_NAME.get(symbol).copied()
48}
49
50fn action(state: usize, token: u16) -> i16 {
52 let base = ACTION_BASE[state];
53 if base != NO_BASE {
54 let at = base + i32::from(token);
55 if let Ok(at) = usize::try_from(at)
56 && CHECK.get(at) == Some(&(token as i16))
57 {
58 return ENTRIES[at];
59 }
60 }
61 match DEFAULT_REDUCTION[state] {
62 0 => ERROR,
63 rule => -(rule as i16),
64 }
65}
66
67fn goto(state: usize, nonterminal: usize) -> usize {
69 let base = GOTO_BASE[nonterminal];
70 if base != NO_BASE {
71 let at = base + state as i32;
72 if let Ok(at) = usize::try_from(at)
73 && CHECK.get(at) == Some(&(state as i16))
74 {
75 return ENTRIES[at] as usize;
76 }
77 }
78 usize::from(DEFAULT_GOTO[nonterminal])
79}
80
81pub(crate) trait Machine {
83 type Value;
85 type Error;
87
88 fn token(&mut self) -> Result<(u16, Self::Value, i32), Self::Error>;
90
91 fn reduce(
93 &mut self,
94 rule: usize,
95 rhs: Drain<'_, Self::Value>,
96 at: &[i32],
97 here: i32,
98 ) -> Result<Self::Value, Self::Error>;
99}
100
101pub(crate) fn run<M: Machine>(machine: &mut M) -> Result<Option<M::Value>, M::Error> {
103 let mut stack: Vec<usize> = vec![0];
104 let mut values: Vec<M::Value> = Vec::new();
106 let mut locations: Vec<i32> = Vec::new();
107 let mut ahead: Option<(u16, M::Value, i32)> = None;
108 loop {
109 let state = stack[stack.len() - 1];
111 let entry = if ACTION_BASE[state] == NO_BASE {
112 match DEFAULT_REDUCTION[state] {
113 0 => ERROR,
114 rule => -(rule as i16),
115 }
116 } else {
117 let token = match &ahead {
118 Some((token, _, _)) => *token,
119 None => ahead.insert(machine.token()?).0,
120 };
121 action(state, token)
122 };
123 if entry == ERROR {
124 if ahead.is_none() {
125 machine.token()?;
126 }
127 return Ok(None);
128 }
129 if entry > 0 {
130 if entry == FINAL {
131 return Ok(values.pop());
132 }
133 let Some((_, value, location)) = ahead.take() else {
134 return Ok(None);
136 };
137 stack.push(entry as usize);
138 values.push(value);
139 locations.push(location);
140 continue;
141 }
142 let rule = usize::from(entry.unsigned_abs());
143 let from = stack.len() - 1 - usize::from(RULE_LENGTH[rule]);
144 let at = &locations[from..];
145 let here = at.iter().copied().find(|&location| location >= 0).unwrap_or(-1);
147 let value = machine.reduce(rule, values.drain(from..), at, here)?;
148 locations.truncate(from);
149 values.push(value);
150 locations.push(here);
151 stack.truncate(from + 1);
152 let state = stack[stack.len() - 1];
153 stack.push(goto(state, usize::from(RULE_LHS[rule])));
154 }
155}
156
157struct List<'a> {
159 tokens: &'a [u16],
160 at: usize,
161}
162
163impl Machine for List<'_> {
164 type Value = ();
165 type Error = std::convert::Infallible;
166
167 fn token(&mut self) -> Result<(u16, (), i32), Self::Error> {
168 self.at += 1;
169 Ok((self.tokens.get(self.at - 1).copied().unwrap_or(0), (), -1))
170 }
171
172 fn reduce(&mut self, _: usize, _: Drain<'_, ()>, _: &[i32], _: i32) -> Result<(), Self::Error> {
173 Ok(())
174 }
175}
176
177pub fn recognize(tokens: &[u16]) -> Result<(), SyntaxError> {
179 let mut list = List { tokens, at: 0 };
180 match run(&mut list) {
181 Ok(Some(())) => Ok(()),
182 Ok(None) | Err(_) => Err(SyntaxError { token: list.at - 1 }),
183 }
184}
185
186struct Check<'a> {
188 tokens: Tokens<'a>,
189 last: (usize, usize),
190}
191
192impl Machine for Check<'_> {
193 type Value = ();
194 type Error = Error;
195
196 fn token(&mut self) -> Result<(u16, (), i32), Error> {
197 let token = self.tokens.next_token()?;
198 self.last = (token.start, token.end);
199 Ok((token.kind, (), -1))
200 }
201
202 fn reduce(&mut self, _: usize, _: Drain<'_, ()>, _: &[i32], _: i32) -> Result<(), Error> {
203 Ok(())
204 }
205}
206
207pub fn check(text: &str) -> Result<Vec<Notice>, Error> {
209 let mut check = Check { tokens: Tokens::new(text), last: (0, 0) };
210 if run(&mut check)?.is_some() {
211 Ok(check.tokens.take_notices())
212 } else {
213 let text = Lexer::new(text).text().as_bytes();
214 Err(Error::syntax(text, "syntax error", check.last.0, check.last.1))
215 }
216}
217
218pub fn parse(text: &str) -> Result<(crate::nodes::List, Vec<Notice>), Error> {
220 let mut parser = Parser::new(text);
221 if run(&mut parser)?.is_none() {
222 return Err(parser.yyerror("syntax error"));
223 }
224 Ok(parser.finish())
225}
226
227pub fn parse_type_name(text: &str) -> Result<crate::nodes::TypeName, Error> {
229 let mut parser = Parser::with_mode(text, Some(crate::token::MODE_TYPE_NAME));
230 if run(&mut parser)?.is_none() {
231 return Err(parser.yyerror("syntax error"));
232 }
233 match parser.finish().0.pop() {
234 Some(Some(crate::nodes::Node::TypeName(name))) => Ok(*name),
235 _ => Err(Error::internal("the mode of a type name gave no TypeName")),
236 }
237}
238
239pub fn parse_plpgsql_expr(text: &str) -> Result<crate::nodes::Node, Error> {
241 let mut parser = Parser::with_mode(text, Some(crate::token::MODE_PLPGSQL_EXPR));
242 if run(&mut parser)?.is_none() {
243 return Err(parser.yyerror("syntax error"));
244 }
245 match parser.finish().0.pop() {
246 Some(Some(crate::nodes::Node::RawStmt(raw))) => {
247 raw.stmt.ok_or_else(|| Error::internal("the mode of an expression gave no statement"))
248 }
249 _ => Err(Error::internal("the mode of an expression gave no RawStmt")),
250 }
251}
252
253pub fn parse_plpgsql_assign(text: &str, names: usize) -> Result<crate::nodes::Node, Error> {
255 let mode = match names {
256 1 => crate::token::MODE_PLPGSQL_ASSIGN1,
257 2 => crate::token::MODE_PLPGSQL_ASSIGN2,
258 _ => crate::token::MODE_PLPGSQL_ASSIGN3,
259 };
260 let mut parser = Parser::with_mode(text, Some(mode));
261 if run(&mut parser)?.is_none() {
262 return Err(parser.yyerror("syntax error"));
263 }
264 match parser.finish().0.pop() {
265 Some(Some(crate::nodes::Node::RawStmt(raw))) => {
266 raw.stmt.ok_or_else(|| Error::internal("the mode of an assignment gave no statement"))
267 }
268 _ => Err(Error::internal("the mode of an assignment gave no RawStmt")),
269 }
270}
271
272#[cfg(test)]
273mod tests {
274 use super::*;
275 use crate::token::*;
276
277 fn c(ch: u8) -> u16 {
278 character(ch).unwrap()
279 }
280
281 #[test]
282 fn statements() {
283 assert_eq!(recognize(&[]), Ok(()));
284 assert_eq!(recognize(&[SELECT, ICONST]), Ok(()));
285 assert_eq!(recognize(&[SELECT, ICONST, c(b'+'), ICONST, c(b';'), SELECT]), Ok(()));
286 assert_eq!(recognize(&[SELECT, FROM, IDENT]), Ok(()));
287 let create = [CREATE, TABLE, IDENT, c(b'('), IDENT, INT_P, c(b','), IDENT, IDENT, c(b')')];
288 assert_eq!(recognize(&create), Ok(()));
289 let not_in = [SELECT, IDENT, NOT_LA, IN_P, c(b'('), ICONST, c(b')')];
290 assert_eq!(recognize(¬_in), Ok(()));
291 }
292
293 #[test]
294 fn names() {
295 assert_eq!(rule_name(1), Some("parse_toplevel.1"));
296 assert_eq!(rule_name(crate::RULES), None);
297 assert_eq!(symbol_name(usize::from(SELECT)), Some("SELECT"));
298 assert_eq!(symbol_name(usize::from(c(b'+'))), Some("'+'"));
299 assert_eq!(symbol_name(crate::TOKENS), Some("$accept"));
300 }
301
302 #[test]
303 fn errors() {
304 assert_eq!(recognize(&[SELECT, ICONST, c(b'+')]), Err(SyntaxError { token: 3 }));
305 assert_eq!(recognize(&[SELECT, ICONST, ICONST]), Err(SyntaxError { token: 2 }));
306 assert_eq!(recognize(&[CREATE, SELECT]), Err(SyntaxError { token: 1 }));
307 let chain = [SELECT, IDENT, c(b'='), IDENT, c(b'='), IDENT];
309 assert_eq!(recognize(&chain), Err(SyntaxError { token: 4 }));
310 }
311
312 #[test]
313 fn keywords() {
314 use crate::Category;
315 use crate::generated::keywords::KEYWORDS;
316 assert_eq!(
317 keyword("SELECT").map(|k| (k.token, k.category)),
318 Some((SELECT, Category::Reserved))
319 );
320 assert_eq!(keyword("abort").map(|k| k.token), Some(ABORT_P));
321 assert_eq!(keyword("zone").map(|k| k.bare_label), Some(true));
322 assert!(keyword("selectx").is_none());
323 assert!(keyword("").is_none());
324 for k in &KEYWORDS {
325 assert!(std::ptr::eq(keyword(k.name).unwrap(), k));
326 assert!(std::ptr::eq(keyword(&k.name.to_uppercase()).unwrap(), k));
327 }
328 }
329
330 #[test]
332 fn check_errors() {
333 let cases = [
334 ("SELEC 1", "42601", 1, "syntax error at or near \"SELEC\""),
335 ("SELECT 1 +", "42601", 11, "syntax error at end of input"),
336 ("SELECT 'abc", "42601", 8, "unterminated quoted string at or near \"'abc\""),
337 (
338 "SELECT /* x /* y */",
339 "42601",
340 8,
341 "unterminated /* comment at or near \"/* x /* y */\"",
342 ),
343 ("SELECT 1abc", "42601", 8, "trailing junk after numeric literal at or near \"1abc\""),
344 ("SELECT 0x", "42601", 8, "invalid hexadecimal integer at or near \"0x\""),
345 ("SELECT 0b2", "42601", 8, "trailing junk after numeric literal at or near \"0b2\""),
346 (
347 "SELECT 1_000$1",
348 "42601",
349 8,
350 "trailing junk after numeric literal at or near \"1_000$1\"",
351 ),
352 ("SELECT $1a", "42601", 8, "trailing junk after parameter at or near \"$1a\""),
353 ("SELECT \"\"", "42601", 8, "zero-length delimited identifier at or near \"\"\"\""),
354 ("SELECT E'\\u12'", "22025", 10, "invalid Unicode escape"),
355 ("SELECT E'\\0'", "22021", 0, "invalid byte sequence for encoding \"UTF8\": 0x00"),
356 ("SELECT E'\\xff'", "22021", 0, "invalid byte sequence for encoding \"UTF8\": 0xff"),
357 ("SELECT U&'\\0000'", "42601", 11, "invalid Unicode escape value"),
358 (
359 "SELECT U&'x' UESCAPE 'ab'",
360 "42601",
361 22,
362 "invalid Unicode escape character at or near \"'ab'\"",
363 ),
364 (
365 "SELECT U&'x' UESCAPE 1",
366 "42601",
367 22,
368 "UESCAPE must be followed by a simple string literal at or near \"1\"",
369 ),
370 (
371 "SELECT U&'x' UESCAPE",
372 "42601",
373 21,
374 "UESCAPE must be followed by a simple string literal at end of input",
375 ),
376 (
377 "SELECT $99999999999",
378 "42601",
379 8,
380 "parameter number too large at or near \"$99999999999\"",
381 ),
382 ("SELECT 'a' 'b'", "42601", 12, "syntax error at or near \"'b'\""),
383 (
384 "SELECT $a$abc$b$",
385 "42601",
386 8,
387 "unterminated dollar-quoted string at or near \"$a$abc$b$\"",
388 ),
389 ("SELECT B'01", "42601", 8, "unterminated bit string literal at or near \"B'01\""),
390 (
391 "SELECT X'0f",
392 "42601",
393 8,
394 "unterminated hexadecimal string literal at or near \"X'0f\"",
395 ),
396 ("SELECT E'\\ud83d'", "42601", 16, "invalid Unicode surrogate pair at or near \"'\""),
397 (
398 "SELECT E'\\ud83d\\u0041'",
399 "42601",
400 16,
401 "invalid Unicode surrogate pair at or near \"\\u0041\"",
402 ),
403 (
404 "SELECT E'\\udc00'",
405 "42601",
406 10,
407 "invalid Unicode surrogate pair at or near \"\\udc00\"",
408 ),
409 (
410 "SELECT E'\\U00110000'",
411 "42601",
412 10,
413 "invalid Unicode escape value at or near \"\\U00110000\"",
414 ),
415 ("SELECT U&'\\D83Dx'", "42601", 16, "invalid Unicode surrogate pair"),
416 ("SELECT U&'\\12'", "42601", 11, "invalid Unicode escape"),
417 ("SELECT U&'!0041' UESCAPE '!' 1", "42601", 30, "syntax error at or near \"1\""),
418 ("SELECT 1e+", "42601", 8, "trailing junk after numeric literal at or near \"1e+\""),
419 ("SELECT 1..2", "42601", 9, "syntax error at or near \"..\""),
420 ("SELECT é FROM 1", "42601", 15, "syntax error at or near \"1\""),
421 ("SELECT 'a'\n-- c\n'b' 1", "42601", 21, "syntax error at or near \"1\""),
422 ("SELECT 1 =- 1 1", "42601", 15, "syntax error at or near \"1\""),
423 ("SELECT 1 <=> 1 1", "42601", 16, "syntax error at or near \"1\""),
424 ("SELECT 0x_", "42601", 8, "invalid hexadecimal integer at or near \"0x_\""),
425 ("SELECT E'\\", "42601", 8, "unterminated quoted string at or near \"E'\\\""),
426 ("SELECT 1 WITH ORDINALITY", "42601", 10, "syntax error at or near \"WITH\""),
427 ("SELECT 1 /* a */ /* b", "42601", 18, "unterminated /* comment at or near \"/* b\""),
428 ("SELECT $1 $2", "42601", 11, "syntax error at or near \"$2\""),
429 ];
430 for (text, code, position, message) in cases {
431 let error = check(text).unwrap_err();
432 let got =
433 (error.code.as_str(), error.position(text).unwrap_or(0), error.message.as_str());
434 assert_eq!(got, (code, position, message), "{text}");
435 }
436 assert_eq!(check("SELECT 1 NOT IN (1); SELECT 'a'\n'b'"), Ok(Vec::new()));
437 assert_eq!(check("SELEC 1 'abc").unwrap_err().message, "syntax error at or near \"SELEC\"");
439 assert_eq!(check(&"a".repeat(64)).unwrap_err().code.as_str(), "42601");
440 let notices = check(&format!("SELECT {}", "a".repeat(64))).unwrap();
441 assert_eq!(notices[0].code.as_str(), "42622");
442 }
443}