A class needs a way to contain the very characters that structure it. POSIX handles this by position - a closing bracket right after the open is a literal member, and a dash at the start or end is a literal dash. Today the scanner honours both.
Let a class hold a literal closing bracket or dash by position.
A class has to be able to contain ] and -, the characters that give it its
shape. POSIX resolves the ambiguity with position rather than escaping: a ]
that appears immediately after the opening [ (or after the ! or ^) is a
plain member, not the closing bracket - so []a] is the class of ] and a. A
- is a range only when it sits between two characters; at the start or end of
the list it is a literal dash, so [a-] matches a or -.
Both fall out of two small guards in the scan. First, only treat a ] as the
terminator when it is past the first member slot, so a leading ] is read as a
member. Second - which lesson 6 already set up - only read a - as a range when a
non-] character follows it; otherwise it is an ordinary member. With these, a
class can hold every character it needs to without any escape mechanism at all.
first := j // position of the first member (after any '!' or '^')for j < len(pat) {if pat[j] == ']' && j > first { break } // a ']' past the first slot closes// a '-' is a range only when a real 'high' follows (not ']')// ... otherwise it is a literal member}