File Coverage

lib/Devel/Trepan/CmdProcessor/Parse/BPLocation.pm
Criterion Covered Total %
statement 50 55 90.9
branch 11 18 61.1
condition 2 3 66.6
subroutine 10 11 90.9
pod 0 4 0.0
total 73 91 80.2


line stmt bran cond sub pod time code
1             #!/usr/bin/perl
2             # A Marpa2 parser for gdb list range hs
3             # Many thanks to Jeffrey Kegler
4              
5 1     1   76956 use 5.010;
  1         12  
6 1     1   5 use strict;
  1         3  
  1         21  
7 1     1   5 use warnings;
  1         2  
  1         31  
8 1     1   504 use Marpa::R2 4.000;
  1         144037  
  1         43  
9              
10 1     1   10 use Exporter;
  1         2  
  1         106  
11              
12             my $grammar_rules = <<'END_OF_GRAMMAR';
13              
14             # Use longest acceptable token match
15             lexeme default = latm => 1
16             :default ::= action => [name,values]
17              
18             # ======== productions ===========
19              
20             location_if ::= location 'if' tokens
21             | location
22             location ::= FILE_LINE | FUNCNAME
23              
24             # If location is just a number is given, the filename is implied
25             location ::= NUMBER
26             FILE_LINE ::= FILENAME ':' NUMBER
27              
28             # "tokens" is used to gobble up stuff after the "if"
29             tokens ::= token+
30             token ::= ':' | FILENAME | FUNCNAME | NUMBER | SYMBOL
31              
32             # ======== tokens ===========
33             :discard ~ whitespace
34              
35             # Note no space is allowed between FILENAME, COLON, and number
36             NUMBER ~ number
37             number ~ digits
38             digits ~ [\d]+
39             whitespace ~ [\s]+
40             FILENAME ~ [^:\s]+
41             FUNCNAME ~ name '()'
42             name ~ name_first_char name_later_chars
43             name_first_char ~ [A-Za-z_]
44             name_later_chars ~ name_later_char*
45             name_later_char ~ [\w]
46             SYMBOL ~ [^:\d]
47              
48             END_OF_GRAMMAR
49              
50             my $range_grammar = Marpa::R2::Scanless::G->new(
51             { source => \$grammar_rules } );
52              
53             package Devel::Trepan::CmdProcessor::Parse::BPLocation;
54 1     1   8 use English qw( -no_match_vars );
  1         2  
  1         7  
55              
56 1     1   356 use vars qw(@EXPORT @ISA);
  1         3  
  1         399  
57             @ISA = qw(Exporter);
58             @EXPORT = qw(bp_location_build parse_bp_location);
59             sub parse_bp_location
60             {
61 4     4 0 1556 my ( $input ) = @_;
62 4         31 my $recce = Marpa::R2::Scanless::R->new( { grammar => $range_grammar } );
63 4         1087 my $input_length = length ${$input};
  4         12  
64 4         14 my $pos = $recce->read($input);
65 4 50       2673 if ( $pos < $input_length ) {
66 0         0 die sprintf qq{Unfinished parse: remainder="%" }, substr(${$input}, $pos);
  0         0  
67             }
68 4         13 my $value_ref = $recce->value();
69 4 50       17150 if ( !$value_ref ) {
70 0         0 die "input read, but there was no parse";
71             }
72 4         98 return $value_ref;
73             }
74              
75             #===== info-building routines ================
76             sub ref2hash
77             {
78 0     0 0 0 my %h = @_;
79 0         0 return \%h;
80             }
81              
82             sub bp_location_build
83             {
84 4     4 0 1347 my $loc = shift;
85 4         10 my $result = {};
86 4 50       25 if (ref $$loc eq 'ARRAY') {
87 4         13 my @ary = @$$loc;
88              
89 4 50       10 if ($ary[0] eq 'location_if') {
90 4         12 $result = location_build($ary[1]);
91 4 100 66     19 if (@ary > 2 && $ary[2] eq 'if') {
92 2         6 $result->{is_conditional} = 1;
93             } else {
94 2         6 $result->{is_conditional} = 0;
95             }
96             }
97             }
98 4         22 return $result;
99             }
100              
101             sub location_build
102             {
103 4     4 0 8 my $loc = shift;
104 4 50       14 if (ref $loc eq 'ARRAY') {
105             # FIXME: handle offset or number $start
106 4         11 my @loc_ary = @$loc;
107 4         7 my $kind = $loc_ary[0];
108 4 50       11 if ($kind eq 'location') {
109 4         8 my $func_or_ary = $loc_ary[1];
110 4 100       21 if (ref $func_or_ary) {
111 2         4 $kind = $func_or_ary->[0];
112 2 50       9 if ($kind eq 'FILE_LINE') {
113             return {
114 2         12 filename => $func_or_ary->[1],
115             line_num => $func_or_ary->[3]
116             }
117             }
118             } else {
119             return {
120 2         9 funcname => $func_or_ary
121             }
122             }
123             }
124             }
125             }
126              
127             # # Demo/test
128             # unless (caller()) {
129             # eval {use Test::More};
130             # eval {use Data::Dumper};
131              
132             # my @test = (
133             # [ 'List.pm:1', 'OK',
134             # {
135             # filename => "List.pm",
136             # is_conditional => 0,
137             # line_num => 1
138             # }],
139             # [ 'abc()', 'OK',
140             # {
141             # funcname => "abc()",
142             # is_conditional => 0,
143             # },
144             # ],
145             # [ 'abs() if 1', 'OK',
146             # {
147             # funcname => "abs()",
148             # is_conditional => 1,
149             # }
150             # ],
151             # [ 'List.pm:10 if y > 3', 'OK',
152             # {
153             # filename => "List.pm",
154             # is_conditional => 1,
155             # line_num => 10
156             # }
157             # ]
158             # );
159              
160             # for my $ix (0 .. $#test) {
161             # my ($input, $expected_result, $expected_value) = @{$test[$ix]};
162             # my $i = $ix + 1;
163             # say "\n** Test #$i: ", $input;
164              
165             # my $value_ref;
166             # my $result = 'OK';
167              
168             # # Parse input and build tree
169             # my $eval_ok = eval { $value_ref = parse_bp_location( \$input ); 1; };
170             # if ( !$eval_ok ) {
171             # my $eval_error = $EVAL_ERROR;
172             # PARSE_EVAL_ERROR: {
173             # $result = "Error: $EVAL_ERROR";
174             # Test::More::diag($result);
175             # }
176             # $result = "no parse";
177             # }
178             # if ($result ne $expected_result) {
179             # Test::More::fail(qq{Parse of "$input" "$result"; expected "$expected_result"});
180             # } else {
181             # Test::More::pass(qq{Parse of "$input" okay});
182             # }
183              
184             # # say Data::Dumper::Dumper($value_ref);
185             # my %bp_location = %{bp_location_build($value_ref)};
186             # # use Data::Printer;
187             # # p $bp_location;
188             # my %expected_value = %$expected_value;
189             # if (%bp_location ne %expected_value) {
190             # Test::More::fail(qq{Test of "$input" value was "%bp_location"; expected "%expected_value"});
191             # } else {
192             # Test::More::pass(qq{Parsed Value of "$input" matches});
193             # }
194             # }
195             # done_testing();
196             # }
197             1;