diff --git a/ibfi-ts/src/App.scss b/ibfi-ts/src/App.scss index 8316c9a..adfc14f 100644 --- a/ibfi-ts/src/App.scss +++ b/ibfi-ts/src/App.scss @@ -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; + } } \ No newline at end of file diff --git a/ibfi-ts/src/App.tsx b/ibfi-ts/src/App.tsx index da6bf5b..b719e72 100644 --- a/ibfi-ts/src/App.tsx +++ b/ibfi-ts/src/App.tsx @@ -17,12 +17,19 @@ function App() { return 65; }, []); + const runHandler = (run: boolean) => { + setRunning(run); + if (!run) { + setOut(""); + } + } + return (
{ !running && setInput(input)}/> } - + { running && } diff --git a/ibfi-ts/src/brainfuck/Interpreter.ts b/ibfi-ts/src/brainfuck/Interpreter.ts index ab40799..82cc6b8 100644 --- a/ibfi-ts/src/brainfuck/Interpreter.ts +++ b/ibfi-ts/src/brainfuck/Interpreter.ts @@ -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 { diff --git a/ibfi-ts/src/components/Runner.tsx b/ibfi-ts/src/components/Runner.tsx index ce48d5a..c62112d 100644 --- a/ibfi-ts/src/components/Runner.tsx +++ b/ibfi-ts/src/components/Runner.tsx @@ -12,64 +12,76 @@ interface RunInfoProps { } const Runner = ({setRunning, running, inHandler, outHandler, input}: RunInfoProps) => { - const [speed, setSpeed] = useState(0); - const [interpreter, setInterpreter] = useState(null); + const [speed, setSpeed] = useState(0); + const [interpreter, setInterpreter] = useState(null); + const [error, setError] = useState(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 ( -
- {running && interpreter && <> + return ( +
+ { + running && interpreter && <> - } -
- - - -
- { - running && - <> -
- - setSpeed(+e.target.value)}/> - {speed} -
- - } + } +
+ + +
- ); - } -; + { + running && <> +
+ + setSpeed(+e.target.value)}/> + {speed} +
+ + } + { + error &&
Error: '{error}'
+ } +
+ ); +}; export default Runner; \ No newline at end of file