riml 0.2.0 → 0.2.1
Sign up to get free protection for your applications and to get access to all the features.
- data/LICENSE +1 -1
- data/Rakefile +8 -1
- data/bin/riml +3 -0
- data/lib/ast_rewriter.rb +73 -7
- data/lib/compiler.rb +44 -5
- data/lib/constants.rb +18 -1
- data/lib/errors.rb +4 -0
- data/lib/grammar.y +10 -0
- data/lib/lexer.rb +11 -2
- data/lib/nodes.rb +78 -18
- data/lib/parser.rb +1064 -984
- data/lib/repl.rb +9 -1
- data/lib/riml.rb +30 -17
- data/version.rb +2 -2
- metadata +2 -2
data/LICENSE
CHANGED
data/Rakefile
CHANGED
@@ -4,12 +4,19 @@ require 'rake/testtask'
|
|
4
4
|
task :default => :test
|
5
5
|
task :test => [:output_test_count]
|
6
6
|
|
7
|
-
desc 'Run all
|
7
|
+
desc 'Run all tests (default)'
|
8
8
|
Rake::TestTask.new(:test) do |t|
|
9
9
|
TEST_LIST = FileList['test/**/*_test.rb'].to_a
|
10
10
|
t.test_files = TEST_LIST
|
11
11
|
end
|
12
12
|
|
13
|
+
desc 'recreate lib/parser.rb from lib/grammar.y using racc'
|
14
|
+
task :parser do
|
15
|
+
Dir.chdir(File.expand_path("../lib", __FILE__)) do
|
16
|
+
sh 'racc -o parser.rb grammar.y'
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
13
20
|
task :output_test_count do
|
14
21
|
puts "#{TEST_LIST.size} test files to run."
|
15
22
|
end
|
data/bin/riml
CHANGED
data/lib/ast_rewriter.rb
CHANGED
@@ -13,20 +13,25 @@ module Riml
|
|
13
13
|
@ast = ast
|
14
14
|
@classes = classes || ClassMap.new
|
15
15
|
@rewritten_include_files = {}
|
16
|
+
# keeps track of which files included which, to prevent infinite loops
|
17
|
+
@included_file_refs = {}
|
16
18
|
end
|
17
19
|
|
18
|
-
def rewrite(
|
19
|
-
if
|
20
|
-
|
20
|
+
def rewrite(from_file = nil)
|
21
|
+
if from_file
|
22
|
+
if rewritten_ast = rewritten_include_files[from_file]
|
23
|
+
return rewritten_ast
|
24
|
+
end
|
25
|
+
rewrite_included_files!(from_file)
|
21
26
|
end
|
22
|
-
rewrite_included_files!
|
23
27
|
establish_parents(ast)
|
24
28
|
rewriters = [
|
25
29
|
StrictEqualsComparisonOperator.new(ast, classes),
|
26
30
|
VarEqualsComparisonOperator.new(ast, classes),
|
27
31
|
ClassDefinitionToFunctions.new(ast, classes),
|
28
32
|
ObjectInstantiationToCall.new(ast, classes),
|
29
|
-
CallToExplicitCall.new(ast, classes)
|
33
|
+
CallToExplicitCall.new(ast, classes),
|
34
|
+
DefaultParamToIfNode.new(ast, classes)
|
30
35
|
]
|
31
36
|
rewriters.each do |rewriter|
|
32
37
|
rewriter.rewrite_on_match
|
@@ -53,14 +58,30 @@ module Riml
|
|
53
58
|
replace node if match?(node)
|
54
59
|
end
|
55
60
|
|
56
|
-
|
61
|
+
# We need to rewrite the included files before anything else. This is in
|
62
|
+
# order to keep track of any classes defined in the included files (and
|
63
|
+
# files included in those, etc...). We keep a cache of rewritten asts
|
64
|
+
# because the 'riml_include'd files are parsed more than once. They're
|
65
|
+
# parsed first before anything else, plus whenever the compiler visits a
|
66
|
+
# 'compile_include' node in order to compile it on the spot.
|
67
|
+
def rewrite_included_files!(from_file)
|
57
68
|
old_ast = ast
|
58
69
|
ast.children.each do |node|
|
59
70
|
next unless RimlCommandNode === node && node.name == 'riml_include'
|
60
71
|
node.each_existing_file! do |file|
|
72
|
+
if from_file && @included_file_refs[file] == from_file
|
73
|
+
msg = "#{from_file.inspect} can't include #{file.inspect}, as " \
|
74
|
+
" #{file.inspect} already included #{from_file.inspect}"
|
75
|
+
raise IncludeFileLoop, msg
|
76
|
+
elsif from_file == file
|
77
|
+
raise UserArgumentError, "#{file.inspect} can't include itself"
|
78
|
+
end
|
79
|
+
@included_file_refs[from_file] = file
|
61
80
|
full_path = File.join(Riml.source_path, file)
|
62
81
|
riml_src = File.read(full_path)
|
63
|
-
|
82
|
+
# recursively parse included files with this ast_rewriter in order
|
83
|
+
# to pick up any classes that are defined there
|
84
|
+
rewritten_ast = Parser.new.parse(riml_src, self, file)
|
64
85
|
rewritten_include_files[file] = rewritten_ast
|
65
86
|
end
|
66
87
|
end
|
@@ -297,5 +318,50 @@ module Riml
|
|
297
318
|
end
|
298
319
|
end
|
299
320
|
|
321
|
+
class DefaultParamToIfNode < AST_Rewriter
|
322
|
+
def match?(node)
|
323
|
+
DefaultParamNode === node
|
324
|
+
end
|
325
|
+
|
326
|
+
def replace(node)
|
327
|
+
def_node = node.parent
|
328
|
+
param_idx = def_node.parameters.index(node)
|
329
|
+
first_default_param = def_node.parameters.detect(&DefNode::DEFAULT_PARAMS)
|
330
|
+
first_default_param_idx = def_node.parameters.index(first_default_param)
|
331
|
+
|
332
|
+
last_default_param = def_node.parameters.reverse.detect(&DefNode::DEFAULT_PARAMS)
|
333
|
+
insert_idx = param_idx - first_default_param_idx
|
334
|
+
|
335
|
+
while param = def_node.parameters[param_idx += 1]
|
336
|
+
unless param == def_node.splat || DefaultParamNode === param
|
337
|
+
raise UserArgumentError, "can't have regular parameter after default parameter in function #{def_node.name.inspect}"
|
338
|
+
end
|
339
|
+
end
|
340
|
+
|
341
|
+
if_expression = construct_if_expression(node)
|
342
|
+
|
343
|
+
if last_default_param == node
|
344
|
+
def_node.parameters.delete_if(&DefNode::DEFAULT_PARAMS)
|
345
|
+
def_node.parameters << SPLAT_LITERAL unless def_node.splat
|
346
|
+
end
|
347
|
+
def_node.expressions.insert(insert_idx, if_expression)
|
348
|
+
reestablish_parents(def_node)
|
349
|
+
end
|
350
|
+
|
351
|
+
def construct_if_expression(node)
|
352
|
+
get_splat_node = CallNode.new(nil, 'get', [ GetVariableNode.new('a:', '000'), NumberNode.new(0), StringNode.new('rimldefault', :s) ])
|
353
|
+
condition_node = BinaryOperatorNode.new('!=#', [ get_splat_node, StringNode.new('rimldefault', :s) ])
|
354
|
+
remove_from_splat_node = CallNode.new(nil, 'remove', [ GetVariableNode.new('a:', '000'), NumberNode.new(0) ])
|
355
|
+
IfNode.new(condition_node,
|
356
|
+
Nodes.new([
|
357
|
+
AssignNode.new('=', GetVariableNode.new(nil, node.parameter), remove_from_splat_node),
|
358
|
+
ElseNode.new(Nodes.new([
|
359
|
+
AssignNode.new('=', GetVariableNode.new(nil, node.parameter), node.expression)
|
360
|
+
]))
|
361
|
+
])
|
362
|
+
)
|
363
|
+
end
|
364
|
+
end
|
365
|
+
|
300
366
|
end
|
301
367
|
end
|
data/lib/compiler.rb
CHANGED
@@ -185,6 +185,19 @@ module Riml
|
|
185
185
|
ContinueNodeVisitor = LiteralNodeVisitor
|
186
186
|
BreakNodeVisitor = LiteralNodeVisitor
|
187
187
|
|
188
|
+
class StringLiteralConcatNodeVisitor < Visitor
|
189
|
+
def compile(nodes)
|
190
|
+
nodes.each_with_index do |node, i|
|
191
|
+
visitor = visitor_for_node(node)
|
192
|
+
node.parent_node = nodes
|
193
|
+
next_node = nodes.nodes[i+1]
|
194
|
+
node.accept(visitor)
|
195
|
+
nodes.compiled_output << ' ' if next_node
|
196
|
+
end
|
197
|
+
nodes.compiled_output
|
198
|
+
end
|
199
|
+
end
|
200
|
+
|
188
201
|
class ListUnpackNodeVisitor < ListNodeVisitor
|
189
202
|
def compile(node)
|
190
203
|
node.compiled_output = super.reverse.sub(',', ';').reverse
|
@@ -219,7 +232,9 @@ module Riml
|
|
219
232
|
def set_modifier(node)
|
220
233
|
# Ex: n:myVariable = "override riml default scoping" compiles into:
|
221
234
|
# myVariable = "override riml default scoping"
|
222
|
-
|
235
|
+
if node.scope_modifier == "n:"
|
236
|
+
node.scope_modifier = ""
|
237
|
+
end
|
223
238
|
return node.scope_modifier if node.scope_modifier
|
224
239
|
node.scope_modifier = scope_modifier_for_node(node)
|
225
240
|
end
|
@@ -495,6 +510,11 @@ module Riml
|
|
495
510
|
root_node(node).current_compiler.compile_queue << file
|
496
511
|
end
|
497
512
|
elsif node.name == 'riml_include'
|
513
|
+
# riml_include has to be top-level
|
514
|
+
unless node.parent == root_node(node)
|
515
|
+
error_msg = %Q(riml_include error, has to be called at top-level)
|
516
|
+
raise IncludeNotTopLevel, error_msg
|
517
|
+
end
|
498
518
|
node.each_existing_file! do |file|
|
499
519
|
full_path = File.join(Riml.source_path, file)
|
500
520
|
riml_src = File.read(full_path)
|
@@ -510,8 +530,10 @@ module Riml
|
|
510
530
|
end
|
511
531
|
|
512
532
|
def root_node(node)
|
513
|
-
|
514
|
-
|
533
|
+
@root_node ||= begin
|
534
|
+
node = node.parent until node.parent.nil?
|
535
|
+
node
|
536
|
+
end
|
515
537
|
end
|
516
538
|
end
|
517
539
|
|
@@ -618,6 +640,20 @@ module Riml
|
|
618
640
|
end
|
619
641
|
end
|
620
642
|
|
643
|
+
class GetVariableByScopeAndDictNameNodeVisitor < Visitor
|
644
|
+
def compile(node)
|
645
|
+
node.scope_modifier.parent = node
|
646
|
+
node.scope_modifier.accept(visitor_for_node(node.scope_modifier))
|
647
|
+
node.keys.each do |key|
|
648
|
+
key.parent = node
|
649
|
+
node.compiled_output << '['
|
650
|
+
key.accept(visitor_for_node(key))
|
651
|
+
node.compiled_output << ']'
|
652
|
+
end
|
653
|
+
node.compiled_output
|
654
|
+
end
|
655
|
+
end
|
656
|
+
|
621
657
|
class ClassDefinitionNodeVisitor < Visitor
|
622
658
|
def compile(node)
|
623
659
|
node.expressions.parent_node = node
|
@@ -645,10 +681,13 @@ module Riml
|
|
645
681
|
@compile_queue ||= []
|
646
682
|
end
|
647
683
|
|
648
|
-
def
|
684
|
+
def sourced_files_compiled
|
685
|
+
@sourced_files_compiled ||= []
|
686
|
+
end
|
687
|
+
|
688
|
+
def compile_include(source, from_file)
|
649
689
|
root_node = parser.parse(source, parser.ast_rewriter, from_file)
|
650
690
|
output = compile(root_node)
|
651
|
-
return output unless from_file
|
652
691
|
(Riml::INCLUDE_COMMENT_FMT % from_file) + output
|
653
692
|
end
|
654
693
|
|
data/lib/constants.rb
CHANGED
@@ -30,7 +30,24 @@ module Riml
|
|
30
30
|
COMPARISON_OPERATORS = IGNORECASE_CAPABLE_OPERATORS.map do |o|
|
31
31
|
[o + '#', o + '?', o]
|
32
32
|
end.flatten
|
33
|
-
|
33
|
+
|
34
|
+
SPLAT_LITERAL = '...'
|
35
|
+
|
36
|
+
# :help registers
|
37
|
+
REGISTERS = [
|
38
|
+
'"',
|
39
|
+
(0..9).to_a.map(&:to_s),
|
40
|
+
'-',
|
41
|
+
('a'..'z').to_a.map(&:to_s),
|
42
|
+
('A'..'Z').to_a.map(&:to_s),
|
43
|
+
':', '.', '%', '#',
|
44
|
+
'=',
|
45
|
+
'*', '+', '~',
|
46
|
+
'_',
|
47
|
+
'/'
|
48
|
+
].flatten
|
49
|
+
|
50
|
+
# :help function-list
|
34
51
|
BUILTIN_FUNCTIONS =
|
35
52
|
%w(
|
36
53
|
abs
|
data/lib/errors.rb
CHANGED
@@ -6,6 +6,10 @@ module Riml
|
|
6
6
|
CompileError = Class.new(RimlError)
|
7
7
|
|
8
8
|
FileNotFound = Class.new(RimlError)
|
9
|
+
IncludeFileLoop = Class.new(RimlError)
|
10
|
+
IncludeNotTopLevel = Class.new(RimlError)
|
11
|
+
# bad user arguments to Riml functions
|
12
|
+
UserArgumentError = Class.new(RimlError)
|
9
13
|
|
10
14
|
ClassNotFound = Class.new(RimlError)
|
11
15
|
ClassRedefinitionError = Class.new(RimlError)
|
data/lib/grammar.y
CHANGED
@@ -122,6 +122,8 @@ rule
|
|
122
122
|
String:
|
123
123
|
STRING_S { result = StringNode.new(val[0], :s) }
|
124
124
|
| STRING_D { result = StringNode.new(val[0], :d) }
|
125
|
+
| String STRING_S { result = StringLiteralConcatNode.new(val[0], StringNode.new(val[1], :s)) }
|
126
|
+
| String STRING_D { result = StringLiteralConcatNode.new(val[0], StringNode.new(val[1], :d)) }
|
125
127
|
;
|
126
128
|
|
127
129
|
Regexp:
|
@@ -318,6 +320,7 @@ rule
|
|
318
320
|
VariableRetrieval:
|
319
321
|
Scope IDENTIFIER { result = GetVariableNode.new(val[0], val[1]) }
|
320
322
|
| SPECIAL_VAR_PREFIX IDENTIFIER { result = GetSpecialVariableNode.new(val[0], val[1]) }
|
323
|
+
| ScopeModifierLiteral ListOrDictGetWithBrackets { result = GetVariableByScopeAndDictNameNode.new(val[0], val[1]) }
|
321
324
|
;
|
322
325
|
|
323
326
|
AllVariableRetrieval:
|
@@ -374,7 +377,13 @@ rule
|
|
374
377
|
ParamList:
|
375
378
|
/* nothing */ { result = [] }
|
376
379
|
| IDENTIFIER { result = val }
|
380
|
+
| DefaultParam { result = val }
|
377
381
|
| ParamList ',' IDENTIFIER { result = val[0] << val[2] }
|
382
|
+
| ParamList ',' DefaultParam { result = val[0] << val[2] }
|
383
|
+
;
|
384
|
+
|
385
|
+
DefaultParam:
|
386
|
+
IDENTIFIER '=' ValueExpression { result = DefaultParamNode.new(val[0], val[2]) }
|
378
387
|
;
|
379
388
|
|
380
389
|
Return:
|
@@ -419,6 +428,7 @@ rule
|
|
419
428
|
For:
|
420
429
|
FOR IDENTIFIER IN ValueExpression Block END { result = ForNode.new(val[1], val[3], val[4]) }
|
421
430
|
| FOR List IN ValueExpression Block END { result = ForNode.new(val[1], val[3], val[4]) }
|
431
|
+
| FOR ListUnpack IN ValueExpression Block END { result = ForNode.new(val[1], val[3], val[4]) }
|
422
432
|
;
|
423
433
|
|
424
434
|
Try:
|
data/lib/lexer.rb
CHANGED
@@ -88,8 +88,17 @@ module Riml
|
|
88
88
|
@token_buf << [:SCOPE_MODIFIER_LITERAL, scope_modifier_literal]
|
89
89
|
elsif special_var_prefix = chunk[/\A(&(\w:)?(?!&)|\$|@)/]
|
90
90
|
@token_buf << [:SPECIAL_VAR_PREFIX, special_var_prefix.strip]
|
91
|
-
@expecting_identifier = true
|
92
91
|
@i += special_var_prefix.size
|
92
|
+
if special_var_prefix == '@'
|
93
|
+
new_chunk = get_new_chunk
|
94
|
+
next_char = new_chunk[0]
|
95
|
+
if REGISTERS.include?(next_char)
|
96
|
+
@token_buf << [:IDENTIFIER, next_char]
|
97
|
+
@i += 1
|
98
|
+
end
|
99
|
+
else
|
100
|
+
@expecting_identifier = true
|
101
|
+
end
|
93
102
|
elsif function_method = chunk[/\A(function)\(/, 1]
|
94
103
|
@token_buf << [:IDENTIFIER, function_method]
|
95
104
|
@i += function_method.size
|
@@ -153,7 +162,7 @@ module Riml
|
|
153
162
|
@token_buf << [:NUMBER, hex]
|
154
163
|
@i += hex.size
|
155
164
|
# integer or float (decimal)
|
156
|
-
elsif decimal = chunk[/\A[0-9]+(\.[0-9]+)?/]
|
165
|
+
elsif decimal = chunk[/\A[0-9]+(\.[0-9]+([eE][+-]?[0-9]+)?)?/]
|
157
166
|
@token_buf << [:NUMBER, decimal]
|
158
167
|
@i += decimal.size
|
159
168
|
elsif interpolation = chunk[INTERPOLATION_REGEX]
|
data/lib/nodes.rb
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
require File.expand_path('../constants', __FILE__)
|
2
|
+
require File.expand_path('../errors', __FILE__)
|
2
3
|
require 'set'
|
3
4
|
|
4
5
|
module Visitable
|
@@ -29,9 +30,8 @@ module Visitable
|
|
29
30
|
super
|
30
31
|
end
|
31
32
|
end
|
32
|
-
def
|
33
|
-
|
34
|
-
super
|
33
|
+
def respond_to?(method, include_private = false)
|
34
|
+
super || method =~ DESCENDANT_OF_REGEX
|
35
35
|
end
|
36
36
|
end
|
37
37
|
|
@@ -127,9 +127,8 @@ class Nodes < Struct.new(:nodes)
|
|
127
127
|
end
|
128
128
|
end
|
129
129
|
|
130
|
-
def
|
131
|
-
|
132
|
-
super
|
130
|
+
def respond_to?(method, include_private = false)
|
131
|
+
super || nodes.respond_to?(method, include_private)
|
133
132
|
end
|
134
133
|
|
135
134
|
def children
|
@@ -155,6 +154,20 @@ class StringNode < Struct.new(:value, :type) # type: :d or :s for double- or sin
|
|
155
154
|
include Visitable
|
156
155
|
end
|
157
156
|
|
157
|
+
class StringLiteralConcatNode < Struct.new(:string_nodes)
|
158
|
+
include Visitable
|
159
|
+
include Walkable
|
160
|
+
|
161
|
+
def initialize(*string_nodes)
|
162
|
+
super(string_nodes)
|
163
|
+
end
|
164
|
+
alias nodes string_nodes
|
165
|
+
|
166
|
+
def children
|
167
|
+
string_nodes
|
168
|
+
end
|
169
|
+
end
|
170
|
+
|
158
171
|
class RegexpNode < LiteralNode; end
|
159
172
|
|
160
173
|
class ListNode < LiteralNode
|
@@ -306,6 +319,14 @@ end
|
|
306
319
|
# call s:Method(argument1, argument2)
|
307
320
|
class ExplicitCallNode < CallNode; end
|
308
321
|
class RimlCommandNode < CallNode
|
322
|
+
|
323
|
+
def initialize(*)
|
324
|
+
super
|
325
|
+
if arguments.empty? || !arguments.all? { |arg| arg.is_a?(StringNode) }
|
326
|
+
raise Riml::UserArgumentError, "#{name.inspect} error: must pass string (name of file)"
|
327
|
+
end
|
328
|
+
end
|
329
|
+
|
309
330
|
def each_existing_file!
|
310
331
|
files = []
|
311
332
|
arguments.map(&:value).each do |file|
|
@@ -316,8 +337,15 @@ class RimlCommandNode < CallNode
|
|
316
337
|
"source path (#{Riml.source_path.inspect})"
|
317
338
|
end
|
318
339
|
end
|
340
|
+
return unless block_given?
|
319
341
|
# all files exist
|
320
|
-
files.each
|
342
|
+
files.each do |f|
|
343
|
+
begin
|
344
|
+
yield f
|
345
|
+
rescue Riml::IncludeFileLoop
|
346
|
+
arguments.delete_if { |arg| arg.value == f }
|
347
|
+
end
|
348
|
+
end
|
321
349
|
end
|
322
350
|
end
|
323
351
|
|
@@ -477,28 +505,30 @@ class DefNode < Struct.new(:bang, :scope_modifier, :name, :parameters, :keyword,
|
|
477
505
|
def initialize(*args)
|
478
506
|
super
|
479
507
|
# max number of arguments in viml
|
480
|
-
if parameters.size > 20
|
481
|
-
raise
|
508
|
+
if parameters.reject(&DEFAULT_PARAMS).size > 20
|
509
|
+
raise Riml::UserArgumentError, "can't have more than 20 parameters for #{full_name}"
|
482
510
|
end
|
483
511
|
end
|
484
512
|
|
485
|
-
SPLAT = lambda {|arg| arg ==
|
513
|
+
SPLAT = lambda {|arg| arg == Riml::Constants::SPLAT_LITERAL || arg[0] == "*"}
|
514
|
+
DEFAULT_PARAMS = lambda {|p| DefaultParamNode === p}
|
486
515
|
|
487
516
|
# ["arg1", "arg2"}
|
488
517
|
def argument_variable_names
|
489
|
-
|
518
|
+
parameters.reject(&SPLAT)
|
490
519
|
end
|
491
520
|
|
492
521
|
# returns the splat argument or nil
|
493
522
|
def splat
|
494
|
-
|
495
|
-
parameters.select(&SPLAT).first
|
496
|
-
end
|
523
|
+
parameters.detect(&SPLAT)
|
497
524
|
end
|
498
525
|
|
499
526
|
def keyword
|
500
|
-
|
501
|
-
|
527
|
+
if name.include?('.')
|
528
|
+
'dict'
|
529
|
+
else
|
530
|
+
super
|
531
|
+
end
|
502
532
|
end
|
503
533
|
|
504
534
|
def autoload?
|
@@ -516,8 +546,13 @@ class DefNode < Struct.new(:bang, :scope_modifier, :name, :parameters, :keyword,
|
|
516
546
|
end
|
517
547
|
end
|
518
548
|
|
549
|
+
def default_param_nodes
|
550
|
+
parameters.select(&DEFAULT_PARAMS)
|
551
|
+
end
|
552
|
+
|
519
553
|
def children
|
520
|
-
|
554
|
+
children = [expressions]
|
555
|
+
children.concat(default_param_nodes)
|
521
556
|
end
|
522
557
|
|
523
558
|
def method_missing(method, *args, &blk)
|
@@ -529,6 +564,15 @@ class DefNode < Struct.new(:bang, :scope_modifier, :name, :parameters, :keyword,
|
|
529
564
|
end
|
530
565
|
end
|
531
566
|
|
567
|
+
class DefaultParamNode < Struct.new(:parameter, :expression)
|
568
|
+
include Visitable
|
569
|
+
include Walkable
|
570
|
+
|
571
|
+
def children
|
572
|
+
[parameter, expression]
|
573
|
+
end
|
574
|
+
end
|
575
|
+
|
532
576
|
class ScopeNode
|
533
577
|
attr_writer :for_node_variable_names, :argument_variable_names
|
534
578
|
attr_accessor :function
|
@@ -721,6 +765,15 @@ class ListOrDictGetNode < Struct.new(:list_or_dict, :keys)
|
|
721
765
|
end
|
722
766
|
end
|
723
767
|
|
768
|
+
class GetVariableByScopeAndDictNameNode < Struct.new(:scope_modifier, :keys)
|
769
|
+
include Visitable
|
770
|
+
include Walkable
|
771
|
+
|
772
|
+
def children
|
773
|
+
[scope_modifier] + keys
|
774
|
+
end
|
775
|
+
end
|
776
|
+
|
724
777
|
class TryNode < Struct.new(:try_block, :catch_nodes, :finally_block)
|
725
778
|
include Visitable
|
726
779
|
include Indentable
|
@@ -750,7 +803,14 @@ class ClassDefinitionNode < Struct.new(:name, :superclass_name, :expressions)
|
|
750
803
|
|
751
804
|
def constructor
|
752
805
|
expressions.detect do |n|
|
753
|
-
DefNode === n && (n.name == 'initialize' || n.name.match(/Constructor\Z/))
|
806
|
+
next(false) unless DefNode === n && (n.name == 'initialize' || n.name.match(/Constructor\Z/))
|
807
|
+
if n.instance_of?(DefMethodNode)
|
808
|
+
Riml.warn("class #{name.inspect} has an initialize function declared with 'defm'. Please use 'def'.")
|
809
|
+
new_node = n.to_def_node
|
810
|
+
new_node.keyword = nil
|
811
|
+
n.replace_with(new_node)
|
812
|
+
end
|
813
|
+
true
|
754
814
|
end
|
755
815
|
end
|
756
816
|
alias constructor? constructor
|