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);
|