Name

Data::DFA - Deterministic finite state parser from regular expression

Synopsis

Create a deterministic finite state parser to recognize sequences of symbols that match a given regular expression.

To recognize sequences of symbols drawn from 'a'..'e' that match the regular expression: a (b|c)+ d? e:

# Construct a deterministic finite state automaton from the regular expression:

use Data::DFA qw(:all);
use Data::Table::Text qw(:all);
use Test::More qw(no_plan);

my $dfa = fromExpr
 (element("a"),
  oneOrMore(choice(element("b"), element("c"))),
  optional(element("d")),
  element("e")
 );

ok  $dfa->parser->accepts(qw(a b e));
ok !$dfa->parser->accepts(qw(a d));

# Print the symbols used and the transitions table:

is_deeply ['a'..'e'], [$dfa->symbols];

ok $dfa->print("Dfa for a(b|c)+d?e :") eq nws <<END;
Dfa for a(b|c)+d?e :
  State        Final  Symbol  Target       Final
1   0                   a       1 3          0
2   1 2 3 4 5 6  0      b       1 2 3 4 5 6  0
3                       c       1 3 4 5 6    0
4                       d       6            0
5                       e       7            1
6   1 3          0      b       1 2 3 4 5 6  0
7                       c       1 3 4 5 6    0
8   1 3 4 5 6    0      b       1 2 3 4 5 6  0
9                       c       1 3 4 5 6    0
10                      d       6            0
11                      e       7            1
12  6            0      e       7            1
END

# Create a parser and use it to parse a sequence of symbols

my $parser = $dfa->parser;                                                    # New parser

eval { $parser->accept($_) } for qw(a b a);                                   # Try to parse a b a

# Create a parser and use it to parse a sequence of symbols

my $parser = $dfa->parser;

eval { $parser->accept($_) } for qw(a b a);
my $r = $@;
ok $r =~ m(Already processed: a b);
ok $r =~ m(Expected one of  : b c d e);
ok $r =~ m(But found        : a);

is_deeply [$parser->next],     [qw(b c d e)];
is_deeply  $parser->processed, [qw(a b)];
ok !$parser->final;

To construct and parse regular expressions in the format used by !ELEMENT definitions in DTDs:

{my $e = q/element(q(a)), zeroOrMore(choice(element(q(b)), element(q(c)))), element(q(d))/;
 my $d = eval qq/fromExpr($e)/;

 my $E = $d->printAsExpr;
 ok $e eq $E;

 my $R = $d->printAsRe;
 ok $R eq q(a, (b | c)*, d);                                                   # Print as DTD regular expression

 my $D = parseDtdElement($R);                                                  # Parse DTD regular expression
 my $S = $D->printAsExpr;
 ok $e eq $S;
}

Description

Deterministic finite state parser from regular expression

Version "20191029".

The following sections describe the methods in each functional area of this module. For an alphabetic listing of all methods by name see Index.

Construct regular expression

Construct a regular expression that defines the language to be parsed using the following combining operations which can all be imported:

element($)

One element.

   Parameter  Description
1  $label     Transition symbol

Example:

if (1) {
  my $dfa = fromExpr                                                            # Construct DFA
   (𝗲𝗹𝗲𝗺𝗲𝗻𝘁("a"),
    oneOrMore(choice(𝗲𝗹𝗲𝗺𝗲𝗻𝘁("b"), 𝗲𝗹𝗲𝗺𝗲𝗻𝘁("c"))),
    optional(𝗲𝗹𝗲𝗺𝗲𝗻𝘁("d")),
    𝗲𝗹𝗲𝗺𝗲𝗻𝘁("e")
   );

  ok  $dfa->parser->accepts(qw(a b e));                                         # Accept symbols
  ok !$dfa->parser->accepts(qw(a d));

  is_deeply ['a'..'e'], [$dfa->symbols];                                        # List symbols

  ok $dfa->print("Dfa for a(b|c)+d?e :") eq <<END;                              # Print compressed DFA
Dfa for a(b|c)+d?e :
   State  Final  Symbol  Target  Final
1      0         a            2      0
2      1         b            1      0
3                c            1      0
4                d            3      0
5                e            4      1
6      2         b            1      0
7                c            1      0
8      3         e            4      1
END
 }

if (1) {
  my $dfa = fromExpr                                                            # Construct DFA
   (𝗲𝗹𝗲𝗺𝗲𝗻𝘁("a"),
    oneOrMore(choice(𝗲𝗹𝗲𝗺𝗲𝗻𝘁("b"), 𝗲𝗹𝗲𝗺𝗲𝗻𝘁("c"))),
    optional(𝗲𝗹𝗲𝗺𝗲𝗻𝘁("d")),
    𝗲𝗹𝗲𝗺𝗲𝗻𝘁("e")
   );
  my $parser = $dfa->parser;                                                    # New parser

  eval { $parser->accept($_) } for qw(a b a);                                   # Try to parse a b a

  is_deeply [$parser->next],     [qw(b c d e)];                                 # Next acceptable symbol
  is_deeply  $parser->processed, [qw(a b)];                                     # Symbols processed

  ok !$parser->final;                                                           # Not in a final state

  ok $dfa->dumpAsJson eq <<END;                                                 # Dump as json
{
   "finalStates" : {
      "0" : 0,
      "1" : 0,
      "2" : 0,
      "3" : 0,
      "4" : 1
   },
   "transitions" : {
      "0" : {
         "a" : 2
      },
      "1" : {
         "b" : 1,
         "c" : 1,
         "d" : 3,
         "e" : 4
      },
      "2" : {
         "b" : 1,
         "c" : 1
      },
      "3" : {
         "e" : 4
      }
   }
}
END

 }

This is a static method and so should be invoked as:

Data::DFA::element

sequence(@)

Sequence of elements.

   Parameter  Description
1  @elements  Elements

Example:

if (1) {
  my $dfa = fromExpr                                                            # Construct DFA
   (zeroOrMore(𝘀𝗲𝗾𝘂𝗲𝗻𝗰𝗲('a'..'g')),
    except('d'..'g')
   );

  ok  $dfa->parser->accepts(qw(a b c d e f g a b c d e f g a));                 # Accept symbols
  ok !$dfa->parser->accepts(qw(a b c d e f g a b c d e f g g));                 # Fail to accept symbols

  my $parser = $dfa->parser;
  $parser->accept(qw(a b c d e f g a b c d e f g a));
  ok $parser->final;

  ok $dfa->print(q(Test)) eq <<END;                                             # Print compressed DFA
Test
   State  Final  Symbol  Target  Final
1      0         a            1      1
2                b            2      1
3                c            2      1
4      1      1  b            3      0
5      3         c            4      0
6      4         d            5      0
7      5         e            6      0
8      6         f            7      0
9      7         g            0      0
END
 }

This is a static method and so should be invoked as:

Data::DFA::sequence

optional(@)

An optional sequence of element.

   Parameter  Description
1  @element   Elements

Example:

if (1) {
  my $dfa = fromExpr                                                            # Construct DFA
   (element("a"),
    oneOrMore(choice(element("b"), element("c"))),
    𝗼𝗽𝘁𝗶𝗼𝗻𝗮𝗹(element("d")),
    element("e")
   );

  ok  $dfa->parser->accepts(qw(a b e));                                         # Accept symbols
  ok !$dfa->parser->accepts(qw(a d));

  is_deeply ['a'..'e'], [$dfa->symbols];                                        # List symbols

  ok $dfa->print("Dfa for a(b|c)+d?e :") eq <<END;                              # Print compressed DFA
Dfa for a(b|c)+d?e :
   State  Final  Symbol  Target  Final
1      0         a            2      0
2      1         b            1      0
3                c            1      0
4                d            3      0
5                e            4      1
6      2         b            1      0
7                c            1      0
8      3         e            4      1
END
 }

if (1) {
  my $dfa = fromExpr                                                            # Construct DFA
   (element("a"),
    oneOrMore(choice(element("b"), element("c"))),
    𝗼𝗽𝘁𝗶𝗼𝗻𝗮𝗹(element("d")),
    element("e")
   );
  my $parser = $dfa->parser;                                                    # New parser

  eval { $parser->accept($_) } for qw(a b a);                                   # Try to parse a b a

  is_deeply [$parser->next],     [qw(b c d e)];                                 # Next acceptable symbol
  is_deeply  $parser->processed, [qw(a b)];                                     # Symbols processed

  ok !$parser->final;                                                           # Not in a final state

  ok $dfa->dumpAsJson eq <<END;                                                 # Dump as json
{
   "finalStates" : {
      "0" : 0,
      "1" : 0,
      "2" : 0,
      "3" : 0,
      "4" : 1
   },
   "transitions" : {
      "0" : {
         "a" : 2
      },
      "1" : {
         "b" : 1,
         "c" : 1,
         "d" : 3,
         "e" : 4
      },
      "2" : {
         "b" : 1,
         "c" : 1
      },
      "3" : {
         "e" : 4
      }
   }
}
END

 }

This is a static method and so should be invoked as:

Data::DFA::optional

zeroOrMore(@)

Zero or more repetitions of a sequence of elements.

   Parameter  Description
1  @element   Elements

Example:

if (1) {
  my $dfa = fromExpr                                                            # Construct DFA
   (𝘇𝗲𝗿𝗼𝗢𝗿𝗠𝗼𝗿𝗲(sequence('a'..'g')),
    except('d'..'g')
   );

  ok  $dfa->parser->accepts(qw(a b c d e f g a b c d e f g a));                 # Accept symbols
  ok !$dfa->parser->accepts(qw(a b c d e f g a b c d e f g g));                 # Fail to accept symbols

  my $parser = $dfa->parser;
  $parser->accept(qw(a b c d e f g a b c d e f g a));
  ok $parser->final;

  ok $dfa->print(q(Test)) eq <<END;                                             # Print compressed DFA
Test
   State  Final  Symbol  Target  Final
1      0         a            1      1
2                b            2      1
3                c            2      1
4      1      1  b            3      0
5      3         c            4      0
6      4         d            5      0
7      5         e            6      0
8      6         f            7      0
9      7         g            0      0
END
 }

This is a static method and so should be invoked as:

Data::DFA::zeroOrMore

oneOrMore(@)

One or more repetitions of a sequence of elements.

   Parameter  Description
1  @element   Elements

Example:

if (1) {
  my $dfa = fromExpr                                                            # Construct DFA
   (element("a"),
    𝗼𝗻𝗲𝗢𝗿𝗠𝗼𝗿𝗲(choice(element("b"), element("c"))),
    optional(element("d")),
    element("e")
   );

  ok  $dfa->parser->accepts(qw(a b e));                                         # Accept symbols
  ok !$dfa->parser->accepts(qw(a d));

  is_deeply ['a'..'e'], [$dfa->symbols];                                        # List symbols

  ok $dfa->print("Dfa for a(b|c)+d?e :") eq <<END;                              # Print compressed DFA
Dfa for a(b|c)+d?e :
   State  Final  Symbol  Target  Final
1      0         a            2      0
2      1         b            1      0
3                c            1      0
4                d            3      0
5                e            4      1
6      2         b            1      0
7                c            1      0
8      3         e            4      1
END
 }

if (1) {
  my $dfa = fromExpr                                                            # Construct DFA
   (element("a"),
    𝗼𝗻𝗲𝗢𝗿𝗠𝗼𝗿𝗲(choice(element("b"), element("c"))),
    optional(element("d")),
    element("e")
   );
  my $parser = $dfa->parser;                                                    # New parser

  eval { $parser->accept($_) } for qw(a b a);                                   # Try to parse a b a

  is_deeply [$parser->next],     [qw(b c d e)];                                 # Next acceptable symbol
  is_deeply  $parser->processed, [qw(a b)];                                     # Symbols processed

  ok !$parser->final;                                                           # Not in a final state

  ok $dfa->dumpAsJson eq <<END;                                                 # Dump as json
{
   "finalStates" : {
      "0" : 0,
      "1" : 0,
      "2" : 0,
      "3" : 0,
      "4" : 1
   },
   "transitions" : {
      "0" : {
         "a" : 2
      },
      "1" : {
         "b" : 1,
         "c" : 1,
         "d" : 3,
         "e" : 4
      },
      "2" : {
         "b" : 1,
         "c" : 1
      },
      "3" : {
         "e" : 4
      }
   }
}
END

 }

This is a static method and so should be invoked as:

Data::DFA::oneOrMore

choice(@)

Choice from amongst one or more elements.

   Parameter  Description
1  @elements  Elements to be chosen from

Example:

if (1) {
  my $dfa = fromExpr                                                            # Construct DFA
   (element("a"),
    oneOrMore(𝗰𝗵𝗼𝗶𝗰𝗲(element("b"), element("c"))),
    optional(element("d")),
    element("e")
   );

  ok  $dfa->parser->accepts(qw(a b e));                                         # Accept symbols
  ok !$dfa->parser->accepts(qw(a d));

  is_deeply ['a'..'e'], [$dfa->symbols];                                        # List symbols

  ok $dfa->print("Dfa for a(b|c)+d?e :") eq <<END;                              # Print compressed DFA
Dfa for a(b|c)+d?e :
   State  Final  Symbol  Target  Final
1      0         a            2      0
2      1         b            1      0
3                c            1      0
4                d            3      0
5                e            4      1
6      2         b            1      0
7                c            1      0
8      3         e            4      1
END
 }

if (1) {
  my $dfa = fromExpr                                                            # Construct DFA
   (element("a"),
    oneOrMore(𝗰𝗵𝗼𝗶𝗰𝗲(element("b"), element("c"))),
    optional(element("d")),
    element("e")
   );
  my $parser = $dfa->parser;                                                    # New parser

  eval { $parser->accept($_) } for qw(a b a);                                   # Try to parse a b a

  is_deeply [$parser->next],     [qw(b c d e)];                                 # Next acceptable symbol
  is_deeply  $parser->processed, [qw(a b)];                                     # Symbols processed

  ok !$parser->final;                                                           # Not in a final state

  ok $dfa->dumpAsJson eq <<END;                                                 # Dump as json
{
   "finalStates" : {
      "0" : 0,
      "1" : 0,
      "2" : 0,
      "3" : 0,
      "4" : 1
   },
   "transitions" : {
      "0" : {
         "a" : 2
      },
      "1" : {
         "b" : 1,
         "c" : 1,
         "d" : 3,
         "e" : 4
      },
      "2" : {
         "b" : 1,
         "c" : 1
      },
      "3" : {
         "e" : 4
      }
   }
}
END

 }

This is a static method and so should be invoked as:

Data::DFA::choice

except(@)

Choice from amongst all symbols except the ones mentioned

   Parameter  Description
1  @elements  Elements to be chosen from

Example:

if (1) {
  my $dfa = fromExpr                                                            # Construct DFA
   (zeroOrMore(sequence('a'..'g')),
    𝗲𝘅𝗰𝗲𝗽𝘁('d'..'g')
   );

  ok  $dfa->parser->accepts(qw(a b c d e f g a b c d e f g a));                 # Accept symbols
  ok !$dfa->parser->accepts(qw(a b c d e f g a b c d e f g g));                 # Fail to accept symbols

  my $parser = $dfa->parser;
  $parser->accept(qw(a b c d e f g a b c d e f g a));
  ok $parser->final;

  ok $dfa->print(q(Test)) eq <<END;                                             # Print compressed DFA
Test
   State  Final  Symbol  Target  Final
1      0         a            1      1
2                b            2      1
3                c            2      1
4      1      1  b            3      0
5      3         c            4      0
6      4         d            5      0
7      5         e            6      0
8      6         f            7      0
9      7         g            0      0
END
 }

This is a static method and so should be invoked as:

Data::DFA::except

fromExpr(@)

Create a DFA from a regular expression.

   Parameter  Description
1  @expr      Expression

Example:

if (1) {
  my $dfa = 𝗳𝗿𝗼𝗺𝗘𝘅𝗽𝗿                                                            # Construct DFA
   (element("a"),
    oneOrMore(choice(element("b"), element("c"))),
    optional(element("d")),
    element("e")
   );

  ok  $dfa->parser->accepts(qw(a b e));                                         # Accept symbols
  ok !$dfa->parser->accepts(qw(a d));

  is_deeply ['a'..'e'], [$dfa->symbols];                                        # List symbols

  ok $dfa->print("Dfa for a(b|c)+d?e :") eq <<END;                              # Print compressed DFA
Dfa for a(b|c)+d?e :
   State  Final  Symbol  Target  Final
1      0         a            2      0
2      1         b            1      0
3                c            1      0
4                d            3      0
5                e            4      1
6      2         b            1      0
7                c            1      0
8      3         e            4      1
END
 }

if (1) {
  my $dfa = 𝗳𝗿𝗼𝗺𝗘𝘅𝗽𝗿                                                            # Construct DFA
   (element("a"),
    oneOrMore(choice(element("b"), element("c"))),
    optional(element("d")),
    element("e")
   );
  my $parser = $dfa->parser;                                                    # New parser

  eval { $parser->accept($_) } for qw(a b a);                                   # Try to parse a b a

  is_deeply [$parser->next],     [qw(b c d e)];                                 # Next acceptable symbol
  is_deeply  $parser->processed, [qw(a b)];                                     # Symbols processed

  ok !$parser->final;                                                           # Not in a final state

  ok $dfa->dumpAsJson eq <<END;                                                 # Dump as json
{
   "finalStates" : {
      "0" : 0,
      "1" : 0,
      "2" : 0,
      "3" : 0,
      "4" : 1
   },
   "transitions" : {
      "0" : {
         "a" : 2
      },
      "1" : {
         "b" : 1,
         "c" : 1,
         "d" : 3,
         "e" : 4
      },
      "2" : {
         "b" : 1,
         "c" : 1
      },
      "3" : {
         "e" : 4
      }
   }
}
END

 }

This is a static method and so should be invoked as:

Data::DFA::fromExpr

compress($)

Compress the DFA by removing duplicate states and deleting no longer needed NFA states

   Parameter  Description
1  $dfa       DFA

Example:

if (1) {
  my $dfa = fromExpr                                                            # Construct DFA
   (zeroOrMore(sequence('a'..'g')),
    except('d'..'g')
   );

  ok  $dfa->parser->accepts(qw(a b c d e f g a b c d e f g a));                 # Accept symbols
  ok !$dfa->parser->accepts(qw(a b c d e f g a b c d e f g g));                 # Fail to accept symbols

  my $parser = $dfa->parser;
  $parser->accept(qw(a b c d e f g a b c d e f g a));
  ok $parser->final;

  ok $dfa->print(q(Test)) eq <<END;                                             # Print compressed DFA
Test
   State  Final  Symbol  Target  Final
1      0         a            1      1
2                b            2      1
3                c            2      1
4      1      1  b            3      0
5      3         c            4      0
6      4         d            5      0
7      5         e            6      0
8      6         f            7      0
9      7         g            0      0
END
 }

print($$$)

Print DFA to a string and optionally to STDERR or STDOUT

   Parameter  Description
1  $dfa       DFA
2  $title     Title
3  $print     1 - STDOUT or 2 - STDERR

Example:

if (1) {
  my $dfa = fromExpr                                                            # Construct DFA
   (zeroOrMore(sequence('a'..'g')),
    except('d'..'g')
   );

  ok  $dfa->parser->accepts(qw(a b c d e f g a b c d e f g a));                 # Accept symbols
  ok !$dfa->parser->accepts(qw(a b c d e f g a b c d e f g g));                 # Fail to accept symbols

  my $parser = $dfa->parser;
  $parser->accept(qw(a b c d e f g a b c d e f g a));
  ok $parser->final;

  ok $dfa->𝗽𝗿𝗶𝗻𝘁(q(Test)) eq <<END;                                             # Print compressed DFA
Test
   State  Final  Symbol  Target  Final
1      0         a            1      1
2                b            2      1
3                c            2      1
4      1      1  b            3      0
5      3         c            4      0
6      4         d            5      0
7      5         e            6      0
8      6         f            7      0
9      7         g            0      0
END
 }

symbols($)

Return an array of all the symbols accepted by the DFA

   Parameter  Description
1  $dfa       DFA

Example:

if (1) {
  my $dfa = fromExpr                                                            # Construct DFA
   (element("a"),
    oneOrMore(choice(element("b"), element("c"))),
    optional(element("d")),
    element("e")
   );

  ok  $dfa->parser->accepts(qw(a b e));                                         # Accept 𝘀𝘆𝗺𝗯𝗼𝗹𝘀
  ok !$dfa->parser->accepts(qw(a d));

  is_deeply ['a'..'e'], [$dfa->𝘀𝘆𝗺𝗯𝗼𝗹𝘀];                                        # List 𝘀𝘆𝗺𝗯𝗼𝗹𝘀

  ok $dfa->print("Dfa for a(b|c)+d?e :") eq <<END;                              # Print compressed DFA
Dfa for a(b|c)+d?e :
   State  Final  Symbol  Target  Final
1      0         a            2      0
2      1         b            1      0
3                c            1      0
4                d            3      0
5                e            4      1
6      2         b            1      0
7                c            1      0
8      3         e            4      1
END
 }

parser($)

Create a parser from a deterministic finite state automaton constructed from a regular expression.

   Parameter  Description
1  $dfa       Deterministic finite state automaton generated from an expression

Example:

if (1) {
  my $dfa = fromExpr                                                            # Construct DFA
   (element("a"),
    oneOrMore(choice(element("b"), element("c"))),
    optional(element("d")),
    element("e")
   );

  ok  $dfa->𝗽𝗮𝗿𝘀𝗲𝗿->accepts(qw(a b e));                                         # Accept symbols
  ok !$dfa->𝗽𝗮𝗿𝘀𝗲𝗿->accepts(qw(a d));

  is_deeply ['a'..'e'], [$dfa->symbols];                                        # List symbols

  ok $dfa->print("Dfa for a(b|c)+d?e :") eq <<END;                              # Print compressed DFA
Dfa for a(b|c)+d?e :
   State  Final  Symbol  Target  Final
1      0         a            2      0
2      1         b            1      0
3                c            1      0
4                d            3      0
5                e            4      1
6      2         b            1      0
7                c            1      0
8      3         e            4      1
END
 }

if (1) {
  my $dfa = fromExpr                                                            # Construct DFA
   (element("a"),
    oneOrMore(choice(element("b"), element("c"))),
    optional(element("d")),
    element("e")
   );
  my $𝗽𝗮𝗿𝘀𝗲𝗿 = $dfa->𝗽𝗮𝗿𝘀𝗲𝗿;                                                    # New 𝗽𝗮𝗿𝘀𝗲𝗿

  eval { $𝗽𝗮𝗿𝘀𝗲𝗿->accept($_) } for qw(a b a);                                   # Try to parse a b a

  is_deeply [$𝗽𝗮𝗿𝘀𝗲𝗿->next],     [qw(b c d e)];                                 # Next acceptable symbol
  is_deeply  $𝗽𝗮𝗿𝘀𝗲𝗿->processed, [qw(a b)];                                     # Symbols processed

  ok !$𝗽𝗮𝗿𝘀𝗲𝗿->final;                                                           # Not in a final state

  ok $dfa->dumpAsJson eq <<END;                                                 # Dump as json
{
   "finalStates" : {
      "0" : 0,
      "1" : 0,
      "2" : 0,
      "3" : 0,
      "4" : 1
   },
   "transitions" : {
      "0" : {
         "a" : 2
      },
      "1" : {
         "b" : 1,
         "c" : 1,
         "d" : 3,
         "e" : 4
      },
      "2" : {
         "b" : 1,
         "c" : 1
      },
      "3" : {
         "e" : 4
      }
   }
}
END

 }

dumpAsJson($)

Create a JSON representation {transitions=>{symbol=>state}, finalStates=>{state=>1}}

   Parameter  Description
1  $dfa       Deterministic finite state automaton generated from an expression

Example:

if (1) {
  my $dfa = fromExpr                                                            # Construct DFA
   (element("a"),
    oneOrMore(choice(element("b"), element("c"))),
    optional(element("d")),
    element("e")
   );

  ok  $dfa->parser->accepts(qw(a b e));                                         # Accept symbols
  ok !$dfa->parser->accepts(qw(a d));

  is_deeply ['a'..'e'], [$dfa->symbols];                                        # List symbols

  ok $dfa->print("Dfa for a(b|c)+d?e :") eq <<END;                              # Print compressed DFA
Dfa for a(b|c)+d?e :
   State  Final  Symbol  Target  Final
1      0         a            2      0
2      1         b            1      0
3                c            1      0
4                d            3      0
5                e            4      1
6      2         b            1      0
7                c            1      0
8      3         e            4      1
END
 }

printAsExpr($)

Print a $dfa as an expression

   Parameter  Description
1  $dfa       DFA

Example:

if (1)
 {my $e = q/element(q(a)), zeroOrMore(choice(element(q(b)), element(q(c)))), element(q(d))/;
  my $d = eval qq/fromExpr($e)/;
  confess $@ if $@;

  my $E = $d->𝗽𝗿𝗶𝗻𝘁𝗔𝘀𝗘𝘅𝗽𝗿;
  ok $e eq $E;

  my $R = $d->printAsRe;
  ok $R eq q(((a, (b | c)*, d)));

  my $D = parseDtdElement($R);
  my $S = $D->𝗽𝗿𝗶𝗻𝘁𝗔𝘀𝗘𝘅𝗽𝗿;
  ok $e eq $S;
 }

printAsRe($)

Print a $dfa as a regular expression

   Parameter  Description
1  $dfa       DFA

Example:

if (1)
 {my $e = q/element(q(a)), zeroOrMore(choice(element(q(b)), element(q(c)))), element(q(d))/;
  my $d = eval qq/fromExpr($e)/;
  confess $@ if $@;

  my $E = $d->printAsExpr;
  ok $e eq $E;

  my $R = $d->𝗽𝗿𝗶𝗻𝘁𝗔𝘀𝗥𝗲;
  ok $R eq q(((a, (b | c)*, d)));

  my $D = parseDtdElement($R);
  my $S = $D->printAsExpr;
  ok $e eq $S;
 }

parseDtdElement($)

Convert the Dtd Element definition in $stringto a DFA

   Parameter  Description
1  $string    String representation of DTD element expression

Example:

if (1)
 {my $e = q/element(q(a)), zeroOrMore(choice(element(q(b)), element(q(c)))), element(q(d))/;
  my $d = eval qq/fromExpr($e)/;
  confess $@ if $@;

  my $E = $d->printAsExpr;
  ok $e eq $E;

  my $R = $d->printAsRe;
  ok $R eq q(((a, (b | c)*, d)));

  my $D = 𝗽𝗮𝗿𝘀𝗲𝗗𝘁𝗱𝗘𝗹𝗲𝗺𝗲𝗻𝘁($R);
  my $S = $D->printAsExpr;
  ok $e eq $S;
 }

Parser methods

Use the DFA to parse a sequence of symbols

Data::DFA::Parser::accept($$)

Accept the next symbol drawn from the symbol set if possible by moving to a new state otherwise confessing with a helpful message

   Parameter  Description
1  $parser    DFA Parser
2  $symbol    Next symbol to be processed by the finite state automaton

Example:

if (1) {
  my $dfa = fromExpr                                                            # Construct DFA
   (element("a"),
    oneOrMore(choice(element("b"), element("c"))),
    optional(element("d")),
    element("e")
   );
  my $parser = $dfa->parser;                                                    # New parser

  eval { $parser->accept($_) } for qw(a b a);                                   # Try to parse a b a

  is_deeply [$parser->next],     [qw(b c d e)];                                 # Next acceptable symbol
  is_deeply  $parser->processed, [qw(a b)];                                     # Symbols processed

  ok !$parser->final;                                                           # Not in a final state

  ok $dfa->dumpAsJson eq <<END;                                                 # Dump as json
{
   "finalStates" : {
      "0" : 0,
      "1" : 0,
      "2" : 0,
      "3" : 0,
      "4" : 1
   },
   "transitions" : {
      "0" : {
         "a" : 2
      },
      "1" : {
         "b" : 1,
         "c" : 1,
         "d" : 3,
         "e" : 4
      },
      "2" : {
         "b" : 1,
         "c" : 1
      },
      "3" : {
         "e" : 4
      }
   }
}
END

 }

if (1) {
  my $dfa = fromExpr                                                            # Construct DFA
   (zeroOrMore(sequence('a'..'g')),
    except('d'..'g')
   );

  ok  $dfa->parser->accepts(qw(a b c d e f g a b c d e f g a));                 # Accept symbols
  ok !$dfa->parser->accepts(qw(a b c d e f g a b c d e f g g));                 # Fail to accept symbols

  my $parser = $dfa->parser;
  $parser->accept(qw(a b c d e f g a b c d e f g a));
  ok $parser->final;

  ok $dfa->print(q(Test)) eq <<END;                                             # Print compressed DFA
Test
   State  Final  Symbol  Target  Final
1      0         a            1      1
2                b            2      1
3                c            2      1
4      1      1  b            3      0
5      3         c            4      0
6      4         d            5      0
7      5         e            6      0
8      6         f            7      0
9      7         g            0      0
END
 }

Data::DFA::Parser::final($)

Returns whether we are currently in a final state or not

   Parameter  Description
1  $parser    DFA Parser

Example:

if (1) {
  my $dfa = fromExpr                                                            # Construct DFA
   (zeroOrMore(sequence('a'..'g')),
    except('d'..'g')
   );

  ok  $dfa->parser->accepts(qw(a b c d e f g a b c d e f g a));                 # Accept symbols
  ok !$dfa->parser->accepts(qw(a b c d e f g a b c d e f g g));                 # Fail to accept symbols

  my $parser = $dfa->parser;
  $parser->accept(qw(a b c d e f g a b c d e f g a));
  ok $parser->final;

  ok $dfa->print(q(Test)) eq <<END;                                             # Print compressed DFA
Test
   State  Final  Symbol  Target  Final
1      0         a            1      1
2                b            2      1
3                c            2      1
4      1      1  b            3      0
5      3         c            4      0
6      4         d            5      0
7      5         e            6      0
8      6         f            7      0
9      7         g            0      0
END
 }

Data::DFA::Parser::next($)

Returns an array of symbols that would be accepted in the current state

   Parameter  Description
1  $parser    DFA Parser

Example:

if (1) {
  my $dfa = fromExpr                                                            # Construct DFA
   (element("a"),
    oneOrMore(choice(element("b"), element("c"))),
    optional(element("d")),
    element("e")
   );
  my $parser = $dfa->parser;                                                    # New parser

  eval { $parser->accept($_) } for qw(a b a);                                   # Try to parse a b a

  is_deeply [$parser->next],     [qw(b c d e)];                                 # Next acceptable symbol
  is_deeply  $parser->processed, [qw(a b)];                                     # Symbols processed

  ok !$parser->final;                                                           # Not in a final state

  ok $dfa->dumpAsJson eq <<END;                                                 # Dump as json
{
   "finalStates" : {
      "0" : 0,
      "1" : 0,
      "2" : 0,
      "3" : 0,
      "4" : 1
   },
   "transitions" : {
      "0" : {
         "a" : 2
      },
      "1" : {
         "b" : 1,
         "c" : 1,
         "d" : 3,
         "e" : 4
      },
      "2" : {
         "b" : 1,
         "c" : 1
      },
      "3" : {
         "e" : 4
      }
   }
}
END

 }

Data::DFA::Parser::accepts($@)

Confirm that a DFA accepts an array representing a sequence of symbols

   Parameter  Description
1  $parser    DFA Parser
2  @symbols   Array of symbols

Example:

if (1) {
  my $dfa = fromExpr                                                            # Construct DFA
   (element("a"),
    oneOrMore(choice(element("b"), element("c"))),
    optional(element("d")),
    element("e")
   );

  ok  $dfa->parser->accepts(qw(a b e));                                         # Accept symbols
  ok !$dfa->parser->accepts(qw(a d));

  is_deeply ['a'..'e'], [$dfa->symbols];                                        # List symbols

  ok $dfa->print("Dfa for a(b|c)+d?e :") eq <<END;                              # Print compressed DFA
Dfa for a(b|c)+d?e :
   State  Final  Symbol  Target  Final
1      0         a            2      0
2      1         b            1      0
3                c            1      0
4                d            3      0
5                e            4      1
6      2         b            1      0
7                c            1      0
8      3         e            4      1
END
 }

if (1) {
  my $dfa = fromExpr                                                            # Construct DFA
   (element("a"),
    oneOrMore(choice(element("b"), element("c"))),
    optional(element("d")),
    element("e")
   );
  my $parser = $dfa->parser;                                                    # New parser

  eval { $parser->accept($_) } for qw(a b a);                                   # Try to parse a b a

  is_deeply [$parser->next],     [qw(b c d e)];                                 # Next acceptable symbol
  is_deeply  $parser->processed, [qw(a b)];                                     # Symbols processed

  ok !$parser->final;                                                           # Not in a final state

  ok $dfa->dumpAsJson eq <<END;                                                 # Dump as json
{
   "finalStates" : {
      "0" : 0,
      "1" : 0,
      "2" : 0,
      "3" : 0,
      "4" : 1
   },
   "transitions" : {
      "0" : {
         "a" : 2
      },
      "1" : {
         "b" : 1,
         "c" : 1,
         "d" : 3,
         "e" : 4
      },
      "2" : {
         "b" : 1,
         "c" : 1
      },
      "3" : {
         "e" : 4
      }
   }
}
END

 }

Data::DFA Definition

DFA State

Output fields

final - Whether this state is final

nfaStates - Hash whose keys are the NFA states that contributed to this super state

pump - Pumping lemmas for this state

return - Return state

sequence - Sequence of states to final state minus pumped states

state - Name of the state - the join of the NFA keys

transitions - Transitions from this state

Data::DFA::State Definition

DFA State

Output fields

final - Whether this state is final

nfaStates - Hash whose keys are the NFA states that contributed to this super state

pump - Pumping lemmas for this state

return - Return state

sequence - Sequence of states to final state minus pumped states

state - Name of the state - the join of the NFA keys

transitions - Transitions from this state

Private Methods

newDFA()

Create a new DFA

newState(%)

Create a new DFA state

   Parameter  Description
1  %options   DFA state as hash

fromNfa($)

Create a DFA from an NFA.

   Parameter  Description
1  $nfa       Nfa

finalState($$)

Check whether any of the specified states in the NFA are final

   Parameter  Description
1  $nfa       NFA
2  $reach     Hash of states in the NFA

superState($$$$$)

Create super states from existing superstate

   Parameter              Description
1  $dfa                   DFA
2  $superStateName        Start state in DFA
3  $nfa                   NFA we are converting
4  $symbols               Symbols in the NFA we are converting
5  $nfaSymbolTransitions  States reachable from each state by symbol

superStates($$$)

Create super states from existing superstate

   Parameter        Description
1  $dfa             DFA
2  $SuperStateName  Start state in DFA
3  $nfa             NFA we are tracking

transitionOnSymbol($$$)

The super state reached by transition on a symbol from a specified state

   Parameter        Description
1  $dfa             DFA
2  $superStateName  Start state in DFA
3  $symbol          Symbol

removeDuplicatedStates($)

Remove duplicated states in a DFA

   Parameter  Description
1  $dfa       Deterministic finite state automaton generated from an expression

printAsExpr2($%)

Print a DFA $dfa_ in expression form

   Parameter  Description
1  $dfa       Dfa
2  %options   Options

Index

1 choice - Choice from amongst one or more elements.

2 compress - Compress the DFA by removing duplicate states and deleting no longer needed NFA states

3 Data::DFA::Parser::accept - Accept the next symbol drawn from the symbol set if possible by moving to a new state otherwise confessing with a helpful message

4 Data::DFA::Parser::accepts - Confirm that a DFA accepts an array representing a sequence of symbols

5 Data::DFA::Parser::final - Returns whether we are currently in a final state or not

6 Data::DFA::Parser::next - Returns an array of symbols that would be accepted in the current state

7 dumpAsJson - Create a JSON representation {transitions=>{symbol=>state}, finalStates=>{state=>1}}

8 element - One element.

9 except - Choice from amongst all symbols except the ones mentioned

10 finalState - Check whether any of the specified states in the NFA are final

11 fromExpr - Create a DFA from a regular expression.

12 fromNfa - Create a DFA from an NFA.

13 newDFA - Create a new DFA

14 newState - Create a new DFA state

15 oneOrMore - One or more repetitions of a sequence of elements.

16 optional - An optional sequence of element.

17 parseDtdElement - Convert the Dtd Element definition in $stringto a DFA

18 parser - Create a parser from a deterministic finite state automaton constructed from a regular expression.

19 print - Print DFA to a string and optionally to STDERR or STDOUT

20 printAsExpr - Print a $dfa as an expression

21 printAsExpr2 - Print a DFA $dfa_ in expression form

22 printAsRe - Print a $dfa as a regular expression

23 removeDuplicatedStates - Remove duplicated states in a DFA

24 sequence - Sequence of elements.

25 superState - Create super states from existing superstate

26 superStates - Create super states from existing superstate

27 symbols - Return an array of all the symbols accepted by the DFA

28 transitionOnSymbol - The super state reached by transition on a symbol from a specified state

29 zeroOrMore - Zero or more repetitions of a sequence of elements.

Installation

This module is written in 100% Pure Perl and, thus, it is easy to read, comprehend, use, modify and install via cpan:

sudo cpan install Data::DFA

Author

philiprbrenan@gmail.com

http://www.appaapps.com

Copyright

Copyright (c) 2016-2019 Philip R Brenan.

This module is free software. It may be used, redistributed and/or modified under the same terms as Perl itself.