line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
#!/usr/bin/perl |
2
|
|
|
|
|
|
|
|
3
|
24
|
|
|
24
|
|
80478
|
use strict; |
|
24
|
|
|
|
|
55
|
|
|
24
|
|
|
|
|
836
|
|
4
|
24
|
|
|
24
|
|
108
|
use warnings; |
|
24
|
|
|
|
|
42
|
|
|
24
|
|
|
|
|
1004
|
|
5
|
|
|
|
|
|
|
|
6
|
|
|
|
|
|
|
package WWW::Shopify::Liquid::Token; |
7
|
24
|
|
|
24
|
|
106
|
use base 'WWW::Shopify::Liquid::Element'; |
|
24
|
|
|
|
|
49
|
|
|
24
|
|
|
|
|
9778
|
|
8
|
0
|
|
|
0
|
|
|
sub new { return bless { line => $_[1], core => $_[2] }, $_[0]; }; |
9
|
0
|
|
|
0
|
|
|
sub stringify { return $_[0]->{core}; } |
10
|
0
|
|
|
0
|
|
|
sub tokens { return $_[0]; } |
11
|
|
|
|
|
|
|
|
12
|
|
|
|
|
|
|
package WWW::Shopify::Liquid::Token::Operator; |
13
|
24
|
|
|
24
|
|
170
|
use base 'WWW::Shopify::Liquid::Token'; |
|
24
|
|
|
|
|
39
|
|
|
24
|
|
|
|
|
8066
|
|
14
|
|
|
|
|
|
|
|
15
|
|
|
|
|
|
|
package WWW::Shopify::Liquid::Token::Operand; |
16
|
24
|
|
|
24
|
|
181
|
use base 'WWW::Shopify::Liquid::Token'; |
|
24
|
|
|
|
|
50
|
|
|
24
|
|
|
|
|
6679
|
|
17
|
|
|
|
|
|
|
|
18
|
|
|
|
|
|
|
package WWW::Shopify::Liquid::Token::String; |
19
|
24
|
|
|
24
|
|
138
|
use base 'WWW::Shopify::Liquid::Token::Operand'; |
|
24
|
|
|
|
|
52
|
|
|
24
|
|
|
|
|
8307
|
|
20
|
0
|
|
|
0
|
|
|
sub process { my ($self, $hash) = @_; return $self->{core}; } |
|
0
|
|
|
|
|
|
|
21
|
|
|
|
|
|
|
|
22
|
|
|
|
|
|
|
package WWW::Shopify::Liquid::Token::Number; |
23
|
24
|
|
|
24
|
|
153
|
use base 'WWW::Shopify::Liquid::Token::Operand'; |
|
24
|
|
|
|
|
40
|
|
|
24
|
|
|
|
|
7496
|
|
24
|
0
|
|
|
0
|
|
|
sub process { my ($self, $hash) = @_; return $self->{core}; } |
|
0
|
|
|
|
|
|
|
25
|
|
|
|
|
|
|
|
26
|
|
|
|
|
|
|
package WWW::Shopify::Liquid::Token::NULL; |
27
|
24
|
|
|
24
|
|
140
|
use base 'WWW::Shopify::Liquid::Token::Operand'; |
|
24
|
|
|
|
|
37
|
|
|
24
|
|
|
|
|
6854
|
|
28
|
0
|
|
|
0
|
|
|
sub process { return undef; } |
29
|
|
|
|
|
|
|
|
30
|
|
|
|
|
|
|
package WWW::Shopify::Liquid::Token::Bool; |
31
|
24
|
|
|
24
|
|
131
|
use base 'WWW::Shopify::Liquid::Token::Operand'; |
|
24
|
|
|
|
|
40
|
|
|
24
|
|
|
|
|
6963
|
|
32
|
0
|
|
|
0
|
|
|
sub process { my ($self, $hash) = @_; return $self->{core}; } |
|
0
|
|
|
|
|
|
|
33
|
|
|
|
|
|
|
|
34
|
|
|
|
|
|
|
package WWW::Shopify::Liquid::Token::Variable; |
35
|
24
|
|
|
24
|
|
149
|
use base 'WWW::Shopify::Liquid::Token::Operand'; |
|
24
|
|
|
|
|
52
|
|
|
24
|
|
|
|
|
6271
|
|
36
|
|
|
|
|
|
|
|
37
|
24
|
|
|
24
|
|
140
|
use Scalar::Util qw(looks_like_number reftype blessed); |
|
24
|
|
|
|
|
46
|
|
|
24
|
|
|
|
|
15318
|
|
38
|
|
|
|
|
|
|
|
39
|
0
|
|
|
0
|
|
|
sub new { my $package = shift; return bless { line => shift, core => [@_] }, $package; }; |
|
0
|
|
|
|
|
|
|
40
|
|
|
|
|
|
|
sub process { |
41
|
0
|
|
|
0
|
|
|
my ($self, $hash, $action, $pipeline) = @_; |
42
|
0
|
|
|
|
|
|
my $place = $hash; |
43
|
|
|
|
|
|
|
|
44
|
0
|
|
|
|
|
|
my @inner = @{$self->{core}}; |
|
0
|
|
|
|
|
|
|
45
|
0
|
|
|
|
|
|
my $unprocessed = 0; |
46
|
0
|
|
|
|
|
|
foreach my $part_idx (0..$#inner) { |
47
|
0
|
|
|
|
|
|
my $part = $inner[$part_idx]; |
48
|
0
|
0
|
|
|
|
|
if (ref($part) eq 'WWW::Shopify::Liquid::Token::Variable::Processing') { |
49
|
0
|
|
|
|
|
|
$place = $part->$action($pipeline, $hash, $place); |
50
|
|
|
|
|
|
|
} |
51
|
|
|
|
|
|
|
else { |
52
|
0
|
0
|
|
|
|
|
my $key = $self->is_processed($part) ? $part : $part->$action($pipeline, $hash); |
53
|
|
|
|
|
|
|
|
54
|
0
|
0
|
0
|
|
|
|
return $self unless defined $key && $key ne ''; |
55
|
0
|
0
|
0
|
|
|
|
$self->{core}->[$part_idx] = $key if $self->is_processed($key) && $action eq "optimize"; |
56
|
0
|
0
|
|
|
|
|
if (defined $place) { |
57
|
0
|
0
|
0
|
|
|
|
if (reftype($place) && reftype($place) eq "HASH" && exists $place->{$key}) { |
|
|
0
|
0
|
|
|
|
|
|
|
0
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
58
|
0
|
|
|
|
|
|
$place = $place->{$key}; |
59
|
|
|
|
|
|
|
} elsif (reftype($place) && reftype($place) eq "ARRAY" && looks_like_number($key) && defined $place->[$key]) { |
60
|
0
|
|
|
|
|
|
$place = $place->[$key]; |
61
|
|
|
|
|
|
|
} elsif ($pipeline->make_method_calls && blessed($place) && $place->can($key)) { |
62
|
0
|
|
|
|
|
|
$place = $place->$key; |
63
|
|
|
|
|
|
|
} else { |
64
|
0
|
|
|
|
|
|
$unprocessed = 1; |
65
|
0
|
|
|
|
|
|
$place = undef; |
66
|
|
|
|
|
|
|
} |
67
|
|
|
|
|
|
|
} |
68
|
|
|
|
|
|
|
|
69
|
|
|
|
|
|
|
} |
70
|
|
|
|
|
|
|
} |
71
|
0
|
0
|
|
|
|
|
return $unprocessed ? $self : $place; |
72
|
|
|
|
|
|
|
} |
73
|
0
|
|
|
0
|
|
|
sub stringify { return join(".", map { $_->stringify } @{$_[0]->{core}}); } |
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
74
|
|
|
|
|
|
|
|
75
|
|
|
|
|
|
|
sub set { |
76
|
0
|
|
|
0
|
|
|
my ($self, $pipeline, $hash, $value) = @_; |
77
|
0
|
0
|
|
|
|
|
my @vars = map { $self->is_processed($_) ? $_ : $_->render($pipeline, $hash) } @{$self->{core}}; |
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
78
|
0
|
|
|
|
|
|
my ($reference) = $pipeline->variable_reference($hash, \@vars); |
79
|
0
|
|
|
|
|
|
$$reference = $value; |
80
|
0
|
|
|
|
|
|
return 1; |
81
|
|
|
|
|
|
|
} |
82
|
|
|
|
|
|
|
|
83
|
|
|
|
|
|
|
|
84
|
|
|
|
|
|
|
sub get { |
85
|
0
|
|
|
0
|
|
|
my ($self, $pipeline, $hash) = @_; |
86
|
0
|
0
|
|
|
|
|
my @vars = map { $self->is_processed($_) ? $_ : $_->render($pipeline, $hash) } @{$self->{core}}; |
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
87
|
0
|
|
|
|
|
|
my ($reference) = $pipeline->variable_reference($hash, \@vars, 1); |
88
|
0
|
0
|
|
|
|
|
return $reference ? $$reference : undef; |
89
|
|
|
|
|
|
|
} |
90
|
|
|
|
|
|
|
|
91
|
|
|
|
|
|
|
package WWW::Shopify::Liquid::Token::Variable::Processing; |
92
|
24
|
|
|
24
|
|
170
|
use base 'WWW::Shopify::Liquid::Token::Operand'; |
|
24
|
|
|
|
|
51
|
|
|
24
|
|
|
|
|
8389
|
|
93
|
|
|
|
|
|
|
sub process { |
94
|
0
|
|
|
0
|
|
|
my ($self, $hash, $argument, $action, $pipeline) = @_; |
95
|
0
|
0
|
|
|
|
|
return $self if !$self->is_processed($argument); |
96
|
0
|
|
|
|
|
|
my $result = $self->{core}->operate($hash, $argument); |
97
|
0
|
0
|
|
|
|
|
return $self if !$self->is_processed($result); |
98
|
0
|
|
|
|
|
|
return $result; |
99
|
|
|
|
|
|
|
} |
100
|
|
|
|
|
|
|
|
101
|
|
|
|
|
|
|
package WWW::Shopify::Liquid::Token::Variable::Named; |
102
|
24
|
|
|
24
|
|
143
|
use base 'WWW::Shopify::Liquid::Token::Operand'; |
|
24
|
|
|
|
|
43
|
|
|
24
|
|
|
|
|
8431
|
|
103
|
|
|
|
|
|
|
|
104
|
0
|
|
|
0
|
|
|
sub new { my $package = shift; return bless { line => shift, name => shift, core => shift }, $package; }; |
|
0
|
|
|
|
|
|
|
105
|
|
|
|
|
|
|
sub process { |
106
|
0
|
|
|
0
|
|
|
my ($self, $hash, $action, $pipeline) = @_; |
107
|
0
|
|
|
|
|
|
return { $self->{name} => $self->{core}->$action($pipeline, $hash) }; |
108
|
|
|
|
|
|
|
} |
109
|
|
|
|
|
|
|
|
110
|
|
|
|
|
|
|
|
111
|
|
|
|
|
|
|
package WWW::Shopify::Liquid::Token::Grouping; |
112
|
24
|
|
|
24
|
|
189
|
use base 'WWW::Shopify::Liquid::Token::Operand'; |
|
24
|
|
|
|
|
41
|
|
|
24
|
|
|
|
|
7800
|
|
113
|
0
|
|
|
0
|
|
|
sub new { my $package = shift; return bless { line => shift, members => [@_] }, $package; }; |
|
0
|
|
|
|
|
|
|
114
|
0
|
|
|
0
|
|
|
sub members { return @{$_[0]->{members}}; } |
|
0
|
|
|
|
|
|
|
115
|
|
|
|
|
|
|
|
116
|
|
|
|
|
|
|
# Parentheses |
117
|
|
|
|
|
|
|
package WWW::Shopify::Liquid::Token::Grouping::Parenthetical; |
118
|
24
|
|
|
24
|
|
135
|
use base 'WWW::Shopify::Liquid::Token::Grouping'; |
|
24
|
|
|
|
|
34
|
|
|
24
|
|
|
|
|
7867
|
|
119
|
|
|
|
|
|
|
|
120
|
|
|
|
|
|
|
# Like a grouping, but not really. |
121
|
|
|
|
|
|
|
# Square brackets |
122
|
|
|
|
|
|
|
package WWW::Shopify::Liquid::Token::Array; |
123
|
24
|
|
|
24
|
|
148
|
use base 'WWW::Shopify::Liquid::Token::Operand'; |
|
24
|
|
|
|
|
40
|
|
|
24
|
|
|
|
|
15374
|
|
124
|
|
|
|
|
|
|
|
125
|
|
|
|
|
|
|
sub new { |
126
|
0
|
|
|
0
|
|
|
my ($package, $line, @members) = @_; |
127
|
|
|
|
|
|
|
# Check to see whether or not the incoming member array is separated by commas. |
128
|
|
|
|
|
|
|
# Should never begin with a comma, should never end with a comma, should always be |
129
|
|
|
|
|
|
|
# 101010101 in terms of data and separators. If this is not the case, then we |
130
|
|
|
|
|
|
|
# Should throw a lexing exception. |
131
|
|
|
|
|
|
|
die new WWW::Shopify::Liquid::Exception::Lexer::InvalidSeparator($line) unless |
132
|
0
|
0
|
|
|
|
|
int(grep { ($_ % 2) == 0 && $members[$_]->isa('WWW::Shopify::Liquid::Token::Separator') } (0..$#members)) == 0 && |
133
|
0
|
0
|
0
|
|
|
|
int(grep { ($_ % 2) == 1 && (!$members[$_]->isa('WWW::Shopify::Liquid::Token::Separator') || $members[$_]->{core} ne ",") } (0..$#members)) == 0; |
|
0
|
0
|
0
|
|
|
|
|
134
|
|
|
|
|
|
|
|
135
|
|
|
|
|
|
|
my $self = bless { |
136
|
|
|
|
|
|
|
line => $line, |
137
|
0
|
|
|
|
|
|
members => [grep { !$_->isa('WWW::Shopify::Liquid::Token::Separator') } @members] |
|
0
|
|
|
|
|
|
|
138
|
|
|
|
|
|
|
}, $package; |
139
|
0
|
|
|
|
|
|
return $self; |
140
|
|
|
|
|
|
|
}; |
141
|
|
|
|
|
|
|
|
142
|
0
|
|
|
0
|
|
|
sub members { return @{$_[0]->{members}}; } |
|
0
|
|
|
|
|
|
|
143
|
|
|
|
|
|
|
sub process { |
144
|
0
|
|
|
0
|
|
|
my ($self, $hash, $action, $pipeline) = @_; |
145
|
0
|
|
|
|
|
|
my @members = $self->members; |
146
|
0
|
|
|
|
|
|
$members[$_] = $members[$_]->$action($pipeline, $hash) for (grep { !$self->is_processed($members[$_]) } (0..$#members)); |
|
0
|
|
|
|
|
|
|
147
|
0
|
0
|
|
|
|
|
if ($action eq "optimize") { |
148
|
0
|
|
|
|
|
|
$self->{members}->[$_] = $_ for (grep { $self->is_processed($members[$_]) } 0..$#members); |
|
0
|
|
|
|
|
|
|
149
|
|
|
|
|
|
|
} |
150
|
0
|
|
|
|
|
|
return [@members]; |
151
|
|
|
|
|
|
|
} |
152
|
|
|
|
|
|
|
|
153
|
|
|
|
|
|
|
# Curly brackets |
154
|
|
|
|
|
|
|
package WWW::Shopify::Liquid::Token::Hash; |
155
|
24
|
|
|
24
|
|
143
|
use base 'WWW::Shopify::Liquid::Token::Operand'; |
|
24
|
|
|
|
|
43
|
|
|
24
|
|
|
|
|
14793
|
|
156
|
|
|
|
|
|
|
|
157
|
0
|
|
|
0
|
|
|
sub members { return @{$_[0]->{members}}; } |
|
0
|
|
|
|
|
|
|
158
|
|
|
|
|
|
|
sub new { |
159
|
0
|
|
|
0
|
|
|
my ($package, $line, @members) = @_; |
160
|
|
|
|
|
|
|
|
161
|
|
|
|
|
|
|
die new WWW::Shopify::Liquid::Exception::Lexer::InvalidSeparator($line) unless |
162
|
0
|
0
|
|
|
|
|
int(grep { ($_ % 2) == 0 && $members[$_]->isa('WWW::Shopify::Liquid::Token::Separator') } (0..$#members)) == 0 && |
163
|
|
|
|
|
|
|
int(grep { |
164
|
|
|
|
|
|
|
($_ % 4) == 1 && (!$members[$_]->isa('WWW::Shopify::Liquid::Token::Separator') || $members[$_]->{core} ne ":") || |
165
|
0
|
0
|
0
|
|
|
|
($_ % 4) == 3 && (!$members[$_]->isa('WWW::Shopify::Liquid::Token::Separator') || $members[$_]->{core} ne ",") |
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
166
|
|
|
|
|
|
|
} (0..$#members)) == 0 && |
167
|
0
|
0
|
0
|
|
|
|
int(grep { !$_->isa('WWW::Shopify::Liquid::Token::Separator') } @members) % 2 == 0; |
|
0
|
|
0
|
|
|
|
|
168
|
|
|
|
|
|
|
|
169
|
|
|
|
|
|
|
return bless { |
170
|
|
|
|
|
|
|
line => $line, |
171
|
0
|
|
|
|
|
|
members => [grep { !$_->isa('WWW::Shopify::Liquid::Token::Separator') } @members] |
|
0
|
|
|
|
|
|
|
172
|
|
|
|
|
|
|
}, $package; |
173
|
|
|
|
|
|
|
}; |
174
|
|
|
|
|
|
|
|
175
|
|
|
|
|
|
|
sub process { |
176
|
0
|
|
|
0
|
|
|
my ($self, $hash, $action, $pipeline) = @_; |
177
|
0
|
|
|
|
|
|
my @members = $self->members; |
178
|
0
|
|
|
|
|
|
$members[$_] = $members[$_]->$action($pipeline, $hash) for (grep { !$self->is_processed($members[$_]) } (0..$#members)); |
|
0
|
|
|
|
|
|
|
179
|
0
|
0
|
|
|
|
|
if ($action eq "optimize") { |
180
|
0
|
|
|
|
|
|
$self->{members}->[$_] = $_ for (grep { $self->is_processed($members[$_]) } 0..$#members); |
|
0
|
|
|
|
|
|
|
181
|
|
|
|
|
|
|
} |
182
|
0
|
|
|
|
|
|
return { @members }; |
183
|
|
|
|
|
|
|
} |
184
|
|
|
|
|
|
|
|
185
|
|
|
|
|
|
|
|
186
|
|
|
|
|
|
|
package WWW::Shopify::Liquid::Token::Text; |
187
|
24
|
|
|
24
|
|
173
|
use base 'WWW::Shopify::Liquid::Token::Operand'; |
|
24
|
|
|
|
|
63
|
|
|
24
|
|
|
|
|
9529
|
|
188
|
|
|
|
|
|
|
sub new { |
189
|
0
|
|
|
0
|
|
|
my $self = { line => $_[1], core => $_[2] }; |
190
|
0
|
|
|
|
|
|
my $package = $_[0]; |
191
|
0
|
0
|
0
|
|
|
|
$package = 'WWW::Shopify::Liquid::Token::Text::Whitespace' if !defined $_[2] || $_[2] =~ m/^\s*$/; |
192
|
0
|
|
|
|
|
|
return bless $self, $package; |
193
|
|
|
|
|
|
|
}; |
194
|
0
|
|
|
0
|
|
|
sub process { my ($self, $hash) = @_; return $self->{core}; } |
|
0
|
|
|
|
|
|
|
195
|
|
|
|
|
|
|
|
196
|
|
|
|
|
|
|
package WWW::Shopify::Liquid::Token::Text::Whitespace; |
197
|
24
|
|
|
24
|
|
152
|
use base 'WWW::Shopify::Liquid::Token::Text'; |
|
24
|
|
|
|
|
42
|
|
|
24
|
|
|
|
|
7554
|
|
198
|
|
|
|
|
|
|
|
199
|
|
|
|
|
|
|
package WWW::Shopify::Liquid::Token::Tag; |
200
|
24
|
|
|
24
|
|
134
|
use base 'WWW::Shopify::Liquid::Token'; |
|
24
|
|
|
|
|
57
|
|
|
24
|
|
|
|
|
9703
|
|
201
|
0
|
|
|
0
|
|
|
sub new { return bless { line => $_[1], tag => $_[2], arguments => $_[3], strip_left => $_[4], strip_right => $_[5] }, $_[0] }; |
202
|
0
|
|
|
0
|
|
|
sub tag { return $_[0]->{tag}; } |
203
|
0
|
|
|
0
|
|
|
sub stringify { return $_[0]->tag; } |
204
|
|
|
|
|
|
|
|
205
|
|
|
|
|
|
|
package WWW::Shopify::Liquid::Token::Output; |
206
|
24
|
|
|
24
|
|
148
|
use base 'WWW::Shopify::Liquid::Token'; |
|
24
|
|
|
|
|
38
|
|
|
24
|
|
|
|
|
7723
|
|
207
|
0
|
|
|
0
|
|
|
sub new { return bless { line => $_[1], core => $_[2], strip_left => $_[3], strip_right => $_[4] }, $_[0]; }; |
208
|
|
|
|
|
|
|
|
209
|
|
|
|
|
|
|
package WWW::Shopify::Liquid::Token::Separator; |
210
|
24
|
|
|
24
|
|
155
|
use base 'WWW::Shopify::Liquid::Token'; |
|
24
|
|
|
|
|
53
|
|
|
24
|
|
|
|
|
6916
|
|
211
|
|
|
|
|
|
|
|
212
|
|
|
|
|
|
|
|
213
|
|
|
|
|
|
|
package WWW::Shopify::Liquid::Lexer; |
214
|
24
|
|
|
24
|
|
142
|
use base 'WWW::Shopify::Liquid::Pipeline'; |
|
24
|
|
|
|
|
34
|
|
|
24
|
|
|
|
|
7752
|
|
215
|
24
|
|
|
24
|
|
154
|
use Scalar::Util qw(looks_like_number blessed); |
|
24
|
|
|
|
|
43
|
|
|
24
|
|
|
|
|
24594
|
|
216
|
|
|
|
|
|
|
|
217
|
0
|
|
|
0
|
0
|
|
sub new { return bless { operators => {}, lexing_halters => {}, transparent_filters => {}, unparse_spaces => 0, parse_escaped_characters => 1 }, $_[0]; } |
218
|
0
|
|
|
0
|
0
|
|
sub operators { return $_[0]->{operators}; } |
219
|
0
|
0
|
|
0
|
0
|
|
sub unparse_spaces { $_[0]->{unparse_spaces} = $_[1] if defined $_[1]; return $_[0]->{unparse_spaces}; } |
|
0
|
|
|
|
|
|
|
220
|
0
|
0
|
|
0
|
0
|
|
sub parse_escaped_characters { $_[0]->{parse_escaped_characters} = $_[1] if defined $_[1]; return $_[0]->{parse_escaped_characters}; } |
|
0
|
|
|
|
|
|
|
221
|
0
|
|
|
0
|
0
|
|
sub register_operator { $_[0]->{operators}->{$_} = $_[1] for ($_[1]->symbol); } |
222
|
|
|
|
|
|
|
sub register_tag { |
223
|
0
|
|
|
0
|
0
|
|
my ($self, $package) = @_; |
224
|
0
|
0
|
0
|
|
|
|
$self->{lexing_halters}->{$package->name} = $package if $package->is_enclosing && $package->inner_halt_lexing; |
225
|
|
|
|
|
|
|
} |
226
|
|
|
|
|
|
|
sub register_filter { |
227
|
0
|
|
|
0
|
0
|
|
my ($self, $package) = @_; |
228
|
0
|
0
|
|
|
|
|
$self->{transparent_filters}->{$package->name} = $package if ($package->transparent); |
229
|
|
|
|
|
|
|
} |
230
|
0
|
|
|
0
|
0
|
|
sub transparent_filters { return $_[0]->{transparent_filters}; } |
231
|
|
|
|
|
|
|
|
232
|
|
|
|
|
|
|
sub parse_token { |
233
|
0
|
|
|
0
|
0
|
|
my ($self, $line, $token) = @_; |
234
|
|
|
|
|
|
|
|
235
|
|
|
|
|
|
|
# Strip token of whitespace. |
236
|
0
|
0
|
|
|
|
|
return undef unless defined $token; |
237
|
0
|
|
|
|
|
|
$token =~ s/^\s*(.*?)\s*$/$1/; |
238
|
0
|
0
|
|
|
|
|
return WWW::Shopify::Liquid::Token::Operator->new($line, $token) if $self->operators->{$token}; |
239
|
0
|
0
|
0
|
|
|
|
return WWW::Shopify::Liquid::Token::String->new($line, do { |
240
|
0
|
|
|
|
|
|
my $string = $1; |
241
|
0
|
0
|
|
|
|
|
if ($self->parse_escaped_characters) { |
242
|
0
|
|
|
|
|
|
$string =~ s/\\r/\r/g; |
243
|
0
|
|
|
|
|
|
$string =~ s/\\n/\n/g; |
244
|
0
|
|
|
|
|
|
$string =~ s/\\t/\t/g; |
245
|
|
|
|
|
|
|
} |
246
|
0
|
|
|
|
|
|
$string; |
247
|
|
|
|
|
|
|
}) if $token =~ m/^'(.*)'$/s || $token =~ m/^"(.*)"$/s; |
248
|
0
|
0
|
|
|
|
|
return WWW::Shopify::Liquid::Token::Number->new($line, $1) if looks_like_number($token); |
249
|
0
|
0
|
0
|
|
|
|
return WWW::Shopify::Liquid::Token::NULL->new() if $token eq '' || $token eq 'null'; |
250
|
0
|
0
|
0
|
|
|
|
return WWW::Shopify::Liquid::Token::Separator->new($line, $token) if ($token eq ":" || $token eq ","); |
251
|
0
|
0
|
|
|
|
|
return WWW::Shopify::Liquid::Token::Array->new($line) if ($token eq "[]"); |
252
|
0
|
0
|
|
|
|
|
return WWW::Shopify::Liquid::Token::Hash->new($line) if ($token eq "{}"); |
253
|
|
|
|
|
|
|
# We're a variable. Let's see what's going on. Split along non quoted . and [ ] fields. |
254
|
0
|
|
|
|
|
|
my ($squot, $dquot, $start, @parts) = (0,0,0); |
255
|
|
|
|
|
|
|
# customer['test']['b'] |
256
|
0
|
|
|
|
|
|
my $open_square_bracket = 0; |
257
|
0
|
|
|
|
|
|
my $open_curly_bracket = 0; |
258
|
0
|
|
|
|
|
|
while ($token =~ m/(\.|\[|\]|\{|\}|(?<!\\)\"|(?<!\\)\'|\b$)/g) { |
259
|
0
|
|
|
|
|
|
my $sym = $&; |
260
|
0
|
|
|
|
|
|
my $begin = $-[0]; |
261
|
0
|
|
|
|
|
|
my $end = $+[0]; |
262
|
0
|
0
|
0
|
|
|
|
if (!$squot && !$dquot) { |
263
|
0
|
0
|
0
|
|
|
|
$open_square_bracket-- if ($sym && $sym eq "]"); |
264
|
0
|
0
|
0
|
|
|
|
$open_curly_bracket-- if ($sym && $sym eq "}"); |
265
|
0
|
0
|
0
|
|
|
|
if (($sym eq "." || $sym eq "]" || $sym eq "[" || $sym eq "{" || $sym eq "}" || !$sym) && $open_square_bracket == 0 && $open_curly_bracket == 0) { |
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
266
|
0
|
|
|
|
|
|
my $contents = substr($token, $start, $begin - $start); |
267
|
|
|
|
|
|
|
|
268
|
0
|
0
|
0
|
|
|
|
if (defined $contents && $contents !~ m/^\s*$/) { |
269
|
0
|
|
|
|
|
|
my @variables = (); |
270
|
0
|
0
|
0
|
|
|
|
if (!$sym || $sym eq "." || $sym eq "[") { |
|
|
0
|
0
|
|
|
|
|
|
|
0
|
|
|
|
|
|
271
|
0
|
0
|
|
|
|
|
@variables = $self->transparent_filters->{$contents} ? WWW::Shopify::Liquid::Token::Variable::Processing->new($line, $self->transparent_filters->{$contents}) : WWW::Shopify::Liquid::Token::String->new($line, $contents); |
272
|
|
|
|
|
|
|
} |
273
|
|
|
|
|
|
|
elsif ($sym eq "]") { |
274
|
0
|
|
|
|
|
|
@variables = $self->parse_expression($line, $contents); |
275
|
0
|
0
|
|
|
|
|
return WWW::Shopify::Liquid::Token::Array->new($line, @variables) if (int(@parts) == 0); |
276
|
|
|
|
|
|
|
} |
277
|
|
|
|
|
|
|
elsif ($sym eq "}") { |
278
|
0
|
|
|
|
|
|
@variables = $self->parse_expression($line, $contents); |
279
|
0
|
0
|
|
|
|
|
return WWW::Shopify::Liquid::Token::Hash->new($line, @variables) if (int(@parts) == 0); |
280
|
|
|
|
|
|
|
} |
281
|
0
|
0
|
|
|
|
|
if (int(@variables) > 0) { |
282
|
0
|
0
|
|
|
|
|
if (int(@variables) == 1) { |
283
|
0
|
|
|
|
|
|
push(@parts, @variables); |
284
|
|
|
|
|
|
|
} |
285
|
|
|
|
|
|
|
else { |
286
|
0
|
|
|
|
|
|
push(@parts, WWW::Shopify::Liquid::Token::Grouping->new($line, @variables)) ; |
287
|
|
|
|
|
|
|
} |
288
|
|
|
|
|
|
|
} |
289
|
|
|
|
|
|
|
} |
290
|
|
|
|
|
|
|
} |
291
|
0
|
0
|
0
|
|
|
|
$start = $end if $sym ne '"' && $sym ne "'" && !$open_curly_bracket && !$open_square_bracket; |
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
292
|
0
|
0
|
0
|
|
|
|
$open_square_bracket++ if ($sym && $sym eq "["); |
293
|
0
|
0
|
0
|
|
|
|
$open_curly_bracket++ if ($sym && $sym eq "{"); |
294
|
|
|
|
|
|
|
|
295
|
|
|
|
|
|
|
} |
296
|
0
|
0
|
|
|
|
|
$squot = !$squot if $token eq "'"; |
297
|
0
|
0
|
|
|
|
|
$dquot = !$dquot if $token eq '"'; |
298
|
|
|
|
|
|
|
} |
299
|
0
|
|
|
|
|
|
return WWW::Shopify::Liquid::Token::Variable->new($line, @parts); |
300
|
|
|
|
|
|
|
} |
301
|
|
|
|
|
|
|
|
302
|
24
|
|
|
24
|
|
11662
|
use utf8; |
|
24
|
|
|
|
|
221
|
|
|
24
|
|
|
|
|
185
|
|
303
|
|
|
|
|
|
|
# Returns a single token repsending the whole a expression. |
304
|
|
|
|
|
|
|
sub parse_expression { |
305
|
0
|
|
|
0
|
0
|
|
my ($self, $line, $exp) = @_; |
306
|
0
|
0
|
0
|
|
|
|
return () if !defined $exp || $exp eq ''; |
307
|
0
|
|
|
|
|
|
my @tokens = (); |
308
|
0
|
|
|
|
|
|
my ($start_paren, $start_space, $level, $squot, $dquot, $start_sq, $sq_level, $start_hsh, $hsh_level) = (undef, 0, 0, 0, 0, undef, 0, undef, 0); |
309
|
|
|
|
|
|
|
# We regex along parentheses, quotation marks (both kinds), whitespace, and non-word-operators. |
310
|
|
|
|
|
|
|
# We sort along length, so that we make sure to get all the largest operators first, so that way if a larger operator is made from a smaller one (=, ==) |
311
|
|
|
|
|
|
|
# There's no confusion, we always try to match the largest first. |
312
|
0
|
|
|
|
|
|
my $non_word_operators = join("|", map { quotemeta($_) } grep { $_ =~ m/^\W+$/; } sort { length($b) <=> length($a) } keys(%{$self->operators})); |
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
313
|
0
|
|
|
|
|
|
while ($exp =~ m/(?:\(|\)|\]|\[|\}|\{|(?<!\\)["ââ]|(?<!\\)['ââ]|(\s+|$)|($non_word_operators|,|:))/sg) { |
314
|
0
|
|
|
|
|
|
my ($rs, $re, $rc, $whitespace, $nword_op) = ($-[0], $+[0], $&, $1, $2); |
315
|
|
|
|
|
|
|
# Specifically to allow variables to have a - in them, and be treated as a long literal, instead of a minus sign. |
316
|
|
|
|
|
|
|
# This is terrible behaviour, but mimics Shopify's lexer. |
317
|
|
|
|
|
|
|
# Only if of course the entire first half of this ISN'T a number, though. 'cause that'd be insane. |
318
|
0
|
0
|
0
|
|
|
|
next if $nword_op && $nword_op eq "-" && $rs > 0 && substr($exp, $rs-1, 1) ne " " && substr($exp, 0, $rs) !~ m/\b\d+$/; |
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
319
|
0
|
0
|
0
|
|
|
|
if (!$squot && !$dquot) { |
320
|
0
|
0
|
0
|
|
|
|
$start_paren = $re if $rc eq "(" && $level++ == 0; |
321
|
0
|
0
|
0
|
|
|
|
$start_sq = $re if $rc eq "[" && $sq_level++ == 0; |
322
|
0
|
0
|
0
|
|
|
|
$start_hsh = $re if $rc eq "{" && $hsh_level++ == 0; |
323
|
|
|
|
|
|
|
# Deal with parentheses; always the highest level of operation, except when inside a square bracket. |
324
|
0
|
0
|
0
|
|
|
|
if ($rc eq ")" && --$level == 0 && $sq_level == 0) { |
|
|
|
0
|
|
|
|
|
325
|
0
|
|
|
|
|
|
$start_space = $re; |
326
|
0
|
|
|
|
|
|
push(@tokens, WWW::Shopify::Liquid::Token::Grouping->new($line, $self->parse_expression($line, substr($exp, $start_paren, $rs - $start_paren)))); |
327
|
|
|
|
|
|
|
} |
328
|
0
|
0
|
|
|
|
|
--$sq_level if $rc eq "]"; |
329
|
0
|
0
|
|
|
|
|
--$hsh_level if $rc eq "}"; |
330
|
0
|
0
|
0
|
|
|
|
if ($level == 0 && $sq_level == 0 && $hsh_level == 0) { |
|
|
|
0
|
|
|
|
|
331
|
|
|
|
|
|
|
# If we're only spaces, that means we're a new a token. |
332
|
0
|
0
|
0
|
|
|
|
if (defined $whitespace || $nword_op) { |
333
|
0
|
0
|
|
|
|
|
if (defined $start_space) { |
334
|
0
|
|
|
|
|
|
my $contents = substr($exp, $start_space, $rs - $start_space); |
335
|
0
|
0
|
|
|
|
|
push(@tokens, $self->parse_token($line, $contents)) if $contents !~ m/^\s*$/; |
336
|
|
|
|
|
|
|
} |
337
|
0
|
0
|
|
|
|
|
push(@tokens, $self->parse_token($line, $nword_op)) if $nword_op; |
338
|
0
|
|
|
|
|
|
$start_space = $re; |
339
|
|
|
|
|
|
|
} |
340
|
|
|
|
|
|
|
} |
341
|
|
|
|
|
|
|
} |
342
|
0
|
0
|
0
|
|
|
|
$squot = !$squot if ($rc eq "'" && !$dquot); |
343
|
0
|
0
|
0
|
|
|
|
$dquot = !$dquot if ($rc eq '"' && !$squot); |
344
|
|
|
|
|
|
|
} |
345
|
0
|
0
|
|
|
|
|
die WWW::Shopify::Liquid::Exception::Lexer::UnbalancedBrace->new($line) unless $level == 0; |
346
|
|
|
|
|
|
|
# Go through and combine any -1 from OP NUM to NUM. |
347
|
|
|
|
|
|
|
my @ids = grep { |
348
|
0
|
|
|
|
|
|
$tokens[$_]->isa('WWW::Shopify::Liquid::Token::Number') && |
349
|
0
|
0
|
0
|
|
|
|
$tokens[$_-1]->isa('WWW::Shopify::Liquid::Token::Operator') && $tokens[$_-1]->{core} eq "-" && |
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
350
|
|
|
|
|
|
|
($_ == 1 || $tokens[$_-2]->isa('WWW::Shopify::Liquid::Token::Separator') || $tokens[$_-2]->isa('WWW::Shopify::Liquid::Token::Operator')) |
351
|
|
|
|
|
|
|
} 1..$#tokens; |
352
|
0
|
|
|
|
|
|
for (@ids) { $tokens[$_]->{core} *= -1; $tokens[$_-1] = undef; } |
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
353
|
0
|
|
|
|
|
|
@tokens = grep { defined $_ } @tokens; |
|
0
|
|
|
|
|
|
|
354
|
|
|
|
|
|
|
# Go through and combine colon separated arguments into named arguments. |
355
|
|
|
|
|
|
|
@ids = grep { |
356
|
0
|
|
|
|
|
|
($_ == 2 || !$tokens[$_-3]->isa('WWW::Shopify::Liquid::Token::Operator') || $tokens[$_-3]->{core} ne "|") && |
357
|
0
|
|
|
|
|
|
$tokens[$_-2]->isa('WWW::Shopify::Liquid::Token::Variable') && int(@{$tokens[$_-2]->{core}}) == 1 && $tokens[$_-2]->{core}->[0]->isa('WWW::Shopify::Liquid::Token::String') && |
358
|
0
|
0
|
0
|
|
|
|
$tokens[$_-1]->isa('WWW::Shopify::Liquid::Token::Separator') && $tokens[$_-1]->{core} eq ":" && |
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
359
|
|
|
|
|
|
|
($tokens[$_]->isa('WWW::Shopify::Liquid::Token::Grouping') || $tokens[$_]->isa('WWW::Shopify::Liquid::Token::Variable') || $tokens[$_]->isa('WWW::Shopify::Liquid::Token::Number') || $tokens[$_]->isa('WWW::Shopify::Liquid::Token::String')) |
360
|
|
|
|
|
|
|
} (2..$#tokens); |
361
|
0
|
|
|
|
|
|
for (@ids) { $tokens[$_] = WWW::Shopify::Liquid::Token::Variable::Named->new($line, $tokens[$_-2]->{core}->[0]->{core}, $tokens[$_]); $tokens[$_-2] = undef; $tokens[$_-1] = undef; } |
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
362
|
0
|
|
|
|
|
|
@tokens = grep { defined $_ } @tokens; |
|
0
|
|
|
|
|
|
|
363
|
0
|
|
|
|
|
|
return @tokens; |
364
|
|
|
|
|
|
|
} |
365
|
|
|
|
|
|
|
|
366
|
|
|
|
|
|
|
sub parse_text { |
367
|
0
|
|
|
0
|
0
|
|
my ($self, $text) = @_; |
368
|
0
|
0
|
|
|
|
|
return () unless defined $text; |
369
|
0
|
|
|
|
|
|
my @tokens = (); |
370
|
0
|
|
|
|
|
|
my $start = 0; |
371
|
0
|
|
|
|
|
|
my $line = 1; |
372
|
0
|
|
|
|
|
|
my $column = 0; |
373
|
0
|
|
|
|
|
|
my $lexing_halter = undef; |
374
|
|
|
|
|
|
|
|
375
|
0
|
|
|
|
|
|
my $start_line = 1; |
376
|
0
|
|
|
|
|
|
my $start_column = 0; |
377
|
0
|
|
|
|
|
|
my $line_position = 0; |
378
|
|
|
|
|
|
|
|
379
|
0
|
0
|
|
|
|
|
return (WWW::Shopify::Liquid::Token::Text->new(1, '')) if $text eq ''; |
380
|
|
|
|
|
|
|
|
381
|
|
|
|
|
|
|
# No need to worry about quotations; liquid overrides everything. |
382
|
|
|
|
|
|
|
# We specifically look for tags within tags due to lexing halters abilities to be placed pretty much anywhere. |
383
|
|
|
|
|
|
|
# Oh god no, we have to rewrite this so nested output braces inside strings inside output braces are parsed properly. |
384
|
|
|
|
|
|
|
#while ($text =~ m/(?:(?:{%\s*(\w+)\s*(.*?)(?:\s*%}|\s*(?={%)))|(?:{{\s*(.*?)\s*}})|(\n)|$)/sg) { |
385
|
0
|
|
|
|
|
|
my $open_tag = undef; |
386
|
0
|
|
|
|
|
|
my $open_output_tag = undef; |
387
|
0
|
|
|
|
|
|
my $open_single_quote = undef; |
388
|
0
|
|
|
|
|
|
my $open_double_quote = undef; |
389
|
0
|
|
|
|
|
|
my $start_tag_strip = undef; |
390
|
0
|
|
|
|
|
|
my $end_tag_strip = undef; |
391
|
0
|
|
|
|
|
|
my @lines = split(/\n/, $text); |
392
|
0
|
|
|
|
|
|
while ($text =~ m/(?:(?:({%\-?)|(\-?%}))|(?:(\{\{\-?)|(\-?\}\}))|(\n)|(")|(')|$)/sg) { |
393
|
0
|
|
|
|
|
|
my ($start_tag, $end_tag, $start_output_tag, $end_output_tag, $newline, $double_quote, $single_quote) = ($1, $2, $3, $4, $5, $6, $7); |
394
|
0
|
0
|
0
|
|
|
|
next if ($double_quote || $single_quote) && !$open_tag && !$open_output_tag; |
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
395
|
0
|
|
|
|
|
|
my $column = $+[0] - $line_position; |
396
|
0
|
|
|
|
|
|
my $position = [$start_line, $start_column, $-[0], $self->file_context]; |
397
|
0
|
|
|
|
|
|
my $end_position = [$start_line, $start_column, $+[0], $self->file_context]; |
398
|
|
|
|
|
|
|
|
399
|
0
|
0
|
0
|
|
|
|
if ($start_output_tag || $start_tag) { |
|
|
0
|
0
|
|
|
|
|
400
|
0
|
|
0
|
|
|
|
$start_tag_strip = index(($start_output_tag || $start_tag), "-") != -1; |
401
|
0
|
|
|
|
|
|
$end_tag_strip = undef; |
402
|
|
|
|
|
|
|
} elsif ($end_output_tag || $end_tag) { |
403
|
0
|
|
0
|
|
|
|
$end_tag_strip = index(($end_output_tag || $end_tag), "-") != -1; |
404
|
|
|
|
|
|
|
} |
405
|
|
|
|
|
|
|
|
406
|
0
|
0
|
|
|
|
|
if (!$lexing_halter) { |
407
|
0
|
0
|
0
|
|
|
|
if ($double_quote || $single_quote) { |
408
|
0
|
0
|
0
|
|
|
|
if (!$open_double_quote && $single_quote) { |
|
|
0
|
0
|
|
|
|
|
409
|
0
|
0
|
|
|
|
|
$open_single_quote = $open_single_quote ? undef : $position; |
410
|
|
|
|
|
|
|
} elsif (!$open_single_quote && $double_quote) { |
411
|
0
|
0
|
|
|
|
|
$open_double_quote = $open_double_quote ? undef : $position; |
412
|
|
|
|
|
|
|
} |
413
|
0
|
|
|
|
|
|
next; |
414
|
|
|
|
|
|
|
} |
415
|
0
|
0
|
0
|
|
|
|
next if $open_single_quote || $open_double_quote; |
416
|
|
|
|
|
|
|
|
417
|
0
|
0
|
0
|
|
|
|
die new WWW::Shopify::Liquid::Exception::Lexer::UnbalancedTag([$line, ($-[0] - $line_position), $-[0]]) if ($end_tag && !defined $open_tag); |
418
|
|
|
|
|
|
|
# Unbalanced outputs tags shouldn't throw a lexer exception, due to CSS rules. We just treat this as part of text. |
419
|
|
|
|
|
|
|
# die new WWW::Shopify::Liquid::Exception::Lexer::UnbalancedOutputTag($position) if $end_output_tag && !defined $open_output_tag; |
420
|
0
|
0
|
0
|
|
|
|
next if $end_output_tag && !defined $open_output_tag; |
421
|
|
|
|
|
|
|
} |
422
|
0
|
0
|
|
|
|
|
if ($newline) { |
423
|
0
|
|
|
|
|
|
++$line; |
424
|
0
|
|
|
|
|
|
$line_position = $+[0]; |
425
|
0
|
|
|
|
|
|
next; |
426
|
|
|
|
|
|
|
} |
427
|
0
|
0
|
|
|
|
|
if ($start_tag) { |
428
|
0
|
|
|
|
|
|
$open_tag = $position; |
429
|
0
|
|
|
|
|
|
next; |
430
|
|
|
|
|
|
|
} |
431
|
0
|
0
|
|
|
|
|
if ($start_output_tag) { |
432
|
0
|
|
|
|
|
|
$open_output_tag = $position; |
433
|
0
|
|
|
|
|
|
next; |
434
|
|
|
|
|
|
|
} |
435
|
|
|
|
|
|
|
|
436
|
0
|
|
|
|
|
|
my ($tag, $arguments, $output); |
437
|
0
|
|
|
|
|
|
my $start_element = undef; |
438
|
0
|
|
|
|
|
|
my $end_element = undef; |
439
|
0
|
0
|
0
|
|
|
|
if ($end_tag && $open_tag) { |
|
|
0
|
0
|
|
|
|
|
440
|
0
|
|
|
|
|
|
my $tag_line = substr($text, $open_tag->[2]+2, $-[0] - $open_tag->[2] - 2); |
441
|
0
|
|
|
|
|
|
$start_element = $open_tag; |
442
|
0
|
|
|
|
|
|
$end_element = $end_position; |
443
|
0
|
|
|
|
|
|
$open_tag = undef; |
444
|
0
|
0
|
|
|
|
|
die new WWW::Shopify::Liquid::Exception::Lexer::Tag($start_element, $tag_line) unless $tag_line =~ m/\s*(\w+)\s*(.*?)\s*$/s; |
445
|
0
|
|
|
|
|
|
($tag, $arguments) = ($1, $2); |
446
|
|
|
|
|
|
|
} elsif ($end_output_tag && $open_output_tag) { |
447
|
0
|
|
|
|
|
|
$start_element = $open_output_tag; |
448
|
0
|
|
|
|
|
|
$end_element = $end_position; |
449
|
0
|
|
|
|
|
|
$output = substr($text, $open_output_tag->[2]+2, $-[0] - $open_output_tag->[2] - 2); |
450
|
0
|
|
|
|
|
|
$output =~ s/(^\s*|\s*$)//g; |
451
|
0
|
|
|
|
|
|
$open_output_tag = undef; |
452
|
|
|
|
|
|
|
} else { |
453
|
0
|
|
|
|
|
|
$start_element = $position; |
454
|
0
|
|
|
|
|
|
$end_element = $end_position; |
455
|
|
|
|
|
|
|
} |
456
|
|
|
|
|
|
|
|
457
|
0
|
0
|
0
|
|
|
|
if ($tag && !$lexing_halter && exists $self->{lexing_halters}->{$tag}) { |
|
|
0
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
458
|
0
|
|
|
|
|
|
$lexing_halter = $tag; |
459
|
0
|
0
|
|
|
|
|
push(@tokens, WWW::Shopify::Liquid::Token::Text->new($position, substr($text, $start, $start_element->[2] - $start), $start_tag_strip, $end_tag_strip)) if ($start < $start_element->[2]); |
460
|
0
|
|
|
|
|
|
push(@tokens, WWW::Shopify::Liquid::Token::Tag->new($position, $tag, [$self->parse_expression($position, $arguments)], $start_tag_strip, $end_tag_strip)); |
461
|
0
|
|
|
|
|
|
$start = $end_element->[2]; |
462
|
0
|
|
|
|
|
|
$start_line = $line; |
463
|
0
|
|
|
|
|
|
$start_column = $start_element->[2] - $line_position; |
464
|
0
|
|
|
|
|
|
$position = [$start_line, $start_column, $start_element->[2], $self->file_context]; |
465
|
0
|
|
|
|
|
|
$start_element = $position; |
466
|
|
|
|
|
|
|
} |
467
|
|
|
|
|
|
|
elsif ($tag && $lexing_halter && $tag eq "end" . $lexing_halter) { |
468
|
0
|
|
|
|
|
|
$lexing_halter = undef; |
469
|
|
|
|
|
|
|
} |
470
|
0
|
0
|
|
|
|
|
if (!$lexing_halter) { |
471
|
0
|
0
|
|
|
|
|
if ($start < $start_element->[2]) { |
472
|
0
|
|
|
|
|
|
push(@tokens, WWW::Shopify::Liquid::Token::Text->new([$start_line, $start_column, $start, $self->file_context], substr($text, $start, $start_element->[2] - $start), $start_tag_strip, $end_tag_strip)); |
473
|
|
|
|
|
|
|
# Strip whitespace. |
474
|
0
|
0
|
0
|
|
|
|
if (int(@tokens) > 1 && $tokens[-1]->isa('WWW::Shopify::Liquid::Token::Text') && ($tokens[-2]->isa('WWW::Shopify::Liquid::Token::Tag') || $tokens[-2]->isa('WWW::Shopify::Liquid::Token::Output')) && $tokens[-2]->{strip_right}) { |
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
475
|
0
|
0
|
|
|
|
|
if ($tokens[-1]->isa('WWW::Shopify::Liquid::Token::Text::Whitespace')) { |
476
|
0
|
|
|
|
|
|
splice(@tokens, -1, 1); |
477
|
|
|
|
|
|
|
} else { |
478
|
0
|
|
|
|
|
|
$tokens[-1]->{core} =~ s/^\s+//ms; |
479
|
|
|
|
|
|
|
} |
480
|
|
|
|
|
|
|
} |
481
|
|
|
|
|
|
|
|
482
|
0
|
|
|
|
|
|
$start_line = $line; |
483
|
0
|
|
|
|
|
|
$start_column = $start_element->[2] - $line_position; |
484
|
0
|
|
|
|
|
|
$position = [$start_line, $start_column, $start_element->[2], $self->file_context]; |
485
|
0
|
|
|
|
|
|
$start_element = $position; |
486
|
|
|
|
|
|
|
} |
487
|
0
|
0
|
|
|
|
|
push(@tokens, WWW::Shopify::Liquid::Token::Tag->new($start_element, $tag, [$self->parse_expression($position, $arguments)], $start_tag_strip, $end_tag_strip)) if $tag; |
488
|
0
|
0
|
|
|
|
|
push(@tokens, WWW::Shopify::Liquid::Token::Output->new($start_element, [$self->parse_expression($position, $output)], $start_tag_strip, $end_tag_strip)) if $output; |
489
|
0
|
|
|
|
|
|
$start = $end_element->[2]; |
490
|
0
|
|
|
|
|
|
$start_line = $line; |
491
|
0
|
|
|
|
|
|
$start_column = $column; |
492
|
|
|
|
|
|
|
} |
493
|
|
|
|
|
|
|
# Strip whitespace. |
494
|
0
|
0
|
0
|
|
|
|
if ($tag && $start_tag_strip && int(@tokens) > 1 && $tokens[-2]->isa('WWW::Shopify::Liquid::Token::Text')) { |
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
495
|
0
|
0
|
|
|
|
|
if ($tokens[-2]->isa('WWW::Shopify::Liquid::Token::Text::Whitespace')) { |
496
|
0
|
|
|
|
|
|
splice(@tokens, -2, 1); |
497
|
|
|
|
|
|
|
} else { |
498
|
0
|
|
|
|
|
|
$tokens[-2]->{core} =~ s/\s+$//ms; |
499
|
|
|
|
|
|
|
} |
500
|
|
|
|
|
|
|
} |
501
|
|
|
|
|
|
|
|
502
|
|
|
|
|
|
|
} |
503
|
0
|
0
|
|
|
|
|
die new WWW::Shopify::Liquid::Exception::Lexer::UnbalancedSingleQuote($open_single_quote) if $open_single_quote; |
504
|
0
|
0
|
|
|
|
|
die new WWW::Shopify::Liquid::Exception::Lexer::UnbalancedDoubleQuote($open_double_quote) if $open_double_quote; |
505
|
0
|
0
|
|
|
|
|
die new WWW::Shopify::Liquid::Exception::Lexer::UnbalancedTag($open_tag) if $open_tag; |
506
|
0
|
0
|
|
|
|
|
die new WWW::Shopify::Liquid::Exception::Lexer::UnbalancedLexingHalt() if $lexing_halter; |
507
|
0
|
0
|
|
|
|
|
die new WWW::Shopify::Liquid::Exception::Lexer::UnbalancedOutputTag($open_output_tag) if $open_output_tag; |
508
|
0
|
|
|
|
|
|
return @tokens; |
509
|
|
|
|
|
|
|
} |
510
|
|
|
|
|
|
|
|
511
|
|
|
|
|
|
|
sub unparse_token { |
512
|
0
|
|
|
0
|
0
|
|
my ($self, $token) = @_; |
513
|
0
|
0
|
|
|
|
|
return "null" if !defined $token; |
514
|
0
|
0
|
|
|
|
|
return '' if $token eq ''; |
515
|
0
|
0
|
0
|
|
|
|
return "'" . do { $token =~ s/'/\\'/g; $token } . "'" if !blessed($token) && !looks_like_number($token); |
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
516
|
0
|
0
|
|
|
|
|
return $token unless blessed($token); |
517
|
0
|
0
|
|
|
|
|
return "null" if $token->isa('WWW::Shopify::Liquid::Token::NULL'); |
518
|
|
|
|
|
|
|
sub translate_variable { |
519
|
0
|
|
|
0
|
0
|
|
my ($self, $token) = @_; |
520
|
|
|
|
|
|
|
my $variable = join(".", map { |
521
|
|
|
|
|
|
|
!$self->is_processed($_) ? ( |
522
|
|
|
|
|
|
|
$_->isa('WWW::Shopify::Liquid::Token::Variable') ? ('[' . translate_variable($self, $_) . ']') : $_->{core} |
523
|
0
|
0
|
|
|
|
|
) : $_ |
|
|
0
|
|
|
|
|
|
524
|
0
|
|
|
|
|
|
} @{$token->{core}}); |
|
0
|
|
|
|
|
|
|
525
|
0
|
|
|
|
|
|
$variable =~ s/\.\[/\[/g; |
526
|
0
|
|
|
|
|
|
return $variable; |
527
|
|
|
|
|
|
|
} |
528
|
|
|
|
|
|
|
|
529
|
0
|
0
|
|
|
|
|
return translate_variable($self, $token) if $token->isa('WWW::Shopify::Liquid::Token::Variable'); |
530
|
0
|
0
|
|
|
|
|
return "(" . join("", map { $self->unparse_token($_); } @{$token->{members}}) . ")" if $token->isa('WWW::Shopify::Liquid::Token::Grouping'); |
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
531
|
0
|
0
|
|
|
|
|
return "'" . $token->{core} . "'" if $token->isa('WWW::Shopify::Liquid::Token::String'); |
532
|
0
|
|
|
|
|
|
return $token->{core}; |
533
|
|
|
|
|
|
|
} |
534
|
|
|
|
|
|
|
|
535
|
|
|
|
|
|
|
sub unparse_expression { |
536
|
0
|
|
|
0
|
0
|
|
my ($self, @tokens) = @_; |
537
|
0
|
0
|
0
|
|
|
|
my $a = join("", map { ($_ eq ":" || $_ eq ",") ? $_ : " " . $_; } grep { defined $_ } map { $self->unparse_token($_) } @tokens); |
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
538
|
0
|
|
|
|
|
|
$a =~ s/^ //; |
539
|
0
|
|
|
|
|
|
return $a; |
540
|
|
|
|
|
|
|
} |
541
|
|
|
|
|
|
|
|
542
|
|
|
|
|
|
|
sub unparse_text_segment { |
543
|
0
|
|
|
0
|
0
|
|
my ($self, $token) = @_; |
544
|
0
|
0
|
|
|
|
|
my $space = $self->unparse_spaces ? " " : ""; |
545
|
0
|
0
|
|
|
|
|
return $token if $self->is_processed($token); |
546
|
0
|
0
|
|
|
|
|
if ($token->isa('WWW::Shopify::Liquid::Token::Tag')) { |
547
|
0
|
0
|
0
|
|
|
|
return "{%" . $space . $token->{tag} . $space . "%}" if !$token->{arguments} || int(@{$token->{arguments}}) == 0; |
|
0
|
|
|
|
|
|
|
548
|
0
|
|
|
|
|
|
return "{%" . $space . $token->{tag} . " " . $self->unparse_expression(@{$token->{arguments}}) . $space . "%}"; |
|
0
|
|
|
|
|
|
|
549
|
|
|
|
|
|
|
} |
550
|
0
|
0
|
|
|
|
|
return "{{" . $space . $self->unparse_expression(@{$token->{core}}) . $space . "}}" if $token->isa('WWW::Shopify::Liquid::Token::Output'); |
|
0
|
|
|
|
|
|
|
551
|
0
|
|
|
|
|
|
return $token->{core}; |
552
|
|
|
|
|
|
|
} |
553
|
|
|
|
|
|
|
|
554
|
|
|
|
|
|
|
sub unparse_text { |
555
|
0
|
|
|
0
|
0
|
|
my ($self, @tokens) = @_; |
556
|
0
|
|
|
|
|
|
return join('', grep { defined $_ } map { $self->unparse_text_segment($_) } @tokens); |
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
557
|
|
|
|
|
|
|
} |
558
|
|
|
|
|
|
|
|
559
|
|
|
|
|
|
|
1; |