From 3f9e231ebf99277d19267c4a299b5a3a855f406d Mon Sep 17 00:00:00 2001 From: Volodymyr Patuta <6977238-fiplox@users.noreply.gitlab.com> Date: Thu, 5 Nov 2020 00:06:48 +0100 Subject: [PATCH] new paser + generate random instructions --- Cargo.toml | 2 + src/conf.pest | 3 ++ src/main.rs | 138 ++++++++++++++++++++++++-------------------------- 3 files changed, 71 insertions(+), 72 deletions(-) create mode 100644 src/conf.pest diff --git a/Cargo.toml b/Cargo.toml index a264f32..858be56 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,3 +9,5 @@ edition = "2018" [dependencies] clap = "2.33.3" rand = "0.7.3" +pest = "2.0" +pest_derive = "2.0" diff --git a/src/conf.pest b/src/conf.pest new file mode 100644 index 0000000..67df1e6 --- /dev/null +++ b/src/conf.pest @@ -0,0 +1,3 @@ +world = { ASCII_DIGIT* ~ " " ~ ASCII_DIGIT* } +robot_init = { ASCII_DIGIT* ~ " " ~ ASCII_DIGIT* ~ " " ~ ("S" | "N" | "W" | "E") } +robot_instructions = { ASCII_ALPHA_UPPER+ } diff --git a/src/main.rs b/src/main.rs index 22feaca..1568944 100644 --- a/src/main.rs +++ b/src/main.rs @@ -13,7 +13,13 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . +extern crate pest; +#[macro_use] +extern crate pest_derive; + use clap::{App, Arg}; +use pest::Parser; +use rand::Rng; use std::collections::HashMap; use std::fs; use std::io; @@ -21,6 +27,10 @@ use std::io; mod robot; mod world; +#[derive(Parser)] +#[grammar = "conf.pest"] +pub struct ConfParser; + /// Check if the robot is in the map. fn check_map(r: &robot::Robot, w: &world::World) -> Result<(), String> { if r.p.x < 0 || r.p.y < 0 || r.p.x > w.x || r.p.y > w.y { @@ -48,119 +58,96 @@ fn create_hash_map(pool: &Vec, hash: &mut HashMap String { + let mut rng = rand::thread_rng(); + let n = rng.gen_range(5, 10); + let mut instructions = String::with_capacity(n); + const CHARSET: &[u8] = b"LRF"; + for _ in 0..n { + let l = rng.gen_range(0, CHARSET.len()); + instructions.push(CHARSET[l] as char); + } + + instructions +} + /// Parse the config file, generate the world and robot pool. fn parse_config(conf: String, pool: &mut Vec) -> Result { - let mut lines = conf.lines(); - // The first line of the config file should be the World. - let raw_line: &str = match lines.next() { - Some(raw) => raw, - None => { - return Err(String::from( - "Could not read the first line of the config file !", - )) - } + let mut lines: Vec<&str> = conf.split('\n').collect(); + let raw_world = match ConfParser::parse(Rule::world, lines.remove(0)) { + Ok(s) => s.as_str(), + Err(e) => return Err(format!("{}", e)), }; - let mut tokens = raw_line.split_whitespace(); - let token1 = match tokens.next() { - Some(raw) => raw, - None => { - return Err(String::from( - "Could not read the first token of the first line !", - )) - } - }; - let token2 = match tokens.next() { - Some(raw) => raw, - None => { - return Err(String::from( - "Could not read the second token of the first line !", - )) - } - }; - let x: i32 = match token1.parse::() { - Ok(x) => x, - Err(_) => { - return Err(String::from( - "Could not convert token one from the first string to i32", - )) - } - }; - let y: i32 = match token2.parse::() { - Ok(x) => x, - Err(_) => { - return Err(String::from( - "Could not convert token two from the first string to i32", - )) - } - }; - let w = world::World { x, y }; + let mut w: Vec = Vec::with_capacity(2); + for n in raw_world.split_whitespace() { + let v: i32 = match n.parse::() { + Ok(x) => x, + Err(_) => return Err(String::from("World config is broken.")), + }; + w.push(v); + } + let world = world::World { x: w[0], y: w[1] }; + lines.remove(0); let mut r_id: u32 = 0; loop { r_id += 1; - // This line should be empty. - let empty_line = match lines.next() { - None => break, - Some(x) => x, - }; - if !empty_line.is_empty() { - return Err(String::from("This line should be empty !")); + if lines.len() == 0 { + break; } - let raw_setup = match lines.next() { - None => return Err(String::from("This line should be the config !")), - Some(raw) => raw, + let raw_setup = match ConfParser::parse(Rule::robot_init, lines.remove(0)) { + Ok(s) => s.as_str(), + Err(e) => return Err(format!("{}", e)), }; - if raw_setup.is_empty() { - return Err(String::from("This line should not be empty !")); - } - let raw_inst = match lines.next() { - None => return Err(String::from("This line should be the instruction !")), - Some(raw) => raw, + + let rand_instructions = gen_random_instructions(); + let l = lines.remove(0); + + let instructions = match ConfParser::parse(Rule::robot_instructions, l) { + Ok(s) => s.as_str(), + Err(_) => rand_instructions.as_str(), }; - if raw_inst.is_empty() { - return Err(String::from("This line should not be empty !")); - } - // Parse the setup line of the robot. + let mut setup = raw_setup.split_whitespace(); let pos_x = match setup.next() { + Some(raw) => raw, None => { return Err(String::from( "Could not read the first token of the setup line !", )) } - Some(raw) => raw, }; let pos_y = match setup.next() { + Some(raw) => raw, None => { return Err(String::from( "Could not read the second token of the setup line !", )) } - Some(raw) => raw, }; let orientation = match setup.next() { + Some(raw) => raw, None => { return Err(String::from( "Could not read the third token of the setup line !", )) } - Some(raw) => raw, }; // Convert values of the setup line let r_x = match pos_x.parse::() { + Ok(raw) => raw, Err(_) => { return Err(String::from( "Could not convert the first token of the setup ligne to i32 !", )) } - Ok(raw) => raw, }; let r_y = match pos_y.parse::() { + Ok(raw) => raw, Err(_) => { return Err(String::from( "Could not convert the second token of the setup ligne to i32 !", )) } - Ok(raw) => raw, }; let r_o = match orientation { "N" => robot::Orientation::N, @@ -173,9 +160,8 @@ fn parse_config(conf: String, pool: &mut Vec) -> Result = raw_inst.chars().rev().collect(); + let inst: Vec = instructions.chars().rev().collect(); if !robot::is_instructions(&inst) { return Err(String::from("Invalid instructions !")); } @@ -183,13 +169,21 @@ fn parse_config(conf: String, pool: &mut Vec) -> Result pool.push(r), Err(err) => return Err(err), } + + if lines.len() == 0 { + break; + } + if l.len() == 0 { + continue; + } + lines.remove(0); } - Ok(w) + Ok(world) } /// Retrieve the content of a file and return it as a string.