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-brighter: #323942;
$font-color: ghostwhite;
$light-color: ghostwhite;
$medium-color: #78787f;
.App {
text-align: center;
@ -19,7 +20,7 @@ $font-color: ghostwhite;
align-items: center;
justify-content: center;
font-size: calc(10px + 2vmin);
color: $font-color;
color: $light-color;
}
.App-link {
@ -44,7 +45,7 @@ $font-color: ghostwhite;
}
.memory-display-table {
$border: 1px solid $font-color;
$border: 1px solid $light-color;
th, .cell {
border: $border;
@ -67,6 +68,10 @@ $font-color: ghostwhite;
height: 50px;
width: 200px;
}
.error {
background-color: #664242FF;
}
}
.bf-output {
@ -80,5 +85,16 @@ $font-color: ghostwhite;
textarea {
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;
}, []);
const runHandler = (run: boolean) => {
setRunning(run);
if (!run) {
setOut("");
}
}
return (
<div className="App-header">
{
!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}/>
}

View file

@ -1,13 +1,18 @@
type InHandler = (() => number);
type OutHandler = ((char: number) => void);
type ErrorHandler = ((msg: string) => void);
export default class Interpreter {
private readonly _array: Uint8Array;
private _pointer: number;
private readonly _code: string;
private _codePointer: number;
private readonly _inHandler: (() => number);
private readonly _outHandler: ((char: number) => void);
private readonly _inHandler: InHandler;
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);
this._array = new Uint8Array(buf);
this._pointer = 0;
@ -15,6 +20,7 @@ export default class Interpreter {
this._codePointer = 0;
this._inHandler = inHandler;
this._outHandler = outHandler;
this._errorHandler = errorHandler;
}
public next() {
@ -29,6 +35,10 @@ export default class Interpreter {
this._pointer++;
break;
case '<':
if (this._pointer === 0) {
this._errorHandler("Cannot wrap left");
break;
}
this._pointer--;
break;
case '.':
@ -40,24 +50,25 @@ export default class Interpreter {
case '[':
if (this.value === 0) {
let level = 0;
while (this.instruction !== ']' || level > -1) {
while (this.lastInstruction !== ']' || level > -1) {
this._codePointer++;
if (this.instruction === '[') level++;
else if (this.instruction === ']') level--;
if (this.lastInstruction === '[') level++;
else if (this.lastInstruction === ']') level--;
}
}
break;
case ']':
if (this.value !== 0) {
let level = 0;
while (this.instruction !== '[' || level > -1) {
while (this.lastInstruction !== '[' || level > -1) {
this._codePointer--;
if (this.instruction === '[') level--;
else if (this.instruction === ']') level++;
if (this.lastInstruction === '[') level--;
else if (this.lastInstruction === ']') level++;
}
}
break;
case undefined:
this._pointer--;
console.warn("reached end");
break;
default: {
@ -70,8 +81,12 @@ export default class Interpreter {
}
get instruction(): string {
return this._code[this._codePointer];
get reachedEnd(): boolean {
return this._codePointer === this._code.length - 1;
}
get lastInstruction(): string {
return this._code[this._codePointer - 1];
}
get value(): number {

View file

@ -12,64 +12,76 @@ interface RunInfoProps {
}
const Runner = ({setRunning, running, inHandler, outHandler, input}: RunInfoProps) => {
const [speed, setSpeed] = useState(0);
const [interpreter, setInterpreter] = useState<Interpreter | null>(null);
const [speed, setSpeed] = useState(0);
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);
setInterpreter(new Interpreter(input, outHandler, inHandler));
setRunning(true);
}, [input, inHandler, outHandler, setRunning]);
}
setRerenderNumber(n => n + 1);
}, [interpreter]);
const stopHandler = () => setRunning(false);
const nextHandler = useCallback(() => {
interpreter?.next();
setRerenderNumber(n => n + 1);
}, [interpreter]);
useEffect(() => {
if (running) {
if (speed === 0) {
return;
}
const interval = setInterval(() => {
nextHandler();
}, 1000 / (speed));
return () => clearInterval(interval);
useEffect(() => {
if (running) {
if (speed === 0) {
return;
}
}, [running, nextHandler, speed]);
const interval = setInterval(() => {
nextHandler();
}, 1000 / (speed));
return () => clearInterval(interval);
}
}, [running, nextHandler, speed]);
return (
<div className="bf-run">
{running && interpreter && <>
return (
<div className="bf-run">
{
running && interpreter && <>
<CodeDisplay code={input} index={interpreter.codePointer}/>
<RunDisplay interpreter={interpreter}/>
</>
}
<div>
<button onClick={startHandler}>Start</button>
<button onClick={stopHandler}>Stop</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>
<button onClick={stopHandler}>Back</button>
<button onClick={startHandler}>Start</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>
</>
}
{
error && <div className="error">Error: '{error}'</div>
}
</div>
);
};
export default Runner;