build-a-glob-matcher / lesson-09.md
Lesson 09 · Character classes and escaping

An unclosed class is a literal

A stray open bracket with no closing bracket is not an error - the convention is to treat it as an ordinary character. Today the scanner falls back to a literal open bracket when it never finds the close.

The goal

Treat an open bracket with no closing bracket as a literal character.

Start here - the target
TO DO
Scenario: A class that never closes matches a literal open bracket
Givenpatterns with an unterminated bracket
WhenMatch is called against various names
ThenMatch("[abc", "[abc") is true and Match("[abc", "a") is false - the open bracket is just a character
Andthe fallback is local: Match("a[b", "a[b") is true and Match("[", "[") is true
Background

Real patterns contain stray brackets - a filename like a[b is perfectly legal - so an open [ that never finds a matching ] must not break the matcher. The convention, shared by the shell and by git, is to treat the lone [ as a literal character. So [abc (no closing bracket) matches the four-character name [abc, and matches nothing shorter.

The change is a single fallback: after the scanner walks looking for the closing ], check whether it ran off the end of the pattern. If it did, no class was ever formed, so return the [ as an ordinary literal and advance by one - letting the main scan continue with abc as plain characters. Only when a real ] is found do you treat the span as a class. This keeps a malformed pattern predictable instead of throwing, which matters even more once these patterns come from a file.

Make it work
// after scanning for ']': if we ran off the end, no class was closed
if j >= len(pat) {
return pat[p] == c, p + 1 // the '[' is a plain literal character
}
return matched, j + 1
CheckpointDONE
An unterminated class degrades to a literal open bracket. Commit and stop here.