Skip to content

Commit

Permalink
add regex to NFA implementation using a parse tree
Browse files Browse the repository at this point in the history
  • Loading branch information
deniskyashif committed Sep 15, 2019
1 parent 23a9389 commit c3fabed
Show file tree
Hide file tree
Showing 2 changed files with 66 additions and 4 deletions.
58 changes: 57 additions & 1 deletion src/nfa.js
Expand Up @@ -100,7 +100,7 @@ function closure(nfa) {
Converts a postfix regular expression into a Thompson NFA.
*/
function toNFA(postfixExp) {
if(postfixExp === '') {
if (postfixExp === '') {
return fromEpsilon();
}

Expand All @@ -125,6 +125,61 @@ function toNFA(postfixExp) {
return stack.pop();
}

/*
Regex to NFA construction using a parse tree.
*/
const antlr = require('antlr4');
const RegexLexer = require('./grammar/RegexLexer').RegexLexer;
const RegexParser = require('./grammar/RegexParser').RegexParser;
const RegexVisitor = require('./grammar/RegexVisitor').RegexVisitor;

class RegexToNFAVisitor extends RegexVisitor {
visitStart(ctx) {
return this.visit(ctx.getChild(0));
}

visitExpr(ctx) {
if (ctx.children.length === 1) {
return fromSymbol(ctx.getChild(0).getText());
}

if (ctx.children.length === 2 && ctx.getChild(1).getText() === '*') {
const leftEvaluated = this.visit(ctx.getChild(0));
return closure(leftEvaluated);
}

const left = ctx.getChild(0);
const mid = ctx.getChild(1);
const right = ctx.getChild(2);

if (left.getText() === '(' && right.getText() === ')') {
return this.visit(mid);
}

const leftEvaluated = this.visit(left);
const rightEvaluated = this.visit(right);

return mid.getText() === '|'
? union(leftEvaluated, rightEvaluated)
: concat(leftEvaluated, rightEvaluated);
}
}

function toNFAFromInfixExp(infixExp) {
if (infixExp === '') {
return fromEpsilon();
}

const chars = new antlr.InputStream(infixExp);
const lexer = new RegexLexer(chars);
const tokens = new antlr.CommonTokenStream(lexer);
const parser = new RegexParser(tokens);
const parseTree = parser.start();
const nfa = parseTree.accept(new RegexToNFAVisitor());

return nfa;
}

/*
Process a string through an NFA by recurisively (depth-first) traversing all the possible paths until finding a matching one.
Expand Down Expand Up @@ -215,5 +270,6 @@ function recognize(nfa, word) {

module.exports = {
toNFA,
toNFAFromInfixExp,
recognize
};
12 changes: 9 additions & 3 deletions src/regex.js
@@ -1,10 +1,16 @@
const { insertExplicitConcatOperator, toPostfix } = require('./parser');
const { toNFA, recognize } = require('./nfa');
const { toNFA, toNFAFromInfixExp, recognize } = require('./nfa');

function createMatcher(exp) {
const postfixExp = toPostfix(insertExplicitConcatOperator(exp));
const nfa = toNFA(postfixExp);
const expWithConcatenationOperator = insertExplicitConcatOperator(exp);

// Generates an NFA using a stack
// const postfixExp = toPostfix(expWithConcatenationOperator);
// const nfa = toNFA(postfixExp);

// Generates an NFA by constructing a parse tree
const nfa = toNFAFromInfixExp(expWithConcatenationOperator);

return word => recognize(nfa, word);
}

Expand Down

0 comments on commit c3fabed

Please sign in to comment.