Master the fundamental concepts of build a bytecode vm through this focused micro-challenge.
You have read the whole brief, and the concepts above stay free on every task. Writing and running the code needs a plan.
Three hints are available for this task, revealed one at a time inside the code workspace so you can struggle productively before seeing them.
Every task includes starter code, theory, and hidden tests so you can implement and verify locally in the browser.
How it worksCoroutines suspend mid-function and resume later with local state intact. Unlike threads, they yield explicitly. Lua's \`coroutine.yield\`, Python generators, and \`asyncio\` all build on this save-restore pattern.
A coroutine object stores its own \`pc\`, operand stack, and frames:
The producer-consumer pattern is the classic demo: a coroutine generates values, yielding each one; the driver resumes it to pull the next.
For example, a coroutine yielding 1, 2, 3 prints those values across three resume calls without threads or preemption.
States to track: \`CREATED\`, \`RUNNING\`, \`SUSPENDED\`, \`DEAD\`.
Only one coroutine runs at a time in this model, so you avoid locks entirely. That cooperative guarantee is why Lua coroutines and Python generators can mutate VM state without the synchronization threads demand.
This exercise asks you to add coroutine objects with independent execution state. You will implement \`YIELD\`, \`RESUME\`, and a producer coroutine that the main program drives in a loop.
You will use the same mental model here when reading production interpreter source later in the track. Sketch one concrete input on paper, predict the outcome, then confirm with code. That discipline catches logic errors early and makes debugging far faster when you extend the implementation in follow-on tasks.
Add coroutines (generators) to the VM. A coroutine is an independent execution context with its own pc, operand stack and locals. YIELD pops a value, hands it to whoever resumed the coroutine, and suspends it. The next resume continues right after the YIELD, with all its state intact. RETURN finishes it for good. Define coroutine bodies, create instances with arguments, and drive them.
cLoading…
Body instructions: PUSH n POP ADD SUB MUL NEG DUP SWAP EQ LT GT JMP L JZ L LOAD i STORE i YIELD RETURN (8 locals per instance, starting at 0).
CO yielded V) or RETURN (CO returned V);resume CO: cannot resume a dead coroutine for each requested resume;CO: error, no YIELD within 10000 steps.CO: error at PC (OP): stack underflow, CO: error at PC: stack overflow (64 values), and CO: error, ran off the end of BODY.CO: STATE, resumed N time(s), pc P.BODY: unsupported instruction XBODY: bad operand for OPBODY: JMP needs a label / BODY: JZ needs a labelBODY: undefined label X (the body becomes empty)create CO: no body named Bcreate CO: name in useresume CO: no such coroutinecreate prints created CO from BODY with N argument(s).cLoading…
Input:
cLoading…
Output:
cLoading…
Hidden tests cover a generator that finishes after several resumes, a body that never yields (the step budget), an underflow inside a coroutine, resuming more times than values exist, and setup errors.