File Coverage

blib/lib/Soar/Production/Parser.pm
Criterion Covered Total %
statement 71 75 94.6
branch 12 16 75.0
condition 2 3 66.6
subroutine 18 19 94.7
pod 5 5 100.0
total 108 118 91.5


line stmt bran cond sub pod time code
1             #
2             # This file is part of Soar-Production
3             #
4             # This software is copyright (c) 2012 by Nathan Glenn.
5             #
6             # This is free software; you can redistribute it and/or modify it under
7             # the same terms as the Perl 5 programming language system itself.
8             #
9             package Soar::Production::Parser;
10             # ABSTRACT: Parse Soar productions
11              
12 26     26   1761383 use strict;
  26         61  
  26         893  
13 26     26   134 use warnings;
  26         53  
  26         641  
14             #needed for advanced regex expressions
15 26     26   654 use 5.010;
  26         132  
  26         1595  
16              
17             our $VERSION = '0.03'; # VERSION
18             use Exporter::Easy (
19 26         204 OK => [qw(no_comment)]
20 26     26   26677 );
  26         48780  
21 26     26   21589 use Soar::Production::Parser::PRDGrammar;
  26         81  
  26         790  
22 26     26   53682 use Parse::RecDescent;
  26         1487512  
  26         246  
23 26     26   1652 use Carp;
  26         54  
  26         2451  
24 26     26   9750 use Data::Dumper;
  26         68796  
  26         24812  
25              
26              
27              
28             #a regular expression to split text into productions
29             my $splitter = qr/
30             (sp\s+ #start with 'sp'
31             \{ #opening brace
32             ( #save to $2
33             (?: #either
34             \{ (?-1) \} #more braces and recurse
35             | #or
36             (?: #group
37             [^|{}]++ #not bar or braces
38             | #or
39             \| #a bar
40             (?: #group
41             [^\\|]++ #no slashes or bars, no backtracking
42             | #or
43             \\. #slash anything
44             )*+
45             \|
46             )++ #one or more, no backtracking
47             )* #0 or more times
48             ) #end $2
49             \} #ending brace
50             ) #end $1
51             /x;
52              
53             __PACKAGE__->new->_run(shift) unless caller;
54              
55             sub _run {
56 0     0   0 my ($soarParser, $filePath) = @_;
57 0         0 print Dumper($soarParser->productions(file => $filePath, parse => 1) );
58 0         0 return;
59             }
60              
61              
62             sub new {
63 28     28 1 228411 my ($class) = @_;
64 28         1628 my $soarParser = bless {}, $class;
65 28         6077 $soarParser->_init;
66 28         337 return $soarParser;
67             }
68              
69             sub _init {
70 28     28   1376 my ($soarParser) = @_;
71 28         3023 $soarParser->{parser} = Parse::RecDescent->new($Soar::Production::Parser::PRDGrammar::GRAMMAR);
72              
73             #if you wish to debug the grammar, try turning on traces by uncommenting the following lines:
74             # $::RD_TRACE = 1;
75             # $::RD_HINT = 1;
76 28         9250711 return;
77             }
78              
79              
80             sub productions {## no critic RequireArgUnpacking
81 14     14 1 43923 my ($soarParser) = shift;
82 14         115 my %args = (
83             parse => 0,
84             text => undef,
85             file => undef,
86             @_
87             );
88 14 50 66     126 defined $args{text} or defined $args{file}
89             or croak 'Must specify parameter \'file\' or \'text\' to extract productions.';
90              
91 14 100       52 if($args{text}){
92 6         25 return $soarParser->_productions_from_text($args{text}, $args{parse});
93             }
94 8 50       149 if($args{file}){
95             # print "$args{file}\n";
96 8         47 return $soarParser->_productions_from_file($args{file}, $args{parse});
97             }
98             }
99              
100             sub _productions_from_text {
101 6     6   11 my ( $soarParser, $text, $parse) = @_;
102              
103             #remove comments
104 6         15 $text = no_comment($text);
105 6         20 my $productions = _split_text(\$text);
106 6 50       42 return $productions
107             unless($parse);
108              
109 0         0 return $soarParser->get_parses($productions);
110             }
111              
112             sub _productions_from_file {
113 8     8   23 my ( $soarParser, $file, $parse) = @_;
114 8         37 my $text = _readFile($file);
115              
116 8         47 my $productions = _split_text($text);
117 8 100       100 return $productions
118             unless($parse);
119              
120 1         5 return $soarParser->get_parses($productions);
121             }
122              
123             #split text reference into production
124             sub _split_text {
125 14     14   30 my ($text) = @_;
126             #split the text into productions by looking for 'sp { ... }'
127 14         24 my @productions;
128 14         369 while($$text =~ /$splitter/g){
129             # print "found production: $1";
130 2489         34367 push @productions, $1;
131             }
132 14         53 return \@productions;
133             }
134              
135              
136             sub parse_text {
137 2604     2604 1 379246405 my ( $soarParser, $text ) = @_;
138 2604 100       13260 croak 'no text to parse!'
139             unless defined $text;
140             # $soarParser->{input} = \$text;
141 2603         33426 return $soarParser->{parser}->parse($text);
142             }
143              
144              
145              
146             sub get_parses {
147 1     1 1 2 my ($soarParser, $productions) = @_;
148 1         3 my @parses;
149 1         3 for(@$productions){
150             # print STDERR $_;
151 3         222937 push @parses, $soarParser->{parser}->parse($_);
152             }
153 1         79042 return \@parses;
154             }
155              
156             #argument should be an opened file handle
157             #returns string pointer to text
158             sub _readFile {
159 8     8   20 my ($file) = @_;
160 8 50       819 open my $fh, '<', $file
161             or croak "Couldn't open $file";
162              
163 8         212 my $text = q();
164 8         56030 $text .= $_ while (<$fh>);
165              
166 8         193 close $fh;
167 8         54 $text = no_comment($text);
168 8         80 return \$text;
169             }
170              
171              
172             sub no_comment {
173 22     22 1 13489 my ($text) = @_;
174 22         6709 $text =~ s/
175             ( #save in $1
176             \| #literal bar
177             (?: #group
178             \\[|] #an escaped bar
179             |
180             [^|] #or anything but a literal bar
181             )* #zero or more of previous group
182             \| #literal bar
183             ) #end $1
184             | #or
185             (?:;\s*)? # optional semicolon
186             \# # pound character
187             .* #followed by anything
188             / #replace with
189 2290 100       278061 $1||'' # $1 or nothing (the quote if there was one;
190             # no quote will simply remove matching comment)
191             /xeg;
192 22         2240 return $text;
193             }
194             1;
195              
196             __END__