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