├── docs
├── static
│ ├── .nojekyll
│ └── img
│ │ └── favicon.svg
├── docs
│ ├── fa
│ │ ├── _category_.yml
│ │ └── index.md
│ ├── cfg
│ │ ├── _category_.yml
│ │ └── index.md
│ └── testing
│ │ ├── _category_.yml
│ │ └── index.md
├── babel.config.js
├── scripts
│ ├── tsconfig.json
│ ├── package.json
│ ├── package-lock.json
│ └── index.ts
├── tsconfig.json
├── src
│ ├── components
│ │ ├── HomepageFeatures.module.css
│ │ └── HomepageFeatures.tsx
│ ├── pages
│ │ ├── index.module.css
│ │ └── index.tsx
│ └── theme
│ │ ├── IconDarkMode
│ │ └── index.js
│ │ ├── IconLightMode
│ │ └── index.js
│ │ └── Toggle
│ │ └── styles.module.css
├── .gitignore
├── sidebars.js
├── README.md
└── package.json
├── apps
├── landing
│ ├── .eslintignore
│ ├── public
│ │ ├── favicon.ico
│ │ ├── favicon-16x16.png
│ │ ├── favicon-32x32.png
│ │ └── apple-touch-icon.png
│ ├── src
│ │ └── pages
│ │ │ ├── _app.tsx
│ │ │ ├── _document.tsx
│ │ │ └── index.tsx
│ ├── postcss.config.js
│ ├── styles
│ │ └── main.css
│ ├── next-env.d.ts
│ ├── .gitignore
│ ├── next.config.js
│ ├── LICENSE
│ ├── tailwind.config.js
│ ├── tsconfig.json
│ ├── .eslintrc.js
│ └── package.json
└── playground
│ ├── .eslintignore
│ ├── src
│ ├── utils
│ │ └── index.ts
│ ├── hooks
│ │ └── index.ts
│ ├── contexts
│ │ ├── index.ts
│ │ ├── DrawerContext.ts
│ │ └── CfgContext.ts
│ ├── pages
│ │ ├── cfg
│ │ │ └── generateCfgLanguage.tsx
│ │ ├── _app.tsx
│ │ ├── _document.tsx
│ │ └── index.tsx
│ ├── components
│ │ ├── index.ts
│ │ ├── Flex.tsx
│ │ ├── cfg
│ │ │ └── GrammarString.tsx
│ │ ├── Icons.tsx
│ │ ├── Button.tsx
│ │ ├── Drawer.tsx
│ │ └── Select.tsx
│ └── types.ts
│ ├── public
│ ├── favicon.ico
│ ├── favicon-16x16.png
│ ├── favicon-32x32.png
│ └── apple-touch-icon.png
│ ├── postcss.config.js
│ ├── styles
│ └── main.css
│ ├── next-env.d.ts
│ ├── .gitignore
│ ├── next.config.js
│ ├── tailwind.config.js
│ ├── LICENSE
│ ├── tsconfig.json
│ ├── .eslintrc.js
│ └── package.json
├── utils
├── index.ts
└── equivalency.ts
├── packages
├── fa
│ ├── libs
│ │ ├── utils
│ │ │ ├── index.ts
│ │ │ └── expandCharacterRanges.ts
│ │ ├── index.ts
│ │ ├── FiniteAutomaton
│ │ │ ├── validate.ts
│ │ │ ├── index.ts
│ │ │ └── generateParseTreeForString.ts
│ │ ├── DeterministicFiniteAutomaton
│ │ │ ├── checkEquivalenceBetweenStatesGroups.ts
│ │ │ ├── generateStateGroupsRecord.ts
│ │ │ └── generateEquivalenceStates.ts
│ │ ├── NonDeterministicFiniteAutomaton
│ │ │ ├── epsilonClosureOfState.ts
│ │ │ ├── moveAndEpsilonClosureStateSet.ts
│ │ │ ├── convertToRegularNfa.ts
│ │ │ └── index.ts
│ │ └── types.ts
│ ├── tsconfig.json
│ ├── tests
│ │ ├── FiniteAutomaton
│ │ │ ├── validate.test.ts
│ │ │ ├── generatePostNormalizationErrors.test.ts
│ │ │ └── generatePreNormalizationErrors.test.ts
│ │ ├── DeterministicFiniteAutomaton
│ │ │ ├── generateStateGroupsRecord.test.ts
│ │ │ ├── checkEquivalenceBetweenStatesGroups.test.ts
│ │ │ └── generateEquivalenceStates.test.ts
│ │ ├── NonDeterministicFiniteAutomaton
│ │ │ ├── epsilonClosureOfState.test.ts
│ │ │ ├── convertToRegularNfa.test.ts
│ │ │ ├── moveAndEpsilonClosureStateSet.test.ts
│ │ │ └── convertToDeterministicFiniteAutomaton.test.ts
│ │ └── utils
│ │ │ └── expandCharacterRanges.test.ts
│ ├── jest.config.js
│ ├── package-lock.json
│ ├── readme.md
│ ├── .eslintrc.js
│ └── package.json
├── regex
│ ├── readme.md
│ ├── tsconfig.json
│ ├── libs
│ │ ├── utils
│ │ │ ├── index.ts
│ │ │ ├── addConcatOperatorToRegex.ts
│ │ │ └── generateRegexTree.ts
│ │ ├── index.ts
│ │ └── types.ts
│ ├── jest.config.js
│ ├── tests
│ │ ├── validateRegex.test.ts
│ │ ├── generateRegexTree.test.ts
│ │ ├── convertInfixRegexToPostfix.test.ts
│ │ └── addConcatOperatorToRegex.test.ts
│ ├── .eslintrc.js
│ └── package.json
├── cfg
│ ├── tsconfig.json
│ ├── libs
│ │ ├── utils
│ │ │ ├── index.ts
│ │ │ ├── isAllTerminal.ts
│ │ │ ├── setOperations.ts
│ │ │ ├── populateCfg.ts
│ │ │ ├── generateNewVariable.ts
│ │ │ └── removeProductionRules.ts
│ │ ├── convertGrammarToString.ts
│ │ ├── removeUselessProduction.ts
│ │ ├── simplifyCfg.ts
│ │ ├── removeEmptyProduction.ts
│ │ ├── types.ts
│ │ ├── extractTerminalsFromCfg.ts
│ │ ├── convertStringToGrammar.ts
│ │ ├── parseWithLL1Table.ts
│ │ ├── generateParseTreeFromDerivations.ts
│ │ ├── generateVariableReferenceRecord.ts
│ │ ├── validateCfg.ts
│ │ ├── index.ts
│ │ ├── generateLR0ParsingTable.ts
│ │ ├── removeUnreachableProduction.ts
│ │ └── generateLL1ParsingTable.ts
│ ├── tests
│ │ ├── utils
│ │ │ ├── setOperations.test.ts
│ │ │ ├── isAllTerminal.test.ts
│ │ │ ├── generateNewVariable.test.ts
│ │ │ └── populateCfg.test.ts
│ │ ├── setEquivalency.ts
│ │ ├── convertGrammarToString.test.ts
│ │ ├── removeEmptyProduction.test.ts
│ │ ├── removeUnreachableProduction.test.ts
│ │ ├── generateParseTreeFromDerivations.test.ts
│ │ ├── parseWithLL1Table.test.ts
│ │ ├── convertStringToGrammar.test.ts
│ │ ├── extractTerminalsFromCfg.test.ts
│ │ ├── removeNonDeterminism.test.ts
│ │ ├── generateLL1ParsingTable.test.ts
│ │ ├── removeUnitProduction.test.ts
│ │ ├── removeNonTerminableProduction.test.ts
│ │ ├── removeLeftRecursion.test.ts
│ │ ├── generateLR0ParsingTable.test.ts
│ │ ├── generateVariableReferenceRecord.test.ts
│ │ ├── validateCfg.test.ts
│ │ ├── cykParse.test.ts
│ │ └── removeNullProduction.test.ts
│ ├── jest.config.js
│ ├── package-lock.json
│ ├── readme.md
│ ├── .eslintrc.js
│ └── package.json
└── testing
│ ├── tsconfig.json
│ ├── libs
│ ├── utils
│ │ ├── generateRandomNumber.ts
│ │ ├── index.ts
│ │ ├── countFileLines.ts
│ │ ├── generateCaseMessage.ts
│ │ ├── generateUniversalLanguage.ts
│ │ ├── generateRandomLanguage.ts
│ │ └── createFileWriteStreams.ts
│ └── types.ts
│ ├── tests
│ ├── generateRandomLanguage.test.ts
│ ├── generateAggregateMessage.test.ts
│ └── generateUniversalLanguage.test.ts
│ ├── jest.config.js
│ ├── .eslintrc.js
│ ├── readme.md
│ ├── package.json
│ └── package-lock.json
├── lerna.json
├── public
├── pre_dfa_test.png
├── epsilon_to_nfa.png
├── generated_graph.png
├── post_dfa_test.png
├── nfa_starts_with_ab.png
├── starts_with_bc_dfa.png
├── post_dfa_test_terminal.png
├── sample_case_artifact_file.png
├── sample_accepted_artifact_file.png
├── sample_aggregate_artifact_file.png
├── sample_correct_artifact_file.png
└── divisible_by_3_or_2_but_not_both.jpg
├── .prettierrc.js
├── .npmignore
├── scripts
├── vercel.sh
├── uploadCoverage.sh
└── build.sh
├── tsconfig.json
├── .github
└── workflows
│ ├── build.yaml
│ └── deploy.yaml
├── LICENSE
├── package.json
└── .gitignore
/docs/static/.nojekyll:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/apps/landing/.eslintignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | out
3 |
--------------------------------------------------------------------------------
/utils/index.ts:
--------------------------------------------------------------------------------
1 | export * from './equivalency';
2 |
--------------------------------------------------------------------------------
/apps/playground/.eslintignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | out
3 |
--------------------------------------------------------------------------------
/docs/docs/fa/_category_.yml:
--------------------------------------------------------------------------------
1 | label: "@fauton/fa"
2 | position: 0
3 |
--------------------------------------------------------------------------------
/apps/playground/src/utils/index.ts:
--------------------------------------------------------------------------------
1 | export * from './generateTheme';
2 |
--------------------------------------------------------------------------------
/docs/docs/cfg/_category_.yml:
--------------------------------------------------------------------------------
1 | label: "@fauton/cfg"
2 | position: 0
3 |
--------------------------------------------------------------------------------
/apps/playground/src/hooks/index.ts:
--------------------------------------------------------------------------------
1 | export * from './useGrammarInput';
2 |
--------------------------------------------------------------------------------
/docs/docs/testing/_category_.yml:
--------------------------------------------------------------------------------
1 | label: "@fauton/testing"
2 | position: 0
3 |
--------------------------------------------------------------------------------
/packages/fa/libs/utils/index.ts:
--------------------------------------------------------------------------------
1 | export * from './expandCharacterRanges';
2 |
--------------------------------------------------------------------------------
/lerna.json:
--------------------------------------------------------------------------------
1 | {
2 | "packages": [
3 | "packages/*"
4 | ],
5 | "version": "0.0.0"
6 | }
7 |
--------------------------------------------------------------------------------
/public/pre_dfa_test.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Devorein/fauton/HEAD/public/pre_dfa_test.png
--------------------------------------------------------------------------------
/public/epsilon_to_nfa.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Devorein/fauton/HEAD/public/epsilon_to_nfa.png
--------------------------------------------------------------------------------
/public/generated_graph.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Devorein/fauton/HEAD/public/generated_graph.png
--------------------------------------------------------------------------------
/public/post_dfa_test.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Devorein/fauton/HEAD/public/post_dfa_test.png
--------------------------------------------------------------------------------
/apps/landing/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Devorein/fauton/HEAD/apps/landing/public/favicon.ico
--------------------------------------------------------------------------------
/packages/regex/readme.md:
--------------------------------------------------------------------------------
1 | # `@fauton/regex`
2 |
3 | A package to work with regex validation and parsing
4 |
--------------------------------------------------------------------------------
/public/nfa_starts_with_ab.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Devorein/fauton/HEAD/public/nfa_starts_with_ab.png
--------------------------------------------------------------------------------
/public/starts_with_bc_dfa.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Devorein/fauton/HEAD/public/starts_with_bc_dfa.png
--------------------------------------------------------------------------------
/apps/playground/src/contexts/index.ts:
--------------------------------------------------------------------------------
1 | export * from './CfgContext';
2 | export * from './DrawerContext';
3 |
4 |
--------------------------------------------------------------------------------
/public/post_dfa_test_terminal.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Devorein/fauton/HEAD/public/post_dfa_test_terminal.png
--------------------------------------------------------------------------------
/apps/playground/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Devorein/fauton/HEAD/apps/playground/public/favicon.ico
--------------------------------------------------------------------------------
/docs/babel.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | presets: [require.resolve('@docusaurus/core/lib/babel/preset')],
3 | };
4 |
--------------------------------------------------------------------------------
/public/sample_case_artifact_file.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Devorein/fauton/HEAD/public/sample_case_artifact_file.png
--------------------------------------------------------------------------------
/apps/landing/public/favicon-16x16.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Devorein/fauton/HEAD/apps/landing/public/favicon-16x16.png
--------------------------------------------------------------------------------
/apps/landing/public/favicon-32x32.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Devorein/fauton/HEAD/apps/landing/public/favicon-32x32.png
--------------------------------------------------------------------------------
/apps/landing/public/apple-touch-icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Devorein/fauton/HEAD/apps/landing/public/apple-touch-icon.png
--------------------------------------------------------------------------------
/apps/playground/public/favicon-16x16.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Devorein/fauton/HEAD/apps/playground/public/favicon-16x16.png
--------------------------------------------------------------------------------
/apps/playground/public/favicon-32x32.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Devorein/fauton/HEAD/apps/playground/public/favicon-32x32.png
--------------------------------------------------------------------------------
/public/sample_accepted_artifact_file.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Devorein/fauton/HEAD/public/sample_accepted_artifact_file.png
--------------------------------------------------------------------------------
/public/sample_aggregate_artifact_file.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Devorein/fauton/HEAD/public/sample_aggregate_artifact_file.png
--------------------------------------------------------------------------------
/public/sample_correct_artifact_file.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Devorein/fauton/HEAD/public/sample_correct_artifact_file.png
--------------------------------------------------------------------------------
/.prettierrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | printWidth: 100,
3 | singleQuote: true,
4 | trailingComma: 'es5',
5 | useTabs: true,
6 | };
7 |
--------------------------------------------------------------------------------
/apps/playground/public/apple-touch-icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Devorein/fauton/HEAD/apps/playground/public/apple-touch-icon.png
--------------------------------------------------------------------------------
/public/divisible_by_3_or_2_but_not_both.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Devorein/fauton/HEAD/public/divisible_by_3_or_2_but_not_both.jpg
--------------------------------------------------------------------------------
/apps/playground/src/pages/cfg/generateCfgLanguage.tsx:
--------------------------------------------------------------------------------
1 |
2 | export default function GenerateCfgLanguage() {
3 | return
4 |
5 |
6 | }
--------------------------------------------------------------------------------
/.npmignore:
--------------------------------------------------------------------------------
1 | # Node files
2 | package-lock.json
3 | **/*.tgz
4 |
5 | # source files
6 | **/*.ts
7 | !**/dist
8 | !**/*.d.ts
9 | !**/*.map.js
10 | **/src
11 | /workflows
12 | /.github
--------------------------------------------------------------------------------
/packages/fa/libs/index.ts:
--------------------------------------------------------------------------------
1 | export * from './DeterministicFiniteAutomaton';
2 | export * from './FiniteAutomaton';
3 | export * from './NonDeterministicFiniteAutomaton';
4 | export * from './types';
5 |
--------------------------------------------------------------------------------
/packages/fa/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../tsconfig.json",
3 | "include": [
4 | "./libs/**/*.ts"
5 | ],
6 | "compilerOptions": {
7 | "outDir": "./dist",
8 | "rootDir": "./",
9 | },
10 | }
--------------------------------------------------------------------------------
/apps/landing/src/pages/_app.tsx:
--------------------------------------------------------------------------------
1 | import { AppProps } from 'next/app';
2 | import "../../styles/main.css";
3 |
4 |
5 | const MyApp = ({ Component, pageProps }: AppProps) => ;
6 |
7 | export default MyApp;
8 |
--------------------------------------------------------------------------------
/docs/scripts/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../tsconfig.shared.json",
3 | "include": [
4 | "index.ts"
5 | ],
6 | "compilerOptions": {
7 | "outDir": "./dist",
8 | "module": "ES2020"
9 | },
10 | }
--------------------------------------------------------------------------------
/docs/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | // This file is not used in compilation. It is here just for a nice editor experience.
3 | "extends": "@tsconfig/docusaurus/tsconfig.json",
4 | "compilerOptions": {
5 | "baseUrl": "."
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/packages/cfg/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../tsconfig.json",
3 | "include": [
4 | "./libs/**/*.ts"
5 | ],
6 | "compilerOptions": {
7 | "outDir": "./dist",
8 | "rootDir": "./libs",
9 | },
10 | }
--------------------------------------------------------------------------------
/packages/testing/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../tsconfig.json",
3 | "include": [
4 | "./libs/**/*.ts"
5 | ],
6 | "compilerOptions": {
7 | "outDir": "./dist",
8 | "rootDir": "./libs",
9 | },
10 | }
--------------------------------------------------------------------------------
/packages/cfg/libs/utils/index.ts:
--------------------------------------------------------------------------------
1 | export * from '../extractTerminalsFromCfg';
2 | export * from './generateNewVariable';
3 | export * from './isAllTerminal';
4 | export * from './removeProductionRules';
5 | export * from './setOperations';
6 |
--------------------------------------------------------------------------------
/apps/playground/src/components/index.ts:
--------------------------------------------------------------------------------
1 | import Drawer from "./Drawer";
2 |
3 | export * from './Button';
4 | export * from './Flex';
5 | export * from './Icons';
6 | export * from './Select';
7 | export {
8 | Drawer
9 | };
10 |
11 |
--------------------------------------------------------------------------------
/packages/fa/tests/FiniteAutomaton/validate.test.ts:
--------------------------------------------------------------------------------
1 | import { validate } from '../../libs/FiniteAutomaton/validate';
2 |
3 | it(`Should throw an error`, () => {
4 | expect(() => validate('DFA', ['Error 1'])).toThrow(`Error validating automaton`);
5 | });
6 |
--------------------------------------------------------------------------------
/packages/regex/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../tsconfig.json",
3 | "include": [
4 | "./libs/**/*.ts",
5 | "./examples"
6 | ],
7 | "compilerOptions": {
8 | "outDir": "./dist",
9 | "rootDir": "./",
10 | },
11 | }
--------------------------------------------------------------------------------
/docs/src/components/HomepageFeatures.module.css:
--------------------------------------------------------------------------------
1 | .features {
2 | display: flex;
3 | align-items: center;
4 | padding: 2rem 0;
5 | width: 100%;
6 | padding: 0em 5em;
7 | }
8 |
9 | .featureSvg {
10 | height: 200px;
11 | width: 200px;
12 | }
13 |
--------------------------------------------------------------------------------
/apps/landing/postcss.config.js:
--------------------------------------------------------------------------------
1 | // Please do not use the array form (like ['tailwindcss', 'postcss-preset-env'])
2 | // it will create an unexpected error: Invalid PostCSS Plugin found: [0]
3 |
4 | module.exports = {
5 | plugins: { tailwindcss: {}, autoprefixer: {} },
6 | };
7 |
--------------------------------------------------------------------------------
/apps/playground/postcss.config.js:
--------------------------------------------------------------------------------
1 | // Please do not use the array form (like ['tailwindcss', 'postcss-preset-env'])
2 | // it will create an unexpected error: Invalid PostCSS Plugin found: [0]
3 |
4 | module.exports = {
5 | plugins: { tailwindcss: {}, autoprefixer: {} },
6 | };
7 |
--------------------------------------------------------------------------------
/apps/landing/styles/main.css:
--------------------------------------------------------------------------------
1 | @tailwind base;
2 | @tailwind utilities;
3 | @tailwind variants;
4 |
5 | body {
6 | font-family: 'Inconsolata', monospace;
7 | height: 100%;
8 | }
9 |
10 | html {
11 | height: 100vh;
12 | }
13 |
14 | #__next {
15 | height: 100%;
16 | }
--------------------------------------------------------------------------------
/apps/playground/styles/main.css:
--------------------------------------------------------------------------------
1 | @tailwind base;
2 | @tailwind utilities;
3 | @tailwind variants;
4 |
5 | body {
6 | font-family: 'Roboto', monospace;
7 | height: 100%;
8 | }
9 |
10 | html {
11 | height: 100vh;
12 | }
13 |
14 | #__next {
15 | height: 100%;
16 | }
--------------------------------------------------------------------------------
/apps/landing/next-env.d.ts:
--------------------------------------------------------------------------------
1 | ///
2 | ///
3 | ///
4 |
5 | // NOTE: This file should not be edited
6 | // see https://nextjs.org/docs/basic-features/typescript for more information.
7 |
--------------------------------------------------------------------------------
/packages/cfg/tests/utils/setOperations.test.ts:
--------------------------------------------------------------------------------
1 | import { setCrossProduct } from "../../libs/utils/setOperations"
2 |
3 | describe('setCrossProduct', () => {
4 | it(`Set cross product`, () => {
5 | expect(Array.from(setCrossProduct(new Set(), new Set()))).toStrictEqual([])
6 | })
7 | })
--------------------------------------------------------------------------------
/apps/playground/next-env.d.ts:
--------------------------------------------------------------------------------
1 | ///
2 | ///
3 | ///
4 |
5 | // NOTE: This file should not be edited
6 | // see https://nextjs.org/docs/basic-features/typescript for more information.
7 |
--------------------------------------------------------------------------------
/scripts/vercel.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | echo "VERCEL_ENV: $VERCEL_ENV"
4 |
5 | if [[ "$VERCEL_ENV" == "production" ]] ; then
6 | # Proceed with the build
7 | echo "✅ - Build can proceed"
8 | exit 1;
9 | else
10 | # Don't build for preview
11 | echo "🛑 - Build cancelled"
12 | exit 0;
13 | fi
--------------------------------------------------------------------------------
/packages/regex/libs/utils/index.ts:
--------------------------------------------------------------------------------
1 | export * from './addConcatOperatorToRegex';
2 | export * from './convertInfixRegexToPostfix';
3 | export * from './generateEpsilonNfaFromRegex';
4 | export * from './generateRegexNodeTransitions';
5 | export * from './generateRegexTree';
6 | export * from './validateRegex';
7 |
--------------------------------------------------------------------------------
/apps/playground/src/contexts/DrawerContext.ts:
--------------------------------------------------------------------------------
1 | import React, { Dispatch, SetStateAction } from 'react';
2 |
3 | interface IDrawerContext {
4 | isDrawerOpen: boolean
5 | setIsDrawerOpen: Dispatch>
6 | }
7 |
8 | export const DrawerContext = React.createContext({} as IDrawerContext);
9 |
--------------------------------------------------------------------------------
/apps/playground/src/components/Flex.tsx:
--------------------------------------------------------------------------------
1 | import styled from "@emotion/styled";
2 | import { Box } from "@mui/material";
3 |
4 | export const Flex = styled(Box)`
5 | display: flex;
6 | gap: ${({theme}) => theme.spacing(1)};
7 | overflow: auto;
8 | `
9 |
10 | export const FlexCol = styled(Flex)`
11 | flex-direction: column;
12 | `;
--------------------------------------------------------------------------------
/docs/.gitignore:
--------------------------------------------------------------------------------
1 | # Dependencies
2 | /node_modules
3 |
4 | # Production
5 | /build
6 |
7 | # Generated files
8 | .docusaurus
9 | .cache-loader
10 |
11 | # Misc
12 | .DS_Store
13 | .env.local
14 | .env.development.local
15 | .env.test.local
16 | .env.production.local
17 |
18 | npm-debug.log*
19 | yarn-debug.log*
20 | yarn-error.log*
21 |
22 | /docs/**/api
23 | .vercel
24 |
--------------------------------------------------------------------------------
/packages/testing/libs/utils/generateRandomNumber.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * Generate a random number within a min and max limit
3 | * @param min Minimum limit
4 | * @param max Maximum limit
5 | * @returns generated random number
6 | */
7 | export function generateRandomNumber(min: number, max: number) {
8 | return Math.floor(min + Math.random() * (max + 1 - min));
9 | }
10 |
--------------------------------------------------------------------------------
/packages/testing/libs/utils/index.ts:
--------------------------------------------------------------------------------
1 | export * from './countFileLines';
2 | export * from './createFileWriteStreams';
3 | export * from './generateAggregateMessage';
4 | export * from './generateCaseMessage';
5 | export * from './generateRandomLanguage';
6 | export * from './generateUniversalLanguage';
7 | export * from './testAutomata';
8 | export * from './testAutomaton';
9 |
--------------------------------------------------------------------------------
/packages/testing/tests/generateRandomLanguage.test.ts:
--------------------------------------------------------------------------------
1 | import { generateRandomLanguage } from '../libs/utils/generateRandomLanguage';
2 |
3 | it(`Should generate random unique strings within length`, () => {
4 | const randomStrings = generateRandomLanguage(10, ['a', 'b', 'c'], 5, 5);
5 | expect(randomStrings.length).toBe(10);
6 | expect(new Set(randomStrings).size === 10).toBe(true);
7 | });
8 |
--------------------------------------------------------------------------------
/packages/cfg/tests/utils/isAllTerminal.test.ts:
--------------------------------------------------------------------------------
1 | import { isAllTerminal } from '../../libs/utils/isAllTerminal';
2 |
3 | it(`Should return true if all is terminal`, () => {
4 | expect(isAllTerminal(['The', 'A', 'An'], 'The A An')).toBe(true);
5 | });
6 |
7 | it(`Should return false if one is not terminal`, () => {
8 | expect(isAllTerminal(['The', 'A', 'An'], 'The Var An')).toBe(false);
9 | });
10 |
--------------------------------------------------------------------------------
/utils/equivalency.ts:
--------------------------------------------------------------------------------
1 | export function arrayEquivalency(array1: Array, array2: Array) {
2 | return setEquivalency(new Set(array1), new Set(array2));
3 | }
4 |
5 | export function setEquivalency(set1: Set, set2: Set) {
6 | if (set1.size !== set2.size) {
7 | return false;
8 | } else {
9 | const set1Array = Array.from(set1);
10 | return set1Array.every((array1Element) => set2.has(array1Element));
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/packages/cfg/tests/setEquivalency.ts:
--------------------------------------------------------------------------------
1 | export function arrayEquivalency(array1: Array, array2: Array) {
2 | return setEquivalency(new Set(array1), new Set(array2));
3 | }
4 |
5 | export function setEquivalency(set1: Set, set2: Set) {
6 | if (set1.size !== set2.size) {
7 | return false;
8 | } else {
9 | const set1Array = Array.from(set1);
10 | return set1Array.every((array1Element) => set2.has(array1Element));
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/packages/cfg/libs/utils/isAllTerminal.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * Checks if all the letters of a word are terminal
3 | * @param terminals Array of terminals
4 | * @param word Word that is required to be check
5 | * @returns True if all the letters of a word are terminals false otherwise
6 | */
7 | export function isAllTerminal(terminals: string[], word: string) {
8 | const terminalsSet = new Set(terminals);
9 | return word.split(' ').every((letter) => terminalsSet.has(letter));
10 | }
11 |
--------------------------------------------------------------------------------
/packages/fa/libs/FiniteAutomaton/validate.ts:
--------------------------------------------------------------------------------
1 | export function validate(automatonLabel: string, automatonValidationErrors: string[]) {
2 | if (automatonValidationErrors.length !== 0) {
3 | console.log(`${automatonLabel} ${automatonValidationErrors.length.toString()} Errors`);
4 | automatonValidationErrors.forEach((automatonValidationError) =>
5 | console.log(`${automatonValidationError}\n`)
6 | );
7 | throw new Error(`Error validating automaton`);
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/docs/src/pages/index.module.css:
--------------------------------------------------------------------------------
1 | .heroBanner {
2 | padding: 4rem 0;
3 | text-align: center;
4 | position: relative;
5 | overflow: hidden;
6 | }
7 |
8 | @media screen and (max-width: 966px) {
9 | .heroBanner {
10 | padding: 2rem;
11 | }
12 | }
13 |
14 | .buttons {
15 | display: flex;
16 | align-items: center;
17 | justify-content: center;
18 | }
19 |
20 | .features {
21 | display: flex;
22 | align-items: center;
23 | padding: 2rem 0;
24 | width: 100%;
25 | }
26 |
27 | .featureImage {
28 | height: 200px;
29 | width: 200px;
30 | }
--------------------------------------------------------------------------------
/apps/playground/src/pages/_app.tsx:
--------------------------------------------------------------------------------
1 | import { ThemeProvider } from "@mui/material/styles";
2 | import { AppProps } from 'next/app';
3 | import "../../styles/main.css";
4 | import { generateTheme } from '../utils/generateTheme';
5 |
6 | const generatedTheme = generateTheme();
7 |
8 | const MyApp = ({ Component, pageProps }: AppProps) => {
9 | return
10 |
11 |
12 |
13 |
14 | };
15 |
16 | export default MyApp;
17 |
--------------------------------------------------------------------------------
/packages/cfg/libs/utils/setOperations.ts:
--------------------------------------------------------------------------------
1 | export function setDifference(setA: Set, setB: Set) {
2 | return new Set(Array.from(setA).filter((setAElement) => !setB.has(setAElement)));
3 | }
4 |
5 | export function setCrossProduct(setA: Set, setB: Set) {
6 | const crossProductSet: Set = new Set();
7 | if (setA.size === 0 || setB.size === 0) return crossProductSet;
8 | setA.forEach((setAItem) => {
9 | setB.forEach((setBItem) => crossProductSet.add(`${setAItem} ${setBItem}`));
10 | });
11 | return crossProductSet;
12 | }
13 |
--------------------------------------------------------------------------------
/apps/landing/.gitignore:
--------------------------------------------------------------------------------
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 | Thumbs.db
22 |
23 | # debug
24 | npm-debug.log*
25 | yarn-debug.log*
26 | yarn-error.log*
27 |
28 | # dotenv local files
29 | .env*.local
30 |
31 | # local folder
32 | local
33 |
34 | # vercel
35 | .vercel
36 |
--------------------------------------------------------------------------------
/apps/playground/.gitignore:
--------------------------------------------------------------------------------
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 | Thumbs.db
22 |
23 | # debug
24 | npm-debug.log*
25 | yarn-debug.log*
26 | yarn-error.log*
27 |
28 | # dotenv local files
29 | .env*.local
30 |
31 | # local folder
32 | local
33 |
34 | # vercel
35 | .vercel
36 |
--------------------------------------------------------------------------------
/packages/fa/tests/DeterministicFiniteAutomaton/generateStateGroupsRecord.test.ts:
--------------------------------------------------------------------------------
1 | import { generateStateGroupsRecord } from '../../libs/DeterministicFiniteAutomaton/generateStateGroupsRecord';
2 |
3 | describe('generateStateGroupsRecord', () => {
4 | it(`Generate state groups record`, () => {
5 | const stateGroupRecords = generateStateGroupsRecord(
6 | ['A', 'B,C', 'E', 'D'],
7 | [['A'], ['B,C', 'E'], ['D']]
8 | );
9 | expect(stateGroupRecords).toStrictEqual({
10 | A: 0,
11 | 'B,C': 1,
12 | D: 2,
13 | E: 1,
14 | });
15 | });
16 | });
17 |
--------------------------------------------------------------------------------
/apps/landing/next.config.js:
--------------------------------------------------------------------------------
1 | /* eslint-disable import/no-extraneous-dependencies */
2 | const withBundleAnalyzer = require('@next/bundle-analyzer')({
3 | enabled: process.env.ANALYZE === 'true',
4 | });
5 |
6 | module.exports = withBundleAnalyzer({
7 | poweredByHeader: false,
8 | trailingSlash: true,
9 | basePath: '',
10 | // The starter code load resources from `public` folder with `router.basePath` in React components.
11 | // So, the source code is "basePath-ready".
12 | // You can remove `basePath` if you don't need it.
13 | reactStrictMode: true,
14 | });
15 |
--------------------------------------------------------------------------------
/apps/playground/next.config.js:
--------------------------------------------------------------------------------
1 | /* eslint-disable import/no-extraneous-dependencies */
2 | const withBundleAnalyzer = require('@next/bundle-analyzer')({
3 | enabled: process.env.ANALYZE === 'true',
4 | });
5 |
6 | module.exports = withBundleAnalyzer({
7 | poweredByHeader: false,
8 | trailingSlash: true,
9 | basePath: '',
10 | // The starter code load resources from `public` folder with `router.basePath` in React components.
11 | // So, the source code is "basePath-ready".
12 | // You can remove `basePath` if you don't need it.
13 | reactStrictMode: true,
14 | });
15 |
--------------------------------------------------------------------------------
/apps/playground/src/contexts/CfgContext.ts:
--------------------------------------------------------------------------------
1 | import React, { Dispatch, SetStateAction } from 'react';
2 | import { ContextFreeGrammarWithLabel, UserInputGrammar } from '../types';
3 |
4 | interface ICfgContext {
5 | grammars: ContextFreeGrammarWithLabel[]
6 | currentSelectedGrammar: ContextFreeGrammarWithLabel | null;
7 | setCurrentSelectedGrammar: Dispatch<
8 | SetStateAction
9 | >;
10 | addGrammar: ((userInputGrammar: UserInputGrammar) => void)
11 | }
12 |
13 | export const CfgContext = React.createContext({} as ICfgContext);
14 |
--------------------------------------------------------------------------------
/packages/fa/tests/NonDeterministicFiniteAutomaton/epsilonClosureOfState.test.ts:
--------------------------------------------------------------------------------
1 | import { epsilonClosureOfState } from '../../libs/NonDeterministicFiniteAutomaton/epsilonClosureOfState';
2 |
3 | describe('epsilonClosureOfState', () => {
4 | it(`Epsilon closure of state`, () => {
5 | const epsilonClosuredStates = epsilonClosureOfState(
6 | {
7 | A: ['B'],
8 | B: ['C', 'D'],
9 | },
10 | 'A'
11 | );
12 | expect(epsilonClosuredStates).toStrictEqual(['A', 'B', 'C', 'D']);
13 | });
14 | });
15 |
16 | it(`Should generate epsilon closure of a state`, () => {});
17 |
--------------------------------------------------------------------------------
/packages/testing/jest.config.js:
--------------------------------------------------------------------------------
1 | module.exports = async () => {
2 | return {
3 | rootDir: process.cwd(),
4 | testTimeout: 30000,
5 | testEnvironment: 'node',
6 | verbose: true,
7 | testPathIgnorePatterns: ['/node_modules', '/dist'],
8 | modulePathIgnorePatterns: ['/dist'],
9 | roots: ['/tests'],
10 | testMatch: ['/tests/**/*.test.ts'],
11 | transform: {
12 | '^.+\\.(ts)$': 'ts-jest',
13 | },
14 | collectCoverageFrom: ['libs/**/*.ts'],
15 | collectCoverage: true,
16 | coverageDirectory: './coverage',
17 | };
18 | };
19 |
--------------------------------------------------------------------------------
/packages/cfg/tests/convertGrammarToString.test.ts:
--------------------------------------------------------------------------------
1 | import { convertGrammarToString } from '../libs/convertGrammarToString';
2 |
3 | describe('convertGrammarToString', () => {
4 | it(`Convert grammar to string`, () => {
5 | const stringifiedGrammar = convertGrammarToString({
6 | S: ['Adj Noun Verb', 'Adj Verb'],
7 | Noun: ['Sam', 'Alice', ''],
8 | Adj: ['quickly'],
9 | Verb: ['talked'],
10 | });
11 | expect(stringifiedGrammar).toStrictEqual([
12 | 'S -> Adj Noun Verb | Adj Verb',
13 | 'Noun -> Sam | Alice | ϵ',
14 | 'Adj -> quickly',
15 | 'Verb -> talked',
16 | ]);
17 | });
18 | });
19 |
--------------------------------------------------------------------------------
/docs/src/theme/IconDarkMode/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | export default function IconDarkMode(props) {
3 | return (
4 |
5 |
9 |
10 | );
11 | }
12 |
--------------------------------------------------------------------------------
/apps/playground/src/components/cfg/GrammarString.tsx:
--------------------------------------------------------------------------------
1 | import { convertGrammarToString, IContextFreeGrammar } from "@fauton/cfg";
2 |
3 | interface GrammarStringProps {
4 | productionRules: IContextFreeGrammar["productionRules"]
5 | }
6 |
7 | export function GrammarString(props: GrammarStringProps) {
8 | const { productionRules } = props;
9 | const grammarStringLines = convertGrammarToString(productionRules);
10 | return
11 | {grammarStringLines.map((grammarStringLine, grammarStringLineIndex) => {grammarStringLine} )}
12 |
13 | }
--------------------------------------------------------------------------------
/docs/scripts/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "doc-scripts",
3 | "version": "1.0.0",
4 | "description": "",
5 | "main": "./dist/index.js",
6 | "exports": "./dist/index.js",
7 | "type": "module",
8 | "scripts": {
9 | "start": "node dist/index.js",
10 | "build": "tsc --sourceMap false --declaration false",
11 | "build:watch": "tsc --sourceMap false --declaration false --watch"
12 | },
13 | "keywords": [],
14 | "author": "",
15 | "license": "ISC",
16 | "dependencies": {
17 | "marked": "^4.0.10"
18 | },
19 | "devDependencies": {
20 | "@types/marked": "^4.0.1"
21 | },
22 | "engines": {
23 | "node": "^12.20.0 || ^14.13.1 || >=16.0.0"
24 | }
25 | }
--------------------------------------------------------------------------------
/docs/scripts/package-lock.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "doc-scripts",
3 | "version": "1.0.0",
4 | "lockfileVersion": 1,
5 | "requires": true,
6 | "dependencies": {
7 | "@types/marked": {
8 | "version": "4.0.1",
9 | "resolved": "https://registry.npmjs.org/@types/marked/-/marked-4.0.1.tgz",
10 | "integrity": "sha512-ZigEmCWdNUU7IjZEuQ/iaimYdDHWHfTe3kg8ORfKjyGYd9RWumPoOJRQXB0bO+XLkNwzCthW3wUIQtANaEZ1ag==",
11 | "dev": true
12 | },
13 | "marked": {
14 | "version": "4.0.10",
15 | "resolved": "https://registry.npmjs.org/marked/-/marked-4.0.10.tgz",
16 | "integrity": "sha512-+QvuFj0nGgO970fySghXGmuw+Fd0gD2x3+MqCWLIPf5oxdv1Ka6b2q+z9RP01P/IaKPMEramy+7cNy/Lw8c3hw=="
17 | }
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/packages/cfg/tests/utils/generateNewVariable.test.ts:
--------------------------------------------------------------------------------
1 | import { generateNewVariable } from '../../libs/utils/generateNewVariable';
2 |
3 | it(`Should generate new variable`, () => {
4 | const newVariable = generateNewVariable(['1']);
5 | expect(newVariable.match(/[A-Z][0-9]/)).toBeTruthy();
6 | });
7 |
8 | it(`Should generate different variable if it exists in variables array`, () => {
9 | // Simulating a variable that is already present
10 | jest
11 | .spyOn(Math, 'random')
12 | .mockReturnValueOnce(0)
13 | .mockReturnValueOnce(0)
14 | .mockReturnValueOnce(0.05)
15 | .mockReturnValueOnce(0.1);
16 | const newVariable = generateNewVariable(['A0']);
17 | expect(newVariable).toBe('B1');
18 | jest.restoreAllMocks();
19 | });
20 |
--------------------------------------------------------------------------------
/apps/playground/src/pages/_document.tsx:
--------------------------------------------------------------------------------
1 | import { Head, Html, Main, NextScript } from 'next/document';
2 |
3 | // Need to create a custom _document because i18n support is not compatible with `next export`.
4 | const MyDocument = () => {
5 | return (
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 | );
18 | }
19 |
20 | export default MyDocument;
21 |
--------------------------------------------------------------------------------
/packages/cfg/jest.config.js:
--------------------------------------------------------------------------------
1 | module.exports = async () => {
2 | return {
3 | rootDir: process.cwd(),
4 | testTimeout: 30000,
5 | testEnvironment: 'node',
6 | verbose: true,
7 | testPathIgnorePatterns: ['/node_modules', '/dist'],
8 | modulePathIgnorePatterns: ['/dist'],
9 | roots: ['/tests'],
10 | testMatch: ['/tests/**/*.test.ts'],
11 | transform: {
12 | '^.+\\.(ts)$': 'ts-jest',
13 | },
14 | collectCoverageFrom: ['libs/**/*.ts'],
15 | collectCoverage: true,
16 | coverageDirectory: './coverage',
17 | coverageThreshold: {
18 | global: {
19 | branches: 95,
20 | functions: 95,
21 | lines: 95,
22 | statements: -10,
23 | },
24 | },
25 | };
26 | };
27 |
--------------------------------------------------------------------------------
/apps/landing/src/pages/_document.tsx:
--------------------------------------------------------------------------------
1 | import { Head, Html, Main, NextScript } from 'next/document';
2 |
3 | // Need to create a custom _document because i18n support is not compatible with `next export`.
4 | const MyDocument = () => {
5 | return (
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 | );
18 | }
19 |
20 | export default MyDocument;
21 |
--------------------------------------------------------------------------------
/packages/cfg/libs/convertGrammarToString.ts:
--------------------------------------------------------------------------------
1 | import { IContextFreeGrammar } from './types';
2 |
3 | /**
4 | * Convert a cfg to its string representation
5 | * @param productionRules Production rules record
6 | * @returns A string representation of the cfg production rules
7 | */
8 | export function convertGrammarToString(productionRules: IContextFreeGrammar['productionRules']) {
9 | const grammarStringLines: string[] = [];
10 | const productionRulesEntries = Object.entries(productionRules);
11 | productionRulesEntries.forEach(([variable, rules]) => {
12 | grammarStringLines.push(
13 | `${variable} -> ${rules.map((rule) => (rule.length === 0 ? 'ϵ' : rule)).join(' | ')}`
14 | );
15 | });
16 | return grammarStringLines;
17 | }
18 |
--------------------------------------------------------------------------------
/packages/fa/jest.config.js:
--------------------------------------------------------------------------------
1 | module.exports = async () => {
2 | return {
3 | rootDir: process.cwd(),
4 | testTimeout: 30000,
5 | testEnvironment: 'node',
6 | verbose: true,
7 | testPathIgnorePatterns: ['/node_modules', '/dist'],
8 | modulePathIgnorePatterns: ['/dist'],
9 | roots: ['/tests'],
10 | testMatch: ['/tests/**/*.test.ts'],
11 | transform: {
12 | '^.+\\.(ts)$': 'ts-jest',
13 | },
14 | collectCoverageFrom: ['libs/**/*.ts'],
15 | collectCoverage: true,
16 | coverageDirectory: './coverage',
17 | coverageThreshold: {
18 | global: {
19 | branches: 95,
20 | functions: 95,
21 | lines: 95,
22 | statements: -10,
23 | },
24 | },
25 | };
26 | };
27 |
--------------------------------------------------------------------------------
/packages/regex/jest.config.js:
--------------------------------------------------------------------------------
1 | module.exports = async () => {
2 | return {
3 | rootDir: process.cwd(),
4 | testTimeout: 30000,
5 | testEnvironment: 'node',
6 | verbose: true,
7 | testPathIgnorePatterns: ['/node_modules', '/dist'],
8 | modulePathIgnorePatterns: ['/dist'],
9 | roots: ['/tests'],
10 | testMatch: ['/tests/**/*.test.ts'],
11 | transform: {
12 | '^.+\\.(ts)$': 'ts-jest',
13 | },
14 | collectCoverageFrom: ['libs/**/*.ts'],
15 | collectCoverage: true,
16 | coverageDirectory: './coverage',
17 | coverageThreshold: {
18 | global: {
19 | branches: 95,
20 | functions: 95,
21 | lines: 95,
22 | statements: -10,
23 | },
24 | },
25 | };
26 | };
27 |
--------------------------------------------------------------------------------
/packages/fa/package-lock.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@fauton/fa",
3 | "version": "0.0.1",
4 | "lockfileVersion": 1,
5 | "requires": true,
6 | "dependencies": {
7 | "nanoid": {
8 | "version": "2.1.11",
9 | "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-2.1.11.tgz",
10 | "integrity": "sha512-s/snB+WGm6uwi0WjsZdaVcuf3KJXlfGl2LcxgwkEwJF0D/BWzVWAZW/XY4bFaiR7s0Jk3FPvlnepg1H1b1UwlA=="
11 | },
12 | "shortid": {
13 | "version": "2.2.16",
14 | "resolved": "https://registry.npmjs.org/shortid/-/shortid-2.2.16.tgz",
15 | "integrity": "sha512-Ugt+GIZqvGXCIItnsL+lvFJOiN7RYqlGy7QE41O3YC1xbNSeDGIRO7xg2JJXIAj1cAGnOeC1r7/T9pgrtQbv4g==",
16 | "requires": {
17 | "nanoid": "^2.1.0"
18 | }
19 | }
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/packages/cfg/tests/removeEmptyProduction.test.ts:
--------------------------------------------------------------------------------
1 | import { removeEmptyProduction } from '../libs/removeEmptyProduction';
2 |
3 | describe('removeEmptyProduction', () => {
4 | it(`Remove empty production`, () => {
5 | const productionRules = {
6 | S: ['Verb', '', 'B', 'B Verb', 'A', 'A Verb', 'A B', 'A B Verb'],
7 | A: ['a', 'a A'],
8 | B: ['b', 'b B'],
9 | Verb: [],
10 | };
11 | const nonEmptyProductionVariables = removeEmptyProduction({
12 | productionRules,
13 | variables: ['S', 'A', 'B', 'Verb'],
14 | });
15 | expect(productionRules).toStrictEqual({
16 | S: ['', 'B', 'A', 'A B'],
17 | A: ['a', 'a A'],
18 | B: ['b', 'b B'],
19 | });
20 | expect(nonEmptyProductionVariables).toStrictEqual(['S', 'A', 'B']);
21 | });
22 | });
23 |
--------------------------------------------------------------------------------
/packages/fa/libs/DeterministicFiniteAutomaton/checkEquivalenceBetweenStatesGroups.ts:
--------------------------------------------------------------------------------
1 | export function checkEquivalenceBetweenStatesGroups(statesGroups: [string[][], string[][]]) {
2 | const [statesGroupsOne, statesGroupsTwo] = statesGroups;
3 | if (statesGroupsOne.length !== statesGroupsTwo.length) return false;
4 |
5 | let isEquivalent = true;
6 | const stateGroupsSet: Set = new Set();
7 | statesGroupsOne.forEach((statesGroup) => {
8 | stateGroupsSet.add(statesGroup.join(''));
9 | });
10 | for (let index = 0; index < statesGroupsTwo.length; index += 1) {
11 | const statesGroup = statesGroupsTwo[index];
12 | if (!stateGroupsSet.has(statesGroup.join(''))) {
13 | isEquivalent = false;
14 | break;
15 | }
16 | }
17 |
18 | return isEquivalent;
19 | }
20 |
--------------------------------------------------------------------------------
/packages/regex/tests/validateRegex.test.ts:
--------------------------------------------------------------------------------
1 | import { validateRegex } from '../libs/utils/validateRegex';
2 |
3 | it(`Should validate regex`, () => {
4 | expect(validateRegex('(')).toBe(false);
5 | expect(validateRegex(')')).toBe(false);
6 | expect(validateRegex('()')).toBe(false);
7 | expect(validateRegex('(*')).toBe(false);
8 | expect(validateRegex('*')).toBe(false);
9 | expect(validateRegex('(a**')).toBe(false);
10 | expect(validateRegex('((a|')).toBe(false);
11 | expect(validateRegex('((a|b')).toBe(false);
12 | expect(validateRegex('((a)|b)')).toBe(true);
13 | expect(validateRegex('((a)|(b))')).toBe(true);
14 | expect(validateRegex('((a)|(b)|)')).toBe(false);
15 | expect(validateRegex('a|b')).toBe(true);
16 | expect(validateRegex('(((a)|(b))|(c))')).toBe(true);
17 | });
18 |
--------------------------------------------------------------------------------
/apps/playground/tailwind.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | mode: 'jit',
3 | purge: ['./src/**/*.{js,ts,jsx,tsx}'],
4 | darkMode: true,
5 | theme: {
6 | fontSize: {
7 | xs: '0.75rem',
8 | sm: '0.875rem',
9 | base: '1rem',
10 | lg: '1.125rem',
11 | xl: '1.25rem',
12 | '2xl': '1.5rem',
13 | '3xl': '1.875rem',
14 | '4xl': '2.25rem',
15 | '5xl': '3rem',
16 | '6xl': '4rem',
17 | },
18 | extend: {
19 | colors: {
20 | gray: {
21 | 100: '#f7fafc',
22 | 200: '#edf2f7',
23 | 300: '#e2e8f0',
24 | 400: '#cbd5e0',
25 | 500: '#a0aec0',
26 | 600: '#718096',
27 | 700: '#4a5568',
28 | 800: '#2d3748',
29 | 900: '#1a202c',
30 | },
31 | },
32 | },
33 | },
34 | variants: {},
35 | plugins: [],
36 | };
37 |
--------------------------------------------------------------------------------
/packages/testing/libs/utils/countFileLines.ts:
--------------------------------------------------------------------------------
1 | import fs from 'fs';
2 |
3 | /**
4 | * Count the number of lines of a given file
5 | * @param filePath Path of the file
6 | * @returns Total lines of a file
7 | */
8 | export function countFileLines(filePath: string): Promise {
9 | return new Promise((resolve, reject) => {
10 | let lineCount = 0;
11 | fs.createReadStream(filePath)
12 | // On receiving data
13 | .on('data', (buffer) => {
14 | // Loop through the buffer
15 | for (let i = 0; i < buffer.length; i += 1) {
16 | // And check if the buffer item is 10, which indicates a new line
17 | if (buffer[i] === 10) lineCount += 1;
18 | }
19 | })
20 | .on('end', () => {
21 | resolve(lineCount);
22 | })
23 | .on('error', reject);
24 | });
25 | }
26 |
--------------------------------------------------------------------------------
/scripts/uploadCoverage.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | codecov_file="${GITHUB_WORKSPACE}/scripts/codecov.sh"
4 |
5 | curl -Os https://uploader.codecov.io/latest/linux/codecov > $codecov_file
6 | chmod +x $codecov_file
7 |
8 | packages=( cfg fa testing )
9 |
10 | RED='\033[0;31m'
11 | GREEN='\033[0;32m'
12 | NC='\033[0m'
13 |
14 | for package in "${packages[@]}" ; do
15 | package_name="@fauton/$package"
16 | file="${GITHUB_WORKSPACE}/packages/$package/coverage/lcov.info"
17 | echo -e "${GREEN}Uploading coverage for package $package_name${NC}"
18 |
19 | if ! ($codecov_file -f $file -F $package -t $CODECOV_TOKEN) then
20 | echo -e "${RED}Error uploading coverage for $package_name${NC}"
21 | exit 1
22 | else
23 | echo -e "${GREEN}Successfully uploaded coverage for $package_name${NC}"
24 | fi
25 | done
--------------------------------------------------------------------------------
/apps/playground/src/components/Icons.tsx:
--------------------------------------------------------------------------------
1 | import AddCircleIcon from '@mui/icons-material/AddCircle';
2 | import MuiDeleteIcon from '@mui/icons-material/Delete';
3 | import { SvgIconProps } from "@mui/material";
4 | import { grey } from "@mui/material/colors";
5 |
6 | interface IconProps extends SvgIconProps{
7 | size?: number
8 | disabled?: boolean
9 | }
10 |
11 | export function AddIcon(props: IconProps) {
12 | const { disabled, ...rest } = props;
13 | return
16 | }
17 |
18 | export function DeleteIcon(props: IconProps) {
19 | const { disabled, ...rest } = props;
20 | return
23 |
24 | }
--------------------------------------------------------------------------------
/packages/cfg/libs/removeUselessProduction.ts:
--------------------------------------------------------------------------------
1 | import { removeNonTerminableProduction } from './removeNonTerminableProduction';
2 | import { removeUnreachableProduction } from './removeUnreachableProduction';
3 | import { IContextFreeGrammarInput } from './types';
4 | import { populateCfg } from './utils/populateCfg';
5 |
6 | /**
7 | * Reduces an input cfg by removing non terminable and non reachable variables
8 | * @param cfg Variables, start symbol and production rules of a cfg
9 | * @returns An array of terminable and reachable variables
10 | */
11 | export function removeUselessProduction(inputCfg: IContextFreeGrammarInput) {
12 | const cfg = populateCfg(inputCfg);
13 | const terminableVariables = removeNonTerminableProduction(cfg);
14 | return removeUnreachableProduction({
15 | ...cfg,
16 | variables: terminableVariables,
17 | });
18 | }
19 |
--------------------------------------------------------------------------------
/packages/regex/libs/index.ts:
--------------------------------------------------------------------------------
1 | import { IAutomatonTestLogicFn, IRegularExpression } from './types';
2 | import * as RegularExpressionUtils from './utils';
3 |
4 | export class RegularExpression {
5 | automaton: IRegularExpression;
6 |
7 | testLogic: IAutomatonTestLogicFn;
8 |
9 | constructor(testLogic: IAutomatonTestLogicFn, automaton: IRegularExpression, flags?: string[]) {
10 | this.automaton = {
11 | ...automaton,
12 | regex: new RegExp(automaton.regex, ...(flags ?? [])),
13 | };
14 | this.testLogic = testLogic;
15 | }
16 |
17 | test(inputString: string) {
18 | return Boolean(inputString.match(this.automaton.regex));
19 | }
20 |
21 | convertToPostfix() {
22 | return RegularExpressionUtils.convertInfixRegexToPostfix(String(this.automaton.regex))
23 | }
24 | }
25 |
26 | export { RegularExpressionUtils };
27 |
28 |
--------------------------------------------------------------------------------
/docs/sidebars.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Creating a sidebar enables you to:
3 | - create an ordered group of docs
4 | - render a sidebar for each doc of that group
5 | - provide next/previous navigation
6 |
7 | The sidebars can be generated from the filesystem, or explicitly defined here.
8 |
9 | Create as many sidebars as you want.
10 | */
11 |
12 | // @ts-check
13 |
14 | /** @type {import('@docusaurus/plugin-content-docs').SidebarsConfig} */
15 | const sidebars = {
16 | // By default, Docusaurus generates a sidebar from the docs folder structure
17 | tutorialSidebar: [{type: 'autogenerated', dirName: '.'}],
18 |
19 | // But you can create a sidebar manually
20 | /*
21 | tutorialSidebar: [
22 | {
23 | type: 'category',
24 | label: 'Tutorial',
25 | items: ['hello'],
26 | },
27 | ],
28 | */
29 | };
30 |
31 | module.exports = sidebars;
32 |
--------------------------------------------------------------------------------
/packages/cfg/tests/removeUnreachableProduction.test.ts:
--------------------------------------------------------------------------------
1 | import { removeUnreachableProduction } from '../libs/removeUnreachableProduction';
2 |
3 | describe('removeUnreachableProduction', () => {
4 | it(`Remove unreachable production`, () => {
5 | const cfg = {
6 | productionRules: {
7 | Sub: ['Noun', 'Adj Verb'],
8 | Adj: ['an Adj', 'an'],
9 | Verb: ['be Verb', 'be'],
10 | Conj: ['can Conj', 'can'],
11 | },
12 | startVariable: 'Sub',
13 | variables: ['Sub', 'Adj', 'Verb', 'Conj', 'Noun'],
14 | };
15 | const reachableVariables = removeUnreachableProduction(cfg);
16 | expect(reachableVariables).toStrictEqual(['Sub', 'Noun', 'Adj', 'Verb']);
17 | expect(cfg.productionRules).toStrictEqual({
18 | Sub: ['Noun', 'Adj Verb'],
19 | Adj: ['an Adj', 'an'],
20 | Verb: ['be Verb', 'be'],
21 | });
22 | });
23 | });
24 |
--------------------------------------------------------------------------------
/apps/playground/src/types.ts:
--------------------------------------------------------------------------------
1 | import { IContextFreeGrammar } from '@fauton/cfg';
2 | import { Theme as MaterialUITheme } from '@mui/material';
3 |
4 | export type TGrammarOperations =
5 | | 'cnf'
6 | | 'remove_null'
7 | | 'remove_unit'
8 | | 'remove_empty'
9 | | 'remove_useless'
10 | | 'remove_unreachable'
11 | | 'remove_non_terminable';
12 |
13 | export interface GrammarPipelineStep {
14 | operation: TGrammarOperations;
15 | input: IContextFreeGrammar;
16 | output: IContextFreeGrammar;
17 | }
18 |
19 | export type UserInputGrammar = {
20 | label: string;
21 | rules: Array<{
22 | variable: string;
23 | substitutions: string[][];
24 | }>;
25 | };
26 |
27 | export type ContextFreeGrammarWithLabel = IContextFreeGrammar & {label: string}
28 |
29 | declare module '@emotion/react' {
30 | export interface Theme extends MaterialUITheme {}
31 | }
--------------------------------------------------------------------------------
/packages/cfg/tests/generateParseTreeFromDerivations.test.ts:
--------------------------------------------------------------------------------
1 | import { generateParseTreeFromDerivations } from "../libs/generateParseTreeFromDerivations";
2 |
3 | describe('generateParseTreeFromDerivations', () => {
4 | it(`Parse tree from derivations`, () => {
5 | const parseTree = generateParseTreeFromDerivations(["S", "A"], [
6 | ["S", ["A", "A"]],
7 | ["A", ["a", "A"]],
8 | ["A", ["b"]],
9 | ["A", ["a", "A"]],
10 | ["A", ["b"]]
11 | ])
12 | expect(parseTree).toStrictEqual({
13 | S: [
14 | {
15 | "A": [
16 | "a",
17 | {
18 | "A": ["b"]
19 | }
20 | ]
21 | },
22 | {
23 | "A": [
24 | "a",
25 | {
26 | "A": ["b"]
27 | }
28 | ]
29 | }
30 | ]
31 | })
32 | })
33 | })
--------------------------------------------------------------------------------
/packages/cfg/libs/simplifyCfg.ts:
--------------------------------------------------------------------------------
1 | import { removeEmptyProduction } from './removeEmptyProduction';
2 | import { removeNullProduction } from './removeNullProduction';
3 | import { removeUnitProduction } from './removeUnitProduction';
4 | import { removeUselessProduction } from './removeUselessProduction';
5 | import { IContextFreeGrammarInput } from './types';
6 | import { populateCfg } from './utils/populateCfg';
7 |
8 | /**
9 | *
10 | * @param inputCfg Input cfg to simplify
11 | * @returns
12 | */
13 | export function simplifyCfg(inputCfg: IContextFreeGrammarInput) {
14 | const cfg = populateCfg(inputCfg);
15 | removeNullProduction(cfg);
16 | removeUnitProduction(cfg);
17 | const reducedVariables = removeUselessProduction(cfg);
18 | cfg.variables = reducedVariables;
19 | const nonEmptyVariables = removeEmptyProduction(cfg);
20 | cfg.variables = nonEmptyVariables;
21 | }
22 |
--------------------------------------------------------------------------------
/apps/playground/src/pages/index.tsx:
--------------------------------------------------------------------------------
1 | import { useRouter } from "next/dist/client/router";
2 | import Head from "next/head";
3 | import { Button } from "../components";
4 |
5 | const Index = () => {
6 | const router = useRouter();
7 | return <>
8 |
9 | Fauton Playground
10 |
11 |
12 | {
13 | router.push("/regex")
14 | }}/>
15 | {
16 | router.push("/cfg")
17 | }}/>
18 | {
19 | router.push("/fa")
20 | }}/>
21 | {
22 | router.push("/pda")
23 | }}/>
24 |
25 | >;
26 | };
27 |
28 | export default Index;
--------------------------------------------------------------------------------
/packages/cfg/package-lock.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@fauton/cfg",
3 | "version": "0.0.1",
4 | "lockfileVersion": 1,
5 | "requires": true,
6 | "dependencies": {
7 | "@datastructures-js/binary-search-tree": {
8 | "version": "4.3.1",
9 | "resolved": "https://registry.npmjs.org/@datastructures-js/binary-search-tree/-/binary-search-tree-4.3.1.tgz",
10 | "integrity": "sha512-YmxFM5zBz3E9vCnXUidDgOgUaWNqYBqjJp9EkAc3dInpNcq5qutVfJJxI3P4RDUl9DV6wzADN7shqIA1YQI93g=="
11 | },
12 | "@datastructures-js/linked-list": {
13 | "version": "5.1.1",
14 | "resolved": "https://registry.npmjs.org/@datastructures-js/linked-list/-/linked-list-5.1.1.tgz",
15 | "integrity": "sha512-fj5vOozZX5Sw+Z7dJcicvQxMbWTuOizZ+dW+pMcYWMMgMiLERzJ3Zv6Tdn+VStej9ax6tq7LIQxKGiZyXMvWmg==",
16 | "requires": {
17 | "@datastructures-js/binary-search-tree": "^4.1.0"
18 | }
19 | }
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/docs/scripts/index.ts:
--------------------------------------------------------------------------------
1 | import path from 'node:path';
2 | import { fileURLToPath } from 'node:url';
3 | import { extractExamples, generateExamples } from 'typedoc-example-generator';
4 |
5 | const packages = ['cfg'];
6 |
7 | const __dirname = path.dirname(fileURLToPath(import.meta.url));
8 |
9 | async function main() {
10 | for (let index = 0; index < packages.length; index++) {
11 | const packageName = packages[index];
12 | const modulesMarkdownFilePath = path.resolve(
13 | __dirname,
14 | `../../docs/fauton-${packageName}/modules.md`
15 | );
16 | const testFilesDirectory = path.resolve(__dirname, `../../../packages/${packageName}/tests`);
17 | const functionExamplesRecord = await extractExamples(testFilesDirectory);
18 | await generateExamples(
19 | modulesMarkdownFilePath,
20 | functionExamplesRecord,
21 | `@fauton/${packageName}`
22 | );
23 | }
24 | }
25 |
26 | main();
27 |
--------------------------------------------------------------------------------
/packages/cfg/libs/utils/populateCfg.ts:
--------------------------------------------------------------------------------
1 | import { extractTerminalsFromCfg } from '../extractTerminalsFromCfg';
2 | import { IContextFreeGrammar, IContextFreeGrammarInput } from '../types';
3 |
4 | /**
5 | * Populates the variables and terminals of cfg via extraction
6 | * @param cfg Cfg to populate
7 | * @returns Populated cfg
8 | */
9 | export function populateCfg(cfg: IContextFreeGrammarInput) {
10 | // TODO: Loop through all the production rules and extract variables from them
11 | if (!cfg.variables) {
12 | cfg.variables = Object.keys(cfg.productionRules);
13 | }
14 | if (!cfg.startVariable) {
15 | // eslint-disable-next-line
16 | cfg.startVariable = cfg.variables[0];
17 | }
18 |
19 | if (!cfg.terminals) {
20 | cfg.terminals = extractTerminalsFromCfg({
21 | productionRules: cfg.productionRules,
22 | variables: cfg.variables,
23 | });
24 | }
25 |
26 | return cfg as IContextFreeGrammar;
27 | }
28 |
--------------------------------------------------------------------------------
/docs/README.md:
--------------------------------------------------------------------------------
1 | # Website
2 |
3 | This website is built using [Docusaurus 2](https://docusaurus.io/), a modern static website generator.
4 |
5 | ### Installation
6 |
7 | ```
8 | $ yarn
9 | ```
10 |
11 | ### Local Development
12 |
13 | ```
14 | $ yarn start
15 | ```
16 |
17 | This command starts a local development server and opens up a browser window. Most changes are reflected live without having to restart the server.
18 |
19 | ### Build
20 |
21 | ```
22 | $ yarn build
23 | ```
24 |
25 | This command generates static content into the `build` directory and can be served using any static contents hosting service.
26 |
27 | ### Deployment
28 |
29 | Using SSH:
30 |
31 | ```
32 | $ USE_SSH=true yarn deploy
33 | ```
34 |
35 | Not using SSH:
36 |
37 | ```
38 | $ GIT_USER= yarn deploy
39 | ```
40 |
41 | If you are using GitHub pages for hosting, this command is a convenient way to build the website and push to the `gh-pages` branch.
42 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "ES2018",
4 | "module": "commonjs",
5 | "lib": [
6 | "esnext"
7 | ],
8 | "strict": false,
9 | "skipLibCheck": true,
10 | "sourceMap": true,
11 | "declaration": true,
12 | "moduleResolution": "node",
13 | "noImplicitAny": true,
14 | "strictNullChecks": true,
15 | "strictFunctionTypes": true,
16 | "noImplicitThis": true,
17 | "noUnusedLocals": true,
18 | "noUnusedParameters": true,
19 | "noImplicitReturns": true,
20 | "noFallthroughCasesInSwitch": true,
21 | "allowSyntheticDefaultImports": true,
22 | "esModuleInterop": true,
23 | "emitDecoratorMetadata": true,
24 | "experimentalDecorators": true,
25 | "resolveJsonModule": true,
26 | "incremental": false,
27 | "baseUrl": "./",
28 | "watch": false,
29 | "types": [
30 | "node",
31 | "jest"
32 | ]
33 | }
34 | }
--------------------------------------------------------------------------------
/packages/fa/readme.md:
--------------------------------------------------------------------------------
1 | # `@fauton/fa`
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 | Github |
13 | Docs |
14 | NPM
15 |
16 |
17 | A package to work with finite automata
18 |
--------------------------------------------------------------------------------
/packages/fa/.eslintrc.js:
--------------------------------------------------------------------------------
1 | const path = require('path');
2 |
3 | module.exports = {
4 | env: {
5 | es2021: true,
6 | node: true,
7 | },
8 | extends: ['airbnb-base', 'prettier', 'plugin:import/recommended', 'plugin:import/typescript'],
9 | parser: '@typescript-eslint/parser',
10 | ignorePatterns: ['dist', 'tests', 'examples', 'experiment'],
11 | parserOptions: {
12 | project: path.join(__dirname, './tsconfig.json'),
13 | ecmaVersion: 12,
14 | sourceType: 'module',
15 | },
16 | plugins: ['@typescript-eslint', 'prettier', 'import'],
17 | rules: {
18 | 'import/extensions': [
19 | 'error',
20 | 'ignorePackages',
21 | {
22 | js: 'never',
23 | jsx: 'never',
24 | ts: 'never',
25 | tsx: 'never',
26 | },
27 | ],
28 | 'no-await-in-loop': 'off',
29 | 'import/prefer-default-export': 'off',
30 | 'no-else-return': 'off',
31 | 'one-var': 'off',
32 | 'no-console': 'off',
33 | 'no-bitwise': 'off',
34 | },
35 | };
36 |
--------------------------------------------------------------------------------
/packages/regex/.eslintrc.js:
--------------------------------------------------------------------------------
1 | const path = require('path');
2 |
3 | module.exports = {
4 | env: {
5 | es2021: true,
6 | node: true,
7 | },
8 | extends: ['airbnb-base', 'prettier', 'plugin:import/recommended', 'plugin:import/typescript'],
9 | parser: '@typescript-eslint/parser',
10 | ignorePatterns: ['dist', 'tests', 'examples', 'experiment'],
11 | parserOptions: {
12 | project: path.join(__dirname, './tsconfig.json'),
13 | ecmaVersion: 12,
14 | sourceType: 'module',
15 | },
16 | plugins: ['@typescript-eslint', 'prettier', 'import'],
17 | rules: {
18 | 'import/extensions': [
19 | 'error',
20 | 'ignorePackages',
21 | {
22 | js: 'never',
23 | jsx: 'never',
24 | ts: 'never',
25 | tsx: 'never',
26 | },
27 | ],
28 | 'no-await-in-loop': 'off',
29 | 'import/prefer-default-export': 'off',
30 | 'no-else-return': 'off',
31 | 'one-var': 'off',
32 | 'no-console': 'off',
33 | 'no-bitwise': 'off',
34 | },
35 | };
36 |
--------------------------------------------------------------------------------
/packages/testing/.eslintrc.js:
--------------------------------------------------------------------------------
1 | const path = require('path');
2 |
3 | module.exports = {
4 | env: {
5 | es2021: true,
6 | node: true,
7 | },
8 | extends: ['airbnb-base', 'prettier', 'plugin:import/recommended', 'plugin:import/typescript'],
9 | parser: '@typescript-eslint/parser',
10 | ignorePatterns: ['dist', 'tests', 'examples', 'experiment'],
11 | parserOptions: {
12 | project: path.join(__dirname, './tsconfig.json'),
13 | ecmaVersion: 12,
14 | sourceType: 'module',
15 | },
16 | plugins: ['@typescript-eslint', 'prettier', 'import'],
17 | rules: {
18 | 'import/extensions': [
19 | 'error',
20 | 'ignorePackages',
21 | {
22 | js: 'never',
23 | jsx: 'never',
24 | ts: 'never',
25 | tsx: 'never',
26 | },
27 | ],
28 | 'no-await-in-loop': 'off',
29 | 'import/prefer-default-export': 'off',
30 | 'no-else-return': 'off',
31 | 'one-var': 'off',
32 | 'no-console': 'off',
33 | 'no-bitwise': 'off',
34 | },
35 | };
36 |
--------------------------------------------------------------------------------
/packages/cfg/readme.md:
--------------------------------------------------------------------------------
1 | # `@fauton/cfg`
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 | Github |
13 | Docs |
14 | NPM
15 |
16 |
17 | A package to work with context free grammars
18 |
--------------------------------------------------------------------------------
/packages/regex/tests/generateRegexTree.test.ts:
--------------------------------------------------------------------------------
1 | import { addConcatOperatorToRegex } from '../libs/utils/addConcatOperatorToRegex';
2 | import { convertInfixRegexToPostfix } from '../libs/utils/convertInfixRegexToPostfix';
3 | import { generateRegexTree } from '../libs/utils/generateRegexTree';
4 |
5 | describe('generateRegexTree', () => {
6 | it(`Simple regex`, () => {
7 | const generatedRegexTree = generateRegexTree(
8 | convertInfixRegexToPostfix(addConcatOperatorToRegex('a|b?c*'))
9 | );
10 | expect(generatedRegexTree).toStrictEqual([
11 | {
12 | operands: [
13 | { operands: ['a'], operator: 'Literal' },
14 | {
15 | operands: [
16 | { operands: [{ operands: ['b'], operator: 'Literal' }], operator: 'Optional' },
17 | { operands: [{ operands: ['c'], operator: 'Literal' }], operator: 'Kleene' },
18 | ],
19 | operator: 'Concat',
20 | },
21 | ],
22 | operator: 'Or',
23 | },
24 | '',
25 | ]);
26 | });
27 | });
28 |
--------------------------------------------------------------------------------
/apps/playground/src/components/Button.tsx:
--------------------------------------------------------------------------------
1 | import styled from "@emotion/styled";
2 | import { Typography } from "@mui/material";
3 | import MuiButton from "@mui/material/Button";
4 | import { MouseEventHandler } from "react";
5 |
6 | const StyledMuiButton = styled(MuiButton)`
7 | text-transform: capitalize;
8 | text-align: center;
9 | shadow: ${({theme}) => theme.shadows[1]};
10 | cursor: pointer;
11 | padding: ${({theme}) => theme.spacing(1, 2)};
12 | width: fit-content;
13 | border-radius: ${({theme}) => theme.spacing(0.5)};
14 | `;
15 |
16 | export function Button(props: { disabled?: boolean, className?: string, label: string, onClick?: MouseEventHandler }) {
17 | const { onClick, label, className = "", disabled = false } = props;
18 | return
19 |
20 | {label}
21 |
22 |
23 | }
--------------------------------------------------------------------------------
/apps/landing/src/pages/index.tsx:
--------------------------------------------------------------------------------
1 | import router from "next/router";
2 | import { MouseEventHandler } from "react";
3 |
4 | function Button(props: { className?: string, label: string, onClick?: MouseEventHandler }) {
5 | const { onClick, label, className = "" } = props;
6 | return
7 | {label}
8 |
9 | }
10 |
11 | const Index = () => {
12 | return
13 |
14 | Welcome to fauton
15 |
16 |
17 | router.push(`https://playground.fauton.xyz`)} />
18 |
19 |
20 |
;
21 | };
22 |
23 | export default Index;
--------------------------------------------------------------------------------
/packages/cfg/.eslintrc.js:
--------------------------------------------------------------------------------
1 | const path = require('path');
2 |
3 | module.exports = {
4 | env: {
5 | es2021: true,
6 | node: true,
7 | },
8 | extends: ['airbnb-base', 'prettier', 'plugin:import/recommended', 'plugin:import/typescript'],
9 | parser: '@typescript-eslint/parser',
10 | ignorePatterns: ['dist', 'tests', 'examples', 'experiment'],
11 | parserOptions: {
12 | project: path.join(__dirname, './tsconfig.json'),
13 | ecmaVersion: 12,
14 | sourceType: 'module',
15 | },
16 | plugins: ['@typescript-eslint', 'prettier', 'import'],
17 | rules: {
18 | 'import/extensions': [
19 | 'error',
20 | 'ignorePackages',
21 | {
22 | js: 'never',
23 | jsx: 'never',
24 | ts: 'never',
25 | tsx: 'never',
26 | },
27 | ],
28 | 'no-await-in-loop': 'off',
29 | 'import/prefer-default-export': 'off',
30 | 'no-else-return': 'off',
31 | 'one-var': 'off',
32 | 'no-console': 'off',
33 | 'no-bitwise': 'off',
34 | 'no-param-reassign': 'off',
35 | },
36 | };
37 |
--------------------------------------------------------------------------------
/packages/fa/libs/DeterministicFiniteAutomaton/generateStateGroupsRecord.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * Generates a record that maps each input states, to its index in the statesGroups
3 | * @param states An array of states of the automaton
4 | * @param statesGroups Array of state groups
5 | * @returns A record which maps each state to its state group index
6 | */
7 | export function generateStateGroupsRecord(states: string[], statesGroups: string[][]) {
8 | const statesGroupsRecord: Record = {};
9 | const allStatesSet: Set = new Set(states);
10 | statesGroups.forEach((statesGroup, statesGroupIndex) => {
11 | // State group would contain combinations of states
12 | statesGroup.forEach((state) => {
13 | // Since states could be joined by , or other separators
14 | // We should check whether the character actually is a state
15 | if (allStatesSet.has(state)) {
16 | statesGroupsRecord[state] = statesGroupIndex;
17 | }
18 | });
19 | });
20 | return statesGroupsRecord;
21 | }
22 |
--------------------------------------------------------------------------------
/packages/cfg/tests/parseWithLL1Table.test.ts:
--------------------------------------------------------------------------------
1 | import { parseWithLL1Table } from "../libs/parseWithLL1Table";
2 |
3 | describe('parseWithLL1Table', () => {
4 | it(`Parsable string`, () => {
5 | const {parsed, derivations} = parseWithLL1Table({
6 | productionRules: {
7 | S: ["A A"],
8 | A: ["a A", "b"],
9 | }
10 | }, "abab")
11 | expect(derivations).toStrictEqual([
12 | ["S", ["A", "A"]],
13 | ["A", ["a", "A"]],
14 | ["A", ["b"]],
15 | ["A", ["a", "A"]],
16 | ["A", ["b"]]
17 | ])
18 | expect(parsed).toBe(true)
19 | })
20 |
21 | it(`Unparsable string`, () => {
22 | const {parsed, derivations} = parseWithLL1Table({
23 | productionRules: {
24 | S: ["A A"],
25 | A: ["a A", "b"],
26 | }
27 | }, "abcab")
28 | expect(derivations).toStrictEqual([
29 | ["S", ["A", "A"]],
30 | ["A", ["a", "A"]],
31 | ["A", ["b"]],
32 | ])
33 | expect(parsed).toBe(false)
34 | })
35 | })
--------------------------------------------------------------------------------
/packages/fa/tests/NonDeterministicFiniteAutomaton/convertToRegularNfa.test.ts:
--------------------------------------------------------------------------------
1 | import { convertToRegularNfa } from '../../libs/NonDeterministicFiniteAutomaton/convertToRegularNfa';
2 |
3 | describe('convertToRegularNfa', () => {
4 | it(`Converting e-nfa to nfa`, () => {
5 | const transitions = {
6 | A: {
7 | 0: ['A'],
8 | },
9 | B: {
10 | 1: ['B'],
11 | },
12 | C: {
13 | 0: ['C'],
14 | 1: ['C'],
15 | },
16 | };
17 | convertToRegularNfa({
18 | alphabets: ['0', '1'],
19 | epsilon_transitions: {
20 | A: ['B'],
21 | B: ['C'],
22 | D: ['C'],
23 | },
24 | states: ['A', 'B', 'C', 'D'],
25 | transitions,
26 | });
27 | expect(transitions).toStrictEqual({
28 | A: {
29 | 0: ['A', 'C', 'B'],
30 | 1: ['B', 'C'],
31 | },
32 | B: {
33 | 0: ['C'],
34 | 1: ['B', 'C'],
35 | },
36 | C: {
37 | 0: ['C'],
38 | 1: ['C'],
39 | },
40 | D: {
41 | 0: ['C'],
42 | 1: ['C'],
43 | },
44 | });
45 | });
46 | });
47 |
--------------------------------------------------------------------------------
/packages/cfg/tests/convertStringToGrammar.test.ts:
--------------------------------------------------------------------------------
1 | import { convertStringToGrammar } from '../libs/convertStringToGrammar';
2 |
3 | describe('convertStringToGrammar', () => {
4 | it(`Convert string to grammar`, () => {
5 | const generatedGrammar = convertStringToGrammar(
6 | `S -> Noun Article Verb | Noun Adj Verb\nNoun -> Sam | Bob | Alice\nArticle -> A | The | An\nAdj -> quickly | swiftly\nVerb -> ran | ate\nVerb -> walked`
7 | );
8 | expect(generatedGrammar).toStrictEqual({
9 | productionRules: {
10 | S: ['Noun Article Verb', 'Noun Adj Verb'],
11 | Noun: ['Sam', 'Bob', 'Alice'],
12 | Article: ['A', 'The', 'An'],
13 | Adj: ['quickly', 'swiftly'],
14 | Verb: ['ran', 'ate', 'walked'],
15 | },
16 | variables: ['S', 'Noun', 'Article', 'Adj', 'Verb'],
17 | terminals: [
18 | 'Sam',
19 | 'Bob',
20 | 'Alice',
21 | 'A',
22 | 'The',
23 | 'An',
24 | 'quickly',
25 | 'swiftly',
26 | 'ran',
27 | 'ate',
28 | 'walked',
29 | ],
30 | startVariable: 'S',
31 | });
32 | });
33 | });
34 |
--------------------------------------------------------------------------------
/packages/fa/libs/NonDeterministicFiniteAutomaton/epsilonClosureOfState.ts:
--------------------------------------------------------------------------------
1 | import { TransformedFiniteAutomaton } from '../types';
2 |
3 | /**
4 | * Returns an array of states that can be reached from a given state for epsilon symbol
5 | * @param epsilonTransitions Epsilon transitions record of automaton
6 | * @param state Source state
7 | * @returns Array of states
8 | */
9 | export function epsilonClosureOfState(
10 | epsilonTransitions: TransformedFiniteAutomaton['epsilon_transitions'],
11 | state: string
12 | ) {
13 | const statesStack: string[] = [];
14 | const allEpsilonStates: Set = new Set([state]);
15 | statesStack.push(state);
16 | while (statesStack.length !== 0) {
17 | const currentState = statesStack.pop()!;
18 | epsilonTransitions![currentState]?.forEach((epsilonTransitionState) => {
19 | if (!allEpsilonStates.has(epsilonTransitionState)) {
20 | statesStack.push(epsilonTransitionState);
21 | allEpsilonStates.add(epsilonTransitionState);
22 | }
23 | });
24 | }
25 |
26 | return Array.from(allEpsilonStates);
27 | }
28 |
--------------------------------------------------------------------------------
/packages/cfg/libs/removeEmptyProduction.ts:
--------------------------------------------------------------------------------
1 | import { IContextFreeGrammarInput } from './types';
2 | import { populateCfg } from './utils/populateCfg';
3 | import { removeProductionRules } from './utils/removeProductionRules';
4 |
5 | /**
6 | * Removes productions that has no rules and updates rules to remove those rules that references empty production variables
7 | * @param cfg Variables array and production rules record of cfg
8 | * @returns New production rules and variables without empty rule variables
9 | */
10 | export function removeEmptyProduction(
11 | inputCfg: Pick
12 | ) {
13 | const cfg = populateCfg(inputCfg);
14 | const { productionRules, variables } = cfg;
15 | // Filtering all the variables which dont have any production rules
16 | const emptyProductionVariables = variables.filter(
17 | (variable) => productionRules[variable].length === 0
18 | );
19 | return removeProductionRules({
20 | productionRules,
21 | removedVariables: emptyProductionVariables,
22 | variables,
23 | });
24 | }
25 |
--------------------------------------------------------------------------------
/packages/testing/readme.md:
--------------------------------------------------------------------------------
1 | # `@fauton/testing`
2 |
3 |
4 |
@fauton/testing
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 | Github |
14 | Docs |
15 | NPM
16 |
17 |
18 | A package to test your automaton (regex/dfa/nfa/e-nfa/cfg)
19 |
--------------------------------------------------------------------------------
/packages/fa/tests/NonDeterministicFiniteAutomaton/moveAndEpsilonClosureStateSet.test.ts:
--------------------------------------------------------------------------------
1 | import { moveAndEpsilonClosureStateSet } from '../../libs/NonDeterministicFiniteAutomaton/moveAndEpsilonClosureStateSet';
2 |
3 | describe('moveAndEpsilonClosureStateSet', () => {
4 | it(`Without epsilon transitions record`, () => {
5 | const epsilonClosuredStates = moveAndEpsilonClosureStateSet(
6 | {
7 | A: {
8 | 0: ['B', 'C'],
9 | },
10 | B: {
11 | 0: ['C'],
12 | },
13 | },
14 | null,
15 | ['A', 'B'],
16 | '0'
17 | );
18 | expect(epsilonClosuredStates).toStrictEqual(['B', 'C']);
19 | });
20 |
21 | it(`With epsilon transitions record`, () => {
22 | const epsilonClosuredStates = moveAndEpsilonClosureStateSet(
23 | {
24 | A: {
25 | 0: ['B', 'C'],
26 | },
27 | B: {
28 | 0: ['C'],
29 | },
30 | },
31 | {
32 | B: ['A', 'E'],
33 | C: ['D'],
34 | },
35 | ['A', 'B'],
36 | '0'
37 | );
38 | expect(epsilonClosuredStates).toStrictEqual(['B', 'C', 'A', 'E', 'D']);
39 | });
40 | });
41 |
--------------------------------------------------------------------------------
/packages/testing/tests/generateAggregateMessage.test.ts:
--------------------------------------------------------------------------------
1 | import { generateAggregateMessage } from '../libs/utils/generateAggregateMessage';
2 |
3 | // To cover case where description is present and label is not present
4 | generateAggregateMessage(undefined, 'dfa description', {
5 | falseNegatives: 5,
6 | falsePositives: 10,
7 | trueNegatives: 50,
8 | truePositives: 25,
9 | });
10 |
11 | describe('generateAggregateMessage', () => {
12 | it(`Generate correct aggregate message values`, () => {
13 | const { values } = generateAggregateMessage('dfa', undefined, {
14 | falseNegatives: 5,
15 | falsePositives: 10,
16 | trueNegatives: 50,
17 | truePositives: 25,
18 | });
19 | expect(values).toStrictEqual({
20 | totalCorrect: 75,
21 | totalIncorrect: 15,
22 | totalStrings: 90,
23 | correctPercentage: 83.33333,
24 | incorrectPercentage: 16.66667,
25 | falsePositivesPercentage: 11.11111,
26 | falseNegativesPercentage: 5.55556,
27 | truePositivesPercentage: 27.77778,
28 | trueNegativesPercentage: 55.55556,
29 | });
30 | });
31 | });
32 |
--------------------------------------------------------------------------------
/.github/workflows/build.yaml:
--------------------------------------------------------------------------------
1 | name: Lint, Build and Test
2 |
3 | on:
4 | push:
5 | branches: ["dev"]
6 | pull_request:
7 | branches: ["dev"]
8 |
9 | jobs:
10 | build:
11 | runs-on: ubuntu-latest
12 |
13 | strategy:
14 | matrix:
15 | node-version: [14.x]
16 | steps:
17 | - uses: actions/checkout@v2
18 | - name: Use Node.js ${{ matrix.node-version }}
19 | uses: actions/setup-node@v1
20 | with:
21 | node-version: ${{ matrix.node-version }}
22 | - name: Lint, Build and Test packages
23 | run: |
24 | # Install shared dependencies
25 | npm install
26 | npm run bootstrap
27 | npm run lint
28 | npm run build
29 | npm run test
30 | - name: Upload test coverage
31 | run: |
32 | upload_coverage_script="${GITHUB_WORKSPACE}/scripts/uploadCoverage.sh"
33 | chmod +x $upload_coverage_script
34 | bash $upload_coverage_script
35 | env:
36 | CODECOV_TOKEN: ${{secrets.CODECOV_TOKEN}}
37 |
--------------------------------------------------------------------------------
/packages/cfg/tests/extractTerminalsFromCfg.test.ts:
--------------------------------------------------------------------------------
1 | import { extractTerminalsFromCfg } from '../libs/extractTerminalsFromCfg';
2 |
3 | describe('extractTerminalsFromCfg', () => {
4 | it(`Extract terminals from cfg when variables is present`, () => {
5 | const extractedTerminals = extractTerminalsFromCfg({
6 | productionRules: {
7 | S: ['Noun Verb Adj'],
8 | Verb: ['walk', 'talk'],
9 | Noun: ['Sam', 'Alice'],
10 | Adj: ['quickly'],
11 | },
12 | variables: ['S', 'Verb', 'Noun', 'Adj'],
13 | });
14 | expect(extractedTerminals).toStrictEqual(['walk', 'talk', 'Sam', 'Alice', 'quickly']);
15 | });
16 |
17 | it(`Extract terminals from cfg when variables is not present`, () => {
18 | const extractedTerminals = extractTerminalsFromCfg({
19 | productionRules: {
20 | S: ['Noun Verb Adj'],
21 | Verb: ['walk', 'talk'],
22 | Noun: ['Sam', 'Alice'],
23 | Adj: ['quickly'],
24 | },
25 | });
26 |
27 | expect(
28 | extractedTerminals
29 | ).toStrictEqual(['walk', 'talk', 'Sam', 'Alice', 'quickly']);
30 | });
31 | });
32 |
--------------------------------------------------------------------------------
/packages/cfg/tests/utils/populateCfg.test.ts:
--------------------------------------------------------------------------------
1 | import { populateCfg } from '../../libs/utils/populateCfg';
2 |
3 | it(`Should populate cfg, when variables, startVariable and terminals is not present`, () => {
4 | expect(
5 | populateCfg({
6 | productionRules: {
7 | A: ['An'],
8 | B: ['A', 'B', 'The'],
9 | },
10 | })
11 | ).toStrictEqual({
12 | productionRules: {
13 | A: ['An'],
14 | B: ['A', 'B', 'The'],
15 | },
16 | variables: ['A', 'B'],
17 | terminals: ['An', 'The'],
18 | startVariable: 'A',
19 | });
20 | });
21 |
22 | it(`Should not do anything when variables, startVariable and terminals is present`, () => {
23 | expect(
24 | populateCfg({
25 | productionRules: {
26 | A: ['An'],
27 | B: ['A', 'B', 'The'],
28 | },
29 | variables: ['A', 'B'],
30 | terminals: ['An', 'The'],
31 | startVariable: 'A',
32 | })
33 | ).toStrictEqual({
34 | productionRules: {
35 | A: ['An'],
36 | B: ['A', 'B', 'The'],
37 | },
38 | variables: ['A', 'B'],
39 | terminals: ['An', 'The'],
40 | startVariable: 'A',
41 | });
42 | });
43 |
--------------------------------------------------------------------------------
/packages/cfg/libs/types.ts:
--------------------------------------------------------------------------------
1 | export interface IContextFreeGrammar {
2 | variables: string[];
3 | terminals: string[];
4 | productionRules: Record;
5 | startVariable: string;
6 | }
7 |
8 | // Cfg input might not have all the properties, and they could be null as well
9 | export interface IContextFreeGrammarInput {
10 | variables?: string[] | null;
11 | terminals?: string[] | null;
12 | productionRules: Record;
13 | startVariable?: string;
14 | }
15 |
16 | // eslint-disable-next-line
17 | export type LanguageChecker = (inputString: string) => boolean;
18 |
19 | export interface ICfgLanguageGenerationOption {
20 | minTokenLength: number;
21 | maxTokenLength: number;
22 | skipSimplification?: boolean;
23 | skipValidation?: boolean;
24 | generateTerminals?: boolean;
25 | generateVariables?: boolean;
26 | autoCapitalizeFirstToken?: boolean;
27 | useSpaceWhenJoiningTokens?: boolean;
28 | parseDirection?: 'left' | 'right';
29 | }
30 |
31 | export type ParseTree = {
32 | [k: string]: (string | ParseTree)[]
33 | }
34 |
35 | export type Derivation = [string, string[]]
--------------------------------------------------------------------------------
/packages/regex/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@fauton/regex",
3 | "version": "0.0.1",
4 | "description": "A package to work with regex validation and parsing",
5 | "main": "dist/libs/index.js",
6 | "typings": "dist/libs/index.d.ts",
7 | "files": [
8 | "dist/libs"
9 | ],
10 | "scripts": {
11 | "prebuild": "del-cli ./dist",
12 | "prepublishOnly": "npm run lint && npm run build && npm run test",
13 | "build": "tsc --sourceMap false",
14 | "build:watch": "tsc -w",
15 | "lint": "npx eslint \"./libs\" --ext tsx,ts",
16 | "lint:fix": "npx eslint \"./libs\" --ext tsx,ts --fix",
17 | "format": "npx prettier ./libs/**/*.{ts,js} --write",
18 | "test": "npx jest --runInBand"
19 | },
20 | "keywords": [
21 | "regex",
22 | "regex-parsing",
23 | "regex-validation"
24 | ],
25 | "author": "Safwan Shaheer ",
26 | "license": "MIT",
27 | "repository": {
28 | "type": "git",
29 | "url": "git+https://github.com/Devorein/fauton.git"
30 | },
31 | "publishConfig": {
32 | "access": "public"
33 | },
34 | "dependencies": {},
35 | "devDependencies": {}
36 | }
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2020 Fauton
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/docs/docs/cfg/index.md:
--------------------------------------------------------------------------------
1 | ---
2 | id: 'cfg'
3 | title: '@fauton/cfg'
4 | slug: '/cfg/'
5 | sidebar_position: 0
6 | custom_edit_url: null
7 | ---
8 |
9 |
10 |
@fauton/cfg
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 | Github |
20 | Docs |
21 | NPM
22 |
23 |
24 | A package to work with context free grammars
25 |
--------------------------------------------------------------------------------
/docs/docs/fa/index.md:
--------------------------------------------------------------------------------
1 | ---
2 | id: 'fa'
3 | title: '@fauton/fa'
4 | slug: '/fa/'
5 | sidebar_position: 0
6 | custom_edit_url: null
7 | ---
8 |
9 | # `@fauton/fa`
10 |
11 |
12 |
@fauton/fa
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 | Github |
22 | Docs |
23 | NPM
24 |
25 |
26 | A package to work with finite automata
27 |
--------------------------------------------------------------------------------
/packages/testing/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@fauton/testing",
3 | "version": "0.0.1",
4 | "description": "A package to test your automaton (regex/dfa/nfa/e-nfa/cfg)",
5 | "main": "dist/index.js",
6 | "typings": "dist/index.d.ts",
7 | "files": [
8 | "dist"
9 | ],
10 | "scripts": {
11 | "prebuild": "del-cli ./dist",
12 | "prepublishOnly": "npm run lint && npm run build && npm run test",
13 | "build": "tsc --sourceMap false",
14 | "build:watch": "tsc -w",
15 | "lint": "npx eslint \"./libs\" --ext tsx,ts",
16 | "lint:fix": "npx eslint \"./libs\" --ext tsx,ts --fix",
17 | "format": "npx prettier ./libs/**/*.{ts,js} --write",
18 | "test": "npx jest --runInBand"
19 | },
20 | "keywords": [
21 | "automaton-testing"
22 | ],
23 | "author": "Safwan Shaheer ",
24 | "license": "MIT",
25 | "repository": {
26 | "type": "git",
27 | "url": "git+https://github.com/Devorein/fauton.git"
28 | },
29 | "publishConfig": {
30 | "access": "public"
31 | },
32 | "dependencies": {
33 | "cli-progress": "^3.9.1",
34 | "colors": "^1.4.0"
35 | },
36 | "devDependencies": {}
37 | }
--------------------------------------------------------------------------------
/packages/regex/tests/convertInfixRegexToPostfix.test.ts:
--------------------------------------------------------------------------------
1 | import { convertInfixRegexToPostfix } from "../libs/utils/convertInfixRegexToPostfix";
2 |
3 | describe('convertInfixRegexToPostfix', () => {
4 | it(`Work for single operator regex without parenthesis`, () => {
5 | const postfixRegexString = convertInfixRegexToPostfix("a|b");
6 | expect(postfixRegexString).toBe("ab|")
7 | })
8 |
9 | it(`Work for multi operator regex without parenthesis`, () => {
10 | const postfixRegexString1 = convertInfixRegexToPostfix("a?.b");
11 | const postfixRegexString2 = convertInfixRegexToPostfix("a?+b");
12 | expect(postfixRegexString1).toBe("a?b.")
13 | expect(postfixRegexString2).toBe("ab+?")
14 | })
15 |
16 | it(`Work for multi operator regex with parenthesis`, () => {
17 | const postfixRegexString1 = convertInfixRegexToPostfix("(a?.b)");
18 | expect(postfixRegexString1).toBe("a?b.")
19 | })
20 |
21 | it(`Work for multi operator regex with mismatched parenthesis`, () => {
22 | const postfixRegexString1 = convertInfixRegexToPostfix("(a?.b");
23 | expect(postfixRegexString1).toBe("a?b.")
24 | })
25 | })
--------------------------------------------------------------------------------
/packages/cfg/libs/extractTerminalsFromCfg.ts:
--------------------------------------------------------------------------------
1 | import { IContextFreeGrammarInput } from './types';
2 |
3 | /**
4 | * Extract terminals from cfg rules
5 | * @param inputCfg input Cfg
6 | * @returns An array of terminals
7 | */
8 | export function extractTerminalsFromCfg(
9 | inputCfg: Omit
10 | ) {
11 | if (!inputCfg.variables) {
12 | inputCfg.variables = Object.keys(inputCfg.productionRules);
13 | }
14 | const terminals: string[] = [];
15 | // Creating a set of variables initially to improve search performance
16 | const variablesSet = new Set(inputCfg.variables);
17 |
18 | Object.values(inputCfg.productionRules).forEach((rules) => {
19 | rules.forEach((rule) => {
20 | const tokens = rule.split(' ');
21 | // Loop through each of the tokens to see which of them are terminals
22 | tokens.forEach((token) => {
23 | // If the token is not a variable and not empty string, its a terminal
24 | if (!variablesSet.has(token) && token) {
25 | terminals.push(token);
26 | }
27 | });
28 | });
29 | });
30 | return Array.from(new Set(terminals));
31 | }
32 |
--------------------------------------------------------------------------------
/apps/landing/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2020 Remi W.
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/apps/playground/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2020 Remi W.
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/docs/docs/testing/index.md:
--------------------------------------------------------------------------------
1 | ---
2 | id: 'testing'
3 | title: '@fauton/testing'
4 | slug: '/testing/'
5 | sidebar_position: 0
6 | custom_edit_url: null
7 | ---
8 |
9 | # `@fauton/testing`
10 |
11 |
12 |
@fauton/testing
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 | Github |
22 | Docs |
23 | NPM
24 |
25 |
26 | A package to test your automaton (regex/dfa/nfa/e-nfa/cfg)
27 |
--------------------------------------------------------------------------------
/packages/cfg/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@fauton/cfg",
3 | "version": "0.0.10",
4 | "description": "A package to work with context free grammars and LL1 parsers",
5 | "main": "dist/index.js",
6 | "typings": "dist/index.d.ts",
7 | "files": [
8 | "dist/*"
9 | ],
10 | "scripts": {
11 | "prebuild": "del-cli ./dist",
12 | "prepublishOnly": "npm run lint && npm run build && npm run test",
13 | "build": "tsc --sourceMap false",
14 | "build:watch": "tsc -w",
15 | "lint": "eslint \"./libs/**/*.{tsx,ts}\"",
16 | "lint:fix": "eslint \"./libs/**/*.{tsx,ts}\"",
17 | "format": "prettier ./libs/**/*.{ts,js} --write",
18 | "test": "jest --runInBand"
19 | },
20 | "keywords": [
21 | "context-free-grammar",
22 | "context-free-language",
23 | "cyk-parse",
24 | "cnf",
25 | "ll1-parse-table"
26 | ],
27 | "author": "Safwan Shaheer ",
28 | "license": "MIT",
29 | "repository": {
30 | "type": "git",
31 | "url": "git+https://github.com/Devorein/fauton.git"
32 | },
33 | "publishConfig": {
34 | "access": "public"
35 | },
36 | "dependencies": {
37 | "@datastructures-js/linked-list": "^5.1.1"
38 | },
39 | "devDependencies": {}
40 | }
--------------------------------------------------------------------------------
/apps/landing/tailwind.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | mode: 'jit',
3 | purge: ['./src/**/*.{js,ts,jsx,tsx}'],
4 | darkMode: false,
5 | theme: {
6 | fontSize: {
7 | xs: '0.75rem',
8 | sm: '0.875rem',
9 | base: '1rem',
10 | lg: '1.125rem',
11 | xl: '1.25rem',
12 | '2xl': '1.5rem',
13 | '3xl': '1.875rem',
14 | '4xl': '2.25rem',
15 | '5xl': '3rem',
16 | '6xl': '4rem',
17 | },
18 | extend: {
19 | colors: {
20 | gray: {
21 | 100: '#f7fafc',
22 | 200: '#edf2f7',
23 | 300: '#e2e8f0',
24 | 400: '#cbd5e0',
25 | 500: '#a0aec0',
26 | 600: '#718096',
27 | 700: '#4a5568',
28 | 800: '#2d3748',
29 | 900: '#1a202c',
30 | },
31 | blue: {
32 | 100: '#ebf8ff',
33 | 200: '#bee3f8',
34 | 300: '#90cdf4',
35 | 400: '#63b3ed',
36 | 500: '#4299e1',
37 | 600: '#3182ce',
38 | 700: '#2b6cb0',
39 | 800: '#2c5282',
40 | 900: '#2a4365',
41 | },
42 | },
43 | },
44 | },
45 | variants: {},
46 | plugins: [],
47 | };
48 |
--------------------------------------------------------------------------------
/packages/fa/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@fauton/fa",
3 | "version": "0.0.2",
4 | "description": "A package to work with finite automata",
5 | "main": "dist/libs/index.js",
6 | "typings": "dist/libs/index.d.ts",
7 | "files": [
8 | "dist/libs"
9 | ],
10 | "scripts": {
11 | "prebuild": "del-cli ./dist",
12 | "prepublishOnly": "npm run lint && npm run build && npm run test",
13 | "build": "tsc --sourceMap false",
14 | "build:watch": "tsc -w",
15 | "lint": "npx eslint \"./libs\" --ext tsx,ts",
16 | "lint:fix": "npx eslint \"./libs\" --ext tsx,ts --fix",
17 | "format": "npx prettier ./libs/**/*.{ts,js} --write",
18 | "test": "npx jest --runInBand"
19 | },
20 | "keywords": [
21 | "finite-automaton",
22 | "deterministic-finite-automaton",
23 | "non-deterministic-finite-automaton",
24 | "epsilon-finite-automaton"
25 | ],
26 | "author": "Safwan Shaheer ",
27 | "license": "MIT",
28 | "repository": {
29 | "type": "git",
30 | "url": "git+https://github.com/Devorein/fauton.git"
31 | },
32 | "publishConfig": {
33 | "access": "public"
34 | },
35 | "dependencies": {
36 | "shortid": "^2.2.16"
37 | },
38 | "devDependencies": {}
39 | }
--------------------------------------------------------------------------------
/packages/fa/tests/DeterministicFiniteAutomaton/checkEquivalenceBetweenStatesGroups.test.ts:
--------------------------------------------------------------------------------
1 | import { checkEquivalenceBetweenStatesGroups } from '../../libs/DeterministicFiniteAutomaton/checkEquivalenceBetweenStatesGroups';
2 |
3 | describe('checkEquivalenceBetweenStatesGroups', () => {
4 | it(`Detect equivalency between state groups`, () => {
5 | const stateGroupEquivalency = checkEquivalenceBetweenStatesGroups([
6 | [['3', '5'], ['4'], ['1', '2']],
7 | [['1', '2'], ['4'], ['3', '5']],
8 | ]);
9 | expect(stateGroupEquivalency).toStrictEqual(true);
10 | });
11 |
12 | it(`Detect non-equivalency with same length`, () => {
13 | // For different length of state groups
14 | const stateGroupEquivalency = checkEquivalenceBetweenStatesGroups([
15 | [['3', '5'], ['4'], ['1', '2']],
16 | [['1', '2'], ['3', '5'], ['1']],
17 | ]);
18 | expect(stateGroupEquivalency).toStrictEqual(false);
19 | });
20 |
21 | it(`Detect non-equivalency with different length`, () => {
22 | const stateGroupEquivalency = checkEquivalenceBetweenStatesGroups([
23 | [['3', '5'], ['4'], ['1', '2']],
24 | [['4'], ['3', '5']],
25 | ]);
26 | expect(stateGroupEquivalency).toStrictEqual(false);
27 | });
28 | });
29 |
--------------------------------------------------------------------------------
/packages/fa/tests/utils/expandCharacterRanges.test.ts:
--------------------------------------------------------------------------------
1 | import { expandCharacterRanges } from '../../libs/utils/expandCharacterRanges';
2 |
3 | it(`Should work with upper case`, () => {
4 | expect(expandCharacterRanges('A-E')).toStrictEqual(['A', 'B', 'C', 'D', 'E']);
5 | });
6 |
7 | it(`Should work with negative numbers case`, () => {
8 | expect(expandCharacterRanges('-E')).toStrictEqual(['-E']);
9 | });
10 |
11 | it(`Should work with lowercase case`, () => {
12 | expect(expandCharacterRanges('a-e')).toStrictEqual(['a', 'b', 'c', 'd', 'e']);
13 | });
14 |
15 | it(`Should work with numbers`, () => {
16 | expect(expandCharacterRanges('1-5')).toStrictEqual(['1', '2', '3', '4', '5']);
17 | });
18 |
19 | it(`Should work with multiple character classes`, () => {
20 | expect(expandCharacterRanges('a-e,1-5')).toStrictEqual([
21 | 'a',
22 | 'b',
23 | 'c',
24 | 'd',
25 | 'e',
26 | '1',
27 | '2',
28 | '3',
29 | '4',
30 | '5',
31 | ]);
32 | });
33 |
34 | it(`Should work for regular symbols classes`, () => {
35 | expect(expandCharacterRanges('a')).toStrictEqual(['a']);
36 | });
37 |
38 | it(`Should skip expansion altogether`, () => {
39 | expect(expandCharacterRanges('a-b', true)).toStrictEqual(['a-b']);
40 | });
41 |
--------------------------------------------------------------------------------
/docs/src/theme/IconLightMode/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | export default function IconLightMode(props) {
3 | return (
4 |
5 |
9 |
10 | );
11 | }
12 |
--------------------------------------------------------------------------------
/packages/fa/tests/DeterministicFiniteAutomaton/generateEquivalenceStates.test.ts:
--------------------------------------------------------------------------------
1 | import { generateEquivalenceStates } from '../../libs/DeterministicFiniteAutomaton/generateEquivalenceStates';
2 |
3 | describe('generateEquivalenceStates', () => {
4 | it(`Generate equivalent state groups`, () => {
5 | const equivalentStateGroups = generateEquivalenceStates(
6 | {
7 | alphabets: ['0', '1'],
8 | states: ['0', '1', '2', '3', '4', '5', '6', '7'],
9 | transitions: {
10 | 0: {
11 | 0: ['1'],
12 | 1: ['5'],
13 | },
14 | 1: {
15 | 0: ['6'],
16 | 1: ['2'],
17 | },
18 | 2: {
19 | 0: ['0'],
20 | 1: ['2'],
21 | },
22 | 3: {
23 | 0: ['2'],
24 | 1: ['6'],
25 | },
26 | 4: {
27 | 0: ['7'],
28 | 1: ['5'],
29 | },
30 | 5: {
31 | 0: ['2'],
32 | 1: ['6'],
33 | },
34 | 6: {
35 | 0: ['6'],
36 | 1: ['4'],
37 | },
38 | 7: {
39 | 0: ['6'],
40 | 1: ['2'],
41 | },
42 | },
43 | },
44 | [['0', '1', '3', '4', '5', '6', '7'], ['2']]
45 | );
46 |
47 | expect(equivalentStateGroups).toStrictEqual([['3', '5'], ['0', '4', '6'], ['1', '7'], ['2']]);
48 | });
49 | });
50 |
--------------------------------------------------------------------------------
/.github/workflows/deploy.yaml:
--------------------------------------------------------------------------------
1 | name: Deploy
2 |
3 | on:
4 | push:
5 | branches: ["main"]
6 |
7 | jobs:
8 | build:
9 | runs-on: ubuntu-latest
10 |
11 | strategy:
12 | matrix:
13 | node-version: [14.x]
14 | steps:
15 | - uses: actions/checkout@v2
16 | - name: Use Node.js ${{ matrix.node-version }}
17 | uses: actions/setup-node@v1
18 | with:
19 | node-version: ${{ matrix.node-version }}
20 | - name: Install dependencies
21 | run: |
22 | # Install vercel globally
23 | npm install vercel -g
24 | npm install
25 | npm run bootstrap
26 | - name: Deploy docs
27 | run: |
28 | cd ./docs
29 | npm install
30 | npm run build
31 | # Create the .vercel dir
32 | cd build
33 | mkdir .vercel
34 | ls -la .
35 | # Create vercel project.json
36 | echo {\"projectId\": \"$VERCEL_PROJECT_ID\", \"orgId\": \"$VERCEL_ORG_ID\"} > .vercel/project.json
37 | vercel --prod --token $VERCEL_TOKEN
38 | env:
39 | VERCEL_PROJECT_ID: ${{secrets.VERCEL_PROJECT_ID}}
40 | VERCEL_ORG_ID: ${{secrets.VERCEL_ORG_ID}}
41 | VERCEL_TOKEN: ${{secrets.VERCEL_TOKEN}}
42 |
--------------------------------------------------------------------------------
/apps/landing/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "lib": [
4 | "dom",
5 | "dom.iterable",
6 | "esnext"
7 | ],
8 | "module": "esnext",
9 | "moduleResolution": "node",
10 | "resolveJsonModule": true,
11 | "removeComments": true,
12 | "preserveConstEnums": true,
13 | "strict": true,
14 | "alwaysStrict": true,
15 | "strictNullChecks": true,
16 | "noUncheckedIndexedAccess": true,
17 | "noImplicitAny": true,
18 | "noImplicitReturns": true,
19 | "noImplicitThis": true,
20 | "noUnusedLocals": true,
21 | "noUnusedParameters": true,
22 | "allowUnreachableCode": false,
23 | "noFallthroughCasesInSwitch": true,
24 | "target": "ES2018",
25 | "outDir": "out",
26 | "declaration": true,
27 | "sourceMap": true,
28 | "esModuleInterop": true,
29 | "allowSyntheticDefaultImports": true,
30 | "allowJs": false,
31 | "skipLibCheck": true,
32 | "forceConsistentCasingInFileNames": true,
33 | "jsx": "preserve",
34 | "noEmit": true,
35 | "isolatedModules": true,
36 | "incremental": true
37 | },
38 | "exclude": [
39 | "./out/**/*",
40 | "./node_modules/**/*"
41 | ],
42 | "include": [
43 | "next-env.d.ts",
44 | "**/*.ts",
45 | "**/*.tsx"
46 | ]
47 | }
--------------------------------------------------------------------------------
/apps/playground/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "lib": [
4 | "dom",
5 | "dom.iterable",
6 | "esnext"
7 | ],
8 | "module": "esnext",
9 | "moduleResolution": "node",
10 | "resolveJsonModule": true,
11 | "removeComments": true,
12 | "preserveConstEnums": true,
13 | "strict": true,
14 | "alwaysStrict": true,
15 | "strictNullChecks": true,
16 | "noUncheckedIndexedAccess": true,
17 | "noImplicitAny": true,
18 | "noImplicitReturns": true,
19 | "noImplicitThis": true,
20 | "noUnusedLocals": true,
21 | "noUnusedParameters": true,
22 | "allowUnreachableCode": false,
23 | "noFallthroughCasesInSwitch": true,
24 | "target": "ES2018",
25 | "outDir": "out",
26 | "declaration": true,
27 | "sourceMap": true,
28 | "esModuleInterop": true,
29 | "allowSyntheticDefaultImports": true,
30 | "allowJs": false,
31 | "skipLibCheck": true,
32 | "forceConsistentCasingInFileNames": true,
33 | "jsx": "preserve",
34 | "noEmit": true,
35 | "isolatedModules": true,
36 | "incremental": true
37 | },
38 | "exclude": [
39 | "./out/**/*",
40 | "./node_modules/**/*"
41 | ],
42 | "include": [
43 | "next-env.d.ts",
44 | "**/*.ts",
45 | "**/*.tsx"
46 | ]
47 | }
--------------------------------------------------------------------------------
/packages/testing/libs/utils/generateCaseMessage.ts:
--------------------------------------------------------------------------------
1 | import colors from 'colors';
2 |
3 | /**
4 | * Generate case message for each input string
5 | * @param isWrong Whether or not the case is wrong
6 | * @param inputString The input string that was used in the case
7 | * @param automataTestResult Verdict of the automata test for the input string
8 | * @param logicTestResult Verdict of the login test for the input string
9 | * @returns
10 | */
11 | export function generateCaseMessage(
12 | isWrong: boolean,
13 | inputString: string,
14 | automataTestResult: boolean,
15 | logicTestResult: boolean
16 | ) {
17 | // Generate two strings both with and without colors
18 | // Coloured could be used inside a terminal
19 | // While non coloured could be used for storing to a file
20 | return {
21 | withColors: `${[
22 | `V: ${isWrong ? colors.red.bold(`WRONG`) : colors.green.bold(`CORRECT`)}`,
23 | `I: ${colors.yellow.bold(inputString)}`,
24 | `L: ${colors.blue.bold(logicTestResult.toString())}`,
25 | `A: ${colors.blue.bold(automataTestResult.toString())}`,
26 | ].join('\n')}\n`,
27 | withoutColors: `${[
28 | `V: ${isWrong ? `WRONG` : `CORRECT`}`,
29 | `I: ${inputString}`,
30 | `L: ${logicTestResult}`,
31 | `A: ${automataTestResult}`,
32 | ].join('\n')}\n`,
33 | };
34 | }
35 |
--------------------------------------------------------------------------------
/apps/playground/src/components/Drawer.tsx:
--------------------------------------------------------------------------------
1 | import Box from '@mui/material/Box';
2 | import MuiDrawer from '@mui/material/Drawer';
3 | import * as React from 'react';
4 | import { DrawerContext } from '../contexts/DrawerContext';
5 |
6 | interface DrawerProps {
7 | drawerContent: React.ReactNode
8 | }
9 |
10 | export default function Drawer(props: DrawerProps) {
11 | const {drawerContent} = props;
12 | const {isDrawerOpen, setIsDrawerOpen} = React.useContext(DrawerContext);
13 |
14 | const toggleDrawer = (open: boolean, event: React.KeyboardEvent) => {
15 | if (event.type === 'keydown' && (event.key === 'Tab' || event.key === 'Shift')) {
16 | return;
17 | }
18 |
19 | setIsDrawerOpen(open);
20 | };
21 |
22 | return (
23 | toggleDrawer(false, event as any)}
27 | sx={{
28 | p: 1
29 | }}
30 | >
31 | toggleDrawer(false ,event as unknown as React.KeyboardEvent)}
36 | onKeyDown={(event) => toggleDrawer(false ,event)}
37 | >
38 | {drawerContent}
39 |
40 |
41 | );
42 | }
43 |
--------------------------------------------------------------------------------
/packages/cfg/tests/removeNonDeterminism.test.ts:
--------------------------------------------------------------------------------
1 | import { removeNonDeterminism } from '../libs/removeNonDeterminism';
2 |
3 | describe('removeNonDeterminism', () => {
4 | it(`Should be able to left factor for multiple production rule`, () => {
5 | const leftRefactored = removeNonDeterminism({
6 | productionRules: {
7 | S: ["a A d", "a B"],
8 | A: ["a", "a b"],
9 | B: ["c c d", "d d c"]
10 | },
11 | startVariable: "S"
12 | });
13 | expect(leftRefactored).toStrictEqual({
14 | productionRules: {
15 | S: ["a S'"],
16 | "S'": ['A d', 'B'],
17 | A: ["a A'"],
18 | "A'": ['', 'b'],
19 | B: ['c c d', 'd d c'],
20 | },
21 | startVariable: 'S',
22 | variables: ['S', "S'", 'A', "A'", 'B'],
23 | terminals: ['a', 'd', 'b', 'c'],
24 | });
25 | });
26 |
27 | it(`Should be able to left factor for single production rule`, () => {
28 | const leftRefactored = removeNonDeterminism({
29 | productionRules: {
30 | S: ['b', 'a S S b S', 'a S a S b', 'a b b'],
31 | },
32 | startVariable: 'S',
33 | });
34 | expect(leftRefactored).toStrictEqual({
35 | productionRules: {
36 | S: ['b', "a S'"],
37 | "S'": ["S S''", 'b b'],
38 | "S''": ['S b S', 'a S b'],
39 | },
40 | startVariable: 'S',
41 | variables: ['S', "S'", "S''"],
42 | terminals: ['b', 'a'],
43 | });
44 | });
45 | });
46 |
--------------------------------------------------------------------------------
/packages/cfg/tests/generateLL1ParsingTable.test.ts:
--------------------------------------------------------------------------------
1 | import { generateLL1ParsingTable } from '../libs/generateLL1ParsingTable';
2 |
3 | describe('generateLL1ParsingTable', () => {
4 | it(`Simple LL1 non parsable grammar`, () => {
5 | const ll1ParsingTable = generateLL1ParsingTable({
6 | productionRules: {
7 | S: ['a S b S', 'b S a S', ''],
8 | },
9 | });
10 | expect(
11 | ll1ParsingTable
12 | ).toStrictEqual({ parseTable: { S: { a: 2, b: 2, $: 2 } }, isParsable: false });
13 | });
14 |
15 | it(`Complex LL1 parsable grammar`, () => {
16 | const ll1ParsingTable = generateLL1ParsingTable({
17 | productionRules: {
18 | E: ["T E'"],
19 | "E'": ["+ T E'", ''],
20 | T: ["F T'"],
21 | "T'": ["* F T'", ''],
22 | F: ['id', '( E )'],
23 | },
24 | })
25 | expect(
26 | ll1ParsingTable
27 | ).toStrictEqual({
28 | parseTable: {
29 | E: {
30 | id: 0,
31 | '(': 0,
32 | },
33 | "E'": {
34 | '+': 0,
35 | ')': 1,
36 | $: 1,
37 | },
38 | T: {
39 | id: 0,
40 | '(': 0,
41 | },
42 | "T'": {
43 | '+': 1,
44 | '*': 0,
45 | ')': 1,
46 | $: 1,
47 | },
48 | F: {
49 | id: 0,
50 | '(': 1,
51 | },
52 | },
53 | isParsable: true,
54 | });
55 | });
56 | });
57 |
--------------------------------------------------------------------------------
/packages/cfg/tests/removeUnitProduction.test.ts:
--------------------------------------------------------------------------------
1 | import { findFirstUnitProductionRule, removeUnitProduction } from '../libs/removeUnitProduction';
2 |
3 | describe('.findFirstUnitProductionRule', () => {
4 | it(`Should find first unit production rule`, () => {
5 | expect(
6 | findFirstUnitProductionRule(['Sub', 'Adj', 'Verb', 'Conj'], {
7 | Sub: ['0 Adj', '1 Verb', 'Conj'],
8 | Adj: ['0 Sub', '0 0'],
9 | Verb: ['1', 'Adj'],
10 | Conj: ['0 1'],
11 | })
12 | ).toStrictEqual(['Sub', 2]);
13 | });
14 |
15 | it(`Should return null if no production rule returns unit`, () => {
16 | expect(
17 | findFirstUnitProductionRule(['Sub', 'Adj', 'Verb'], {
18 | Sub: ['0 Adj', '1 Verb'],
19 | Adj: ['0 Sub', '0 0'],
20 | Verb: ['1'],
21 | })
22 | ).toStrictEqual(null);
23 | });
24 | });
25 |
26 | describe('removeUnitProduction', () => {
27 | it(`Remove unit production`, () => {
28 | const productionRules = {
29 | Sub: ['0 Adj', '1 Verb', 'Conj'],
30 | Adj: ['0 Sub', '0 0'],
31 | Verb: ['1', 'Adj'],
32 | Conj: ['0 1'],
33 | };
34 |
35 | removeUnitProduction({ productionRules, variables: ['Sub', 'Adj', 'Verb', 'Conj'] });
36 |
37 | expect(productionRules).toStrictEqual({
38 | Sub: ['0 Adj', '1 Verb', '0 1'],
39 | Adj: ['0 Sub', '0 0'],
40 | Verb: ['1', '0 Sub', '0 0'],
41 | Conj: ['0 1'],
42 | });
43 | });
44 | });
45 |
--------------------------------------------------------------------------------
/docs/static/img/favicon.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "fauton",
3 | "homepage": "https://github.com/Devorein/fauton/blob/master/README.md",
4 | "bugs": {
5 | "url": "https://github.com/Devorein/fauton/issues"
6 | },
7 | "scripts": {
8 | "prebuild": "lerna run prebuild",
9 | "build": "lerna run build",
10 | "test": "lerna run test",
11 | "lint": "lerna run lint",
12 | "format": "lerna run format",
13 | "build:watch": "lerna run build:watch --parallel",
14 | "bootstrap": "lerna bootstrap"
15 | },
16 | "repository": {
17 | "type": "git",
18 | "url": "git+https://github.com/Devorein/fauton.git"
19 | },
20 | "author": "Safwan Shaheer ",
21 | "license": "MIT",
22 | "devDependencies": {
23 | "@types/cli-progress": "^3.9.2",
24 | "@types/jest": "^27.0.2",
25 | "@types/node": "^16.11.6",
26 | "@types/shortid": "^0.0.29",
27 | "@typescript-eslint/eslint-plugin": "^5.4.0",
28 | "@typescript-eslint/parser": "^5.4.0",
29 | "del-cli": "^4.0.1",
30 | "eslint": "^8.2.0",
31 | "eslint-config-airbnb-base": "^15.0.0",
32 | "eslint-config-prettier": "^8.3.0",
33 | "eslint-import-resolver-node": "^0.3.6",
34 | "eslint-plugin-import": "^2.25.3",
35 | "eslint-plugin-prettier": "^4.0.0",
36 | "jest": "^27.3.1",
37 | "np": "^7.5.0",
38 | "prettier": "^2.4.1",
39 | "ts-jest": "^27.0.7",
40 | "typescript": "^4.4.4"
41 | },
42 | "dependencies": {
43 | "lerna": "^4.0.0"
44 | }
45 | }
--------------------------------------------------------------------------------
/packages/cfg/libs/convertStringToGrammar.ts:
--------------------------------------------------------------------------------
1 | import { extractTerminalsFromCfg } from './extractTerminalsFromCfg';
2 | import { IContextFreeGrammar } from './types';
3 |
4 | /**
5 | * Convert a string representation of cfg object to a cfg object
6 | * @param grammarString Grammar string to convert to cfg object
7 | * @returns Converted cfg object
8 | */
9 | export function convertStringToGrammar(grammarString: string): IContextFreeGrammar {
10 | const productionRules = grammarString.split('\n');
11 |
12 | const cfg: IContextFreeGrammar = {
13 | productionRules: {},
14 | startVariable: '',
15 | terminals: [],
16 | variables: [],
17 | };
18 |
19 | productionRules.forEach((productionRule) => {
20 | const [variable, rules] = productionRule.split(' -> ');
21 | // Only create the production rule if it doesn't exist, otherwise we might replace the existing one
22 | if (!cfg.productionRules[variable]) {
23 | cfg.productionRules[variable] = [];
24 | }
25 |
26 | // Each production rule is separated by |
27 | rules.split(' | ').forEach((rule) => {
28 | cfg.productionRules[variable].push(rule);
29 | });
30 | cfg.variables.push(variable);
31 | });
32 |
33 | cfg.variables = Array.from(new Set(cfg.variables));
34 | // Set the first variable as the start variable
35 | // eslint-disable-next-line
36 | cfg.startVariable = cfg.variables[0];
37 |
38 | cfg.terminals = extractTerminalsFromCfg(cfg);
39 | return cfg;
40 | }
41 |
--------------------------------------------------------------------------------
/docs/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "docs",
3 | "version": "0.0.0",
4 | "private": true,
5 | "scripts": {
6 | "docusaurus": "docusaurus",
7 | "start": "docusaurus start",
8 | "build": "docusaurus build",
9 | "swizzle": "docusaurus swizzle @docusaurus/theme-classic",
10 | "deploy": "vercel ./build --prod",
11 | "clear": "docusaurus clear",
12 | "serve": "docusaurus serve",
13 | "write-translations": "docusaurus write-translations",
14 | "write-heading-ids": "docusaurus write-heading-ids",
15 | "typecheck": "tsc"
16 | },
17 | "dependencies": {
18 | "@docusaurus/core": "^2.0.0-beta.20",
19 | "@docusaurus/preset-classic": "^2.0.0-beta.20",
20 | "@mdx-js/react": "^1.6.21",
21 | "clsx": "^1.1.1",
22 | "docusaurus-plugin-exgen": "0.0.1",
23 | "prism-react-renderer": "^1.3.3",
24 | "react": "^17.0.1",
25 | "react-dom": "^17.0.1",
26 | "react-toggle": "^4.1.2"
27 | },
28 | "devDependencies": {
29 | "@docusaurus/module-type-aliases": "^2.0.0-beta.20",
30 | "@tsconfig/docusaurus": "^1.0.5",
31 | "docusaurus-plugin-typedoc": "^0.17.5",
32 | "typedoc": "^0.22.15",
33 | "typedoc-plugin-markdown": "^3.12.1",
34 | "typescript": "^4.6.4"
35 | },
36 | "browserslist": {
37 | "production": [
38 | ">0.5%",
39 | "not dead",
40 | "not op_mini all"
41 | ],
42 | "development": [
43 | "last 1 chrome version",
44 | "last 1 firefox version",
45 | "last 1 safari version"
46 | ]
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/packages/cfg/tests/removeNonTerminableProduction.test.ts:
--------------------------------------------------------------------------------
1 | import { removeNonTerminableProduction } from '../libs/removeNonTerminableProduction';
2 |
3 | describe('removeNonTerminableProduction', () => {
4 | it(`Remove non terminable production`, () => {
5 | const cfg = {
6 | productionRules: {
7 | S: ['Adj Con', 'Verb'],
8 | Adj: ['another'],
9 | Con: ['can', 'Verb Con'],
10 | E: ['another Adj', 'early'],
11 | Verb: ['Verb'],
12 | },
13 | startVariable: 'S',
14 | // Note that production rules for Noun variable is not present,
15 | variables: ['S', 'Adj', 'Con', 'E', 'Verb', 'Noun'],
16 | terminals: ['another', 'can', 'early'],
17 | };
18 |
19 | const terminalVariables = removeNonTerminableProduction(cfg);
20 | expect(terminalVariables).toStrictEqual(['S', 'Adj', 'Con', 'E']);
21 | expect(cfg.productionRules).toStrictEqual({
22 | S: ['Adj Con'],
23 | Adj: ['another'],
24 | Con: ['can'],
25 | E: ['another Adj', 'early'],
26 | });
27 | });
28 | });
29 |
30 | it(`Should throw error if start variable is non terminable`, () => {
31 | const cfg = {
32 | productionRules: {
33 | S: ['S', 'Verb'],
34 | Verb: ['Verb'],
35 | },
36 | startVariable: 'S',
37 | // Note that production rules for Noun variable is not present,
38 | variables: ['S', 'Verb'],
39 | terminals: ['another', 'can', 'early'],
40 | };
41 |
42 | expect(() => removeNonTerminableProduction(cfg)).toThrow(`This grammar can't be convert to cnf`);
43 | });
44 |
--------------------------------------------------------------------------------
/packages/cfg/tests/removeLeftRecursion.test.ts:
--------------------------------------------------------------------------------
1 | import { removeLeftRecursion } from '../libs/removeLeftRecursion';
2 |
3 | describe('removeLeftRecursion', () => {
4 | it(`Should remove both direct and indirect left recursion`, () => {
5 | const leftRecursionRemovedCfg = removeLeftRecursion({
6 | productionRules: {
7 | S: ["A f", "b"],
8 | A: ["A c", "S d", "B e", "C"],
9 | B: ["A g", "S h", "k"],
10 | C: ["B k m A", "A S", "j"],
11 | },
12 | startVariable: "S"
13 | });
14 | expect(leftRecursionRemovedCfg).toStrictEqual({
15 | productionRules: {
16 | S: ['A f', 'b'],
17 | A: ["b d A'", "B e A'", "C A'"],
18 | "A'": ["c A'", "f d A'", ''],
19 | B: ["b d A' g B'", "C A' g B'", "b d A' f h B'", "C A' f h B'", "b h B'", "k B'"],
20 | "B'": ["e A' g B'", "e A' f h B'", ''],
21 | C: [
22 | "b d A' g B' k m A C'",
23 | "b d A' f h B' k m A C'",
24 | "b h B' k m A C'",
25 | "k B' k m A C'",
26 | "b d A' S C'",
27 | "b d A' g B' e A' S C'",
28 | "b d A' f h B' e A' S C'",
29 | "b h B' e A' S C'",
30 | "k B' e A' S C'",
31 | "j C'",
32 | ],
33 | "C'": [
34 | "A' g B' k m A C'",
35 | "A' f h B' k m A C'",
36 | "A' g B' e A' S C'",
37 | "A' f h B' e A' S C'",
38 | "A' S C'",
39 | '',
40 | ],
41 | },
42 | startVariable: 'S',
43 | variables: ['S', 'A', "A'", 'B', "B'", 'C', "C'"],
44 | terminals: ['f', 'b', 'd', 'e', 'c', 'g', 'h', 'k', 'm', 'j'],
45 | });
46 | });
47 | });
48 |
--------------------------------------------------------------------------------
/packages/regex/libs/utils/addConcatOperatorToRegex.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * Add concat (.) operator to regex string where necessary
3 | * @param regexString Regex string to add concat operator to
4 | * @returns Concat operator added regex string
5 | */
6 | export function addConcatOperatorToRegex(regexString: string) {
7 | if (regexString.length === 1) {
8 | return regexString;
9 | }
10 |
11 | const regexOperatorGroup1 = new Set('(*+?|');
12 | const regexOperatorGroup2 = new Set(')*+?|');
13 | const regexOperatorGroup3 = new Set('*+?');
14 |
15 | let newRegexString = '';
16 | for (let index = 0; index < regexString.length; index += 1) {
17 | const regexSymbol = regexString[index];
18 | newRegexString += regexSymbol;
19 | // Break on last character
20 | // We could've looped one less time
21 | // but then the last symbol wouldn't have been added
22 | if (index === regexString.length -1 ) {
23 | break;
24 | }
25 | // add concatenation operator between
26 | // literal.literal, right_bracket.left_bracket, literal.left_bracket
27 | if (
28 | !regexOperatorGroup1.has(regexSymbol) &&
29 | !regexOperatorGroup2.has(regexString[index + 1])
30 | ) {
31 | newRegexString += '.';
32 | } else if (
33 | // add concatenation operator between
34 | // +*? and ( or literal
35 | regexOperatorGroup3.has(regexSymbol) &&
36 | !regexOperatorGroup2.has(regexString[index + 1])
37 | ) {
38 | newRegexString += '.';
39 | }
40 | }
41 | return newRegexString;
42 | }
43 |
--------------------------------------------------------------------------------
/packages/fa/libs/utils/expandCharacterRanges.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * Generates an array of symbols by expanding the character classes string separate by ,
3 | * @param characterRangesString String containing character classes separated by ,
4 | * @returns An array of symbols that are present in the character classes
5 | */
6 | export function expandCharacterRanges(characterRangesString: string, skipExpansion?: boolean) {
7 | const shouldSkipExpansion = skipExpansion ?? false;
8 |
9 | if (!shouldSkipExpansion) {
10 | const characterRanges = characterRangesString.split(',');
11 | const symbols: Set = new Set();
12 | characterRanges.forEach((characterRange) => {
13 | // Handling negative signs
14 | if (characterRange.length === 2) {
15 | symbols.add(characterRange);
16 | } else {
17 | const characterRangeRanges = characterRange.split('-');
18 | if (characterRangeRanges.length === 2) {
19 | const startCharacter = characterRangeRanges[0].charCodeAt(0);
20 | const endCharacter = characterRangeRanges[1].charCodeAt(0);
21 | for (
22 | let characterCodePoint = startCharacter;
23 | characterCodePoint <= endCharacter;
24 | characterCodePoint += 1
25 | ) {
26 | symbols.add(String.fromCodePoint(characterCodePoint));
27 | }
28 | } else {
29 | // When its a single symbol
30 | symbols.add(characterRange);
31 | }
32 | }
33 | });
34 | return Array.from(symbols);
35 | } else {
36 | return [characterRangesString];
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/packages/cfg/libs/utils/generateNewVariable.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * Generates a random integer between two intervals
3 | * @param min Left limit of generated int
4 | * @param max Right limit of generated int
5 | * @returns A random integer
6 | */
7 | function randomIntFromInterval(min: number, max: number) {
8 | return Math.floor(Math.random() * (max - min + 1) + min);
9 | }
10 |
11 | const CAPITAL_LETTERS = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';
12 | const DIGITS = '0123456789';
13 |
14 | /**
15 | * Generates a new variable that is not part of the passed variables array
16 | * @param variables A set of variables which shouldn't be generated
17 | * @returns Generated variable string
18 | */
19 | export function generateNewVariable(variables: string[]) {
20 | const variablesSet = new Set(variables);
21 | // Create a variable with first character to be any capital letter and 2nd letter to be digit
22 | let newVariable =
23 | CAPITAL_LETTERS[randomIntFromInterval(0, CAPITAL_LETTERS.length - 1)] +
24 | DIGITS[randomIntFromInterval(0, DIGITS.length - 1)];
25 |
26 | // While the new variable is present in our variables set we need to keep on creating it
27 | while (variablesSet.has(newVariable)) {
28 | // New variable will be a combination of capital letter and a digit
29 | newVariable =
30 | CAPITAL_LETTERS[randomIntFromInterval(0, CAPITAL_LETTERS.length - 1)] +
31 | DIGITS[randomIntFromInterval(0, DIGITS.length - 1)];
32 | }
33 | // Push the newly generated variable to the passed variables array
34 | variables.push(newVariable);
35 | return newVariable;
36 | }
--------------------------------------------------------------------------------
/packages/cfg/libs/utils/removeProductionRules.ts:
--------------------------------------------------------------------------------
1 | import { IContextFreeGrammar } from '../types';
2 | import { setDifference } from './setOperations';
3 |
4 | export function removeProductionRules(
5 | cfg: Pick & {
6 | removedVariables: string[];
7 | }
8 | ) {
9 | const { productionRules, removedVariables, variables } = cfg;
10 | // Remove production variables from the production rules record
11 | removedVariables.forEach((removedVariable) => {
12 | delete productionRules[removedVariable];
13 | });
14 | const removedVariablesSet = new Set(removedVariables);
15 |
16 | // Now we need to remove all the production rules that references any removed production variables
17 | Object.entries(productionRules).forEach(([productionVariable, productionRulesSubstitutions]) => {
18 | productionRules[productionVariable] = productionRulesSubstitutions.filter(
19 | (productionRulesSubstitution) => {
20 | // productionRulesSubstitution = Verb Adj Noun
21 | const productionRulesSubstitutionTokensSet = new Set(
22 | productionRulesSubstitution.split(' ')
23 | );
24 | const difference = setDifference(productionRulesSubstitutionTokensSet, removedVariablesSet);
25 | // If we dont need to remove any variables then the size before and after the set difference would be similar
26 | return difference.size === productionRulesSubstitutionTokensSet.size;
27 | }
28 | );
29 | });
30 |
31 | return Array.from(setDifference(new Set(variables), new Set(removedVariables)));
32 | }
33 |
--------------------------------------------------------------------------------
/scripts/build.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | packages=( cfg fa testing )
4 |
5 | RED='\033[0;31m'
6 | GREEN='\033[0;32m'
7 | BLUE='\033[0;34m'
8 | NC='\033[0m'
9 |
10 | echo $GITHUB_WORKSPACE
11 |
12 | for package in "${packages[@]}" ; do
13 | package_name="@fauton/$package"
14 | cd "${GITHUB_WORKSPACE}/packages/$package"
15 | tsc="${GITHUB_WORKSPACE}/node_modules/.bin/tsc"
16 | jest="${GITHUB_WORKSPACE}/node_modules/.bin/jest"
17 | eslint="${GITHUB_WORKSPACE}/node_modules/.bin/eslint"
18 |
19 | # if ! (npm install -g) then
20 | # echo -e "${RED}Error installing $package_name globally${NC}"
21 | # exit 1
22 | # else
23 | # echo -e "${GREEN}Successfully installed $package_name globally${NC}"
24 | # fi
25 |
26 | if ! (npm install) then
27 | echo -e "${RED}Error installing $package_name dependencies${NC}"
28 | exit 1
29 | else
30 | echo -e "${GREEN}Successfully installed $package_name dependencies${NC}"
31 | fi
32 |
33 | if ! (node $eslint ./libs --ext tsx,ts) then
34 | echo -e "${RED}Error linting $package_name${NC}"
35 | exit 1
36 | else
37 | echo -e "${GREEN}Successfully linted $package_name${NC}"
38 | fi
39 |
40 | if ! (node $tsc --sourceMap false) then
41 | echo -e "${RED}Error building $package_name${NC}"
42 | exit 1
43 | else
44 | echo -e "${GREEN}Successfully build $package_name${NC}"
45 | fi
46 |
47 | if ! (node $jest --runInBand) then
48 | echo -e "${RED}Error testing $package_name${NC}"
49 | exit 1
50 | else
51 | echo -e "${GREEN}Successfully tested $package_name${NC}"
52 | fi
53 | done
--------------------------------------------------------------------------------
/packages/regex/tests/addConcatOperatorToRegex.test.ts:
--------------------------------------------------------------------------------
1 | import { addConcatOperatorToRegex } from "../libs/utils/addConcatOperatorToRegex";
2 |
3 | describe('addConcatOperatorToRegex', () => {
4 | it(`Single length regex string`, () => {
5 | const concatOperatorAddedRegexString = addConcatOperatorToRegex("a");
6 | expect(concatOperatorAddedRegexString).toBe("a")
7 | })
8 |
9 | it(`Multi length regex string (between two literals)`, () => {
10 | const concatOperatorAddedRegexString = addConcatOperatorToRegex("ab");
11 | expect(concatOperatorAddedRegexString).toStrictEqual("a.b")
12 | })
13 |
14 | it(`Multi length regex string (between two opposite brackets)`, () => {
15 | const concatOperatorAddedRegexString = addConcatOperatorToRegex(")(");
16 | expect(concatOperatorAddedRegexString).toStrictEqual(").(")
17 | })
18 |
19 | it(`Multi length regex string (between literal and left bracket)`, () => {
20 | const concatOperatorAddedRegexString = addConcatOperatorToRegex("a(");
21 | expect(concatOperatorAddedRegexString).toStrictEqual("a.(")
22 | })
23 |
24 | it(`Multi length regex string (between operator and left bracket)`, () => {
25 | const concatOperatorAddedRegexString = addConcatOperatorToRegex("+(");
26 | expect(concatOperatorAddedRegexString).toStrictEqual("+.(")
27 | })
28 |
29 | it(`Multi length regex string (between operator and literal)`, () => {
30 | const concatOperatorAddedRegexString = addConcatOperatorToRegex("+a");
31 | expect(concatOperatorAddedRegexString).toStrictEqual("+.a")
32 | })
33 | })
--------------------------------------------------------------------------------
/packages/testing/libs/types.ts:
--------------------------------------------------------------------------------
1 | /* eslint-disable no-unused-vars */
2 |
3 | export interface AutomatonTestInfo {
4 | falsePositives: number;
5 | falseNegatives: number;
6 | truePositives: number;
7 | trueNegatives: number;
8 | }
9 |
10 | export interface IOutputFiles {
11 | case: boolean;
12 | incorrect: boolean;
13 | correct: boolean;
14 | input: boolean;
15 | aggregate: boolean;
16 | accepted: boolean;
17 | rejected: boolean;
18 | }
19 |
20 | export type InputStringOption =
21 | | {
22 | type: 'generate';
23 | random?: {
24 | total: number;
25 | minTokenLength: number;
26 | maxTokenLength: number;
27 | };
28 | combo?: undefined | null;
29 | outputFiles?: Partial;
30 | }
31 | | {
32 | type: 'generate';
33 | combo: {
34 | maxTokenLength: number;
35 | startLength?: number;
36 | };
37 | random?: undefined | null;
38 | outputFiles?: Partial;
39 | }
40 | | {
41 | type: 'file';
42 | filePath: string;
43 | outputFiles?: Partial;
44 | }
45 | | {
46 | type: 'custom';
47 | inputs: string[][];
48 | outputFiles?: Partial;
49 | };
50 |
51 | export type IAutomatonTestLogicFn = (
52 | inputTokens: string[],
53 | automatonTestResult: boolean
54 | ) => boolean;
55 | export type IAutomatonTestFn = (inputTokens: string[]) => boolean;
56 |
57 | export interface IAutomatonInfo {
58 | test: IAutomatonTestFn;
59 | testLogic: IAutomatonTestLogicFn;
60 | automaton: {
61 | label: string;
62 | alphabets: string[];
63 | description?: string;
64 | };
65 | }
66 |
--------------------------------------------------------------------------------
/packages/cfg/tests/generateLR0ParsingTable.test.ts:
--------------------------------------------------------------------------------
1 | import { addDotToProductionRule, augmentCfg, generateClosureOfLR0Item } from "../libs/generateLR0ParsingTable";
2 |
3 | describe('addDotToProductionRule', () => {
4 | it(`Simple grammar`, () => {
5 | const productionRules = {
6 | S: ["A A"],
7 | A: ["a A", "b"]
8 | };
9 |
10 | addDotToProductionRule(productionRules, "S")
11 |
12 | expect(productionRules).toStrictEqual({
13 | S: ["$.$ A A"],
14 | A: ["a A", "b"]
15 | })
16 | })
17 | })
18 |
19 | describe('generateClosureOfLR0Item', () => {
20 | it(`Simple grammar`, () => {
21 | const productionRules = {
22 | "S'": ["$.$ S"],
23 | S: ["A A"],
24 | A: ["a A", "b"]
25 | };
26 |
27 | generateClosureOfLR0Item({
28 | productionRules,
29 | variables: ["S'", "S", "A"]
30 | }, "S'")
31 |
32 | expect(productionRules).toStrictEqual({
33 | "S'": ["$.$ S"],
34 | S: ["$.$ A A"],
35 | A: ["$.$ a A", "$.$ b"]
36 | })
37 | })
38 | })
39 |
40 | describe('augmentCfg', () => {
41 | it(`Simple grammar`, () => {
42 | const augmentedGrammar = augmentCfg({
43 | productionRules: {
44 | S: ["A a", "B"],
45 | A: ["a b"],
46 | B: ["S a"]
47 | },
48 | })
49 |
50 | expect(augmentedGrammar).toStrictEqual({
51 | productionRules: {
52 | "S'": ["S"],
53 | S: ["A a", "B"],
54 | A: ["a b"],
55 | B: ["S a"]
56 | },
57 | variables: [ "S'", "S", "A", "B"],
58 | startVariable: "S'",
59 | terminals: ["a", "b"]
60 | })
61 | })
62 | })
--------------------------------------------------------------------------------
/packages/cfg/tests/generateVariableReferenceRecord.test.ts:
--------------------------------------------------------------------------------
1 | import generateVariableReferenceRecord from '../libs/generateVariableReferenceRecord';
2 |
3 | describe('generateVariableReferenceRecord', () => {
4 | it(`Get variable referenced in all rules`, () => {
5 | expect(
6 | generateVariableReferenceRecord({
7 | productionRules: {
8 | A: ['word1 A word2', 'B A word3'],
9 | B: ['A B', 'C B B word4'],
10 | C: ['word1', 'C A'],
11 | },
12 | variables: ['A', 'B', 'C'],
13 | })
14 | ).toStrictEqual({
15 | A: [
16 | {
17 | variable: 'A',
18 | tokenNumber: 1,
19 | ruleNumber: 0,
20 | },
21 | {
22 | variable: 'A',
23 | tokenNumber: 1,
24 | ruleNumber: 1,
25 | },
26 | {
27 | variable: 'B',
28 | tokenNumber: 0,
29 | ruleNumber: 0,
30 | },
31 | {
32 | variable: 'C',
33 | tokenNumber: 1,
34 | ruleNumber: 1,
35 | },
36 | ],
37 | B: [
38 | {
39 | variable: 'A',
40 | tokenNumber: 0,
41 | ruleNumber: 1,
42 | },
43 | {
44 | variable: 'B',
45 | tokenNumber: 1,
46 | ruleNumber: 0,
47 | },
48 | {
49 | variable: 'B',
50 | tokenNumber: 1,
51 | ruleNumber: 1,
52 | },
53 | {
54 | variable: 'B',
55 | tokenNumber: 2,
56 | ruleNumber: 1,
57 | },
58 | ],
59 | C: [
60 | {
61 | variable: 'B',
62 | tokenNumber: 0,
63 | ruleNumber: 1,
64 | },
65 | {
66 | variable: 'C',
67 | tokenNumber: 0,
68 | ruleNumber: 1,
69 | }
70 | ]
71 | });
72 | });
73 | });
74 |
--------------------------------------------------------------------------------
/packages/fa/libs/NonDeterministicFiniteAutomaton/moveAndEpsilonClosureStateSet.ts:
--------------------------------------------------------------------------------
1 | import { TransformedFiniteAutomaton } from '../types';
2 | import { epsilonClosureOfState } from './epsilonClosureOfState';
3 |
4 | /**
5 | * Move_DFA(states_arr, symbol)
6 | * @param transitions Transition record of automaton
7 | * @param epsilonTransitions epsilon transitions record of automaton
8 | * @param states array of states
9 | * @param symbol Symbol for which to find e-closure of
10 | * @returns array of states obtained by epsilon closure for a symbol
11 | */
12 | export function moveAndEpsilonClosureStateSet(
13 | transitions: TransformedFiniteAutomaton['transitions'],
14 | epsilonTransitions: TransformedFiniteAutomaton['epsilon_transitions'],
15 | states: string[],
16 | symbol: string
17 | ) {
18 | const transitionRecordExtractedStates: Set = new Set();
19 | states.forEach((state) => {
20 | const statesForSymbol = transitions[state]?.[symbol];
21 | if (statesForSymbol) {
22 | statesForSymbol.forEach((stateForSymbol) =>
23 | transitionRecordExtractedStates.add(stateForSymbol)
24 | );
25 | }
26 | });
27 | const finalStates = new Set(transitionRecordExtractedStates);
28 | // No need to calculate epsilon closures of regular nfa
29 | if (epsilonTransitions) {
30 | transitionRecordExtractedStates.forEach((transitionRecordExtractedState) => {
31 | const epsilonClosuredStates = epsilonClosureOfState(
32 | epsilonTransitions,
33 | transitionRecordExtractedState
34 | );
35 | epsilonClosuredStates.forEach((epsilonClosuredState) => {
36 | finalStates.add(epsilonClosuredState);
37 | });
38 | });
39 | }
40 | return Array.from(finalStates);
41 | }
42 |
--------------------------------------------------------------------------------
/packages/cfg/libs/parseWithLL1Table.ts:
--------------------------------------------------------------------------------
1 | import { generateLL1ParsingTable } from "./generateLL1ParsingTable";
2 | import { generateParseTreeFromDerivations } from "./generateParseTreeFromDerivations";
3 | import { Derivation, IContextFreeGrammarInput } from "./types";
4 | import { populateCfg } from "./utils/populateCfg";
5 |
6 | export function parseWithLL1Table(inputCfg: IContextFreeGrammarInput, textContent: string) {
7 | const derivations: Derivation[] = []
8 | const cfg = populateCfg(inputCfg)
9 | const {parseTable} = generateLL1ParsingTable(cfg);
10 | const ruleStack = ["$", cfg.startVariable];
11 | let lookAheadPointer = 0;
12 | let parsed: null | boolean = null
13 | while (ruleStack.length !== 1 && lookAheadPointer !== textContent.length) {
14 | const variableAtStackTop = ruleStack.pop()!;
15 | if (variableAtStackTop === textContent[lookAheadPointer]) {
16 | lookAheadPointer += 1
17 | } else {
18 | const char = textContent[lookAheadPointer];
19 | if (char in parseTable[variableAtStackTop]) {
20 | const ruleNumber = parseTable[variableAtStackTop][char]!;
21 | const productionRule = cfg.productionRules[variableAtStackTop][ruleNumber];
22 | const productionRuleTokens = productionRule.split(" ");
23 | ruleStack.push(...[...productionRuleTokens].reverse())
24 | derivations.push([variableAtStackTop, productionRuleTokens])
25 | } else {
26 | parsed = false;
27 | break;
28 | }
29 | }
30 | }
31 |
32 | return {
33 | parsed: parsed ?? (ruleStack.length === 1 && ruleStack[0] === "$"),
34 | derivations,
35 | tree: generateParseTreeFromDerivations(cfg.variables, derivations)
36 | }
37 | }
--------------------------------------------------------------------------------
/apps/landing/.eslintrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | // Configuration for JavaScript files
3 | extends: ['airbnb-base', 'plugin:prettier/recommended'],
4 | rules: {
5 | 'prettier/prettier': 'off',
6 | },
7 | overrides: [
8 | // Configuration for TypeScript files
9 | {
10 | files: ['**/*.ts', '**/*.tsx'],
11 | plugins: ['@typescript-eslint', 'unused-imports'],
12 | extends: ['airbnb-typescript', 'next', 'next/core-web-vitals', 'plugin:prettier/recommended'],
13 | parserOptions: {
14 | project: './tsconfig.json',
15 | tsconfigRootDir: __dirname,
16 | },
17 | rules: {
18 | 'one-var': 'off',
19 | 'prefer-destructuring': 'off',
20 | 'react/jsx-props-no-spreading': 'off',
21 | 'no-underscore-dangle': 'off',
22 | 'prettier/prettier': 'off',
23 | 'react/destructuring-assignment': 'off', // Vscode doesn't support automatic destructuring, it's a pain to add a new variable
24 | 'jsx-a11y/anchor-is-valid': 'off', // Next.js use his own internal link system
25 | 'react/require-default-props': 'off', // Allow non-defined react props as undefined
26 | '@next/next/no-img-element': 'off', // We currently not using next/image because it isn't supported with SSG mode
27 | 'import/prefer-default-export': 'off',
28 | '@typescript-eslint/no-unused-vars': 'off',
29 | 'jsx-a11y/click-events-have-key-events': 'off',
30 | 'unused-imports/no-unused-imports': 'error',
31 | 'unused-imports/no-unused-vars': ['error', { argsIgnorePattern: '^_' }],
32 | 'no-param-reassign': ['error', { props: false }],
33 | 'no-else-return': ['error', { allowElseIf: true }],
34 | "@next/next/no-page-custom-font": "off"
35 | },
36 | },
37 | ],
38 | };
39 |
--------------------------------------------------------------------------------
/apps/playground/.eslintrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | // Configuration for JavaScript files
3 | extends: ['airbnb-base', 'plugin:prettier/recommended'],
4 | rules: {
5 | 'prettier/prettier': 'off',
6 | },
7 | overrides: [
8 | // Configuration for TypeScript files
9 | {
10 | files: ['**/*.ts', '**/*.tsx'],
11 | plugins: ['@typescript-eslint', 'unused-imports'],
12 | extends: ['airbnb-typescript', 'next', 'next/core-web-vitals', 'plugin:prettier/recommended'],
13 | parserOptions: {
14 | project: './tsconfig.json',
15 | tsconfigRootDir: __dirname,
16 | },
17 | rules: {
18 | 'one-var': 'off',
19 | 'prefer-destructuring': 'off',
20 | 'react/jsx-props-no-spreading': 'off',
21 | 'no-underscore-dangle': 'off',
22 | 'prettier/prettier': 'off',
23 | 'react/destructuring-assignment': 'off', // Vscode doesn't support automatic destructuring, it's a pain to add a new variable
24 | 'jsx-a11y/anchor-is-valid': 'off', // Next.js use his own internal link system
25 | 'react/require-default-props': 'off', // Allow non-defined react props as undefined
26 | '@next/next/no-img-element': 'off', // We currently not using next/image because it isn't supported with SSG mode
27 | 'import/prefer-default-export': 'off',
28 | '@typescript-eslint/no-unused-vars': 'off',
29 | 'jsx-a11y/click-events-have-key-events': 'off',
30 | 'unused-imports/no-unused-imports': 'error',
31 | 'unused-imports/no-unused-vars': ['error', { argsIgnorePattern: '^_' }],
32 | 'no-param-reassign': ['error', { props: false }],
33 | 'no-else-return': ['error', { allowElseIf: true }],
34 | '@next/next/no-page-custom-font': 'off',
35 | },
36 | },
37 | ],
38 | };
39 |
--------------------------------------------------------------------------------
/packages/testing/tests/generateUniversalLanguage.test.ts:
--------------------------------------------------------------------------------
1 | import { generateUniversalLanguage } from '../libs/utils/generateUniversalLanguage';
2 |
3 | describe('generateUniversalLanguage', () => {
4 | it(`Generate all string combinations`, () => {
5 | expect(generateUniversalLanguage(['a', 'b', 'c'], 3, 2)).toStrictEqual([
6 | ['a', 'a'],
7 | ['a', 'b'],
8 | ['a', 'c'],
9 | ['b', 'a'],
10 | ['b', 'b'],
11 | ['b', 'c'],
12 | ['c', 'a'],
13 | ['c', 'b'],
14 | ['c', 'c'],
15 | ['a', 'a', 'a'],
16 | ['a', 'a', 'b'],
17 | ['a', 'a', 'c'],
18 | ['a', 'b', 'a'],
19 | ['a', 'b', 'b'],
20 | ['a', 'b', 'c'],
21 | ['a', 'c', 'a'],
22 | ['a', 'c', 'b'],
23 | ['a', 'c', 'c'],
24 | ['b', 'a', 'a'],
25 | ['b', 'a', 'b'],
26 | ['b', 'a', 'c'],
27 | ['b', 'b', 'a'],
28 | ['b', 'b', 'b'],
29 | ['b', 'b', 'c'],
30 | ['b', 'c', 'a'],
31 | ['b', 'c', 'b'],
32 | ['b', 'c', 'c'],
33 | ['c', 'a', 'a'],
34 | ['c', 'a', 'b'],
35 | ['c', 'a', 'c'],
36 | ['c', 'b', 'a'],
37 | ['c', 'b', 'b'],
38 | ['c', 'b', 'c'],
39 | ['c', 'c', 'a'],
40 | ['c', 'c', 'b'],
41 | ['c', 'c', 'c'],
42 | ]);
43 | });
44 | });
45 |
46 | it(`Should generate string combinations when callback is present`, () => {
47 | const mockFn = jest.fn();
48 | expect(generateUniversalLanguage(['a', 'b'], 2, undefined, mockFn)).toStrictEqual([
49 | ['a'],
50 | ['b'],
51 | ['a', 'a'],
52 | ['a', 'b'],
53 | ['b', 'a'],
54 | ['b', 'b'],
55 | ]);
56 |
57 | expect(mockFn.mock.calls).toEqual([
58 | [['a']],
59 | [['b']],
60 | [['a', 'a']],
61 | [['a', 'b']],
62 | [['b', 'a']],
63 | [['b', 'b']],
64 | ]);
65 | });
66 |
--------------------------------------------------------------------------------
/packages/regex/libs/types.ts:
--------------------------------------------------------------------------------
1 | /* eslint-disable no-use-before-define */
2 |
3 | export type UnaryRegexOperators = "+" | "*" | "?";
4 | export type UnaryRegexOperatorsName = "Plus" | "Kleene" | "Optional";
5 | export type BinaryRegexOperators = "." | "|";
6 | export type BinaryRegexOperatorsName = "Concat" | "Or";
7 |
8 | export interface UnaryRegexNode {
9 | operator: Operator
10 | operands: [RegexNode];
11 | }
12 |
13 | export interface BinaryRegexNode {
14 | operator: Operator
15 | operands: [RegexNode, RegexNode];
16 | }
17 |
18 | export interface ConcatRegexNode extends BinaryRegexNode<"Concat"> {}
19 | export interface OrRegexNode extends BinaryRegexNode<"Or"> {}
20 | export interface KleeneRegexNode extends UnaryRegexNode<"Kleene"> {}
21 | export interface OptionalRegexNode extends UnaryRegexNode<"Optional"> {}
22 | export interface PlusRegexNode extends UnaryRegexNode<"Plus"> {}
23 |
24 | export interface LiteralRegexNode {
25 | operator: 'Literal';
26 | operands: [string | number];
27 | }
28 |
29 | export interface IRegularExpression {
30 | alphabets: string[];
31 | regex: RegExp;
32 | label: string;
33 | description?: string;
34 | }
35 |
36 | export type RegexNode =
37 | | ConcatRegexNode
38 | | KleeneRegexNode
39 | | OptionalRegexNode
40 | | OrRegexNode
41 | | LiteralRegexNode
42 | | PlusRegexNode;
43 |
44 | // eslint-disable-next-line
45 | export type IAutomatonTestLogicFn = (inputString: string, automatonTestResult: boolean) => boolean;
46 | // eslint-disable-next-line
47 | export type IAutomatonTestFn = (inputString: string) => boolean;
48 |
--------------------------------------------------------------------------------
/packages/cfg/libs/generateParseTreeFromDerivations.ts:
--------------------------------------------------------------------------------
1 | import { Derivation, ParseTree } from "./types";
2 |
3 | /**
4 | * Generate parse tree from derivations
5 | * @param variables Array of variables string
6 | * @param derivations Array of production variable and tokens tuple
7 | * @returns Parse tree
8 | */
9 | export function generateParseTreeFromDerivations(variables: string[], derivations: Derivation[]) {
10 | const rootTree: ParseTree[] = []
11 | const variablesSet = new Set(variables);
12 |
13 | // Global state to keep track of which derivation we are currently at
14 | let derivationNumber = 0;
15 |
16 | function recurse(derivation: Derivation, childContainer: (string | ParseTree)[]) {
17 | const [productionVariable, productionRuleTokens] = derivation;
18 | // Create the parent node
19 | const parentNode: ParseTree = {
20 | [productionVariable]: []
21 | };
22 |
23 | // Loop through all the tokens
24 | for (let index = 0; index < productionRuleTokens.length; index+=1) {
25 | const token = productionRuleTokens[index];
26 | // Check if the token is a variable
27 | if (variablesSet.has(token)) {
28 | // Move to the next derivation
29 | derivationNumber += 1;
30 | // Make sure we haven't exhausted the derivations
31 | if (derivationNumber < derivations.length) {
32 | recurse(derivations[derivationNumber], parentNode[productionVariable])
33 | } else {
34 | break;
35 | }
36 | } else {
37 | // Always push terminals as is
38 | parentNode[productionVariable].push(token)
39 | }
40 | }
41 | childContainer.push(parentNode)
42 | }
43 |
44 | recurse(derivations[0], rootTree)
45 | return rootTree[0]
46 | }
--------------------------------------------------------------------------------
/packages/fa/libs/NonDeterministicFiniteAutomaton/convertToRegularNfa.ts:
--------------------------------------------------------------------------------
1 | import { TransformedFiniteAutomaton } from '../types';
2 | import { epsilonClosureOfState } from './epsilonClosureOfState';
3 | import { moveAndEpsilonClosureStateSet } from './moveAndEpsilonClosureStateSet';
4 |
5 | /**
6 | * Convert an epsilon-nfa to nfa
7 | * @param automaton epsilon-nfa to be converted
8 | */
9 | export function convertToRegularNfa(
10 | automaton: Pick<
11 | TransformedFiniteAutomaton,
12 | 'transitions' | 'epsilon_transitions' | 'alphabets' | 'states'
13 | >
14 | ) {
15 | const { transitions, states, alphabets, epsilon_transitions: epsilonTransitions } = automaton;
16 | const epsilonClosureOfStateCache: Record = {};
17 | alphabets.forEach((symbol) => {
18 | states.forEach((state) => {
19 | // Only if the state has a key on the epsilon transition record, we are gonna expand it
20 | if (epsilonTransitions && epsilonTransitions[state]) {
21 | const epsilonClosuredStates = !epsilonClosureOfStateCache[state]
22 | ? epsilonClosureOfState(automaton.epsilon_transitions, state)
23 | : epsilonClosureOfStateCache[state];
24 | if (!epsilonClosureOfStateCache[state]) {
25 | epsilonClosureOfStateCache[state] = epsilonClosuredStates;
26 | }
27 |
28 | const epsilonClosuredStatesForSymbol = moveAndEpsilonClosureStateSet(
29 | transitions,
30 | epsilonTransitions,
31 | Array.from(epsilonClosuredStates),
32 | symbol
33 | );
34 | if (transitions[state]) {
35 | transitions[state][symbol] = epsilonClosuredStatesForSymbol;
36 | } else {
37 | transitions[state] = {
38 | [symbol]: epsilonClosuredStatesForSymbol,
39 | };
40 | }
41 | }
42 | });
43 | });
44 | }
45 |
--------------------------------------------------------------------------------
/apps/landing/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "next-js-boilerplate",
3 | "version": "1.0.0",
4 | "scripts": {
5 | "dev": "next dev",
6 | "build": "next build",
7 | "start": "next start",
8 | "build-stats": "cross-env ANALYZE=true npm run build",
9 | "export": "next export",
10 | "build-prod": "run-s clean build export",
11 | "clean": "rimraf .next out",
12 | "lint": "next lint",
13 | "build-types": "tsc --noEmit --pretty"
14 | },
15 | "dependencies": {
16 | "@fauton/cfg": "0.0.2",
17 | "next": "^12.1.0",
18 | "next-seo": "^4.28.1",
19 | "react": "^17.0.2",
20 | "react-dom": "^17.0.2",
21 | "styled-jsx-plugin-postcss": "^4.0.1"
22 | },
23 | "devDependencies": {
24 | "@next/bundle-analyzer": "^12.0.2",
25 | "@types/node": "^16.11.6",
26 | "@types/react": "^17.0.33",
27 | "@typescript-eslint/eslint-plugin": "^4.33.0",
28 | "@typescript-eslint/parser": "^4.33.0",
29 | "autoprefixer": "^10.4.0",
30 | "cross-env": "^7.0.3",
31 | "eslint": "^7.32.0",
32 | "eslint-config-airbnb-base": "^14.2.1",
33 | "eslint-config-airbnb-typescript": "^14.0.1",
34 | "eslint-config-next": "^12.0.2",
35 | "eslint-config-prettier": "^8.3.0",
36 | "eslint-plugin-import": "^2.25.2",
37 | "eslint-plugin-jsx-a11y": "^6.4.1",
38 | "eslint-plugin-prettier": "^4.0.0",
39 | "eslint-plugin-react": "^7.26.1",
40 | "eslint-plugin-react-hooks": "^4.2.0",
41 | "eslint-plugin-unused-imports": "^1.1.5",
42 | "husky": "^7.0.4",
43 | "lint-staged": "^11.2.6",
44 | "npm-run-all": "^4.1.5",
45 | "postcss": "^8.3.11",
46 | "prettier": "^2.4.1",
47 | "rimraf": "^3.0.2",
48 | "tailwindcss": "^2.2.19",
49 | "typescript": "^4.4.4"
50 | },
51 | "license": "ISC"
52 | }
--------------------------------------------------------------------------------
/docs/src/components/HomepageFeatures.tsx:
--------------------------------------------------------------------------------
1 | import useBaseUrl from '@docusaurus/useBaseUrl';
2 | import clsx from 'clsx';
3 | import React from 'react';
4 | import styles from './HomepageFeatures.module.css';
5 |
6 | type FeatureItem = {
7 | title: string;
8 | image: string;
9 | description: JSX.Element;
10 | };
11 |
12 | const FeatureList: FeatureItem[] = [
13 | {
14 | title: 'Easy to Use',
15 | image: '/img/easy.svg',
16 | description: (
17 | <>
18 | All the packages are well documented and crafted with ease of use in mind
19 | >
20 | ),
21 | },
22 | {
23 | title: 'Multi Purpose',
24 | image: '/img/multi_purpose.svg',
25 | description: (
26 | <>
27 | Fauton's ecosystem provides various packages to do almost anything with automaton
28 | >
29 | ),
30 | },
31 | {
32 | title: 'Typescript Support',
33 | image: '/img/ts_support.svg',
34 | description: (
35 | <>
36 | Typescript support right out of the box for static typechecking.
37 | >
38 | ),
39 | },
40 | ];
41 |
42 | function Feature({ image, title, description }: FeatureItem) {
43 | const imgUrl = useBaseUrl(image);
44 | return (
45 |
46 |
47 |
{title}
48 |
{description}
49 |
50 | );
51 | }
52 |
53 | export default function HomepageFeatures(): JSX.Element {
54 | return (
55 |
56 |
57 |
58 | {FeatureList.map((props, idx) => (
59 |
60 | ))}
61 |
62 |
63 |
64 | );
65 | }
66 |
--------------------------------------------------------------------------------
/packages/cfg/tests/validateCfg.test.ts:
--------------------------------------------------------------------------------
1 | import { validateCfg } from '../libs/validateCfg';
2 |
3 | it(`Should throw error if transition record contains a variable that is not present in variables array`, () => {
4 | try {
5 | validateCfg({
6 | productionRules: {
7 | S: ['a'],
8 | },
9 | startVariable: 'S',
10 | terminals: [],
11 | variables: ['A'],
12 | });
13 | } catch (err) {
14 | expect(err.message).toBe(
15 | `Transition record contains a variable S, that is not present in variables array`
16 | );
17 | }
18 | });
19 |
20 | it(`Should throw error if transition record substitution chunk is neither a variable nor a terminal`, () => {
21 | try {
22 | validateCfg({
23 | productionRules: {
24 | A: ['a'],
25 | },
26 | startVariable: 'S',
27 | terminals: ['b'],
28 | variables: ['A'],
29 | });
30 | } catch (err) {
31 | expect(err.message).toBe(
32 | `Transition record substitution chunk a is neither a variable nor a terminal`
33 | );
34 | }
35 | });
36 |
37 | it(`Should throw error if all variables are not present in transition record`, () => {
38 | try {
39 | validateCfg({
40 | productionRules: {
41 | A: ['a'],
42 | B: ['a'],
43 | },
44 | startVariable: 'S',
45 | terminals: ['b'],
46 | variables: ['B'],
47 | });
48 | } catch (err) {
49 | expect(err.message).toBe(`All variables must be present in the transition record`);
50 | }
51 | });
52 |
53 | it(`Should throw error if starting variable is not part of variables array`, () => {
54 | try {
55 | validateCfg({
56 | productionRules: {
57 | A: ['a'],
58 | B: ['a'],
59 | },
60 | startVariable: 'S',
61 | terminals: ['a'],
62 | variables: ['B', 'A'],
63 | });
64 | } catch (err) {
65 | expect(err.message).toBe(`Starting variable must be part of variables array`);
66 | }
67 | });
68 |
--------------------------------------------------------------------------------
/packages/fa/libs/DeterministicFiniteAutomaton/generateEquivalenceStates.ts:
--------------------------------------------------------------------------------
1 | import { TransformedFiniteAutomaton } from '../types';
2 | import { generateStateGroupsRecord } from './generateStateGroupsRecord';
3 |
4 | export function generateEquivalenceStates(
5 | automaton: Pick,
6 | stateGroups: string[][]
7 | ) {
8 | const stateGroupsRecord = generateStateGroupsRecord(automaton.states, stateGroups);
9 | const stateGroupsSymbolsRecord: Record = {};
10 | // Segregating state groups based on its length
11 | const singleStateGroups: string[][] = [];
12 | const compositeStateGroups: string[][] = [];
13 | stateGroups.forEach((stateGroup) => {
14 | if (stateGroup.length > 1) {
15 | compositeStateGroups.push(stateGroup);
16 | } else if (stateGroup.length !== 0) {
17 | singleStateGroups.push(stateGroup);
18 | }
19 | });
20 | // Looping through only composite state groups as only they can be broken down further
21 | compositeStateGroups.forEach((compositeStateGroup) => {
22 | compositeStateGroup.forEach((state) => {
23 | // Each combination of state group (for each symbol) we will be the key
24 | let stateGroup = '';
25 | automaton.alphabets.forEach((symbol) => {
26 | stateGroup += stateGroupsRecord[automaton.transitions[state][symbol].toString()];
27 | });
28 | if (!stateGroupsSymbolsRecord[stateGroup]) {
29 | stateGroupsSymbolsRecord[stateGroup] = [state];
30 | } else {
31 | stateGroupsSymbolsRecord[stateGroup].push(state);
32 | }
33 | });
34 | });
35 |
36 | const statesGroups = Object.values(stateGroupsSymbolsRecord);
37 | // Attaching the single state groups as they were not present in the record
38 | if (singleStateGroups.length !== 0) {
39 | return statesGroups.concat(singleStateGroups);
40 | } else {
41 | // Don't concat single state
42 | return statesGroups;
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/apps/playground/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "next-js-boilerplate",
3 | "version": "1.0.0",
4 | "scripts": {
5 | "dev": "next dev",
6 | "build": "next build",
7 | "start": "next start",
8 | "build-stats": "cross-env ANALYZE=true npm run build",
9 | "export": "next export",
10 | "build-prod": "run-s clean build export",
11 | "clean": "rimraf .next out",
12 | "lint": "next lint",
13 | "build-types": "tsc --noEmit --pretty"
14 | },
15 | "dependencies": {
16 | "@emotion/react": "^11.7.1",
17 | "@emotion/styled": "^11.6.0",
18 | "@fauton/cfg": "0.0.4",
19 | "@mui/icons-material": "^5.2.5",
20 | "@mui/material": "^5.2.5",
21 | "@types/tailwindcss": "^2.2.4",
22 | "next": "^12.1.0",
23 | "next-seo": "^4.28.1",
24 | "react": "^17.0.2",
25 | "react-dom": "^17.0.2",
26 | "styled-jsx-plugin-postcss": "^4.0.1"
27 | },
28 | "devDependencies": {
29 | "@next/bundle-analyzer": "^12.0.2",
30 | "@types/node": "^16.11.6",
31 | "@types/react": "^17.0.33",
32 | "@typescript-eslint/eslint-plugin": "^4.33.0",
33 | "@typescript-eslint/parser": "^4.33.0",
34 | "autoprefixer": "^10.4.0",
35 | "cross-env": "^7.0.3",
36 | "eslint": "^7.32.0",
37 | "eslint-config-airbnb-base": "^14.2.1",
38 | "eslint-config-airbnb-typescript": "^14.0.1",
39 | "eslint-config-next": "^12.0.2",
40 | "eslint-config-prettier": "^8.3.0",
41 | "eslint-plugin-import": "^2.25.2",
42 | "eslint-plugin-jsx-a11y": "^6.4.1",
43 | "eslint-plugin-prettier": "^4.0.0",
44 | "eslint-plugin-react": "^7.26.1",
45 | "eslint-plugin-react-hooks": "^4.2.0",
46 | "eslint-plugin-unused-imports": "^1.1.5",
47 | "husky": "^7.0.4",
48 | "lint-staged": "^11.2.6",
49 | "npm-run-all": "^4.1.5",
50 | "postcss": "^8.3.11",
51 | "prettier": "^2.4.1",
52 | "rimraf": "^3.0.2",
53 | "tailwindcss": "^2.2.19",
54 | "typescript": "^4.4.4"
55 | },
56 | "license": "ISC"
57 | }
--------------------------------------------------------------------------------
/packages/testing/libs/utils/generateUniversalLanguage.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * Generates all combination of strings that can be using the tokens from length 1 to `maxTokenLength`
3 | * @param tokens Alphabet of the strings
4 | * @param maxTokenLength Highest number of token the generated string can have
5 | * @param startTokenLength Starting token length of the string
6 | * @param languageChecker A cb passed each generated string
7 | * @returns An array of strings
8 | */
9 | export function generateUniversalLanguage(
10 | tokens: string[],
11 | maxTokenLength: number,
12 | startTokenLength?: number,
13 | // eslint-disable-next-line
14 | cb?: (inputTokens: string[]) => void
15 | ) {
16 | // An array to store all the generated strings
17 | // there is no point in using a set for two reasons
18 | // 1. Each string would be unique, the algorithm ensures that, ofc depending on the token
19 | // 2. Set would throw an error if we store huge number of items in it
20 | const generatedStrings: Array> = [];
21 |
22 | // Recursive function to generate all possible combinations
23 | function generateAllKLength(generatedTokens: string[], tokensLength: number) {
24 | // Base case, if we dont have any more token to use
25 | // Call the callback and pass the generated token
26 | if (tokensLength === 0) {
27 | if (cb) {
28 | cb(generatedTokens);
29 | }
30 | // Push the generated tokens to the array
31 | generatedStrings.push(generatedTokens);
32 | return;
33 | }
34 | // Loop through all the tokens and recursively call the function decreasing the token length
35 | for (let i = 0; i < tokens.length; i += 1) {
36 | generateAllKLength(generatedTokens.concat(tokens[i]), tokensLength - 1);
37 | }
38 | }
39 |
40 | // Generate all possible combinations of token from $startTokenLength to $maxTokenLength
41 | for (let length = startTokenLength ?? 1; length <= maxTokenLength; length += 1) {
42 | generateAllKLength([], length);
43 | }
44 |
45 | return generatedStrings;
46 | }
47 |
--------------------------------------------------------------------------------
/packages/cfg/libs/generateVariableReferenceRecord.ts:
--------------------------------------------------------------------------------
1 | import { IContextFreeGrammar } from "./types";
2 |
3 | export interface VariableReferenceLocation {
4 | // The production variable name
5 | variable: string,
6 | // Rule number of the production variable
7 | ruleNumber: number,
8 | // Token number of the rule
9 | tokenNumber: number
10 | }
11 |
12 | /**
13 | * Return a record providing details of the location of each variable of the cfg
14 | * @param cfg Input context free grammar
15 | * @returns A record which provides info regarding the location of a variable
16 | */
17 | export default function generateVariableReferenceRecord(cfg: Pick) {
18 | const {variables, productionRules} = cfg;
19 | const variableReferenceLocationRecord: Record = {};
20 |
21 | // Loop through all variables
22 | variables.forEach(targetVariable => {
23 | // Array to keep track of all the locations outer variable is being referenced
24 | const variableReferences: VariableReferenceLocation[] = []
25 | // Loop though all variables again
26 | variables.forEach(productionVariable => {
27 | // Get the rules of the inner variable
28 | const rules = productionRules[productionVariable];
29 | // Loop through each rule
30 | rules.forEach((rule, ruleNumber) => {
31 | // Get the tokens of the rule
32 | const tokens = rule.split(" ");
33 | // Loop through all token
34 | tokens.forEach((token, tokenNumber) => {
35 | // Check if the token is the same as outer variable
36 | if (token === targetVariable) {
37 | variableReferences.push({
38 | variable: productionVariable,
39 | ruleNumber,
40 | tokenNumber
41 | })
42 | }
43 | })
44 | })
45 | })
46 | variableReferenceLocationRecord[targetVariable] = variableReferences;
47 | })
48 |
49 | return variableReferenceLocationRecord;
50 | }
--------------------------------------------------------------------------------
/apps/playground/src/components/Select.tsx:
--------------------------------------------------------------------------------
1 | import styled from "@emotion/styled";
2 | import { MenuItem, Select as MuiSelect, SelectProps as MuiSelectProps } from "@mui/material";
3 | import { ReactNode, SelectHTMLAttributes } from "react";
4 |
5 | interface SelectProps {
6 | value: SelectHTMLAttributes["value"],
7 | onChange: MuiSelectProps["onChange"],
8 | valueLabelRecord: Record
9 | renderValue?: (value: string | number) => ReactNode
10 | className?: string
11 | menuItemRender?: (item: string | number) => ReactNode
12 | }
13 |
14 | const StyledMuiSelect = styled(MuiSelect)`
15 | padding-left: ${({theme}) => theme.spacing(0.5)};
16 | border-radius: ${({theme}) => theme.spacing(0.5)};
17 | font-weight: bold;
18 | `;
19 |
20 | const SelectRenderedValue = styled.div`
21 | font-weight: semi-bold;
22 | text-transform: capitalize;
23 | border-radius: ${({theme}) => theme.spacing(0.5)};
24 | `
25 |
26 | export function Select(props: SelectProps) {
27 | const { menuItemRender, renderValue, className = "", valueLabelRecord, value, onChange } = props;
28 | const items = Object.entries(valueLabelRecord);
29 | return
33 | (
34 |
35 | {renderValue ? renderValue(valueToRender as string) : valueLabelRecord[valueToRender as string]}
36 |
37 | )
38 | }
39 | disabled={items.length === 0}
40 | sx={{
41 | "& .MuiSvgIcon-root": {
42 | fill: `white`
43 | },
44 | }}
45 | onChange={onChange}
46 | >
47 | {items.length !== 0 ? items.map(([itemValue, itemLabel]) => (
48 |
55 | {menuItemRender ? menuItemRender(itemValue) : itemLabel}
56 |
57 | )) : null}
58 |
59 | }
--------------------------------------------------------------------------------
/packages/cfg/tests/cykParse.test.ts:
--------------------------------------------------------------------------------
1 | import { cykParse } from '../libs/cykParse';
2 |
3 | describe('cykParse', () => {
4 | it(`Basic CYK parsing`, () => {
5 | const cykParseResult = cykParse(
6 | {
7 | startVariable: 'S',
8 | productionRules: {
9 | S: ['A B', 'B'],
10 | A: ['B A', 'a'],
11 | B: ['A', 'b'],
12 | },
13 | },
14 | 'b a b'.split(' ')
15 | );
16 | expect(cykParseResult).toStrictEqual({
17 | verdict: true,
18 | cykTableDetailed: [
19 | [
20 | {
21 | combinations: [
22 | {
23 | merged: ['A B'],
24 | parts: [['A'], ['B']],
25 | },
26 | {
27 | merged: ['B S'],
28 | parts: [['B'], ['S']],
29 | },
30 | ],
31 | value: ['S'],
32 | },
33 | ],
34 | [
35 | {
36 | combinations: [
37 | {
38 | merged: ['B A'],
39 | parts: [['B'], ['A']],
40 | },
41 | ],
42 | value: ['A'],
43 | },
44 | {
45 | combinations: [
46 | {
47 | merged: ['A B'],
48 | parts: [['A'], ['B']],
49 | },
50 | ],
51 | value: ['S'],
52 | },
53 | ],
54 | [
55 | {
56 | combinations: [
57 | {
58 | merged: ['B'],
59 | parts: [['B']],
60 | },
61 | ],
62 | value: ['B'],
63 | },
64 | {
65 | combinations: [
66 | {
67 | merged: ['A'],
68 | parts: [['A']],
69 | },
70 | ],
71 | value: ['A'],
72 | },
73 | {
74 | combinations: [
75 | {
76 | merged: ['B'],
77 | parts: [['B']],
78 | },
79 | ],
80 | value: ['B'],
81 | },
82 | ],
83 | ],
84 | cykTable: [['S'], ['A', 'S'], ['B', 'A', 'B']],
85 | nodeVariablesRecord: {
86 | 'A B': ['S'],
87 | B: ['S'],
88 | 'B A': ['A'],
89 | a: ['A'],
90 | A: ['B'],
91 | b: ['B'],
92 | },
93 | sentenceTokens: ['b', 'a', 'b'],
94 | });
95 | });
96 | });
97 |
--------------------------------------------------------------------------------
/packages/cfg/libs/validateCfg.ts:
--------------------------------------------------------------------------------
1 | import { IContextFreeGrammar } from './types';
2 |
3 | /**
4 | * Validates a cfg
5 | * @param cfg Input cfg to validate
6 | */
7 | export function validateCfg(cfg: IContextFreeGrammar) {
8 | const { startVariable, terminals, productionRules, variables } = cfg;
9 | // Check if all the variables is present in transition record
10 | const productionRulesEntries = Object.entries(productionRules);
11 | if (productionRulesEntries.length !== variables.length) {
12 | throw new Error('All variables must be present in the transition record');
13 | }
14 | const terminalsSet = new Set(terminals);
15 | const variablesSet = new Set(variables);
16 | // Validate that all the keys of transition record are variables
17 | productionRulesEntries.forEach(([productionRuleVariable, productionRuleSubstitutions]) => {
18 | if (!variablesSet.has(productionRuleVariable)) {
19 | throw new Error(
20 | `Transition record contains a variable ${productionRuleVariable}, that is not present in variables array`
21 | );
22 | }
23 | // Check if all the substitutions contain either variable or terminal
24 | productionRuleSubstitutions.forEach((productionRuleSubstitution) => {
25 | const tokens = productionRuleSubstitution.split(' ');
26 | for (let index = 0; index < tokens.length; index += 1) {
27 | const productionRuleSubstitutionToken = tokens[index];
28 | // Check if the letter is a terminal
29 | const isTerminal = terminalsSet.has(productionRuleSubstitutionToken);
30 | // Check if the letter is a variable
31 | const isVariable = variablesSet.has(productionRuleSubstitutionToken);
32 |
33 | if (!isTerminal && !isVariable) {
34 | throw new Error(
35 | `Transition record substitution chunk ${productionRuleSubstitutionToken} is neither a variable nor a terminal`
36 | );
37 | }
38 | }
39 | });
40 | });
41 | // Check if the starting variable is part of variables
42 | if (!variablesSet.has(startVariable)) {
43 | throw new Error(`Starting variable must be part of variables array`);
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/packages/fa/tests/FiniteAutomaton/generatePostNormalizationErrors.test.ts:
--------------------------------------------------------------------------------
1 | import { generatePostNormalizationErrors } from '../../libs/FiniteAutomaton/generatePostNormalizationErrors';
2 |
3 | describe('generatePostNormalizationErrors', () => {
4 | it(`Using valid dfa`, () => {
5 | const postNormalizationErrors = generatePostNormalizationErrors({
6 | alphabets: ['a', 'b'],
7 | final_states: ['0', '1'],
8 | label: 'DFA',
9 | start_state: '0',
10 | states: ['0', '1', '2'],
11 | transitions: {
12 | 0: {
13 | a: ['1'],
14 | b: ['2'],
15 | },
16 | 1: {
17 | a: ['2'],
18 | b: ['1'],
19 | },
20 | 2: {
21 | a: ['2'],
22 | b: ['2'],
23 | },
24 | },
25 | epsilon_transitions: null,
26 | });
27 | expect(postNormalizationErrors.length).toStrictEqual(0);
28 | });
29 |
30 | it(`Using invalid dfa`, () => {
31 | const postNormalizationErrors = generatePostNormalizationErrors({
32 | alphabets: ['a', 'b'],
33 | final_states: ['0', '3'],
34 | label: 'DFA',
35 | start_state: '0',
36 | states: ['0', '1', '2'],
37 | transitions: {
38 | 0: {
39 | a: ['1'],
40 | b: ['2'],
41 | },
42 | 1: {
43 | a: ['2'],
44 | b: ['1'],
45 | },
46 | 2: {
47 | a: ['2'],
48 | b: ['2'],
49 | },
50 | 3: {
51 | c: ['4'],
52 | },
53 | },
54 | epsilon_transitions: {
55 | 1: ['2'],
56 | 2: ['0', '3'],
57 | 3: ['1'],
58 | },
59 | });
60 | expect(postNormalizationErrors).toStrictEqual([
61 | `Automaton final_states must reference a state (3) that is present in states`,
62 | `Epsilon transitions state 2 must reference a state 3 that is present in states`,
63 | `Epsilon transitions state 3 must reference a state that is present in states`,
64 | `Automaton transitions (3) must reference a state that is present in states`,
65 | `Automaton transitions symbol (c), must reference a valid alphabet`,
66 | `Automaton transitions value (4) when a tuple, must reference a valid state`,
67 | ]);
68 | });
69 | });
70 |
--------------------------------------------------------------------------------
/packages/testing/libs/utils/generateRandomLanguage.ts:
--------------------------------------------------------------------------------
1 | import { generateRandomNumber } from './generateRandomNumber';
2 |
3 | /**
4 | * Generates an array of unique random strings over a given tokens
5 | * @param total Total unique random strings
6 | * @param tokens Alphabet of the random strings
7 | * @param minTokenLength Minimum length of each string
8 | * @param maxTokenLength Maximum length of each string
9 | * @param initialInputStrings Initial array of strings
10 | * @returns An array of unique random strings
11 | */
12 | export function generateRandomLanguage(
13 | total: number,
14 | tokens: string[],
15 | minTokenLength: number,
16 | maxTokenLength: number,
17 | initialInputStrings?: string[][]
18 | ) {
19 | // Using a set to store only unique input strings
20 | const generatedStringsSet: Set = new Set(
21 | initialInputStrings?.map((initialInputString) => initialInputString.join(' ')) ?? []
22 | );
23 |
24 | // WARN This could potentially cause an infinite loop,
25 | // While we haven't reached total tokens equal to total
26 | while (generatedStringsSet.size < total) {
27 | // Generate a random number for the token length
28 | const tokenLength = generateRandomNumber(minTokenLength, maxTokenLength);
29 | const generatedString: string[] = [];
30 | for (let index = 0; index < tokenLength; index += 1) {
31 | // Generate a random token
32 | generatedString.push(tokens[generateRandomNumber(0, tokens.length - 1)]);
33 | }
34 | // Add the generated string by joining the tokens with space
35 | generatedStringsSet.add(generatedString.join(' '));
36 | }
37 | // Using a array of tokens, rather than a set as there is a limit to the number of items a set can contain
38 | // If $total is a very large number using a set would throw an error
39 | const generatedStrings: string[][] = [];
40 | // Convert the set to an array
41 | Array.from(generatedStringsSet).forEach((generatedString) => {
42 | // Generate tokens from the string by splitting it via space
43 | generatedStrings.push(generatedString.split(' '));
44 | });
45 | return generatedStrings;
46 | }
47 |
--------------------------------------------------------------------------------
/packages/cfg/libs/index.ts:
--------------------------------------------------------------------------------
1 | import { convertGrammarToString } from './convertGrammarToString';
2 | import { convertStringToGrammar } from './convertStringToGrammar';
3 | import { convertToCnf } from './convertToCnf';
4 | import { cykParse } from './cykParse';
5 | import { extractTerminalsFromCfg } from './extractTerminalsFromCfg';
6 | import { findFirst } from './findFirst';
7 | import { findFollow } from './findFollow';
8 | import { generateCfgLanguage } from './generateCfgLanguage';
9 | import { generateLL1ParsingTable } from './generateLL1ParsingTable';
10 | import { addDotToProductionRule, generateClosureOfLR0Item, generateLR0ParsingTable } from './generateLR0ParsingTable';
11 | import { parseWithLL1Table } from "./parseWithLL1Table";
12 | import { removeEmptyProduction } from './removeEmptyProduction';
13 | import { removeLeftRecursion } from './removeLeftRecursion';
14 | import { removeNonDeterminism } from './removeNonDeterminism';
15 | import { removeNonTerminableProduction } from './removeNonTerminableProduction';
16 | import { removeNullProduction } from './removeNullProduction';
17 | import { removeUnitProduction } from './removeUnitProduction';
18 | import { removeUnreachableProduction } from './removeUnreachableProduction';
19 | import { removeUselessProduction } from './removeUselessProduction';
20 | import { simplifyCfg } from './simplifyCfg';
21 | import { validateCfg } from './validateCfg';
22 |
23 | // TODO: Create separate classes for grouping modules
24 | // Name-spacing lr0 parser related modules
25 | export const LRO = {
26 | addDotToProductionRule,
27 | generateClosureOfLR0Item,
28 | generateLR0ParsingTable,
29 | }
30 |
31 | export * from './types';
32 | export {
33 | removeLeftRecursion,
34 | removeNonDeterminism,
35 | parseWithLL1Table,
36 | generateLL1ParsingTable,
37 | findFollow,
38 | convertGrammarToString,
39 | convertStringToGrammar,
40 | convertToCnf,
41 | cykParse,
42 | generateCfgLanguage,
43 | removeEmptyProduction,
44 | removeNonTerminableProduction,
45 | removeNullProduction,
46 | removeUnitProduction,
47 | removeUnreachableProduction,
48 | removeUselessProduction,
49 | extractTerminalsFromCfg,
50 | simplifyCfg,
51 | validateCfg,
52 | findFirst
53 | };
54 |
55 |
--------------------------------------------------------------------------------
/packages/fa/tests/FiniteAutomaton/generatePreNormalizationErrors.test.ts:
--------------------------------------------------------------------------------
1 | import { generatePreNormalizationErrors } from '../../libs/FiniteAutomaton/generatePreNormalizationErrors';
2 |
3 | describe('generatePreNormalizationErrors', () => {
4 | it(`Using valid dfa`, () => {
5 | const preNormalizationErrors = generatePreNormalizationErrors(() => true, 'deterministic', {
6 | alphabets: ['a', 'b'],
7 | final_states: [0, 1],
8 | label: 'DFA',
9 | start_state: 0,
10 | states: [0, 1, 2],
11 | transitions: {
12 | 0: [1, 2],
13 | 1: [2, 1],
14 | 2: 'loop',
15 | },
16 | });
17 | expect(preNormalizationErrors.length).toBe(0);
18 | });
19 |
20 | it(`Using invalid dfa without any properties`, () => {
21 | const preNormalizationErrors = generatePreNormalizationErrors(
22 | undefined as any,
23 | 'deterministic',
24 | {
25 | epsilon_transitions: {},
26 | } as any
27 | );
28 | expect(preNormalizationErrors).toStrictEqual([
29 | `testLogic function is required in automaton`,
30 | 'Automaton label is required',
31 | 'Automaton transitions is required',
32 | 'Automaton states is required',
33 | 'Automaton alphabets is required',
34 | `Deterministic automaton can't contain epsilon transitions`,
35 | 'Automaton start_state is required',
36 | 'Automaton final_states is required',
37 | 'Automaton states must be an array of length > 0',
38 | 'Automaton alphabets must be an array of length > 0',
39 | 'Automaton final_states must be an array of length > 0',
40 | ]);
41 | });
42 |
43 | it(`Using invalid dfa with properties`, () => {
44 | const preNormalizationErrors = generatePreNormalizationErrors(() => true, 'deterministic', {
45 | states: 5,
46 | alphabets: 5,
47 | final_states: 5,
48 | start_state: 0,
49 | label: 'DFA',
50 | transitions: {},
51 | } as any);
52 | expect(preNormalizationErrors).toStrictEqual([
53 | 'Automaton alphabets must be an array',
54 | 'Automaton states must be an array',
55 | 'Automaton final_states must be an array',
56 | 'Automaton states must be an array of length > 0',
57 | 'Automaton alphabets must be an array of length > 0',
58 | 'Automaton final_states must be an array of length > 0',
59 | ]);
60 | });
61 | });
62 |
--------------------------------------------------------------------------------
/docs/src/pages/index.tsx:
--------------------------------------------------------------------------------
1 | import Head from "@docusaurus/Head";
2 | import Link from '@docusaurus/Link';
3 | import useBaseUrl from '@docusaurus/useBaseUrl';
4 | import useDocusaurusContext from '@docusaurus/useDocusaurusContext';
5 | import Layout from '@theme/Layout';
6 | import clsx from 'clsx';
7 | import React from 'react';
8 | import HomepageFeatures from "../components/HomepageFeatures";
9 | import styles from './index.module.css';
10 |
11 | function Feature({ imageUrl, title, description }) {
12 | const imgUrl = useBaseUrl(imageUrl);
13 | return (
14 |
15 |
16 |
{title}
17 |
{description}
18 |
19 | );
20 | }
21 |
22 | function Home() {
23 | const context = useDocusaurusContext();
24 | const { siteConfig } = context;
25 | return (
26 |
29 |
30 |
31 |
32 |
33 |
52 |
53 |
54 |
55 |
56 | );
57 | }
58 |
59 | export default Home;
60 |
--------------------------------------------------------------------------------
/packages/fa/libs/types.ts:
--------------------------------------------------------------------------------
1 | export interface InputFiniteAutomaton {
2 | // Append a string to all the states
3 | append?: string;
4 | alphabets: (string | number)[];
5 | label: string;
6 | description?: string;
7 | start_state: string | number;
8 | final_states: (string | number)[];
9 | states: (string | number)[];
10 | // each key of transitions indicate a state
11 | transitions: Record<
12 | string | number,
13 | (Array | (string | number) | null)[] | 'loop'
14 | >;
15 | epsilon_transitions?: Record;
16 | }
17 |
18 | export interface TransformedFiniteAutomaton {
19 | append?: string;
20 | alphabets: string[];
21 | label: string;
22 | description?: string;
23 | start_state: string;
24 | final_states: string[];
25 | states: string[];
26 | // each key of transitions indicate a state, which in turn represents alphabets
27 | transitions: Record>;
28 | epsilon_transitions: null | Record;
29 | }
30 |
31 | // eslint-disable-next-line
32 | export type IAutomatonTestLogicFn = (inputString: string, automatonTestResult: boolean) => boolean;
33 | // eslint-disable-next-line
34 | export type IAutomatonTestFn = (inputString: string) => boolean;
35 | export interface IFiniteAutomaton {
36 | testLogic: IAutomatonTestLogicFn;
37 | automaton: TransformedFiniteAutomaton;
38 | automatonId: string;
39 | }
40 |
41 | export interface IAutomatonInfo {
42 | test: IAutomatonTestFn;
43 | testLogic: IAutomatonTestLogicFn;
44 | automaton: {
45 | label: string;
46 | alphabets: string[];
47 | description?: string;
48 | };
49 | }
50 |
51 | export type TFiniteAutomatonType = 'deterministic' | 'non-deterministic' | 'epsilon';
52 | export interface GraphNode {
53 | name: string;
54 | state: string;
55 | symbol: null | string;
56 | children: GraphNode[];
57 | depth: number;
58 | string: string;
59 | }
60 |
61 | export type GeneratedAutomatonOptions = Partial<
62 | Pick['automaton'], 'label' | 'description'> & {
63 | separator: string;
64 | }
65 | >;
66 | export type TMergeOperation = 'or' | 'and' | 'not';
67 |
68 | export interface SkipOptions {
69 | skipValidation: boolean;
70 | skipNormalization: boolean;
71 | skipCharacterRangesExpansion: boolean;
72 | }
73 |
--------------------------------------------------------------------------------
/packages/cfg/libs/generateLR0ParsingTable.ts:
--------------------------------------------------------------------------------
1 | import { IContextFreeGrammar, IContextFreeGrammarInput } from "./types";
2 | import { populateCfg } from "./utils/populateCfg";
3 |
4 | const dotSymbol = "$.$"
5 |
6 | export function addDotToProductionRule (productionRules: IContextFreeGrammar["productionRules"], productionVariable: string) {
7 | const rulesForVariable = productionRules[productionVariable];
8 | // Loop through all the rules for the variable
9 | rulesForVariable.forEach((rule, ruleIndex) => {
10 | // Add the dot symbol to the left
11 | rulesForVariable[ruleIndex] = `${dotSymbol} ${rule}`
12 | });
13 | }
14 |
15 | export function generateClosureOfLR0Item(cfg: Omit, productionVariable: string) {
16 | const {productionRules, variables} = cfg;
17 | // Creating a set of variables for faster membership lookup
18 | const variablesSet = new Set(variables);
19 |
20 | const rules = productionRules[productionVariable];
21 | rules.forEach(rule => {
22 | const tokens = rule.split(" ");
23 | for (let tokenNumber = 0; tokenNumber < tokens.length - 1; tokenNumber+=1) {
24 | const token = tokens[tokenNumber];
25 | // We wont overflow the tokens array so its safe
26 | const nextToken = tokens[tokenNumber + 1];
27 | // Using $.$ as its a lot less common than regular .
28 | // Check if the next token is a variable
29 | if (token === dotSymbol && variablesSet.has(nextToken)) {
30 | // Add dotSymbol to the left of all the substitution for the variable
31 | addDotToProductionRule(productionRules, nextToken);
32 | // Generate closure of the next variable
33 | generateClosureOfLR0Item(cfg, nextToken)
34 | }
35 | }
36 | })
37 | }
38 |
39 | export function augmentCfg(inputCfg: IContextFreeGrammarInput) {
40 | const cfg = populateCfg(inputCfg);
41 | const { productionRules, startVariable, variables } = cfg;
42 | // Create the augmented grammar
43 | const newStartVariable = `${startVariable}'`;
44 | variables.unshift(newStartVariable);
45 | productionRules[newStartVariable] = [startVariable]
46 | cfg.startVariable = newStartVariable
47 | return cfg;
48 | }
49 |
50 | export function generateLR0ParsingTable(inputCfg: IContextFreeGrammarInput) {
51 | augmentCfg(inputCfg);
52 | }
--------------------------------------------------------------------------------
/packages/fa/tests/NonDeterministicFiniteAutomaton/convertToDeterministicFiniteAutomaton.test.ts:
--------------------------------------------------------------------------------
1 | import { convertToDeterministicFiniteAutomaton } from '../../libs/NonDeterministicFiniteAutomaton/convertToDeterministicFiniteAutomaton';
2 |
3 | describe('convertToDeterministicFiniteAutomaton', () => {
4 | it(`Converting regular nfa to dfa`, () => {
5 | const convertedDfa = convertToDeterministicFiniteAutomaton(
6 | {
7 | start_state: 'q0',
8 | alphabets: ['a', 'b'],
9 | final_states: ['q1'],
10 | label: 'sample nfa',
11 | states: ['q0', 'q1', 'q2'],
12 | transitions: {
13 | q0: {
14 | a: ['q2', 'q1'],
15 | },
16 | q2: {
17 | a: ['q2', 'q1'],
18 | b: ['q2'],
19 | },
20 | },
21 | epsilon_transitions: null,
22 | },
23 | {
24 | description: 'New Description',
25 | label: 'New label',
26 | separator: '-',
27 | }
28 | );
29 | expect(convertedDfa).toStrictEqual({
30 | epsilon_transitions: null,
31 | alphabets: ['a', 'b'],
32 | final_states: ['q1-q2'],
33 | start_state: 'q0',
34 | states: ['q0', 'q1-q2', 'Ø', 'q2'],
35 | transitions: {
36 | q0: [['q1-q2'], ['Ø']],
37 | 'q1-q2': [['q1-q2'], ['q2']],
38 | q2: [['q1-q2'], ['q2']],
39 | Ø: ['Ø', 'Ø'],
40 | },
41 | description: 'New Description',
42 | label: 'New label',
43 | });
44 | });
45 |
46 | it(`Converting regular e-nfa to dfa`, () => {
47 | const convertedDfa = convertToDeterministicFiniteAutomaton({
48 | start_state: 'q0',
49 | alphabets: ['a', 'b'],
50 | final_states: ['q1'],
51 | label: 'sample nfa',
52 | states: ['q0', 'q1', 'q2'],
53 | transitions: {
54 | q0: {
55 | a: ['q2', 'q1'],
56 | },
57 | q2: {
58 | a: ['q2', 'q1'],
59 | b: ['q2'],
60 | },
61 | },
62 | epsilon_transitions: {
63 | q0: ['q1'],
64 | },
65 | });
66 | expect(convertedDfa).toStrictEqual({
67 | description: undefined,
68 | epsilon_transitions: null,
69 | alphabets: ['a', 'b'],
70 | final_states: ['q0,q1', 'q1,q2'],
71 | label: 'sample nfa',
72 | start_state: 'q0,q1',
73 | states: ['q0,q1', 'q1,q2', 'Ø', 'q2'],
74 | transitions: {
75 | 'q0,q1': [['q1,q2'], ['Ø']],
76 | 'q1,q2': [['q1,q2'], ['q2']],
77 | q2: [['q1,q2'], ['q2']],
78 | Ø: ['Ø', 'Ø'],
79 | },
80 | });
81 | });
82 | });
83 |
--------------------------------------------------------------------------------
/packages/cfg/libs/removeUnreachableProduction.ts:
--------------------------------------------------------------------------------
1 | import { LinkedList } from '@datastructures-js/linked-list';
2 | import { IContextFreeGrammarInput } from './types';
3 | import { populateCfg } from './utils/populateCfg';
4 | import { setDifference } from './utils/setOperations';
5 |
6 | /**
7 | * Removes unreachable variables and production of a cfg
8 | * @param cfg Production rules, start variable and variables array of cfg
9 | * @returns A new production rule record and variables with unreachable variable and rules removed
10 | */
11 | export function removeUnreachableProduction(
12 | inputCfg: Pick
13 | ) {
14 | const cfg = populateCfg(inputCfg);
15 | const { productionRules, startVariable, variables } = cfg;
16 |
17 | const variablesSet = new Set(variables);
18 | const unvisitedVariables = new LinkedList();
19 | unvisitedVariables.insertFirst(startVariable);
20 | const visitedVariables: Set = new Set();
21 | visitedVariables.add(startVariable);
22 |
23 | // While we have unvisited variables
24 | while (unvisitedVariables.count()) {
25 | const unvisitedVariable = unvisitedVariables.removeFirst();
26 | // For each of the unvisited variable, check which variables we can reach
27 | productionRules[unvisitedVariable.getValue()]?.forEach((productionRuleSubstitution) => {
28 | const tokens = productionRuleSubstitution.split(' ');
29 | for (let tokenIndex = 0; tokenIndex < tokens.length; tokenIndex += 1) {
30 | const token = tokens[tokenIndex];
31 | // If the letter is a variable, and we haven't visited the variable yet
32 | if (variablesSet.has(token) && !visitedVariables.has(token)) {
33 | visitedVariables.add(token);
34 | unvisitedVariables.insertLast(token);
35 | }
36 | }
37 | });
38 | }
39 | // The difference between the variables set and visited variables set will return the variables which couldn't be reached
40 | const nonReachableVariables = Array.from(setDifference(variablesSet, visitedVariables));
41 |
42 | // Delete the production rules for each of the unreachable variables
43 | nonReachableVariables.forEach((nonReachableVariable) => {
44 | delete productionRules[nonReachableVariable];
45 | });
46 | // There is no need to update the production rules as unreachable variables are not referenced there
47 | return Array.from(visitedVariables);
48 | }
49 |
--------------------------------------------------------------------------------
/packages/testing/package-lock.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@fauton/testing",
3 | "version": "0.0.1",
4 | "lockfileVersion": 1,
5 | "requires": true,
6 | "dependencies": {
7 | "ansi-regex": {
8 | "version": "5.0.1",
9 | "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
10 | "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="
11 | },
12 | "cli-progress": {
13 | "version": "3.9.1",
14 | "resolved": "https://registry.npmjs.org/cli-progress/-/cli-progress-3.9.1.tgz",
15 | "integrity": "sha512-AXxiCe2a0Lm0VN+9L0jzmfQSkcZm5EYspfqXKaSIQKqIk+0hnkZ3/v1E9B39mkD6vYhKih3c/RPsJBSwq9O99Q==",
16 | "requires": {
17 | "colors": "^1.1.2",
18 | "string-width": "^4.2.0"
19 | }
20 | },
21 | "colors": {
22 | "version": "1.4.0",
23 | "resolved": "https://registry.npmjs.org/colors/-/colors-1.4.0.tgz",
24 | "integrity": "sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA=="
25 | },
26 | "emoji-regex": {
27 | "version": "8.0.0",
28 | "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
29 | "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="
30 | },
31 | "is-fullwidth-code-point": {
32 | "version": "3.0.0",
33 | "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
34 | "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg=="
35 | },
36 | "string-width": {
37 | "version": "4.2.3",
38 | "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
39 | "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
40 | "requires": {
41 | "emoji-regex": "^8.0.0",
42 | "is-fullwidth-code-point": "^3.0.0",
43 | "strip-ansi": "^6.0.1"
44 | }
45 | },
46 | "strip-ansi": {
47 | "version": "6.0.1",
48 | "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
49 | "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
50 | "requires": {
51 | "ansi-regex": "^5.0.1"
52 | }
53 | }
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/packages/cfg/libs/generateLL1ParsingTable.ts:
--------------------------------------------------------------------------------
1 | import { findFollow } from "./findFollow";
2 | import { IContextFreeGrammarInput } from "./types";
3 | import { populateCfg } from "./utils/populateCfg";
4 |
5 | /**
6 | * Generate LL1 Parsing table
7 | * @param inputCfg Input context free grammar
8 | * @returns A record for LL1 Parsed table and whether the grammar is LL1 parsable
9 | */
10 | export function generateLL1ParsingTable(inputCfg: IContextFreeGrammarInput) {
11 | const cfg = populateCfg(inputCfg);
12 | // Find the first and follow record of cfg
13 | const followRecord = findFollow(cfg);
14 | let isParsable = true;
15 | const {variables} = cfg;
16 |
17 | // First key for variable, 2nd key for terminal and value is rule number or null
18 | const llRecord: Record> = {};
19 |
20 | // Populating the table
21 | // All variables as the row
22 | // All terminals as the column
23 | variables.forEach(variable => {
24 | llRecord[variable] = {};
25 | })
26 |
27 | // Loop through all the first record entries
28 | Object.entries(followRecord.first).forEach(([productionVariable, firstRecord]) => {
29 | // Substitutions is an array which contains first(substitution)
30 | firstRecord.substitutions.forEach((firstTokensForSubstitution, ruleNumber) => {
31 | // Loop through all the first token for the substitution
32 | firstTokensForSubstitution.forEach(firstTokenForSubstitution => {
33 | // If its epsilon we need to get the followed tokens of this variable
34 | if (firstTokenForSubstitution === "") {
35 | // Loop through each followed token and assign the rule number
36 | followRecord.follow[productionVariable].forEach((followedToken) => {
37 | if (followedToken in llRecord[productionVariable]) {
38 | isParsable = false;
39 | }
40 | llRecord[productionVariable][followedToken] = ruleNumber
41 | })
42 | } else {
43 | // TODO: No tests reaches this path, maybe its not required?
44 | // if (firstTokenForSubstitution in llRecord[productionVariable]) {
45 | // isParsable = false;
46 | // }
47 | llRecord[productionVariable][firstTokenForSubstitution] = ruleNumber
48 | }
49 | })
50 | })
51 | })
52 |
53 | return {
54 | parseTable: llRecord,
55 | isParsable
56 | };
57 | }
--------------------------------------------------------------------------------
/packages/testing/libs/utils/createFileWriteStreams.ts:
--------------------------------------------------------------------------------
1 | import fs from 'fs';
2 | import path from 'path';
3 | import { IOutputFiles } from '../types';
4 |
5 | type IWriteStreams = Record<`${keyof IOutputFiles}WriteStream`, null | fs.WriteStream>;
6 |
7 | /**
8 | * Create file write streams for logging various info regarding the automaton test
9 | * @param logsPath Path to the log directory
10 | * @param automatonLabel Label of the automaton
11 | * @param outputFiles A record that represents which files to generate
12 | * @returns A record of write streams and a function to end all streams
13 | */
14 | export function createFileWriteStreams(
15 | logsPath: string,
16 | automatonLabel: string,
17 | outputFiles?: Partial
18 | ) {
19 | // Create the write stream record
20 | // Each key would represent which sort of file it is and the value would be the write stream itself
21 | // Not all write stream need to be created, so only create those that are necessary
22 | const writeStreamsRecord: IWriteStreams = {
23 | caseWriteStream: outputFiles?.case
24 | ? fs.createWriteStream(path.resolve(logsPath, `${automatonLabel}.case.txt`))
25 | : null,
26 | aggregateWriteStream: outputFiles?.aggregate
27 | ? fs.createWriteStream(path.resolve(logsPath, `${automatonLabel}.aggregate.txt`))
28 | : null,
29 | incorrectWriteStream: outputFiles?.incorrect
30 | ? fs.createWriteStream(path.resolve(logsPath, `${automatonLabel}.incorrect.txt`))
31 | : null,
32 | correctWriteStream: outputFiles?.correct
33 | ? fs.createWriteStream(path.resolve(logsPath, `${automatonLabel}.correct.txt`))
34 | : null,
35 | inputWriteStream: outputFiles?.input
36 | ? fs.createWriteStream(path.resolve(logsPath, `${automatonLabel}.input.txt`))
37 | : null,
38 | acceptedWriteStream: outputFiles?.accepted
39 | ? fs.createWriteStream(path.resolve(logsPath, `${automatonLabel}.accepted.txt`))
40 | : null,
41 | rejectedWriteStream: outputFiles?.rejected
42 | ? fs.createWriteStream(path.resolve(logsPath, `${automatonLabel}.rejected.txt`))
43 | : null,
44 | };
45 | return {
46 | record: writeStreamsRecord,
47 | // Loop through all the streams and end them
48 | endStreams() {
49 | Object.values(writeStreamsRecord).forEach((writeStream) => {
50 | // A write stream might not be defined
51 | if (writeStream) {
52 | writeStream.end();
53 | }
54 | });
55 | },
56 | };
57 | }
58 |
--------------------------------------------------------------------------------
/docs/src/theme/Toggle/styles.module.css:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (c) Facebook, Inc. and its affiliates.
3 | *
4 | * This source code is licensed under the MIT license found in the
5 | * LICENSE file in the root directory of this source tree.
6 | */
7 |
8 | .toggle {
9 | touch-action: pan-x;
10 | position: relative;
11 | cursor: pointer;
12 | user-select: none;
13 | -webkit-tap-highlight-color: transparent;
14 | }
15 |
16 | .toggleScreenReader {
17 | border: 0;
18 | clip: rect(0 0 0 0);
19 | height: 1px;
20 | margin: -1px;
21 | overflow: hidden;
22 | position: absolute;
23 | width: 1px;
24 | }
25 |
26 | .toggleDisabled {
27 | cursor: not-allowed;
28 | }
29 |
30 | .toggleTrack {
31 | width: 50px;
32 | height: 24px;
33 | border-radius: 30px;
34 | background-color: #4d4d4d;
35 | transition: all 0.2s ease;
36 | }
37 |
38 | .toggleTrackCheck {
39 | position: absolute;
40 | width: 14px;
41 | height: 10px;
42 | top: 0px;
43 | bottom: 0px;
44 | margin: auto 0;
45 | left: 8px;
46 | opacity: 0;
47 | transition: opacity 0.25s ease;
48 | }
49 |
50 | [data-theme='dark'] .toggle .toggleTrackCheck,
51 | .toggleChecked .toggleTrackCheck {
52 | opacity: 1;
53 | transition: opacity 0.25s ease;
54 | }
55 |
56 | .toggleTrackX {
57 | position: absolute;
58 | width: 10px;
59 | height: 10px;
60 | top: 0px;
61 | bottom: 0px;
62 | margin: auto 0;
63 | right: 10px;
64 | opacity: 1;
65 | transition: opacity 0.25s ease;
66 | }
67 |
68 | [data-theme='dark'] .toggle .toggleTrackX,
69 | .toggleChecked .toggleTrackX {
70 | opacity: 0;
71 | }
72 |
73 | .toggleTrackThumb {
74 | position: absolute;
75 | top: 1px;
76 | left: 1px;
77 | width: 22px;
78 | height: 22px;
79 | border: 1px solid #4d4d4d;
80 | border-radius: 50%;
81 | background-color: #fafafa;
82 | transition: all 0.25s ease;
83 | }
84 |
85 | [data-theme='dark'] .toggle .toggleTrackThumb,
86 | .toggleChecked .toggleTrackThumb {
87 | left: 27px;
88 | }
89 |
90 | .toggleFocused .toggleTrackThumb,
91 | .toggle:hover .toggleTrackThumb {
92 | box-shadow: 0px 0px 2px 3px var(--ifm-color-primary);
93 | }
94 |
95 | .toggle:active:not(.toggleDisabled) .toggleTrackThumb {
96 | box-shadow: 0px 0px 5px 5px var(--ifm-color-primary);
97 | }
98 |
99 | .toggleIcon {
100 | align-items: center;
101 | display: flex;
102 | height: 10px;
103 | justify-content: center;
104 | width: 10px;
105 | }
106 |
--------------------------------------------------------------------------------
/packages/cfg/tests/removeNullProduction.test.ts:
--------------------------------------------------------------------------------
1 | import {
2 | createProductionCombinations,
3 | findNullableVariables,
4 | removeNullProduction,
5 | } from '../libs/removeNullProduction';
6 | import { arrayEquivalency } from './setEquivalency';
7 |
8 | describe('removeNullProduction', () => {
9 | it(`Known variables present in production rules`, () => {
10 | const productionRules = {
11 | Sub: ['Adj Verb Adj Conj'],
12 | Adj: ['a Adj', ''],
13 | Verb: ['b Verb', ''],
14 | Conj: ['c'],
15 | };
16 | removeNullProduction({
17 | productionRules,
18 | variables: ['Sub', 'Adj', 'Verb', 'Conj'],
19 | startVariable: 'Sub',
20 | });
21 |
22 | expect(productionRules).toStrictEqual({
23 | Sub: [
24 | 'Conj',
25 | 'Verb Conj',
26 | 'Adj Conj',
27 | 'Adj Verb Conj',
28 | 'Verb Adj Conj',
29 | 'Adj Adj Conj',
30 | 'Adj Verb Adj Conj',
31 | ],
32 | Adj: ['a', 'a Adj'],
33 | Verb: ['b', 'b Verb'],
34 | Conj: ['c'],
35 | });
36 | });
37 |
38 | it(`Unknown variable in production rules`, () => {
39 | const productionRules = {
40 | Sub: ['Adj', 'Verb', 'Conj'],
41 | Adj: ['an Adj for', '', 'Verb'],
42 | Verb: ['be Verb early', '', 'Conj'],
43 | Conj: ['can Conj do', ''],
44 | };
45 | removeNullProduction({
46 | startVariable: 'Sub',
47 | productionRules,
48 | // Note that Unknown is not present in productionRules
49 | variables: ['Sub', 'Adj', 'Verb', 'Conj', 'Unknown'],
50 | });
51 |
52 | expect(productionRules).toStrictEqual({
53 | Sub: ['Adj', 'Verb', 'Conj'],
54 | Adj: ['an for', 'an Adj for', 'Verb'],
55 | Verb: ['be early', 'be Verb early', 'Conj'],
56 | Conj: ['can do', 'can Conj do'],
57 | });
58 | });
59 | });
60 |
61 | it(`Should create all combinations of production rules`, () => {
62 | expect(createProductionCombinations('Adj be Adj', 'Adj', 'be')).toStrictEqual([
63 | ['Adj be', 'be Adj', 'Adj be Adj'],
64 | false,
65 | 2,
66 | ]);
67 | });
68 |
69 | it(`Should find all nullable variables`, () => {
70 | const productionRules = {
71 | Sub: ['Adj Verb', 'an be can'],
72 | Adj: [''],
73 | Verb: ['Adj', 'be'],
74 | Conj: ['can'],
75 | Det: ['Adj Verb', 'can'],
76 | };
77 | expect(
78 | arrayEquivalency(
79 | findNullableVariables({
80 | productionRules,
81 | variables: ['Sub', 'Adj', 'Verb', 'Conj', 'Det', 'Noun'],
82 | }),
83 | ['Adj', 'Verb', 'Det', 'Sub']
84 | )
85 | ).toBe(true);
86 | });
87 |
--------------------------------------------------------------------------------
/packages/fa/libs/NonDeterministicFiniteAutomaton/index.ts:
--------------------------------------------------------------------------------
1 | import { DeterministicFiniteAutomaton } from '../DeterministicFiniteAutomaton';
2 | import { FiniteAutomaton } from '../FiniteAutomaton';
3 | import {
4 | GeneratedAutomatonOptions,
5 | IAutomatonTestLogicFn,
6 | InputFiniteAutomaton,
7 | SkipOptions,
8 | } from '../types';
9 | import { convertToDeterministicFiniteAutomaton } from './convertToDeterministicFiniteAutomaton';
10 | import { convertToRegularNfa } from './convertToRegularNfa';
11 | import { epsilonClosureOfState } from './epsilonClosureOfState';
12 | import { moveAndEpsilonClosureStateSet } from './moveAndEpsilonClosureStateSet';
13 |
14 | export {
15 | convertToDeterministicFiniteAutomaton,
16 | convertToRegularNfa,
17 | epsilonClosureOfState,
18 | moveAndEpsilonClosureStateSet,
19 | };
20 |
21 | export class NonDeterministicFiniteAutomaton extends FiniteAutomaton {
22 | constructor(
23 | testLogic: IAutomatonTestLogicFn,
24 | automaton: InputFiniteAutomaton,
25 | automatonId?: string,
26 | skipOptions?: Partial
27 | ) {
28 | super(
29 | testLogic,
30 | automaton,
31 | automaton.epsilon_transitions ? 'epsilon' : 'non-deterministic',
32 | automatonId,
33 | skipOptions
34 | );
35 | if (this.automaton.epsilon_transitions) {
36 | this.convertToRegularNfa();
37 | }
38 | }
39 |
40 | convertToRegularNfa() {
41 | convertToRegularNfa(this.automaton);
42 | }
43 |
44 | /**
45 | * Generates a set of states reachable from input state, using depth-first-search algorithm
46 | * @param state state from where to start searching for epsilon closure states
47 | * @returns A set of states reachable from the input state on all epsilon transitions
48 | */
49 | epsilonClosureOfState(state: string) {
50 | return epsilonClosureOfState(this.automaton.epsilon_transitions, state);
51 | }
52 |
53 | // ε-closure(Move_NFA(states, letter))
54 | moveAndEpsilonClosureStateSet(states: string[], symbol: string) {
55 | return moveAndEpsilonClosureStateSet(
56 | this.automaton.transitions,
57 | this.automaton.epsilon_transitions,
58 | states,
59 | symbol
60 | );
61 | }
62 |
63 | convertToDeterministicFiniteAutomaton(dfaOptions?: GeneratedAutomatonOptions) {
64 | return new DeterministicFiniteAutomaton(
65 | this.testLogic,
66 | convertToDeterministicFiniteAutomaton(this.automaton, dfaOptions),
67 | undefined,
68 | {
69 | skipCharacterRangesExpansion: true,
70 | }
71 | );
72 | }
73 | }
74 |
--------------------------------------------------------------------------------
/packages/fa/libs/FiniteAutomaton/index.ts:
--------------------------------------------------------------------------------
1 | /* eslint-disable no-param-reassign, no-console */
2 | import shortid from 'shortid';
3 | import {
4 | IAutomatonTestLogicFn,
5 | InputFiniteAutomaton,
6 | SkipOptions,
7 | TFiniteAutomatonType,
8 | TransformedFiniteAutomaton,
9 | } from '../types';
10 | import { generateParseTreeForString } from './generateParseTreeForString';
11 | import { generatePostNormalizationErrors } from './generatePostNormalizationErrors';
12 | import { generatePreNormalizationErrors } from './generatePreNormalizationErrors';
13 | import { normalize } from './normalize';
14 | import { validate } from './validate';
15 |
16 | export {
17 | generateParseTreeForString,
18 | generatePostNormalizationErrors,
19 | generatePreNormalizationErrors,
20 | normalize,
21 | validate,
22 | };
23 | export class FiniteAutomaton {
24 | testLogic: IAutomatonTestLogicFn;
25 |
26 | automaton: TransformedFiniteAutomaton;
27 |
28 | #automatonId: string;
29 |
30 | #automatonType: TFiniteAutomatonType;
31 |
32 | constructor(
33 | testLogic: IAutomatonTestLogicFn,
34 | finiteAutomaton: InputFiniteAutomaton | TransformedFiniteAutomaton,
35 | automatonType: TFiniteAutomatonType,
36 | automatonId?: string,
37 | skipOptions?: Partial
38 | ) {
39 | this.#automatonType = automatonType;
40 | this.#automatonId = automatonId ?? shortid();
41 | this.testLogic = testLogic;
42 | // Validate the automaton passed before normalizing it
43 | if (!skipOptions?.skipValidation) {
44 | validate(
45 | finiteAutomaton.label,
46 | generatePreNormalizationErrors(testLogic, this.#automatonType, finiteAutomaton)
47 | );
48 | }
49 | if (!skipOptions?.skipNormalization) {
50 | this.automaton = normalize(
51 | finiteAutomaton,
52 | skipOptions?.skipCharacterRangesExpansion ?? false
53 | );
54 | } else {
55 | this.automaton = finiteAutomaton as any;
56 | }
57 |
58 | if (!skipOptions?.skipValidation) {
59 | // Validate the automaton passed after normalizing it
60 | validate(
61 | finiteAutomaton.label,
62 | generatePostNormalizationErrors(this.automaton as TransformedFiniteAutomaton)
63 | );
64 | }
65 | }
66 |
67 | getAutomatonId() {
68 | return this.#automatonId;
69 | }
70 |
71 | generateParseTreeForString(inputString: string) {
72 | return generateParseTreeForString(this.automaton, inputString);
73 | }
74 |
75 | test(inputString: string) {
76 | return this.generateParseTreeForString(inputString).verdict;
77 | }
78 | }
79 |
--------------------------------------------------------------------------------
/packages/regex/libs/utils/generateRegexTree.ts:
--------------------------------------------------------------------------------
1 | /* eslint-disable no-use-before-define */
2 |
3 | import { BinaryRegexNode, BinaryRegexOperators, BinaryRegexOperatorsName, RegexNode, UnaryRegexNode, UnaryRegexOperators, UnaryRegexOperatorsName } from '../types';
4 |
5 | const RegexOperatorKeywordRecord: Record = {
6 | ".": "Concat",
7 | "|": "Or",
8 | "*": "Kleene",
9 | "+": "Plus",
10 | "?": "Optional"
11 | };
12 |
13 | // Set of binary regex operators
14 | const binaryRegexOperators = new Set('.|');
15 | // Set of unary regex operators
16 | const unaryRegexOperators = new Set('*+?');
17 |
18 | export function generateRegexTreeBinaryOperatorNode(regexString: string): [RegexNode, string] {
19 | const [rightNode, regex1] = generateRegexTree(regexString.slice(0, regexString.length - 1));
20 | const [leftNode, regex2] = generateRegexTree(regex1);
21 |
22 | return [
23 | {
24 | operator: RegexOperatorKeywordRecord[regexString[regexString.length - 1] as BinaryRegexOperators],
25 | operands: [leftNode, rightNode],
26 | } as BinaryRegexNode,
27 | regex2,
28 | ];
29 | }
30 |
31 | export function generateRegexTreeUnaryOperatorNode(regexString: string): [RegexNode, string] {
32 | const [childNodes, regex] = generateRegexTree(regexString.slice(0, regexString.length - 1));
33 | return [
34 | {
35 | operator: RegexOperatorKeywordRecord[regexString[regexString.length - 1] as UnaryRegexOperators],
36 | operands: [childNodes],
37 | } as UnaryRegexNode,
38 | regex,
39 | ];
40 | }
41 |
42 | export function generateRegexTreeLiteralNode(regexString: string): [RegexNode, string] {
43 | return [
44 | {
45 | operator: 'Literal',
46 | operands: [regexString[regexString.length - 1]],
47 | },
48 | regexString.slice(0, regexString.length - 1),
49 | ];
50 | }
51 |
52 | export function generateRegexTree(postfixRegexString: string) {
53 | const lastSymbolInPostfixRegexString = postfixRegexString[postfixRegexString.length - 1];
54 | // Checking for binary operators
55 | if (binaryRegexOperators.has(lastSymbolInPostfixRegexString)) {
56 | return generateRegexTreeBinaryOperatorNode(postfixRegexString);
57 | }
58 | // Checking for unary operators
59 | else if (unaryRegexOperators.has(lastSymbolInPostfixRegexString)) {
60 | return generateRegexTreeUnaryOperatorNode(postfixRegexString);
61 | }
62 | // Checking for regular literals
63 | return generateRegexTreeLiteralNode(postfixRegexString);
64 | }
65 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Logs
2 | logs
3 | *.log
4 | npm-debug.log*
5 | yarn-debug.log*
6 | yarn-error.log*
7 | lerna-debug.log*
8 | .pnpm-debug.log*
9 |
10 | # Diagnostic reports (https://nodejs.org/api/report.html)
11 | report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
12 |
13 | # Runtime data
14 | pids
15 | *.pid
16 | *.seed
17 | *.pid.lock
18 |
19 | # Directory for instrumented libs generated by jscoverage/JSCover
20 | lib-cov
21 |
22 | # Coverage directory used by tools like istanbul
23 | coverage
24 | *.lcov
25 |
26 | # nyc test coverage
27 | .nyc_output
28 |
29 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
30 | .grunt
31 |
32 | # Bower dependency directory (https://bower.io/)
33 | bower_components
34 |
35 | # node-waf configuration
36 | .lock-wscript
37 |
38 | # Compiled binary addons (https://nodejs.org/api/addons.html)
39 | build/Release
40 |
41 | # Dependency directories
42 | node_modules/
43 | jspm_packages/
44 |
45 | # Snowpack dependency directory (https://snowpack.dev/)
46 | web_modules/
47 |
48 | # TypeScript cache
49 | *.tsbuildinfo
50 |
51 | # Optional npm cache directory
52 | .npm
53 |
54 | # Optional eslint cache
55 | .eslintcache
56 |
57 | # Microbundle cache
58 | .rpt2_cache/
59 | .rts2_cache_cjs/
60 | .rts2_cache_es/
61 | .rts2_cache_umd/
62 |
63 | # Optional REPL history
64 | .node_repl_history
65 |
66 | # Output of 'npm pack'
67 | *.tgz
68 |
69 | # Yarn Integrity file
70 | .yarn-integrity
71 |
72 | # dotenv environment variables file
73 | .env
74 | .env.test
75 | .env.production
76 |
77 | # parcel-bundler cache (https://parceljs.org/)
78 | .cache
79 | .parcel-cache
80 |
81 | # Next.js build output
82 | .next
83 | out
84 |
85 | # Nuxt.js build / generate output
86 | .nuxt
87 | dist
88 |
89 | # Gatsby files
90 | .cache/
91 | # Comment in the public line in if your project uses Gatsby and not Next.js
92 | # https://nextjs.org/blog/next-9-1#public-directory-support
93 | # public
94 |
95 | # vuepress build output
96 | .vuepress/dist
97 |
98 | # Serverless directories
99 | .serverless/
100 |
101 | # FuseBox cache
102 | .fusebox/
103 |
104 | # DynamoDB Local files
105 | .dynamodb/
106 |
107 | # TernJS port file
108 | .tern-port
109 |
110 | # Stores VSCode versions used for testing VSCode extensions
111 | .vscode-test
112 |
113 | # yarn v2
114 | .yarn/cache
115 | .yarn/unplugged
116 | .yarn/build-state.yml
117 | .yarn/install-state.gz
118 | .pnp.*
119 |
120 | build
121 | old
122 | *.zip
123 |
124 | eslint.txt
125 | experiment
126 | dfa
127 | todo.md
128 |
129 | .vscode
--------------------------------------------------------------------------------
/packages/fa/libs/FiniteAutomaton/generateParseTreeForString.ts:
--------------------------------------------------------------------------------
1 | import { GraphNode, TransformedFiniteAutomaton } from '../types';
2 |
3 | /**
4 | * Generate parse tree for a given automaton and input string
5 | * @param automaton Automaton to generate parse tree from
6 | * @param inputString Input string to parse
7 | * @returns A parse tree, verdict on whether the string is accepted and the leaf nodes
8 | */
9 | export function generateParseTreeForString(
10 | automaton: Pick,
11 | // TODO: This should be an array of tokens
12 | inputString: string
13 | ) {
14 | let currentParents: GraphNode[] = [
15 | {
16 | name: `${automaton.start_state}`,
17 | state: automaton.start_state,
18 | string: '',
19 | depth: 0,
20 | symbol: null,
21 | children: [],
22 | },
23 | ];
24 | let verdict = false;
25 | const finalStates = new Set(automaton.final_states);
26 |
27 | const tree = currentParents;
28 | for (let index = 0; index < inputString.length; index += 1) {
29 | const newParents: GraphNode[] = [];
30 | const symbol = inputString[index];
31 | currentParents.forEach((currentParent) => {
32 | const transitionStateRecord = automaton.transitions[currentParent.state];
33 | if (transitionStateRecord) {
34 | const transitionTargetStates = transitionStateRecord[symbol];
35 | // Guarding against null values
36 | if (Array.isArray(transitionTargetStates)) {
37 | transitionTargetStates.forEach((transitionTargetState) => {
38 | const parentGraphNode = {
39 | name: `${transitionTargetState}(${symbol})`,
40 | state: transitionTargetState,
41 | string: inputString.slice(0, index + 1),
42 | depth: index + 1,
43 | symbol,
44 | children: [],
45 | };
46 | currentParent.children.push(parentGraphNode);
47 | newParents.push(parentGraphNode);
48 | });
49 | }
50 | }
51 | });
52 | // for the Last symbol
53 | if (index === inputString.length - 1) {
54 | // Looping through each of the new parent nodes
55 | // to see if any of their state matches the final state
56 | for (let newParentsIndex = 0; newParentsIndex < newParents.length; newParentsIndex += 1) {
57 | const newChild = newParents[newParentsIndex];
58 | if (finalStates.has(newChild.state)) {
59 | verdict = true;
60 | break;
61 | }
62 | }
63 | }
64 | currentParents = newParents;
65 | }
66 | return {
67 | verdict,
68 | leafNodes: currentParents,
69 | tree: tree[0],
70 | };
71 | }
72 |
--------------------------------------------------------------------------------