barbarosa/cube_n/parser/
mod.rs

1mod 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    // because E and M slices are inverted :eyeroll:
178    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                // If it's a depth 0 wide move, just make it an axis move
195                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}