| 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; |