let draw = SVG().addTo('#forth').size('100%', '100%') let stack_element = (container, text) => { let group = container.group() group.add( container.rect() .size(100, 25) .stroke('#000').fill('#ddd') .attr('stroke-width', 2)); group.add(container.text(text).dmove((65 - text.length) / 2, -2)); console.log(group); return group; } let the_code = [ [ 'push', 2 ], [ 'push', 3 ], [ 'push', 4 ], [ 'mul' ], [ 'add' ] ] let the_stack = [], pc = 0, final = false; let stack_container = draw.nested().move(draw.width() - '10%', 0) let code_node = document.getElementById('code'); let push_val = (int) => { let the_element = stack_element(stack_container, int.toString()).move(10, 0); the_element.animate(100, 0, 'now').move(10, 10); the_stack.forEach(elem => elem.svg.animate(100, 0, 'now').dy(25)); the_stack.push({ svg: the_element, val: int }); } let pop_val = () => { let item = the_stack.pop() item.svg.remove(); the_stack.forEach(elem => elem.svg.dy(-25)); return item.val; } let render_code = (code, pc) => { while (code_node.firstChild) { code_node.removeChild(code_node.firstChild); } let list = document.createElement('ul'); list.style = 'list-style-type: none;'; code.forEach((instruction, idx) => { let i_type = instruction[0]; let li = document.createElement('li'); if (idx == pc) { let cursor = document.createElement('span') cursor.innerText = '> '; cursor.classList.add('instruction-cursor'); li.appendChild(cursor); } let type_field = document.createElement('span'); type_field.innerText = i_type; type_field.classList.add('instruction'); li.appendChild(type_field); for (let i = 1; i < instruction.length; i++) { li.append(' '); let operand_field = document.createElement('span'); operand_field.innerText = instruction[i]; operand_field.classList.add('operand'); li.appendChild(operand_field); } list.appendChild(li); }); code_node.appendChild(list); }; let reset = () => { the_stack.forEach(e => e.svg.remove()); the_stack = []; pc = 0; final = false; document.getElementById('step').disabled = false; render_code(the_code, 0); } let step = () => { if (!final) { const insn = the_code[pc++]; switch (insn[0]) { case 'push': push_val(insn[1]); break; case 'add': if (the_stack.length < 2) { console.error("machine error"); document.getElementById('step').disabled = true; } else { let x = pop_val(), y = pop_val(); push_val(x + y); } break; case 'mul': if (the_stack.length < 2) { console.error("machine error"); document.getElementById('step').disabled = true; } else { let x = pop_val(), y = pop_val(); push_val(x * y); } break; } } render_code(the_code, pc); if (pc >= the_code.length) { console.log("final state"); document.getElementById('step').disabled = true; final = true; } } render_code(the_code, pc);