1use std::str::FromStr;
5
6use anyhow::{Context, anyhow};
7use nom::{
8 IResult as IResultBase, Parser,
9 branch::alt,
10 bytes::complete::tag,
11 character::complete::{alpha1, alphanumeric0, char, digit1 as digit, space0},
12 combinator::{map, map_res, opt, peek},
13 error::{ErrorKind, ParseError, context},
14 multi::{many0, separated_list1},
15 sequence::{delimited, pair, preceded},
16};
17
18use crate::{adem_algebra::AdemBasisElement, algebra::milnor_algebra::PPart};
19
20type IResult<I, O> = IResultBase<I, O, nom::error::Error<I>>;
21
22#[derive(Debug, Clone)]
23pub enum AlgebraBasisElt {
24 AList(Vec<BocksteinOrSq>), PList(PPart),
26 P(u32),
27 Q(u32),
28}
29
30#[derive(Debug, Clone)]
31pub enum AlgebraNode {
32 Product(Box<Self>, Box<Self>),
33 Sum(Box<Self>, Box<Self>),
34 BasisElt(AlgebraBasisElt),
35 Scalar(i32),
36}
37
38pub type ModuleNode = Vec<(AlgebraNode, String)>;
39
40pub(crate) fn space<'a, O, E: ParseError<&'a str>, F: Parser<&'a str, Output = O, Error = E>>(
42 f: F,
43) -> impl Parser<&'a str, Output = O, Error = E> {
44 delimited(space0, f, space0)
45}
46
47pub(crate) fn brackets<'a, O, E: ParseError<&'a str>, F: Parser<&'a str, Output = O, Error = E>>(
49 f: F,
50) -> impl Parser<&'a str, Output = O, Error = E> {
51 delimited(char('('), f, char(')'))
52}
53
54pub(crate) fn digits<T: FromStr + Copy>(i: &str) -> IResult<&str, T> {
55 map_res(space(digit), FromStr::from_str).parse(i)
56}
57
58pub(crate) fn p_or_sq(i: &str) -> IResult<&str, &str> {
59 alt((tag("P"), tag("Sq"))).parse(i)
60}
61
62fn fold_separated<I: Clone, OS, O, E>(
63 mut sep: impl Parser<I, Output = OS, Error = E>,
64 mut f: impl Parser<I, Output = O, Error = E>,
65 mut acc: impl FnMut(O, O) -> O,
66) -> impl FnMut(I) -> IResultBase<I, O, E> {
67 move |i: I| {
68 let (mut i, mut res) = f.parse(i)?;
69 loop {
70 match sep.parse(i.clone()) {
71 Err(nom::Err::Error(_)) => return Ok((i, res)),
72 Err(e) => return Err(e),
73 Ok((i1, _)) => match f.parse(i1.clone()) {
74 Err(nom::Err::Error(_)) => return Ok((i, res)),
75 Err(e) => return Err(e),
76 Ok((i2, o)) => {
77 i = i2;
78 res = acc(res, o);
79 }
80 },
81 }
82 }
83 }
84}
85
86#[derive(Clone, Debug, Copy)]
87pub enum BocksteinOrSq {
88 Bockstein,
89 Sq(u32),
90}
91
92impl BocksteinOrSq {
93 pub(crate) fn to_adem_basis_elt(self, q: i32) -> AdemBasisElement {
94 match self {
95 Self::Bockstein => {
96 if q == 1 {
97 AdemBasisElement {
98 degree: 1,
99 bocksteins: 0,
100 ps: vec![1],
101 p_or_sq: false,
102 }
103 } else {
104 AdemBasisElement {
105 degree: 1,
106 bocksteins: 1,
107 ps: vec![],
108 p_or_sq: true,
109 }
110 }
111 }
112 Self::Sq(x) => AdemBasisElement {
113 degree: x as i32 * q,
114 bocksteins: 0,
115 ps: vec![x],
116 p_or_sq: q != 1,
117 },
118 }
119 }
120}
121
122fn algebra_generator(i: &str) -> IResult<&str, AlgebraBasisElt> {
123 alt((
124 map(char('b'), |_| AlgebraBasisElt::Q(0)),
125 map(preceded(char('Q'), digits), AlgebraBasisElt::Q),
126 map(preceded(p_or_sq, digits), AlgebraBasisElt::P),
127 map(
128 alt((
129 preceded(p_or_sq, brackets(separated_list1(char(','), digits))),
130 preceded(char('M'), brackets(many0(digits))),
131 )),
132 AlgebraBasisElt::PList,
133 ),
134 map(
135 preceded(
136 char('A'),
137 brackets(many0(alt((
138 map(char('b'), |_| BocksteinOrSq::Bockstein),
139 map(digits, BocksteinOrSq::Sq),
140 )))),
141 ),
142 AlgebraBasisElt::AList,
143 ),
144 ))
145 .parse(i)
146}
147
148fn scalar(i: &str) -> IResult<&str, i32> {
149 alt((
150 digits,
151 preceded(char('+'), digits),
152 map(preceded(char('-'), digits), |x: i32| -x),
153 ))
154 .parse(i)
155}
156
157fn algebra_factor(i: &str) -> IResult<&str, AlgebraNode> {
158 space(alt((
159 map(algebra_generator, AlgebraNode::BasisElt),
160 map(scalar, AlgebraNode::Scalar),
161 brackets(algebra_expr),
162 )))
163 .parse(i)
164}
165
166fn algebra_term(i: &str) -> IResult<&str, AlgebraNode> {
167 let (i, sign) = opt(alt((char('+'), char('-')))).parse(i)?;
168
169 let (i, mut res) = fold_separated(char('*'), algebra_factor, |acc, val| {
170 AlgebraNode::Product(Box::new(acc), Box::new(val))
171 })(i)?;
172
173 if let Some('-') = sign {
174 res = AlgebraNode::Product(Box::new(AlgebraNode::Scalar(-1)), Box::new(res));
175 }
176 Ok((i, res))
177}
178
179fn algebra_expr(i: &str) -> IResult<&str, AlgebraNode> {
180 fold_separated(
181 peek(alt((char('+'), char('-')))),
182 space(algebra_term),
183 |acc, val| AlgebraNode::Sum(Box::new(acc), Box::new(val)),
184 )(i)
185}
186
187fn module_generator(i: &str) -> IResult<&str, String> {
188 let (rest, (a, more_str)) = pair(alpha1, alphanumeric0).parse(i)?;
189 if a.starts_with("Sq") || a.starts_with('P') || a.starts_with('Q') {
190 return Err(nom::Err::Failure(nom::error::Error::new(
191 &i[0..a.len()],
192 ErrorKind::Verify,
193 )));
194 }
195 Ok((rest, a.to_string() + more_str))
196}
197
198fn module_term(i: &str) -> IResult<&str, ModuleNode> {
199 use AlgebraNode::*;
200
201 let (i, prefix) = opt(alt((
202 map(pair(space(algebra_term), char('*')), |(a, _)| a),
203 map(char('-'), |_| Scalar(-1)),
204 map(char('+'), |_| Scalar(1)),
205 )))
206 .parse(i)?;
207
208 match space(module_generator).parse(i) {
209 Ok((i, g)) => return Ok((i, vec![(prefix.unwrap_or(Scalar(1)), g)])),
210 Err(nom::Err::Error(_)) => (),
211 Err(e) => return Err(e),
212 }
213
214 let (i, expr) =
215 context("Parsing bracketed expression", space(brackets(module_expr))).parse(i)?;
216 Ok((
217 i,
218 match prefix {
219 Some(a) => expr
220 .into_iter()
221 .map(|(b, v)| (Product(Box::new(a.clone()), Box::new(b)), v))
222 .collect(),
223 None => expr,
224 },
225 ))
226}
227
228fn module_expr(i: &str) -> IResult<&str, ModuleNode> {
229 fold_separated(
230 peek(alt((char('+'), char('-')))),
231 module_term,
232 |mut a, b| {
233 a.extend_from_slice(&b);
234 a
235 },
236 )(i)
237}
238
239fn convert_error(i: &str) -> impl FnOnce(nom::Err<nom::error::Error<&str>>) -> anyhow::Error + '_ {
240 move |err| {
241 anyhow!(match err {
242 nom::Err::Error(e) | nom::Err::Failure(e) => format!(
243 "Parse error at position {}: {:?}",
244 i.len() - e.input.len(),
245 e.code
246 ),
247 _ => format!("{err:#}"),
248 })
249 }
250}
251
252pub fn parse_algebra(i: &str) -> anyhow::Result<AlgebraNode> {
253 let (rest, parse_tree) = algebra_expr(i)
254 .map_err(convert_error(i))
255 .with_context(|| format!("Error when parsing algebra string {i}"))?;
256 if rest.is_empty() {
257 Ok(parse_tree)
258 } else {
259 Err(anyhow!(
260 "Failed to consume all of input. Remaining: '{rest}'"
261 ))
262 }
263}
264
265pub fn parse_module(i: &str) -> anyhow::Result<ModuleNode> {
266 let (rest, parse_tree) = module_expr(i)
267 .map_err(convert_error(i))
268 .with_context(|| format!("Error when parsing module string {i}"))?;
269 if rest.is_empty() {
270 Ok(parse_tree)
271 } else {
272 Err(anyhow!(
273 "Failed to consume all of input. Remaining: '{rest}'"
274 ))
275 }
276}
277
278#[cfg(test)]
279mod tests {
280 use expect_test::{Expect, expect};
281
282 use super::*;
283
284 #[test]
285 fn test_parse_algebra() {
286 let check = |input, output: Expect| {
287 output.assert_eq(&format!("{:?}", parse_algebra(input).unwrap()));
288 };
289
290 check(
291 "b * Q3 * (Sq1 * A(2 b 5) + M(0 0 2) * P(0, 1) * Sq(1, 0))",
292 expect![[
293 r#"Product(Product(BasisElt(Q(0)), BasisElt(Q(3))), Sum(Product(BasisElt(P(1)), BasisElt(AList([Sq(2), Bockstein, Sq(5)]))), Product(Product(BasisElt(PList([0, 0, 2])), BasisElt(PList([0, 1]))), BasisElt(PList([1, 0])))))"#
294 ]],
295 );
296 }
297
298 #[test]
299 fn test_parse_module() {
300 let check = |input, output: Expect| {
301 output.assert_eq(&format!("{:?}", parse_module(input).unwrap()));
302 };
303
304 check("x0", expect![[r#"[(Scalar(1), "x0")]"#]]);
305
306 check("Sq2 * x0", expect![[r#"[(BasisElt(P(2)), "x0")]"#]]);
307
308 check(
309 "Sq1 * x0 + x1",
310 expect![[r#"[(BasisElt(P(1)), "x0"), (Scalar(1), "x1")]"#]],
311 );
312
313 check(
314 "(Sq3 + Sq2 * Sq1) * x0",
315 expect![[r#"[(Sum(BasisElt(P(3)), Product(BasisElt(P(2)), BasisElt(P(1)))), "x0")]"#]],
316 );
317
318 check(
319 "Sq3 * x0 + Sq2 * x1",
320 expect![[r#"[(BasisElt(P(3)), "x0"), (BasisElt(P(2)), "x1")]"#]],
321 );
322
323 check(
324 "(Sq3 - Sq2 * Sq1) * (Sq1 * x0 + x1)",
325 expect![[
326 r#"[(Product(Sum(BasisElt(P(3)), Product(Scalar(-1), Product(BasisElt(P(2)), BasisElt(P(1))))), BasisElt(P(1))), "x0"), (Product(Sum(BasisElt(P(3)), Product(Scalar(-1), Product(BasisElt(P(2)), BasisElt(P(1))))), Scalar(1)), "x1")]"#
327 ]],
328 );
329
330 check(
331 "x3 - (Sq3 * x0 + Sq1 * x2)",
332 expect![[
333 r#"[(Scalar(1), "x3"), (Product(Scalar(-1), BasisElt(P(3))), "x0"), (Product(Scalar(-1), BasisElt(P(1))), "x2")]"#
334 ]],
335 );
336 }
337
338 #[test]
339 fn test_parse_module_errors() {
340 let check = |input| {
342 assert!(parse_module(input).is_err());
343 };
344
345 check("x0 + ");
346 check("2 * (x1 + Sq1 * x0");
347 check("Sqx");
348 }
349}