partiql_parser/lib.rs
1#![deny(rust_2018_idioms)]
2#![deny(clippy::all)]
3// Copyright Amazon.com, Inc. or its affiliates.
4
5//! Provides a parser for the [PartiQL][partiql] query language.
6//!
7//! # Usage
8//!
9//! ```
10//! use std::fmt;
11//! use std::fmt::Formatter;
12//! use itertools::Itertools;
13//! use partiql_common::syntax::location::LineAndColumn;
14//! use partiql_parser::{Parser, ParserError, ParserResult};
15//!
16//! let parser = Parser::default();
17//!
18//! let parsed = parser.parse("SELECT g FROM data GROUP BY a").expect("successful parse");
19//!
20//! let errs: ParserError = parser.parse("SELECT").expect_err("expected error");
21//!
22//! // Print out messages with byte offsets
23//! let errs_at: ParserError =
24//! parser.parse("SELECT * FROM a AY a CROSS JOIN c AS c AT q").unwrap_err();
25//! assert_eq!(errs_at.errors[0].to_string(), "Unexpected token `<a:UNQUOTED_IDENT>` at `(b19..b20)`");
26//!
27//! // Print out messages with line:column offsets
28//! let errs_at_nice: ParserError =
29//! parser.parse("SELECT * FROM a AY a CROSS JOIN c AS c AT q").unwrap_err();
30//! let offsets = &errs_at_nice.offsets;
31//! let source = &errs_at_nice.text;
32//! let err_msg = errs_at_nice.errors.iter().map(|e|
33//! e.clone().map_loc(|loc| LineAndColumn::from(offsets.at(source, loc).unwrap()).to_string())).join("\n");
34//! assert_eq!(err_msg, "Unexpected token `<a:UNQUOTED_IDENT>` at `(1:20..1:21)`");
35//!
36//!
37//!
38//! // Print out messages with custom line:column offsets
39//! #[derive(Debug, Copy, Clone, PartialEq, Eq, Ord, PartialOrd, Hash)]
40//! pub struct VerboseLineAndColumn(LineAndColumn);
41//!
42//! impl fmt::Display for VerboseLineAndColumn {
43//! fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
44//! write!(f, "Line {}, Offset {}", self.0.line, self.0.column)
45//! }
46//! }
47//!
48//! let err_msg = errs_at_nice.errors.iter().map(|e|
49//! e.clone().map_loc(|loc| VerboseLineAndColumn(LineAndColumn::from(offsets.at(source, loc).unwrap())).to_string())).join("\n");
50//! assert_eq!(err_msg, "Unexpected token `<a:UNQUOTED_IDENT>` at `(Line 1, Offset 20..Line 1, Offset 21)`");
51//! ```
52//!
53//! [partiql]: https://partiql.org
54
55mod error;
56mod lexer;
57mod parse;
58mod preprocessor;
59mod token_parser;
60
61use parse::{parse_partiql, AstData, ErrorData};
62use partiql_ast::ast;
63use partiql_common::syntax::line_offset_tracker::LineOffsetTracker;
64use partiql_common::syntax::location::BytePosition;
65use partiql_common::syntax::metadata::LocationMap;
66#[cfg(feature = "serde")]
67use serde::{Deserialize, Serialize};
68
69/// [`std::error::Error`] type for errors in the lexical structure for the `PartiQL` parser.
70pub type LexicalError<'input> = error::LexError<'input>;
71
72/// [`std::error::Error`] type for errors in the syntactic structure for the `PartiQL` parser.
73pub type ParseError<'input> = error::ParseError<'input, BytePosition>;
74
75/// General [`Result`] type for the `PartiQL` [`Parser`].
76pub type ParserResult<'input> = Result<Parsed<'input>, ParserError<'input>>;
77
78/// A `PartiQL` parser from statement strings to AST.
79#[non_exhaustive]
80#[derive(Debug, Default)]
81pub struct Parser {}
82
83impl Parser {
84 /// Parse a `PartiQL` statement into an AST.
85 pub fn parse<'input>(&self, text: &'input str) -> ParserResult<'input> {
86 match parse_partiql(text) {
87 Ok(AstData {
88 ast,
89 locations,
90 offsets,
91 }) => Ok(Parsed {
92 text,
93 offsets,
94 ast,
95 locations,
96 }),
97 Err(ErrorData { errors, offsets }) => Err(ParserError {
98 text,
99 offsets,
100 errors,
101 }),
102 }
103 }
104}
105
106/// The output of parsing `PartiQL` statement strings: an AST and auxiliary data.
107#[non_exhaustive]
108#[derive(Debug)]
109#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
110#[allow(dead_code)]
111pub struct Parsed<'input> {
112 pub text: &'input str,
113 pub offsets: LineOffsetTracker,
114 pub ast: ast::AstNode<ast::TopLevelQuery>,
115 pub locations: LocationMap,
116}
117
118/// The output of errors when parsing `PartiQL` statement strings: an errors and auxiliary data.
119#[non_exhaustive]
120#[allow(dead_code)]
121#[derive(Debug)]
122#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
123pub struct ParserError<'input> {
124 pub text: &'input str,
125 pub offsets: LineOffsetTracker,
126 pub errors: Vec<ParseError<'input>>,
127}