Skip to main content

cow_parser/
lib.rs

1use pest::error::Error as PestError;
2use pest::Parser;
3use pest_derive::Parser;
4use thiserror::Error;
5
6#[derive(Error, Debug)]
7pub enum ParseError {
8    #[error("Grammar parsing error: {0}")]
9    Pest(#[from] PestError<Rule>),
10}
11
12#[derive(Parser)]
13#[grammar = "cow.pest"]
14pub struct CowParser;
15
16pub fn parse_cow_source(source: &str) -> Result<pest::iterators::Pairs<Rule>, ParseError> {
17    let pairs = CowParser::parse(Rule::program, source)?;
18    Ok(pairs)
19}
20
21pub fn get_commands(source: &str) -> Result<Vec<String>, ParseError> {
22    let pairs = CowParser::parse(Rule::program, source)?;
23
24    let commands = pairs
25        .flatten()
26        .filter(|pair| {
27            matches!(
28                pair.as_rule(),
29                Rule::increment | Rule::decrement | Rule::move_left | Rule::move_right | Rule::other_command
30            )
31        })
32        .map(|pair| pair.as_str().to_string())
33        .collect();
34
35    Ok(commands)
36}
37
38pub fn optimize_cow_source(source: &str) -> Result<Vec<String>, ParseError> {
39    // Reuse the existing parser helper to get a flat list of commands.
40    let commands = get_commands(source)?;
41
42    let mut optimized = Vec::new();
43
44    let mut inc: usize = 0;
45    let mut dec: usize = 0;
46    let mut left_moves: usize = 0;
47    let mut right_moves: usize = 0;
48
49    let mut flush_counts = |inc: &mut usize, dec: &mut usize, out: &mut Vec<String>| {
50        if *inc > 0 {
51            out.push(format!("increment({})", *inc));
52            *inc = 0;
53        }
54        if *dec > 0 {
55            out.push(format!("decrement({})", *dec));
56            *dec = 0;
57        }
58    };
59
60    for cmd in commands {
61        match cmd.as_str() {
62            "MoO" => {
63                if dec > 0 {
64                    flush_counts(&mut inc, &mut dec, &mut optimized);
65                }
66                inc += 1;
67            }
68            "MOo" => {
69                if inc > 0 {
70                    flush_counts(&mut inc, &mut dec, &mut optimized);
71                }
72                dec += 1;
73            }
74            "mOo" => {
75                left_moves += 1;
76            }
77            "moO" => {
78                right_moves += 1;
79            }
80            _ => {
81                flush_counts(&mut inc, &mut dec, &mut optimized);
82                optimized.push(cmd);
83            }
84        }
85    }
86    flush_counts(&mut inc, &mut dec, &mut optimized);
87
88    if right_moves > left_moves {
89        for _ in 0..(right_moves - left_moves) {
90            optimized.push("moO".to_string());
91        }
92    } else if left_moves > right_moves {
93        for _ in 0..(left_moves - right_moves) {
94            optimized.push("mOo".to_string());
95        }
96    }
97
98    Ok(optimized)
99}