mirror of
https://github.com/Noratrieb/brainfuck.git
synced 2026-01-14 13:35:00 +01:00
input
This commit is contained in:
parent
a96567038d
commit
579d81021a
7 changed files with 108 additions and 46 deletions
|
|
@ -51,12 +51,8 @@ $medium-color: #78787f;
|
|||
border: $border;
|
||||
}
|
||||
|
||||
.pointer {
|
||||
|
||||
}
|
||||
|
||||
th, td {
|
||||
width: 30px;
|
||||
min-width: 60px;
|
||||
text-align: center;
|
||||
}
|
||||
}
|
||||
|
|
@ -69,6 +65,13 @@ $medium-color: #78787f;
|
|||
width: 200px;
|
||||
}
|
||||
|
||||
.program-input-area {
|
||||
resize: none;
|
||||
width: 80vw;
|
||||
height: 50px;
|
||||
font-size: 30px;
|
||||
}
|
||||
|
||||
.error {
|
||||
background-color: #664242FF;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,22 +1,18 @@
|
|||
import './App.scss';
|
||||
import CodeInput from "./components/CodeInput";
|
||||
import CodeInput, {CodeOptions} from "./components/CodeInput";
|
||||
import ProgramOutput from "./components/ProgramOutput";
|
||||
import React, {useCallback, useState} from "react";
|
||||
import Runner from "./components/Runner";
|
||||
|
||||
function App() {
|
||||
const [out, setOut] = useState("");
|
||||
const [input, setInput] = useState("");
|
||||
const [input, setInput] = useState<[string, CodeOptions]>(["", {}]);
|
||||
const [running, setRunning] = useState(false);
|
||||
|
||||
const outHandler = useCallback((char: number) => {
|
||||
setOut(out => out + String.fromCharCode(char))
|
||||
}, []);
|
||||
|
||||
const inHandler = useCallback((): number => {
|
||||
return 65;
|
||||
}, []);
|
||||
|
||||
const runHandler = (run: boolean) => {
|
||||
setRunning(run);
|
||||
if (!run) {
|
||||
|
|
@ -24,12 +20,14 @@ function App() {
|
|||
}
|
||||
}
|
||||
|
||||
const inputHandler = (code: string, options: CodeOptions) => setInput([code, options]);
|
||||
|
||||
return (
|
||||
<div className="App-header">
|
||||
{
|
||||
!running && <CodeInput setInput={input => setInput(input)}/>
|
||||
!running && <CodeInput code={input[0]} setInput={inputHandler}/>
|
||||
}
|
||||
<Runner running={running} setRunning={runHandler} input={input} outHandler={outHandler} inHandler={inHandler}/>
|
||||
<Runner running={running} setRunning={runHandler} input={input} outHandler={outHandler}/>
|
||||
{
|
||||
running && <ProgramOutput text={out}/>
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,3 +1,5 @@
|
|||
import {CodeOptions} from "../components/CodeInput";
|
||||
|
||||
type InHandler = (() => number);
|
||||
type OutHandler = ((char: number) => void);
|
||||
type ErrorHandler = ((msg: string) => void);
|
||||
|
|
@ -6,25 +8,31 @@ export default class Interpreter {
|
|||
private readonly _array: Uint8Array;
|
||||
private _pointer: number;
|
||||
private readonly _code: string;
|
||||
private _codePointer: number;
|
||||
private _programCounter: number;
|
||||
|
||||
private readonly _inHandler: InHandler;
|
||||
private readonly _outHandler: OutHandler;
|
||||
private readonly _errorHandler: ErrorHandler;
|
||||
|
||||
constructor(code: string, outHandler: OutHandler, inHandler: InHandler, errorHandler: ErrorHandler) {
|
||||
constructor(input: [string, CodeOptions], outHandler: OutHandler, inHandler: InHandler, errorHandler: ErrorHandler) {
|
||||
const buf = new ArrayBuffer(32000);
|
||||
this._array = new Uint8Array(buf);
|
||||
this._pointer = 0;
|
||||
this._code = code;
|
||||
this._codePointer = 0;
|
||||
|
||||
if (input[1].minify) {
|
||||
this._code = minify(input[0])
|
||||
} else {
|
||||
this._code = input[0];
|
||||
}
|
||||
|
||||
this._programCounter = 0;
|
||||
this._inHandler = inHandler;
|
||||
this._outHandler = outHandler;
|
||||
this._errorHandler = errorHandler;
|
||||
}
|
||||
|
||||
public next() {
|
||||
switch (this._code[this._codePointer++]) {
|
||||
switch (this._code[this._programCounter++]) {
|
||||
case '+':
|
||||
this._array[this._pointer]++;
|
||||
break;
|
||||
|
|
@ -45,13 +53,18 @@ export default class Interpreter {
|
|||
this._outHandler(this.value);
|
||||
break;
|
||||
case ',':
|
||||
this._array[this._pointer] = this._inHandler();
|
||||
try {
|
||||
this._array[this._pointer] = this._inHandler();
|
||||
} catch {
|
||||
this._programCounter--;
|
||||
this._errorHandler("Could not read input, trying again next time.")
|
||||
}
|
||||
break;
|
||||
case '[':
|
||||
if (this.value === 0) {
|
||||
let level = 0;
|
||||
while (this.lastInstruction !== ']' || level > -1) {
|
||||
this._codePointer++;
|
||||
this._programCounter++;
|
||||
if (this.lastInstruction === '[') level++;
|
||||
else if (this.lastInstruction === ']') level--;
|
||||
}
|
||||
|
|
@ -61,20 +74,20 @@ export default class Interpreter {
|
|||
if (this.value !== 0) {
|
||||
let level = 0;
|
||||
while (this.lastInstruction !== '[' || level > -1) {
|
||||
this._codePointer--;
|
||||
this._programCounter--;
|
||||
if (this.lastInstruction === '[') level--;
|
||||
else if (this.lastInstruction === ']') level++;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case undefined:
|
||||
this._pointer--;
|
||||
this._pointer = this._code.length;
|
||||
console.warn("reached end");
|
||||
break;
|
||||
default: {
|
||||
}
|
||||
}
|
||||
console.log(`char: ${this.code[this.codePointer - 1]} pointer: ${this.pointer} value: ${this.array[this.pointer]}`)
|
||||
console.log(`char: ${this.code[this.programCounter - 1]} pointer: ${this.pointer} value: ${this.array[this.pointer]}`)
|
||||
}
|
||||
|
||||
public prev() {
|
||||
|
|
@ -82,11 +95,11 @@ export default class Interpreter {
|
|||
}
|
||||
|
||||
get reachedEnd(): boolean {
|
||||
return this._codePointer === this._code.length - 1;
|
||||
return this._programCounter === this._code.length;
|
||||
}
|
||||
|
||||
get lastInstruction(): string {
|
||||
return this._code[this._codePointer - 1];
|
||||
return this._code[this._programCounter - 1];
|
||||
}
|
||||
|
||||
get value(): number {
|
||||
|
|
@ -105,7 +118,14 @@ export default class Interpreter {
|
|||
return this._code;
|
||||
}
|
||||
|
||||
get codePointer(): number {
|
||||
return this._codePointer;
|
||||
get programCounter(): number {
|
||||
return this._programCounter;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const CHARS = ['+', '-', '<', '>', '.', ',', '[', ']'];
|
||||
const minify = (code: string): string =>
|
||||
code.split("")
|
||||
.filter(c => CHARS.includes(c))
|
||||
.join("");
|
||||
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ const CodeDisplay = ({code, index}: CodeDisplayProps) => {
|
|||
return (
|
||||
<div className="code-display-wrapper">
|
||||
<span>{firstCodePart}</span>
|
||||
<span style={{backgroundColor: "red"}}>{code[index]}</span>
|
||||
<span style={{backgroundColor: "red"}}>{code[index] || " "}</span>
|
||||
<span>{secondCodePart}</span>
|
||||
</div>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -1,27 +1,48 @@
|
|||
import React, {useState} from 'react';
|
||||
|
||||
interface CodeInputProps {
|
||||
setInput: ((input: string) => void),
|
||||
export interface CodeOptions {
|
||||
minify?: boolean
|
||||
}
|
||||
|
||||
const CodeInput = ({setInput}: CodeInputProps) => {
|
||||
interface CodeInputProps {
|
||||
setInput: ((code: string, options: CodeOptions) => void),
|
||||
code: string
|
||||
}
|
||||
|
||||
const CodeInput = ({code, setInput}: CodeInputProps) => {
|
||||
const [fontSize, setFontSize] = useState(40);
|
||||
|
||||
const [codeOptions, setCodeOptions] = useState<CodeOptions>({});
|
||||
|
||||
|
||||
const setStart = () => {
|
||||
setInput(
|
||||
"++++++++[>++++[>++>+++>+++>+<<<<-]>+>+>->>+[<]<-]>>.>---.+++++++..+++.>>.<-.<.+++.------.--------.>>+.>++.");
|
||||
"++++++++[>++++[>++>+++>+++>+<<<<-]>+>+>->>+[<]<-]>>.>---.+++++++..+++.>>.<-.<.+++.------.--------.>>+.>++.",
|
||||
codeOptions);
|
||||
}
|
||||
|
||||
const changeMinify = (e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
setCodeOptions(old => ({...old, minify: e.target.checked}))
|
||||
setInput(code, codeOptions);
|
||||
}
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div className="bf-input">
|
||||
<div>
|
||||
<span>
|
||||
<label htmlFor="bf-input-fontsize-range">Font Size</label>
|
||||
<input type="range" id="bf-input-fontsize-range" onChange={v => setFontSize(+v.target.value)}/>
|
||||
<button onClick={setStart}>set init</button>
|
||||
</span>
|
||||
<input type="checkbox" checked={codeOptions.minify} id="input-options-minify" onChange={changeMinify}/>
|
||||
<label htmlFor="input-options-minify">Minify Code</label>
|
||||
</div>
|
||||
<textarea onChange={e => setInput(e.target.value)} style={{fontSize}} className="code-input"
|
||||
<textarea value={code} onChange={e => setInput(e.target.value, codeOptions)} style={{fontSize}}
|
||||
className="code-input"
|
||||
placeholder="Input your code here..."/>
|
||||
<div>
|
||||
<button onClick={setStart}>Set Hello World</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import React from 'react';
|
||||
import Interpreter from "../brainfuck/Interpreter";
|
||||
|
||||
const MAX_TABLE_COLUMNS = 30;
|
||||
const MAX_TABLE_COLUMNS = 20;
|
||||
|
||||
interface RunDisplayProps {
|
||||
interpreter: Interpreter,
|
||||
|
|
|
|||
|
|
@ -1,33 +1,47 @@
|
|||
import React, {useCallback, useEffect, useState} from 'react';
|
||||
import React, {useCallback, useEffect, useRef, useState} from 'react';
|
||||
import Interpreter from "../brainfuck/Interpreter";
|
||||
import CodeDisplay from "./CodeDisplay";
|
||||
import RunDisplay from "./RunDisplay";
|
||||
import {CodeOptions} from "./CodeInput";
|
||||
|
||||
interface RunInfoProps {
|
||||
input: string,
|
||||
input: [string, CodeOptions],
|
||||
setRunning: (running: boolean) => void,
|
||||
running: boolean
|
||||
inHandler: () => number,
|
||||
outHandler: (char: number) => void,
|
||||
}
|
||||
|
||||
const Runner = ({setRunning, running, inHandler, outHandler, input}: RunInfoProps) => {
|
||||
const Runner = ({setRunning, running, outHandler, input}: RunInfoProps) => {
|
||||
const [speed, setSpeed] = useState(0);
|
||||
const [interpreter, setInterpreter] = useState<Interpreter | null>(null);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
|
||||
const [, setRerenderNumber] = useState(0);
|
||||
|
||||
const errorHandler = (msg: string) => {
|
||||
setError(msg);
|
||||
const inputArea = useRef<HTMLTextAreaElement>(null);
|
||||
|
||||
|
||||
const inputHandler = () => {
|
||||
if (!inputArea.current) {
|
||||
throw new Error("Could not read input")
|
||||
}
|
||||
const value = inputArea.current.value;
|
||||
if (value.length < 1) {
|
||||
throw new Error("No input found");
|
||||
}
|
||||
const char = value.charCodeAt(0);
|
||||
inputArea.current.value = value.substr(1);
|
||||
return char;
|
||||
}
|
||||
|
||||
const errorHandler = (msg: string) => setError(msg);
|
||||
|
||||
const startHandler = useCallback(() => {
|
||||
setSpeed(0);
|
||||
setInterpreter(new Interpreter(input, outHandler, inHandler, errorHandler));
|
||||
setInterpreter(new Interpreter(input, outHandler, inputHandler, errorHandler));
|
||||
setRunning(false);
|
||||
setRunning(true);
|
||||
}, [input, inHandler, outHandler, setRunning]);
|
||||
}, [input, outHandler, setRunning]);
|
||||
|
||||
const stopHandler = () => setRunning(false);
|
||||
|
||||
|
|
@ -58,7 +72,7 @@ const Runner = ({setRunning, running, inHandler, outHandler, input}: RunInfoProp
|
|||
<div className="bf-run">
|
||||
{
|
||||
running && interpreter && <>
|
||||
<CodeDisplay code={input} index={interpreter.codePointer}/>
|
||||
<CodeDisplay code={interpreter.code} index={interpreter.programCounter}/>
|
||||
<RunDisplay interpreter={interpreter}/>
|
||||
</>
|
||||
}
|
||||
|
|
@ -78,7 +92,13 @@ const Runner = ({setRunning, running, inHandler, outHandler, input}: RunInfoProp
|
|||
</>
|
||||
}
|
||||
{
|
||||
error && <div className="error">Error: '{error}'</div>
|
||||
error && <div className="error">{error}</div>
|
||||
}
|
||||
{
|
||||
running && <div>
|
||||
<div>Input:</div>
|
||||
<textarea className="program-input-area" ref={inputArea}/>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue