mirror of
https://github.com/Noratrieb/brainfuck.git
synced 2026-01-16 22:35:03 +01:00
display
This commit is contained in:
parent
5f6d1d5b4b
commit
273027e8af
4 changed files with 114 additions and 64 deletions
|
|
@ -1,6 +1,7 @@
|
||||||
$main-color: #282c34;
|
$main-color: #282c34;
|
||||||
$main-color-brighter: #323942;
|
$main-color-brighter: #323942;
|
||||||
$font-color: ghostwhite;
|
$light-color: ghostwhite;
|
||||||
|
$medium-color: #78787f;
|
||||||
|
|
||||||
.App {
|
.App {
|
||||||
text-align: center;
|
text-align: center;
|
||||||
|
|
@ -19,7 +20,7 @@ $font-color: ghostwhite;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
font-size: calc(10px + 2vmin);
|
font-size: calc(10px + 2vmin);
|
||||||
color: $font-color;
|
color: $light-color;
|
||||||
}
|
}
|
||||||
|
|
||||||
.App-link {
|
.App-link {
|
||||||
|
|
@ -44,7 +45,7 @@ $font-color: ghostwhite;
|
||||||
}
|
}
|
||||||
|
|
||||||
.memory-display-table {
|
.memory-display-table {
|
||||||
$border: 1px solid $font-color;
|
$border: 1px solid $light-color;
|
||||||
|
|
||||||
th, .cell {
|
th, .cell {
|
||||||
border: $border;
|
border: $border;
|
||||||
|
|
@ -67,6 +68,10 @@ $font-color: ghostwhite;
|
||||||
height: 50px;
|
height: 50px;
|
||||||
width: 200px;
|
width: 200px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.error {
|
||||||
|
background-color: #664242FF;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.bf-output {
|
.bf-output {
|
||||||
|
|
@ -80,5 +85,16 @@ $font-color: ghostwhite;
|
||||||
|
|
||||||
textarea {
|
textarea {
|
||||||
background-color: $main-color-brighter;
|
background-color: $main-color-brighter;
|
||||||
color: $font-color;
|
color: $light-color;
|
||||||
|
}
|
||||||
|
|
||||||
|
button {
|
||||||
|
background-color: $medium-color;
|
||||||
|
font-size: 20px;
|
||||||
|
border: 1px solid $main-color;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
cursor: pointer;
|
||||||
|
background-color: $light-color;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -17,12 +17,19 @@ function App() {
|
||||||
return 65;
|
return 65;
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
|
const runHandler = (run: boolean) => {
|
||||||
|
setRunning(run);
|
||||||
|
if (!run) {
|
||||||
|
setOut("");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="App-header">
|
<div className="App-header">
|
||||||
{
|
{
|
||||||
!running && <CodeInput setInput={input => setInput(input)}/>
|
!running && <CodeInput setInput={input => setInput(input)}/>
|
||||||
}
|
}
|
||||||
<Runner running={running} setRunning={setRunning} input={input} outHandler={outHandler} inHandler={inHandler}/>
|
<Runner running={running} setRunning={runHandler} input={input} outHandler={outHandler} inHandler={inHandler}/>
|
||||||
{
|
{
|
||||||
running && <ProgramOutput text={out}/>
|
running && <ProgramOutput text={out}/>
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,13 +1,18 @@
|
||||||
|
type InHandler = (() => number);
|
||||||
|
type OutHandler = ((char: number) => void);
|
||||||
|
type ErrorHandler = ((msg: string) => void);
|
||||||
|
|
||||||
export default class Interpreter {
|
export default class Interpreter {
|
||||||
private readonly _array: Uint8Array;
|
private readonly _array: Uint8Array;
|
||||||
private _pointer: number;
|
private _pointer: number;
|
||||||
private readonly _code: string;
|
private readonly _code: string;
|
||||||
private _codePointer: number;
|
private _codePointer: number;
|
||||||
|
|
||||||
private readonly _inHandler: (() => number);
|
private readonly _inHandler: InHandler;
|
||||||
private readonly _outHandler: ((char: number) => void);
|
private readonly _outHandler: OutHandler;
|
||||||
|
private readonly _errorHandler: ErrorHandler;
|
||||||
|
|
||||||
constructor(code: string, outHandler: ((char: number) => void), inHandler: (() => number)) {
|
constructor(code: string, outHandler: OutHandler, inHandler: InHandler, errorHandler: ErrorHandler) {
|
||||||
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;
|
||||||
|
|
@ -15,6 +20,7 @@ export default class Interpreter {
|
||||||
this._codePointer = 0;
|
this._codePointer = 0;
|
||||||
this._inHandler = inHandler;
|
this._inHandler = inHandler;
|
||||||
this._outHandler = outHandler;
|
this._outHandler = outHandler;
|
||||||
|
this._errorHandler = errorHandler;
|
||||||
}
|
}
|
||||||
|
|
||||||
public next() {
|
public next() {
|
||||||
|
|
@ -29,6 +35,10 @@ export default class Interpreter {
|
||||||
this._pointer++;
|
this._pointer++;
|
||||||
break;
|
break;
|
||||||
case '<':
|
case '<':
|
||||||
|
if (this._pointer === 0) {
|
||||||
|
this._errorHandler("Cannot wrap left");
|
||||||
|
break;
|
||||||
|
}
|
||||||
this._pointer--;
|
this._pointer--;
|
||||||
break;
|
break;
|
||||||
case '.':
|
case '.':
|
||||||
|
|
@ -40,24 +50,25 @@ export default class Interpreter {
|
||||||
case '[':
|
case '[':
|
||||||
if (this.value === 0) {
|
if (this.value === 0) {
|
||||||
let level = 0;
|
let level = 0;
|
||||||
while (this.instruction !== ']' || level > -1) {
|
while (this.lastInstruction !== ']' || level > -1) {
|
||||||
this._codePointer++;
|
this._codePointer++;
|
||||||
if (this.instruction === '[') level++;
|
if (this.lastInstruction === '[') level++;
|
||||||
else if (this.instruction === ']') level--;
|
else if (this.lastInstruction === ']') level--;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case ']':
|
case ']':
|
||||||
if (this.value !== 0) {
|
if (this.value !== 0) {
|
||||||
let level = 0;
|
let level = 0;
|
||||||
while (this.instruction !== '[' || level > -1) {
|
while (this.lastInstruction !== '[' || level > -1) {
|
||||||
this._codePointer--;
|
this._codePointer--;
|
||||||
if (this.instruction === '[') level--;
|
if (this.lastInstruction === '[') level--;
|
||||||
else if (this.instruction === ']') level++;
|
else if (this.lastInstruction === ']') level++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case undefined:
|
case undefined:
|
||||||
|
this._pointer--;
|
||||||
console.warn("reached end");
|
console.warn("reached end");
|
||||||
break;
|
break;
|
||||||
default: {
|
default: {
|
||||||
|
|
@ -70,8 +81,12 @@ export default class Interpreter {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
get instruction(): string {
|
get reachedEnd(): boolean {
|
||||||
return this._code[this._codePointer];
|
return this._codePointer === this._code.length - 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
get lastInstruction(): string {
|
||||||
|
return this._code[this._codePointer - 1];
|
||||||
}
|
}
|
||||||
|
|
||||||
get value(): number {
|
get value(): number {
|
||||||
|
|
|
||||||
|
|
@ -12,64 +12,76 @@ interface RunInfoProps {
|
||||||
}
|
}
|
||||||
|
|
||||||
const Runner = ({setRunning, running, inHandler, outHandler, input}: RunInfoProps) => {
|
const Runner = ({setRunning, running, inHandler, 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 [, setRerenderNumber] = useState(0);
|
const [, setRerenderNumber] = useState(0);
|
||||||
|
|
||||||
const startHandler = useCallback(() => {
|
const errorHandler = (msg: string) => {
|
||||||
|
setError(msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
const startHandler = useCallback(() => {
|
||||||
|
setSpeed(0);
|
||||||
|
setInterpreter(new Interpreter(input, outHandler, inHandler, errorHandler));
|
||||||
|
setRunning(false);
|
||||||
|
setRunning(true);
|
||||||
|
}, [input, inHandler, outHandler, setRunning]);
|
||||||
|
|
||||||
|
const stopHandler = () => setRunning(false);
|
||||||
|
|
||||||
|
const nextHandler = useCallback(() => {
|
||||||
|
setError(null);
|
||||||
|
interpreter?.next();
|
||||||
|
if (interpreter?.reachedEnd) {
|
||||||
setSpeed(0);
|
setSpeed(0);
|
||||||
setInterpreter(new Interpreter(input, outHandler, inHandler));
|
}
|
||||||
setRunning(true);
|
setRerenderNumber(n => n + 1);
|
||||||
}, [input, inHandler, outHandler, setRunning]);
|
}, [interpreter]);
|
||||||
|
|
||||||
const stopHandler = () => setRunning(false);
|
useEffect(() => {
|
||||||
|
if (running) {
|
||||||
const nextHandler = useCallback(() => {
|
if (speed === 0) {
|
||||||
interpreter?.next();
|
return;
|
||||||
setRerenderNumber(n => n + 1);
|
|
||||||
}, [interpreter]);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (running) {
|
|
||||||
if (speed === 0) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const interval = setInterval(() => {
|
|
||||||
nextHandler();
|
|
||||||
}, 1000 / (speed));
|
|
||||||
|
|
||||||
return () => clearInterval(interval);
|
|
||||||
}
|
}
|
||||||
}, [running, nextHandler, speed]);
|
const interval = setInterval(() => {
|
||||||
|
nextHandler();
|
||||||
|
}, 1000 / (speed));
|
||||||
|
|
||||||
|
return () => clearInterval(interval);
|
||||||
|
}
|
||||||
|
}, [running, nextHandler, speed]);
|
||||||
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="bf-run">
|
<div className="bf-run">
|
||||||
{running && interpreter && <>
|
{
|
||||||
|
running && interpreter && <>
|
||||||
<CodeDisplay code={input} index={interpreter.codePointer}/>
|
<CodeDisplay code={input} index={interpreter.codePointer}/>
|
||||||
<RunDisplay interpreter={interpreter}/>
|
<RunDisplay interpreter={interpreter}/>
|
||||||
</>
|
</>
|
||||||
}
|
}
|
||||||
<div>
|
<div>
|
||||||
<button onClick={startHandler}>Start</button>
|
<button onClick={stopHandler}>Back</button>
|
||||||
<button onClick={stopHandler}>Stop</button>
|
<button onClick={startHandler}>Start</button>
|
||||||
<button onClick={nextHandler}>Next</button>
|
<button onClick={nextHandler}>Next</button>
|
||||||
</div>
|
|
||||||
{
|
|
||||||
running &&
|
|
||||||
<>
|
|
||||||
<div>
|
|
||||||
<label htmlFor="run-info-speed-range">Speed</label>
|
|
||||||
<input type="range" id="run-info-speed-range" value={speed}
|
|
||||||
onChange={e => setSpeed(+e.target.value)}/>
|
|
||||||
<span> {speed}</span>
|
|
||||||
</div>
|
|
||||||
</>
|
|
||||||
}
|
|
||||||
</div>
|
</div>
|
||||||
);
|
{
|
||||||
}
|
running && <>
|
||||||
;
|
<div>
|
||||||
|
<label htmlFor="run-info-speed-range">Speed</label>
|
||||||
|
<input type="range" id="run-info-speed-range" value={speed}
|
||||||
|
onChange={e => setSpeed(+e.target.value)}/>
|
||||||
|
<span> {speed}</span>
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
}
|
||||||
|
{
|
||||||
|
error && <div className="error">Error: '{error}'</div>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
export default Runner;
|
export default Runner;
|
||||||
Loading…
Add table
Add a link
Reference in a new issue