Skip to content

Commit 6cd56cb

Browse files
committed
Initial commit
0 parents  commit 6cd56cb

18 files changed

+3224
-0
lines changed

.babelrc

+4
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
{
2+
"presets": ["next/babel"],
3+
"plugins": [["styled-components", { "ssr": true }]]
4+
}

.gitignore

+34
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2+
3+
# dependencies
4+
/node_modules
5+
/.pnp
6+
.pnp.js
7+
8+
# testing
9+
/coverage
10+
11+
# next.js
12+
/.next/
13+
/out/
14+
15+
# production
16+
/build
17+
18+
# misc
19+
.DS_Store
20+
*.pem
21+
22+
# debug
23+
npm-debug.log*
24+
yarn-debug.log*
25+
yarn-error.log*
26+
27+
# local env files
28+
.env.local
29+
.env.development.local
30+
.env.test.local
31+
.env.production.local
32+
33+
# vercel
34+
.vercel

.prettierrc.js

+14
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
module.exports = {
2+
printWidth: 100, // Yes I know prettier says it's not a good idea.
3+
trailingComma: 'all',
4+
singleQuote: true,
5+
6+
overrides: [
7+
{
8+
files: '*.md',
9+
options: {
10+
printWidth: 60,
11+
},
12+
},
13+
],
14+
};

README.md

+24
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
# @iter-tools/regex-playground
2+
3+
`regex-playground` provides a playground for `@iter-tools/regex` to help you see how the engine evaluates patterns. It is useful for learning how a regex engine works, and for debugging.
4+
5+
This is a [Next.js](https://nextjs.org/) project bootstrapped with [`create-next-app`](https://github.com/vercel/next.js/tree/canary/packages/create-next-app). It is using the [Grommet](https://v2.grommet.io/) component library and [Typescript](https://www.typescriptlang.org/).
6+
7+
## Getting Started
8+
9+
To develop on this project run the `dev` [script](https://gist.github.com/conartist6/4f784765b76977ab3b95dc2b26ee9a15), which runs a local web server which live-reloads changes.
10+
11+
## Learn More
12+
13+
To learn more about Next.js, take a look at the following resources:
14+
15+
- [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API.
16+
- [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial.
17+
18+
You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js/) - your feedback and contributions are welcome!
19+
20+
## Deploy on Vercel
21+
22+
The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js.
23+
24+
Check out our [Next.js deployment documentation](https://nextjs.org/docs/deployment) for more details.

components/glyph-stream.tsx

+27
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
import { Box } from 'grommet';
2+
import { take, map, drop, isEmpty, execPipe, toArray } from 'iter-tools-es';
3+
import { Glyph } from './glyph';
4+
5+
export const GlyphStream = ({ nLetters = 10, index, width, engine, forkr }: any) => {
6+
const streamLetters = execPipe(
7+
forkr as IterableIterator<string>,
8+
take(nLetters),
9+
map((glyph, i) => <Glyph key={index + i} index={index + i} glyph={glyph} />),
10+
toArray,
11+
);
12+
13+
if (index === 0 && width === 0) {
14+
streamLetters.unshift(<Glyph key="start" index={null} glyph={null} />);
15+
}
16+
if (isEmpty(drop(nLetters, forkr))) {
17+
streamLetters.push(<Glyph key="end" index={null} glyph={null} />);
18+
} else {
19+
streamLetters.push(<Glyph key="more" index={null} glyph={'...'} />);
20+
}
21+
22+
return (
23+
<Box direction="row" gap="medium" style={{ paddingTop: '1.4em' }}>
24+
{streamLetters}
25+
</Box>
26+
);
27+
};

components/glyph.tsx

+13
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import { Box } from 'grommet';
2+
3+
export const Glyph = ({ glyph, index }: any) => (
4+
<Box
5+
pad="small"
6+
background={glyph === null ? 'black' : 'white'}
7+
elevation="medium"
8+
style={{ position: 'relative' }}
9+
>
10+
<Box style={{ position: 'absolute', top: '-24px' }}>{index !== null ? index : ' '}</Box>
11+
<pre style={{ margin: 0 }}>{glyph || ' '}</pre>
12+
</Box>
13+
);

components/inspector.tsx

+20
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
import { Button, Main } from 'grommet';
2+
3+
import { GlyphStream } from '../components/glyph-stream';
4+
import { Matches } from '../components/matches';
5+
6+
export const Inspector = ({ index, width, engine, forkr, done, matches, dispatch }) => {
7+
return (
8+
<Main pad="medium" gap="large">
9+
<Matches matches={matches} />
10+
<Button
11+
primary
12+
disabled={done}
13+
label={done ? 'done' : width ? 'step1' : 'step0'}
14+
alignSelf="start"
15+
onClick={() => dispatch({ type: 'step' })}
16+
/>
17+
<GlyphStream index={index} width={width} engine={engine} forkr={forkr} />
18+
</Main>
19+
);
20+
};

components/matches.tsx

+13
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import { Box, Card, CardBody } from 'grommet';
2+
3+
export const Matches = ({ matches }) => {
4+
const content = !matches.length
5+
? 'There are no matches yet'
6+
: matches.map((match, i) => (
7+
<Card key={i} background="white" pad="small">
8+
<CardBody>Match {JSON.stringify(match)}</CardBody>
9+
</Card>
10+
));
11+
12+
return <Box direction="row">{content}</Box>;
13+
};

next-env.d.ts

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
/// <reference types="next" />
2+
/// <reference types="next/image-types/global" />
3+
4+
// NOTE: This file should not be edited
5+
// see https://nextjs.org/docs/basic-features/typescript for more information.

next.config.js

+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
/** @type {import('next').NextConfig} */
2+
const nextConfig = {
3+
reactStrictMode: true,
4+
}
5+
6+
module.exports = nextConfig

package.json

+31
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
{
2+
"name": "@iter-tools/regex-playground",
3+
"version": "0.1.0",
4+
"private": true,
5+
"scripts": {
6+
"dev": "next dev",
7+
"build": "next build",
8+
"start": "next start",
9+
"lint": "next lint"
10+
},
11+
"dependencies": {
12+
"@iter-tools/regex": "iter-tools/regex#c6328c4768b9532b60e5a017d14ee86bf6fd82d3",
13+
"grommet": "^2.17.2",
14+
"grommet-icons": "^4.5.0",
15+
"iter-tools-es": "^7.3.1",
16+
"next": "latest",
17+
"react": "17.0.2",
18+
"react-dom": "17.0.2",
19+
"react-is": "17.0.2",
20+
"styled-components": "^5.3.0"
21+
},
22+
"devDependencies": {
23+
"@types/node": "17.0.23",
24+
"@types/react": "17.0.43",
25+
"@types/react-dom": "17.0.14",
26+
"@types/styled-components": "5.1.24",
27+
"babel-plugin-styled-components": "^1.12.0",
28+
"prettier": "2.6.2",
29+
"typescript": "4.6.3"
30+
}
31+
}

pages/_app.tsx

+12
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
import type { AppProps } from 'next/app'
2+
import { grommet, Grommet } from 'grommet';
3+
4+
function MyApp({ Component, pageProps }: AppProps) {
5+
return (
6+
<Grommet theme={grommet}>
7+
<Component {...pageProps} />
8+
</Grommet>
9+
);
10+
}
11+
12+
export default MyApp;

pages/_document.tsx

+43
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
import Document, { Html, Head, Main, NextScript, DocumentContext } from 'next/document';
2+
import { ServerStyleSheet } from 'styled-components';
3+
4+
class MyDocument extends Document {
5+
static async getInitialProps(ctx: DocumentContext) {
6+
const sheet = new ServerStyleSheet();
7+
const originalRenderPage = ctx.renderPage;
8+
9+
try {
10+
ctx.renderPage = () =>
11+
originalRenderPage({
12+
enhanceApp: (App) => (props) => sheet.collectStyles(<App {...props} />),
13+
});
14+
15+
const initialProps = await Document.getInitialProps(ctx);
16+
return {
17+
...initialProps,
18+
styles: (
19+
<>
20+
{initialProps.styles}
21+
{sheet.getStyleElement()}
22+
</>
23+
),
24+
};
25+
} finally {
26+
sheet.seal();
27+
}
28+
}
29+
30+
render() {
31+
return (
32+
<Html>
33+
<Head />
34+
<body style={{ margin: 0 }}>
35+
<Main />
36+
<NextScript />
37+
</body>
38+
</Html>
39+
);
40+
}
41+
}
42+
43+
export default MyDocument;

pages/index.tsx

+119
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,119 @@
1+
import Head from 'next/head';
2+
import { parse } from '@iter-tools/regex';
3+
// @ts-ignore-error
4+
import { Engine } from '@iter-tools/regex/internal/engine';
5+
import { Box, Button, CheckBox, Header, Main, Markdown, TextInput } from 'grommet';
6+
7+
import { Grid, BlockQuote } from 'grommet-icons';
8+
import { PureComponent } from 'react';
9+
import { forkerate } from 'iter-tools-es';
10+
import { Inspector } from '../components/inspector';
11+
12+
type InspectorState = {
13+
index: number;
14+
width: number;
15+
engine: any;
16+
forkr: any;
17+
done: boolean;
18+
matches: Array<Array<string | undefined>>;
19+
};
20+
21+
const welcomeText = `
22+
23+
Welcome to the [@iter-tools/regex](https://github.com/iter-tools/regex) playground!
24+
25+
This playground allows you to step through the incremental process by which \`@iter-tools/regex\`
26+
does matching.
27+
28+
To get started, enter a pattern and some text to match against, press "execute" to setup the engine,
29+
then press the step button repeatedly to see how matching progresses.`;
30+
31+
const Welcome = () => {
32+
return (
33+
<Main pad="medium">
34+
<Markdown components={{ p: { props: { fill: true } } }}>{welcomeText}</Markdown>
35+
</Main>
36+
);
37+
};
38+
39+
export default class App extends PureComponent<never, InspectorState> {
40+
state: InspectorState = {
41+
index: null!,
42+
width: null!,
43+
engine: null,
44+
forkr: null,
45+
done: null!,
46+
matches: null!,
47+
};
48+
49+
dispatch = (action) => {
50+
switch (action.type) {
51+
case 'step':
52+
this.step();
53+
break;
54+
}
55+
};
56+
57+
execute = () => {
58+
const { pattern, text, ...flags } = Object.fromEntries(new FormData(document.forms[0]));
59+
this.setState({
60+
index: 0,
61+
width: 0,
62+
forkr: forkerate(text as string),
63+
engine: new Engine(parse(pattern as string, Object.keys(flags).join(''))),
64+
done: false,
65+
matches: [],
66+
});
67+
};
68+
69+
step = () => {
70+
const { index, width, forkr, engine, matches } = this.state;
71+
72+
if (width === 0) {
73+
const { value, done } = engine.step0(index === 0, forkr.done, forkr.index, forkr.value);
74+
75+
if (value !== null) {
76+
}
77+
78+
this.setState({ width: 1, matches: value ? [...matches, ...value] : matches, done });
79+
} else {
80+
engine.step1(forkr.value);
81+
82+
forkr.advance();
83+
this.setState({ width: 0, index: index + 1 });
84+
}
85+
};
86+
87+
render() {
88+
const { engine } = this.state;
89+
const content = engine ? <Inspector {...this.state} dispatch={this.dispatch} /> : <Welcome />;
90+
91+
return (
92+
<Box
93+
flex
94+
margin={{ horizontal: 'auto', vertical: '0' }}
95+
width={{ max: '100%' }}
96+
height={{ min: '100vh' }}
97+
background="light-2"
98+
>
99+
<Head>
100+
<title>Regex playground</title>
101+
<link rel="icon" href="/favicon.ico" />
102+
</Head>
103+
<form id="inputs">
104+
<Header background="white" pad="medium">
105+
<TextInput name="pattern" icon={<Grid />} placeholder="pattern" />
106+
<TextInput name="text" icon={<BlockQuote />} placeholder="text" />
107+
<CheckBox name="g" label="g" />
108+
<CheckBox name="i" label="i" />
109+
<CheckBox name="s" label="s" />
110+
<CheckBox name="m" label="m" />
111+
<CheckBox name="y" label="y" />
112+
<Button primary label="execute" onClick={this.execute} />
113+
</Header>
114+
</form>
115+
{content}
116+
</Box>
117+
);
118+
}
119+
}

0 commit comments

Comments
 (0)