The Chant-Stack of the Ystrevel Golems
Beneath the ruined foundry at Ystrevel, the old clay golems still wake when an adventurer reads a chant to them. Each golem keeps a stack of whole numbers in its chest and obeys the chant one token at a time. The Foundry Wardens of Ystrevel will pay anyone who can say, before a chant is spoken, exactly what a golem will hold when it falls silent, or exactly how it will fail.
Write golemChant(script). script is a string. Return either a list of integers (the final stack, bottom first) or one of the four error strings listed below.
Tokens. Split script on whitespace (spaces, tabs, newlines). Runs of whitespace and leading or trailing whitespace never produce empty tokens. Each token must be one of these: - an integer: an optional - followed by one or more digits 0-9, such as 7, -12, 007 or -0. Nothing else is an integer, so +5, - and 3.5 are not. - a word: one of add sub mul div dup drop swap if else end, matched case-insensitively. ADD and Add both mean add.
Structure. Every if must be closed by a later matching end, and they nest like brackets. Between an if and its matching end, at that same nesting level, there may be at most one else. The structure is malformed if an else or end appears with no open if, if an if has two elses at its own level, or if an if is never closed.
Running. Start with an empty stack and process tokens from left to right: - An integer is pushed. - add, sub, mul and div pop b (the top value) and then pop a. They push a + b, a - b, a * b, or a / b truncated toward zero, so -7 / 2 gives -3. - dup pushes a copy of the top value. drop pops and discards the top value. swap exchanges the top two values. - if pops the top value. - If that value is nonzero, the tokens after the if run up to its else (or up to its end if it has no else), and running then continues after its end. - If that value is zero, running continues just after its else. If it has no else, running continues just after its end. - When running reaches an else, it jumps to just after that else's matching end. When running reaches an end, it does nothing.
Tokens in a branch that is skipped are not run.
Result. These checks are made in order: 1. If any token is neither an integer nor a word, return "unknown". This applies to every token in the script, including tokens in branches that would be skipped. 2. Otherwise, if the structure is malformed, return "malformed". 3. Otherwise run the chant. Running stops at the first error: - If a token needs more values than the stack holds, return "underflow". if, dup and drop need 1 value. add, sub, mul, div and swap need 2. - If a div has enough values but b is 0, return "division by zero". 4. If the chant finishes, return the stack as a list, bottom first. An empty or all-whitespace script returns [].
No test produces a value outside the range of a 32-bit signed integer.
examples
- case 1
script of "5 0 if 10 else 20 end"[5, 20]. The 0 is popped, so the else branch runs and 5 stays below it. - case 2
script of "-7 2 div 7 -2 div"[-3, -3]. Division truncates toward zero, never toward minus infinity. - case 3
script of "0 if add end 3"[3]. The add sits in a skipped branch, so it never runs and cannot underflow. - case 4
script of "drop if 1 end end""malformed". The stray end is found before anything runs, so the drop never gets the chance to underflow.
- reward
- 540 XP
- signature
golemChant
TypeScript, run as ES2022 — types are stripped, not checked. If a JS engine would run it, so will we.
- empty chant
- plain arithmetic
- subtraction order
- division truncates toward zero
- case-insensitive words
- if takes true branch
- if takes else branch
- if without else skipped
- negative is truthy
- nested if inside else
- skipping respects nesting
- uppercase structure words
- empty branches
- empty true branch with else
- skipped branch cannot underflow
- skipped branch cannot divide by zero
- executed branch underflows
- if on empty stack
- swap needs two
- underflow before division by zero
- division by zero stops the chant
- unknown beats underflow
- plus-signed number is unknown
- decimal is unknown
- lone minus is unknown
- unknown beats malformed
- malformed beats underflow
- malformed beats division by zero
- two elses at one level
- else with no open if
- unclosed if
- messy whitespace
- leading zeros and negative zero
- zero times negative is zero
- residue stack order