File Coverage

File:blib/lib/CSS/Grammar.pm
Coverage:98.5%

linestmtbrancondsubtimecode
1package CSS::Grammar;
2
3
12
12
12
52
17
57
use strict;
4
12
12
12
108
22
46
use warnings;
5
6
12
12
12
85
25
88
use CSS::Parse::Rule;
7
12
12
12
93
25
80
use CSS::Stylesheet;
8
12
12
12
61
18
53
use Time::HiRes qw(gettimeofday tv_interval);
9
12
12
12
67
20
63
use Data::Dumper;
10
11sub new {
12
36
265
        my ($class) = @_;
13
36
137
        my $self = bless {}, $class;
14
15
36
130
        $self->{toke_rules} = {};
16
36
122
        $self->{toke_order} = [];
17
36
102
        $self->{case_insensitive} = 0;
18
36
104
        $self->{base_rule} = '';
19
36
113
        $self->{lex_rules} = {};
20
36
106
        $self->{error} = '';
21
22
36
139
        $self->init();
23
24
36
198
        return $self;
25}
26
27sub init {
28
4
16
        my ($self) = @_;
29}
30
31sub add_toke_rule {
32
957
3147
        my ($self, $name, $rx) = @_;
33
34
957
2422
        $self->{error} = '';
35
36
957
2934
        if ($self->{case_insensitive}){
37
38
954
17737
                $self->{toke_rules}->{$name} = qr/^($rx)/is;
39        }else{
40
3
44
                $self->{toke_rules}->{$name} = qr/^($rx)/s;
41        }
42
43
957
957
1888
3930
        push @{$self->{toke_order}}, $name;
44}
45
46sub add_lex_rule {
47
521
1709
        my ($self, $name, $rule_source) = @_;
48
49
521
1358
        $self->{error} = '';
50
51
521
2009
        my $rule = CSS::Parse::Rule->new($self, $name, $rule_source);
52
53
521
1842
        if ($rule->{error}){
54
1
5
                $self->{error} = $rule->{error};
55
1
11
                return 0;
56        }
57
58
520
1867
        $self->{lex_rules}->{$name} = $rule;
59
520
1330
        return 1;
60}
61
62sub toke {
63
28
105
        my ($self, $input) = @_;
64
65
28
78
        $self->{error} = '';
66
67
28
73
        my $tokens = [];
68
69
28
115
        while(length $input){
70
71
1058
2115
                my $matched = 0;
72
73
1058
1058
1780
3765
                for my $rule(@{$self->{toke_order}}){
74
75                        #my $match_start = [gettimeofday];
76
77
14416
78313
                        if ($input =~ $self->{toke_rules}->{$rule}){
78
79                                #$self->{time_match} += tv_interval($match_start, [gettimeofday]);
80
81                                #my $push_start = [gettimeofday];
82
1056
1056
1654
4007
                                push @{$tokens}, CSS::Token->new($rule, $1);
83                                #$self->{time_push} += tv_interval($push_start, [gettimeofday]);
84
85                                #my $substr_start = [gettimeofday];
86
1056
3020
                                $input = substr $input, length $1;
87                                #$self->{time_substr} += tv_interval($substr_start, [gettimeofday]);
88
89
1056
1813
                                $matched = 1;
90
91
1056
1770
                                last;
92                        }
93
94                        #$self->{time_no_match} += tv_interval($match_start, [gettimeofday]);
95                }
96
97
1058
4767
                unless ($matched){
98
99
2
2
4
14
                        push @{$tokens}, CSS::Token->new('MISC', substr $input, 0, 1);
100
2
11
                        $input = substr $input, 1;
101                }
102        }
103
104
28
96
        return $tokens;
105}
106
107sub lex {
108
26
137
        my ($self, $input) = @_;
109
110
26
78
        $self->{error} = '';
111
112
26
107
        my $rule = $self->{lex_rules}->{$self->{base_rule}};
113
114
26
86
        return undef unless defined $rule;
115
116
25
104
        my $match = $rule->match($input, 0);
117
118
25
86
        return undef unless defined $match;
119
120
24
87
        my $leftover = $match->tokens_left;
121
122
24
74
        if ($leftover){
123
124
1
5
                $self->{error} = "Lexer didn't match all tokens with base rule ($leftover left over)\n";
125        }
126
127
24
73
        return $match;
128}
129
130sub set_base {
131
33
109
        my ($self, $base_rule) = @_;
132
133
33
236
        $self->{base_rule} = $base_rule;
134}
135
136sub find_lex_rule {
137
4550
12083
        my ($self, $rule_name) = @_;
138
139
4550
19344
        return $self->{lex_rules}->{$rule_name};
140}
141
142sub parse {
143
12
41
        my ($self, $input) = @_;
144
145        #
146        # this method just ties together a bunch of stuff to turn an input string into a
147        # CSS::Stylesheet object
148        #
149
150
12
46
        my $tokens = $self->toke($input);
151
12
12
22
47
        return undef unless scalar(@{$tokens});
152
153
12
98
        my $tree = $self->lex($tokens);
154
12
37
        return undef unless defined $tree;
155
156
12
52
        $tree->scrub;
157
12
47
        $tree->reduce;
158
159
12
73
        my $sheet = $self->walk($tree);
160
161
12
671
        return $sheet;
162}
163
164sub walk {
165
16
53
        my ($self, $tree) = @_;
166
167
16
82
        my $stylesheet = new CSS::Stylesheet;
168
169
16
55
        return $stylesheet unless defined $tree;
170
15
72
        return $stylesheet unless $tree->{subrule} eq $self->{base_rule};
171
172
14
64
        $self->walk_stylesheet($stylesheet, $tree->{submatches});
173
174
14
37
        return $stylesheet;
175}
176
177
178package CSS::Token;
179
180sub new {
181
1059
3372
        my ($class, $type, $content) = @_;
182
1059
3133
        my $self = bless {}, $class;
183
184
1059
3273
        $self->{type} = $type;
185
1059
2705
        $self->{content} = $content;
186
187
1059
2703
        return $self;
188}
189
1901;
191