mirror of
https://github.com/Noratrieb/brainfuck.git
synced 2026-01-14 13:35:00 +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-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;
|
||||
}
|
||||
}
|
||||
|
|
@ -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}/>
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
Loading…
Add table
Add a link
Reference in a new issue