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