parent
e88318f104
commit
59f78b9069
@ -0,0 +1,11 @@ |
|||||||
|
[package] |
||||||
|
name = "day19" |
||||||
|
version = "0.1.0" |
||||||
|
authors = ["inga-lovinde <52715130+inga-lovinde@users.noreply.github.com>"] |
||||||
|
edition = "2018" |
||||||
|
|
||||||
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html |
||||||
|
|
||||||
|
[dependencies] |
||||||
|
lazy_static = "1.4.0" |
||||||
|
regex = "1" |
@ -0,0 +1,146 @@ |
|||||||
|
use std::error::Error; |
||||||
|
use std::io::{self, BufRead}; |
||||||
|
use std::str::FromStr; |
||||||
|
|
||||||
|
#[macro_use] extern crate lazy_static; |
||||||
|
use regex::Regex; |
||||||
|
|
||||||
|
enum Rule { |
||||||
|
None, |
||||||
|
Char(char), |
||||||
|
Ref(usize), |
||||||
|
Sequence(Vec<Rule>), |
||||||
|
Choice(Vec<Rule>), |
||||||
|
} |
||||||
|
|
||||||
|
impl Default for Rule { |
||||||
|
fn default() -> Self { |
||||||
|
Rule::None |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
impl FromStr for Rule { |
||||||
|
type Err = Box<dyn Error>; |
||||||
|
|
||||||
|
fn from_str(s: &str) -> Result<Self, Self::Err> { |
||||||
|
lazy_static! { |
||||||
|
static ref CHAR_RE: Regex = Regex::new(r#"^"(\w)"$"#).unwrap(); |
||||||
|
static ref REF_RE: Regex = Regex::new(r"^(\d+)$").unwrap(); |
||||||
|
static ref CHOICE_SEPARATOR_RE: Regex = Regex::new(r"\s*\|\s*").unwrap(); |
||||||
|
static ref SEQ_SEPARATOR_RE: Regex = Regex::new(r"\s+").unwrap(); |
||||||
|
} |
||||||
|
|
||||||
|
if let Some(char_captures) = CHAR_RE.captures(s) { |
||||||
|
return Ok(Rule::Char(char_captures[1].chars().next().unwrap())); |
||||||
|
} |
||||||
|
|
||||||
|
if let Some(ref_captures) = REF_RE.captures(s) { |
||||||
|
return Ok(Rule::Ref(ref_captures[1].parse()?)); |
||||||
|
} |
||||||
|
|
||||||
|
if CHOICE_SEPARATOR_RE.is_match(s) { |
||||||
|
return Ok(Rule::Choice(CHOICE_SEPARATOR_RE.split(s).map(|s| s.parse()).collect::<Result<_, _>>()?)); |
||||||
|
} |
||||||
|
|
||||||
|
if SEQ_SEPARATOR_RE.is_match(s) { |
||||||
|
return Ok(Rule::Sequence(SEQ_SEPARATOR_RE.split(s).map(|s| s.parse()).collect::<Result<_, _>>()?)); |
||||||
|
} |
||||||
|
|
||||||
|
return Err(Box::from(format!("Failed to parse string '{}'", s))); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
struct RuleWithIndex { |
||||||
|
index: usize, |
||||||
|
rule: Rule, |
||||||
|
} |
||||||
|
|
||||||
|
impl FromStr for RuleWithIndex { |
||||||
|
type Err = Box<dyn Error>; |
||||||
|
|
||||||
|
fn from_str(s: &str) -> Result<Self, Self::Err> { |
||||||
|
lazy_static! { |
||||||
|
static ref RULE_RE: Regex = Regex::new(r"^(\d+):\s*([\S].*)$").unwrap(); |
||||||
|
} |
||||||
|
|
||||||
|
match RULE_RE.captures(s) { |
||||||
|
Some(captures) => Ok(Self { |
||||||
|
index: captures[1].parse()?, |
||||||
|
rule: captures[2].parse()?, |
||||||
|
}), |
||||||
|
None => Err(Box::from(format!("Failed to parse string '{}'", s))), |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
fn get_match(rules: &[Rule], rule: &Rule, data: &[char], offset: usize) -> Vec<usize> { |
||||||
|
match rule { |
||||||
|
Rule::None => vec![], |
||||||
|
Rule::Char(ch) => { |
||||||
|
if offset < data.len() && data[offset] == *ch { |
||||||
|
vec![offset + 1] |
||||||
|
} else { |
||||||
|
vec![] |
||||||
|
} |
||||||
|
}, |
||||||
|
Rule::Ref(rule_index) => get_match(&rules, &rules[*rule_index], &data, offset), |
||||||
|
Rule::Sequence(rules_sequence) => { |
||||||
|
let mut current_offsets = vec![offset]; |
||||||
|
for subrule in rules_sequence { |
||||||
|
current_offsets = current_offsets.into_iter() |
||||||
|
.flat_map(|current_offset| get_match(&rules, subrule, &data, current_offset)) |
||||||
|
.collect(); |
||||||
|
} |
||||||
|
|
||||||
|
current_offsets |
||||||
|
}, |
||||||
|
Rule::Choice(rules_choice) => rules_choice.iter() |
||||||
|
.flat_map(|subrule| get_match(&rules, subrule, &data, offset)) |
||||||
|
.collect(), |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
fn is_match(rules: &[Rule], rule: &Rule, data: &[char]) -> bool { |
||||||
|
get_match(&rules, &rule, &data, 0).contains(&data.len()) |
||||||
|
} |
||||||
|
|
||||||
|
fn main() { |
||||||
|
let stdin = io::stdin(); |
||||||
|
let mut lines = stdin.lock().lines().into_iter(); |
||||||
|
let mut rules_lines: Vec<RuleWithIndex> = Vec::new(); |
||||||
|
loop { |
||||||
|
let line = lines.next().unwrap().unwrap(); |
||||||
|
if line == "" { |
||||||
|
break; |
||||||
|
} |
||||||
|
|
||||||
|
rules_lines.push(line.parse().unwrap()); |
||||||
|
} |
||||||
|
|
||||||
|
let mut rules: Vec<Rule> = Vec::new(); |
||||||
|
rules.resize_with( |
||||||
|
rules_lines.iter().map(|rule_with_index| rule_with_index.index).max().unwrap() + 1, |
||||||
|
Default::default |
||||||
|
); |
||||||
|
for rule_line in rules_lines { |
||||||
|
rules[rule_line.index] = rule_line.rule; |
||||||
|
} |
||||||
|
|
||||||
|
let mut matches_count = 0; |
||||||
|
loop { |
||||||
|
match lines.next() { |
||||||
|
Some(Ok(line)) => { |
||||||
|
let result = is_match(&rules, &Rule::Ref(0), &line.chars().collect::<Vec<_>>()); |
||||||
|
println!("Match: {}", result); |
||||||
|
if result { |
||||||
|
matches_count += 1; |
||||||
|
} |
||||||
|
} |
||||||
|
_ => { |
||||||
|
break; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
println!("{}", matches_count); |
||||||
|
} |
Loading…
Reference in new issue