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.
- checksums.yaml +5 -5
- data/.gitlab-ci.yml +1 -2
- data/.rubocop.yml +45 -5
- data/.rubocop_todo.yml +1 -634
- data/Gemfile +3 -1
- data/README.md +22 -0
- data/Rakefile +3 -1
- data/delorean.gemspec +18 -17
- data/lib/delorean/abstract_container.rb +4 -2
- data/lib/delorean/base.rb +30 -27
- data/lib/delorean/cache.rb +2 -0
- data/lib/delorean/cache/adapters.rb +2 -0
- data/lib/delorean/cache/adapters/base.rb +2 -0
- data/lib/delorean/cache/adapters/ruby_cache.rb +5 -0
- data/lib/delorean/const.rb +5 -3
- data/lib/delorean/debug.rb +6 -5
- data/lib/delorean/delorean.rb +466 -147
- data/lib/delorean/delorean.treetop +13 -1
- data/lib/delorean/engine.rb +61 -50
- data/lib/delorean/error.rb +2 -1
- data/lib/delorean/model.rb +12 -9
- data/lib/delorean/nodes.rb +130 -67
- data/lib/delorean/ruby.rb +2 -0
- data/lib/delorean/ruby/whitelists.rb +2 -0
- data/lib/delorean/ruby/whitelists/base.rb +7 -3
- data/lib/delorean/ruby/whitelists/default.rb +6 -6
- data/lib/delorean/ruby/whitelists/empty.rb +3 -2
- data/lib/delorean/ruby/whitelists/matchers.rb +2 -0
- data/lib/delorean/ruby/whitelists/matchers/arguments.rb +2 -0
- data/lib/delorean/ruby/whitelists/matchers/method.rb +5 -2
- data/lib/delorean/ruby/whitelists/whitelist_error.rb +2 -0
- data/lib/delorean/version.rb +3 -1
- data/lib/delorean_lang.rb +3 -1
- data/spec/cache_spec.rb +4 -2
- data/spec/dev_spec.rb +68 -69
- data/spec/eval_spec.rb +824 -729
- data/spec/func_spec.rb +172 -176
- data/spec/parse_spec.rb +516 -522
- data/spec/ruby/whitelist_spec.rb +6 -3
- data/spec/spec_helper.rb +26 -23
- 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:
|
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
|
data/lib/delorean/engine.rb
CHANGED
@@ -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
|
-
|
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
|
20
|
-
@
|
21
|
-
@
|
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,
|
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
|
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 =
|
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
|
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,
|
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
|
-
|
134
|
-
|
135
|
-
|
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,
|
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
|
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
|
-
|
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,
|
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,
|
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
|
264
|
+
@m = BaseModule.clone
|
265
|
+
@pm = Module.new
|
257
266
|
|
258
|
-
multi_line
|
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
|
274
|
+
next if line =~ /^\s*\#/
|
265
275
|
|
266
276
|
# remove trailing blanks
|
267
277
|
line.rstrip!
|
268
278
|
|
269
|
-
next if line.
|
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,
|
289
|
+
err(ParseError, 'syntax error') unless t
|
280
290
|
|
281
291
|
generate(t)
|
282
|
-
multi_line
|
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,
|
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,
|
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({})
|
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
|
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
|
345
|
+
klass.methods.map(&:to_s).select do |x|
|
335
346
|
x.end_with?(POST)
|
336
|
-
|
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(
|
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
|
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
|
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
|
391
|
-
x
|
402
|
+
bt = exc.backtrace.map do |x|
|
403
|
+
x =~ /^#{MOD}(.+?):(\d+)(|:in `(.+)')$/
|
392
404
|
$1 && [$1, $2.to_i, $4.sub(/#{POST}$/, '')]
|
393
|
-
|
405
|
+
end.reject(&:!)
|
394
406
|
|
395
|
-
{
|
407
|
+
{ 'error' => exc.message, 'backtrace' => bt }
|
396
408
|
end
|
397
409
|
|
398
410
|
######################################################################
|
399
|
-
|
400
411
|
end
|
401
412
|
end
|
data/lib/delorean/error.rb
CHANGED
data/lib/delorean/model.rb
CHANGED
@@ -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 = {}
|
12
|
+
def delorean_fn(name, options = {})
|
11
13
|
define_singleton_method(name) do |*args|
|
12
|
-
|
14
|
+
yield(*args)
|
13
15
|
end
|
14
16
|
|
15
17
|
sig = options[:sig]
|
16
18
|
|
17
|
-
raise
|
19
|
+
raise 'no signature' unless sig
|
18
20
|
|
19
21
|
if sig
|
20
22
|
sig = [sig, sig] if sig.is_a? Integer
|
21
|
-
raise
|
22
|
-
|
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 = {}
|
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
|
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 =
|
56
|
+
res = yield(*args)
|
54
57
|
|
55
58
|
delorean_cache_adapter.cache_item(
|
56
59
|
klass: self, cache_key: cache_key, item: res
|
data/lib/delorean/nodes.rb
CHANGED
@@ -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
|
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,
|
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 ?
|
136
|
+
(debug ? '_debug =' : '') +
|
112
137
|
"_e[self.name+'.#{i.text_value}'] ||= #{e.rewrite(context)};" +
|
113
138
|
(debug ? 'Delorean::Debug.log(_debug); _debug;' : '') +
|
114
|
-
|
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
|
-
|
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
|
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!='' ?
|
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(
|
279
|
+
def check(_context, *)
|
221
280
|
[]
|
222
281
|
end
|
223
282
|
|
224
283
|
# Delorean literals have same syntax as Ruby
|
225
|
-
def rewrite(
|
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(
|
291
|
+
def check(_context, *)
|
233
292
|
[]
|
234
293
|
end
|
235
294
|
|
236
|
-
def rewrite(
|
237
|
-
|
295
|
+
def rewrite(_context)
|
296
|
+
'_sanitize_hash(_e)'
|
238
297
|
end
|
239
298
|
end
|
240
299
|
|
241
300
|
class Sup < SNode
|
242
|
-
def check(
|
301
|
+
def check(_context, *)
|
243
302
|
[]
|
244
303
|
end
|
245
304
|
|
246
|
-
def rewrite(
|
247
|
-
|
305
|
+
def rewrite(_context)
|
306
|
+
'superclass'
|
248
307
|
end
|
249
308
|
end
|
250
309
|
|
251
310
|
class IString < Literal
|
252
|
-
def rewrite(
|
311
|
+
def rewrite(_context)
|
253
312
|
# FIXME: hacky to just fail
|
254
|
-
raise
|
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(
|
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) ?
|
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(
|
377
|
+
def check(_context, *)
|
319
378
|
[]
|
320
379
|
end
|
321
380
|
|
322
|
-
def rewrite(
|
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
|
395
|
+
args_str = ''
|
396
|
+
arg_count = 0
|
337
397
|
else
|
338
|
-
args_str
|
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? ?
|
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 =
|
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
|
407
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
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
|
-
[
|
549
|
-
|
550
|
-
|
551
|
-
|
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
|
-
[
|
577
|
-
|
578
|
-
|
579
|
-
|
580
|
-
|
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
|
-
|
587
|
-
|
588
|
-
|
589
|
-
|
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
|