mirror of
https://github.com/Noratrieb/brainfuck.git
synced 2026-01-14 13:35:00 +01:00
more settings
This commit is contained in:
parent
e13d82ff46
commit
dc79c96f63
10 changed files with 246 additions and 116 deletions
3
ibfi-ts/.gitignore
vendored
3
ibfi-ts/.gitignore
vendored
|
|
@ -8,6 +8,9 @@
|
||||||
# testing
|
# testing
|
||||||
/coverage
|
/coverage
|
||||||
|
|
||||||
|
# production
|
||||||
|
/build
|
||||||
|
|
||||||
# misc
|
# misc
|
||||||
.DS_Store
|
.DS_Store
|
||||||
.env.local
|
.env.local
|
||||||
|
|
|
||||||
|
|
@ -1,46 +1,25 @@
|
||||||
# Getting Started with Create React App
|
# Interactive Brainfuck Interpreter in React TS
|
||||||
|
|
||||||
This project was bootstrapped with [Create React App](https://github.com/facebook/create-react-app).
|
This is an interactive interpreter for the brainfuck programming language.
|
||||||
|
It provides simple brainfuck execution, along with a view of the memory, the memory pointer, the current state of the
|
||||||
|
code and more.
|
||||||
|
|
||||||
## Available Scripts
|
It is great for debugging brainfuck programs, including a memory view, program view, the location of the pointers, the
|
||||||
|
ability to directly edit memory.
|
||||||
|
You can step manually through the program, or set the execution speed to whatever speed fits best.
|
||||||
|
This interpreter also has the ability to set breakpoints using the • symbol. This means you can let your code run fast and
|
||||||
|
stopping it at any point in your program to see what went wrong.
|
||||||
|
|
||||||
In the project directory, you can run:
|
## Features
|
||||||
|
* brainfuck execution including IO
|
||||||
|
* memory view
|
||||||
|
* code state view
|
||||||
|
* manual stepping
|
||||||
|
* breakpoints
|
||||||
|
* error messages
|
||||||
|
|
||||||
### `yarn start`
|
|
||||||
|
|
||||||
Runs the app in the development mode.\
|
### Future features
|
||||||
Open [http://localhost:3000](http://localhost:3000) to view it in the browser.
|
* seeing ASCII characters in memory
|
||||||
|
* fast excecution mode (no debugging info)
|
||||||
The page will reload if you make edits.\
|
* better speed control
|
||||||
You will also see any lint errors in the console.
|
|
||||||
|
|
||||||
### `yarn test`
|
|
||||||
|
|
||||||
Launches the test runner in the interactive watch mode.\
|
|
||||||
See the section about [running tests](https://facebook.github.io/create-react-app/docs/running-tests) for more information.
|
|
||||||
|
|
||||||
### `yarn build`
|
|
||||||
|
|
||||||
Builds the app for production to the `build` folder.\
|
|
||||||
It correctly bundles React in production mode and optimizes the build for the best performance.
|
|
||||||
|
|
||||||
The build is minified and the filenames include the hashes.\
|
|
||||||
Your app is ready to be deployed!
|
|
||||||
|
|
||||||
See the section about [deployment](https://facebook.github.io/create-react-app/docs/deployment) for more information.
|
|
||||||
|
|
||||||
### `yarn eject`
|
|
||||||
|
|
||||||
**Note: this is a one-way operation. Once you `eject`, you can’t go back!**
|
|
||||||
|
|
||||||
If you aren’t satisfied with the build tool and configuration choices, you can `eject` at any time. This command will remove the single build dependency from your project.
|
|
||||||
|
|
||||||
Instead, it will copy all the configuration files and the transitive dependencies (webpack, Babel, ESLint, etc) right into your project so you have full control over them. All of the commands except `eject` will still work, but they will point to the copied scripts so you can tweak them. At this point you’re on your own.
|
|
||||||
|
|
||||||
You don’t have to ever use `eject`. The curated feature set is suitable for small and middle deployments, and you shouldn’t feel obligated to use this feature. However we understand that this tool wouldn’t be useful if you couldn’t customize it when you are ready for it.
|
|
||||||
|
|
||||||
## Learn More
|
|
||||||
|
|
||||||
You can learn more in the [Create React App documentation](https://facebook.github.io/create-react-app/docs/getting-started).
|
|
||||||
|
|
||||||
To learn React, check out the [React documentation](https://reactjs.org/).
|
|
||||||
|
|
@ -24,7 +24,7 @@
|
||||||
"build": "react-scripts build",
|
"build": "react-scripts build",
|
||||||
"test": "react-scripts test",
|
"test": "react-scripts test",
|
||||||
"eject": "react-scripts eject",
|
"eject": "react-scripts eject",
|
||||||
"deploy": "gh-pages -d build"
|
"deploy": "yarn build && gh-pages -d build"
|
||||||
},
|
},
|
||||||
"eslintConfig": {
|
"eslintConfig": {
|
||||||
"extends": [
|
"extends": [
|
||||||
|
|
|
||||||
|
|
@ -38,6 +38,7 @@ $medium-color: #78787f;
|
||||||
|
|
||||||
.code-display-wrapper {
|
.code-display-wrapper {
|
||||||
max-width: 80vw;
|
max-width: 80vw;
|
||||||
|
font-family: monospace;
|
||||||
|
|
||||||
span {
|
span {
|
||||||
word-wrap: break-word;
|
word-wrap: break-word;
|
||||||
|
|
@ -55,6 +56,15 @@ $medium-color: #78787f;
|
||||||
min-width: 60px;
|
min-width: 60px;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.array-set-value-field {
|
||||||
|
min-width: 50px;
|
||||||
|
max-width: 100px;
|
||||||
|
height: 40px;
|
||||||
|
color: $light-color;
|
||||||
|
font-size: 30px;
|
||||||
|
background-color: $main-color-brighter;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.bf-run {
|
.bf-run {
|
||||||
|
|
@ -65,6 +75,11 @@ $medium-color: #78787f;
|
||||||
width: 200px;
|
width: 200px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.small-speed-button {
|
||||||
|
height: 40px;
|
||||||
|
width: 40px;
|
||||||
|
}
|
||||||
|
|
||||||
.program-input-area {
|
.program-input-area {
|
||||||
resize: none;
|
resize: none;
|
||||||
width: 80vw;
|
width: 80vw;
|
||||||
|
|
@ -72,8 +87,8 @@ $medium-color: #78787f;
|
||||||
font-size: 30px;
|
font-size: 30px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.error {
|
.info {
|
||||||
background-color: #664242FF;
|
background-color: #579ca7;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,7 @@ function App() {
|
||||||
const [running, setRunning] = useState(false);
|
const [running, setRunning] = useState(false);
|
||||||
|
|
||||||
const outHandler = useCallback((char: number) => {
|
const outHandler = useCallback((char: number) => {
|
||||||
setOut(out => out + String.fromCharCode(char))
|
setOut(oldOut => oldOut + String.fromCharCode(char))
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const runHandler = (run: boolean) => {
|
const runHandler = (run: boolean) => {
|
||||||
|
|
@ -21,7 +21,6 @@ function App() {
|
||||||
}
|
}
|
||||||
|
|
||||||
const inputHandler = (code: string, options: CodeOptions) => setInput([code, options]);
|
const inputHandler = (code: string, options: CodeOptions) => setInput([code, options]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="App-header">
|
<div className="App-header">
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -12,15 +12,17 @@ export default class Interpreter {
|
||||||
|
|
||||||
private readonly _inHandler: InHandler;
|
private readonly _inHandler: InHandler;
|
||||||
private readonly _outHandler: OutHandler;
|
private readonly _outHandler: OutHandler;
|
||||||
private readonly _errorHandler: ErrorHandler;
|
|
||||||
|
|
||||||
constructor(input: [string, CodeOptions], outHandler: OutHandler, inHandler: InHandler, errorHandler: ErrorHandler) {
|
private readonly _options: CodeOptions;
|
||||||
|
|
||||||
|
constructor(input: [string, CodeOptions], outHandler: OutHandler, inHandler: InHandler) {
|
||||||
const buf = new ArrayBuffer(32000);
|
const buf = new ArrayBuffer(32000);
|
||||||
this._array = new Uint8Array(buf);
|
this._array = new Uint8Array(buf);
|
||||||
this._pointer = 0;
|
this._pointer = 0;
|
||||||
|
|
||||||
|
this._options = input[1];
|
||||||
if (input[1].minify) {
|
if (input[1].minify) {
|
||||||
this._code = minify(input[0])
|
this._code = this.minify(input[0])
|
||||||
} else {
|
} else {
|
||||||
this._code = input[0];
|
this._code = input[0];
|
||||||
}
|
}
|
||||||
|
|
@ -28,7 +30,6 @@ export default class Interpreter {
|
||||||
this._programCounter = 0;
|
this._programCounter = 0;
|
||||||
this._inHandler = inHandler;
|
this._inHandler = inHandler;
|
||||||
this._outHandler = outHandler;
|
this._outHandler = outHandler;
|
||||||
this._errorHandler = errorHandler;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public next() {
|
public next() {
|
||||||
|
|
@ -44,8 +45,7 @@ export default class Interpreter {
|
||||||
break;
|
break;
|
||||||
case '<':
|
case '<':
|
||||||
if (this._pointer === 0) {
|
if (this._pointer === 0) {
|
||||||
this._errorHandler("Cannot wrap left");
|
throw new Error("Cannot wrap left");
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
this._pointer--;
|
this._pointer--;
|
||||||
break;
|
break;
|
||||||
|
|
@ -53,45 +53,66 @@ export default class Interpreter {
|
||||||
this._outHandler(this.value);
|
this._outHandler(this.value);
|
||||||
break;
|
break;
|
||||||
case ',':
|
case ',':
|
||||||
try {
|
this.input();
|
||||||
this._array[this._pointer] = this._inHandler();
|
|
||||||
} catch {
|
|
||||||
this._programCounter--;
|
|
||||||
this._errorHandler("Could not read input, trying again next time.")
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
case '[':
|
case '[':
|
||||||
if (this.value === 0) {
|
this.loopForwards();
|
||||||
let level = 0;
|
|
||||||
while (this.lastInstruction !== ']' || level > -1) {
|
|
||||||
this._programCounter++;
|
|
||||||
if (this.lastInstruction === '[') level++;
|
|
||||||
else if (this.lastInstruction === ']') level--;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
case ']':
|
case ']':
|
||||||
if (this.value !== 0) {
|
this.loopBackwards();
|
||||||
let level = 0;
|
break;
|
||||||
while (this.lastInstruction !== '[' || level > -1) {
|
case '•':
|
||||||
this._programCounter--;
|
if (this._options.enableBreakpoints) {
|
||||||
if (this.lastInstruction === '[') level--;
|
throw new Error("Breakpoint reached");
|
||||||
else if (this.lastInstruction === ']') level++;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case undefined:
|
case undefined:
|
||||||
this._pointer = this._code.length;
|
this._pointer = this._code.length;
|
||||||
console.warn("reached end");
|
|
||||||
break;
|
break;
|
||||||
default: {
|
default:
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
console.log(`char: ${this.code[this.programCounter - 1]} pointer: ${this.pointer} value: ${this.array[this.pointer]}`)
|
|
||||||
|
private loopForwards() {
|
||||||
|
if (this.value === 0) {
|
||||||
|
let level = 0;
|
||||||
|
while (this.lastInstruction !== ']' || level > -1) {
|
||||||
|
this._programCounter++;
|
||||||
|
if (this._programCounter > this._code.length) {
|
||||||
|
throw new Error("Reached end of code while searching ']'");
|
||||||
|
}
|
||||||
|
if (this.lastInstruction === '[') level++;
|
||||||
|
else if (this.lastInstruction === ']') level--;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private loopBackwards() {
|
||||||
|
if (this.value !== 0) {
|
||||||
|
let level = 0;
|
||||||
|
while (this.lastInstruction !== '[' || level > -1) {
|
||||||
|
this._programCounter--;
|
||||||
|
if (this._programCounter < 0) {
|
||||||
|
throw new Error("Reached start of code while searching '['");
|
||||||
|
}
|
||||||
|
if (this.lastInstruction === '[') level--;
|
||||||
|
else if (this.lastInstruction === ']') level++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private input() {
|
||||||
|
try {
|
||||||
|
this._array[this._pointer] = this._inHandler();
|
||||||
|
} catch {
|
||||||
|
this._programCounter--;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public prev() {
|
public prev() {
|
||||||
|
// -
|
||||||
}
|
}
|
||||||
|
|
||||||
get reachedEnd(): boolean {
|
get reachedEnd(): boolean {
|
||||||
|
|
@ -121,11 +142,18 @@ export default class Interpreter {
|
||||||
get programCounter(): number {
|
get programCounter(): number {
|
||||||
return this._programCounter;
|
return this._programCounter;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private minify(code: string): string {
|
||||||
|
const CHARS = ['+', '-', '<', '>', '.', ',', '[', ']'];
|
||||||
|
if (this._options.enableBreakpoints) {
|
||||||
|
CHARS.push('•');
|
||||||
}
|
}
|
||||||
|
|
||||||
const CHARS = ['+', '-', '<', '>', '.', ',', '[', ']'];
|
return code.split("")
|
||||||
const minify = (code: string): string =>
|
|
||||||
code.split("")
|
|
||||||
.filter(c => CHARS.includes(c))
|
.filter(c => CHARS.includes(c))
|
||||||
.join("");
|
.join("");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,10 @@
|
||||||
import React, {useState} from 'react';
|
import React, {useState} from 'react';
|
||||||
|
import presets from "../presets.json";
|
||||||
|
|
||||||
export interface CodeOptions {
|
export interface CodeOptions {
|
||||||
minify?: boolean
|
minify?: boolean,
|
||||||
|
directStart?: boolean,
|
||||||
|
enableBreakpoints?: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
interface CodeInputProps {
|
interface CodeInputProps {
|
||||||
|
|
@ -15,10 +18,8 @@ const CodeInput = ({code, setInput}: CodeInputProps) => {
|
||||||
const [codeOptions, setCodeOptions] = useState<CodeOptions>({});
|
const [codeOptions, setCodeOptions] = useState<CodeOptions>({});
|
||||||
|
|
||||||
|
|
||||||
const setStart = () => {
|
const setPreset = (name: keyof typeof presets) => () => {
|
||||||
setInput(
|
setInput(presets[name], codeOptions);
|
||||||
"++++++++[>++++[>++>+++>+++>+<<<<-]>+>+>->>+[<]<-]>>.>---.+++++++..+++.>>.<-.<.+++.------.--------.>>+.>++.",
|
|
||||||
codeOptions);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const changeMinify = (e: React.ChangeEvent<HTMLInputElement>) => {
|
const changeMinify = (e: React.ChangeEvent<HTMLInputElement>) => {
|
||||||
|
|
@ -26,6 +27,16 @@ const CodeInput = ({code, setInput}: CodeInputProps) => {
|
||||||
setInput(code, codeOptions);
|
setInput(code, codeOptions);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const changeStart = (e: React.ChangeEvent<HTMLInputElement>) => {
|
||||||
|
setCodeOptions(old => ({...old, directStart: e.target.checked}))
|
||||||
|
setInput(code, codeOptions);
|
||||||
|
}
|
||||||
|
|
||||||
|
const changeBreakpoint = (e: React.ChangeEvent<HTMLInputElement>) => {
|
||||||
|
setCodeOptions(old => ({...old, enableBreakpoints: e.target.checked}))
|
||||||
|
setInput(code, codeOptions);
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<div className="bf-input">
|
<div className="bf-input">
|
||||||
|
|
@ -34,14 +45,35 @@ const CodeInput = ({code, setInput}: CodeInputProps) => {
|
||||||
<label htmlFor="bf-input-fontsize-range">Font Size</label>
|
<label htmlFor="bf-input-fontsize-range">Font Size</label>
|
||||||
<input type="range" id="bf-input-fontsize-range" onChange={v => setFontSize(+v.target.value)}/>
|
<input type="range" id="bf-input-fontsize-range" onChange={v => setFontSize(+v.target.value)}/>
|
||||||
</span>
|
</span>
|
||||||
<input type="checkbox" checked={codeOptions.minify} id="input-options-minify" onChange={changeMinify}/>
|
<span>
|
||||||
|
<input type="checkbox" checked={codeOptions.minify} id="input-options-minify"
|
||||||
|
onChange={changeMinify}/>
|
||||||
<label htmlFor="input-options-minify">Minify Code</label>
|
<label htmlFor="input-options-minify">Minify Code</label>
|
||||||
|
</span>
|
||||||
|
<span>
|
||||||
|
<input type="checkbox" checked={codeOptions.directStart} id="input-options-directstart"
|
||||||
|
onChange={changeStart}/>
|
||||||
|
<label htmlFor="input-options-directstart">Start Directly</label>
|
||||||
|
</span>
|
||||||
|
<span>
|
||||||
|
<input type="checkbox" checked={codeOptions.enableBreakpoints} id="input-options-enableBreakpoints"
|
||||||
|
onChange={changeBreakpoint}/>
|
||||||
|
<label htmlFor="input-options-enableBreakpoints">Breakpoints (•)</label>
|
||||||
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<textarea value={code} onChange={e => setInput(e.target.value, codeOptions)} style={{fontSize}}
|
<textarea value={code} onChange={e => setInput(e.target.value, codeOptions)} style={{fontSize}}
|
||||||
className="code-input"
|
className="code-input"
|
||||||
placeholder="Input your code here..."/>
|
placeholder="Input your code here..."/>
|
||||||
<div>
|
<div>
|
||||||
<button onClick={setStart}>Set Hello World</button>
|
<div>Presets</div>
|
||||||
|
<div>
|
||||||
|
<button onClick={setPreset("helloworld")}>Hello World</button>
|
||||||
|
<button onClick={setPreset("hanoi")}>Towers of Hanoi</button>
|
||||||
|
<button onClick={setPreset("quine")}>Quine</button>
|
||||||
|
<button onClick={setPreset("gameoflife")}>Game Of Life</button>
|
||||||
|
<button onClick={setPreset("benchmark")}>Benchmark</button>
|
||||||
|
<button onClick={setPreset("fizzbuzz")}>Fizzbuzz</button>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
import React from 'react';
|
import React, {useRef, useState} from 'react';
|
||||||
import Interpreter from "../brainfuck/Interpreter";
|
import Interpreter from "../brainfuck/Interpreter";
|
||||||
|
|
||||||
const MAX_TABLE_COLUMNS = 20;
|
const MAX_TABLE_COLUMNS = 20;
|
||||||
|
|
@ -35,7 +35,7 @@ const RunDisplay = ({interpreter}: RunDisplayProps) => {
|
||||||
<tbody>
|
<tbody>
|
||||||
<tr>
|
<tr>
|
||||||
{
|
{
|
||||||
arrayWithIndex.map((n) => <td className="cell" key={n}>{interpreter.array[n]}</td>)
|
arrayWithIndex.map((n) => <MemoryCell key={n} index={n} interpreter={interpreter}/>)
|
||||||
}
|
}
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
|
|
@ -50,4 +50,48 @@ const RunDisplay = ({interpreter}: RunDisplayProps) => {
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const MemoryCell = ({index, interpreter}: { index: number, interpreter: Interpreter }) => {
|
||||||
|
const [isEditing, setIsEditing] = useState(false);
|
||||||
|
const [input, setInput] = useState(interpreter.array[index] + "");
|
||||||
|
|
||||||
|
const inputField = useRef<HTMLInputElement>(null);
|
||||||
|
|
||||||
|
const saveAndQuit = () => {
|
||||||
|
interpreter.array[index] = +(input);
|
||||||
|
setIsEditing(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
const click = () => {
|
||||||
|
setIsEditing(true);
|
||||||
|
inputField.current?.select();
|
||||||
|
}
|
||||||
|
|
||||||
|
const keyDown = (e: React.KeyboardEvent<HTMLInputElement>) => {
|
||||||
|
console.log("key", e.key);
|
||||||
|
if (e.key === "Escape") {
|
||||||
|
setIsEditing(false);
|
||||||
|
} else if (e.key === "Enter") {
|
||||||
|
saveAndQuit();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<td onClick={click} className="cell">
|
||||||
|
{
|
||||||
|
isEditing ?
|
||||||
|
<input onKeyDown={keyDown}
|
||||||
|
className="array-set-value-field"
|
||||||
|
ref={inputField}
|
||||||
|
onChange={e => setInput(e.target.value)}
|
||||||
|
value={input}
|
||||||
|
onBlur={saveAndQuit}
|
||||||
|
autoFocus
|
||||||
|
/>
|
||||||
|
:
|
||||||
|
interpreter.array[index]
|
||||||
|
}
|
||||||
|
</td>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
export default RunDisplay;
|
export default RunDisplay;
|
||||||
|
|
@ -14,7 +14,9 @@ interface RunInfoProps {
|
||||||
const Runner = ({setRunning, running, outHandler, input}: RunInfoProps) => {
|
const Runner = ({setRunning, running, outHandler, input}: RunInfoProps) => {
|
||||||
const [speed, setSpeed] = useState(0);
|
const [speed, setSpeed] = useState(0);
|
||||||
const [interpreter, setInterpreter] = useState<Interpreter | null>(null);
|
const [interpreter, setInterpreter] = useState<Interpreter | null>(null);
|
||||||
const [error, setError] = useState<string | null>(null);
|
const [info, setInfo] = useState<string | null>(null);
|
||||||
|
const [startTime, setStartTime] = useState(0);
|
||||||
|
|
||||||
|
|
||||||
const [, setRerenderNumber] = useState(0);
|
const [, setRerenderNumber] = useState(0);
|
||||||
|
|
||||||
|
|
@ -34,34 +36,48 @@ const Runner = ({setRunning, running, outHandler, input}: RunInfoProps) => {
|
||||||
return char;
|
return char;
|
||||||
}
|
}
|
||||||
|
|
||||||
const errorHandler = (msg: string) => setError(msg);
|
|
||||||
|
|
||||||
const startHandler = useCallback(() => {
|
const startHandler = useCallback(() => {
|
||||||
|
if (input[1].directStart) {
|
||||||
|
setSpeed(100);
|
||||||
|
} else {
|
||||||
setSpeed(0);
|
setSpeed(0);
|
||||||
setInterpreter(new Interpreter(input, outHandler, inputHandler, errorHandler));
|
}
|
||||||
|
|
||||||
|
setStartTime(Date.now);
|
||||||
|
setInterpreter(new Interpreter(input, outHandler, inputHandler));
|
||||||
setRunning(false);
|
setRunning(false);
|
||||||
setRunning(true);
|
setRunning(true);
|
||||||
}, [input, outHandler, setRunning]);
|
}, [input, outHandler, setRunning]);
|
||||||
|
|
||||||
const stopHandler = () => setRunning(false);
|
const stopHandler = () => {
|
||||||
|
setRunning(false);
|
||||||
|
setInfo(null);
|
||||||
|
}
|
||||||
|
|
||||||
const nextHandler = useCallback(() => {
|
const nextHandler = useCallback(() => {
|
||||||
setError(null);
|
setInfo(null);
|
||||||
|
try {
|
||||||
interpreter?.next();
|
interpreter?.next();
|
||||||
if (interpreter?.reachedEnd) {
|
} catch (e) {
|
||||||
|
setInfo(e.message);
|
||||||
setSpeed(0);
|
setSpeed(0);
|
||||||
}
|
}
|
||||||
|
if (interpreter?.reachedEnd) {
|
||||||
|
setSpeed(0);
|
||||||
|
setInfo(`Finished Execution. Took ${(Date.now() - startTime) / 1000}s`)
|
||||||
|
}
|
||||||
setRerenderNumber(n => n + 1);
|
setRerenderNumber(n => n + 1);
|
||||||
}, [interpreter]);
|
}, [interpreter, startTime]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (running) {
|
if (running) {
|
||||||
if (speed === 0) {
|
if (speed === 0) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const interval = setInterval(() => {
|
const interval = setInterval(() => {
|
||||||
nextHandler();
|
nextHandler();
|
||||||
}, 1000 / (speed));
|
}, 1000 / (speed * 10));
|
||||||
|
|
||||||
return () => clearInterval(interval);
|
return () => clearInterval(interval);
|
||||||
}
|
}
|
||||||
|
|
@ -77,23 +93,28 @@ const Runner = ({setRunning, running, outHandler, input}: RunInfoProps) => {
|
||||||
</>
|
</>
|
||||||
}
|
}
|
||||||
<div>
|
<div>
|
||||||
<button onClick={stopHandler}>Back</button>
|
{running && <button onClick={stopHandler}>Back</button>}
|
||||||
<button onClick={startHandler}>Start</button>
|
<button onClick={startHandler}>{running ? "Restart" : "Start"}</button>
|
||||||
<button onClick={nextHandler}>Next</button>
|
{running && <button onClick={nextHandler}>Next</button>}
|
||||||
</div>
|
</div>
|
||||||
{
|
{
|
||||||
running && <>
|
running &&
|
||||||
<div>
|
<div>
|
||||||
<label htmlFor="run-info-speed-range">Speed</label>
|
<label htmlFor="run-info-speed-range">Speed</label>
|
||||||
<input type="range" id="run-info-speed-range" value={speed}
|
<input type="range" id="run-info-speed-range" value={speed}
|
||||||
onChange={e => setSpeed(+e.target.value)}/>
|
onChange={e => setSpeed(+e.target.value)}/>
|
||||||
<span> {speed}</span>
|
<span> {speed}</span>
|
||||||
|
<span>
|
||||||
|
<button onClick={() => setSpeed(s => s === 0 ? 0 : s - 1)}
|
||||||
|
className="small-speed-button">-</button>
|
||||||
|
<button onClick={() => setSpeed(0)}
|
||||||
|
className="small-speed-button">0</button>
|
||||||
|
<button onClick={() => setSpeed(s => s === 100 ? 100 : s + 1)}
|
||||||
|
className="small-speed-button">+</button>
|
||||||
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</>
|
|
||||||
}
|
|
||||||
{
|
|
||||||
error && <div className="error">{error}</div>
|
|
||||||
}
|
}
|
||||||
|
{info && <div className="info">{info}</div>}
|
||||||
{
|
{
|
||||||
running && <div>
|
running && <div>
|
||||||
<div>Input:</div>
|
<div>Input:</div>
|
||||||
|
|
|
||||||
9
ibfi-ts/src/presets.json
Normal file
9
ibfi-ts/src/presets.json
Normal file
File diff suppressed because one or more lines are too long
Loading…
Add table
Add a link
Reference in a new issue