Skip to content

Commit

Permalink
Don't stringify tokens before parsing them
Browse files Browse the repository at this point in the history
This parses the tokens directly in-memory, avoiding several memory allocations.

This currently requires a fork of Nom to avoid needing a newtype wrapper with lots of traits.

```
Benchmark 1: target/release/day18
  Time (mean ± σ):     161.3 ms ±   5.0 ms    [User: 160.8 ms, System: 0.5 ms]
  Range (min … max):   156.9 ms … 173.0 ms    17 runs
```
  • Loading branch information
jyn514 committed Dec 24, 2021
1 parent 610890d commit a8fd28a
Show file tree
Hide file tree
Showing 4 changed files with 22 additions and 11 deletions.
3 changes: 1 addition & 2 deletions day18/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion day18/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,4 @@ edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
nom = "7"
nom = { version = "7", git = "https://github.com/jyn514/nom", branch = "blanket-impls" }
26 changes: 19 additions & 7 deletions day18/src/magnitude.rs
Original file line number Diff line number Diff line change
@@ -1,22 +1,25 @@
use nom::{
branch::alt, bytes::complete::take_while_m_n, character::complete::char, combinator::map,
combinator::map_res, sequence::tuple, IResult,
branch::alt, bytes::complete::{take_while_m_n, tag}, combinator::map,
combinator::map_res, sequence::tuple, IResult, Needed,
};

use crate::tokenstream::Token;

/// Iterate over a Snailfish number, adding up its magnitude as you go.
pub fn parse_magnitude(input: &str) -> u16 {
pub fn parse_magnitude(input: &[Token]) -> u16 {
magnitude(input).unwrap().1
}

// Parse a stringified Snailfish number, adding up its magnitude.
fn magnitude(input: &str) -> IResult<&str, u16> {
fn magnitude(input: &[Token]) -> IResult<&[Token], u16> {
// An element can be either a pair of elements, or a number literal.
alt((magnitude_of_pair, parse_number))(input)
alt((magnitude_of_pair, parse_number_from_token))(input)
}

// Parse a stringified Snailfish pair, adding up its magnitude.
fn magnitude_of_pair(input: &str) -> IResult<&str, u16> {
let parser = tuple((char('['), magnitude, char(','), magnitude, char(']')));
fn magnitude_of_pair(input: &[Token]) -> IResult<&[Token], u16> {
let eat = |t| tag(std::slice::from_ref(t));
let parser = tuple((eat(&Token::Open), magnitude, eat(&Token::Comma), magnitude, eat(&Token::Close)));
let mut discard_delimiter_parser = map(parser, |(_, l, _, r, _)| 3 * l + 2 * r);
discard_delimiter_parser(input)
}
Expand All @@ -29,6 +32,15 @@ pub fn parse_number(input: &str) -> IResult<&str, u16> {
)(input)
}

pub fn parse_number_from_token(input: &[Token]) -> IResult<&[Token], u16> {
use nom::Err;
match input.get(0) {
None => Err(Err::Incomplete(Needed::new(1))),
Some(Token::Num(n)) => Ok((&input[1..], *n)),
Some(_) => Err(Err::Error(nom::error::Error { input, code: nom::error::ErrorKind::Tag })),
}
}

#[cfg(test)]
mod tests {
use super::*;
Expand Down
2 changes: 1 addition & 1 deletion day18/src/tokenstream.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ impl TokenStream {
}

pub fn magnitude(self) -> u16 {
parse_magnitude(&self.to_string())
parse_magnitude(&self.0)
}
}

Expand Down

0 comments on commit a8fd28a

Please sign in to comment.