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 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}