opulent 0.0.9 → 1.0.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/.gitignore +9 -0
- data/.libold/opulent.rb +96 -0
- data/.libold/opulent/context.rb +80 -0
- data/.libold/opulent/engine.rb +88 -0
- data/.libold/opulent/filter.rb +101 -0
- data/.libold/opulent/logger.rb +67 -0
- data/.libold/opulent/nodes.rb +13 -0
- data/.libold/opulent/nodes/block.rb +29 -0
- data/.libold/opulent/nodes/comment.rb +35 -0
- data/.libold/opulent/nodes/control.rb +230 -0
- data/.libold/opulent/nodes/define.rb +42 -0
- data/.libold/opulent/nodes/eval.rb +41 -0
- data/.libold/opulent/nodes/expression.rb +28 -0
- data/.libold/opulent/nodes/filter.rb +70 -0
- data/.libold/opulent/nodes/helper.rb +69 -0
- data/.libold/opulent/nodes/node.rb +101 -0
- data/.libold/opulent/nodes/root.rb +62 -0
- data/.libold/opulent/nodes/text.rb +54 -0
- data/.libold/opulent/nodes/theme.rb +36 -0
- data/.libold/opulent/parser.rb +252 -0
- data/.libold/opulent/parser/block.rb +70 -0
- data/.libold/opulent/parser/comment.rb +32 -0
- data/.libold/opulent/parser/control.rb +83 -0
- data/.libold/opulent/parser/define.rb +39 -0
- data/.libold/opulent/parser/eval.rb +33 -0
- data/.libold/opulent/parser/expression.rb +350 -0
- data/.libold/opulent/parser/filter.rb +41 -0
- data/.libold/opulent/parser/node.rb +232 -0
- data/.libold/opulent/parser/root.rb +96 -0
- data/.libold/opulent/parser/text.rb +114 -0
- data/.libold/opulent/parser/theme.rb +36 -0
- data/.libold/opulent/preprocessor.rb +102 -0
- data/.libold/opulent/runtime.rb +144 -0
- data/.libold/opulent/template.rb +43 -0
- data/.libold/opulent/tokens.rb +276 -0
- data/.libold/opulent/version.rb +5 -0
- data/.rspec +2 -0
- data/.travis.yml +4 -0
- data/Gemfile +2 -0
- data/LICENSE +21 -0
- data/README.md +102 -0
- data/Rakefile +6 -0
- data/benchmark/benchmark.rb +46 -0
- data/benchmark/cases/node/node.haml +17 -0
- data/benchmark/cases/node/node.op +14 -0
- data/benchmark/cases/node/node.slim +19 -0
- data/bin/console +14 -0
- data/bin/setup +7 -0
- data/docs/syntax.md +3 -0
- data/docs/usage.md +3 -0
- data/lib/opulent.rb +11 -64
- data/lib/opulent/compiler.rb +132 -0
- data/lib/opulent/compiler/block.rb +31 -0
- data/lib/opulent/compiler/comment.rb +22 -0
- data/lib/opulent/compiler/control.rb +152 -0
- data/lib/opulent/compiler/define.rb +88 -0
- data/lib/opulent/compiler/eval.rb +15 -0
- data/lib/opulent/compiler/filter.rb +54 -0
- data/lib/opulent/compiler/node.rb +232 -0
- data/lib/opulent/compiler/root.rb +19 -0
- data/lib/opulent/compiler/text.rb +60 -0
- data/lib/opulent/context.rb +88 -0
- data/lib/opulent/engine.rb +60 -0
- data/lib/opulent/filters.rb +222 -0
- data/lib/opulent/logger.rb +47 -0
- data/lib/opulent/parser.rb +196 -0
- data/lib/opulent/parser/block.rb +56 -0
- data/lib/opulent/parser/comment.rb +27 -0
- data/lib/opulent/parser/control.rb +112 -0
- data/lib/opulent/parser/define.rb +30 -0
- data/lib/opulent/parser/eval.rb +21 -0
- data/lib/opulent/parser/expression.rb +344 -0
- data/lib/opulent/parser/filter.rb +25 -0
- data/lib/opulent/parser/node.rb +246 -0
- data/lib/opulent/parser/root.rb +48 -0
- data/lib/opulent/parser/text.rb +127 -0
- data/lib/opulent/settings.rb +79 -0
- data/lib/opulent/template.rb +67 -0
- data/lib/opulent/tokens.rb +166 -0
- data/lib/opulent/version.rb +4 -0
- data/opulent.gemspec +36 -0
- metadata +160 -7
@@ -0,0 +1,21 @@
|
|
1
|
+
# @Opulent
|
2
|
+
module Opulent
|
3
|
+
# @Parser
|
4
|
+
class Parser
|
5
|
+
# Match one line or multiline, escaped or unescaped text
|
6
|
+
#
|
7
|
+
def evaluate(parent, indent)
|
8
|
+
# Accept eval or multiline eval syntax and return a new node,
|
9
|
+
if (evaluate = accept(:eval))
|
10
|
+
eval_node = [:evaluate, evaluate[1..-1].strip, {}, nil, indent]
|
11
|
+
elsif (evaluate = accept(:eval_multiline))
|
12
|
+
# Get all the lines which are more indented than the current one
|
13
|
+
eval_node = [:evaluate, evaluate[1..-1].strip, {}, nil, indent]
|
14
|
+
eval_node[@value] += accept(:newline) || ""
|
15
|
+
eval_node[@value] += get_indented_lines(indent)
|
16
|
+
end
|
17
|
+
|
18
|
+
parent[@children] << eval_node if eval_node
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,344 @@
|
|
1
|
+
# @Opulent
|
2
|
+
module Opulent
|
3
|
+
# @Parser
|
4
|
+
class Parser
|
5
|
+
# Check if the parser matches an expression node
|
6
|
+
#
|
7
|
+
def expression(allow_assignments = true, wrapped = true, whitespace = true)
|
8
|
+
buffer = ""
|
9
|
+
|
10
|
+
# Build a ruby expression out of accepted literals
|
11
|
+
while (term = (whitespace ? accept(:whitespace) : nil) ||
|
12
|
+
modifier ||
|
13
|
+
identifier ||
|
14
|
+
method_call ||
|
15
|
+
paranthesis ||
|
16
|
+
array ||
|
17
|
+
hash ||
|
18
|
+
symbol ||
|
19
|
+
percent ||
|
20
|
+
primary_term)
|
21
|
+
buffer += term
|
22
|
+
|
23
|
+
# Accept operations which have a right term and raise an error if
|
24
|
+
# we have an unfinished expression such as "a +", "b - 1 >" and other
|
25
|
+
# expressions following the same pattern
|
26
|
+
if wrapped && (op = operation || (allow_assignments ? accept_stripped(:exp_assignment) : nil))
|
27
|
+
buffer += op
|
28
|
+
if (right_term = expression(allow_assignments, wrapped)).nil?
|
29
|
+
error :expression
|
30
|
+
else
|
31
|
+
buffer += right_term[@value]
|
32
|
+
end
|
33
|
+
elsif (conditional = ternary_operator allow_assignments, wrapped)
|
34
|
+
buffer += conditional
|
35
|
+
end
|
36
|
+
|
37
|
+
# Do not continue if the expression has whitespace method calls in
|
38
|
+
# an unwrapped context because this will confuse the parser
|
39
|
+
unless buffer.strip.empty?
|
40
|
+
break unless wrapped || lookahead(:exp_identifier_lookahead).nil?
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
if buffer.strip.empty?
|
45
|
+
return undo buffer
|
46
|
+
else
|
47
|
+
return [:expression, buffer.strip, {evaluate: true}]
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
# Check if it's possible to parse a ruby array literal. First, try to see
|
52
|
+
# if the next sequence is a hash_open token: "[", and if it is, then a
|
53
|
+
# hash_close: "]" token is required next
|
54
|
+
#
|
55
|
+
# [array_elements]
|
56
|
+
#
|
57
|
+
def array
|
58
|
+
if (buffer = accept :square_bracket)
|
59
|
+
accept_newline
|
60
|
+
buffer += array_elements
|
61
|
+
accept_newline
|
62
|
+
buffer += accept :'[', :*
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
# Recursively gather expressions separated by a comma and add them to the
|
67
|
+
# expression buffer
|
68
|
+
#
|
69
|
+
# experssion1, experssion2, experssion3
|
70
|
+
#
|
71
|
+
# @param buffer [String] Accumulator for the array elements
|
72
|
+
#
|
73
|
+
def array_elements(buffer = '')
|
74
|
+
if (term = expression)
|
75
|
+
buffer += term[@value]
|
76
|
+
# If there is an array_terminator ",", recursively gather the next
|
77
|
+
# array element into the buffer
|
78
|
+
if (terminator = accept_stripped :comma) then
|
79
|
+
accept_newline
|
80
|
+
buffer += array_elements terminator
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
# Array ended prematurely with a trailing comma, therefore the current
|
85
|
+
# parsing process will stop
|
86
|
+
if buffer.strip[-1] == ','
|
87
|
+
error :array_elements_terminator
|
88
|
+
end
|
89
|
+
|
90
|
+
return buffer
|
91
|
+
end
|
92
|
+
|
93
|
+
# Check if it's possible to parse a ruby hash literal. First, try to see
|
94
|
+
# if the next sequence is a hash_open token: "{", and if it is, then a
|
95
|
+
# hash_close: "}" token is required next
|
96
|
+
#
|
97
|
+
# { hash_elements }
|
98
|
+
#
|
99
|
+
def hash
|
100
|
+
if (buffer = accept :curly_bracket)
|
101
|
+
accept_newline
|
102
|
+
buffer += hash_elements
|
103
|
+
accept_newline
|
104
|
+
buffer += accept :'{', :*
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
# Recursively gather expression attributions separated by a comma and add
|
109
|
+
# them to the expression buffer
|
110
|
+
#
|
111
|
+
# key1: experssion1, key2 => experssion2, :key3 => experssion3
|
112
|
+
#
|
113
|
+
# @param buffer [String] Accumulator for the hash elements
|
114
|
+
#
|
115
|
+
def hash_elements(buffer = '')
|
116
|
+
value = Proc.new do
|
117
|
+
# Get the value associated to the current hash key
|
118
|
+
if (exp = expression)
|
119
|
+
buffer += exp[@value]
|
120
|
+
else
|
121
|
+
error :hash_elements
|
122
|
+
end
|
123
|
+
|
124
|
+
# If there is an hash_terminator ",", recursively gather the next
|
125
|
+
# array element into the buffer
|
126
|
+
if (terminator = accept_stripped :comma) then
|
127
|
+
accept_newline
|
128
|
+
buffer += hash_elements terminator
|
129
|
+
end
|
130
|
+
end
|
131
|
+
|
132
|
+
# Accept both shorthand and default ruby hash style. Following DRY
|
133
|
+
# principles, a Proc is used to assign the value to the current key
|
134
|
+
#
|
135
|
+
# key:
|
136
|
+
# :key =>
|
137
|
+
if (symbol = accept_stripped :hash_symbol)
|
138
|
+
buffer += symbol
|
139
|
+
value[]
|
140
|
+
elsif (exp = expression false)
|
141
|
+
buffer += exp[@value]
|
142
|
+
if(assign = accept_stripped :hash_assignment)
|
143
|
+
buffer += assign
|
144
|
+
value[]
|
145
|
+
else
|
146
|
+
error :hash_assignment
|
147
|
+
end
|
148
|
+
end
|
149
|
+
|
150
|
+
# Array ended prematurely with a trailing comma, therefore the current
|
151
|
+
# parsing process will stop
|
152
|
+
if buffer.strip[-1] == ','
|
153
|
+
error :hash_elements_terminator
|
154
|
+
end
|
155
|
+
|
156
|
+
return buffer
|
157
|
+
end
|
158
|
+
|
159
|
+
# Accept a ruby identifier such as a class, module, method or variable
|
160
|
+
#
|
161
|
+
def identifier
|
162
|
+
if (buffer = accept :exp_identifier)
|
163
|
+
if (args = call)
|
164
|
+
buffer += args
|
165
|
+
end
|
166
|
+
return buffer
|
167
|
+
end
|
168
|
+
end
|
169
|
+
|
170
|
+
# Check if it's possible to parse a ruby paranthesis expression wrapper.
|
171
|
+
#
|
172
|
+
def paranthesis
|
173
|
+
if (buffer = accept :round_bracket)
|
174
|
+
buffer += expression[@value]
|
175
|
+
buffer += accept_stripped :'(', :*
|
176
|
+
end
|
177
|
+
end
|
178
|
+
|
179
|
+
# Check if it's possible to parse a ruby call literal. First, try to see
|
180
|
+
# if the next sequence is a hash_open token: "(", and if it is, then a
|
181
|
+
# hash_close: ")" token is required next
|
182
|
+
#
|
183
|
+
# ( call_elements )
|
184
|
+
#
|
185
|
+
def call
|
186
|
+
if (buffer = accept :round_bracket)
|
187
|
+
buffer += call_elements
|
188
|
+
buffer += accept_stripped :'(', :*
|
189
|
+
end
|
190
|
+
end
|
191
|
+
|
192
|
+
# Recursively gather expression attributes separated by a comma and add
|
193
|
+
# them to the expression buffer
|
194
|
+
#
|
195
|
+
# expression1, a: expression2, expression3
|
196
|
+
#
|
197
|
+
# @param buffer [String] Accumulator for the call elements
|
198
|
+
#
|
199
|
+
def call_elements(buffer = '')
|
200
|
+
# Accept both shorthand and default ruby hash style. Following DRY
|
201
|
+
# principles, a Proc is used to assign the value to the current key
|
202
|
+
#
|
203
|
+
# key: value
|
204
|
+
# :key => value
|
205
|
+
# value
|
206
|
+
if (symbol = accept_stripped :hash_symbol)
|
207
|
+
buffer += symbol
|
208
|
+
|
209
|
+
# Get the value associated to the current hash key
|
210
|
+
if (exp = expression(true))
|
211
|
+
buffer += exp[@value]
|
212
|
+
else
|
213
|
+
error :call_elements
|
214
|
+
end
|
215
|
+
|
216
|
+
# If there is an comma ",", recursively gather the next
|
217
|
+
# array element into the buffer
|
218
|
+
if (terminator = accept_stripped :comma) then
|
219
|
+
buffer += call_elements terminator
|
220
|
+
end
|
221
|
+
elsif (exp = expression(true))
|
222
|
+
buffer += exp[@value]
|
223
|
+
|
224
|
+
if(assign = accept_stripped :hash_assignment)
|
225
|
+
buffer += assign
|
226
|
+
|
227
|
+
# Get the value associated to the current hash key
|
228
|
+
if (exp = expression(true))
|
229
|
+
buffer += exp[@value]
|
230
|
+
else
|
231
|
+
error :call_elements
|
232
|
+
end
|
233
|
+
end
|
234
|
+
|
235
|
+
# If there is an comma ",", recursively gather the next
|
236
|
+
# array element into the buffer
|
237
|
+
if (terminator = accept_stripped :comma) then
|
238
|
+
buffer += call_elements terminator
|
239
|
+
end
|
240
|
+
end
|
241
|
+
|
242
|
+
buffer
|
243
|
+
end
|
244
|
+
|
245
|
+
# Accept a ruby symbol defined through a colon and a trailing expression
|
246
|
+
#
|
247
|
+
# :'symbol'
|
248
|
+
# :symbol
|
249
|
+
#
|
250
|
+
def symbol
|
251
|
+
if (colon = accept :colon)
|
252
|
+
return undo colon if lookahead(:whitespace)
|
253
|
+
|
254
|
+
if (exp = expression).nil?
|
255
|
+
error :symbol
|
256
|
+
else
|
257
|
+
colon + exp[@value]
|
258
|
+
end
|
259
|
+
end
|
260
|
+
end
|
261
|
+
|
262
|
+
# Accept a ruby module, method or context modifier
|
263
|
+
#
|
264
|
+
# Module::
|
265
|
+
# @, @@, $
|
266
|
+
#
|
267
|
+
def modifier
|
268
|
+
accept(:exp_context) || accept(:exp_module)
|
269
|
+
end
|
270
|
+
|
271
|
+
|
272
|
+
# Accept a ruby percentage operator for arrays of strings, symbols and
|
273
|
+
# simple escaped strings
|
274
|
+
#
|
275
|
+
# %w(word1 word2 word3)
|
276
|
+
#
|
277
|
+
def percent
|
278
|
+
if (buffer = accept_stripped :exp_percent)
|
279
|
+
match_start = buffer[-1]
|
280
|
+
match_name = :"percent#{match_start}"
|
281
|
+
|
282
|
+
unless Tokens[match_name]
|
283
|
+
match_end = Tokens.bracket(match_start) || match_start
|
284
|
+
|
285
|
+
match_inner = "\\#{match_start}"
|
286
|
+
match_inner += "\\#{match_end}" unless match_end == match_start
|
287
|
+
|
288
|
+
pattern = /(((?:[^#{match_inner}\\]|\\.)*?)#{'\\' + match_end})/
|
289
|
+
|
290
|
+
Tokens[match_name] = pattern
|
291
|
+
end
|
292
|
+
|
293
|
+
buffer += accept match_name
|
294
|
+
end
|
295
|
+
end
|
296
|
+
|
297
|
+
# Accept any primary term and return it without the leading whitespace to
|
298
|
+
# the expression buffer
|
299
|
+
#
|
300
|
+
# "string"
|
301
|
+
# 123
|
302
|
+
# 123.456
|
303
|
+
# nil
|
304
|
+
# true
|
305
|
+
# false
|
306
|
+
# /.*/
|
307
|
+
#
|
308
|
+
def primary_term
|
309
|
+
accept_stripped(:exp_string) ||
|
310
|
+
accept_stripped(:exp_fixnum) ||
|
311
|
+
accept_stripped(:exp_double) ||
|
312
|
+
accept_stripped(:exp_nil) ||
|
313
|
+
accept_stripped(:exp_regex) ||
|
314
|
+
accept_stripped(:exp_boolean)
|
315
|
+
end
|
316
|
+
|
317
|
+
# Accept an operation between two or more expressions
|
318
|
+
#
|
319
|
+
def operation
|
320
|
+
accept(:exp_operation)
|
321
|
+
end
|
322
|
+
|
323
|
+
# Accept a ruby method call modifier
|
324
|
+
#
|
325
|
+
def method_call
|
326
|
+
accept(:exp_method_call)
|
327
|
+
end
|
328
|
+
|
329
|
+
# Accept ternary operator syntax
|
330
|
+
#
|
331
|
+
# condition ? expression1 : expression2
|
332
|
+
#
|
333
|
+
def ternary_operator(allow_assignments, wrapped)
|
334
|
+
if (buffer = accept :exp_ternary)
|
335
|
+
buffer += expression(allow_assignments, wrapped)[@value]
|
336
|
+
if (else_branch = accept :exp_ternary_else)
|
337
|
+
buffer += else_branch
|
338
|
+
buffer += expression(allow_assignments, wrapped)[@value]
|
339
|
+
end
|
340
|
+
return buffer
|
341
|
+
end
|
342
|
+
end
|
343
|
+
end
|
344
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
# @Opulent
|
2
|
+
module Opulent
|
3
|
+
# @Parser
|
4
|
+
class Parser
|
5
|
+
# Check if we match an compile time filter
|
6
|
+
#
|
7
|
+
# :filter
|
8
|
+
#
|
9
|
+
# @param parent [Node] Parent node to which we append the element
|
10
|
+
#
|
11
|
+
def filter(parent, indent)
|
12
|
+
if (filter_name = accept :filter)
|
13
|
+
# Get element attributes
|
14
|
+
atts = attributes(shorthand_attributes) || {}
|
15
|
+
|
16
|
+
# Accept inline text or multiline text feed as first child
|
17
|
+
error :fiter unless accept(:line_feed).strip.empty?
|
18
|
+
|
19
|
+
# Get everything under the filter and set it as the node value
|
20
|
+
# and create a new node and set its extension
|
21
|
+
parent[@children] << [:filter, filter_name[1..-1].to_sym, atts, get_indented_lines(indent), indent]
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,246 @@
|
|
1
|
+
# @Opulent
|
2
|
+
module Opulent
|
3
|
+
# @Parser
|
4
|
+
class Parser
|
5
|
+
# Check if we match an node node with its attributes and possibly
|
6
|
+
# inline text
|
7
|
+
#
|
8
|
+
# node [ attributes ] Inline text
|
9
|
+
#
|
10
|
+
# @param parent [Node] Parent node to which we append the node
|
11
|
+
#
|
12
|
+
def node(parent, indent = nil)
|
13
|
+
if (name = lookahead(:node_lookahead) || lookahead(:shorthand_lookahead))
|
14
|
+
return nil if Keywords.include? name[0].to_sym
|
15
|
+
|
16
|
+
# Accept either explicit node_name or implicit :div node_name
|
17
|
+
# with shorthand attributes
|
18
|
+
if (node_name = accept :node)
|
19
|
+
node_name = node_name.to_sym
|
20
|
+
shorthand = shorthand_attributes
|
21
|
+
elsif (shorthand = shorthand_attributes)
|
22
|
+
node_name = :div
|
23
|
+
end
|
24
|
+
|
25
|
+
# Node creation options
|
26
|
+
options = {}
|
27
|
+
|
28
|
+
# Get leading and trailing whitespace
|
29
|
+
if accept_stripped :leading_whitespace
|
30
|
+
options[:leading_whitespace] = true
|
31
|
+
if accept :leading_trailing_whitespace
|
32
|
+
options[:trailing_whitespace] = true
|
33
|
+
end
|
34
|
+
elsif accept_stripped :trailing_whitespace
|
35
|
+
options[:trailing_whitespace] = true
|
36
|
+
end
|
37
|
+
|
38
|
+
# Get wrapped node attributes
|
39
|
+
atts = attributes(shorthand) || {}
|
40
|
+
|
41
|
+
# Inherit attributes from definition
|
42
|
+
options[:extension] = extension = extend_attributes
|
43
|
+
|
44
|
+
# Get unwrapped node attributes
|
45
|
+
options[:attributes] = attributes_assignments atts, false
|
46
|
+
|
47
|
+
# Create node
|
48
|
+
current_node = [:node, node_name, options, [], indent]
|
49
|
+
|
50
|
+
# Check if the node is explicitly self enclosing
|
51
|
+
if(close = accept_stripped :self_enclosing) || Settings::SelfEnclosing.include?(node_name)
|
52
|
+
current_node[@options][:self_enclosing] = true
|
53
|
+
|
54
|
+
unless close.nil? || close.strip.empty?
|
55
|
+
undo close; error :self_enclosing
|
56
|
+
end
|
57
|
+
|
58
|
+
# For self enclosing tag error reporting purposes
|
59
|
+
line = @i
|
60
|
+
end
|
61
|
+
|
62
|
+
# Check whether we have explicit inline elements and add them
|
63
|
+
# with increased base indentation
|
64
|
+
if (accept :inline_child)
|
65
|
+
# Inline node element
|
66
|
+
unless (child_node = node current_node, indent)
|
67
|
+
error :inline_child
|
68
|
+
end
|
69
|
+
else
|
70
|
+
# Inline text element
|
71
|
+
text_node = text current_node, indent, false
|
72
|
+
end
|
73
|
+
|
74
|
+
# Add the current node to the root
|
75
|
+
root(current_node, indent)
|
76
|
+
|
77
|
+
if current_node[@options][:self_enclosing] && current_node[@children].any?
|
78
|
+
error :self_enclosing_children, line
|
79
|
+
end
|
80
|
+
|
81
|
+
if @definitions.keys.include? node_name
|
82
|
+
model = @definitions[node_name].clone
|
83
|
+
model[@options][:call] = current_node
|
84
|
+
|
85
|
+
parent[@children] << model
|
86
|
+
else
|
87
|
+
parent[@children] << current_node
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
# Helper method to create an array of values when an attribute is set
|
93
|
+
# multiple times. This happens unless the key is id, which is unique
|
94
|
+
#
|
95
|
+
# @param atts [Hash] Current node attributes hash
|
96
|
+
# @param key [Symbol] Attribute name
|
97
|
+
# @param value [String] Attribute value
|
98
|
+
#
|
99
|
+
def add_attribute(atts, key, value)
|
100
|
+
# Check whether the attribute value needs to be evaluated or not
|
101
|
+
value[@options][:evaluate] = if value[@value] =~ Settings::EvaluationCheck
|
102
|
+
value[@value] =~ Settings::InterpolationCheck ? true : false
|
103
|
+
else
|
104
|
+
true
|
105
|
+
end
|
106
|
+
|
107
|
+
# Check for unique key and arrays of attributes
|
108
|
+
if key == :class
|
109
|
+
# If the key is already associated to an array, add the value to the
|
110
|
+
# array, otherwise, create a new array or set it
|
111
|
+
if atts[key]
|
112
|
+
atts[key] << value
|
113
|
+
else
|
114
|
+
atts[key] = [value]
|
115
|
+
end
|
116
|
+
else
|
117
|
+
atts[key] = value
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
# Accept node shorthand attributes. Each shorthand attribute is directly
|
122
|
+
# mapped to an attribute key
|
123
|
+
#
|
124
|
+
# @param atts [Hash] Node attributes
|
125
|
+
#
|
126
|
+
def shorthand_attributes(atts = {})
|
127
|
+
while (key = accept :shorthand)
|
128
|
+
key = Settings::Shorthand[key.to_sym]
|
129
|
+
|
130
|
+
# Check whether the value is escaped or unescaped
|
131
|
+
escaped = accept(:unescaped_value) ? false : true
|
132
|
+
|
133
|
+
# Get the attribute value and process it
|
134
|
+
if (value = accept(:node))
|
135
|
+
value = [:expression, value.inspect, {escaped: escaped}]
|
136
|
+
elsif (value = accept(:exp_string))
|
137
|
+
value = [:expression, value, {escaped: escaped}]
|
138
|
+
elsif (value = paranthesis)
|
139
|
+
value = [:expression, value, {escaped: escaped}]
|
140
|
+
else
|
141
|
+
error :shorthand
|
142
|
+
end
|
143
|
+
|
144
|
+
# IDs are unique, the rest of the attributes turn into arrays in
|
145
|
+
# order to allow multiple values or identifiers
|
146
|
+
add_attribute(atts, key, value)
|
147
|
+
end
|
148
|
+
|
149
|
+
return atts
|
150
|
+
end
|
151
|
+
|
152
|
+
def attributes(atts = {})
|
153
|
+
wrapped_attributes atts
|
154
|
+
attributes_assignments atts, false
|
155
|
+
|
156
|
+
return atts
|
157
|
+
end
|
158
|
+
|
159
|
+
|
160
|
+
# Check if we match node attributes
|
161
|
+
#
|
162
|
+
# [ assignments ]
|
163
|
+
#
|
164
|
+
# @param as_parameters [Boolean] Accept or reject identifier nodes
|
165
|
+
#
|
166
|
+
def wrapped_attributes(list)
|
167
|
+
if (bracket = accept :brackets)
|
168
|
+
attributes_assignments list
|
169
|
+
accept bracket.to_sym, :*
|
170
|
+
end
|
171
|
+
|
172
|
+
return list
|
173
|
+
end
|
174
|
+
|
175
|
+
# Check if we match an expression node or
|
176
|
+
# a node node
|
177
|
+
#
|
178
|
+
# [ assignments ]
|
179
|
+
#
|
180
|
+
# @param parent [Hash] Parent to which we append nodes
|
181
|
+
# @param as_parameters [Boolean] Accept or reject identifier nodes
|
182
|
+
#
|
183
|
+
def attributes_assignments(parent, wrapped = true)
|
184
|
+
unless wrapped
|
185
|
+
return parent if lookahead(:assignment_lookahead).nil?
|
186
|
+
end
|
187
|
+
|
188
|
+
if (argument = accept_stripped :node)
|
189
|
+
argument = argument.to_sym
|
190
|
+
|
191
|
+
if accept :assignment
|
192
|
+
# Check if we have an attribute escape or not
|
193
|
+
escaped = if accept :assignment_unescaped
|
194
|
+
false
|
195
|
+
else
|
196
|
+
true
|
197
|
+
end
|
198
|
+
|
199
|
+
# Get the argument value if we have an assignment
|
200
|
+
if (value = expression(false, wrapped))
|
201
|
+
value[@options][:escaped] = escaped
|
202
|
+
|
203
|
+
# IDs are unique, the rest of the attributes turn into arrays in
|
204
|
+
# order to allow multiple values or identifiers
|
205
|
+
add_attribute(parent, argument, value)
|
206
|
+
else
|
207
|
+
error :assignments_colon
|
208
|
+
end
|
209
|
+
else
|
210
|
+
parent[argument] = [:expression, "nil", {evaluate: true, escaped: false}] unless parent[argument]
|
211
|
+
end
|
212
|
+
|
213
|
+
# If our attributes are wrapped, we allow method calls without
|
214
|
+
# paranthesis, ruby style, therefore we need a terminator to signify
|
215
|
+
# the expression end. If they are not wrapped (inline), we require
|
216
|
+
# paranthesis and allow inline calls
|
217
|
+
if wrapped && accept_stripped(:assignment_terminator)
|
218
|
+
attributes_assignments parent, wrapped
|
219
|
+
elsif !wrapped && lookahead(:assignment_lookahead)
|
220
|
+
attributes_assignments parent, wrapped
|
221
|
+
end
|
222
|
+
|
223
|
+
return parent
|
224
|
+
elsif !parent.empty?
|
225
|
+
error :assignments_comma
|
226
|
+
end
|
227
|
+
end
|
228
|
+
|
229
|
+
# Extend node attributes with hash from
|
230
|
+
#
|
231
|
+
# +value
|
232
|
+
# +{hash: "value"}
|
233
|
+
# +(paranthesis)
|
234
|
+
#
|
235
|
+
def extend_attributes
|
236
|
+
if (accept :extend_attributes)
|
237
|
+
unescaped = accept :unescaped_value
|
238
|
+
|
239
|
+
extension = expression(false, false, false)
|
240
|
+
extension[@options][:escaped] = false if unescaped
|
241
|
+
|
242
|
+
return extension
|
243
|
+
end
|
244
|
+
end
|
245
|
+
end
|
246
|
+
end
|