riml 0.2.0 → 0.2.1
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.
- 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
|