improve layout, store program in url
* fix the command list being way too tall when there are few states * move the output to the top, maybe where it's like this (wrapping): +-----+ |1 2 3| |4 15 | |16 32| +-----+ * store and load program from url (maybe make own hook?)
This commit is contained in:
parent
1b54cd0d12
commit
4cb13e0a16
5 changed files with 15527 additions and 15256 deletions
30682
package-lock.json
generated
30682
package-lock.json
generated
File diff suppressed because it is too large
Load diff
|
@ -40,5 +40,8 @@
|
|||
"last 1 firefox version",
|
||||
"last 1 safari version"
|
||||
]
|
||||
},
|
||||
"devDependencies": {
|
||||
"react-use": "^17.4.0"
|
||||
}
|
||||
}
|
||||
|
|
39
src/App.css
39
src/App.css
|
@ -22,44 +22,65 @@ html {
|
|||
color: var(--fg);
|
||||
background-color: var(--bg);
|
||||
resize: none;
|
||||
outline: 1px solid var(--fg);
|
||||
border: none;
|
||||
border: 1px solid var(--fg);
|
||||
}
|
||||
|
||||
.program:focus-within {
|
||||
filter: brightness(1.2);
|
||||
outline: none;
|
||||
}
|
||||
|
||||
.info {
|
||||
z-index: -413;
|
||||
display: grid;
|
||||
width: 50%;
|
||||
grid-template-areas: "ops stack" "states states";
|
||||
grid-template-rows: max-content;
|
||||
}
|
||||
|
||||
.ops,
|
||||
/* .ops,
|
||||
.states {
|
||||
width: 50vw;
|
||||
}
|
||||
} */
|
||||
|
||||
.ops {
|
||||
/* position: fixed; */
|
||||
/* top: 0;
|
||||
left: 50%; */
|
||||
min-height: 25vh;
|
||||
margin-bottom: 0;
|
||||
background-color: var(--bg);
|
||||
border: 1px solid var(--fg);
|
||||
border-left: none;
|
||||
grid-area: ops;
|
||||
padding: 5px;
|
||||
}
|
||||
|
||||
.states {
|
||||
overflow: scroll;
|
||||
grid-area: states;
|
||||
}
|
||||
|
||||
.state {
|
||||
border-right: 1px solid var(--fg);
|
||||
border-bottom: 1px solid var(--fg);
|
||||
/* margin: 5px; */
|
||||
padding-left: 5px;
|
||||
}
|
||||
|
||||
.state > * {
|
||||
/* .state > * {
|
||||
margin-left: 5px;
|
||||
}
|
||||
} */
|
||||
|
||||
.stack {
|
||||
display: block;
|
||||
border: 1px solid var(--fg);
|
||||
border-left: none;
|
||||
grid-area: stack;
|
||||
padding: 5px;
|
||||
}
|
||||
|
||||
.stack::before {
|
||||
content: "Stack: ";
|
||||
}
|
||||
|
||||
/* later i can do .op.active and .state.active if necessary */
|
||||
|
@ -76,6 +97,10 @@ html {
|
|||
white-space: pre;
|
||||
}
|
||||
|
||||
.output {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
/* https://www.joshwcomeau.com/css/custom-css-reset/ */
|
||||
/*
|
||||
1. Use a more-intuitive box-sizing model.
|
||||
|
|
34
src/App.tsx
34
src/App.tsx
|
@ -1,16 +1,24 @@
|
|||
/* eslint-disable eqeqeq */
|
||||
import React, { useState } from "react";
|
||||
import { useHash } from "react-use";
|
||||
import TextArea from "./TextArea";
|
||||
import "./App.css";
|
||||
|
||||
export default function App() {
|
||||
let [input, setInput] = useState("");
|
||||
let [hash, setHash] = useHash();
|
||||
|
||||
let input = decodeURI(hash.slice(1));
|
||||
function setInput(s: string) {
|
||||
setHash(encodeURI(s));
|
||||
}
|
||||
|
||||
let [activeStateIdx, setActiveStateIdx] = useState(0);
|
||||
let parsed = somes(input.split(/[^a-z0-9-]+/).map(parse_op));
|
||||
let states = steps(INITIAL_STATE, parsed);
|
||||
return (
|
||||
<>
|
||||
<textarea
|
||||
onChange={e => setInput(e.target.value)}
|
||||
<TextArea
|
||||
onChange={(e: any) => setInput(e.target.value)}
|
||||
value={input}
|
||||
className="program"
|
||||
title={`\
|
||||
|
@ -26,14 +34,15 @@ type Op =
|
|||
| "del"
|
||||
| "cmp"
|
||||
| "swp";`}
|
||||
></textarea>
|
||||
></TextArea>
|
||||
<div className="info">
|
||||
<Ops
|
||||
parsed={parsed}
|
||||
ip={(states[activeStateIdx] ?? INITIAL_STATE).ip}
|
||||
ip={states[activeStateIdx]?.ip ?? 0}
|
||||
// // hack
|
||||
// top={document.getElementById("input")?.clientHeight ?? 0}
|
||||
/>
|
||||
<Stack stack={states[activeStateIdx]?.stack ?? []} />
|
||||
<States
|
||||
states={states}
|
||||
activeStateIdx={activeStateIdx}
|
||||
|
@ -131,13 +140,13 @@ function State({
|
|||
{...props}
|
||||
>
|
||||
<div className="ip">Ip: {state.ip}</div>
|
||||
<span>Stack:</span> <Stack stack={state.stack} />
|
||||
{/* <span>Stack:</span> <Stack stack={state.stack} /> */}
|
||||
<span>Output:</span> <Output output={state.output} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function Stack({ stack }: { stack: number[] }): JSX.Element {
|
||||
function Stack({ stack, ...props }: { stack: number[] }): JSX.Element {
|
||||
let ns = [...stack];
|
||||
ns.reverse();
|
||||
return (
|
||||
|
@ -146,6 +155,7 @@ function Stack({ stack }: { stack: number[] }): JSX.Element {
|
|||
children={ns.map((item, i) => (
|
||||
<StackItem item={item} key={i} />
|
||||
))}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
@ -164,7 +174,7 @@ function Output({ output }: { output: number[] }): JSX.Element {
|
|||
<div
|
||||
className="output"
|
||||
children={output.map((item, i) => (
|
||||
<StackItem item={item} key={i} />
|
||||
<span key={i}>{String.fromCharCode(item)}</span>
|
||||
))}
|
||||
></div>
|
||||
);
|
||||
|
@ -219,8 +229,9 @@ function parse_int(n: string, radix?: number): Option<number> {
|
|||
}
|
||||
|
||||
function parse_op(op: string): Option<Op> {
|
||||
if (parse_int(op)) {
|
||||
return parse_int(op);
|
||||
let maybe_int = parse_int(op);
|
||||
if (is_some(maybe_int)) {
|
||||
return maybe_int;
|
||||
}
|
||||
switch (op) {
|
||||
case "add":
|
||||
|
@ -235,6 +246,7 @@ function parse_op(op: string): Option<Op> {
|
|||
case "swp":
|
||||
return op;
|
||||
}
|
||||
console.log(op);
|
||||
}
|
||||
|
||||
type State = {
|
||||
|
@ -249,7 +261,7 @@ const INITIAL_STATE: State = {
|
|||
output: [],
|
||||
};
|
||||
|
||||
function steps(state: State, ops: Op[], limit = 100, num_steps = 0): State[] {
|
||||
function steps(state: State, ops: Op[], limit = 300, num_steps = 0): State[] {
|
||||
if (!ops[state.ip] || num_steps > limit) {
|
||||
return [state];
|
||||
}
|
||||
|
|
25
src/TextArea.tsx
Normal file
25
src/TextArea.tsx
Normal file
|
@ -0,0 +1,25 @@
|
|||
// @ts-nocheck
|
||||
// https://stackoverflow.com/a/68928267/18571336
|
||||
import React, { useEffect, useRef, useState } from "react";
|
||||
|
||||
function TextArea(props) {
|
||||
const { value, onChange, ...rest } = props;
|
||||
const [cursor, setCursor] = useState(null);
|
||||
const ref = useRef(null);
|
||||
|
||||
useEffect(() => {
|
||||
const input = ref.current;
|
||||
if (input) input.setSelectionRange(cursor, cursor);
|
||||
}, [ref, cursor, value]);
|
||||
|
||||
const handleChange = e => {
|
||||
setCursor(e.target.selectionStart);
|
||||
onChange && onChange(e);
|
||||
};
|
||||
|
||||
return (
|
||||
<textarea ref={ref} value={value} onChange={handleChange} {...rest} />
|
||||
);
|
||||
}
|
||||
|
||||
export default TextArea;
|
Loading…
Reference in a new issue