Code for the littler Buttons the Computer used in the Turing Machine portion of the book.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
buttons-computer/src/routes/index.svelte

210 lines
5.7 KiB

<svelte:head>
<title>Buttons the Computer</title>
</svelte:head>
<script>
import {goto, stores} from '@sapper/app';
import { ButtonMachine } from 'buttons';
import Icon from '../components/Icon.svelte';
const { session } = stores();
let code = [
['PUSH', -10], // start at -10
['STOR', 'AX'],
['PUSH', 1], // increment by 1
['ADD'],
['JZ', 5], // if we're at 0 jumps to the end
['JUMP', 1] // the previous test fails so it jumps to loop again
];
let machine = new ButtonMachine(code);
let has_error = false;
$: has_error = machine.error != '';
const data_ops = new Set(['PUSH', 'JZ', 'JNZ', 'JUMP', 'STOR', 'RSTOR', 'HALT']);
const ops = ButtonMachine.operations();
const registers = ButtonMachine.register_names();
const change_op = (i, count) => {
let cur_op = code[i][0];
let cur_index = ops.indexOf(cur_op);
let new_index = (cur_index + count) < 0 ? ops.length - 1 : (cur_index + count) % ops.length;
code[i][0] = ops[new_index];
}
const op_has_data = (op) => data_ops.has(op);
const run_machine = () => {
machine.run();
machine = machine; // make svelte go
}
const step_machine = () => {
machine.step();
machine = machine;
}
const reset_machine = () => {
machine = new ButtonMachine(code);
}
const clone_line = (i) => {
let clone = code[i];
// whew! clone the array without the 2nd one so it's empty
code.splice(i+1, 0, [clone[0]]);
code = code;
}
const delete_line = (i) => {
let item = code.splice(i, 1);
code = code;
}
</script>
<style lang="scss">
@import "sass/_variables";
#status-panel {
font-size: 14px;
}
.active-line {
background-color: $green !important;
}
.empty-data {
width: 100%;
background-color: lighten($primary-color, 40%);
}
.off-canvas-toggle {
position: unset;
z-index: unset;
top: unset;
left: unset;
display: unset;
transition: unset;
padding: 5px;
}
#reg-select {
background-color: $gray;
width: 100%;
}
#reg-select select {
background-color: $gray;
width: 100%;
}
#machine-state {
font-size: 20px;
overflow-x: none;
white-space: pre-wrap;
white-space: -moz-pre-wrap;
white-space: -pre-wrap;
white-space: -o-pre-wrap;
word-wrap: break-word;
}
#machine-state code {
color: lighten($green, 10%) !important;
}
#machine-state .error {
color: lighten($red, 10%) !important;
}
</style>
<div class="container grid-lg" id="content" data-testid="buttons-page">
<div class="columns">
<div class="column col-6 col-sm-12 col-md-12 col-xs-12">
<a alt="Open/Close help sidebar" aria-label="Open/Close help sidebar" class="off-canvas-toggle btn btn-primary btn-action" href="#sidebar-id">
?¿
</a>
<button class="btn btn-primary" on:click={ run_machine }>
<Icon code="►" tooltip="Run it" tooltip_right={ true } light={ true }/>
</button>
<button class="btn btn-primary" on:click={ step_machine }>
<Icon code="→" tooltip="Step through" tooltip_right={ true } light={ true }/>
</button>
<button class="btn btn-primary" on:click={ reset_machine }>
<Icon code="■" tooltip="Reset" light={ true }/>
</button>
<div class="divider"></div>
{#each code as [op, data], i}
<div class="input-group">
{#if has_error && machine.error_line == i}
<Icon name="alert-triangle" color="red" />
{:else}
{i}:
{/if}
<button class:active-line={ machine.ip == i }
on:click={ () => change_op(i, +1) }
on:contextmenu|preventDefault={ () => change_op(i, -1) }
class="btn btn-primary input-group-btn">{op}</button>
{#if op_has_data(op)}
{#if op === 'STOR' || op === 'RSTOR'}
<div class="form-group" id="reg-select">
<select class="form-select" bind:value={data}>
{#each registers as reg}
<option>{reg}</option>
{/each}
</select>
</div>
{:else if op == 'HALT'}
<input class:active-line={ machine.ip == i }
type="text" class="form-input" placeholder="register" bind:value={data}>
{:else}
<input class:active-line={ machine.ip == i }
type="number" class="form-input" placeholder="data" bind:value={data}>
{/if}
{:else}
<div class:active-line={ machine.ip == i } class="empty-data"></div>
{/if}
<button class="btn btn-primary input-group-addon"
on:click={ () => clone_line(i) }>
<Icon name="copy" tooltip='Clone it'/>
</button>
<button class="btn btn-primary input-group-addon"
on:click={ () => delete_line(i) }>
<Icon name="delete" tooltip='Delete it'/>
</button>
</div>
{/each}
</div>
<div class="column col-6 col-sm-12 col-md-12 col-xs-12" id="status-panel">
<pre id="machine-state">
<code>
█LINE {machine.ip} █TICK { machine.tick}
{#if machine.halted }<span class="error">HALT! { machine.error }</span>{/if}
╞ STACK ╡
──────────────────
{#each [...machine.stack].reverse() as datum, i}
{#if i == 0}{i}{datum + '\n'}
{:else}─────────
{i}{datum + '\n'}{/if}
{/each}
╞ REGISTERS ╡
──────────────────
{#each registers as reg }
[{ reg }]={ (machine.registers[reg] !== undefined ? machine.registers[reg] : '█') + '\n'}
{/each}
</code>
</pre>
</div>
</div>
</div>