weekly boss
The Changeling Warrants of Coldharbour Assize
Notice to every blade in Bitwick. A changeling has nested in the copying-loft of the Coldharbour Assize, where every bounty warrant is copied before it reaches this board. It cannot alter what a warrant says, but it alters how each copy is written. It shuffles fields, pads them with spaces, quotes and escapes them at random, and leaves stale entries standing beneath the ones that replace them. No two copies of a warrant now look alike, and the changeling's own forgeries hide among them. The guild will unmask it by reducing every warrant to its one true form, so that copies can be compared letter for letter, and by refusing any scrap that is not a well-formed warrant.
Write canonWrit(writ). writ is a string. Return the true form of the warrant as a string, or null (None in Python) if writ is malformed. Throughout, "space" means the character U+0020 only.
### Stage one: reading the warrant
Read writ from left to right.
- A backslash
\escapes the character after it, whatever that character is. The backslash is dropped, and the character after it is kept as a protected character. If a backslash is the last character ofwrit,writis malformed. - An unescaped
"opens a quoted segment, and the next unescaped"closes it. The quote marks themselves are dropped. Every character between them is protected, and backslash escapes still work inside. If a quoted segment is still open at the end ofwrit,writis malformed. - Outside quoted segments, an unescaped
;is a field separator and an unescaped=is an equals mark. These rules apply across the whole ofwrit. A;or=that is escaped, or that is inside quotes, is an ordinary protected character. - Every other character that is outside quotes and not escaped is a raw character.
A field is the text between two separators, or the text before the first separator, or the text after the last separator. Handle each field as follows:
- If the field's original text is empty, or consists only of spaces, skip the field.
- Otherwise, the field must contain exactly one equals mark. If it does not,
writis malformed. - The field's original text before the equals mark must contain no backslash and no quote mark. Remove the leading and trailing spaces from that text to get the key. The key must then be one or more lowercase letters
a–z. If either condition fails,writis malformed. - The value is the sequence of characters after the equals mark, with the quote marks and escaping backslashes already dropped. Then remove raw spaces from the start and end of that sequence. Protected spaces are never removed, and spaces between other characters are kept.
If any field is malformed, return null, even if every other field is fine.
### Stage two: settling the warrant
Apply the fields in the order they appear.
- A field with a non-empty value sets its key to that value, replacing any earlier value.
- A field with an empty value removes its key if that key is set. Otherwise it does nothing.
### Stage three: writing the true form
- Write one
key=valueentry for every key that is still set. Put the entries in ascending alphabetical order of key and join them with;. Add no spaces and no trailing;. - If no key is set, return the empty string.
- To write a value, first put a backslash before every
\and every"in it. Then, if the value contains a space, a;or an=, wrap the result in a pair of double quotes. Otherwise, leave it unwrapped.
examples
- case 1
writ ` lair = "Brann's Hollow" ; beast=wolf;tally=3;beast= ; tally = 12 ;bounty=40 gold``bounty="40 gold";lair="Brann's Hollow";tally=12`. The later tally wins, and the emptied beast is removed. - case 2
writ `seal=ab"c;d"e``seal="abc;de"`. The `;` inside the quotes does not split the field, and the result is quoted because the value contains `;`. - case 3
writ `toll=a=b`, and separately writ `toll=a\=b`null for the first, because it has two equals marks. `toll="a=b"` for the second, because the escaped `=` is an ordinary character. - case 4
writ ` ; ;; ` (only spaces and separators)the empty string ""
- reward
- 1620 XP
- signature
canonWrit
TypeScript, run as ES2022 — types are stripped, not checked. If a JS engine would run it, so will we.
- sorts keys
- last value wins, empty value deletes
- raw spaces trimmed, inner spaces kept
- quoted semicolon does not split
- quoted spaces survive trimming
- empty writ
- only separators and spaces
- unterminated quote
- trailing backslash
- field without equals mark
- uppercase key
- second equals mark
- escaped equals is ordinary
- escaped quote stays bare
- escaped leading space is kept
- everything deleted
- delete of unset key then re-set
- backslash inside quotes
- true form is a fixed point
- key with inner space
- quoted key
- escaped semicolon
- malformed field after valid ones
- quoted equals
- full warrant