File Coverage

blib/lib/CodeGen/Cpppp/CParser.pm
Criterion Covered Total %
statement 54 63 85.7
branch 10 20 50.0
condition 20 33 60.6
subroutine 10 11 90.9
pod 1 2 50.0
total 95 129 73.6


line stmt bran cond sub pod time code
1             package CodeGen::Cpppp::CParser;
2              
3             our $VERSION = '0.005'; # VERSION
4             # ABSTRACT: C Parser Utility Library
5              
6 4     4   258332 use v5.20;
  4         17  
7 4     4   25 use warnings;
  4         7  
  4         259  
8 4     4   25 use Carp;
  4         7  
  4         357  
9 4     4   763 use experimental 'signatures', 'postderef';
  4         5127  
  4         43  
10              
11             sub new {
12 0     0 0 0 my $class= shift;
13             my $self= bless {
14             !(@_ & 1)? @_
15 0 0 0     0 : @_ == 1 && ref $_[0] eq 'HASH'? %{$_[0]}
  0 0       0  
16             : Carp::croak("Expected hashref or even-length list")
17             }, $class;
18             }
19              
20              
21             sub tokenize {
22 20     20 1 306471 my ($class, undef, $tok_lim)= @_;
23 20 50       72 my $textref= ref $_[1] eq 'SCALAR'? $_[1] : \$_[1];
24 20         79 $class->_get_tokens($textref, $tok_lim);
25             }
26              
27 74     74   189 sub CodeGen::Cpppp::CParser::Token::type { $_[0][0] }
28 8     8   21 sub CodeGen::Cpppp::CParser::Token::value { $_[0][1] }
29 36     36   80 sub CodeGen::Cpppp::CParser::Token::src_pos { $_[0][2] }
30 13     13   26 sub CodeGen::Cpppp::CParser::Token::src_len { $_[0][3] }
31              
32             our %keywords= map +($_ => 1), qw(
33             auto break case char const continue default do double else enum extern
34             float for goto if int long register return short signed sizeof static
35             struct switch typedef union unsigned void volatile while
36              
37             inline _Bool _Complex _Imaginary
38              
39             __FUNCTION__ __PRETTY_FUNCTION__ __alignof __alignof__ __asm
40             __asm__ __attribute __attribute__ __builtin_offsetof __builtin_va_arg
41             __complex __complex__ __const __extension__ __func__ __imag __imag__
42             __inline __inline__ __label__ __null __real __real__
43             __restrict __restrict__ __signed __signed__ __thread __typeof
44             __volatile __volatile__
45              
46             restrict
47             );
48             our %named_escape= (
49             a => "\a", b => "\b", e => "\e", f => "\f",
50             n => "\n", r => "\r", t => "\t", v => "\x0B"
51             );
52             our %tokens_before_infix_minus= map +($_ => 1), (
53             ']', ')', 'integer','real','ident',
54             );
55             sub _get_tokens {
56 20     20   42 my ($class, $textref, $tok_lim)= @_;
57 20 50       122 pos($$textref)= 0 unless defined pos($$textref);
58 20         43 my @tokens;
59 20         32 local our $_type;
60 20         29 local our $_value;
61 20         35 local our $_error;
62 20   33     223 while ((!defined $tok_lim || --$tok_lim >= 0)
      66        
63             && $$textref =~ m{
64             \G
65             (?> \s* ) \K # ignore whitespace
66             (?|
67             # single-line comment
68             // ( [^\r\n]* )
69 4         23 (?{ $_type= 'comment' })
70              
71             # block comment
72             | /\* ( (?: [^*]+ | \* (?=[^/]) )* ) ( \*/ | \Z )
73 7 50       17 (?{ $_type= 'comment'; $_error= "Reached end of input looking for '*/'" unless $2 })
  7         64  
74              
75             # Preprocessor directive
76             | \# \s* ( (?: [^\r\n\\]+ | \\ \r? \n | \\ (?=[^\r\n]) )* )
77 0         0 (?{ $_type= 'directive' })
78              
79             # string literal
80             | " (?{ '' })
81             (?|
82 6         35 ([^"\\]+) (?{ $^R . $1 })
83 1         42 | \\x ([0-9A-Fa-f]+) (?{ $^R . chr(hex $1) })
84 2         11 | \\ ([0-9]{1,3}) (?{ $^R . chr(oct $1) })
85             | \\ \r?\n
86 2   66     35 | \\ (.) (?{ $^R . ($named_escape{$1} // $1) })
87             )*
88             ( " | \Z )
89 6 50       15 (?{ $_type= 'string'; $_value= $^R; $_error= q{Reached end of input looking for '"'} unless $2 })
  6         11  
  6         56  
90              
91             # character constant
92             | ' (?|
93             ([^'\\]) (?{ $1 })
94 0         0 | \\x ([0-9A-Fa-f]+) (?{ chr(hex $1) })
95 0         0 | \\ ([0-9]{1,3}) (?{ chr(oct $1) })
96 0   0     0 | \\ (.) (?{ $named_escape{$1} // $1 })
97             )
98             ( '? )
99 1 0       2 (?{ $_type= 'char'; $_value= $^R; $_error= q{Unterminated character constant} unless $2 })
  1         2  
  1         6  
100              
101             # identifier
102             | ( [A-Za-z_] \w* )
103 17 100       350 (?{ $_type= $keywords{$1}? 'keyword' : 'ident' })
104              
105             # real number
106             | ( (?: [0-9]+ \. [0-9]* | \. [0-9]+ ) (?: e -? [0-9]+ )? [lLfF]? )
107 0         0 (?{ $_type= 'real' })
108              
109             | # integer
110             (?|
111 1         7 0x([A-Fa-f0-9]+) (?{ $_value= hex($1) })
112 0         0 | 0([0-7]+) (?{ $_value= oct($1) })
113             | ([0-9]+)
114             )
115             [uU]?[lL]*
116 14         67 (?{ $_type= 'integer' })
117              
118             | # punctuation and operators
119             ( \+\+ | -- | -> | \+=? | -=? | \*=? | /=? | %=? | >>=? | >=? | <<=? | <=?
120             | \&\&=? | \&=? | \|\|=? | \|=? | \^=? | ==? | !=? | \? | ~
121             | [\[\]\(\)\{\};,.:]
122             )
123 38         186 (?{ $_type= $1 })
124            
125             | # all other characters
126 1         2 (.) (?{ $_type= 'unknown'; $_error= q{parse error} })
  1         2  
127             )
128             }xcg
129             ) {
130 88 100 100     595 my @token= ($_type, $_value // $1, $-[0], $+[0] - $-[0], defined $_error? ($_error) : ());
131             # disambiguate negative number from minus operator
132 88 100 66     522 if (($_type eq 'integer' || $_type eq 'real')
      66        
      100        
      66        
      100        
133             && @tokens && $tokens[-1][0] eq '-'
134             && (@tokens == 1 || !$tokens_before_infix_minus{$tokens[-2]->type})
135             ) {
136 2         33 $token[1]= -$token[1];
137 2         6 $token[2]= $tokens[-1][2];
138 2         7 $token[3]= $+[0] - $tokens[-1][2];
139 2         5 @{$tokens[-1]}= @token;
  2         7  
140             } else {
141 86         196 push @tokens, bless \@token, 'CodeGen::Cpppp::CParser::Token';
142             }
143 88         645 ($_error, $_value)= (undef, undef);
144             }
145 20         121 return @tokens;
146             }
147              
148             1;
149              
150             __END__