Skip to content

Commit a63e874

Browse files
authored
Fix ECR escape sequences containing - (#14739)
1 parent 884f382 commit a63e874

File tree

5 files changed

+118
-19
lines changed

5 files changed

+118
-19
lines changed

spec/std/data/test_template7.ecr

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
<%% if @name %>
2+
Greetings, <%%= @name %>!
3+
<%-% else -%>
4+
Greetings!
5+
<%-% end -%>

spec/std/ecr/ecr_lexer_spec.cr

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -210,6 +210,87 @@ describe "ECR::Lexer" do
210210
token.type.should eq(t :eof)
211211
end
212212

213+
it "lexes with <%-% %> (#14734)" do
214+
lexer = ECR::Lexer.new("hello <%-% foo %> bar")
215+
216+
token = lexer.next_token
217+
token.type.should eq(t :string)
218+
token.value.should eq("hello ")
219+
token.column_number.should eq(1)
220+
token.line_number.should eq(1)
221+
222+
token = lexer.next_token
223+
token.type.should eq(t :string)
224+
token.value.should eq("<%- foo %>")
225+
token.line_number.should eq(1)
226+
token.column_number.should eq(11)
227+
token.suppress_leading?.should be_false
228+
token.suppress_trailing?.should be_false
229+
230+
token = lexer.next_token
231+
token.type.should eq(t :string)
232+
token.value.should eq(" bar")
233+
token.line_number.should eq(1)
234+
token.column_number.should eq(18)
235+
236+
token = lexer.next_token
237+
token.type.should eq(t :eof)
238+
end
239+
240+
it "lexes with <%-%= %> (#14734)" do
241+
lexer = ECR::Lexer.new("hello <%-%= foo %> bar")
242+
243+
token = lexer.next_token
244+
token.type.should eq(t :string)
245+
token.value.should eq("hello ")
246+
token.column_number.should eq(1)
247+
token.line_number.should eq(1)
248+
249+
token = lexer.next_token
250+
token.type.should eq(t :string)
251+
token.value.should eq("<%-= foo %>")
252+
token.line_number.should eq(1)
253+
token.column_number.should eq(11)
254+
token.suppress_leading?.should be_false
255+
token.suppress_trailing?.should be_false
256+
257+
token = lexer.next_token
258+
token.type.should eq(t :string)
259+
token.value.should eq(" bar")
260+
token.line_number.should eq(1)
261+
token.column_number.should eq(19)
262+
263+
token = lexer.next_token
264+
token.type.should eq(t :eof)
265+
end
266+
267+
it "lexes with <%% -%> (#14734)" do
268+
lexer = ECR::Lexer.new("hello <%% foo -%> bar")
269+
270+
token = lexer.next_token
271+
token.type.should eq(t :string)
272+
token.value.should eq("hello ")
273+
token.column_number.should eq(1)
274+
token.line_number.should eq(1)
275+
276+
token = lexer.next_token
277+
token.type.should eq(t :string)
278+
token.value.should eq("<% foo -%>")
279+
token.line_number.should eq(1)
280+
token.column_number.should eq(10)
281+
token.suppress_leading?.should be_false
282+
token.suppress_trailing?.should be_false
283+
284+
token = lexer.next_token
285+
token.type.should eq(t :string)
286+
token.value.should eq(" bar")
287+
token.line_number.should eq(1)
288+
token.column_number.should eq(18)
289+
290+
token = lexer.next_token
291+
token.type.should eq(t :eof)
292+
end
293+
213294
it "lexes with <% %> and correct location info" do
214295
lexer = ECR::Lexer.new("hi\nthere <% foo\nbar %> baz")
215296

spec/std/ecr/ecr_spec.cr

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,18 @@ describe "ECR" do
6565
io.to_s.should eq("string with -%")
6666
end
6767

68+
it "does with <%% %>" do
69+
io = IO::Memory.new
70+
ECR.embed "#{__DIR__}/../data/test_template7.ecr", io
71+
io.to_s.should eq(<<-ECR)
72+
<% if @name %>
73+
Greetings, <%= @name %>!
74+
<%- else -%>
75+
Greetings!
76+
<%- end -%>
77+
ECR
78+
end
79+
6880
it ".render" do
6981
ECR.render("#{__DIR__}/../data/test_template2.ecr").should eq("123")
7082
end

src/ecr.cr

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,8 @@
1414
#
1515
# A comment can be created the same as normal code: `<% # hello %>` or by the special
1616
# tag: `<%# hello %>`. An ECR tag can be inserted directly (i.e. the tag itself may be
17-
# escaped) by using a second `%` like so: `<%% a = b %>` or `<%%= foo %>`.
17+
# escaped) by using a second `%` like so: `<%% a = b %>` or `<%%= foo %>`. Dashes may
18+
# also be present in those escaped tags and have no effect on the surrounding text.
1819
#
1920
# NOTE: To use `ECR`, you must explicitly import it with `require "ecr"`
2021
#

src/ecr/lexer.cr

Lines changed: 18 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -44,12 +44,8 @@ class ECR::Lexer
4444
next_char
4545
next_char
4646

47-
if current_char == '-'
48-
@token.suppress_leading = true
49-
next_char
50-
else
51-
@token.suppress_leading = false
52-
end
47+
suppress_leading = current_char == '-'
48+
next_char if suppress_leading
5349

5450
case current_char
5551
when '='
@@ -64,7 +60,7 @@ class ECR::Lexer
6460
copy_location_info_to_token
6561
end
6662

67-
return consume_control(is_output, is_escape)
63+
return consume_control(is_output, is_escape, suppress_leading)
6864
end
6965
else
7066
# consume string
@@ -97,7 +93,7 @@ class ECR::Lexer
9793
@token
9894
end
9995

100-
private def consume_control(is_output, is_escape)
96+
private def consume_control(is_output, is_escape, suppress_leading)
10197
start_pos = current_pos
10298
while true
10399
case current_char
@@ -126,17 +122,15 @@ class ECR::Lexer
126122
@column_number = column_number
127123

128124
if is_end
129-
@token.suppress_trailing = true
130-
setup_control_token(start_pos, is_escape)
125+
setup_control_token(start_pos, is_escape, suppress_leading, true)
131126
raise "Expecting '>' after '-%'" if current_char != '>'
132127
next_char
133128
break
134129
end
135130
end
136131
when '%'
137132
if peek_next_char == '>'
138-
@token.suppress_trailing = false
139-
setup_control_token(start_pos, is_escape)
133+
setup_control_token(start_pos, is_escape, suppress_leading, false)
140134
break
141135
end
142136
else
@@ -155,12 +149,18 @@ class ECR::Lexer
155149
@token
156150
end
157151

158-
private def setup_control_token(start_pos, is_escape)
159-
@token.value = if is_escape
160-
"<%#{string_range(start_pos, current_pos + 2)}"
161-
else
162-
string_range(start_pos)
163-
end
152+
private def setup_control_token(start_pos, is_escape, suppress_leading, suppress_trailing)
153+
@token.suppress_leading = !is_escape && suppress_leading
154+
@token.suppress_trailing = !is_escape && suppress_trailing
155+
@token.value =
156+
if is_escape
157+
head = suppress_leading ? "<%-" : "<%"
158+
tail = string_range(start_pos, current_pos + (suppress_trailing ? 3 : 2))
159+
head + tail
160+
else
161+
string_range(start_pos)
162+
end
163+
164164
next_char
165165
next_char
166166
end

0 commit comments

Comments
 (0)