The Quarry Clerk's Tally-Slates
The quarry clerk of bitwick kept every day's haul on a slate, written in the old chisel notation. The clerk is gone, the notation went with her, and the guild wants the slates read back — with the cracked ones flagged rather than guessed at.
The argument slate is a string. Return the integer the slate records, or the empty value (null in TypeScript, None in Python) if the slate is not a well-formed carving.
Cutting the slate into marks
- A run of one or more digits
0-9is a *count*, read as a decimal number. Leading zeros are allowed, so007is seven. - Each of
+,-,*,>,!,(,)is a single mark. - A space separates marks and carries no other meaning.
- Any other character makes the slate malformed.
What counts as well-formed
- A *stone* is either a count, or a
()pair enclosing a whole carving, in either case followed by zero or more!marks. - A *carving* is a single stone, or two or more stones separated by the binary marks
+,-,*,>. - The slate is well-formed exactly when its marks form one whole carving with nothing left over.
- Because a space is a separator,
1 2is two counts standing side by side, which is malformed — it is not the count 12.
What the marks mean, in grouping order, loosest first:
1. + — the sum of its two values; groups left to right. 2. - — the difference of its two values; groups right to left. 3. * — the product of its two values; groups left to right. 4. > — the larger of its two values; groups left to right. 5. ! — written after a stone; negates that stone's value, and may be repeated.
Parentheses override all of the above. Every value is an integer, and the returned value is an integer.
examples
- case 1
slate = "10 - 2 + 3 - 4 - 5"12 — the loosest mark splits it into 10 - 2 and 3 - 4 - 5; the right-leaning difference reads the second as 3 - (4 - 5) = 4, so 8 + 4 - case 2
slate = "2 > 3!"2 — the strike falls on the 3 alone, so this is the larger of 2 and -3 - case 3
slate = "(2 > 3)!"-3 — here the strike falls on the bracketed carving, whose value is 3 - case 4
slate = "1 2"null in TypeScript, None in Python — a space breaks a count, so this is two counts side by side
- reward
- 540 XP
- signature
readSlate
TypeScript, run as ES2022 — types are stripped, not checked. If a JS engine would run it, so will we.
- sum splits loosest, difference leans right
- difference alone leans right
- strike falls on a single stone
- brackets take the strike
- product binds tighter than difference
- larger binds tighter than product
- all four binary marks unspaced
- difference right-leaning over a product
- nested carving with a trailing strike
- struck brackets inside a product
- strikes stack
- leading zeros on a lone count
- blank slate
- space breaks a count
- unclosed bracket
- empty brackets
- strike with no stone before it
- no mark stands before a count
- dangling binary mark
- foreign character
- leftover bracket at the end