opulent 1.5.5 → 1.6.0

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.
Files changed (78) hide show
  1. checksums.yaml +4 -4
  2. data/CODE_OF_CONDUCT.md +13 -0
  3. data/{LICENSE → LICENSE.md} +0 -0
  4. data/bin/opulent +0 -0
  5. data/lib/opulent.rb +10 -10
  6. data/lib/opulent/compiler.rb +15 -9
  7. data/lib/opulent/compiler/buffer.rb +123 -62
  8. data/lib/opulent/compiler/comment.rb +3 -4
  9. data/lib/opulent/compiler/control.rb +20 -26
  10. data/lib/opulent/compiler/define.rb +88 -36
  11. data/lib/opulent/compiler/doctype.rb +20 -22
  12. data/lib/opulent/compiler/eval.rb +23 -2
  13. data/lib/opulent/compiler/filter.rb +4 -5
  14. data/lib/opulent/compiler/node.rb +18 -12
  15. data/lib/opulent/compiler/root.rb +4 -5
  16. data/lib/opulent/compiler/text.rb +7 -2
  17. data/lib/opulent/compiler/yield.rb +2 -3
  18. data/lib/opulent/engine.rb +21 -20
  19. data/lib/opulent/exec.rb +21 -63
  20. data/lib/opulent/logger.rb +230 -3
  21. data/lib/opulent/parser.rb +14 -76
  22. data/lib/opulent/parser/comment.rb +45 -34
  23. data/lib/opulent/parser/control.rb +132 -111
  24. data/lib/opulent/parser/define.rb +15 -12
  25. data/lib/opulent/parser/doctype.rb +15 -15
  26. data/lib/opulent/parser/eval.rb +16 -6
  27. data/lib/opulent/parser/expression.rb +87 -84
  28. data/lib/opulent/parser/filter.rb +31 -25
  29. data/lib/opulent/parser/include.rb +38 -42
  30. data/lib/opulent/parser/node.rb +136 -118
  31. data/lib/opulent/parser/root.rb +24 -18
  32. data/lib/opulent/parser/text.rb +150 -123
  33. data/lib/opulent/parser/yield.rb +23 -23
  34. data/lib/opulent/settings.rb +70 -51
  35. data/lib/opulent/tokens.rb +17 -15
  36. data/lib/opulent/utils.rb +5 -4
  37. data/lib/opulent/version.rb +1 -1
  38. metadata +4 -43
  39. data/.libold/opulent.rb +0 -96
  40. data/.libold/opulent/context.rb +0 -80
  41. data/.libold/opulent/engine.rb +0 -88
  42. data/.libold/opulent/filter.rb +0 -101
  43. data/.libold/opulent/logger.rb +0 -67
  44. data/.libold/opulent/nodes.rb +0 -13
  45. data/.libold/opulent/nodes/block.rb +0 -29
  46. data/.libold/opulent/nodes/comment.rb +0 -35
  47. data/.libold/opulent/nodes/control.rb +0 -230
  48. data/.libold/opulent/nodes/define.rb +0 -42
  49. data/.libold/opulent/nodes/eval.rb +0 -41
  50. data/.libold/opulent/nodes/expression.rb +0 -28
  51. data/.libold/opulent/nodes/filter.rb +0 -70
  52. data/.libold/opulent/nodes/helper.rb +0 -69
  53. data/.libold/opulent/nodes/node.rb +0 -101
  54. data/.libold/opulent/nodes/root.rb +0 -62
  55. data/.libold/opulent/nodes/text.rb +0 -54
  56. data/.libold/opulent/nodes/theme.rb +0 -36
  57. data/.libold/opulent/parser.rb +0 -252
  58. data/.libold/opulent/parser/block.rb +0 -70
  59. data/.libold/opulent/parser/comment.rb +0 -32
  60. data/.libold/opulent/parser/control.rb +0 -83
  61. data/.libold/opulent/parser/define.rb +0 -39
  62. data/.libold/opulent/parser/eval.rb +0 -33
  63. data/.libold/opulent/parser/expression.rb +0 -350
  64. data/.libold/opulent/parser/filter.rb +0 -41
  65. data/.libold/opulent/parser/node.rb +0 -232
  66. data/.libold/opulent/parser/root.rb +0 -96
  67. data/.libold/opulent/parser/text.rb +0 -114
  68. data/.libold/opulent/parser/theme.rb +0 -36
  69. data/.libold/opulent/preprocessor.rb +0 -102
  70. data/.libold/opulent/runtime.rb +0 -144
  71. data/.libold/opulent/template.rb +0 -43
  72. data/.libold/opulent/tokens.rb +0 -276
  73. data/.libold/opulent/version.rb +0 -5
  74. data/.travis.yml +0 -4
  75. data/benchmark/benchmark.rb +0 -57
  76. data/benchmark/cases/node/node.haml +0 -7
  77. data/benchmark/cases/node/node.op +0 -7
  78. data/benchmark/cases/node/node.slim +0 -7
@@ -22,7 +22,7 @@ module Opulent
22
22
  #
23
23
  # [:node_type, :value, :attributes, :children, :indent]
24
24
  #
25
- def initialize(file, definitions)
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 = 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
- return @root, @definitions
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.inject('') do |result, line|
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
- def comment(parent, indent)
8
- if (accept :comment)
9
- multiline = true if (accept :comment)
10
-
11
- buffer = accept(:line_feed)
12
- buffer += accept(:newline) || ""
13
- buffer += get_indented_lines indent if multiline
14
-
15
-
16
- # If we have a comment which is visible in the output, we will
17
- # create a new comment element. Otherwise, we ignore the current
18
- # gathered text and we simply begin the root parsing again
19
- if buffer[0] == '!'
20
- offset = 1; options = {}
21
-
22
- # Allow leading comment newline
23
- if buffer[1] == '^'
24
- offset = 2; options[:newline] = true
25
- end
26
-
27
- parent[@children] << [:comment, buffer[offset..-1].strip, options, nil, indent]
28
- end
29
-
30
- return parent
31
- end
32
- end
33
- end
34
- end
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
- if (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
- error :each_arguments unless condition.match Tokens[:each_pattern]
19
-
20
- condition = [$1.split(' '), $2.split(/,(.+)$/).map(&:strip).map(&:to_sym)]
21
- condition[0].unshift '{}' if condition[0].length == 1 # Array loop as default
22
- end
23
-
24
- # Else and default structures are not allowed to have any condition
25
- # set and the other control structures require a condition
26
- if structure == :else
27
- error :condition_exists unless condition.empty?
28
- else
29
- error :condition_missing if condition.empty?
30
- end
31
-
32
- # Add the condition and create a new child to the control parent.
33
- # The control parent keeps condition -> children matches for our
34
- # document's content
35
- add_options = Proc.new do |control_parent|
36
- control_parent.value << condition
37
- control_parent.children << []
38
-
39
- root control_parent
40
- end
41
-
42
- # If the current control structure is a parent which allows multiple
43
- # branches, such as an if or case, we create an array of conditions
44
- # which can be matched and an array of children belonging to each
45
- # conditional branch
46
- if [:if, :unless].include? structure
47
- # Create the control structure and get its child nodes
48
- control_structure = [structure, [condition], {}, [], indent]
49
- root control_structure, indent
50
-
51
- # Turn children into an array because we allow multiple branches
52
- control_structure[@children] = [control_structure[@children]]
53
-
54
- # Add it to the parent node
55
- parent[@children] << control_structure
56
-
57
- elsif structure == :case
58
- # Create the control structure and get its child nodes
59
- control_structure = [structure, [], {condition: condition}, [], indent]
60
-
61
-
62
- # Add it to the parent node
63
- parent[@children] << control_structure
64
-
65
- # If the control structure is a child structure, we need to find the
66
- # node it belongs to amont the current parent. Search from end to
67
- # beginning until we find the node parent
68
- elsif control_child structure
69
- # During the search, we try to find the matching parent type
70
- unless control_parent(structure).include? parent[@children][-1][@type]
71
- error :control_child, control_parent(structure)
72
- end
73
-
74
- # Gather child elements for current structure
75
- control_structure = [structure, [condition], {}, [], indent]
76
- root control_structure, indent
77
-
78
- # Add the new condition and children to our parent structure
79
- parent[@children][-1][@value] << condition
80
- parent[@children][-1][@children] << control_structure[@children]
81
-
82
- # When our control structure isn't a complex composite, we create
83
- # it the same way as a normal node
84
- else
85
- control_structure = [structure, condition, {}, [], indent]
86
- root control_structure, indent
87
-
88
- parent[@children] << control_structure
89
- end
90
- end
91
- end
92
-
93
- # Check if the current control structure requires a parent node and
94
- # return the parent's node type
95
- #
96
- def control_child(structure)
97
- [:else, :elsif, :when].include? structure
98
- end
99
-
100
- # Check if the current control structure requires a parent node and
101
- # return the parent's node type
102
- #
103
- def control_parent(structure)
104
- case structure
105
- when :else then [:if, :unless, :case]
106
- when :elsif then [:if]
107
- when :when then [:case]
108
- end
109
- end
110
- end
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