The earliest memory I have of ‘programming’ is in the early/mid 90s when my father brought home a computer from work. We could play games on it … so of course I took the spreadsheet program he used (LOTUS 123, did I date myself with that?) and tried to modify it to print out a helpful message for him. It … halfway worked? At least I could undo it so he could get back to work…

After that, I picked up programming for real in QBASIC (I still have a few of those programs lying around), got my own (junky) Linux desktop from my cousin, tried to learn VBasic (without a Windows machine), and eventually made it to high school… In college, I studied computer science and mathematics, mostly programming in Java/.NET, although with a bit of everything in the mix. A few of my oldest programming posts on this blog are from that time.

After that, on to grad school! Originally, I was going to study computational linguistics, but that fell through. Then programming languages (the school’s specialty). And finally I ended up studying censorship and computer security. That’s about where I am today!

But really, I still have a habit of doing a little bit of everything. Whatever seems interesting at the time!

AoC 2024 Day 5: (Not) Transitivinator

Source: Day Day 5: Print Queue

Full solution for today (spoilers!).

Part 1

The input is a list of pairs of the form a|b which defines that b must not come before a, an empty line, and then a list of values a,b,c,d.

For each line that is valid for all given a|b rules, sum the middle number of each list.

read more...


Advent of Code 2024

Let’s do this (Advent of Code) thing again!

I’m sticking with Rust again. I still use Python when I need to hammer out something quickly, but if I want to do something correctly (and especially if I want it to be fast), you can’t beat Rust.

Let’s see how it goes!

Full solutions will once again be posted to GitHub (including previous years and possibly some I haven’t written up yet): jpverkamp/advent-of-code

read more...


Ludum Dare(ish) 56: BugShine

Ludum Dare? That’s been a while!

I didn’t actually enter the game jam. Honestly, I wasn’t sure I was going to write anything. But I had a bit of an idea and spent a few hours only on Sunday hammering something out:

Yeah, I did another cellular automata thing 😄

It’s not at all complete, but the basic idea is:

  • Generate a random level
  • See it with multiple players (colonies of bugs)
  • Each bug will send out waves of ‘shine’, expanding their territory
  • Take over the map to win

It’s sort of got that?

I’m using Rust as I’ve been doing a lot recently.

The main libraries are:

  • pixels for the rendering; it gives me direct access to a pixel buffer, which is my favorite
  • winit for windowing; this did require the feature rwh_05 to be properly compatible with pixels, which took a minute to track down

Other than, that, it’s straight custom code which you can see in it’s entirety on my github.

  • main.rs - creates the window and handles input
  • world.rs - runs the simulation mostly in an update function; with generation in new

I think that perhaps the only really interesting bit about the code is how the ‘shine waves’ work. Basically, I have a grid of the state of each cell, but I also have a Vec that tracks ‘active’ pixels. Those are the only ones that can update–which both helps performance and makes the simulation appear the way it does.

Overall, a nice quick project. More than anything, it actually convinced me to try setting up something that can render pixel buffers on Rust. And with a (very minimal) GUI, too! Both things I’ve been meaning to learn.

I probably won’t do anything more with this code, but it’s got the seeds of something more interesting. Keep an eye out. 😄

Onward!


Finish Myself a Grep

Hey, I said that I would follow up on my post about Building Myself a Grep… well here it is!

And I’m actually surprised with myself in how far I actually made it!

You can see the current state of my code on Github. You can install it from that repo (checked out) with cargo install --path .

I mostly worked off the MDN documentation:

Details

Supported Regex Features

  • Assertions:

    • ^ and $ for entire patterns
    • Parsing look head/behind (not matched)
  • Character classes

    • Single characters: [abc]
    • Ranges: [a-z]
    • Negated classes: [^abc]
    • Wildcards: .
    • Classes: \d/\D for digits, \w/\W for ‘words’, and \s/\S for whitespace
    • Escape characters: \t\r\n\v\f
    • Control characters: \cX (I’ve never used these)
    • Hex and unicode literals: \hXX and \uXXXX
    • Disjunction: | (both in capture groups and not)
  • Groups and back references

    • Capture groups: (abc)
    • Named capture groups: (?<name>abc)
    • Non-capturing groups: (?:abc)
    • Flags: (?ims-ims:abc)
      • Both enabling and disabling
      • i and s but not m
    • Backreferences: \n
    • Named backreferences: \k<name>
  • Quantifiers

    • * for zero or more
    • + for one or more
    • ? for zero or one
    • *?, +?, and ?? for lazy / non-greedy matches
    • abc{n} exactly n matches
    • abc{n,} at least n matches
    • abc{,m} up to m matches
    • abc{n,m} at least n and up to m matches (inclusive)
    • Lazy matches for all of those

Most of those were fairly straight forward extensions of previous code. In think the most interesting ones were handling the parsing of all the different things that can go in groups (including flags).

For each of them, you can check my git commit history to see how I implemented specific things. It’s mostly one commit per feature, but not always.

Unsupported Regex Features (so far!)

  • Assertions:

    • Word boundaries (\b and \B)
    • Look ahead/behind (parsed but not matched)
  • Character classes

    • [\b] for backspace characters
    • Long unicode format: \u{XXXXX}
    • Unicode properties: \p{...}/\P{...}
  • Groups and back references:

    • m flag / mode: multiline matches

The look ahead/behind is the one I’m most interested in supporting. I don’t even think it will be that hard, I just honestly missed it.

The more interesting one will be the m flag. Currently, I only match lines, so that will be a decently large restructuring. We’ll see.

Supported CLI flags

I’ve made an awful lot of progress on this one too!

$ jp-grep --help

A custom grep implementation; always behaves as egrep

Usage: jp-grep [OPTIONS] [PATTERN] [PATHS]...

Arguments:
  [PATTERN]   The regular expression to evaluate; may also be specified with -e
  [PATHS]...  Paths to search for matches; if none are provided read from stdin

Options:
  -A, --after-context <AFTER_CONTEXT>
          Lines of context to print after each match
  -B, --before-context <BEFORE_CONTEXT>
          Lines of context to print before each match
  -C, --context <CONTEXT>
          Lines to print both before and after
  -c, --count
          Only print the matching count
  -E, --extended-regexp
          Extended regex mode (egrep); this option is ignored (always true)
  -e, --regexp <ADDITIONAL_PATTERNS>
          Additional patterns, will return a line if any match
  -h, --no-filename
          Never print filenames
      --help
          Display this help message
  -i, --ignore-case
          Default to case insensitive match
  -n, --line-number
          Print line numbers before matches and context
  -r, --recursive
          Recursively add any directories (-R also works)
  -v, --invert-match
          Invert the match; only print lines that don't match any pattern
  -V, --version
          Print version

Of those, the context flags (-A, -B, and -C) were probably the most tricky, since I basically had to implement a circular buffer for them. I could have just read the entire file into memory, but from the beginning, I didn’t want to do that.

-E is a little silly, since that’s the only grep pattern I support (and the only one I actually use in grep, so that’s fair).

So far as supporting multiple files, recursive search, and stdin, read the section on collecting files later.

So far as printing (handling line numbers and file names), read the section on printing lines.

Overall, pretty fun code.

Unsupported CLI flags

So far, there are a bunch of flags that I don’t support for grep. Of those, there are a bunch that I don’t intend to support (like built in compression support and properly dealing with symlinks).

The things that I would still like to support though are:

  • Input options:

    • -f file/--file=file - Read patterns from file
  • Output options:

    • -a/--text - Currently I always have this set; I don’t treat binary files differently
    • -L/--files-without-match - only print files that don’t match
    • -o/--only-matching - only print the matching groups; I have the groups for backreferences, use them!
  • File filtering - files to include/exclude (useful with recursive matches):

    • --exclude pattern
    • --exclude-dir pattern
    • --include pattern
    • --include-dir pattern

That’s not too bad, all things consider.

Error handling

One thing that I actually played a bit with this time around was custom error handling in the parser. Rather than just returning &str all over the place for Err types, I made my own:

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(crate) enum ParserError {
    RemainingInput,
    UnexpectedEnd,
    InvalidCharacter(char, &'static str),
    InvalidUnicodeCodePoint(u32),
    InvalidRange(char, char),
    InvalidRepeatRange(u32, u32),
}

impl std::fmt::Display for ParserError {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match self {
            ParserError::RemainingInput => write!(f, "Unexpected input after parsing"),
            ParserError::UnexpectedEnd => write!(f, "Unexpected end of input"),
            ParserError::InvalidCharacter(c, expected) => {
                write!(f, "Invalid character '{}', expected {}", c, expected)
            }
            ParserError::InvalidUnicodeCodePoint(code_point) => {
                write!(f, "Invalid unicode code point: {}", code_point)
            }
            ParserError::InvalidRange(start, end) => {
                write!(f, "Invalid range: {}-{}", start, end)
            }
            ParserError::InvalidRepeatRange(start, end) => {
                write!(f, "Invalid range: {}-{}", start, end)
            }
        }
    }
}

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(crate) struct ParserErrorWithPosition {
    pub position: usize,
    pub error: ParserError,
}

The WithPosition type also lets me pinpoint exactly where in a pattern I failed:

jp-grep 'this is some long complicated pattern, \hXX see?'

Error parsing regex: Invalid character 'X', expected hex digit
| this is some long complicated pattern, \hXX see?
|                                          ^

That’s pretty neat and I hope helpful! 😄

Expanding past CodeCrafters

Overall, I’m pretty happy with this project. It’s got a pretty decent chunk of code, including…

$ jp-grep -c -v -e '//' -e '^\s*$' **/*.rs

1241

…over 1000 lines of Rust code, including tests but not blank lines or comments. 😄

I’ll probably pick this up at least once more.

Now… will I actually use this? Probably not. But it was certainly interesting to write.

Other than that, was CodeCrafters actually helpful for this? Middling. It was the kick I needed to actually do it (I’ve been meaning to write this for years at this point) and once I was started, I could finish it. On the other hand, the output format they require was a bit annoying at times, I’ve mostly moved away from that.

Still, worth I think. I’ll probably continue to do their free programs. Kafka is up next. Whee servers!

read more...


CodeCrafters: Build Myself an Interpreter

Didn’t I just do one of these? Well, yes. Yes I did. But I love building compilers and interpreters, so when I saw this one was in beta (and thus free 😉), I had to try it! It’s directly an implemention of the Lox languages from the Crafting Interpreters website / book (my review), if incomplete. By the end of the lesson, we’ll have: A tokenizer that handles parentheses, braces, operators (single and multiple character), whitespace, identifiers, string literals, numeric literals, and keywords A parser that can take those tokens and build an abstract syntax tree using recursive descent parsing A simple tree walking interpreter for some subset of the language It doesn’t handle all of the syntax (yet).

read more...


Solving Cosmic Express

Another Rust Solvers puzzle: Cosmic Express. Basically, it’s a routefinding puzzle. You have a train that needs a track from entrance to exit, picking up and dropping off cargo on the way.

It’s actual a relatively simple puzzle, so far as things go, but one thing that’s interesting from a solving perspective is that branching paths really don’t work great with my solver code. Paths just have a crazy branching factor when compared to (for example) playing one of a handful of cards.

But it’s still an interesting puzzle!

read more...