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