This commit is contained in:
nora 2021-06-24 11:51:10 +02:00
parent 5f6d1d5b4b
commit 273027e8af
4 changed files with 114 additions and 64 deletions

View file

@ -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;
}
} }

View file

@ -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}/>
} }

View file

@ -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 {

View file

@ -14,19 +14,29 @@ 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 errorHandler = (msg: string) => {
setError(msg);
}
const startHandler = useCallback(() => { const startHandler = useCallback(() => {
setSpeed(0); setSpeed(0);
setInterpreter(new Interpreter(input, outHandler, inHandler)); setInterpreter(new Interpreter(input, outHandler, inHandler, errorHandler));
setRunning(false);
setRunning(true); setRunning(true);
}, [input, inHandler, outHandler, setRunning]); }, [input, inHandler, outHandler, setRunning]);
const stopHandler = () => setRunning(false); const stopHandler = () => setRunning(false);
const nextHandler = useCallback(() => { const nextHandler = useCallback(() => {
setError(null);
interpreter?.next(); interpreter?.next();
if (interpreter?.reachedEnd) {
setSpeed(0);
}
setRerenderNumber(n => n + 1); setRerenderNumber(n => n + 1);
}, [interpreter]); }, [interpreter]);
@ -46,19 +56,19 @@ const Runner = ({setRunning, running, inHandler, outHandler, input}: RunInfoProp
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={stopHandler}>Back</button>
<button onClick={startHandler}>Start</button> <button onClick={startHandler}>Start</button>
<button onClick={stopHandler}>Stop</button>
<button onClick={nextHandler}>Next</button> <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}
@ -67,9 +77,11 @@ const Runner = ({setRunning, running, inHandler, outHandler, input}: RunInfoProp
</div> </div>
</> </>
} }
{
error && <div className="error">Error: '{error}'</div>
}
</div> </div>
); );
} };
;
export default Runner; export default Runner;