1#![cfg_attr(coverage_nightly, feature(coverage_attribute))]
2#![cfg_attr(test, allow(clippy::needless_pass_by_value))]
3#![cfg_attr(test, allow(clippy::wildcard_imports))]
4
5use std::{
6 convert::From, marker::PhantomData, ops, string::ToString, sync::LazyLock,
7};
8
9use derive_more::Display;
10use itertools::Itertools;
11use lalrpop_util::{ParseError, lalrpop_mod, lexer::Token};
12use openpql_prelude::*;
13use smallvec::{Array, SmallVec};
14
15pub mod ast;
16mod checker;
17mod error;
18
19pub use checker::{BoardRangeChecker, RangeChecker};
20pub use error::Error;
21use error::{LalrError, ResultE};
22
23lalrpop_mod!(
24 #[allow(clippy::empty_line_after_outer_attr)]
25 #[allow(clippy::iter_nth_zero)]
26 #[allow(clippy::nursery)]
27 #[allow(clippy::pedantic)]
28 #[allow(clippy::restriction)]
29 #[allow(clippy::useless_conversion)]
30 parser,
31 "/range.rs"
32);
33
34type Idx = u8;
35pub type Loc = usize;
36pub type LocInfo = (Loc, Loc);
37type Expected = Vec<String>;
38
39pub fn parse_expr(
40 is_shortdeck: bool,
41 src: &str,
42) -> Result<Box<ast::Expr>, Error> {
43 Ok(parser::ExprParser::new().parse(is_shortdeck, src)?)
44}
45
46#[cfg(test)]
47#[macro_use(quickcheck)]
48extern crate quickcheck_macros;
49
50#[cfg(test)]
51pub use tests::*;
52
53#[cfg(test)]
54#[cfg_attr(coverage_nightly, coverage(off))]
55pub mod tests {
56 pub use std::{cmp, fmt};
57
58 pub use openpql_prelude::s4;
59 pub use quickcheck::{Arbitrary, TestResult};
60
61 pub use super::*;
62
63 pub fn parse_card(src: &str) -> ResultE<'_, ast::RangeCard> {
64 parser::RangeCardParser::new().parse(false, src)
65 }
66
67 pub fn parse_list(src: &str) -> ResultE<'_, ast::List> {
68 parser::ListParser::new().parse(false, src)
69 }
70
71 pub fn parse_span(src: &str) -> ResultE<'_, ast::Span> {
72 parser::SpanParser::new().parse(false, src)
73 }
74
75 pub fn parse_term(src: &str) -> ResultE<'_, ast::Term> {
76 parser::TermParser::new().parse(false, src)
77 }
78
79 pub fn parse_card_sd(src: &str) -> ResultE<'_, ast::RangeCard> {
80 parser::RangeCardParser::new().parse(true, src)
81 }
82
83 pub(crate) fn assert_range_card(src: &str, expected: &str) {
84 let range_card = parse_card(src).unwrap();
85
86 assert_eq!(range_card.to_string(), expected);
87 }
88
89 pub(crate) fn assert_err<T: fmt::Debug + cmp::PartialEq>(
90 res: ResultE<'_, T>,
91 expected: Error,
92 ) {
93 assert_eq!(res, Err(expected.into()));
94 }
95
96 fn assert_str_in(vec: &[String], val: &str) {
97 assert!(vec.contains(&format!("\"{val}\"")), "{val} not in {vec:?}");
98 }
99
100 #[test]
101 fn test_error_invalid_token() {
102 assert_eq!(
103 Error::InvalidToken((0, 1)),
104 parse_expr(false, "?").unwrap_err()
105 );
106 }
107
108 #[test]
109 fn test_error_unrecognized_eof() {
110 let res = parse_expr(false, "[").unwrap_err();
111
112 if let Error::UnrecognizedEof(loc, expected) = res {
113 assert_eq!(loc, (1, 2));
114 assert_eq!(expected.len(), 3);
115
116 assert_str_in(&expected, "Suit");
117 assert_str_in(&expected, "Rank");
118 assert_str_in(&expected, "RankSuit");
119 } else {
120 panic!("Expected: UnrecognizedEof. Got: {res:?}")
121 }
122 }
123
124 #[test]
125 fn test_error_unrecognized_token() {
126 let res = parse_expr(false, "[,").unwrap_err();
127
128 if let Error::UnrecognizedToken(loc, expected) = res {
129 assert_eq!(loc, (1, 2));
130 assert_eq!(expected.len(), 3);
131
132 assert_str_in(&expected, "Suit");
133 assert_str_in(&expected, "Rank");
134 assert_str_in(&expected, "RankSuit");
135 } else {
136 panic!("Expected: UnrecognizedToken. Got: {res:?}")
137 }
138 }
139
140 #[test]
141 fn test_error() {
142 let err = LalrError::ExtraToken {
143 token: (0, Token(0, "a"), 1),
144 };
145
146 assert_eq!(Error::ExtraToken((0, 1)), err.into());
147
148 let err = LalrError::User {
149 error: Error::InvalidRank((0, 1)),
150 };
151
152 assert_eq!(Error::InvalidRank((0, 1)), err.into());
153 }
154
155 #[test]
156 fn test_from_error_to_loc() {
157 let err = Error::InvalidToken((5, 10));
158 assert_eq!(LocInfo::from(&err), (5, 10));
159
160 let err = Error::UnrecognizedEof((3, 7), vec![]);
161 assert_eq!(LocInfo::from(&err), (3, 7));
162
163 let err = Error::UnrecognizedToken((1, 4), vec![]);
164 assert_eq!(LocInfo::from(&err), (1, 4));
165
166 let err = Error::ExtraToken((2, 6));
167 assert_eq!(LocInfo::from(&err), (2, 6));
168
169 let err = Error::TooManyCardsInRange((10, 15));
170 assert_eq!(LocInfo::from(&err), (10, 15));
171
172 let err = Error::NumberOfRanksMismatchInSpan((8, 12));
173 assert_eq!(LocInfo::from(&err), (8, 12));
174
175 let err = Error::RankDistanceMismatchInSpan((4, 9));
176 assert_eq!(LocInfo::from(&err), (4, 9));
177
178 let err = Error::SuitMismatchInSpan((6, 11));
179 assert_eq!(LocInfo::from(&err), (6, 11));
180
181 let err = Error::InvalidSpan((7, 13));
182 assert_eq!(LocInfo::from(&err), (7, 13));
183
184 let err = Error::InvalidList((9, 14));
185 assert_eq!(LocInfo::from(&err), (9, 14));
186
187 let err = Error::InvalidRank((0, 1));
188 assert_eq!(LocInfo::from(&err), (0, 1));
189
190 let err = Error::InvalidSuit((11, 16));
191 assert_eq!(LocInfo::from(&err), (11, 16));
192 }
193}