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
@@ -1,36 +0,0 @@
1
- # @Opulent
2
- module Opulent
3
- # @Nodes
4
- module Nodes
5
- # @Theme
6
- #
7
- # Node class used to describe a HTML Element used for building a
8
- # page model during the parsing process
9
- #
10
- class Theme
11
- # Allow direct access to node variables
12
- attr_accessor :name, :indent, :parent, :children
13
-
14
- # Initialize node instance variables
15
- #
16
- # @param name [String] name of the html node
17
- # @param indentation [Fixnum] node indentation for restructuring
18
- # @param attributes [Hash] stores key="value" attributes
19
- # @param children [Array] collection of the node's child elements
20
- #
21
- def initialize(name = '', parent = nil, indent = 0, children = [])
22
- @name = name
23
- @parent = parent
24
- @indent = indent
25
- @children = children
26
- end
27
-
28
- # Add a new node to the nodes array
29
- #
30
- def push(node)
31
- @children << node
32
- self
33
- end
34
- end
35
- end
36
- end
@@ -1,252 +0,0 @@
1
- require_relative 'parser/root'
2
- require_relative 'parser/node'
3
- require_relative 'parser/eval'
4
- require_relative 'parser/expression'
5
- require_relative 'parser/define'
6
- require_relative 'parser/text'
7
- require_relative 'parser/filter'
8
- require_relative 'parser/control'
9
- require_relative 'parser/theme'
10
- require_relative 'parser/block'
11
- require_relative 'parser/comment'
12
-
13
- # @Opulent
14
- module Opulent
15
- # @Lexer
16
- module Parser
17
- # @Singleton
18
- class << self
19
- # Include Opulent tokens regular expressions to be checked using the
20
- # accept (or expect) method together with lookahead expressions
21
- include Root
22
- include Node
23
- include Expression
24
- include Evaluate
25
- include Define
26
- include Text
27
- include Filter
28
- include Control
29
- include Theme
30
- include Block
31
- include Comment
32
-
33
- # Analyze the input code and check for matching tokens.
34
- # In case no match was found, throw an exception.
35
- # In special cases, modify the token hash.
36
- #
37
- # @param code [String] Opulent code that needs to be analyzed
38
- # @return Nodes array
39
- #
40
- def parse(code)
41
- # Code to be parsed
42
- @code = code
43
-
44
- # Keeps track of consumed code
45
- @consumed = ""
46
-
47
- # Current line being parsed, used for error reporting
48
- @current_line = 1
49
-
50
- # Node creation wrapper for comprehensible node creation
51
- @create = Nodes::Helper.new
52
-
53
- # Create root node, our wrapper for all the HTML elements we use
54
- # which knows how to evaluate each of them to output the final code
55
- # The root also stores custom node definitions collection from which we
56
- # replace the nodes which use them
57
- @root = @create.root
58
-
59
- # Loop until we have no tokens left to parse or we find an error
60
- root @root
61
-
62
- # We still have code left to parse
63
- error :root unless @code.strip.empty?
64
-
65
- return @root
66
- end
67
-
68
- # Accept and consume or reject a given token as long as we have tokens
69
- # remaining. Shift the code with the match length plus any extra character
70
- # count around the capture group
71
- #
72
- # @param identifier [RegEx] Token syntax to be accepted by the parser
73
- # @param required [Boolean] Expect the given token syntax
74
- # @param strip [Boolean] Left strip the current code to remove whitespace
75
- #
76
- def accept(identifier, required = false, strip = true)
77
- # Get token from tokens knowledgebase
78
- token = Tokens[identifier]
79
-
80
- # If the token's capture group is smaller than the whole match,
81
- # advance the code chunk with more spaces
82
- extra = token[:extra] || 0
83
-
84
- # Remove leading whitespace between expressions
85
- if @code && strip
86
- if (stripped = @code[/\A +/]) then @consumed += stripped end
87
- @code.lstrip!
88
- end
89
-
90
- # Check to see if
91
- if @code =~ token[:regex]
92
- @current_line += 1 if identifier == :newline
93
-
94
- shift = $1.size + extra
95
-
96
- @consumed += @code[0..shift - 1] if shift > 0
97
- @code = @code[shift..-1]
98
-
99
- return $1
100
- elsif required
101
- error :expect, identifier
102
- else
103
- return nil
104
- end
105
- end
106
-
107
- # Accept and consume or reject a given token as long as we have tokens
108
- # remaining on the current line only. Shift the code with the match length
109
- # plus any extra character count around the capture group
110
- #
111
- # @param identifier [RegEx] Token syntax to be accepted by the parser
112
- # @param required [Boolean] Expect the given token syntax
113
- # @param strip [Boolean] Left strip the current code to remove whitespace
114
- #
115
- def accept_line(identifier, required = false, strip = true)
116
- # Get token from tokens knowledgebase
117
- token = Tokens[identifier]
118
-
119
- # If the token's capture group is smaller than the whole match,
120
- # advance the code chunk with more spaces
121
- extra = token[:extra] || 0
122
-
123
- # Remove leading whitespace between expressions
124
- if @code.lines.first && strip
125
- if (stripped = @code.lines.first[/\A +/]) then @consumed += stripped end
126
- @code.gsub! /\A +/, ''
127
- end
128
-
129
- # Check to see if
130
- if @code.lines.first =~ token[:regex]
131
- @current_line += 1 if identifier == :newline
132
-
133
- shift = $1.size + extra
134
-
135
- @consumed += @code[0..shift - 1] if shift > 0
136
- @code = @code[shift..-1]
137
-
138
- return $1
139
- elsif required
140
- error :expect, identifier
141
- else
142
- return nil
143
- end
144
- end
145
-
146
- # Wrapper method for accepting unstripped tokens such as whitespace or a
147
- # certain required sequence directly
148
- #
149
- # @param identifier [RegEx] Token syntax to be accepted by the parser
150
- # @param required [Boolean] Expect the given token syntax
151
- #
152
- def accept_unstripped(identifier, required = false)
153
- accept(identifier, required, false)
154
- end
155
-
156
- # Wrapper method for accepting unstripped tokens such as whitespace or a
157
- # certain required sequence directly on the current line only
158
- #
159
- # @param identifier [RegEx] Token syntax to be accepted by the parser
160
- # @param required [Boolean] Expect the given token syntax
161
- #
162
- def accept_line_unstripped(identifier, required = false)
163
- accept_line(identifier, required, false)
164
- end
165
-
166
- # Look ahead in the current code to see if we can match an input token.
167
- # No modifications will be done to the code.
168
- #
169
- # @param token [RegEx] Token syntax to be accepted by the parser
170
- # @param strip [Boolean] Left strip the current code to remove whitespace
171
- #
172
- def lookahead(token, strip = true)
173
- # Get token from tokens knowledgebase
174
- token = Tokens[token]
175
-
176
- # We don't want to modify anything in the code directly, so we use a
177
- # local code variable
178
- code = @code
179
-
180
- # Remove leading whitespace between expressions
181
- code = code.lstrip if code && strip
182
-
183
- # Check to see if
184
- if code =~ token[:regex]
185
- return $~[:capture]
186
- else
187
- return nil
188
- end
189
- end
190
-
191
- # Undo a found match by removing the token from the consumed code and
192
- # adding it back to the code chunk
193
- #
194
- # @param match [String] Matched string to be undone
195
- #
196
- def undo(match)
197
- unless match.empty?
198
- @consumed = @consumed[0..-match.length]
199
- @code = match + @code
200
- return nil
201
- end
202
- end
203
-
204
- # Give an explicit error report where an unexpected sequence of tokens
205
- # appears and give indications on how to solve it
206
- #
207
- # @param context [Symbol] Context name in which the error happens
208
- # @param data [Array] Additional error information
209
- #
210
- def error(context, *data)
211
- consumed = @consumed.lines.last.strip if @consumed.lines.last
212
- code = @code.empty? ? ' END' : @code.lines.first.strip
213
-
214
- message = case context
215
- when :root
216
- "Unknown node type encountered on line #{@current_line} of input at:\n\n" +
217
- "#{consumed}" + Logger.red(code)
218
- when :expect
219
- "Expected to find token :#{data[0]} on line #{@current_line} of input at:\n\n" +
220
- "#{consumed}" + Logger.red(code)
221
- when :assignments_colon
222
- "Unexpected end of element attributes reached on line #{@current_line} of input.\n\n" +
223
- "Expected to find an attribute at:\n\n" +
224
- "#{consumed}" + Logger.red(code)
225
- when :assignments_comma
226
- "Unexpected end of element attributes reached on line #{@current_line} of input.\n\n" +
227
- "Expected to find an attribute value at:\n\n" +
228
- "#{consumed}" + Logger.red(code)
229
- when :expression
230
- "Unexpected end of expression reached on line #{@current_line} of input.\n\n" +
231
- "Expected to find another expression term at:\n\n" +
232
- "#{consumed}" + Logger.red(code)
233
- when :whitespace_expression
234
- "Unexpected end of expression reached on line #{@current_line} of input.\n\n" +
235
- "Please use paranthesis for method parameters at:\n\n" +
236
- "#{consumed}" + Logger.red(code)
237
- when :definition
238
- "Unexpected start of definition on line #{@current_line - 1} of input.\n\n" +
239
- "Found a definition inside another definition or element at:\n\n" +
240
- (@consumed.lines[-2] || "") + Logger.red(consumed + "\n " + code)
241
- else
242
- "#{consumed}" + Logger.red(code)
243
- end
244
-
245
- # Reconstruct lines to display where errors occur
246
- fail "\n\nOpulent " + Logger.red("[Parser Error]") + "\n---\n" +
247
- "A parsing error has been encountered in the \"#{context}\" context.\n" +
248
- "#{message}\n\n\n"
249
- end
250
- end
251
- end
252
- end
@@ -1,70 +0,0 @@
1
- # @Opulent
2
- module Opulent
3
- # @Parser
4
- module Parser
5
- # @Block
6
- module Block
7
- # Match a yield with a explicit or implicit target
8
- #
9
- # yield target
10
- #
11
- # @param parent [Node] Parent node to which we append the definition
12
- #
13
- def block_yield(parent)
14
- if lookahead :yield_lookahead
15
- # Get current line's indentation
16
- indent = accept(:indent, false, false) || ""
17
-
18
- if accept :yield
19
- # Get definition name
20
- yield_name = if(yield_name = accept(:yield_identifier, false, false))
21
- yield_name.strip.to_sym
22
- else
23
- Engine::DEFAULT_YIELD
24
- end
25
-
26
- # Create a new node
27
- node = @create.block_yield yield_name, parent, indent.size
28
-
29
- # Consume the newline from the end of the element
30
- error :yield unless accept(:line_feed, false, false).strip.empty?
31
- accept :newline, false, false
32
-
33
- return node
34
- end
35
- end
36
- end
37
-
38
- # Match a block with a explicit target
39
- #
40
- # block target
41
- #
42
- # @param parent [Node] Parent node to which we append the definition
43
- #
44
- def block(parent)
45
- if lookahead :block_lookahead
46
- # Get current line's indentation
47
- indent = accept_unstripped(:indent) || ""
48
-
49
- if accept :block
50
- # Get definition name
51
- block_name = if(block_name = accept_unstripped(:yield_identifier))
52
- block_name.strip.to_sym
53
- else
54
- Engine::DEFAULT_YIELD
55
- end
56
-
57
- # Create a new node
58
- node = @create.block block_name, parent, indent.size
59
-
60
- # Consume the newline from the end of the element
61
- error :block unless accept_unstripped(:line_feed).strip.empty?
62
- accept_unstripped :newline
63
-
64
- return node
65
- end
66
- end
67
- end
68
- end
69
- end
70
- end
@@ -1,32 +0,0 @@
1
- # @SugarCube
2
- module Opulent
3
- # @Parser
4
- module Parser
5
- # @Text
6
- module Comment
7
- # Match one line or multiline comments
8
- #
9
- def comment(parent)
10
- if lookahead(:comment_lookahead)
11
- indent = accept_unstripped(:indent) || ""
12
- indent = indent.size
13
-
14
- if (comment_feed = accept_unstripped :comment)
15
- comment_feed += accept_unstripped(:newline) || ""
16
- comment_feed += get_indented_lines indent
17
-
18
- # If we have a comment which is visible in the output, we will
19
- # create a new comment element. Otherwise, we ignore the current
20
- # gathered text and we simply begin the root parsing again
21
- if comment_feed[0] == '!'
22
- return @create.comment comment_feed[1..-1].strip, parent, indent
23
- else
24
- root parent
25
- return nil
26
- end
27
- end
28
- end
29
- end
30
- end
31
- end
32
- end
@@ -1,83 +0,0 @@
1
- # @SugarCube
2
- module Opulent
3
- # @Parser
4
- module Parser
5
- # @Control
6
- module Control
7
- # Match an if-else control structure
8
- #
9
- def control(parent)
10
- if lookahead :control_lookahead
11
- indent = indent || accept_unstripped(:indent) || ""
12
-
13
- # Accept eval or multiline eval syntax and return a new node,
14
- if (structure = accept_unstripped(:control))
15
- structure = structure.to_sym
16
-
17
- # Handle each and the other control structures
18
- condition = accept_unstripped(:line_feed).strip
19
- accept_unstripped :newline
20
-
21
- # Process each control structure condition
22
- if structure == :each
23
- # Check if arguments provided correctly
24
- error :each_arguments unless condition.match Tokens[:each_pattern][:regex]
25
-
26
- # Split provided arguments for the each structure
27
- condition = condition.split('in').map(&:strip)
28
- condition[0] = condition[0].split(',').map(&:strip).map(&:to_sym)
29
- end
30
-
31
- # Else and default structures are not allowed to have any condition
32
- # set and the other control structures require a condition
33
- if structure == :else
34
- error :condition_exists unless condition.empty?
35
- else
36
- error :condition_missing if condition.empty?
37
- end
38
-
39
- # Add the condition and create a new child to the control parent.
40
- # The control parent keeps condition -> children matches for our
41
- # document's content
42
- add_options = Proc.new do |control_parent|
43
- control_parent.value << condition
44
- control_parent.children << []
45
-
46
- root control_parent
47
- end
48
-
49
- # If the current control structure has a matching parent, we search
50
- # for that type of element in the siblings with the same indentation
51
- # otherwise we return a new control structure parent
52
- if (parent_type = control_parent structure)
53
- begin
54
- last = -1
55
- until parent_type.include? parent.children[last].name
56
- last -= 1
57
- end
58
- add_options[parent.children[last]]
59
- rescue NoMethodError
60
- error :control_parent
61
- end
62
- else
63
- control_node = @create.control(structure, condition, parent, indent.size)
64
- end
65
-
66
- control_node
67
- end
68
- end
69
- end
70
-
71
- # Check if the current control structure requires a parent node and
72
- # return the parent's node type
73
- #
74
- def control_parent(structure)
75
- case structure
76
- when :else then [:if, :unless, :case]
77
- when :elsif then [:if]
78
- when :when then [:case]
79
- end
80
- end
81
- end
82
- end
83
- end