Skip to content

Instantly share code, notes, and snippets.

@AnthonyMikh
Last active December 16, 2022 14:57
Show Gist options
  • Save AnthonyMikh/d582ff0ba1cde2987ab107ec0b05772d to your computer and use it in GitHub Desktop.
Save AnthonyMikh/d582ff0ba1cde2987ab107ec0b05772d to your computer and use it in GitHub Desktop.
Crude parsing
#[derive(Clone)]
struct Lexer<'a> {
s: &'a str,
}
impl<'a> Lexer<'a> {
fn of(s: &'a str) -> Self {
Self { s }
}
fn shift(&mut self, pos: usize) {
self.s = &self.s[pos..];
}
fn end(&mut self) -> Option<()> {
if self.s.is_empty() {
Some(())
} else {
None
}
}
fn literal(&mut self, literal: &str) -> Option<()> {
self.s = self.s.strip_prefix(literal)?;
Some(())
}
fn before_literal(&mut self, literal: &str) -> Option<&'a str> {
let pos = self.s.find(literal)?;
let ret = &self.s[..pos];
self.shift(pos + literal.len());
Some(ret)
}
fn number<Num: std::str::FromStr>(&mut self) -> Option<Num> {
let pos = self
.s
.as_bytes()
.iter()
.position(|ch| !ch.is_ascii_digit())
.unwrap_or(self.s.len());
let ret = self.s[..pos].parse().ok()?;
self.shift(pos);
Some(ret)
}
fn signed_number<Num: std::str::FromStr>(&mut self) -> Option<Num> {
let offset = self.s.starts_with("-") as usize;
let offsetted = &self.s[offset..];
let pos = offsetted
.as_bytes()
.iter()
.position(|ch| !ch.is_ascii_digit())
.unwrap_or(offsetted.len()) + offset;
let ret = self.s[..pos].parse().ok()?;
self.shift(pos);
Some(ret)
}
fn optional<T, F: FnOnce(&mut Self) -> Option<T>>(&mut self, f: F) -> Option<T> {
let backtrack = self.clone();
let ret = f(self);
if ret.is_none() {
*self = backtrack;
}
ret
}
}
fn parse_bags_with_amount(line: &str) -> Option<(&str, Vec<(&str, usize)>)> {
let mut p = Lexer::of(line);
let outer_color = p.before_literal(" bags contain ")?;
if p.optional(|p| {
p.literal("no other bags.")?;
p.end()
})
.is_some()
{
return Some((outer_color, Vec::new()));
}
let mut inner_colors = Vec::new();
loop {
let amount = p.number()?;
let inner_color = p.before_literal(" bag")?.trim_start_matches(' ');
p.optional(|p| p.literal("s"));
inner_colors.push((inner_color, amount));
if p.optional(|p| p.literal(", ")).is_none() {
break;
}
}
p.literal(".")?;
p.end()?;
Some((outer_color, inner_colors))
}
fn main() {
let input = "\
bright white bags contain 1 shiny gold bag.
vibrant plum bags contain 5 faded blue bags, 6 dotted black bags.
faded blue bags contain no other bags.";
let parsed = input
.lines()
.map(parse_bags_with_amount)
.map(Option::unwrap)
.collect::<Vec<_>>();
assert_eq!(
parsed,
[
("bright white", vec![("shiny gold", 1)]),
("vibrant plum", vec![("faded blue", 5), ("dotted black", 6)]),
("faded blue", vec![]),
],
);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment