PageRenderTime 54ms CodeModel.GetById 19ms RepoModel.GetById 0ms app.codeStats 0ms

/FLAT-Legacy-FA/lib/FLAT/Legacy/FA/PFA.pm

https://github.com/estrabd/perl-flat
Perl | 976 lines | 783 code | 65 blank | 128 comment | 76 complexity | 80e062ef23d47ed06ca7d9ae41c3ae9f MD5 | raw file
  1. # $Revision: 1.5 $ $Date: 2006/03/02 21:00:28 $ $Author: estrabd $
  2. package FLAT::Legacy::FA::PFA;
  3. use parent 'FLAT::Legacy::FA';
  4. use strict;
  5. use Carp;
  6. use FLAT::Legacy::FA::NFA;
  7. use Data::Dumper;
  8. sub new {
  9. my $class = shift;
  10. bless {
  11. _START_NODES => [], # start node - subset of nodes to start on;
  12. _NODES => [], # nodes - nodes make up nodes in PFA
  13. _ACTIVE_NODES => [], # list of active nodes - corresponds to a "node"
  14. _FINAL_NODES => [], # Set of final node - a string is accepted when set of active nodes
  15. # is exactly this and end of string is encountered
  16. _SYMBOLS => [], # Symbols
  17. _TRANSITIONS => {}, # Nodal transitions on symbol (gamma functions)
  18. _EPSILON => 'epsilon', # how an epsilon transition is represented
  19. _LAMBDA => 'lambda', # how lambda transitions is represented
  20. _TIED => [], # stores look up of tied nodes; computed when
  21. # $self->find_tied() is called
  22. }, $class;
  23. }
  24. sub load_file {
  25. my $self = shift;
  26. my $file = shift;
  27. if (-e $file) {
  28. open(PFA, "<$file");
  29. my $string = undef;
  30. while (<PFA>) {
  31. $string .= $_;
  32. }
  33. close(PFA);
  34. $self->load_string($string);
  35. }
  36. }
  37. sub load_string {
  38. my $self = shift;
  39. my $string = shift;
  40. my @lines = split("\n", $string);
  41. my $CURR_NODE = undef;
  42. foreach (@lines) {
  43. # strip comments
  44. $_ =~ s/\s*#.*$//;
  45. # check if line is a node, transition, or keyword
  46. if (m/^\s*([\w\d]*)\s*:\s*$/) {
  47. #print STDERR "Found transitions for node $1\n";
  48. $self->add_node($1);
  49. $CURR_NODE = $1;
  50. }
  51. elsif (m/^\s*([\w\d]*)\s*([\w\d,]*)\s*$/ && !m/^$/) {
  52. # treat as transition
  53. #print STDERR "Input: '$1' goes to $2\n";
  54. my @s = split(',', $2);
  55. $self->add_transition($CURR_NODE, $1, @s);
  56. $self->add_symbol($1);
  57. }
  58. elsif (m/^\s*([\w\d]*)\s*::\s*([\w\d,]*)\s*$/ && !m/^$/) {
  59. # Check for known header keywords
  60. my $val = $2;
  61. if ($1 =~ m/START/i) {
  62. my @s = split(',', $val);
  63. $self->set_start(@s);
  64. }
  65. elsif ($1 =~ m/FINAL/i) {
  66. my @s = split(',', $val);
  67. $self->add_final(@s);
  68. }
  69. elsif ($1 =~ m/EPSILON/i) {
  70. $self->set_epsilon($val);
  71. }
  72. elsif ($1 =~ m/LAMBDA/i) {
  73. $self->set_lambda($val);
  74. }
  75. else {
  76. print STDERR "WARNING: $1 is not a valid header...\n";
  77. }
  78. }
  79. }
  80. $self->find_tied();
  81. return;
  82. }
  83. sub jump_start {
  84. my $self = shift;
  85. my $PFA = FLAT::Legacy::FA::PFA->new();
  86. my $symbol = shift;
  87. if (!defined($symbol)) {
  88. $symbol = $PFA->get_epsilon_symbol();
  89. }
  90. else {
  91. chomp($symbol);
  92. }
  93. my $newstart = crypt(rand 8, join('', [rand 8, rand 8]));
  94. my $newfinal = crypt(rand 8, join('', [rand 8, rand 8]));
  95. # add states
  96. $PFA->add_node($newstart, $newfinal);
  97. # add symbol
  98. $PFA->add_symbol($symbol);
  99. # set start and final
  100. $PFA->set_start($newstart);
  101. $PFA->add_final($newfinal);
  102. # add single transition
  103. $PFA->add_transition($newstart, $symbol, $newfinal);
  104. return $PFA;
  105. }
  106. sub find_tied {
  107. my $self = shift;
  108. my $lambda = $self->get_lambda_symbol();
  109. my %tied = ();
  110. foreach my $node ($self->get_nodes()) {
  111. my @trans = $self->get_lambda_transitions($node);
  112. if (@trans) {
  113. my $name = $self->serialize_name(@trans);
  114. if (!defined($tied{$name})) {
  115. $tied{$name} = [];
  116. }
  117. push(@{$tied{$name}}, $node);
  118. }
  119. }
  120. foreach my $t (keys(%tied)) {
  121. push(@{$self->{_TIED}}, [@{$tied{$t}}]);
  122. }
  123. return;
  124. }
  125. sub get_tied {
  126. my $self = shift;
  127. return @{$self->{_TIED}};
  128. }
  129. sub has_tied {
  130. my $self = shift;
  131. my @testset = @_;
  132. my $ok = 0;
  133. foreach my $tied ($self->get_tied()) {
  134. my $allornone = 0;
  135. foreach my $tn (@{$tied}) {
  136. #if $tn is in @testset, increment $allornone
  137. if ($self->is_member($tn, @testset)) {
  138. $allornone++;
  139. }
  140. }
  141. # if $allornone is equal to the number of items in the tied set,
  142. # assume that the entire set is in @testset thus satisfiying the
  143. # tied requirement for lambda transitions
  144. if ($allornone == @{$tied}) {
  145. $ok++;
  146. last;
  147. }
  148. }
  149. return $ok;
  150. }
  151. sub extract_tied {
  152. my $self = shift;
  153. my @testset = @_;
  154. my @ret = ();
  155. foreach my $tied ($self->get_tied()) {
  156. my $count = 0;
  157. my @tmp = ();
  158. foreach my $tn (@{$tied}) {
  159. #if $tn is in @testset, increment $count
  160. if ($self->is_member($tn, @testset)) {
  161. push(@tmp, $tn);
  162. $count++;
  163. }
  164. }
  165. if ($count == @{$tied}) {
  166. foreach (@tmp) {
  167. if (!$self->is_member($_, @ret)) {
  168. push(@ret, $_);
  169. }
  170. }
  171. }
  172. }
  173. return @ret;
  174. }
  175. sub to_nfa {
  176. my $self = shift;
  177. my @Dstates = (); # stack of new states to find transitions for
  178. my %Dtran = (); # hash of serialized state names that have been searched
  179. # New NFA object reference
  180. my $NFA = FLAT::Legacy::FA::NFA->new();
  181. $NFA->set_epsilon($self->get_epsilon_symbol());
  182. # Initialize NFA start state by performing e-closure on the PFA start state
  183. my @Start = $self->get_start();
  184. # Add this state to Dstates - subsets stored as anonymous arrays (no faking here!)
  185. push(@Dstates, [sort(@Start)]);
  186. # Serialize subset into new state name - i.e, generate string-ified name
  187. my $ns = $self->serialize_name(@Start);
  188. # add to Dtran as well for tracking
  189. $Dtran{$ns}++;
  190. # serialize final node set
  191. my $final_state = $self->serialize_name($self->get_final());
  192. # set this state as final - since there will be only ONE!
  193. $NFA->add_final($final_state);
  194. $NFA->add_state($final_state);
  195. # Add start state to NFA (placeholder Dtran not used)
  196. $NFA->set_start($ns);
  197. # Add new state (serialized name) to NFA state array
  198. $NFA->add_state($ns);
  199. # Loop until Dstate stack is exhausted
  200. while (@Dstates) {
  201. # shift next state off to check
  202. my @T = @{pop @Dstates};
  203. # Serialize subset into a string name
  204. my $CURR_STATE = $self->serialize_name(@T);
  205. #print "$CURR_STATE\n";
  206. # loop over each input symbol
  207. foreach my $symbol ($self->get_symbols()) {
  208. if ($symbol eq $self->get_lambda_symbol() && $self->has_tied(@T)) {
  209. # get flattened list of all tied nodes in @T
  210. my @tied = $self->extract_tied(@T);
  211. my @new = ();
  212. my @next = ();
  213. foreach my $t (@tied) {
  214. my @trans = $self->get_lambda_transitions($t);
  215. foreach (@trans) {
  216. if (!$self->is_member($_, @new)) {
  217. push(@new, $_);
  218. }
  219. } # foreach (@trans)
  220. } # foreach my $t (@tied)
  221. # @next contains new, obviously
  222. push(@next, @new);
  223. # @next also contains @T - @tied
  224. push(@next, $self->compliment(\@T, \@tied));
  225. # see if the resulting state can be added to @Dstates
  226. my $state = $self->serialize_name(@next);
  227. if (!defined($Dtran{$state})) {
  228. push(@Dstates, [sort(@next)]);
  229. $Dtran{$state}++;
  230. # add transition to $NFA
  231. }
  232. $NFA->add_transition($CURR_STATE, $self->get_epsilon_symbol(), $state);
  233. }
  234. elsif ($symbol ne $self->get_lambda_symbol()) {
  235. foreach my $node (@T) {
  236. if (defined($self->{_TRANSITIONS}{$node}{$symbol})) {
  237. my @new = $self->get_transition_on($node, $symbol);
  238. foreach my $new (@new) {
  239. my @next = $self->compliment(\@T, [$node]);
  240. push(@next, $new);
  241. my $state = $self->serialize_name(@next);
  242. if (!defined($Dtran{$state})) {
  243. push(@Dstates, [sort(@next)]);
  244. $Dtran{$state}++;
  245. }
  246. # add transition to $NFA
  247. $NFA->add_transition($CURR_STATE, $symbol, $state);
  248. } # foreach my $new (@new)
  249. }
  250. } # foreach my $node (@T)
  251. }
  252. } # foreach my $symbol ($self->get_symbols())
  253. }
  254. return $NFA;
  255. }
  256. sub serialize_name {
  257. my $self = shift;
  258. # note that the nature of Perl subs causes @_ to be flattened
  259. my $name = join('_', sort(@_));
  260. return $name;
  261. }
  262. sub set_start {
  263. my $self = shift;
  264. # flushes out current start nodes, and saves in entire list of provided nodes
  265. $self->{_START_NODES} = [@_];
  266. # these nodes are also reset as the default active nodes
  267. $self->set_active(@_);
  268. # add to node list if not already there
  269. $self->add_node(@_);
  270. return;
  271. }
  272. sub get_start {
  273. my $self = shift;
  274. return @{$self->{_START_NODES}};
  275. }
  276. sub set_active {
  277. my $self = shift;
  278. $self->{_ACTIVE_NODES} = [@_];
  279. # add to node list if not already there
  280. return;
  281. }
  282. sub get_active {
  283. my $self = shift;
  284. return @{$self->{_ACTIVE_NODES}};
  285. }
  286. sub add_node {
  287. my $self = shift;
  288. foreach my $node (@_) {
  289. if (!$self->is_node($node)) {
  290. push(@{$self->{_NODES}}, $node);
  291. }
  292. }
  293. return;
  294. }
  295. sub get_nodes {
  296. my $self = shift;
  297. return @{$self->{_NODES}};
  298. }
  299. sub add_transition {
  300. my $self = shift;
  301. my $node = shift;
  302. my $symbol = shift;
  303. $self->add_symbol($symbol);
  304. foreach my $next (@_) {
  305. if (!$self->is_member($next, @{$self->{_TRANSITIONS}{$node}{$symbol}})) {
  306. push(@{$self->{_TRANSITIONS}{$node}{$symbol}}, $next);
  307. }
  308. }
  309. return;
  310. }
  311. sub get_transition_on {
  312. my $self = shift;
  313. my $node = shift;
  314. my $symbol = shift;
  315. my @ret = undef;
  316. if ($self->is_node($node) && $self->is_symbol($symbol)) {
  317. if (defined($self->{_TRANSITIONS}{$node}{$symbol})) {
  318. @ret = @{$self->{_TRANSITIONS}{$node}{$symbol}};
  319. }
  320. }
  321. return @ret;
  322. }
  323. sub is_start {
  324. my $self = shift;
  325. return $self->is_member(shift, $self->get_start());
  326. }
  327. sub set_epsilon {
  328. my $self = shift;
  329. my $epsilon = shift;
  330. $self->{_EPSILON} = $epsilon;
  331. return;
  332. }
  333. sub get_epsilon_symbol {
  334. my $self = shift;
  335. return $self->{_EPSILON};
  336. }
  337. sub get_epsilon_transitions {
  338. my $self = shift;
  339. my $node = shift;
  340. my @ret = ();
  341. if ($self->is_node($node)) {
  342. if (defined($self->{_TRANSITIONS}{$node}{$self->get_epsilon_symbol()})) {
  343. @ret = @{$self->{_TRANSITIONS}{$node}{$self->get_epsilon_symbol()}};
  344. }
  345. }
  346. return @ret;
  347. }
  348. sub delete_epsilon {
  349. my $self = shift;
  350. delete($self->{_EPSILON});
  351. return;
  352. }
  353. sub set_lambda {
  354. my $self = shift;
  355. my $lambda = shift;
  356. $self->{_LAMBDA} = $lambda;
  357. return;
  358. }
  359. sub get_lambda_symbol {
  360. my $self = shift;
  361. return $self->{_LAMBDA};
  362. }
  363. sub get_lambda_transitions {
  364. my $self = shift;
  365. my $node = shift;
  366. my @ret = ();
  367. if ($self->is_node($node)) {
  368. if (defined($self->{_TRANSITIONS}{$node}{$self->get_lambda_symbol()})) {
  369. @ret = @{$self->{_TRANSITIONS}{$node}{$self->get_lambda_symbol()}};
  370. }
  371. }
  372. return @ret;
  373. }
  374. sub delete_lambda {
  375. my $self = shift;
  376. delete($self->{_LAMBDA});
  377. return;
  378. }
  379. sub is_node {
  380. my $self = shift;
  381. return $self->is_member(shift, $self->get_nodes());
  382. }
  383. sub add_final {
  384. my $self = shift;
  385. foreach my $node (@_) {
  386. if (!$self->is_final($node)) {
  387. push(@{$self->{_FINAL_NODES}}, $node);
  388. }
  389. }
  390. return;
  391. }
  392. sub get_final {
  393. my $self = shift;
  394. return @{$self->{_FINAL_NODES}}
  395. }
  396. sub is_final {
  397. my $self = shift;
  398. return $self->is_member(shift, $self->get_final());
  399. }
  400. sub clone {
  401. my $self = shift;
  402. my $PFA = FLAT::Legacy::FA::PFA->new();
  403. $PFA->add_node($self->get_nodes());
  404. $PFA->add_final($self->get_final());
  405. $PFA->add_symbol($self->get_symbols());
  406. $PFA->set_start($self->get_start());
  407. $PFA->set_epsilon($self->get_epsilon_symbol);
  408. $PFA->set_lambda($self->get_lambda_symbol);
  409. foreach my $node ($self->get_nodes()) {
  410. foreach my $symbol (keys %{$self->{_TRANSITIONS}{$node}}) {
  411. $PFA->add_transition($node, $symbol, @{$self->{_TRANSITIONS}{$node}{$symbol}});
  412. }
  413. }
  414. return $PFA;
  415. }
  416. sub append_pfa {
  417. my $self = shift;
  418. my $PFA = shift;
  419. # clone $PFA
  420. my $PFA1 = $PFA->clone();
  421. # pinch off self - ensures a single final node to append PFA1 to
  422. $self->pinch();
  423. # ensure unique node names
  424. $self->ensure_unique_nodes($PFA1, crypt(rand 8, join('', [rand 8, rand 8])));
  425. # sychronize epsilon symbol
  426. if ($PFA1->get_epsilon_symbol() ne $self->get_epsilon_symbol()) {
  427. $PFA1->rename_symbol($PFA1->get_epsilon_symbol(), $self->get_epsilon_symbol());
  428. }
  429. # add new nodes from PFA1
  430. foreach my $node ($PFA1->get_nodes()) {
  431. $self->add_node($node);
  432. }
  433. # add new symbols from PFA1
  434. foreach my $symbol ($PFA1->get_symbols()) {
  435. $self->add_symbol($symbol);
  436. }
  437. # add epsilon transitions from PFA1
  438. foreach my $node (keys %{$PFA1->{_TRANSITIONS}}) {
  439. foreach my $symbol (keys %{$PFA1->{_TRANSITIONS}{$node}}) {
  440. $self->add_transition($node, $symbol, @{$PFA1->{_TRANSITIONS}{$node}{$symbol}});
  441. }
  442. }
  443. # remove current final node and saves it for future reference
  444. my $oldfinal = pop(@{$self->{_FINAL_NODES}});
  445. # add new epsilon transition from the old final node of $self to the start nodes of PFA1
  446. $self->add_transition($oldfinal, $self->get_epsilon_symbol(), $PFA1->get_start());
  447. # mark the final node of PFA1 as the final node of $self
  448. $self->add_final($PFA1->get_final());
  449. # nodes not renumbered - can done explicity by user
  450. return;
  451. }
  452. sub prepend_pfa {
  453. my $self = shift;
  454. my $PFA = shift;
  455. # clone $PFA
  456. my $PFA1 = $PFA->clone();
  457. # pinch off $PFA1 to ensure a single final node to join self to
  458. $PFA1->pinch();
  459. # ensure unique node names
  460. $self->ensure_unique_nodes($PFA1, crypt(rand 8, join('', [rand 8, rand 8])));
  461. # sychronize epsilon symbol
  462. if ($PFA1->get_epsilon_symbol() ne $self->get_epsilon_symbol()) {
  463. $PFA1->rename_symbol($PFA1->get_epsilon_symbol(), $self->get_epsilon_symbol());
  464. }
  465. # add new nodes from PFA1
  466. foreach my $node ($PFA1->get_nodes()) {
  467. $self->add_node($node);
  468. }
  469. # add new symbols from PFA1
  470. foreach my $symbol ($PFA1->get_symbols()) {
  471. $self->add_symbol($symbol);
  472. }
  473. # add transitions from PFA1
  474. foreach my $node (keys %{$PFA1->{_TRANSITIONS}}) {
  475. foreach my $symbol (keys %{$PFA1->{_TRANSITIONS}{$node}}) {
  476. $self->add_transition($node, $symbol, @{$PFA1->{_TRANSITIONS}{$node}{$symbol}});
  477. }
  478. }
  479. # remove current final node of $PFA1 and saves it for future reference
  480. my $oldfinal = pop(@{$PFA1->{_FINAL_NODES}});
  481. # add new epsilon transition from the old final node of $PFA1 to the start nodes of $self
  482. $self->add_transition($oldfinal, $self->get_epsilon_symbol(), $self->get_start());
  483. # mark the final node of PFA1 as the final node of $self
  484. $self->set_start($PFA1->get_start());
  485. # nodes not renumbered - can done explicity by user
  486. return;
  487. }
  488. sub or_pfa {
  489. my $self = shift;
  490. my $PFA1 = shift;
  491. # (NOTE: epsilon pinch not used)
  492. $self->ensure_unique_nodes($PFA1, crypt(rand 8, join('', [rand 8, rand 8])));
  493. # sychronize epsilon symbol
  494. if ($PFA1->get_epsilon_symbol() ne $self->get_epsilon_symbol()) {
  495. $PFA1->rename_symbol($PFA1->get_epsilon_symbol(), $self->get_epsilon_symbol());
  496. }
  497. # add new nodes from PFA1
  498. foreach my $node ($PFA1->get_nodes()) {
  499. $self->add_node($node);
  500. }
  501. # add new symbols from PFA1
  502. foreach my $symbol ($PFA1->get_symbols()) {
  503. $self->add_symbol($symbol);
  504. }
  505. # add transitions from PFA1
  506. foreach my $node (keys %{$PFA1->{_TRANSITIONS}}) {
  507. foreach my $symbol (keys %{$PFA1->{_TRANSITIONS}{$node}}) {
  508. $self->add_transition($node, $symbol, @{$PFA1->{_TRANSITIONS}{$node}{$symbol}});
  509. }
  510. }
  511. # save old start nodes
  512. my @start1 = $self->get_start();
  513. my @start2 = $PFA1->get_start();
  514. # create new start node
  515. my $newstart = crypt(rand 8, join('', [rand 8, rand 8]));
  516. $self->add_node($newstart);
  517. # set this new node as the start
  518. $self->set_start($newstart);
  519. # add the final node from PFA1
  520. $self->add_final($PFA1->get_final());
  521. # create transitions to old start nodes from new start node
  522. $self->add_transition($newstart, $self->get_epsilon_symbol(), @start1);
  523. $self->add_transition($newstart, $self->get_epsilon_symbol(), @start2);
  524. # pinch the final states into a single final state - required for PFA->to_nfa to work properly
  525. $self->pinch();
  526. return;
  527. }
  528. sub interleave_pfa {
  529. my $self = shift;
  530. my $PFA1 = shift;
  531. # (NOTE: epsilon pinch not used)
  532. # ensure unique node names
  533. $self->ensure_unique_nodes($PFA1, crypt(rand 8, join('', [rand 8, rand 8])));
  534. # sychronize epsilon symbol
  535. if ($PFA1->get_epsilon_symbol() ne $self->get_epsilon_symbol()) {
  536. $PFA1->rename_symbol($PFA1->get_epsilon_symbol(), $self->get_epsilon_symbol());
  537. }
  538. # sychronize lambda symbol
  539. if ($PFA1->get_lambda_symbol() ne $self->get_lambda_symbol()) {
  540. $PFA1->rename_symbol($PFA1->get_lambda_symbol(), $self->get_lambda_symbol());
  541. }
  542. # add new nodes from PFA1
  543. foreach my $node ($PFA1->get_nodes()) {
  544. $self->add_node($node);
  545. }
  546. # add new symbols from PFA1
  547. foreach my $symbol ($PFA1->get_symbols()) {
  548. $self->add_symbol($symbol);
  549. }
  550. # add transitions from PFA1
  551. foreach my $node (keys %{$PFA1->{_TRANSITIONS}}) {
  552. foreach my $symbol (keys %{$PFA1->{_TRANSITIONS}{$node}}) {
  553. $self->add_transition($node, $symbol, @{$PFA1->{_TRANSITIONS}{$node}{$symbol}});
  554. }
  555. }
  556. # save old start nodes
  557. my @start1 = $self->get_start();
  558. my @start2 = $PFA1->get_start();
  559. # create new start node
  560. my $newstart = crypt(rand 8, join('', [rand 8, rand 8]));
  561. $self->add_node($newstart);
  562. # set this new node as the start
  563. $self->set_start($newstart);
  564. # create transitions to old start nodes from new start node
  565. $self->add_transition($newstart, $self->get_lambda_symbol(), @start1);
  566. $self->add_transition($newstart, $self->get_lambda_symbol(), @start2);
  567. # create new final node
  568. # save final nodes from self and PFA1
  569. my @final_tmp = $self->get_final();
  570. push(@final_tmp, $PFA1->get_final());
  571. # reset final node array
  572. my $newfinal = crypt(rand 8, join('', [rand 8, rand 8]));
  573. $self->add_node($newfinal);
  574. $self->{_FINAL_NODES} = [$newfinal];
  575. # add a lambda transition from each of the old final nodes to the new final node
  576. foreach my $final_tmp (@final_tmp) {
  577. $self->add_transition($final_tmp, $self->get_lambda_symbol(), $newfinal);
  578. }
  579. return;
  580. }
  581. sub kleene {
  582. my $self = shift;
  583. my $newstart = crypt(rand 8, join('', [rand 8, rand 8]));
  584. my $newfinal = crypt(rand 8, join('', [rand 8, rand 8]));
  585. # pinch off self - ensures a single final node
  586. $self->pinch();
  587. my @oldstart = $self->get_start();
  588. my $oldfinal = pop(@{$self->{_FINAL_NODES}});
  589. # add new nodes
  590. $self->add_node($newstart, $newfinal);
  591. # set start
  592. $self->set_start($newstart);
  593. # set final
  594. $self->add_final($newfinal);
  595. # $oldfinal->$oldstart
  596. $self->add_transition($oldfinal, $self->get_epsilon_symbol(), @oldstart);
  597. # $newstart->$oldstart
  598. $self->add_transition($newstart, $self->get_epsilon_symbol(), @oldstart);
  599. # $oldfinal->$newfinal
  600. $self->add_transition($oldfinal, $self->get_epsilon_symbol(), $newfinal);
  601. # $newstart->$newfinal
  602. $self->add_transition($newstart, $self->get_epsilon_symbol(), $newfinal);
  603. return;
  604. }
  605. sub pinch {
  606. my $self = shift;
  607. # do only if there is more than one final node
  608. my $newfinal = join(',', @{$self->{_FINAL_NODES}});
  609. $self->add_node($newfinal);
  610. while (@{$self->{_FINAL_NODES}}) {
  611. # design decision - remove all final nodes so that the common
  612. # one is the only final node and all former final nodes have an
  613. # epsilon transition to it - could prove costly for NFA->to_dfa, so
  614. # this could change
  615. my $node = pop(@{$self->{_FINAL_NODES}});
  616. # add new transition unless it is to the final node itself
  617. if ($node ne $newfinal) {
  618. $self->add_transition($node, $self->get_epsilon_symbol(), $newfinal)
  619. }
  620. }
  621. $self->add_final($newfinal);
  622. # FA->number_nodes() could be used here, but the user may not
  623. # want the nodes renamed, so it can be used explicitly
  624. return;
  625. }
  626. sub rename_node {
  627. my $self = shift;
  628. my $oldname = shift;
  629. my $newname = shift;
  630. # make sure $oldname is an actual node in this FA
  631. if (!$self->is_node($newname)) {
  632. if ($self->is_node($oldname)) {
  633. # replace name in _NODES array
  634. my $i = 0;
  635. foreach ($self->get_nodes()) {
  636. if ($_ eq $oldname) {
  637. $self->{_NODES}[$i] = $newname;
  638. last;
  639. }
  640. $i++;
  641. }
  642. # replace name if start node
  643. if ($self->is_start($oldname)) {
  644. my $i = 0;
  645. foreach my $n ($self->get_start()) {
  646. if ($n eq $oldname) {
  647. $self->{_START_NODES}[$i] = $newname;
  648. }
  649. $i++;
  650. }
  651. }
  652. # replace transitions
  653. foreach my $node (keys %{$self->{_TRANSITIONS}}) {
  654. foreach my $symbol (keys %{$self->{_TRANSITIONS}{$node}}) {
  655. my $j = 0;
  656. foreach my $next (@{$self->{_TRANSITIONS}{$node}{$symbol}}) {
  657. # rename destination nodes
  658. if ($self->{_TRANSITIONS}{$node}{$symbol}[$j] eq $oldname) {
  659. $self->{_TRANSITIONS}{$node}{$symbol}[$j] = $newname;
  660. }
  661. $j++;
  662. }
  663. # rename start node
  664. if ($node eq $oldname) {
  665. $self->add_transition($newname, $symbol, @{$self->{_TRANSITIONS}{$node}{$symbol}});
  666. }
  667. }
  668. if ($node eq $oldname) {
  669. # delete all transitions of old node
  670. delete($self->{_TRANSITIONS}{$node});
  671. }
  672. }
  673. # replace final nodes
  674. $i = 0;
  675. foreach ($self->get_final()) {
  676. if ($_ eq $oldname) {
  677. $self->{_FINAL_NODES}[$i] = $newname;
  678. }
  679. $i++;
  680. }
  681. # replace tied nodes
  682. $i = 0;
  683. foreach ($self->get_tied()) {
  684. my $tied = $_;
  685. my $j = 0;
  686. foreach my $node (@{$tied}) {
  687. if ($node eq $oldname) {
  688. $self->{_TIED}[$i]->[$j] = $newname;
  689. }
  690. $j++;
  691. }
  692. $i++;
  693. }
  694. }
  695. else {
  696. print STDERR "Warning: $oldname is not a current node\n";
  697. }
  698. }
  699. else {
  700. print STDERR "Warning: $newname is a current node\n";
  701. }
  702. return;
  703. }
  704. sub ensure_unique_nodes {
  705. my $self = shift;
  706. my $PFA1 = shift;
  707. my $disambigator = shift;
  708. chomp($disambigator);
  709. foreach ($self->get_nodes()) {
  710. my $node1 = $_;
  711. while ($PFA1->is_node($node1) && !$self->is_node($disambigator)) {
  712. $self->rename_node($node1, $disambigator);
  713. # re-assign $node1 with new name
  714. $node1 = $disambigator;
  715. # get new disambiguator just incase this is not unique
  716. $disambigator = crypt(rand 8, join('', [rand 8, rand 8]));
  717. }
  718. }
  719. return;
  720. }
  721. sub number_nodes {
  722. my $self = shift;
  723. my $number = 0;
  724. # generate 5 character string of random numbers
  725. my $prefix = crypt(rand 8, join('', [rand 8, rand 8]));
  726. # add random prefix to node names
  727. foreach ($self->get_nodes()) {
  728. $self->rename_node($_, $prefix . "_$number");
  729. $number++;
  730. }
  731. # rename nodes as actual numbers
  732. $number = 0;
  733. foreach ($self->get_nodes()) {
  734. $self->rename_node($_, $number);
  735. $number++;
  736. }
  737. return;
  738. }
  739. sub append_node_names {
  740. my $self = shift;
  741. my $suffix = shift;
  742. if (defined($suffix)) {
  743. chomp($suffix);
  744. }
  745. else {
  746. $suffix = '';
  747. }
  748. foreach ($self->get_nodes()) {
  749. $self->rename_node($_, "$_" . $suffix);
  750. }
  751. return;
  752. }
  753. sub prepend_node_names {
  754. my $self = shift;
  755. my $prefix = shift;
  756. if (defined($prefix)) {
  757. chomp($prefix);
  758. }
  759. else {
  760. $prefix = '';
  761. }
  762. foreach ($self->get_nodes()) {
  763. $self->rename_node($_, $prefix . "$_");
  764. }
  765. return;
  766. }
  767. # renames symbol
  768. sub rename_symbol {
  769. my $self = shift;
  770. my $oldsymbol = shift;
  771. my $newsymbol = shift;
  772. # make sure $oldsymbol is a symbol and do not bother if
  773. # $newsymbol ne $oldsymbol
  774. if ($self->is_symbol($oldsymbol) && $newsymbol ne $oldsymbol) {
  775. # change in $self->{_SYMBOLS}
  776. my $i = 0;
  777. foreach ($self->get_symbols()) {
  778. if ($_ eq $oldsymbol) {
  779. $self->{_SYMBOLS}[$i] = $newsymbol;
  780. last;
  781. }
  782. $i++;
  783. }
  784. # change in $self->{_TRANSITIONS}
  785. # replace transition symbols
  786. foreach my $node (keys %{$self->{_TRANSITIONS}}) {
  787. foreach my $symbol (keys %{$self->{_TRANSITIONS}{$node}}) {
  788. if ($symbol eq $oldsymbol) {
  789. $self->add_transition($node, $newsymbol, @{$self->{_TRANSITIONS}{$node}{$symbol}});
  790. delete($self->{_TRANSITIONS}{$node}{$symbol});
  791. }
  792. }
  793. }
  794. # also look at $self->{_EPSILON}
  795. if ($self->get_epsilon_symbol() eq $oldsymbol) {
  796. $self->set_epsilon($newsymbol);
  797. }
  798. }
  799. return;
  800. }
  801. sub info {
  802. my $self = shift;
  803. my $out = '';
  804. $out .= sprintf("Nodes : ");
  805. foreach ($self->get_nodes()) {
  806. $out .= sprintf "'$_' ";
  807. }
  808. $out .= sprintf("\nStart State : '%s'\n", join(',', $self->get_start()));
  809. $out .= sprintf("Final State : '%s'\n", join(',', $self->get_final()));
  810. $out .= sprintf("Alphabet : ");
  811. foreach ($self->get_symbols()) {
  812. $out .= sprintf "'$_' ";
  813. }
  814. if (defined($self->get_epsilon_symbol())) {
  815. $out .= sprintf("\nEPSILON Symbol : '%s'", $self->get_epsilon_symbol());
  816. }
  817. if (defined($self->get_lambda_symbol())) {
  818. $out .= sprintf("\nLAMBDA Symbol : '%s'", $self->get_lambda_symbol());
  819. }
  820. $out .= sprintf("\nTied Nodes : ");
  821. foreach my $t ($self->get_tied()) {
  822. $out .= sprintf(join(',', @{$t}));
  823. $out .= '; ';
  824. }
  825. $out .= sprintf("\nTransitions :\n");
  826. foreach my $node ($self->get_nodes()) {
  827. foreach my $symbol (keys %{$self->{_TRANSITIONS}{$node}}) {
  828. if ($symbol ne $self->get_epsilon_symbol() && $symbol ne $self->get_lambda_symbol()) {
  829. $out .=
  830. sprintf("\t('%s'),'%s' --> '%s'\n", $node, $symbol, join('\',\'', $self->get_transition_on($node, $symbol)));
  831. }
  832. elsif ($symbol ne $self->get_lambda_symbol()) {
  833. $out .= sprintf("\t('%s'),'%s' --> '%s'\n", $node, $symbol, join('\',\'', $self->get_epsilon_transitions($node)));
  834. }
  835. else {
  836. $out .= sprintf("\t('%s'),'%s' --> '%s'\n", $node, $symbol, join('\',\'', $self->get_lambda_transitions($node)));
  837. }
  838. }
  839. }
  840. return $out;
  841. }
  842. sub serialize {
  843. my $self = shift;
  844. my $out = '';
  845. $out .= sprintf("START :: %s\n", $self->get_start());
  846. $out .= sprintf("FINAL :: %s\n", join(',', $self->get_final()));
  847. if (defined($self->get_epsilon_symbol())) {
  848. $out .= sprintf("EPSILON :: %s\n", $self->get_epsilon_symbol());
  849. }
  850. if (defined($self->get_lambda_symbol())) {
  851. $out .= sprintf("LAMBDA :: %s\n", $self->get_lambda_symbol());
  852. }
  853. $out .= "\n";
  854. foreach my $node ($self->get_nodes()) {
  855. $out .= sprintf("%s:\n", $node);
  856. foreach my $symbol (keys %{$self->{_TRANSITIONS}{$node}}) {
  857. if ($symbol ne $self->get_epsilon_symbol() && $symbol ne $self->get_lambda_symbol()) {
  858. $out .= sprintf("$symbol %s\n", join(',', $self->get_transition_on($node, $symbol)));
  859. }
  860. elsif ($symbol ne $self->get_lambda_symbol()) {
  861. $out .= sprintf("$symbol %s\n", join(',', $self->get_epsilon_transitions($node)));
  862. }
  863. else {
  864. $out .= sprintf("$symbol %s\n", join(',', $self->get_lambda_transitions($node)));
  865. }
  866. }
  867. $out .= sprintf("\n");
  868. }
  869. return $out;
  870. }
  871. 1;
  872. __END__
  873. =head1 NAME
  874. PFA - A parallel finite automata base class
  875. =head1 SYNOPSIS
  876. use FLAT::Legacy::FA::PFA;
  877. =head1 DESCRIPTION
  878. This module is implements a paralle finite automata,
  879. and the conversion of such to a non deterministic
  880. finite automata;
  881. One key between PFA implementation an PFA & DFA is that the PFA
  882. may contain more than one start node since it may depict
  883. threads of concurrent execution. The main purpose of this
  884. module is to convert a PFA to an PFA.
  885. =head1 AUTHOR
  886. Brett D. Estrade - <estrabd AT mailcan DOT com>
  887. =head1 CAVEATS
  888. Currently, all nodes are stored as labels. There is also
  889. no integrity checking for consistency among the start, final,
  890. and set of all nodes.
  891. =head1 BUGS
  892. I haven't hit any yet :)
  893. =head1 AVAILABILITY
  894. Perl FLaT Project Website at L<http://perl-flat.sourceforge.net/pmwiki>
  895. =head1 ACKNOWLEDGEMENTS
  896. This suite of modules started off as a homework assignment for a compiler
  897. class I took for my MS in computer science at the University of Southern
  898. Mississippi. It then became the basis for my MS research. and thesis.
  899. Mike Rosulek has joined the effort, and is heading up the rewrite of
  900. Perl FLaT, which will soon be released as FLaT 1.0.
  901. =head1 COPYRIGHT
  902. This code is released under the same terms as Perl.
  903. =cut