haml 7.2.0 → 7.2.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.github/workflows/test.yml +1 -0
- data/CHANGELOG.md +8 -0
- data/Gemfile +2 -1
- data/lib/haml/attribute_parser.rb +1 -2
- data/lib/haml/compiler/script_compiler.rb +0 -4
- data/lib/haml/compiler/tag_compiler.rb +2 -4
- data/lib/haml/parser.rb +13 -5
- data/lib/haml/string_splitter.rb +90 -100
- data/lib/haml/version.rb +1 -1
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: def36fc38e0b0116356b085a4120ea0564c5624c766e03f705ec91d3657e2e8c
|
|
4
|
+
data.tar.gz: 937230d9bbf1ad0cbad38457d985bd964a5edd49137f31398fafad0aa474a8bf
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: f57d6abe6fdac34effcac8e52c889142828bf405f915239526fe6540ccb2fe138e79f972536e79a1c5dc7ab4de1871030900d0b2a0118325049d316bd5e534f7
|
|
7
|
+
data.tar.gz: b9135ee6741e0105df6e5f4a92c5c3ea660e10be525aca983d2b0d7f509f80d61216fcc32d340f678c3e1ca7ab91da70d4a436a6c45ce6b956d48cf8105097c8
|
data/.github/workflows/test.yml
CHANGED
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,13 @@
|
|
|
1
1
|
# Haml Changelog
|
|
2
2
|
|
|
3
|
+
## 7.2.2
|
|
4
|
+
|
|
5
|
+
* Remove obsolete TruffleRuby compatibility skips https://github.com/haml/haml/pull/1210
|
|
6
|
+
|
|
7
|
+
## 7.2.1
|
|
8
|
+
|
|
9
|
+
* Do not rely on Ripper quirk in parsing old-style Haml attributes https://github.com/haml/haml/pull/1212
|
|
10
|
+
|
|
3
11
|
## 7.2.0
|
|
4
12
|
|
|
5
13
|
* Do not require rails when haml is required https://github.com/haml/haml/pull/1201
|
data/Gemfile
CHANGED
|
@@ -16,10 +16,11 @@ gem 'mutex_m'
|
|
|
16
16
|
|
|
17
17
|
if /java/.match?(RUBY_PLATFORM) # JRuby
|
|
18
18
|
gem 'pandoc-ruby'
|
|
19
|
+
gem 'rdoc', '< 8' # rdoc 8 depends on rbs, whose native extension doesn't build on JRuby
|
|
19
20
|
else
|
|
20
21
|
gem 'redcarpet'
|
|
21
22
|
|
|
22
|
-
if RUBY_PLATFORM !~ /mswin|mingw/
|
|
23
|
+
if RUBY_PLATFORM !~ /mswin|mingw/
|
|
23
24
|
gem 'stackprof'
|
|
24
25
|
end
|
|
25
26
|
end
|
|
@@ -8,8 +8,7 @@ module Haml
|
|
|
8
8
|
|
|
9
9
|
# @return [TrueClass, FalseClass] - return true if AttributeParser.parse can be used.
|
|
10
10
|
def self.available?
|
|
11
|
-
|
|
12
|
-
defined?(Ripper) && Ripper.respond_to?(:lex) && Temple::StaticAnalyzer.available?
|
|
11
|
+
Temple::StaticAnalyzer.available?
|
|
13
12
|
end
|
|
14
13
|
|
|
15
14
|
def self.parse(text)
|
|
@@ -21,10 +21,6 @@ module Haml
|
|
|
21
21
|
end
|
|
22
22
|
|
|
23
23
|
def compile(node, &block)
|
|
24
|
-
unless Ripper.respond_to?(:lex) # No Ripper.lex in truffleruby
|
|
25
|
-
return dynamic_compile(node, &block)
|
|
26
|
-
end
|
|
27
|
-
|
|
28
24
|
no_children = node.children.empty?
|
|
29
25
|
case
|
|
30
26
|
when no_children && node.value[:escape_interpolation]
|
|
@@ -28,10 +28,8 @@ module Haml
|
|
|
28
28
|
nil
|
|
29
29
|
when node.value[:parse]
|
|
30
30
|
return compile_interpolated_plain(node) if node.value[:escape_interpolation]
|
|
31
|
-
if
|
|
32
|
-
|
|
33
|
-
return delegate_optimization(node) if Temple::StaticAnalyzer.static?(node.value[:value])
|
|
34
|
-
end
|
|
31
|
+
return delegate_optimization(node) if RubyExpression.string_literal?(node.value[:value])
|
|
32
|
+
return delegate_optimization(node) if Temple::StaticAnalyzer.static?(node.value[:value])
|
|
35
33
|
|
|
36
34
|
var = @identity.generate
|
|
37
35
|
[:multi,
|
data/lib/haml/parser.rb
CHANGED
|
@@ -688,8 +688,17 @@ module Haml
|
|
|
688
688
|
#
|
|
689
689
|
# To scan such code correctly, this scans `a( hash, foo: bar }` instead, stops when there is
|
|
690
690
|
# 1 more :on_embexpr_end (the last '}') than :on_embexpr_beg, and resurrects '{' afterwards.
|
|
691
|
-
|
|
692
|
-
|
|
691
|
+
#
|
|
692
|
+
# Old attributes allow invalid Hash constructs (e.g., `{ hash, foo: bar }`).
|
|
693
|
+
# Replacing the opening `{` with `a(` makes the syntax look like a method call so
|
|
694
|
+
# Ripper can lex it reliably.
|
|
695
|
+
|
|
696
|
+
attributes_hash, rest = balance_tokens(
|
|
697
|
+
text.sub(?{, METHOD_CALL_PREFIX),
|
|
698
|
+
[:on_lbrace, :on_tlambeg, :on_embexpr_beg],
|
|
699
|
+
[:on_rbrace, :on_embexpr_end],
|
|
700
|
+
count: 1)
|
|
701
|
+
attributes_hash = attributes_hash.sub(METHOD_CALL_PREFIX, ?{)
|
|
693
702
|
rescue SyntaxError => e
|
|
694
703
|
if e.message == Error.message(:unbalanced_brackets) && !@template.empty?
|
|
695
704
|
text << "\n#{@next_line.text}"
|
|
@@ -850,10 +859,9 @@ module Haml
|
|
|
850
859
|
text = ''.dup
|
|
851
860
|
Ripper.lex(buf).each do |_, token, str|
|
|
852
861
|
text << str
|
|
853
|
-
|
|
854
|
-
when start
|
|
862
|
+
if start.include?(token)
|
|
855
863
|
count += 1
|
|
856
|
-
|
|
864
|
+
elsif finish.include?(token)
|
|
857
865
|
count -= 1
|
|
858
866
|
end
|
|
859
867
|
|
data/lib/haml/string_splitter.rb
CHANGED
|
@@ -1,139 +1,129 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
require 'ripper'
|
|
4
|
-
rescue LoadError
|
|
5
|
-
end
|
|
2
|
+
require 'ripper'
|
|
6
3
|
|
|
7
4
|
module Haml
|
|
8
5
|
# Compile [:dynamic, "foo#{bar}"] to [:multi, [:static, 'foo'], [:dynamic, 'bar']]
|
|
9
6
|
class StringSplitter < Temple::Filter
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
raise(Haml::InternalError, "Expected token size >= 2 but got: #{tokens.size}")
|
|
20
|
-
end
|
|
21
|
-
compile_tokens!(exps, tokens)
|
|
7
|
+
class << self
|
|
8
|
+
# `code` param must be valid string literal
|
|
9
|
+
def compile(code)
|
|
10
|
+
[].tap do |exps|
|
|
11
|
+
tokens = Ripper.lex(code.strip)
|
|
12
|
+
tokens.pop while tokens.last && [:on_comment, :on_sp].include?(tokens.last[1])
|
|
13
|
+
|
|
14
|
+
if tokens.size < 2
|
|
15
|
+
raise(Haml::InternalError, "Expected token size >= 2 but got: #{tokens.size}")
|
|
22
16
|
end
|
|
17
|
+
compile_tokens!(exps, tokens)
|
|
23
18
|
end
|
|
19
|
+
end
|
|
24
20
|
|
|
25
|
-
|
|
21
|
+
private
|
|
26
22
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
23
|
+
def strip_quotes!(tokens)
|
|
24
|
+
_, type, beg_str = tokens.shift
|
|
25
|
+
if type != :on_tstring_beg
|
|
26
|
+
raise(Haml::InternalError, "Expected :on_tstring_beg but got: #{type}")
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
_, type, end_str = tokens.pop
|
|
30
|
+
if type != :on_tstring_end
|
|
31
|
+
raise(Haml::InternalError, "Expected :on_tstring_end but got: #{type}")
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
[beg_str, end_str]
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def compile_tokens!(exps, tokens)
|
|
38
|
+
beg_str, end_str = strip_quotes!(tokens)
|
|
39
|
+
|
|
40
|
+
until tokens.empty?
|
|
41
|
+
_, type, str = tokens.shift
|
|
32
42
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
43
|
+
case type
|
|
44
|
+
when :on_tstring_content
|
|
45
|
+
beg_str, end_str = escape_quotes(beg_str, end_str)
|
|
46
|
+
exps << [:static, eval("#{beg_str}#{str}#{end_str}").to_s]
|
|
47
|
+
when :on_embexpr_beg
|
|
48
|
+
embedded = shift_balanced_embexpr(tokens)
|
|
49
|
+
exps << [:dynamic, embedded] unless embedded.empty?
|
|
36
50
|
end
|
|
51
|
+
end
|
|
52
|
+
end
|
|
37
53
|
|
|
54
|
+
# Some quotes are split-unsafe. Replace such quotes with null characters.
|
|
55
|
+
def escape_quotes(beg_str, end_str)
|
|
56
|
+
case [beg_str[-1], end_str]
|
|
57
|
+
when ['(', ')'], ['[', ']'], ['{', '}']
|
|
58
|
+
[beg_str.sub(/.\z/) { "\0" }, "\0"]
|
|
59
|
+
else
|
|
38
60
|
[beg_str, end_str]
|
|
39
61
|
end
|
|
62
|
+
end
|
|
40
63
|
|
|
41
|
-
|
|
42
|
-
|
|
64
|
+
def shift_balanced_embexpr(tokens)
|
|
65
|
+
String.new.tap do |embedded|
|
|
66
|
+
embexpr_open = 1
|
|
43
67
|
|
|
44
68
|
until tokens.empty?
|
|
45
69
|
_, type, str = tokens.shift
|
|
46
|
-
|
|
47
70
|
case type
|
|
48
|
-
when :on_tstring_content
|
|
49
|
-
beg_str, end_str = escape_quotes(beg_str, end_str)
|
|
50
|
-
exps << [:static, eval("#{beg_str}#{str}#{end_str}").to_s]
|
|
51
71
|
when :on_embexpr_beg
|
|
52
|
-
|
|
53
|
-
|
|
72
|
+
embexpr_open += 1
|
|
73
|
+
when :on_embexpr_end
|
|
74
|
+
embexpr_open -= 1
|
|
75
|
+
break if embexpr_open == 0
|
|
54
76
|
end
|
|
55
|
-
end
|
|
56
|
-
end
|
|
57
77
|
|
|
58
|
-
|
|
59
|
-
def escape_quotes(beg_str, end_str)
|
|
60
|
-
case [beg_str[-1], end_str]
|
|
61
|
-
when ['(', ')'], ['[', ']'], ['{', '}']
|
|
62
|
-
[beg_str.sub(/.\z/) { "\0" }, "\0"]
|
|
63
|
-
else
|
|
64
|
-
[beg_str, end_str]
|
|
65
|
-
end
|
|
66
|
-
end
|
|
67
|
-
|
|
68
|
-
def shift_balanced_embexpr(tokens)
|
|
69
|
-
String.new.tap do |embedded|
|
|
70
|
-
embexpr_open = 1
|
|
71
|
-
|
|
72
|
-
until tokens.empty?
|
|
73
|
-
_, type, str = tokens.shift
|
|
74
|
-
case type
|
|
75
|
-
when :on_embexpr_beg
|
|
76
|
-
embexpr_open += 1
|
|
77
|
-
when :on_embexpr_end
|
|
78
|
-
embexpr_open -= 1
|
|
79
|
-
break if embexpr_open == 0
|
|
80
|
-
end
|
|
81
|
-
|
|
82
|
-
embedded << str
|
|
83
|
-
end
|
|
78
|
+
embedded << str
|
|
84
79
|
end
|
|
85
80
|
end
|
|
86
81
|
end
|
|
82
|
+
end
|
|
87
83
|
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
end
|
|
84
|
+
def on_dynamic(code)
|
|
85
|
+
return [:dynamic, code] unless string_literal?(code)
|
|
86
|
+
return [:dynamic, code] if code.include?("\n")
|
|
87
|
+
|
|
88
|
+
temple = [:multi]
|
|
89
|
+
StringSplitter.compile(code).each do |type, content|
|
|
90
|
+
case type
|
|
91
|
+
when :static
|
|
92
|
+
temple << [:static, content]
|
|
93
|
+
when :dynamic
|
|
94
|
+
temple << on_dynamic(content)
|
|
100
95
|
end
|
|
101
|
-
temple
|
|
102
96
|
end
|
|
97
|
+
temple
|
|
98
|
+
end
|
|
103
99
|
|
|
104
|
-
|
|
100
|
+
private
|
|
105
101
|
|
|
106
|
-
|
|
107
|
-
|
|
102
|
+
def string_literal?(code)
|
|
103
|
+
return false if SyntaxChecker.syntax_error?(code)
|
|
108
104
|
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
105
|
+
type, instructions = Ripper.sexp(code)
|
|
106
|
+
return false if type != :program
|
|
107
|
+
return false if instructions.size > 1
|
|
112
108
|
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
109
|
+
type, _ = instructions.first
|
|
110
|
+
type == :string_literal
|
|
111
|
+
end
|
|
116
112
|
|
|
117
|
-
|
|
118
|
-
|
|
113
|
+
class SyntaxChecker < Ripper
|
|
114
|
+
class ParseError < StandardError; end
|
|
119
115
|
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
116
|
+
def self.syntax_error?(code)
|
|
117
|
+
self.new(code).parse
|
|
118
|
+
false
|
|
119
|
+
rescue ParseError
|
|
120
|
+
true
|
|
121
|
+
end
|
|
126
122
|
|
|
127
|
-
|
|
123
|
+
private
|
|
128
124
|
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
end
|
|
132
|
-
end
|
|
133
|
-
else
|
|
134
|
-
# Do nothing if ripper is unavailable
|
|
135
|
-
def call(ast)
|
|
136
|
-
ast
|
|
125
|
+
def on_parse_error(*)
|
|
126
|
+
raise ParseError
|
|
137
127
|
end
|
|
138
128
|
end
|
|
139
129
|
end
|
data/lib/haml/version.rb
CHANGED