opulent 1.5.5 → 1.6.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CODE_OF_CONDUCT.md +13 -0
- data/{LICENSE → LICENSE.md} +0 -0
- data/bin/opulent +0 -0
- data/lib/opulent.rb +10 -10
- data/lib/opulent/compiler.rb +15 -9
- data/lib/opulent/compiler/buffer.rb +123 -62
- data/lib/opulent/compiler/comment.rb +3 -4
- data/lib/opulent/compiler/control.rb +20 -26
- data/lib/opulent/compiler/define.rb +88 -36
- data/lib/opulent/compiler/doctype.rb +20 -22
- data/lib/opulent/compiler/eval.rb +23 -2
- data/lib/opulent/compiler/filter.rb +4 -5
- data/lib/opulent/compiler/node.rb +18 -12
- data/lib/opulent/compiler/root.rb +4 -5
- data/lib/opulent/compiler/text.rb +7 -2
- data/lib/opulent/compiler/yield.rb +2 -3
- data/lib/opulent/engine.rb +21 -20
- data/lib/opulent/exec.rb +21 -63
- data/lib/opulent/logger.rb +230 -3
- data/lib/opulent/parser.rb +14 -76
- data/lib/opulent/parser/comment.rb +45 -34
- data/lib/opulent/parser/control.rb +132 -111
- data/lib/opulent/parser/define.rb +15 -12
- data/lib/opulent/parser/doctype.rb +15 -15
- data/lib/opulent/parser/eval.rb +16 -6
- data/lib/opulent/parser/expression.rb +87 -84
- data/lib/opulent/parser/filter.rb +31 -25
- data/lib/opulent/parser/include.rb +38 -42
- data/lib/opulent/parser/node.rb +136 -118
- data/lib/opulent/parser/root.rb +24 -18
- data/lib/opulent/parser/text.rb +150 -123
- data/lib/opulent/parser/yield.rb +23 -23
- data/lib/opulent/settings.rb +70 -51
- data/lib/opulent/tokens.rb +17 -15
- data/lib/opulent/utils.rb +5 -4
- data/lib/opulent/version.rb +1 -1
- metadata +4 -43
- data/.libold/opulent.rb +0 -96
- data/.libold/opulent/context.rb +0 -80
- data/.libold/opulent/engine.rb +0 -88
- data/.libold/opulent/filter.rb +0 -101
- data/.libold/opulent/logger.rb +0 -67
- data/.libold/opulent/nodes.rb +0 -13
- data/.libold/opulent/nodes/block.rb +0 -29
- data/.libold/opulent/nodes/comment.rb +0 -35
- data/.libold/opulent/nodes/control.rb +0 -230
- data/.libold/opulent/nodes/define.rb +0 -42
- data/.libold/opulent/nodes/eval.rb +0 -41
- data/.libold/opulent/nodes/expression.rb +0 -28
- data/.libold/opulent/nodes/filter.rb +0 -70
- data/.libold/opulent/nodes/helper.rb +0 -69
- data/.libold/opulent/nodes/node.rb +0 -101
- data/.libold/opulent/nodes/root.rb +0 -62
- data/.libold/opulent/nodes/text.rb +0 -54
- data/.libold/opulent/nodes/theme.rb +0 -36
- data/.libold/opulent/parser.rb +0 -252
- data/.libold/opulent/parser/block.rb +0 -70
- data/.libold/opulent/parser/comment.rb +0 -32
- data/.libold/opulent/parser/control.rb +0 -83
- data/.libold/opulent/parser/define.rb +0 -39
- data/.libold/opulent/parser/eval.rb +0 -33
- data/.libold/opulent/parser/expression.rb +0 -350
- data/.libold/opulent/parser/filter.rb +0 -41
- data/.libold/opulent/parser/node.rb +0 -232
- data/.libold/opulent/parser/root.rb +0 -96
- data/.libold/opulent/parser/text.rb +0 -114
- data/.libold/opulent/parser/theme.rb +0 -36
- data/.libold/opulent/preprocessor.rb +0 -102
- data/.libold/opulent/runtime.rb +0 -144
- data/.libold/opulent/template.rb +0 -43
- data/.libold/opulent/tokens.rb +0 -276
- data/.libold/opulent/version.rb +0 -5
- data/.travis.yml +0 -4
- data/benchmark/benchmark.rb +0 -57
- data/benchmark/cases/node/node.haml +0 -7
- data/benchmark/cases/node/node.op +0 -7
- data/benchmark/cases/node/node.slim +0 -7
data/lib/opulent/parser.rb
CHANGED
@@ -22,7 +22,7 @@ module Opulent
|
|
22
22
|
#
|
23
23
|
# [:node_type, :value, :attributes, :children, :indent]
|
24
24
|
#
|
25
|
-
def initialize(
|
25
|
+
def initialize(settings = {})
|
26
26
|
# Convention accessors
|
27
27
|
@type = 0
|
28
28
|
@value = 1
|
@@ -30,17 +30,20 @@ module Opulent
|
|
30
30
|
@children = 3
|
31
31
|
@indent = 4
|
32
32
|
|
33
|
+
# Inherit settings from Engine
|
34
|
+
@settings = settings
|
35
|
+
|
33
36
|
# Set current compiled file as the first in the file stack together with
|
34
37
|
# its base indentation. The stack is used to allow include directives to
|
35
38
|
# be used with the last parent path found
|
36
|
-
@file = [[file, -1]]
|
39
|
+
@file = [[@settings.delete(:file), -1]]
|
37
40
|
|
38
41
|
# Create a definition stack to disallow recursive calls. When inside a
|
39
42
|
# definition and a named node is called, we render it as a plain node
|
40
43
|
@definition_stack = []
|
41
44
|
|
42
45
|
# Initialize definitions for the parser
|
43
|
-
@definitions =
|
46
|
+
@definitions = @settings.delete(:def) || {}
|
44
47
|
end
|
45
48
|
|
46
49
|
# Initialize the parsing process by splitting the code into lines and
|
@@ -57,6 +60,9 @@ module Opulent
|
|
57
60
|
# Current line index
|
58
61
|
@i = -1
|
59
62
|
|
63
|
+
# Current character index
|
64
|
+
@j = 0
|
65
|
+
|
60
66
|
# Initialize root node
|
61
67
|
@root = [:root, nil, {}, [], -1]
|
62
68
|
|
@@ -64,7 +70,7 @@ module Opulent
|
|
64
70
|
# nodes and definitions
|
65
71
|
root @root
|
66
72
|
|
67
|
-
|
73
|
+
[@root, @definitions]
|
68
74
|
end
|
69
75
|
|
70
76
|
# Check and accept or reject a given token as long as we have tokens
|
@@ -88,10 +94,11 @@ module Opulent
|
|
88
94
|
if (match = @line[@offset..-1].match(Tokens[token]))
|
89
95
|
# Advance current offset with match length
|
90
96
|
@offset += match[0].size
|
97
|
+
@j += match[0].size
|
91
98
|
|
92
99
|
return match[0]
|
93
100
|
elsif required
|
94
|
-
error :expected, token
|
101
|
+
Logger.error :parse, @code, @i, @j, :expected, token
|
95
102
|
end
|
96
103
|
end
|
97
104
|
|
@@ -144,6 +151,7 @@ module Opulent
|
|
144
151
|
def accept_newline
|
145
152
|
return unless @line[@offset..-1].strip.empty?
|
146
153
|
@line = @code[(@i += 1)]
|
154
|
+
@j = 0
|
147
155
|
@offset = 0
|
148
156
|
end
|
149
157
|
|
@@ -154,77 +162,7 @@ module Opulent
|
|
154
162
|
#
|
155
163
|
def indent_lines(text, indent)
|
156
164
|
text ||= ''
|
157
|
-
text.lines.
|
158
|
-
result += indent + line
|
159
|
-
end
|
160
|
-
end
|
161
|
-
|
162
|
-
# Give an explicit error report where an unexpected sequence of tokens
|
163
|
-
# appears and give indications on how to solve it
|
164
|
-
#
|
165
|
-
# @param message [Symbol] Error message to display to the user
|
166
|
-
#
|
167
|
-
def error(error, *data)
|
168
|
-
message = case error
|
169
|
-
when :unknown_node_type
|
170
|
-
"An unknown node type has been encountered at:\n\n#{Logger.red @line}"
|
171
|
-
when :expected
|
172
|
-
if [:'(', :'{', :'[', :'<'].include? data[0]
|
173
|
-
data[0] = "#{Tokens.bracket data[0]}"
|
174
|
-
end
|
175
|
-
"Expected to find a :#{data[0]} token on line #{@i+1} of input at: " \
|
176
|
-
"\n\n#{@line[0..@offset-1]}#{Logger.red @line[@offset..-1].rstrip}"
|
177
|
-
when :root
|
178
|
-
"Unknown node type encountered on line #{@i+1} of input at:\n\n" +
|
179
|
-
"#{@line[0..@offset-1]}#{Logger.red @line[@offset..-1].rstrip}"
|
180
|
-
when :assignments_colon
|
181
|
-
"Unexpected end of element attributes reached on line #{@i+1} of input.\n\n" +
|
182
|
-
"Expected to find an attribute at:\n\n" +
|
183
|
-
"#{@line[0..@offset-1]}#{Logger.red @line[@offset..-1].rstrip}"
|
184
|
-
when :assignments_comma
|
185
|
-
"Unexpected end of element attributes reached on line #{@i+1} of input.\n\n" +
|
186
|
-
"Expected to find an attribute value at:\n\n" +
|
187
|
-
"#{@line[0..@offset-1]}#{Logger.red @line[@offset..-1].rstrip}"
|
188
|
-
when :expression
|
189
|
-
"Unexpected end of expression reached on line #{@i+1} of input.\n\n" +
|
190
|
-
"Expected to find another expression term at:\n\n" +
|
191
|
-
"#{@line[0..@offset-1]}#{Logger.red @line[@offset..-1].rstrip}"
|
192
|
-
when :control_child
|
193
|
-
"Unexpected control structure child found on line #{@i+1} of input.\n\n" +
|
194
|
-
"Expected to find a parent #{data[0]} structure at:\n\n" +
|
195
|
-
"#{@line[0..@offset-1]}#{Logger.red @line[@offset..-1].rstrip}"
|
196
|
-
when :case_children
|
197
|
-
"Unexpected control structure child found on line #{@i+1} of input.\n\n" +
|
198
|
-
"Case structure cannot have any child elements at:\n\n" +
|
199
|
-
"#{@code[@i-1][0..@offset-1]}#{Logger.red @code[@i][@offset..-1].rstrip}"
|
200
|
-
when :whitespace_expression
|
201
|
-
"Unexpected end of expression reached on line #{@i+1} of input.\n\n" +
|
202
|
-
"Please use paranthesis for method parameters at:\n\n" +
|
203
|
-
"#{@line[0..@offset-1]}#{Logger.red @line[@offset..-1].rstrip}"
|
204
|
-
when :definition
|
205
|
-
"Unexpected start of definition on line #{@i+1} of input.\n\n" +
|
206
|
-
"Found a definition inside another definition or element at:\n\n" +
|
207
|
-
"#{@line[0..@offset-1]}#{Logger.red @line[@offset..-1].rstrip}"
|
208
|
-
when :self_enclosing
|
209
|
-
"Unexpected content found after self enclosing node on line #{@i+1} of input at:\n\n" +
|
210
|
-
"#{@line[0..@offset-1]}#{Logger.red @line[@offset..-1].rstrip}"
|
211
|
-
when :self_enclosing_children
|
212
|
-
"Unexpected child elements found for self enclosing node on line #{data[0]+1} of input at:\n\n" +
|
213
|
-
"#{@code[data[0]]}#{Logger.red @code[data[0] + 1]}"
|
214
|
-
when :include
|
215
|
-
"The included file #{data[0]} does not exist or an incorrect path has been specified."
|
216
|
-
when :include_dir
|
217
|
-
"The included file path #{data[0]} is a directory."
|
218
|
-
when :include_end
|
219
|
-
"Missing argument for include on line #{@i+1} of input at:\n\n" +
|
220
|
-
"#{@line[0..@offset-1]}#{Logger.red @line[@offset..-1].rstrip}"
|
221
|
-
else
|
222
|
-
"#{@line[0..@offset-1]}#{Logger.red @line[@offset..-1].rstrip}"
|
223
|
-
end
|
224
|
-
|
225
|
-
# Reconstruct lines to display where errors occur
|
226
|
-
fail "\n\nOpulent " + Logger.red('[Parser Error]') +
|
227
|
-
"\n---\n#{message}\n\n\n"
|
165
|
+
text.lines.map { |line| indent + line }.join
|
228
166
|
end
|
229
167
|
end
|
230
168
|
end
|
@@ -1,34 +1,45 @@
|
|
1
|
-
# @Opulent
|
2
|
-
module Opulent
|
3
|
-
# @Parser
|
4
|
-
class Parser
|
5
|
-
# Match one line or multiline comments
|
6
|
-
#
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
1
|
+
# @Opulent
|
2
|
+
module Opulent
|
3
|
+
# @Parser
|
4
|
+
class Parser
|
5
|
+
# Match one line or multiline comments
|
6
|
+
#
|
7
|
+
# @param parent [Node] Parent node of comment
|
8
|
+
# @param indent [Fixnum] Number of indentation characters
|
9
|
+
#
|
10
|
+
def comment(parent, indent)
|
11
|
+
return unless accept :comment
|
12
|
+
|
13
|
+
# Get first comment line
|
14
|
+
buffer = accept(:line_feed)
|
15
|
+
buffer += accept(:newline) || ''
|
16
|
+
|
17
|
+
# Get indented comment lines
|
18
|
+
buffer += get_indented_lines indent
|
19
|
+
|
20
|
+
# If we have a comment which is visible in the output, we will
|
21
|
+
# create a new comment element. Otherwise, we ignore the current
|
22
|
+
# gathered text and we simply begin the root parsing again
|
23
|
+
if buffer[0] == '!'
|
24
|
+
offset = 1
|
25
|
+
options = {}
|
26
|
+
|
27
|
+
# Allow leading comment newline
|
28
|
+
if buffer[1] == '^'
|
29
|
+
offset = 2
|
30
|
+
options[:newline] = true
|
31
|
+
end
|
32
|
+
|
33
|
+
parent[@children] << [
|
34
|
+
:comment,
|
35
|
+
buffer[offset..-1].strip,
|
36
|
+
options,
|
37
|
+
nil,
|
38
|
+
indent
|
39
|
+
]
|
40
|
+
end
|
41
|
+
|
42
|
+
parent
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
@@ -1,111 +1,132 @@
|
|
1
|
-
# @Opulent
|
2
|
-
module Opulent
|
3
|
-
# @Parser
|
4
|
-
class Parser
|
5
|
-
# Match an if-else control structure
|
6
|
-
#
|
7
|
-
def control(parent, indent)
|
8
|
-
# Accept eval or multiline eval syntax and return a new node,
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
#
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
end
|
1
|
+
# @Opulent
|
2
|
+
module Opulent
|
3
|
+
# @Parser
|
4
|
+
class Parser
|
5
|
+
# Match an if-else control structure
|
6
|
+
#
|
7
|
+
def control(parent, indent)
|
8
|
+
# Accept eval or multiline eval syntax and return a new node,
|
9
|
+
return unless (structure = accept(:control))
|
10
|
+
structure = structure.to_sym
|
11
|
+
|
12
|
+
# Handle each and the other control structures
|
13
|
+
condition = accept(:line_feed).strip
|
14
|
+
|
15
|
+
# Process each control structure condition
|
16
|
+
if structure == :each
|
17
|
+
# Check if arguments provided correctly
|
18
|
+
unless condition.match Tokens[:each_pattern]
|
19
|
+
Logger.error :parse, @code, @i, @j, :each_arguments
|
20
|
+
end
|
21
|
+
|
22
|
+
# Conditions for each iterator
|
23
|
+
condition = [
|
24
|
+
Regexp.last_match[1].split(' '),
|
25
|
+
Regexp.last_match[2].split(/,(.+)$/).map(&:strip).map(&:to_sym)
|
26
|
+
]
|
27
|
+
|
28
|
+
# Array loop as default
|
29
|
+
condition[0].unshift '{}' if condition[0].length == 1
|
30
|
+
end
|
31
|
+
|
32
|
+
# Else and default structures are not allowed to have any condition
|
33
|
+
# set and the other control structures require a condition
|
34
|
+
if structure == :else
|
35
|
+
unless condition.empty?
|
36
|
+
Logger.error :parse, @code, @i, @j, :condition_exists
|
37
|
+
end
|
38
|
+
else
|
39
|
+
if condition.empty?
|
40
|
+
Logger.error :parse, @code, @i, @j, :condition_missing
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
# Add the condition and create a new child to the control parent.
|
45
|
+
# The control parent keeps condition -> children matches for our
|
46
|
+
# document's content
|
47
|
+
# add_options = proc do |control_parent|
|
48
|
+
# control_parent.value << condition
|
49
|
+
# control_parent.children << []
|
50
|
+
#
|
51
|
+
# root control_parent
|
52
|
+
# end
|
53
|
+
|
54
|
+
# If the current control structure is a parent which allows multiple
|
55
|
+
# branches, such as an if or case, we create an array of conditions
|
56
|
+
# which can be matched and an array of children belonging to each
|
57
|
+
# conditional branch
|
58
|
+
if [:if, :unless].include? structure
|
59
|
+
# Create the control structure and get its child nodes
|
60
|
+
control_structure = [structure, [condition], {}, [], indent]
|
61
|
+
root control_structure, indent
|
62
|
+
|
63
|
+
# Turn children into an array because we allow multiple branches
|
64
|
+
control_structure[@children] = [control_structure[@children]]
|
65
|
+
|
66
|
+
# Add it to the parent node
|
67
|
+
parent[@children] << control_structure
|
68
|
+
|
69
|
+
elsif structure == :case
|
70
|
+
# Create the control structure and get its child nodes
|
71
|
+
control_structure = [
|
72
|
+
structure,
|
73
|
+
[],
|
74
|
+
{ condition: condition },
|
75
|
+
[],
|
76
|
+
indent
|
77
|
+
]
|
78
|
+
|
79
|
+
# Add it to the parent node
|
80
|
+
parent[@children] << control_structure
|
81
|
+
|
82
|
+
# If the control structure is a child structure, we need to find the
|
83
|
+
# node it belongs to amont the current parent. Search from end to
|
84
|
+
# beginning until we find the node parent
|
85
|
+
elsif control_child structure
|
86
|
+
# During the search, we try to find the matching parent type
|
87
|
+
unless control_parent(structure).include? parent[@children][-1][@type]
|
88
|
+
Logger.error :parse,
|
89
|
+
@code,
|
90
|
+
@i,
|
91
|
+
@j,
|
92
|
+
:control_child,
|
93
|
+
control_parent(structure)
|
94
|
+
end
|
95
|
+
|
96
|
+
# Gather child elements for current structure
|
97
|
+
control_structure = [structure, [condition], {}, [], indent]
|
98
|
+
root control_structure, indent
|
99
|
+
|
100
|
+
# Add the new condition and children to our parent structure
|
101
|
+
parent[@children][-1][@value] << condition
|
102
|
+
parent[@children][-1][@children] << control_structure[@children]
|
103
|
+
|
104
|
+
# When our control structure isn't a complex composite, we create
|
105
|
+
# it the same way as a normal node
|
106
|
+
else
|
107
|
+
control_structure = [structure, condition, {}, [], indent]
|
108
|
+
root control_structure, indent
|
109
|
+
|
110
|
+
parent[@children] << control_structure
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
# Check if the current control structure requires a parent node and
|
115
|
+
# return the parent's node type
|
116
|
+
#
|
117
|
+
def control_child(structure)
|
118
|
+
[:else, :elsif, :when].include? structure
|
119
|
+
end
|
120
|
+
|
121
|
+
# Check if the current control structure requires a parent node and
|
122
|
+
# return the parent's node type
|
123
|
+
#
|
124
|
+
def control_parent(structure)
|
125
|
+
case structure
|
126
|
+
when :else then [:if, :unless, :case]
|
127
|
+
when :elsif then [:if]
|
128
|
+
when :when then [:case]
|
129
|
+
end
|
130
|
+
end
|
131
|
+
end
|
132
|
+
end
|