barbarosa/cube_n/parser/
mod.rs1mod test;
2
3use pest::iterators::Pair;
4use pest_derive::Parser;
5
6use crate::generic::parse::{FromPest, IntoParseErr, Parsable, ParseError};
7
8type Result<T> = std::result::Result<T, ParseError<Rule>>;
9
10use super::{
11 moves::{rotation::AxisRotation, wide::WideMoveCreationError, Amount, ExtendedAxisMove},
12 space::{Axis, Direction, Face},
13 AxisMove, WideAxisMove,
14};
15
16#[derive(Parser)]
17#[grammar = "grammar/cube_n.pest"]
18pub struct CubeNParser;
19
20macro_rules! impl_with_current_rule {
21 ($implementor:ty; $rule:expr; |$arg:ident| $body:expr) => {
22 impl FromPest for $implementor {
23 type Rule = Rule;
24 type Parser = CubeNParser;
25
26 fn rule() -> Self::Rule {
27 $rule
28 }
29
30 fn from_pest($arg: Pair<Self::Rule>) -> Result<Self> {
31 $body
32 }
33 }
34 };
35}
36
37impl_with_current_rule! {
38 Amount;
39 Rule::amount;
40
41 |pair| match pair.as_str() {
42 "" => Ok(Amount::Single),
43 "2" => Ok(Amount::Double),
44 "'" => Ok(Amount::Inverse),
45 other => Err(ParseError::Unreachable(other.to_string())),
46 }
47}
48
49impl_with_current_rule! {
50 Face;
51 Rule::face;
52
53 |pair| match pair.as_str() {
54 "U" => Ok(Face::U),
55 "D" => Ok(Face::D),
56 "L" => Ok(Face::L),
57 "R" => Ok(Face::R),
58 "F" => Ok(Face::F),
59 "B" => Ok(Face::B),
60 other => Err(ParseError::Unreachable(other.to_string())),
61 }
62}
63
64impl_with_current_rule! {
65 AxisMove;
66 Rule::axis_move;
67
68 |pair| {
69 let mut parent = pair.into_inner();
70 let face = Face::from_pest(parent.next().into_err()?)?;
71 let amount = Amount::from_pest(parent.next().into_err()?)?;
72
73 Ok(AxisMove::new(face, amount))
74 }
75}
76
77impl_with_current_rule! {
78 u32;
79 Rule::depth;
80
81 |pair| {
82 if pair.as_str().is_empty() { Ok(0) } else { Ok(pair.as_str().parse::<u32>()?) }
83 }
84}
85
86fn parse_face_small(pair: Pair<Rule>) -> Result<Face> {
87 Face::parse(&pair.as_str().to_ascii_uppercase())
88}
89
90fn parse_face_wide(pair: Pair<Rule>) -> Result<Face> {
91 let mut inner = pair.into_inner();
92 let first = inner.next().into_err()?;
93
94 match inner.next() {
95 Some(_) => Face::from_pest(first),
96 None => parse_face_small(first),
97 }
98}
99
100impl<const N: u32> FromPest for WideAxisMove<N> {
101 type Rule = Rule;
102 type Parser = CubeNParser;
103
104 fn rule() -> Self::Rule {
105 Rule::wide_move
106 }
107
108 fn from_pest(pair: Pair<Self::Rule>) -> Result<Self> {
109 let mut inner = pair.into_inner();
110
111 let first = inner.next().into_err()?;
112
113 let (face, depth) = match first.as_rule() {
114 Rule::face => (Face::from_pest(first)?, 0),
115 Rule::depth => (
116 parse_face_wide(inner.next().into_err()?)?,
117 u32::from_pest(first)?.max(1),
118 ),
119 _ => return Err(ParseError::Unreachable(first.as_str().to_string())),
120 };
121
122 if depth > N {
123 return Err(ParseError::uknown(WideMoveCreationError::ExcededDepth(
124 depth, N,
125 )));
126 }
127
128 let amount = Amount::from_pest(inner.next().into_err()?)?;
129
130 WideAxisMove::new(face, amount, depth).map_err(ParseError::uknown)
131 }
132}
133
134impl_with_current_rule! {
135 Axis;
136 Rule::axis;
137
138 |pair| match pair.as_str() {
139 "x" => Ok(Axis::X),
140 "y" => Ok(Axis::Y),
141 "z" => Ok(Axis::Z),
142 other => Err(ParseError::Unreachable(other.to_string())),
143 }
144}
145
146impl_with_current_rule! {
147 AxisRotation;
148 Rule::rotation;
149
150 |pair| {
151 let mut parent = pair.into_inner();
152 let axis = Axis::from_pest(parent.next().into_err()?)?;
153 let amount = Amount::from_pest(parent.next().into_err()?)?;
154
155 Ok(AxisRotation::new(axis,amount))
156 }
157}
158
159fn parse_slice(pair: Pair<Rule>) -> Result<(Axis, bool)> {
160 match pair.as_str() {
161 "M" => Ok((Axis::X, false)),
162 "m" => Ok((Axis::X, true)),
163 "E" => Ok((Axis::Y, false)),
164 "e" => Ok((Axis::Y, true)),
165 "S" => Ok((Axis::Z, false)),
166 "s" => Ok((Axis::Z, true)),
167 other => Err(ParseError::Unreachable(other.to_string())),
168 }
169}
170
171fn parse_slice_move(pair: Pair<Rule>) -> Result<(AxisRotation, bool)> {
172 let mut inner = pair.into_inner();
173
174 let (axis, wide) = parse_slice(inner.next().into_err()?)?;
175 let mut amount = Amount::from_pest(inner.next().into_err()?)?;
176
177 if axis != Axis::Z {
179 amount = amount * Direction::Negative;
180 }
181
182 Ok((AxisRotation::new(axis, amount), wide))
183}
184
185impl_with_current_rule! {
186 ExtendedAxisMove;
187 Rule::extended_move;
188
189 |pair| {
190 let pair = pair.into_inner().next().into_err()?;
191 let mov = match pair.as_rule() {
192 Rule::axis_move => ExtendedAxisMove::Regular(AxisMove::from_pest(pair)?),
193 Rule::wide_move => {
194 let mov = WideAxisMove::from_pest(pair)?;
196 match mov.depth() {
197 0 => ExtendedAxisMove::Regular(mov.axis_move),
198 _ => ExtendedAxisMove::Wide(mov),
199 }
200 },
201 Rule::rotation => ExtendedAxisMove::Rotation(AxisRotation::from_pest(pair)?),
202 Rule::slice_move => {
203 let (rot, wide) = parse_slice_move(pair)?;
204 ExtendedAxisMove::Slice { rot, wide }
205 },
206 _ => return Err(ParseError::Unreachable(pair.as_str().to_string())),
207 };
208
209 Ok(mov)
210 }
211}