Monster parser! Or Day 19. Whatever.

This commit is contained in:
2020-12-19 13:25:03 -08:00
parent 7b86c040aa
commit 85fef40e86
5 changed files with 889 additions and 0 deletions

218
src/bin/monster_messages.rs Normal file
View File

@@ -0,0 +1,218 @@
use advent2020::errors::{GrammarParseError, TopLevelError};
use std::collections::HashMap;
use std::env;
use std::fs;
use std::str::FromStr;
struct Grammar {
rules: HashMap<usize, Rule>,
}
impl Grammar {
fn new() -> Grammar {
Grammar {
rules: HashMap::new(),
}
}
fn add_rule(&mut self, number: usize, rule: Rule) -> Result<(), GrammarParseError> {
if self.rules.insert(number, rule).is_some() {
Err(GrammarParseError::DuplicateRule(number))
} else {
Ok(())
}
}
fn parses(&self, s: &str) -> Result<bool, GrammarParseError> {
self.accepts(0, s)
.map(|x| x.iter().filter(|x| x.is_empty()).count() > 0)
}
fn accepts<'a>(&self, rule: usize, s: &'a str) -> Result<Vec<&'a str>, GrammarParseError> {
match self.rules.get(&rule) {
None => Err(GrammarParseError::UnknownRule(rule)),
Some(v) => self.rule_accepts(v, s),
}
}
fn rule_accepts<'a>(&self, rule: &Rule, s: &'a str) -> Result<Vec<&'a str>, GrammarParseError> {
match rule {
Rule::Alternatives(alts) => {
let mut results = Vec::new();
for item in alts.iter() {
let mut news = self.rule_accepts(item, s)?;
results.append(&mut news);
}
Ok(results)
}
Rule::Sequence(seqs) => {
let mut results = vec![s];
for item in seqs.iter() {
let mut new_results = vec![];
for early_result in results.drain(..) {
let mut nexts = self.rule_accepts(item, early_result)?;
new_results.append(&mut nexts);
}
results = new_results;
}
Ok(results)
}
Rule::Nonterminal(new_rule) => self.accepts(*new_rule, s),
Rule::Terminal(term) => {
if let Some(rest) = s.strip_prefix(term) {
Ok(vec![rest])
} else {
Ok(vec![])
}
}
}
}
fn rewrite(&self) -> Grammar {
let rule8 = Rule::Alternatives(vec![
Rule::Nonterminal(42),
Rule::Sequence(vec![Rule::Nonterminal(42), Rule::Nonterminal(8)]),
]);
let rule11 = Rule::Alternatives(vec![
Rule::Sequence(vec![Rule::Nonterminal(42), Rule::Nonterminal(31)]),
Rule::Sequence(vec![
Rule::Nonterminal(42),
Rule::Nonterminal(11),
Rule::Nonterminal(31),
]),
]);
let mut rules = self.rules.clone();
rules.insert(8, rule8);
rules.insert(11, rule11);
Grammar { rules }
}
}
#[derive(Clone)]
enum Rule {
Alternatives(Vec<Rule>),
Sequence(Vec<Rule>),
Nonterminal(usize),
Terminal(String),
}
impl Rule {
fn new(s: &str) -> Result<(usize, Rule), GrammarParseError> {
let mut parts = s.split(": ");
let rule_num_str = parts
.next()
.ok_or_else(|| GrammarParseError::BadRule(s.to_string()))?;
let rule_num = usize::from_str(rule_num_str)?;
let definitions = parts
.next()
.ok_or_else(|| GrammarParseError::BadRule(s.to_string()))?;
let mut alternatives = Vec::new();
for alternate in definitions.split(" | ") {
let trimmed_alternate = alternate.trim();
let mut sequence_members = Vec::new();
for member in trimmed_alternate.split_ascii_whitespace() {
let item = if member.starts_with('"') && member.ends_with('"') {
Rule::Terminal(
member
.strip_prefix('"')
.unwrap()
.strip_suffix('"')
.unwrap()
.to_string(),
)
} else {
Rule::Nonterminal(usize::from_str(member)?)
};
sequence_members.push(item);
}
match sequence_members.len() {
0 => return Err(GrammarParseError::BadRule(s.to_string())),
1 => alternatives.push(sequence_members.pop().unwrap()),
_ => alternatives.push(Rule::Sequence(sequence_members)),
}
}
match alternatives.len() {
0 => Err(GrammarParseError::BadRule(s.to_string())),
1 => Ok((rule_num, alternatives.pop().unwrap())),
_ => Ok((rule_num, Rule::Alternatives(alternatives))),
}
}
}
macro_rules! run_parser {
($parser: ident, $line: ident, $count: ident) => {
if $parser.parses($line)? {
$count += 1;
"YES"
} else {
"NO"
}
};
}
#[test]
fn rewrite_test() {
let line = "aaaaabbaabaaaaababaa";
let contents = fs::read_to_string("inputs/day19_test2.txt").unwrap();
let mut grammar = Grammar::new();
for line in contents.lines() {
if line == "" {
break;
}
let (num, rule) = Rule::new(line).unwrap();
grammar.add_rule(num, rule).unwrap();
}
let rewritten = grammar.rewrite();
assert_eq!(Ok(false), grammar.parses(line));
assert_eq!(Ok(true), rewritten.parses(line));
}
fn main() -> Result<(), TopLevelError> {
let filename = env::args().nth(1).expect("No file argument given.");
let contents = fs::read_to_string(filename)?;
let mut grammar = Grammar::new();
let mut lines = contents.lines();
for line in &mut lines {
if line == "" {
break;
}
let (num, rule) = Rule::new(line)?;
grammar.add_rule(num, rule)?;
}
let mut matched_orig_lines = 0;
let mut matched_rewritten_lines = 0;
let rewritten_grammar = grammar.rewrite();
for line in &mut lines {
let orig = run_parser!(grammar, line, matched_orig_lines);
let rewritten = run_parser!(rewritten_grammar, line, matched_rewritten_lines);
println!("{} ==> {} / {}", line, orig, rewritten);
}
println!("{} lines matched originally.", matched_orig_lines);
println!(
"{} lines matched after it was rewritten.",
matched_rewritten_lines
);
Ok(())
}

View File

@@ -54,6 +54,8 @@ pub enum TopLevelError {
BitmaskCommandParseError(#[from] BitmaskCommandParseError),
#[error("Ticket parsing error: {0}")]
TicketParseError(#[from] TicketParseError),
#[error("Bad rule parse: {0}")]
GrammarParseError(#[from] GrammarParseError),
}
#[derive(Error, Debug)]
@@ -194,3 +196,15 @@ pub enum TicketParseError {
#[error("Bad field definition: {0}")]
BadFieldDefinition(String),
}
#[derive(Error, Debug, PartialEq)]
pub enum GrammarParseError {
#[error("Bad rule reference: {0}")]
ParseIntError(#[from] ParseIntError),
#[error("Reference to unknown rule: {0}")]
UnknownRule(usize),
#[error("Bad rule definition: {0}")]
BadRule(String),
#[error("Duplicate rule definition for {0}")]
DuplicateRule(usize),
}