opulent 1.5.5 → 1.6.0

Sign up to get free protection for your applications and to get access to all the features.
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