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
@@ -6,13 +6,12 @@ module Opulent
6
6
  #
7
7
  # @param current [Array] Current node data with options
8
8
  # @param indent [Fixnum] Indentation size for current node
9
- # @param context [Context] Context holding environment variables
10
9
  #
11
- def root(current, indent, context)
10
+ def root(current, indent)
12
11
  if KEYWORDS.include? current[@type]
13
- send :"#{current[@type]}_node", current, indent, context
14
- else
15
- send current[@type], current, indent, context
12
+ send :"#{current[@type]}_node", current, indent
13
+ else
14
+ send current[@type], current, indent
16
15
  end
17
16
  end
18
17
  end
@@ -6,11 +6,13 @@ module Opulent
6
6
  #
7
7
  # @param node [Array] Node code generation data
8
8
  # @param indent [Fixnum] Size of the indentation to be added
9
- # @param context [Context] Processing environment data
10
9
  #
11
- def plain(node, indent, context)
10
+ def plain(node, indent)
12
11
  value = node[@options][:value]
13
12
 
13
+ # Leading whitespace
14
+ buffer_freeze ' ' if node[@options][:leading_whitespace]
15
+
14
16
  # Evaluate text node if it's marked as such and print nodes in the
15
17
  # current context
16
18
  if node[@value] == :text
@@ -18,6 +20,9 @@ module Opulent
18
20
  else
19
21
  node[@options][:escaped] ? buffer_escape(value) : buffer(value)
20
22
  end
23
+
24
+ # Trailing whitespace
25
+ buffer_freeze ' ' if node[@options][:trailing_whitespace]
21
26
  end
22
27
  end
23
28
  end
@@ -6,10 +6,9 @@ module Opulent
6
6
  #
7
7
  # @param node [Array] Node code generation data
8
8
  # @param indent [Fixnum] Size of the indentation to be added
9
- # @param context [Context] Processing environment data
10
9
  #
11
- def yield_node(node, indent, context)
12
- buffer_eval "yield if block_given?"
10
+ def yield_node(node, indent)
11
+ buffer_eval 'yield if block_given?'
13
12
  end
14
13
  end
15
14
  end
@@ -21,36 +21,23 @@ module Opulent
21
21
  # @param overwrite [Boolean] Write changes directly to the parent binding
22
22
  #
23
23
  def initialize(input, settings = {})
24
- # Set def from other Opulent instances
25
- @def = settings.delete(:def) || {}
26
-
27
24
  # Update default settings with user settings
28
- Settings.update_settings settings unless settings.empty?
25
+ @settings = Settings.new
26
+ @settings.update_settings settings unless settings.empty?
29
27
 
30
28
  # Read input parameter based on opening mode. If we have a file mode, we
31
29
  # get its path and read the code. We need to reset the mode in case the
32
30
  # next render call is on code, not on a file.
33
31
  @code = read input
34
32
 
33
+ # Pass filename into settings for Parser and Compiler
34
+ @settings[:file] = @file
35
+
35
36
  # Get the nodes tree
36
- @nodes, @def = Parser.new(@file, @def).parse @code
37
+ @nodes, @def = Parser.new(@settings).parse @code
37
38
 
38
39
  # Compile our syntax tree using input context
39
- @template = Compiler.new.compile @nodes
40
- end
41
-
42
- # Read input as file or string input
43
- #
44
- # @param input [Object]
45
- #
46
- def read(input)
47
- if input.is_a? Symbol
48
- @file = File.expand_path get_eval_file input
49
- File.read @file
50
- else
51
- @file = File.expand_path __FILE__
52
- input
53
- end
40
+ @template = Compiler.new(@settings).compile @nodes, @def
54
41
  end
55
42
 
56
43
  # Avoid code duplication when layouting is set. When we have a layout, look
@@ -96,6 +83,20 @@ module Opulent
96
83
 
97
84
  private
98
85
 
86
+ # Read input as file or string input
87
+ #
88
+ # @param input [Object]
89
+ #
90
+ def read(input)
91
+ if input.is_a? Symbol
92
+ @file = File.expand_path get_eval_file input
93
+ File.read @file
94
+ else
95
+ @file = File.expand_path __FILE__
96
+ input
97
+ end
98
+ end
99
+
99
100
  # Add .op extension to input file if it isn't already set.
100
101
  #
101
102
  # @param input [Symbol] Input file
@@ -1,6 +1,7 @@
1
1
  require 'json'
2
2
  require 'yaml'
3
3
  require 'opulent/version'
4
+ require 'opulent/logger'
4
5
 
5
6
  # @Opulent
6
7
  module Opulent
@@ -13,10 +14,6 @@ module Opulent
13
14
  def initialize(arguments)
14
15
  i = 0
15
16
 
16
- layout_error = 'Missing input or incorrect file extension for ' \
17
- 'layout [-l] argument. '
18
- layout_error += "Found #{arguments[i + 1]} instead." if arguments[i + 1]
19
-
20
17
  while arguments[i]
21
18
  case arguments[i]
22
19
 
@@ -33,20 +30,19 @@ module Opulent
33
30
  if arguments[i + 1] =~ EXTENSION
34
31
  @layout = arguments[(i += 1)]
35
32
  else
36
- error layout_error
33
+ Logger.error :exec, arguments[i + 1], :layout_error
37
34
  end
38
35
 
39
36
  # opulent -v
40
37
  when '-v', 'version'
41
- write "Version #{Opulent::VERSION} is currently installed."
38
+ Logger.log :version
42
39
  return
43
40
 
44
41
  # opulent -c
45
42
  when '-c', 'context', '-lc', 'locals'
46
43
  @locals_file = arguments[(i += 1)]
47
44
  unless File.file? @locals_file
48
- error "Given context file #{@locals_file.inspect} does not exist" \
49
- ' or an incorrect path has been specified.'
45
+ Logger.error :exec, @locals_file, :locals_file
50
46
  end
51
47
 
52
48
  if File.extname(@locals_file) == '.json'
@@ -54,16 +50,15 @@ module Opulent
54
50
  elsif File.extname(@locals_file) == '.yml'
55
51
  @locals = YAML.load_file @locals_file
56
52
  else
57
- error "Unknown file extension #{@locals_file.inspect} given as" \
58
- ' locals file. Please use JSON or YAML as input.'
53
+ Logger.error :exec, @locals_file, :locals_file_format
59
54
  end
60
55
 
61
56
  # opulent -h
62
57
  when '-h', 'help'
63
- write help
58
+ Logger.log :help
64
59
  return
65
60
  else
66
- error "Unknown input argument [#{arguments[i]}] has been encountered."
61
+ Logger.error :exec, arguments[i], :input_arguments
67
62
  end
68
63
 
69
64
  i += 1
@@ -72,72 +67,35 @@ module Opulent
72
67
  if @input
73
68
  @locals ||= {}
74
69
 
75
- unless File.file? @input
76
- error "Given input file #{@input.inspect} does not exist or " \
77
- 'an incorrect path has been specified.'
78
- end
70
+ Logger.error :exec, @input, :input unless File.file? @input
71
+
72
+ opulent_layout = Opulent.new @layout
73
+ opulent_page = Opulent.new @input
74
+ scope = Object.new
79
75
 
80
- message = ''
81
- opulent = Opulent.new
82
76
  if @layout
83
77
  output = proc do
84
- opulent.render_file(@layout, @locals) {
85
- opulent.render_file(@input, @locals) {}
86
- }
78
+ opulent_layout.render scope, @locals do
79
+ opulent_page.render scope, @locals do
80
+ end
81
+ end
87
82
  end[]
88
83
  else
89
84
  output = proc do
90
- opulent.render_file(@input, @locals)
85
+ opulent_page.render scope, @locals do
86
+ end
91
87
  end[]
92
88
  end
93
89
 
94
90
  if @output
95
91
  File.open(@output, 'w') { |file| file.write output }
96
- message += "Successfully rendered #{@input.inspect} " \
97
- "to #{@output.inspect}."
92
+ Logger.log :successful_render, @input, @output
98
93
  else
99
- message += "Successfully rendered #{@input.inspect}.\n\n"
100
- message += 'No output file specified. Writing result to ' \
101
- "terminal.\n\n#{output}"
94
+ Logger.log :successful_render_print, @input, output
102
95
  end
103
-
104
- write message
105
96
  else
106
- error "You haven't specified an input file."
97
+ Logger.error :exec, :no_input
107
98
  end
108
99
  end
109
-
110
- def help(_extra = '')
111
- <<-HELP #{_extra} You can use the following commands with the Opulent
112
- Command Line Interface: \n\n
113
- opulent input.op output.op Render an input file and write the result to
114
- the output file.\n
115
- opulent layout [-l] layout.op Render an input file using given input
116
- layout file.\n
117
- opulent help [-h] Show available command line options.\n
118
- opulent version [-v] Show installed version.\n"
119
- HELP
120
- end
121
-
122
- # Give an explicit response for the given input arguments
123
- #
124
- # @param message [String] Response message to display to the user
125
- #
126
- def write(message)
127
- # Reconstruct lines to display where errors occur
128
- puts "\nOpulent " + Logger.green('[Engine]') +
129
- "\n---\n#{message}\n\n\n"
130
- end
131
-
132
- # Give an explicit error report where an unexpected sequence of tokens
133
- # appears and give indications on how to solve it
134
- #
135
- # @param message [String] Error message to display to the user
136
- #
137
- def error(message)
138
- # Reconstruct lines to display where errors occur
139
- fail "\nOpulent " + Logger.red('[Engine]') +
140
- "\n---\n#{message}\n\n#{help}\n\n\n"
141
- end
142
100
  end
143
101
  end
@@ -55,17 +55,244 @@ module Opulent
55
55
  # Require windows libraries for ANSI Console output
56
56
  #
57
57
  def require_windows_libs
58
+ return unless RUBY_PLATFORM =~ /win32/
59
+
58
60
  begin
59
- require 'Win32/Console/ANSI' if RUBY_PLATFORM =~ /win32/
61
+ require 'Win32/Console/ANSI'
60
62
  rescue LoadError
61
63
  raise 'You must run "gem install win32console" to use Opulent\'s
62
64
  error reporting on Windows.'
63
65
  end
64
66
  end
65
67
 
66
- # Pretty print Nodes with their important details
68
+
69
+ # Output an error message based on class context and input data
70
+ #
71
+ # @param message [Symbol] Message to be displayed
72
+ # @param data [Array] Data to be displayed with the message
73
+ #
74
+ def log(message, *data)
75
+ case error
76
+ when :version
77
+ title = 'Version'
78
+ message = <<-LOG
79
+ Version #{Opulent::VERSION} is currently installed.
80
+ LOG
81
+ when :successful_render
82
+ title = 'Render Complete'
83
+ message = <<-LOG
84
+ Successfully rendered #{data[0].inspect} to #{data[1].inspect}.
85
+ LOG
86
+ when :successful_render_print
87
+ title = 'Render Complete'
88
+ message = <<-LOG
89
+ Successfully rendered #{data[0].inspect}.
90
+
91
+ No output file specified. Writing result to terminal.
92
+
93
+ #{data[1]}"
94
+
95
+ LOG
96
+ when :help
97
+ title = 'Help'
98
+ message = <<-LOG
99
+ You can use the following commands with the Opulent Command Line Interface:
100
+
101
+ opulent input.op output.op Render an input file and write the result to
102
+ the output file.
103
+ opulent layout [-l] layout.op Render an input file using given input
104
+ layout file.
105
+ opulent help [-h] Show available command line options.
106
+ opulent version [-v] Show installed version.
107
+ LOG
108
+ end
109
+
110
+ puts <<-OPULENT_LOG
111
+ \n
112
+ [Opulent Engine] #{title}
113
+
114
+ #{message}
115
+
116
+ OPULENT_LOG
117
+ end
118
+
119
+ # Display an error message based on context
120
+ #
121
+ def error(type, *data)
122
+ case type
123
+ when :parse
124
+ parse_error data[0], data[1], data[2], data[3], data[4..-1]
125
+ when :compile
126
+ compile_error data[0], data[1], data[2..-1]
127
+ when :exec
128
+ exec_error data[0], data[1..-1]
129
+ end
130
+ end
131
+
132
+ # Output an error message based on class context and input data
133
+ #
134
+ # @param klass [Symbol] Class in which the error happens
135
+ # @param error [Symbol] Error identification symbol
136
+ # @param data [Array] Data to be displayed with the error
137
+ #
138
+ def exec_error(error, *data)
139
+ case error
140
+ when :input
141
+ message = <<-ERROR
142
+ Given input file #{data[0].inspect} does not exist or an incorrect path
143
+ has been specified.
144
+ ERROR
145
+ when :layout_error
146
+ message = <<-ERROR
147
+ Missing input or incorrect file extension for layout [-l] argument.
148
+ Found #{data[0]} instead.
149
+ ERROR
150
+ when :locals_file
151
+ message = <<-ERROR
152
+ Given context file #{data[0].inspect} does not exist or
153
+ an incorrect path has been specified.
154
+ ERROR
155
+ when :locals_file_format
156
+ message = <<-ERROR
157
+ Unknown file extension #{data[0].inspect} given as locals file. Please use
158
+ JSON or YAML as input.
159
+ ERROR
160
+ when :input_arguments
161
+ message = <<-ERROR
162
+ Unknown input argument [#{data[0]}] has been encountered.
163
+ ERROR
164
+ when :no_input
165
+ message = <<-ERROR
166
+ You haven't specified an input file.
167
+ ERROR
168
+ end
169
+
170
+ fail <<-OPULENT_ERROR
171
+ \n
172
+ [Opulent Engine] Runtime Error
173
+
174
+ #{message}
175
+
176
+ OPULENT_ERROR
177
+ end
178
+
179
+ # Output an error message based on class context and input data
180
+ #
181
+ # @param klass [Symbol] Class in which the error happens
182
+ # @param error [Symbol] Error identification symbol
183
+ # @param data [Array] Data to be displayed with the error
184
+ #
185
+ def compile_error(template, error, *data)
186
+ case error
187
+ when :explicit_end
188
+ message = <<-ERROR
189
+ Explicit "end" evaluation nodes are not allowed. End expressions are
190
+ inserted automatically.
191
+ ERROR
192
+ end
193
+
194
+ fail <<-OPULENT_ERROR
195
+ \n
196
+ [Opulent Compiler] Runtime Error
197
+
198
+ #{message}
199
+
200
+ OPULENT_ERROR
201
+ end
202
+
203
+ # Output an error message based on class context and input data
204
+ #
205
+ # @param klass [Symbol] Class in which the error happens
206
+ # @param error [Symbol] Error identification symbol
207
+ # @param data [Array] Data to be displayed with the error
67
208
  #
68
- def pretty_print(_model, _indent = 0)
209
+ def parse_error(code, line, character, error, *data)
210
+ line += 1
211
+
212
+ case error
213
+ when :unknown_node_type
214
+ message = <<-ERROR
215
+ An unknown node type has been encountered.
216
+ ERROR
217
+ when :expected
218
+ if [:'(', :'{', :'[', :'<'].include? data[0]
219
+ data[0] = "#{Tokens.bracket data[0]}"
220
+ end
221
+ message = <<-ERROR
222
+ Expected to find a :#{data[0]} token on line #{line} of input.
223
+ ERROR
224
+ when :root
225
+ message = <<-ERROR
226
+ Unknown node type encountered on line #{line} of input.
227
+ ERROR
228
+ when :assignments_colon
229
+ message = <<-ERROR
230
+ Unexpected end of element attributes reached on line #{line}
231
+ of input.
232
+ ERROR
233
+ when :assignments_comma
234
+ message = <<-ERROR
235
+ Unexpected end of element attributes reached on line #{line}
236
+ of input. Expected to find an attribute value.
237
+ ERROR
238
+ when :expression
239
+ message = <<-ERROR
240
+ Unexpected end of expression reached on line #{line} of input.
241
+ Expected to find another expression term.
242
+ ERROR
243
+ when :control_child
244
+ message = <<-ERROR
245
+ Unexpected control structure child found on line #{line} of input.
246
+ Expected to find a parent #{data[0]} structure.
247
+ ERROR
248
+ when :whitespace_expression
249
+ message = <<-ERROR
250
+ Unexpected end of expression reached on line #{line} of input.
251
+ Please use paranthesis for method parameters
252
+ ERROR
253
+ when :definition
254
+ message = <<-ERROR
255
+ Unexpected start of definition on line #{line} of input.
256
+ Found a definition inside another definition or element.
257
+ ERROR
258
+ when :self_enclosing
259
+ message = <<-ERROR
260
+ Unexpected content found after self enclosing node on line
261
+ #{line} of input.
262
+ ERROR
263
+ when :self_enclosing_children
264
+ message = <<-ERROR
265
+ Unexpected child elements found for self enclosing node on line
266
+ #{data[0] + 1} of input.
267
+ ERROR
268
+ when :include
269
+ message = <<-ERROR
270
+ The included file #{data[0]} does not exist or an incorrect path
271
+ has been specified.
272
+ ERROR
273
+ when :include_dir
274
+ message = <<-ERROR
275
+ The included file path #{data[0]} is a directory.
276
+ ERROR
277
+ when :include_end
278
+ message = <<-ERROR
279
+ Missing argument for include on line #{line} of input.
280
+ ERROR
281
+ else
282
+ message = <<-ERROR
283
+ Unexpected syntax error on line #{line} of input.
284
+ ERROR
285
+ end
286
+
287
+ fail <<-OPULENT_ERROR
288
+ \n
289
+ [Opulent Parser] Runtime Error
290
+
291
+ #{message}
292
+ #{code[line - 1].chomp}
293
+ #{' ' * character}^
294
+
295
+ OPULENT_ERROR
69
296
  end
70
297
  end
71
298
  end