Nice solution! Clever combination of many1 and many_till. Didn't know about those and wrapped everything in Options to discard what I don't need. ``` many0(alt(( map(instruction_parser, Some), // Consume one char and return None if no instruction matches map(anychar, |_| None), ))); ``` Your approach seems more idiomatic to nom imo. At 3:11 you are resizing the terminal window and the vs-code window adjusts its size as well. What magic is that? Looks very useful.
I tried it for the first time Sunday, but ended up with an annoying lifetime issue when trying to bubble errors with `anyhow`. I honestly feel like nom's design might be outdated and I don't understand why it's so ubiquitous.
feel free to ask if you're running into trouble. either in the discord or here in the comments. &str vs &[u8] sounds like you might have been working with different input types and such. I usually stick with &str in the videos but nom can also process &[u8] and such as inputs. It looks easy in the video because I've already crossed the knowledge gap and learned the crate and the language.
not understanding something doesn't mean it is outdated. If you were having lifetime issues, are you familiar with how references work in Rust? nom typically works on &str, so if you're creating new data (like String) inside of parsers and trying to return shared references to that new data you could run into this because the String would drop at the end of the function.
@@chrisbiscardi I fully understood *why* the lifetime issue was happening, but working around it would mean adding extra code all over the place that's not idiomatic for anyhow. Also, it has precisely zero integration with async/await, instead having a bespoke "streaming" copy of every module. And most functions automatically collect things into Vecs instead of returning iterators.
@@chrisbiscardi Yeah, I do like nom and how it's eating the bytes sequentially, but because it's very different from how most other rust libraries it gets a bit confusing. I think I was looking too much at the docs and not enough at the examples and md pages on the github.
I used slices and starts_with, split, and strip_suffix here and honestly I think it's more appropriate for a small problem like this than using a parsing crate, but this would be great for learning that
I followed your part 1 just because I watched the video at work and was interested in the base nom parsing. For part 2, which I found logically pretty simple, but my own logic was failing me a bit, I diverged and instead of doing the whole `ShouldProcess` additional enum, i did similar logic with an impl function on Instruction impl Instruction { fn multiply(&self, process: Instruction) -> (Instruction, u32) { match (self, process.clone()) { (Instruction::Mul((x, y)), Instruction::Do) => (Instruction::Do, x * y), (Instruction::Dont, _) => (Instruction::Dont, 0), (Instruction::Do, _) => (Instruction::Do, 0), (_, Instruction::Dont) => (Instruction::Dont, 0), _ => (process, 0), } } }
I feel like nom is nice but for most scenarios it's over-engineered. The code is more complicated for simple parsers. It's also longer. I really like your videos because that's how i have learned NOM - I was following your AOC in the past. I can now use it, but sometimes it's simpler to use just regex even if it generates heavier code. Regex can be simple as well, and code can be like at most 20 lines long :)
the nom parser for part 1 for today is less than 20 lines long, and its only that long because I wrote it to be readable (and testable/documentatable, which doesn't really hold for regex). There's nothing wrong with using regex, especially on today's problem; it worked out well, but regex is way more complicated than nom... that complexity is largely hidden away in the engine implementation in the crate sure, but its not any less "over-engineered" as you put it. (I don't think either solution is over engineered to be clear). plus if we don't use parsing libraries for parsing problems then why do we have them?
huh, I hadn't heard of Nom before, that's really cool! Definitely a tool to note down for later. I just pulled all the tokens out of a regex 😅
regex worked well for todays too!
Nice solution!
Clever combination of many1 and many_till. Didn't know about those and wrapped everything in Options to discard what I don't need.
```
many0(alt((
map(instruction_parser, Some),
// Consume one char and return None if no instruction matches
map(anychar, |_| None),
)));
```
Your approach seems more idiomatic to nom imo.
At 3:11 you are resizing the terminal window and the vs-code window adjusts its size as well. What magic is that? Looks very useful.
I use yabai for window management on macos: github.com/koekeishiya/yabai
I use Yabai as well, but Aerospace is becoming more popular, and it doesn't need SIP disabled.
@shrugalic I haven't seen aerospace yet, will give it a look
Nom looks so easy when you do it, but when I try I just get issues between `&str` and `&[u8]` or many other hurdles. :(
I tried it for the first time Sunday, but ended up with an annoying lifetime issue when trying to bubble errors with `anyhow`. I honestly feel like nom's design might be outdated and I don't understand why it's so ubiquitous.
feel free to ask if you're running into trouble. either in the discord or here in the comments. &str vs &[u8] sounds like you might have been working with different input types and such. I usually stick with &str in the videos but nom can also process &[u8] and such as inputs.
It looks easy in the video because I've already crossed the knowledge gap and learned the crate and the language.
not understanding something doesn't mean it is outdated. If you were having lifetime issues, are you familiar with how references work in Rust? nom typically works on &str, so if you're creating new data (like String) inside of parsers and trying to return shared references to that new data you could run into this because the String would drop at the end of the function.
@@chrisbiscardi I fully understood *why* the lifetime issue was happening, but working around it would mean adding extra code all over the place that's not idiomatic for anyhow. Also, it has precisely zero integration with async/await, instead having a bespoke "streaming" copy of every module. And most functions automatically collect things into Vecs instead of returning iterators.
@@chrisbiscardi Yeah, I do like nom and how it's eating the bytes sequentially, but because it's very different from how most other rust libraries it gets a bit confusing. I think I was looking too much at the docs and not enough at the examples and md pages on the github.
Very nice. Gonna have to start using nom for parsing because I was just manually parsing the string input using .split and .find haha
I used slices and starts_with, split, and strip_suffix here and honestly I think it's more appropriate for a small problem like this than using a parsing crate, but this would be great for learning that
I followed your part 1 just because I watched the video at work and was interested in the base nom parsing. For part 2, which I found logically pretty simple, but my own logic was failing me a bit, I diverged and instead of doing the whole `ShouldProcess` additional enum, i did similar logic with an impl function on Instruction
impl Instruction {
fn multiply(&self, process: Instruction) -> (Instruction, u32) {
match (self, process.clone()) {
(Instruction::Mul((x, y)), Instruction::Do) => (Instruction::Do, x * y),
(Instruction::Dont, _) => (Instruction::Dont, 0),
(Instruction::Do, _) => (Instruction::Do, 0),
(_, Instruction::Dont) => (Instruction::Dont, 0),
_ => (process, 0),
}
}
}
I feel like nom is nice but for most scenarios it's over-engineered. The code is more complicated for simple parsers. It's also longer. I really like your videos because that's how i have learned NOM - I was following your AOC in the past. I can now use it, but sometimes it's simpler to use just regex even if it generates heavier code. Regex can be simple as well, and code can be like at most 20 lines long :)
the nom parser for part 1 for today is less than 20 lines long, and its only that long because I wrote it to be readable (and testable/documentatable, which doesn't really hold for regex). There's nothing wrong with using regex, especially on today's problem; it worked out well, but regex is way more complicated than nom... that complexity is largely hidden away in the engine implementation in the crate sure, but its not any less "over-engineered" as you put it. (I don't think either solution is over engineered to be clear).
plus if we don't use parsing libraries for parsing problems then why do we have them?
Using crates feels like cheating. I personally use std only for advent of code
that's a totally valid way to do aoc. I think it would be interesting to do a no_std variation or solve some of the days in a compute shader too.
yeah im trying it with only std
@@chrisbiscardi ohhh no_std that would be sick, maybe I'll do that next year