delorean_lang 0.5.1 → 0.5.2

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 (41) hide show
  1. checksums.yaml +5 -5
  2. data/.gitlab-ci.yml +1 -2
  3. data/.rubocop.yml +45 -5
  4. data/.rubocop_todo.yml +1 -634
  5. data/Gemfile +3 -1
  6. data/README.md +22 -0
  7. data/Rakefile +3 -1
  8. data/delorean.gemspec +18 -17
  9. data/lib/delorean/abstract_container.rb +4 -2
  10. data/lib/delorean/base.rb +30 -27
  11. data/lib/delorean/cache.rb +2 -0
  12. data/lib/delorean/cache/adapters.rb +2 -0
  13. data/lib/delorean/cache/adapters/base.rb +2 -0
  14. data/lib/delorean/cache/adapters/ruby_cache.rb +5 -0
  15. data/lib/delorean/const.rb +5 -3
  16. data/lib/delorean/debug.rb +6 -5
  17. data/lib/delorean/delorean.rb +466 -147
  18. data/lib/delorean/delorean.treetop +13 -1
  19. data/lib/delorean/engine.rb +61 -50
  20. data/lib/delorean/error.rb +2 -1
  21. data/lib/delorean/model.rb +12 -9
  22. data/lib/delorean/nodes.rb +130 -67
  23. data/lib/delorean/ruby.rb +2 -0
  24. data/lib/delorean/ruby/whitelists.rb +2 -0
  25. data/lib/delorean/ruby/whitelists/base.rb +7 -3
  26. data/lib/delorean/ruby/whitelists/default.rb +6 -6
  27. data/lib/delorean/ruby/whitelists/empty.rb +3 -2
  28. data/lib/delorean/ruby/whitelists/matchers.rb +2 -0
  29. data/lib/delorean/ruby/whitelists/matchers/arguments.rb +2 -0
  30. data/lib/delorean/ruby/whitelists/matchers/method.rb +5 -2
  31. data/lib/delorean/ruby/whitelists/whitelist_error.rb +2 -0
  32. data/lib/delorean/version.rb +3 -1
  33. data/lib/delorean_lang.rb +3 -1
  34. data/spec/cache_spec.rb +4 -2
  35. data/spec/dev_spec.rb +68 -69
  36. data/spec/eval_spec.rb +824 -729
  37. data/spec/func_spec.rb +172 -176
  38. data/spec/parse_spec.rb +516 -522
  39. data/spec/ruby/whitelist_spec.rb +6 -3
  40. data/spec/spec_helper.rb +26 -23
  41. metadata +27 -27
@@ -10,17 +10,28 @@ grammar Delorean
10
10
  /
11
11
  sp4 i:identifier sp? '=' sp? e:expression <Formula>
12
12
  /
13
+ n:class_name ':' sp? mod:(m:class_name_import_nested) <SubNodeNested> # FIXME: requires to be above SubNode statement, otherwise doesn't work
14
+ /
13
15
  n:class_name ':' sp? mod:(m:class_name '::')? p:class_name <SubNode>
14
16
  /
15
17
  n:class_name ':' <BaseNode>
16
18
  /
17
- 'import' sp n:class_name <Import>
19
+ 'import' sp n:class_name_import <Import>
18
20
  end
19
21
 
20
22
  rule class_name
21
23
  [A-Z] [a-zA-Z0-9_]*
22
24
  end
23
25
 
26
+ rule class_name_import
27
+ [A-Z] [a-zA-Z0-9_]* ('::' [A-Z] [a-zA-Z0-9_]*)*
28
+ end
29
+
30
+ # FIXME: Hacky way to skip Module::Node cases, so only Module::NestedModule::Node will pass
31
+ rule class_name_import_nested
32
+ [A-Z] [a-zA-Z0-9_]* (('::' [A-Z] [a-zA-Z0-9_]*) 2..)
33
+ end
34
+
24
35
  rule expression
25
36
  'ERR(' sp? args:fn_args? sp? ')' <ErrorOp>
26
37
  /
@@ -119,6 +130,7 @@ grammar Delorean
119
130
  list_expr /
120
131
  set_expr /
121
132
  hash_expr /
133
+ c:class_name_import <NodeAsValueNested> / # FIXME: requires to be above NodeAsValue statement, otherwise doesn't work
122
134
  mod:(m:class_name '::')? c:class_name <NodeAsValue> /
123
135
  '(' sp? e:expression sp? ')' <Expr>
124
136
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'delorean/const'
2
4
  require 'delorean/base'
3
5
  require 'set'
@@ -6,9 +8,9 @@ require 'pp'
6
8
  module Delorean
7
9
  class Engine
8
10
  attr_reader :last_node, :module_name, :line_no,
9
- :comp_set, :pm, :m, :imports, :sset
11
+ :comp_set, :pm, :m, :imports, :sset
10
12
 
11
- def initialize(module_name, sset=nil)
13
+ def initialize(module_name, sset = nil)
12
14
  # name of current module
13
15
  @module_name = module_name
14
16
  @sset = sset
@@ -16,9 +18,12 @@ module Delorean
16
18
  end
17
19
 
18
20
  def reset
19
- @m, @pm = nil, nil
20
- @last_node, @node_attrs = nil, {}
21
- @line_no, @multi_no = 0, nil
21
+ @m = nil
22
+ @pm = nil
23
+ @last_node = nil
24
+ @node_attrs = {}
25
+ @line_no = 0
26
+ @multi_no = nil
22
27
 
23
28
  # set of comprehension vars
24
29
  @comp_set = Set.new
@@ -41,24 +46,24 @@ module Delorean
41
46
  end
42
47
 
43
48
  def parse_import(name)
44
- err(ParseError, "No script set") unless sset
49
+ err(ParseError, 'No script set') unless sset
45
50
 
46
51
  err(ParseError, "Module #{name} importing itself") if
47
52
  name == module_name
48
53
 
49
54
  begin
50
55
  @imports[name] = sset.get_engine(name)
51
- rescue => exc
56
+ rescue StandardError => exc
52
57
  err(ImportError, exc.to_s)
53
58
  end
54
59
 
55
- @pm.const_set("#{MOD}#{name}", @imports[name].pm)
60
+ @pm.const_set("#{MOD}#{name.gsub('::', '__')}", @imports[name].pm)
56
61
  end
57
62
 
58
63
  def gen_import(name)
59
64
  @imports.merge!(@imports[name].imports)
60
65
 
61
- @m.const_set("#{MOD}#{name}", @imports[name].m)
66
+ @m.const_set("#{MOD}#{name.gsub('::', '__')}", @imports[name].m)
62
67
  end
63
68
 
64
69
  def get_import_engine(name)
@@ -66,7 +71,7 @@ module Delorean
66
71
  @imports[name]
67
72
  end
68
73
 
69
- def is_node_defined(name)
74
+ def node_defined?(name)
70
75
  @pm.constants.member? name.to_sym
71
76
  end
72
77
 
@@ -74,7 +79,7 @@ module Delorean
74
79
  # method about our expectation. flag=true means that we make sure
75
80
  # that name is defined. flag=false is the opposite.
76
81
  def parse_check_defined_node(name, flag)
77
- isdef = is_node_defined(name)
82
+ isdef = node_defined?(name)
78
83
 
79
84
  if isdef != flag
80
85
  isdef ? err(RedefinedError, "#{name} already defined") :
@@ -83,7 +88,7 @@ module Delorean
83
88
  end
84
89
 
85
90
  def super_name(pname, mname)
86
- mname ? "#{MOD}#{mname}::#{pname}" : pname
91
+ mname ? "#{MOD}#{mname.gsub('::', '__')}::#{pname}" : pname
87
92
  end
88
93
 
89
94
  def parse_check_defined_mod_node(pname, mname)
@@ -91,13 +96,15 @@ module Delorean
91
96
  engine.parse_check_defined_node(pname, true)
92
97
  end
93
98
 
94
- def parse_define_node(name, pname, mname=nil)
99
+ def parse_define_node(name, pname, mname = nil)
95
100
  parse_check_defined_node(name, false)
96
101
  parse_check_defined_mod_node(pname, mname) if pname
97
102
 
98
103
  sname = pname ? super_name(pname, mname) : 'Object'
99
104
 
100
- @pm.module_eval("class #{name} < #{sname}; end")
105
+ @pm.module_eval <<-RUBY, __FILE__, __LINE__ + 1
106
+ class #{name} < #{sname}; end
107
+ RUBY
101
108
 
102
109
  # latest defined node
103
110
  @last_node = name
@@ -125,20 +132,21 @@ module Delorean
125
132
 
126
133
  # Parse-time check to see if attr is available on current node.
127
134
  def parse_call_last_node_attr(attr_name)
128
- err(ParseError, "Not inside a node") unless @last_node
135
+ err(ParseError, 'Not inside a node') unless @last_node
129
136
  parse_call_attr(@last_node, attr_name)
130
137
  end
131
138
 
132
139
  def parse_define_var(var_name)
133
- err(RedefinedError,
134
- "List comprehension can't redefine variable '#{var_name}'") if
135
- comp_set.member? var_name
140
+ if comp_set.member? var_name
141
+ err(RedefinedError,
142
+ "List comprehension can't redefine variable '#{var_name}'")
143
+ end
136
144
 
137
145
  comp_set.add var_name
138
146
  end
139
147
 
140
148
  def parse_undef_var(var_name)
141
- err(ParseError, "internal error") unless comp_set.member? var_name
149
+ err(ParseError, 'internal error') unless comp_set.member? var_name
142
150
  comp_set.delete var_name
143
151
  end
144
152
 
@@ -152,10 +160,10 @@ module Delorean
152
160
 
153
161
  @node_attrs[@last_node] << name
154
162
 
155
- checks = spec.map { |a|
163
+ checks = spec.map do |a|
156
164
  n = a.index('.') ? a : "#{@last_node}.#{a}"
157
165
  "_x.member?('#{n}') ? raise('#{n}') : #{a}#{POST}(_x + ['#{n}'])"
158
- }.join(';')
166
+ end.join(';')
159
167
 
160
168
  code =
161
169
  "class #{@last_node}; def self.#{name}#{POST}(_x); #{checks}; end; end"
@@ -195,7 +203,7 @@ module Delorean
195
203
  raise exc.new(msg, @module_name, curr_line)
196
204
  end
197
205
 
198
- def parse_check_call_fn(fn, argcount, class_name=nil)
206
+ def parse_check_call_fn(fn, argcount, class_name = nil)
199
207
  klass = case class_name
200
208
  when nil
201
209
  @m::BaseClass
@@ -234,7 +242,7 @@ module Delorean
234
242
  # generate ruby code
235
243
  gen = t.rewrite(self)
236
244
  rescue RuntimeError => exc
237
- err(ParseError, "codegen error: " + exc.message)
245
+ err(ParseError, 'codegen error: ' + exc.message)
238
246
  end
239
247
 
240
248
  # puts gen
@@ -242,9 +250,9 @@ module Delorean
242
250
  begin
243
251
  # evaluate generated code in @m
244
252
  @m.module_eval(gen, "#{MOD}#{module_name}", curr_line)
245
- rescue => exc
253
+ rescue StandardError => exc
246
254
  # bad ruby code generated, shoudn't happen
247
- err(ParseError, "codegen error: " + exc.message)
255
+ err(ParseError, 'codegen error: ' + exc.message)
248
256
  end
249
257
  end
250
258
 
@@ -253,20 +261,22 @@ module Delorean
253
261
 
254
262
  # @m module is used at runtime for code evaluation. @pm module
255
263
  # is only used during parsing to check for errors.
256
- @m, @pm = BaseModule.clone, Module.new
264
+ @m = BaseModule.clone
265
+ @pm = Module.new
257
266
 
258
- multi_line, @multi_no = nil, nil
267
+ multi_line = nil
268
+ @multi_no = nil
259
269
 
260
270
  source.each_line do |line|
261
271
  @line_no += 1
262
272
 
263
273
  # skip comments
264
- next if line.match(/^\s*\#/)
274
+ next if line =~ /^\s*\#/
265
275
 
266
276
  # remove trailing blanks
267
277
  line.rstrip!
268
278
 
269
- next if line.length == 0
279
+ next if line.empty?
270
280
 
271
281
  if multi_line
272
282
  # if line starts with >4 spaces, assume it's a multline
@@ -276,17 +286,18 @@ module Delorean
276
286
  next
277
287
  else
278
288
  t = parser.parse(multi_line)
279
- err(ParseError, "syntax error") unless t
289
+ err(ParseError, 'syntax error') unless t
280
290
 
281
291
  generate(t)
282
- multi_line, @multi_no = nil, nil
292
+ multi_line = nil
293
+ @multi_no = nil
283
294
  end
284
295
  end
285
296
 
286
297
  t = parser.parse(line)
287
298
 
288
299
  if !t
289
- err(ParseError, "syntax error") unless line =~ /^\s+/
300
+ err(ParseError, 'syntax error') unless line =~ /^\s+/
290
301
 
291
302
  multi_line = line
292
303
  @multi_no = @line_no
@@ -297,7 +308,7 @@ module Delorean
297
308
 
298
309
  if multi_line
299
310
  t = parser.parse(multi_line)
300
- err(ParseError, "syntax error") unless t
311
+ err(ParseError, 'syntax error') unless t
301
312
  generate(t)
302
313
  end
303
314
  end
@@ -313,14 +324,14 @@ module Delorean
313
324
 
314
325
  # enumerate qualified list of all attrs
315
326
  def enumerate_attrs
316
- @node_attrs.keys.each_with_object({}) { |node, h|
327
+ @node_attrs.keys.each_with_object({}) do |node, h|
317
328
  h[node] = enumerate_attrs_by_node(node)
318
- }
329
+ end
319
330
  end
320
331
 
321
332
  # enumerate qualified list of attrs by node
322
333
  def enumerate_attrs_by_node(node)
323
- raise "bad node" unless node
334
+ raise 'bad node' unless node
324
335
 
325
336
  begin
326
337
  klass = node.is_a?(String) ? @m.module_eval(node) : node
@@ -331,11 +342,11 @@ module Delorean
331
342
 
332
343
  raise "bad node class #{klass}" unless klass.is_a?(Class)
333
344
 
334
- klass.methods.map(&:to_s).select { |x|
345
+ klass.methods.map(&:to_s).select do |x|
335
346
  x.end_with?(POST)
336
- }.map { |x|
347
+ end.map do |x|
337
348
  x.sub(/#{POST}$/, '')
338
- }
349
+ end
339
350
  end
340
351
 
341
352
  # enumerate all params
@@ -346,15 +357,15 @@ module Delorean
346
357
  # enumerate params by a single node
347
358
  def enumerate_params_by_node(node)
348
359
  attrs = enumerate_attrs_by_node(node)
349
- Set.new( attrs.select {|a| @param_set.include?(a)} )
360
+ Set.new(attrs.select { |a| @param_set.include?(a) })
350
361
  end
351
362
 
352
363
  ######################################################################
353
364
  # Runtime
354
365
  ######################################################################
355
366
 
356
- def evaluate(node, attrs, params={})
357
- raise "bad params" unless params.is_a?(Hash)
367
+ def evaluate(node, attrs, params = {})
368
+ raise 'bad params' unless params.is_a?(Hash)
358
369
 
359
370
  if node.is_a?(Class)
360
371
  klass = node
@@ -373,29 +384,29 @@ module Delorean
373
384
  type_arr = attrs.is_a?(Array)
374
385
  attrs = [attrs] unless type_arr
375
386
 
376
- res = attrs.map { |attr|
387
+ res = attrs.map do |attr|
377
388
  raise "bad attribute '#{attr}'" unless attr =~ /^[a-z][A-Za-z0-9_]*$/
389
+
378
390
  klass.send("#{attr}#{POST}".to_sym, params)
379
- }
391
+ end
380
392
  type_arr ? res : res[0]
381
393
  end
382
394
 
383
- def eval_to_hash(node, attrs, params={})
395
+ def eval_to_hash(node, attrs, params = {})
384
396
  res = evaluate(node, attrs, params)
385
397
  Hash[* attrs.zip(res).flatten(1)]
386
398
  end
387
399
 
388
400
  def self.grok_runtime_exception(exc)
389
401
  # parse out the delorean-related backtrace records
390
- bt = exc.backtrace.map{ |x|
391
- x.match(/^#{MOD}(.+?):(\d+)(|:in `(.+)')$/);
402
+ bt = exc.backtrace.map do |x|
403
+ x =~ /^#{MOD}(.+?):(\d+)(|:in `(.+)')$/
392
404
  $1 && [$1, $2.to_i, $4.sub(/#{POST}$/, '')]
393
- }.reject(&:!)
405
+ end.reject(&:!)
394
406
 
395
- {"error" => exc.message, "backtrace" => bt}
407
+ { 'error' => exc.message, 'backtrace' => bt }
396
408
  end
397
409
 
398
410
  ######################################################################
399
-
400
411
  end
401
412
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Delorean
2
4
  ######################################################################
3
5
  # Parse Errors
@@ -48,5 +50,4 @@ module Delorean
48
50
 
49
51
  class InvalidIndex < StandardError
50
52
  end
51
-
52
53
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'delorean/const'
2
4
 
3
5
  module Delorean
@@ -7,23 +9,24 @@ module Delorean
7
9
  end
8
10
 
9
11
  module ClassMethods
10
- def delorean_fn(name, options = {}, &block)
12
+ def delorean_fn(name, options = {})
11
13
  define_singleton_method(name) do |*args|
12
- block.call(*args)
14
+ yield(*args)
13
15
  end
14
16
 
15
17
  sig = options[:sig]
16
18
 
17
- raise "no signature" unless sig
19
+ raise 'no signature' unless sig
18
20
 
19
21
  if sig
20
22
  sig = [sig, sig] if sig.is_a? Integer
21
- raise "Bad signature" unless (sig.is_a? Array and sig.length==2)
22
- self.const_set(name.to_s.upcase+Delorean::SIG, sig)
23
+ raise 'Bad signature' unless sig.is_a?(Array) && (sig.length == 2)
24
+
25
+ const_set(name.to_s.upcase + Delorean::SIG, sig)
23
26
  end
24
27
  end
25
28
 
26
- # FIXME IDEA: we just make :cache an argument to delorean_fn.
29
+ # FIXME: IDEA: we just make :cache an argument to delorean_fn.
27
30
  # That way, we don't need the cached_ flavors. It'll make all
28
31
  # this code a lot simpler. We should also just add the :private
29
32
  # mechanism here.
@@ -33,11 +36,11 @@ module Delorean
33
36
  # values are ActiveRecord objects. Query results can be very
34
37
  # large lists which we count as one item in the cache. Caching
35
38
  # mechanism will result in large processes.
36
- def cached_delorean_fn(name, options = {}, &block)
39
+ def cached_delorean_fn(name, options = {})
37
40
  delorean_fn(name, options) do |*args|
38
41
  delorean_cache_adapter = ::Delorean::Cache.adapter
39
42
  # Check if caching should be performed
40
- next block.call(*args) unless delorean_cache_adapter.cache_item?(
43
+ next yield(*args) unless delorean_cache_adapter.cache_item?(
41
44
  klass: self, method_name: name, args: args
42
45
  )
43
46
 
@@ -50,7 +53,7 @@ module Delorean
50
53
 
51
54
  next cached_item if cached_item != :NF
52
55
 
53
- res = block.call(*args)
56
+ res = yield(*args)
54
57
 
55
58
  delorean_cache_adapter.cache_item(
56
59
  klass: self, cache_key: cache_key, item: res
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Delorean
2
4
  class SNode < Treetop::Runtime::SyntaxNode
3
5
  end
@@ -6,6 +8,7 @@ module Delorean
6
8
  def check(context, *a)
7
9
  f.check(context, *a)
8
10
  end
11
+
9
12
  def rewrite(context)
10
13
  f.rewrite(context)
11
14
  end
@@ -24,11 +27,12 @@ module Delorean
24
27
  # in _e. If not, to compute it we check for the value in _e
25
28
  # (i.e. check for aname). Otherwise, we use the default value
26
29
  # if any.
27
- aname, cname = i.text_value, context.last_node
30
+ aname = i.text_value
31
+ cname = context.last_node
28
32
  not_found = defined?(e) ? e.rewrite(context) :
29
33
  "raise UndefinedParamError, 'undefined parameter #{aname}'"
30
34
 
31
- <<eos
35
+ <<eos
32
36
  class #{cname}
33
37
  def self.#{aname}#{POST}(_e)
34
38
  _e[self.name+'.#{aname}'] ||= _e.fetch('#{aname}') { #{not_found} }
@@ -58,7 +62,7 @@ eos
58
62
 
59
63
  def rewrite(context)
60
64
  context.gen_import(n.text_value)
61
- ""
65
+ ''
62
66
  end
63
67
  end
64
68
 
@@ -71,13 +75,13 @@ eos
71
75
  def def_class(context, base_name)
72
76
  # Nodes are simply translated to classes. Define our own
73
77
  # self.name() since it's extremely slow in MRI 2.0.
74
- "class #{n.text_value} < #{base_name}; " +
75
- "def self.module_name; '#{context.module_name}'; end;" +
78
+ "class #{n.text_value} < #{base_name}; " \
79
+ "def self.module_name; '#{context.module_name}'; end;" \
76
80
  "def self.name; '#{n.text_value}'; end; end"
77
81
  end
78
82
 
79
83
  def rewrite(context)
80
- def_class(context, "BaseClass")
84
+ def_class(context, 'BaseClass')
81
85
  end
82
86
  end
83
87
 
@@ -96,6 +100,27 @@ eos
96
100
  end
97
101
  end
98
102
 
103
+ class SubNodeNested < BaseNode
104
+ def check(context, *)
105
+ module_names = mod.m.text_value.split('::')
106
+ node_name = module_names.pop
107
+ mname = module_names.join('::') if module_names.any?
108
+
109
+ context.parse_define_node(n.text_value, node_name, mname)
110
+ end
111
+
112
+ def rewrite(context)
113
+ module_names = mod.m.text_value.split('::')
114
+ node_name = module_names.pop
115
+ mname = module_names.join('::') if module_names.any?
116
+
117
+ sname = context.super_name(node_name, mname)
118
+
119
+ # A sub-node (derived node) is just a subclass.
120
+ def_class(context, sname)
121
+ end
122
+ end
123
+
99
124
  class Formula < SNode
100
125
  def check(context, *)
101
126
  context.parse_define_attr(i.text_value, e.check(context))
@@ -106,12 +131,12 @@ eos
106
131
  debug = Debug.debug_set.member?(dname)
107
132
 
108
133
  # an attr is defined as a class function on the node class.
109
- "class #{context.last_node}; " +
134
+ "class #{context.last_node}; " \
110
135
  "def self.#{i.text_value}#{POST}(_e); " +
111
- (debug ? "_debug =" : '') +
136
+ (debug ? '_debug =' : '') +
112
137
  "_e[self.name+'.#{i.text_value}'] ||= #{e.rewrite(context)};" +
113
138
  (debug ? 'Delorean::Debug.log(_debug); _debug;' : '') +
114
- "end; end;"
139
+ 'end; end;'
115
140
  end
116
141
  end
117
142
 
@@ -133,7 +158,7 @@ eos
133
158
  end
134
159
 
135
160
  def +(other)
136
- self.to_s + other
161
+ to_s + other
137
162
  end
138
163
 
139
164
  def to_s
@@ -168,6 +193,37 @@ eos
168
193
  end
169
194
  end
170
195
 
196
+ class NodeAsValueNested < SNode
197
+ def check(context, *)
198
+ module_names = c.text_value.split('::')
199
+ node_name = module_names.pop
200
+ mname = module_names.join('::') if module_names.any?
201
+
202
+ begin
203
+ context.parse_check_defined_mod_node(node_name, mname)
204
+ rescue UndefinedError, ParseError
205
+ # Node is a non-Delorean ruby class
206
+ context.parse_class(text_value)
207
+ end
208
+ []
209
+ end
210
+
211
+ def rewrite(context)
212
+ module_names = c.text_value.split('::')
213
+ node_name = module_names.pop
214
+ mname = module_names.join('::') if module_names.any?
215
+
216
+ begin
217
+ context.parse_check_defined_mod_node(node_name, mname)
218
+ context.super_name(node_name, mname)
219
+ rescue UndefinedError, ParseError
220
+ # FIXME: wrap the class name so Call will be able to tell it
221
+ # apart from a regular value.
222
+ ClassText.new(text_value)
223
+ end
224
+ end
225
+ end
226
+
171
227
  # unary operator
172
228
  class UnOp < SNode
173
229
  def check(context, *)
@@ -181,7 +237,8 @@ eos
181
237
 
182
238
  class BinOp < SNode
183
239
  def check(context, *)
184
- vc, ec = v.check(context), e.check(context)
240
+ vc = v.check(context)
241
+ ec = e.check(context)
185
242
  # returns list of attrs used in RHS and LHS
186
243
  ec + vc
187
244
  end
@@ -198,11 +255,13 @@ eos
198
255
  # hacky, for backwards compatibility
199
256
  class ErrorOp < SNode
200
257
  def check(context, *)
201
- args.text_value=='' ? [] : args.check(context)
258
+ args.text_value == '' ? [] : args.check(context)
202
259
  end
203
260
 
204
261
  def rewrite(context, *)
205
- args.text_value!='' ? "_err(#{args.rewrite(context)})" : "binding.pry; 0"
262
+ args.text_value != '' ?
263
+ "_err(#{args.rewrite(context)})" :
264
+ 'binding.pry; 0'
206
265
  end
207
266
  end
208
267
 
@@ -217,41 +276,41 @@ eos
217
276
  end
218
277
 
219
278
  class Literal < SNode
220
- def check(context, *)
279
+ def check(_context, *)
221
280
  []
222
281
  end
223
282
 
224
283
  # Delorean literals have same syntax as Ruby
225
- def rewrite(context)
284
+ def rewrite(_context)
226
285
  text_value
227
286
  end
228
287
  end
229
288
 
230
289
  # _ is self -- a naive implementation of "self" for now.
231
290
  class Self < SNode
232
- def check(context, *)
291
+ def check(_context, *)
233
292
  []
234
293
  end
235
294
 
236
- def rewrite(context)
237
- "_sanitize_hash(_e)"
295
+ def rewrite(_context)
296
+ '_sanitize_hash(_e)'
238
297
  end
239
298
  end
240
299
 
241
300
  class Sup < SNode
242
- def check(context, *)
301
+ def check(_context, *)
243
302
  []
244
303
  end
245
304
 
246
- def rewrite(context)
247
- "superclass"
305
+ def rewrite(_context)
306
+ 'superclass'
248
307
  end
249
308
  end
250
309
 
251
310
  class IString < Literal
252
- def rewrite(context)
311
+ def rewrite(_context)
253
312
  # FIXME: hacky to just fail
254
- raise "String interpolation not supported" if text_value =~ /\#\{.*\}/
313
+ raise 'String interpolation not supported' if text_value =~ /\#\{.*\}/
255
314
 
256
315
  # FIXME: syntax check?
257
316
  text_value
@@ -259,7 +318,7 @@ eos
259
318
  end
260
319
 
261
320
  class DString < Literal
262
- def rewrite(context)
321
+ def rewrite(_context)
263
322
  # remove the quotes and requote. We don't want the likes of #{}
264
323
  # evals to just pass through.
265
324
  text_value[1..-2].inspect
@@ -277,7 +336,7 @@ eos
277
336
  # class method calls. POST is used in mangling the attr names.
278
337
  # _e is the environment. Comprehension vars (in comp_set) are
279
338
  # not passed the env arg.
280
- arg = context.comp_set.member?(text_value) ? "" : '(_e)'
339
+ arg = context.comp_set.member?(text_value) ? '' : '(_e)'
281
340
  text_value + POST + arg
282
341
  end
283
342
  end
@@ -315,11 +374,11 @@ eos
315
374
  end
316
375
 
317
376
  class GetAttr < SNode
318
- def check(context, *)
377
+ def check(_context, *)
319
378
  []
320
379
  end
321
380
 
322
- def rewrite(context, vcode)
381
+ def rewrite(_context, vcode)
323
382
  attr = i.text_value
324
383
  attr = "'#{attr}'" unless attr =~ /\A[0-9]+\z/
325
384
  "_get_attr(#{vcode}, #{attr}, _e)"
@@ -333,9 +392,11 @@ eos
333
392
 
334
393
  def rewrite(context, vcode)
335
394
  if al.text_value.empty?
336
- args_str, arg_count = "", 0
395
+ args_str = ''
396
+ arg_count = 0
337
397
  else
338
- args_str, arg_count = al.rewrite(context), al.arg_count
398
+ args_str = al.rewrite(context)
399
+ arg_count = al.arg_count
339
400
  end
340
401
 
341
402
  if vcode.is_a?(ClassText)
@@ -356,7 +417,7 @@ eos
356
417
 
357
418
  def rewrite(context, node_name)
358
419
  var = "_h#{context.hcount}"
359
- res = al.text_value.empty? ? "" : al.rewrite(context, var)
420
+ res = al.text_value.empty? ? '' : al.rewrite(context, var)
360
421
  "(#{var}={}; #{res}; _node_call(#{node_name}, _e, #{var}))"
361
422
  end
362
423
  end
@@ -375,7 +436,7 @@ eos
375
436
  # element since it'll be "".
376
437
  attrs.shift
377
438
 
378
- attrs.inject(v.rewrite(context)) {|x, y| "_get_attr(#{x}, '#{y}', _e)"}
439
+ attrs.inject(v.rewrite(context)) { |x, y| "_get_attr(#{x}, '#{y}', _e)" }
379
440
  end
380
441
  end
381
442
 
@@ -389,7 +450,7 @@ eos
389
450
  end
390
451
 
391
452
  def rewrite(context)
392
- rest = ", " + args_rest.args.rewrite(context) if
453
+ rest = ', ' + args_rest.args.rewrite(context) if
393
454
  defined?(args_rest.args) && !args_rest.args.text_value.empty?
394
455
 
395
456
  [arg0.rewrite(context), rest].compact.sum
@@ -403,8 +464,9 @@ eos
403
464
 
404
465
  class IfElse < SNode
405
466
  def check(context, *)
406
- vc, e1c, e2c =
407
- v.check(context), e1.check(context), e2.check(context)
467
+ vc = v.check(context)
468
+ e1c = e1.check(context)
469
+ e2c = e2.check(context)
408
470
  vc + e1c + e2c
409
471
  end
410
472
 
@@ -420,7 +482,7 @@ eos
420
482
  end
421
483
 
422
484
  def rewrite(context)
423
- "[" + (defined?(args) ? args.rewrite(context) : "") + "]"
485
+ '[' + (defined?(args) ? args.rewrite(context) : '') + ']'
424
486
  end
425
487
  end
426
488
 
@@ -434,7 +496,7 @@ eos
434
496
  def rewrite(context)
435
497
  arg0.rewrite(context) +
436
498
  (defined?(args_rest.args) && !args_rest.args.text_value.empty? ?
437
- ", " + args_rest.args.rewrite(context) : "")
499
+ ', ' + args_rest.args.rewrite(context) : '')
438
500
  end
439
501
  end
440
502
 
@@ -442,19 +504,18 @@ eos
442
504
  def check(context, *)
443
505
  unpack_vars = args.check(context)
444
506
  e1c = e1.check(context)
445
- unpack_vars.each {|vname| context.parse_define_var(vname)}
507
+ unpack_vars.each { |vname| context.parse_define_var(vname) }
446
508
 
447
509
  # need to check e2/e3 in a context where the comprehension var
448
510
  # is defined.
449
511
  e2c = e2.check(context)
450
512
  e3c = defined?(ifexp.e3) ? ifexp.e3.check(context) : []
451
513
 
452
- unpack_vars.each {
453
- |vname|
514
+ unpack_vars.each do |vname|
454
515
  context.parse_undef_var(vname)
455
516
  e2c.delete(vname)
456
517
  e3c.delete(vname)
457
- }
518
+ end
458
519
 
459
520
  e1c + e2c + e3c
460
521
  end
@@ -462,13 +523,13 @@ eos
462
523
  def rewrite(context)
463
524
  res = ["(#{e1.rewrite(context)})"]
464
525
  unpack_vars = args.check(context)
465
- unpack_vars.each {|vname| context.parse_define_var(vname)}
526
+ unpack_vars.each { |vname| context.parse_define_var(vname) }
466
527
  args_str = args.rewrite(context)
467
528
 
468
529
  res << ".select{|#{args_str}|(#{ifexp.e3.rewrite(context)})}" if
469
530
  defined?(ifexp.e3)
470
531
  res << ".map{|#{args_str}| (#{e2.rewrite(context)}) }"
471
- unpack_vars.each {|vname| context.parse_undef_var(vname)}
532
+ unpack_vars.each { |vname| context.parse_undef_var(vname) }
472
533
  res.sum
473
534
  end
474
535
  end
@@ -492,7 +553,7 @@ eos
492
553
  def check(context, *)
493
554
  unpack_vars = args.check(context)
494
555
  e1c = e1.check(context)
495
- unpack_vars.each {|vname| context.parse_define_var(vname)}
556
+ unpack_vars.each { |vname| context.parse_define_var(vname) }
496
557
 
497
558
  # need to check el/er/ei in a context where the comprehension var
498
559
  # is defined.
@@ -500,20 +561,19 @@ eos
500
561
  erc = er.check(context)
501
562
  eic = defined?(ifexp.ei) ? ifexp.ei.check(context) : []
502
563
 
503
- unpack_vars.each {
504
- |vname|
564
+ unpack_vars.each do |vname|
505
565
  context.parse_undef_var(vname)
506
566
  elc.delete(vname)
507
567
  erc.delete(vname)
508
568
  eic.delete(vname)
509
- }
569
+ end
510
570
  e1c + elc + erc + eic
511
571
  end
512
572
 
513
573
  def rewrite(context)
514
574
  res = ["(#{e1.rewrite(context)})"]
515
575
  unpack_vars = args.check(context)
516
- unpack_vars.each {|vname| context.parse_define_var(vname)}
576
+ unpack_vars.each { |vname| context.parse_define_var(vname) }
517
577
  args_str = args.rewrite(context)
518
578
 
519
579
  hid = @@comp_count += 1
@@ -523,10 +583,10 @@ eos
523
583
 
524
584
  unpack_str = unpack_vars.count > 1 ? "(#{args_str})" : args_str
525
585
 
526
- res << ".each_with_object({}){|#{unpack_str}, _h#{hid}| " +
527
- "_h#{hid}[#{el.rewrite(context)}]=(#{er.rewrite(context)})}"
586
+ res << ".each_with_object({}){|#{unpack_str}, _h#{hid}| " \
587
+ "_h#{hid}[#{el.rewrite(context)}]=(#{er.rewrite(context)})}"
528
588
 
529
- unpack_vars.each {|vname| context.parse_undef_var(vname)}
589
+ unpack_vars.each { |vname| context.parse_undef_var(vname) }
530
590
  res.sum
531
591
  end
532
592
  end
@@ -537,7 +597,8 @@ eos
537
597
  end
538
598
 
539
599
  def rewrite(context)
540
- return "{}" unless defined?(args)
600
+ return '{}' unless defined?(args)
601
+
541
602
  var = "_h#{context.hcount}"
542
603
  "(#{var}={}; " + args.rewrite(context, var) + "; #{var})"
543
604
  end
@@ -545,14 +606,15 @@ eos
545
606
 
546
607
  class KwArgs < SNode
547
608
  def check(context, *)
548
- [arg0.check(context),
549
- (ifexp.e3.check(context) if defined?(ifexp.e3)),
550
- (args_rest.al.check(context) if
551
- defined?(args_rest.al) && !args_rest.al.empty?)
609
+ [
610
+ arg0.check(context),
611
+ (ifexp.e3.check(context) if defined?(ifexp.e3)),
612
+ (args_rest.al.check(context) if
613
+ defined?(args_rest.al) && !args_rest.al.empty?)
552
614
  ].compact.sum
553
615
  end
554
616
 
555
- def rewrite(context, var, i=0)
617
+ def rewrite(context, var, i = 0)
556
618
  arg0_rw = arg0.rewrite(context)
557
619
 
558
620
  if defined?(splat)
@@ -564,7 +626,7 @@ eos
564
626
  end
565
627
 
566
628
  res += " if (#{ifexp.e3.rewrite(context)})" if defined?(ifexp.e3)
567
- res += ";"
629
+ res += ';'
568
630
  res += args_rest.al.rewrite(context, var, i) if
569
631
  defined?(args_rest.al) && !args_rest.al.text_value.empty?
570
632
  res
@@ -573,22 +635,23 @@ eos
573
635
 
574
636
  class HashArgs < SNode
575
637
  def check(context, *)
576
- [e0.check(context),
577
- (e1.check(context) unless defined?(splat)),
578
- (ifexp.e3.check(context) if defined?(ifexp.e3)),
579
- (args_rest.al.check(context) if
580
- defined?(args_rest.al) && !args_rest.al.empty?),
638
+ [
639
+ e0.check(context),
640
+ (e1.check(context) unless defined?(splat)),
641
+ (ifexp.e3.check(context) if defined?(ifexp.e3)),
642
+ (args_rest.al.check(context) if
643
+ defined?(args_rest.al) && !args_rest.al.empty?),
581
644
  ].compact.sum
582
645
  end
583
646
 
584
647
  def rewrite(context, var)
585
648
  res = if defined?(splat)
586
- "#{var}.merge!(#{e0.rewrite(context)})"
587
- else
588
- "#{var}[#{e0.rewrite(context)}]=(#{e1.rewrite(context)})"
589
- end
649
+ "#{var}.merge!(#{e0.rewrite(context)})"
650
+ else
651
+ "#{var}[#{e0.rewrite(context)}]=(#{e1.rewrite(context)})"
652
+ end
590
653
  res += " if (#{ifexp.e3.rewrite(context)})" if defined?(ifexp.e3)
591
- res += ";"
654
+ res += ';'
592
655
  res += args_rest.al.rewrite(context, var) if
593
656
  defined?(args_rest.al) && !args_rest.al.text_value.empty?
594
657
  res