Comparing version 0.0.7 to 0.0.8
@@ -15,3 +15,2 @@ "use strict"; | ||
}); | ||
const finalStateString = automaton.final_states.join(''); | ||
let currentEquivalentStatesGroups = [nonFinalStates, automaton.final_states]; | ||
@@ -41,2 +40,3 @@ let previousEquivalentStatesGroups = []; | ||
}); | ||
const newStates = currentEquivalentStatesGroups.map((currentEquivalentStatesGroup) => currentEquivalentStatesGroup.join('')); | ||
return { | ||
@@ -46,5 +46,8 @@ label: minimizedDfaOptions?.label ?? automaton.label, | ||
description: minimizedDfaOptions?.description ?? automaton.description, | ||
final_states: [finalStateString], | ||
final_states: newStates.filter((newState) => { | ||
const newStateStrings = new Set(newState); | ||
return automaton.final_states.find((finalState) => newStateStrings.has(finalState)); | ||
}), | ||
start_state: newStartState.join(''), | ||
states: currentEquivalentStatesGroups.map((currentEquivalentStatesGroup) => currentEquivalentStatesGroup.join('')), | ||
states: newStates, | ||
transitions: newTransitions, | ||
@@ -51,0 +54,0 @@ epsilon_transitions: null, |
@@ -5,7 +5,13 @@ /// <reference types="node" /> | ||
import { FiniteAutomaton } from '../FiniteAutomaton'; | ||
import { RegularExpression } from '../RegularExpression'; | ||
import * as FiniteAutomataTestUtils from './utils'; | ||
export interface IAutomataTestConfig { | ||
export declare type IAutomataTestConfig = { | ||
automaton: FiniteAutomaton; | ||
regex?: RegularExpression; | ||
options: InputStringOption; | ||
} | ||
} | { | ||
automaton?: FiniteAutomaton; | ||
regex: RegularExpression; | ||
options: InputStringOption; | ||
}; | ||
declare type IWriteStreams = Record<`${keyof IOutputFiles}WriteStream`, null | fs.WriteStream>; | ||
@@ -27,5 +33,5 @@ export declare class FiniteAutomataTest { | ||
}; | ||
testAutomata(finiteAutomaton: FiniteAutomaton, finiteAutomatonTestInfo: FiniteAutomatonTestInfo, writeStreams: IWriteStreams, inputStrings: string[]): void; | ||
testAutomata(finiteAutomaton: FiniteAutomaton | RegularExpression, finiteAutomatonTestInfo: FiniteAutomatonTestInfo, writeStreams: IWriteStreams, inputStrings: string[]): void; | ||
test(configs: { | ||
automaton: FiniteAutomaton; | ||
automaton: FiniteAutomaton | RegularExpression; | ||
options: InputStringOption; | ||
@@ -32,0 +38,0 @@ }[]): Promise<void>; |
import { InputStringOption } from '../../../types'; | ||
import { FiniteAutomaton } from '../../FiniteAutomaton'; | ||
import { RegularExpression } from '../../RegularExpression'; | ||
export declare function test(logsPath: string, configs: { | ||
automaton: FiniteAutomaton; | ||
automaton: FiniteAutomaton | RegularExpression; | ||
options: InputStringOption; | ||
}[], preAutomatonTestCb: (totalInputStrings: number) => void, postAutomatonTestCb: () => void): Promise<void>; |
@@ -51,3 +51,3 @@ "use strict"; | ||
else { | ||
generatedStrings = GenerateString_1.GenerateString.generateAllCombosWithinLength(automaton.automaton.alphabets, options.combo.maxLength); | ||
generatedStrings = GenerateString_1.GenerateString.generateAllCombosWithinLength(automaton.automaton.alphabets, options.combo.maxLength, options.combo.startLength ?? 1); | ||
} | ||
@@ -54,0 +54,0 @@ } |
@@ -5,4 +5,5 @@ /// <reference types="node" /> | ||
import { FiniteAutomaton } from '../../FiniteAutomaton'; | ||
import { RegularExpression } from '../../RegularExpression'; | ||
declare type IWriteStreams = Record<`${keyof IOutputFiles}WriteStream`, null | fs.WriteStream>; | ||
export declare function testAutomaton(finiteAutomaton: FiniteAutomaton, finiteAutomatonTestInfo: FiniteAutomatonTestInfo, writeStreams: IWriteStreams, inputStrings: string[], postAutomatonTestCb?: () => void): void; | ||
export declare function testAutomaton(finiteAutomaton: FiniteAutomaton | RegularExpression, finiteAutomatonTestInfo: FiniteAutomatonTestInfo, writeStreams: IWriteStreams, inputStrings: string[], postAutomatonTestCb?: () => void): void; | ||
export {}; |
@@ -10,3 +10,3 @@ "use strict"; | ||
if (inputString.length !== 0) { | ||
const { automatonTestResult } = finiteAutomaton.generateGraphFromString(inputString); | ||
const automatonTestResult = finiteAutomaton.test(inputString); | ||
const logicTestResult = finiteAutomaton.testLogic(inputString, automatonTestResult); | ||
@@ -13,0 +13,0 @@ const isWrong = automatonTestResult !== logicTestResult; |
@@ -15,3 +15,4 @@ import { IAutomatonTestLogicFn, InputFiniteAutomaton, TFiniteAutomatonType, TransformedFiniteAutomaton } from '../../types'; | ||
}; | ||
test(inputString: string): boolean; | ||
} | ||
export { FiniteAutomatonUtils }; |
@@ -49,3 +49,6 @@ "use strict"; | ||
} | ||
test(inputString) { | ||
return this.generateGraphFromString(inputString).automatonTestResult; | ||
} | ||
} | ||
exports.FiniteAutomaton = FiniteAutomaton; |
export declare class GenerateString { | ||
static generateAllCombosWithinLength(alphabet: string[], maxLength: number, cb?: (generatedString: string) => void): string[]; | ||
static generateAllCombosWithinLength(alphabet: string[], maxLength: number, startLength: number, cb?: (generatedString: string) => void): string[]; | ||
static generateRandomUnique(total: number, alphabet: string[], minLength: number, maxLength: number, initialInputStrings?: string[]): string[]; | ||
} |
@@ -6,3 +6,3 @@ "use strict"; | ||
class GenerateString { | ||
static generateAllCombosWithinLength(alphabet, maxLength, cb) { | ||
static generateAllCombosWithinLength(alphabet, maxLength, startLength, cb) { | ||
const generatedStrings = new Set(); | ||
@@ -20,3 +20,3 @@ function generateAllKLength(generatedString, stringLength) { | ||
} | ||
for (let length = 1; length <= maxLength; length += 1) { | ||
for (let length = startLength; length <= maxLength; length += 1) { | ||
generateAllKLength('', length); | ||
@@ -23,0 +23,0 @@ } |
@@ -7,2 +7,3 @@ import { GenerateString } from './GenerateString'; | ||
export * from './NonDeterministicFiniteAutomaton'; | ||
export * from './RegularExpression'; | ||
export { Render, GenerateString }; |
@@ -22,1 +22,2 @@ "use strict"; | ||
__exportStar(require("./NonDeterministicFiniteAutomaton"), exports); | ||
__exportStar(require("./RegularExpression"), exports); |
@@ -5,40 +5,22 @@ "use strict"; | ||
const epsilonClosureOfState_1 = require("./epsilonClosureOfState"); | ||
const moveAndEpsilonClosureStateSet_1 = require("./moveAndEpsilonClosureStateSet"); | ||
function convertToRegularNfa(automaton) { | ||
const { transitions, states, alphabets, epsilon_transitions: epsilonTransitions } = automaton; | ||
const epsilonClosureOfStateCache = {}; | ||
alphabets.forEach((alphabet) => { | ||
alphabets.forEach((symbol) => { | ||
states.forEach((state) => { | ||
if (epsilonTransitions && epsilonTransitions[state]) { | ||
const firstPhaseStates = !epsilonClosureOfStateCache[state] | ||
const epsilonClosuredStates = !epsilonClosureOfStateCache[state] | ||
? (0, epsilonClosureOfState_1.epsilonClosureOfState)(automaton.epsilon_transitions, state) | ||
: epsilonClosureOfStateCache[state]; | ||
if (!epsilonClosureOfStateCache[state]) { | ||
epsilonClosureOfStateCache[state] = firstPhaseStates; | ||
epsilonClosureOfStateCache[state] = epsilonClosuredStates; | ||
} | ||
const secondPhaseStates = new Set(); | ||
const thirdPhaseSet = new Set(); | ||
firstPhaseStates.forEach((firstPhaseState) => { | ||
if (transitions[firstPhaseState]) { | ||
transitions[firstPhaseState][alphabet]?.forEach((transitionRecordState) => { | ||
secondPhaseStates.add(transitionRecordState); | ||
}); | ||
} | ||
}); | ||
secondPhaseStates.forEach((secondPhaseState) => { | ||
const epsilonClosuredStates = !epsilonClosureOfStateCache[secondPhaseState] | ||
? (0, epsilonClosureOfState_1.epsilonClosureOfState)(automaton.epsilon_transitions, secondPhaseState) | ||
: epsilonClosureOfStateCache[secondPhaseState]; | ||
if (!epsilonClosureOfStateCache[secondPhaseState]) { | ||
epsilonClosureOfStateCache[secondPhaseState] = epsilonClosuredStates; | ||
} | ||
epsilonClosuredStates.forEach((closuredState) => { | ||
thirdPhaseSet.add(closuredState); | ||
}); | ||
}); | ||
const epsilonClosuredStatesForSymbol = (0, moveAndEpsilonClosureStateSet_1.moveAndEpsilonClosureStateSet)(transitions, epsilonTransitions, Array.from(epsilonClosuredStates), symbol); | ||
if (transitions[state]) { | ||
transitions[state][alphabet] = Array.from(thirdPhaseSet); | ||
transitions[state][symbol] = epsilonClosuredStatesForSymbol; | ||
} | ||
else { | ||
transitions[state] = { | ||
[alphabet]: Array.from(thirdPhaseSet), | ||
[symbol]: epsilonClosuredStatesForSymbol, | ||
}; | ||
@@ -45,0 +27,0 @@ } |
@@ -66,2 +66,3 @@ export interface InputFiniteAutomaton { | ||
maxLength: number; | ||
startLength?: number; | ||
}; | ||
@@ -83,1 +84,7 @@ random?: undefined | null; | ||
export declare type TMergeOperation = 'or' | 'and' | 'not'; | ||
export interface IRegularExpression { | ||
alphabets: string[]; | ||
regex: RegExp; | ||
label: string; | ||
description?: string; | ||
} |
@@ -13,8 +13,5 @@ "use strict"; | ||
.on('data', (buffer) => { | ||
let idx = -1; | ||
lineCount = -1; | ||
do { | ||
idx = buffer.indexOf(10, idx + 1); | ||
lineCount += 1; | ||
} while (idx !== -1); | ||
for (let i = 0; i < buffer.length; i += 1) | ||
if (buffer[i] === 10) | ||
lineCount += 1; | ||
}) | ||
@@ -21,0 +18,0 @@ .on('end', () => { |
{ | ||
"name": "fauton", | ||
"version": "0.0.7", | ||
"version": "0.0.8", | ||
"description": "A library to test any finite automaton with arbitrary alphabets", | ||
@@ -79,2 +79,2 @@ "main": "dist/index.js", | ||
} | ||
} | ||
} |
122
readme.md
@@ -13,3 +13,3 @@ <div align="center"> <h1>Fauton</h1> </div> | ||
<img src="https://img.shields.io/github/commit-activity/m/devorein/fauton?color=yellow" /> | ||
<img src="https://img.shields.io/github/repo-size/devorein/fauton?style=flat-square&color=orange"/> | ||
<img src="https://img.shields.io/github/repo-size/devorein/fauton?style=flat-square&color=ocombo"/> | ||
<img src="https://img.shields.io/github/contributors/devorein/fauton?label=contributors&color=red"/> | ||
@@ -31,2 +31,4 @@ <img src="https://img.shields.io/librariesio/release/npm/fauton"/> | ||
- [Dfa minimization](#dfa-minimization) | ||
- [Dfa equivalency by testing](#dfa-equivalency-by-testing) | ||
- [Testing regular expressions](#testing-regular-expressions) | ||
- [Conditions for DFA](#conditions-for-dfa) | ||
@@ -56,2 +58,4 @@ - [Transitions Record Transformation](#transitions-record-transformation) | ||
- [Contributors](#contributors) | ||
- [Algorithm Sources](#algorithm-sources) | ||
- [Credits](#credits) | ||
@@ -62,3 +66,3 @@ **Please note that I won't be following semver at the initial stages, as there could be a lot of (breaking) changes between each release which will all be patch** | ||
1. Test any dfa/nfa/ε-nfa | ||
1. Test any valid dfa/nfa/ε-nfa/regex | ||
2. Supports arbitrary alphabets | ||
@@ -133,3 +137,3 @@ 3. Easy to use api to generate input strings | ||
type: 'generate', | ||
range: { | ||
combo: { | ||
maxLength: 10, | ||
@@ -202,3 +206,3 @@ }, | ||
type: 'generate', | ||
range: { | ||
combo: { | ||
maxLength: 10, | ||
@@ -264,3 +268,3 @@ }, | ||
type: 'generate', | ||
range: { | ||
combo: { | ||
maxLength: 10, | ||
@@ -580,2 +584,91 @@ }, | ||
## Dfa equivalency by testing | ||
Testing if two dfa are equal through testing. One of the dfa is the minimized version of the other dfa, all the input string should return similar test result for both of them. | ||
```js | ||
import { DeterministicFiniteAutomaton, FiniteAutomataTest, FiniteAutomatonUtils } from 'fauton'; | ||
import path from 'path'; | ||
const dfa = new DeterministicFiniteAutomaton(() => true, { | ||
states: [0, 1, 2, 3, 4, 5, 6, 7], | ||
alphabets: ['0', '1'], | ||
final_states: [2], | ||
start_state: 0, | ||
label: 'dfa', | ||
transitions: { | ||
0: [1, 5], | ||
1: [6, 2], | ||
2: [0, 2], | ||
3: [2, 6], | ||
4: [7, 5], | ||
5: [2, 6], | ||
6: [6, 4], | ||
7: [6, 2], | ||
}, | ||
}); | ||
const minimized_dfa = dfa.minimize(); | ||
minimized_dfa.testLogic = (inputString) => { | ||
return FiniteAutomatonUtils.generateGraphFromString(dfa.automaton, inputString) | ||
.automatonTestResult; | ||
}; | ||
const finiteAutomataTest = new FiniteAutomataTest(path.join(__dirname, 'logs')); | ||
finiteAutomataTest.test([ | ||
{ | ||
automaton: minimized_dfa, | ||
options: { | ||
type: 'generate', | ||
combo: { | ||
maxLength: 10, | ||
}, | ||
}, | ||
}, | ||
]); | ||
``` | ||
## Testing regular expressions | ||
Rather than testing only a finite automaton, you can also test your regular expressions against generated strings | ||
```js | ||
import { FiniteAutomataTest, RegularExpression } from 'fauton'; | ||
import path from 'path'; | ||
const regex = new RegularExpression( | ||
(inputString) => { | ||
return ( | ||
inputString[0] === 'a' && | ||
inputString[1] === 'b' && | ||
inputString | ||
.slice(2) | ||
.split('') | ||
.every((char) => char === 'c') | ||
); | ||
}, | ||
{ | ||
alphabets: ['a', 'b', 'c'], | ||
label: 'Starts with a and b, ends with any number of c', | ||
regex: /^abc*$/g, | ||
} | ||
); | ||
const finiteAutomataTest = new FiniteAutomataTest(path.join(__dirname, 'logs')); | ||
finiteAutomataTest.test([ | ||
{ | ||
automaton: regex, | ||
options: { | ||
type: 'generate', | ||
combo: { | ||
maxLength: 10, | ||
}, | ||
}, | ||
}, | ||
]); | ||
``` | ||
Take a look at [examples](./examples) folder for more examples. | ||
@@ -762,3 +855,3 @@ | ||
type: 'generate', | ||
range: { | ||
combo: { | ||
maxLength: 3, | ||
@@ -828,3 +921,3 @@ }, | ||
Contains all the input strings. Useful when you are generating random or ranged strings and want to reuse it for later | ||
Contains all the input strings. Useful when you are generating random or combo strings and want to reuse it for later | ||
@@ -884,2 +977,17 @@ Same as `<fa.label>.accepted.txt` | ||
# Algorithm Sources | ||
Wikipedia sources for all the algorithms used in the package | ||
1. [Thompson-McNaughton-Yamada](https://en.wikipedia.org/wiki/Thompson%27s_construction) algorithm for converting regex to e-nfa | ||
2. [Hopcroft](https://en.wikipedia.org/wiki/DFA_minimization#Hopcroft's_algorithm) algorithm for dfa-minimization | ||
3. [Rabin–Scott powerset construction](https://en.wikipedia.org/wiki/Powerset_construction) algorithm to convert nfa to dfa | ||
4. [Shunting-Yard](https://en.wikipedia.org/wiki/Shunting-yard_algorithm) algorithm to convert regex string from infix to postfix | ||
# Credits | ||
Big thanks to all these wonderful repos. | ||
1. [Orban](https://github.com/wevial/orban) Regular expression engine that uses the Thompson-McNaughton-Yamada algorithm implemented in Python. | ||
Feel free to submit a pull request or open a new issue, contributions are more than welcome !!! |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
117848
79
1739
983
0