riml 0.3.8 → 0.3.9
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 +15 -0
- data/LICENSE +1 -1
- data/README.md +83 -30
- data/Rakefile +5 -0
- data/bin/riml +1 -0
- data/lib/riml.rb +17 -2
- data/lib/riml/ast_rewriter.rb +206 -166
- data/lib/riml/backtrace_filter.rb +2 -2
- data/lib/riml/class_map.rb +7 -1
- data/lib/riml/compiler.rb +14 -18
- data/lib/riml/file_rollback.rb +3 -1
- data/lib/riml/grammar.y +6 -6
- data/lib/riml/lexer.rb +11 -8
- data/lib/riml/nodes.rb +67 -22
- data/lib/riml/parser.rb +666 -667
- data/lib/riml/repl.rb +71 -13
- data/lib/riml/walker.rb +5 -2
- data/version.rb +2 -2
- metadata +31 -46
checksums.yaml
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
---
|
2
|
+
!binary "U0hBMQ==":
|
3
|
+
metadata.gz: !binary |-
|
4
|
+
MDk4OTEyMjkyYTk1YzVjYjEzNzAxNTllZGVmNjY0NGE5YTJjZWZmYQ==
|
5
|
+
data.tar.gz: !binary |-
|
6
|
+
OTY1ZDRlMDVjNGY3ZjA3YmViZGE2M2RjY2VkODg2ZjJhMDk5NTkxZA==
|
7
|
+
SHA512:
|
8
|
+
metadata.gz: !binary |-
|
9
|
+
MTk4MTY0Y2FiMGQ0ZDc4ODNjNDk4NDdmOGViZmFhODUxMmYxY2E1NjY5Njk4
|
10
|
+
ZDIxNGI3ZjI3ZjQyZjcxNDNmYWQxYWUyNTNlNzk0ODc1ZmEzZDU1YmUwZGQ1
|
11
|
+
YmQ1Y2E0N2M4YzE1OWFhMmZlNjkzYTZlOWQ4OWUyYmFlOWVlZmU=
|
12
|
+
data.tar.gz: !binary |-
|
13
|
+
MTc4ZjZkZjE1MmU3YTRkNDVlYjA2NTE4YzRhYjk1MGE1NTkwM2FlMjM1NTYw
|
14
|
+
OGU2ZWRmZDM2YmM2OTZlZmIwODI0ZmYxNTFjMWNlZWIxM2EyNWMyNDQxYzBk
|
15
|
+
MTU3NjJhMjNlM2Q3YTVkNDYxNjljZjZiNDNlMzA2NzQyOTZlODI=
|
data/LICENSE
CHANGED
data/README.md
CHANGED
@@ -1,10 +1,10 @@
|
|
1
1
|
[](https://travis-ci.org/luke-gru/riml)
|
2
2
|
|
3
|
-
Riml, a relaxed
|
4
|
-
|
3
|
+
Riml, a relaxed VimL
|
4
|
+
====================
|
5
5
|
|
6
|
-
Riml is a subset of
|
7
|
-
plain
|
6
|
+
Riml is a subset of VimL with some added features, and it compiles to
|
7
|
+
plain VimL. Some of the added features include classes, string interpolation,
|
8
8
|
heredocs, default case-sensitive string comparison, default parameters
|
9
9
|
in functions, and other things programmers tend to take for granted.
|
10
10
|
To see how Riml is compiled to VimL, just take a look at this README. The left
|
@@ -19,7 +19,7 @@ Variables
|
|
19
19
|
count += 1 let s:count += 1
|
20
20
|
end endwhile
|
21
21
|
|
22
|
-
If you don't specify a scope modifier (or namespace in Vimspeak), it's script local (s:)
|
22
|
+
If you don't specify a scope modifier (or 'namespace' in Vimspeak), it's script local (s:)
|
23
23
|
by default in the global scope. Within a function, variables without scope modifiers are plain
|
24
24
|
old local variables.
|
25
25
|
|
@@ -70,7 +70,7 @@ Notice in the last line of Riml there's string interpolation. This works
|
|
70
70
|
in double-quoted strings and heredocs, which we'll encounter later.
|
71
71
|
|
72
72
|
In Riml, you can choose to end any block with 'end', or with whatever you used
|
73
|
-
to do in
|
73
|
+
to do in VimL ('endif', 'endfunction', etc...). Also, 'if' and 'unless' can
|
74
74
|
now be used as statement modifiers:
|
75
75
|
|
76
76
|
callcount = 0 unless callcount?
|
@@ -82,15 +82,15 @@ Here, the compiled output is the same as the previous example's. Both 'if' and
|
|
82
82
|
|
83
83
|
###True and False
|
84
84
|
|
85
|
-
a = true let a = 1
|
86
|
-
b = false let b = 0
|
85
|
+
a = true let s:a = 1
|
86
|
+
b = false let s:b = 0
|
87
87
|
|
88
88
|
Operators And Case Sensitivity
|
89
89
|
------------------------------
|
90
90
|
|
91
|
-
if "hi" == greeting
|
92
|
-
echo greeting
|
93
|
-
end
|
91
|
+
if "hi" == greeting if "hi" ==# s:greeting
|
92
|
+
echo greeting echo s:greeting
|
93
|
+
end end
|
94
94
|
|
95
95
|
Comparisons compile to case-sensitive by default. To get case-insensitive
|
96
96
|
comparisons, you have to explicitly use the form ending in '?' (ex: '==?').
|
@@ -101,7 +101,7 @@ cousin 'is#', and the same is true of 'isnot'.
|
|
101
101
|
|
102
102
|
###=== Operator
|
103
103
|
|
104
|
-
Oh no, not another of
|
104
|
+
Oh no, not another of __those__ operators! Well, this one is pretty sweet
|
105
105
|
actually. In VimL, automatic type conversion can be a pain. For example:
|
106
106
|
|
107
107
|
echo 4 ==# "4"
|
@@ -116,7 +116,7 @@ regarding equality:
|
|
116
116
|
The '===' operator wraps both operands in lists:
|
117
117
|
|
118
118
|
echo 4 === "4" echo [4] ==# ["4"]
|
119
|
-
|
119
|
+
=> 0
|
120
120
|
|
121
121
|
Heredocs
|
122
122
|
--------
|
@@ -126,8 +126,8 @@ Heredocs
|
|
126
126
|
Hooray!
|
127
127
|
EOS
|
128
128
|
|
129
|
-
Riml heredocs must have the ending pattern ('EOS' in this case) start
|
130
|
-
beginning of the line. Interpolated expressions are allowed in heredocs.
|
129
|
+
Riml heredocs must have the ending pattern ('EOS' in this case, but can be anything) start
|
130
|
+
at the beginning of the line. Interpolated expressions are allowed in heredocs.
|
131
131
|
|
132
132
|
Functions
|
133
133
|
---------
|
@@ -137,15 +137,16 @@ Functions
|
|
137
137
|
return @/ return @/
|
138
138
|
end endfunction
|
139
139
|
|
140
|
-
When defining a function with no parameters, the parens after the function name are optional.
|
141
|
-
|
142
140
|
Functions are by default prepended by 's:' unless explicitly prepended with a
|
143
141
|
different scope modifier. Of course, you can use the old form ('function! Name()')
|
144
142
|
for defining functions if you want, as Riml aims to be as compatible as possible
|
145
143
|
with VimL. There are a few exceptions where Riml and VimL aren't compatible, and
|
146
144
|
these differences are explained in the section 'Incompatibilities with VimL'.
|
147
145
|
|
148
|
-
|
146
|
+
When defining a function with no parameters and no keywords (such as 'dict' or 'abort'),
|
147
|
+
the parens after the function name are optional.
|
148
|
+
|
149
|
+
###Default Parameters
|
149
150
|
|
150
151
|
def fillSearchPat(pat = getDefaultSearchPat()) function! s:fillSearchPat(...)
|
151
152
|
@/ = pat if get(a:000, 0, 'rimldefault') !=# 'rimldefault'
|
@@ -158,13 +159,63 @@ these differences are explained in the section 'Incompatibilities with VimL'.
|
|
158
159
|
endfunction
|
159
160
|
|
160
161
|
|
161
|
-
Default
|
162
|
-
than one default
|
163
|
-
Splats will be explained in the next section.
|
162
|
+
Default parameters must be the last parameters given to a function, but there can be more
|
163
|
+
than one default parameter. Also, a splat parameter (... or \*argName) can come after default parameters.
|
164
|
+
Splats parameters and splat arguments will be explained in the next section.
|
164
165
|
|
165
166
|
We can now call the function 'fillSearchPat' without any arguments and it will use the default
|
166
|
-
|
167
|
-
is useful if a function has many default
|
167
|
+
parameter. Also, if we pass the string 'rimldefault', it will use the default parameter as well. This
|
168
|
+
is useful if a function has many default parameters.
|
169
|
+
|
170
|
+
###Splat Parameters
|
171
|
+
|
172
|
+
A splat parameter is a formal parameter to a function starting with a '\*'. They must be the last
|
173
|
+
parameters given to a function.
|
174
|
+
|
175
|
+
def my_echo(*words) function! s:my_echo(...)
|
176
|
+
echo words echo a:000
|
177
|
+
end endfunction
|
178
|
+
my_echo("hello", "world") call my_echo("hello", "world")
|
179
|
+
|
180
|
+
=> ["hello", "world"]
|
181
|
+
|
182
|
+
###Splat Arguments
|
183
|
+
|
184
|
+
Splat arguments are splats used in a function call context. With the function `my_echo` defined in the
|
185
|
+
previous section, here's an example at work:
|
186
|
+
|
187
|
+
words = ["hello", "world"] let s:words = ["hello", "world"]
|
188
|
+
echo 'without splat:' echo 'without splat:'
|
189
|
+
my_echo(words) call my_echo(s:words)
|
190
|
+
echo 'with splat:' echo 'with splat:'
|
191
|
+
my_echo(*words) let s:__riml_splat_list = s:words
|
192
|
+
|
193
|
+
let s:__riml_splat_size = len(s:__riml_splat_list)
|
194
|
+
let s:__riml_splat_str_vars = []
|
195
|
+
let s:__riml_splat_idx = 1
|
196
|
+
while s:__riml_splat_idx <=# s:__riml_splat_size
|
197
|
+
let s:__riml_splat_var_{s:__riml_splat_idx} = get(s:__riml_splat_list, s:__riml_splat_idx - 1)
|
198
|
+
call add(s:__riml_splat_str_vars, '__riml_splat_var_' . s:__riml_splat_idx)
|
199
|
+
let s:__riml_splat_idx += 1
|
200
|
+
endwhile
|
201
|
+
execute 'call s:my_echo(' . join(s:__riml_splat_str_vars, ', ') . ')'
|
202
|
+
|
203
|
+
=> without splat:
|
204
|
+
=> [["hello", "world"]]
|
205
|
+
|
206
|
+
=> with splat:
|
207
|
+
=> ["hello", "world"]
|
208
|
+
|
209
|
+
The way to think about how splat arguments work is that the list variable that's
|
210
|
+
splatted is broke into its constituent parts, just like in Ruby. So, `my_echo(*words)`
|
211
|
+
is equivalent to `my_echo("hello", "world")` in this example. Notice how you can now
|
212
|
+
pass the exact same arguments your function received to another function by splatting a
|
213
|
+
parameter and passing it on to another function.
|
214
|
+
|
215
|
+
def some_func(*args)
|
216
|
+
other_func(*args) " passes same arguments along to other function.
|
217
|
+
end
|
218
|
+
|
168
219
|
|
169
220
|
Classes
|
170
221
|
-------
|
@@ -193,6 +244,10 @@ Classes
|
|
193
244
|
return self.otherData
|
194
245
|
endfunction
|
195
246
|
|
247
|
+
In this basic example of a class, we see a \*splat variable. This is just a
|
248
|
+
convenient way to refer to 'a:000' in the body of a function. Splat variables
|
249
|
+
are optional parameters and get compiled to '...'.
|
250
|
+
|
196
251
|
Classes can only be defined once, and cannot be reopened. Public member
|
197
252
|
functions are defined with 'defm'. If you want to create a non-public function
|
198
253
|
inside a class, use 'def'. To create an instance of this class, simply:
|
@@ -208,9 +263,6 @@ For example:
|
|
208
263
|
...
|
209
264
|
end
|
210
265
|
|
211
|
-
In this basic example of a class, we see a \*splat variable. This is just a
|
212
|
-
convenient way to refer to 'a:000' in the body of a function. Splat variables
|
213
|
-
are optional parameters and get compiled to '...'.
|
214
266
|
|
215
267
|
###Class Inheritance
|
216
268
|
|
@@ -251,6 +303,11 @@ instances use the initialize function from 'Translation', and new instances must
|
|
251
303
|
be provided with an 'input' argument on creation. Basically, if a class doesn't
|
252
304
|
provide an initialize function, it uses its superclass's.
|
253
305
|
|
306
|
+
If you look at the last line of Riml in the previous example, you'll see that
|
307
|
+
it doesn't use VimL's builtin 'call' function for calling the 'translate'
|
308
|
+
method on the translation object. Riml can figure out when 'call' is necessary,
|
309
|
+
and will add it to the compiled VimL.
|
310
|
+
|
254
311
|
A base class and inheriting class can have different scope modifiers. For example, if you
|
255
312
|
had a script-local base class and wanted to extend it but have the extending class be
|
256
313
|
global, this is not a problem. Simply:
|
@@ -259,10 +316,6 @@ global, this is not a problem. Simply:
|
|
259
316
|
...
|
260
317
|
end
|
261
318
|
|
262
|
-
If you look at the last line of Riml in the previous example, you'll see that
|
263
|
-
it doesn't use Vim script's builtin 'call' function for calling the 'translate'
|
264
|
-
method on the translation object. Riml can figure out when 'call' is necessary,
|
265
|
-
and will add it to the compiled Vim script.
|
266
319
|
|
267
320
|
###Using 'super'
|
268
321
|
|
data/Rakefile
CHANGED
@@ -15,6 +15,11 @@ Rake::TestTask.new(:test) do |t|
|
|
15
15
|
t.test_files = FileList['test/**/*_test.rb'].to_a
|
16
16
|
end
|
17
17
|
|
18
|
+
desc 'Run benchmarks'
|
19
|
+
task :bench => [:parser] do
|
20
|
+
load File.expand_path('../benchmarks/run', __FILE__)
|
21
|
+
end
|
22
|
+
|
18
23
|
desc 'recreate lib/parser.rb from lib/grammar.y using racc'
|
19
24
|
task :parser do
|
20
25
|
in_libdir { sh 'racc -o parser.rb grammar.y' }
|
data/bin/riml
CHANGED
data/lib/riml.rb
CHANGED
@@ -1,5 +1,6 @@
|
|
1
1
|
require 'pathname'
|
2
2
|
require 'fileutils'
|
3
|
+
require 'ostruct'
|
3
4
|
|
4
5
|
if RUBY_VERSION < '1.9'
|
5
6
|
require 'thread'
|
@@ -229,6 +230,20 @@ module Riml
|
|
229
230
|
self.warnings = true
|
230
231
|
self.debug = false
|
231
232
|
|
233
|
+
# @return OpenStruct|nil
|
234
|
+
def self.config
|
235
|
+
@config
|
236
|
+
end
|
237
|
+
|
238
|
+
# possible values: OpenStruct|nil
|
239
|
+
def self.config=(config)
|
240
|
+
unless config.nil? || OpenStruct === config
|
241
|
+
raise ArgumentError, "config must be OpenStruct or NilClass, is #{config.class}"
|
242
|
+
end
|
243
|
+
@config = config
|
244
|
+
end
|
245
|
+
self.config = nil # avoid ivar warnings
|
246
|
+
|
232
247
|
private
|
233
248
|
|
234
249
|
def self.flush_warnings
|
@@ -259,8 +274,8 @@ module Riml
|
|
259
274
|
cache_files_in_path(path, force_cache_bust)
|
260
275
|
path
|
261
276
|
end
|
262
|
-
self.source_path = nil #
|
263
|
-
self.include_path = nil #
|
277
|
+
self.source_path = nil # avoid ivar warnings
|
278
|
+
self.include_path = nil # avoid ivar warnings
|
264
279
|
|
265
280
|
def self.cache_files_in_path(path, force_cache_bust = false)
|
266
281
|
@path_cache[path] = nil if force_cache_bust
|
data/lib/riml/ast_rewriter.rb
CHANGED
@@ -13,19 +13,24 @@ module Riml
|
|
13
13
|
|
14
14
|
def initialize(ast = nil, classes = nil, class_dependency_graph = nil)
|
15
15
|
@ast = ast
|
16
|
-
@classes = classes
|
17
|
-
|
18
|
-
#
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
16
|
+
@classes = classes
|
17
|
+
@class_dependency_graph = class_dependency_graph
|
18
|
+
# only the top-most rewriter has these properties and defaults
|
19
|
+
if self.instance_of?(Riml::AST_Rewriter)
|
20
|
+
@classes ||= ClassMap.new
|
21
|
+
# AST_Rewriter shares options with Parser. Parser set AST_Rewriter's
|
22
|
+
# options before call to `rewrite`.
|
23
|
+
@options = nil
|
24
|
+
# Keeps track of filenames with their rewritten ASTs, to prevent rewriting
|
25
|
+
# the same AST more than once.
|
26
|
+
@rewritten_included_and_sourced_files = {}
|
27
|
+
# Keeps track of which filenames included/sourced which.
|
28
|
+
# ex: { nil => ["main.riml"], "main.riml" => ["lib1.riml", "lib2.riml"],
|
29
|
+
# "lib1.riml" => [], "lib2.riml" => [] }
|
30
|
+
@included_and_sourced_file_refs = Hash.new { |h, k| h[k] = [] }
|
31
|
+
@class_dependency_graph ||= ClassDependencyGraph.new
|
32
|
+
@resolving_class_dependencies = nil
|
33
|
+
end
|
29
34
|
end
|
30
35
|
|
31
36
|
def rewrite(filename = nil, included = false)
|
@@ -57,7 +62,8 @@ module Riml
|
|
57
62
|
CallToExplicitCall.new(ast, classes),
|
58
63
|
DefaultParamToIfNode.new(ast, classes),
|
59
64
|
DeserializeVarAssignment.new(ast, classes),
|
60
|
-
TopLevelDefMethodToDef.new(ast, classes)
|
65
|
+
TopLevelDefMethodToDef.new(ast, classes),
|
66
|
+
SplatsToExecuteInCallingContext.new(ast, classes)
|
61
67
|
]
|
62
68
|
rewriters.each do |rewriter|
|
63
69
|
rewriter.rewrite_on_match
|
@@ -72,8 +78,8 @@ module Riml
|
|
72
78
|
|
73
79
|
def do_establish_parents(node)
|
74
80
|
node.children.each do |child|
|
75
|
-
child.parent_node = node if child
|
76
|
-
end if node
|
81
|
+
child.parent_node = node if Visitable === child
|
82
|
+
end if Visitable === node
|
77
83
|
end
|
78
84
|
|
79
85
|
def rewrite_on_match(node = ast)
|
@@ -437,150 +443,6 @@ module Riml
|
|
437
443
|
end
|
438
444
|
end
|
439
445
|
|
440
|
-
# Rewrite constructs like:
|
441
|
-
#
|
442
|
-
# let animalObj = s:AnimalConstructor(*a:000)
|
443
|
-
#
|
444
|
-
# to:
|
445
|
-
#
|
446
|
-
# let __riml_splat_list = a:000
|
447
|
-
# let __riml_splat_size = len(__riml_splat_list)
|
448
|
-
# let __riml_splat_str_vars = []
|
449
|
-
# let __riml_splat_idx = 1
|
450
|
-
# while __riml_splat_idx <=# __riml_splat_size
|
451
|
-
# let __riml_splat_var_{__riml_splat_idx} = get(__riml_splat_list, __riml_splat_idx - 1)
|
452
|
-
# call add(__riml_splat_str_vars, __riml_splat_var_{__riml_splat_idx})
|
453
|
-
# let __riml_splat_idx += 1
|
454
|
-
# endwhile
|
455
|
-
# execute 'let l:animalObj = s:AnimalConstructor(' . join(__riml_splat_str_vars, ', ') . ')'
|
456
|
-
#
|
457
|
-
# Basically, mimic Ruby's approach to expanding lists to their
|
458
|
-
# constituent argument parts with '*' in calling context.
|
459
|
-
# NOTE: currently only works with `super`.
|
460
|
-
class SplatsToExecuteInCallingContext < AST_Rewriter
|
461
|
-
|
462
|
-
def match?(node)
|
463
|
-
if SplatNode === node && CallNode === node.parent
|
464
|
-
@splat_node = node
|
465
|
-
end
|
466
|
-
end
|
467
|
-
|
468
|
-
def replace(node)
|
469
|
-
construct_splat_str_vars_node = build_construct_splat_str_vars_node
|
470
|
-
call_node_args =
|
471
|
-
CallNode.new(
|
472
|
-
nil,
|
473
|
-
'join',
|
474
|
-
[
|
475
|
-
GetVariableNode.new('n:', '__riml_splat_str_vars'),
|
476
|
-
StringNode.new(', ', :s)
|
477
|
-
]
|
478
|
-
)
|
479
|
-
call_node = node.parent
|
480
|
-
node_to_execute = if AssignNode === call_node.parent
|
481
|
-
assign_node = call_node.parent
|
482
|
-
# This is necessary because this node is getting put into a new
|
483
|
-
# compiler where it's not wrapped in a function context, therefore
|
484
|
-
# variables will be script-local there unless their scope_modifier
|
485
|
-
# is set
|
486
|
-
assign_node.lhs.scope_modifier = 'l:'
|
487
|
-
assign_node
|
488
|
-
else
|
489
|
-
call_node
|
490
|
-
end
|
491
|
-
call_node.arguments.clear
|
492
|
-
compiler = Compiler.new
|
493
|
-
# have to dup node_to_execute here because, if not, its parent will
|
494
|
-
# get reset during this next compilation step
|
495
|
-
output = compiler.compile(Nodes.new([node_to_execute.dup]))
|
496
|
-
execute_string_node = StringNode.new(output.chomp[0..-2], :s)
|
497
|
-
execute_string_node.value.insert(0, 'call ') if CallNode === node_to_execute
|
498
|
-
execute_arg = BinaryOperatorNode.new(
|
499
|
-
'.',
|
500
|
-
[
|
501
|
-
execute_string_node,
|
502
|
-
BinaryOperatorNode.new(
|
503
|
-
'.',
|
504
|
-
[
|
505
|
-
call_node_args,
|
506
|
-
StringNode.new(')', :s)
|
507
|
-
]
|
508
|
-
)
|
509
|
-
]
|
510
|
-
)
|
511
|
-
execute_node = CallNode.new(nil, 'execute', [execute_arg])
|
512
|
-
establish_parents(execute_node)
|
513
|
-
node.remove
|
514
|
-
node_to_execute.replace_with(construct_splat_str_vars_node)
|
515
|
-
execute_node.parent = construct_splat_str_vars_node.parent
|
516
|
-
construct_splat_str_vars_node.parent.insert_after(construct_splat_str_vars_node, execute_node)
|
517
|
-
end
|
518
|
-
|
519
|
-
private
|
520
|
-
|
521
|
-
def build_construct_splat_str_vars_node
|
522
|
-
nodes = Nodes.new([])
|
523
|
-
splat_list_init = AssignNode.new(
|
524
|
-
'=',
|
525
|
-
GetVariableNode.new('n:', '__riml_splat_list'),
|
526
|
-
splat_value
|
527
|
-
)
|
528
|
-
splat_size = AssignNode.new(
|
529
|
-
'=',
|
530
|
-
GetVariableNode.new('n:', '__riml_splat_size'),
|
531
|
-
CallNode.new(nil, 'len', [GetVariableNode.new('n:', '__riml_splat_list')])
|
532
|
-
)
|
533
|
-
splat_string_vars_init = AssignNode.new(
|
534
|
-
'=',
|
535
|
-
GetVariableNode.new('n:', '__riml_splat_str_vars'),
|
536
|
-
ListNode.new([])
|
537
|
-
)
|
538
|
-
splat_list_idx_init = AssignNode.new(
|
539
|
-
'=',
|
540
|
-
GetVariableNode.new('n:', '__riml_splat_idx'),
|
541
|
-
NumberNode.new('1')
|
542
|
-
)
|
543
|
-
while_loop = WhileNode.new(
|
544
|
-
# condition
|
545
|
-
BinaryOperatorNode.new('<=', [GetVariableNode.new('n:', '__riml_splat_idx'), GetVariableNode.new('n:', '__riml_splat_size')]),
|
546
|
-
# body
|
547
|
-
Nodes.new([
|
548
|
-
AssignNode.new(
|
549
|
-
'=',
|
550
|
-
GetCurlyBraceNameNode.new('n:', CurlyBraceVariable.new([CurlyBracePart.new('__riml_splat_var_'), CurlyBraceVariable.new([CurlyBracePart.new(GetVariableNode.new('n:', '__riml_splat_idx'))])])),
|
551
|
-
CallNode.new(nil, 'get', [
|
552
|
-
GetVariableNode.new('n:', '__riml_splat_list'),
|
553
|
-
BinaryOperatorNode.new('-', [
|
554
|
-
GetVariableNode.new('n:', '__riml_splat_idx'),
|
555
|
-
NumberNode.new('1')
|
556
|
-
])
|
557
|
-
])
|
558
|
-
),
|
559
|
-
ExplicitCallNode.new(nil, 'add', [
|
560
|
-
GetVariableNode.new('n:', '__riml_splat_str_vars'),
|
561
|
-
BinaryOperatorNode.new('.', [StringNode.new('__riml_splat_var_', :s), GetVariableNode.new('n:', '__riml_splat_idx')])
|
562
|
-
]),
|
563
|
-
AssignNode.new('+=', GetVariableNode.new('n:', '__riml_splat_idx'), NumberNode.new('1'))
|
564
|
-
])
|
565
|
-
)
|
566
|
-
nodes << splat_list_init << splat_size << splat_string_vars_init <<
|
567
|
-
splat_list_idx_init << while_loop
|
568
|
-
establish_parents(nodes)
|
569
|
-
nodes
|
570
|
-
end
|
571
|
-
|
572
|
-
def splat_value
|
573
|
-
n = @splat_node
|
574
|
-
until DefNode === n || n.nil?
|
575
|
-
n = n.parent
|
576
|
-
end
|
577
|
-
var_str_without_star = @splat_node.value[1..-1]
|
578
|
-
var_without_star = GetVariableNode.new(nil, var_str_without_star)
|
579
|
-
return var_without_star if n.nil? || !n.splat || (n.splat != @splat_node.value)
|
580
|
-
GetVariableNode.new('a:', '000')
|
581
|
-
end
|
582
|
-
end
|
583
|
-
|
584
446
|
class SelfToObjArgumentInPrivateFunction < AST_Rewriter
|
585
447
|
def initialize(ast, classes, class_node)
|
586
448
|
super(ast, classes)
|
@@ -744,7 +606,7 @@ module Riml
|
|
744
606
|
# initialize method taking *splat parameter and call super with it
|
745
607
|
elsif class_node.superclass?
|
746
608
|
def_node = DefNode.new(
|
747
|
-
'!', nil, nil, "initialize", ['...'], nil, Nodes.new([SuperNode.new([SplatNode.new('
|
609
|
+
'!', nil, nil, "initialize", ['...'], nil, Nodes.new([SuperNode.new([SplatNode.new(GetVariableNode.new('a:', '000'))], false)])
|
748
610
|
)
|
749
611
|
else
|
750
612
|
def_node = DefNode.new(
|
@@ -851,7 +713,7 @@ module Riml
|
|
851
713
|
raise error
|
852
714
|
end
|
853
715
|
node_args = if node.arguments.empty? && !node.with_parens && superclass_function.splat
|
854
|
-
[SplatNode.new('
|
716
|
+
[SplatNode.new(GetVariableNode.new('a:', '000'))]
|
855
717
|
else
|
856
718
|
if @function_node.private_function?
|
857
719
|
node.arguments.unshift GetVariableNode.new(nil, @function_node.parameters.first)
|
@@ -863,9 +725,19 @@ module Riml
|
|
863
725
|
# private function, we have to add the explicit object (first
|
864
726
|
# parameter to the function we're in) to the splat arg
|
865
727
|
if @function_node.private_function?
|
866
|
-
if (splat_node = node_args.detect { |arg| SplatNode === arg })
|
867
|
-
|
868
|
-
|
728
|
+
if (splat_node = node_args.detect { |arg| SplatNode === arg })
|
729
|
+
splat_node.expression = WrapInParensNode.new(
|
730
|
+
BinaryOperatorNode.new(
|
731
|
+
'+',
|
732
|
+
[
|
733
|
+
ListNode.new([
|
734
|
+
GetVariableNode.new('a:', @function_node.parameters.first)
|
735
|
+
]),
|
736
|
+
GetVariableNode.new('a:', '000')
|
737
|
+
]
|
738
|
+
)
|
739
|
+
)
|
740
|
+
establish_parents(splat_node.expression)
|
869
741
|
end
|
870
742
|
# call s.ClassA_private_func(args)
|
871
743
|
call_node_name = superclass_func_name(superclass)
|
@@ -925,6 +797,174 @@ module Riml
|
|
925
797
|
end
|
926
798
|
end # ClassDefinitionToFunctions
|
927
799
|
|
800
|
+
# Rewrite constructs like:
|
801
|
+
#
|
802
|
+
# let animalObj = s:AnimalConstructor(*a:000)
|
803
|
+
#
|
804
|
+
# to:
|
805
|
+
#
|
806
|
+
# let __riml_splat_list = a:000
|
807
|
+
# let __riml_splat_size = len(__riml_splat_list)
|
808
|
+
# let __riml_splat_str_vars = []
|
809
|
+
# let __riml_splat_idx = 1
|
810
|
+
# while __riml_splat_idx <=# __riml_splat_size
|
811
|
+
# let __riml_splat_var_{__riml_splat_idx} = get(__riml_splat_list, __riml_splat_idx - 1)
|
812
|
+
# call add(__riml_splat_str_vars, __riml_splat_var_{__riml_splat_idx})
|
813
|
+
# let __riml_splat_idx += 1
|
814
|
+
# endwhile
|
815
|
+
# execute 'let s:animalObj = s:AnimalConstructor(' . join(__riml_splat_str_vars, ', ') . ')'
|
816
|
+
#
|
817
|
+
# Basically, mimic Ruby's approach to expanding lists to their
|
818
|
+
# constituent argument parts with '*' in calling context.
|
819
|
+
class SplatsToExecuteInCallingContext < AST_Rewriter
|
820
|
+
|
821
|
+
def match?(node)
|
822
|
+
if SplatNode === node && CallNode === node.parent
|
823
|
+
@splat_node = node
|
824
|
+
end
|
825
|
+
end
|
826
|
+
|
827
|
+
def replace(node)
|
828
|
+
construct_splat_str_vars_node = build_construct_splat_str_vars_node
|
829
|
+
call_node_args =
|
830
|
+
CallNode.new(
|
831
|
+
nil,
|
832
|
+
'join',
|
833
|
+
[
|
834
|
+
GetVariableNode.new(tmp_var_modifier.dup, '__riml_splat_str_vars'),
|
835
|
+
StringNode.new(', ', :s)
|
836
|
+
]
|
837
|
+
)
|
838
|
+
call_node = node.parent
|
839
|
+
node_to_execute = if AssignNode === call_node.parent
|
840
|
+
assign_node = call_node.parent
|
841
|
+
n = call_node
|
842
|
+
until DefNode === n || n.nil?
|
843
|
+
n = n.parent
|
844
|
+
end
|
845
|
+
# This is necessary because this node is getting put into a new
|
846
|
+
# compiler where it not wrapped in a function context, therefore
|
847
|
+
# variables will be script-local there unless their scope_modifier
|
848
|
+
# is set
|
849
|
+
if n && assign_node.lhs.scope_modifier.nil?
|
850
|
+
if global_scope?
|
851
|
+
assign_node.lhs.scope_modifier = 's:'
|
852
|
+
else
|
853
|
+
assign_node.lhs.scope_modifier = 'l:'
|
854
|
+
end
|
855
|
+
end
|
856
|
+
assign_node
|
857
|
+
else
|
858
|
+
call_node
|
859
|
+
end
|
860
|
+
call_node.arguments.clear
|
861
|
+
compiler = Compiler.new
|
862
|
+
# have to dup node_to_execute here because, if not, its parent will
|
863
|
+
# get reset during this next compilation step
|
864
|
+
output = compiler.compile(Nodes.new([node_to_execute.clone]))
|
865
|
+
execute_string_node = StringNode.new(output.chomp[0..-2], :s)
|
866
|
+
if node_to_execute.instance_of?(CallNode)
|
867
|
+
execute_string_node.value.insert(0, 'call ')
|
868
|
+
end
|
869
|
+
execute_arg = BinaryOperatorNode.new(
|
870
|
+
'.',
|
871
|
+
[
|
872
|
+
execute_string_node,
|
873
|
+
BinaryOperatorNode.new(
|
874
|
+
'.',
|
875
|
+
[
|
876
|
+
call_node_args,
|
877
|
+
StringNode.new(')', :s)
|
878
|
+
]
|
879
|
+
)
|
880
|
+
]
|
881
|
+
)
|
882
|
+
execute_node = CallNode.new(nil, 'execute', [execute_arg])
|
883
|
+
establish_parents(execute_node)
|
884
|
+
node.remove
|
885
|
+
node_to_execute.replace_with(construct_splat_str_vars_node)
|
886
|
+
execute_node.parent = construct_splat_str_vars_node.parent
|
887
|
+
construct_splat_str_vars_node.parent.insert_after(construct_splat_str_vars_node, execute_node)
|
888
|
+
end
|
889
|
+
|
890
|
+
private
|
891
|
+
|
892
|
+
def build_construct_splat_str_vars_node
|
893
|
+
nodes = Nodes.new([])
|
894
|
+
splat_list_init = AssignNode.new(
|
895
|
+
'=',
|
896
|
+
GetVariableNode.new(tmp_var_modifier.dup, '__riml_splat_list'),
|
897
|
+
@splat_node.expression
|
898
|
+
)
|
899
|
+
splat_size = AssignNode.new(
|
900
|
+
'=',
|
901
|
+
GetVariableNode.new(tmp_var_modifier.dup, '__riml_splat_size'),
|
902
|
+
CallNode.new(nil, 'len', [GetVariableNode.new(tmp_var_modifier.dup, '__riml_splat_list')])
|
903
|
+
)
|
904
|
+
splat_string_vars_init = AssignNode.new(
|
905
|
+
'=',
|
906
|
+
GetVariableNode.new(tmp_var_modifier.dup, '__riml_splat_str_vars'),
|
907
|
+
ListNode.new([])
|
908
|
+
)
|
909
|
+
splat_list_idx_init = AssignNode.new(
|
910
|
+
'=',
|
911
|
+
GetVariableNode.new(tmp_var_modifier.dup, '__riml_splat_idx'),
|
912
|
+
NumberNode.new('1')
|
913
|
+
)
|
914
|
+
while_loop = WhileNode.new(
|
915
|
+
# condition
|
916
|
+
BinaryOperatorNode.new('<=', [GetVariableNode.new(tmp_var_modifier.dup, '__riml_splat_idx'), GetVariableNode.new(tmp_var_modifier.dup, '__riml_splat_size')]),
|
917
|
+
# body
|
918
|
+
Nodes.new([
|
919
|
+
AssignNode.new(
|
920
|
+
'=',
|
921
|
+
GetCurlyBraceNameNode.new(tmp_var_modifier.dup, CurlyBraceVariable.new([CurlyBracePart.new('__riml_splat_var_'), CurlyBraceVariable.new([CurlyBracePart.new(GetVariableNode.new(tmp_var_modifier.dup, '__riml_splat_idx'))])])),
|
922
|
+
CallNode.new(nil, 'get', [
|
923
|
+
GetVariableNode.new(tmp_var_modifier.dup, '__riml_splat_list'),
|
924
|
+
BinaryOperatorNode.new('-', [
|
925
|
+
GetVariableNode.new(tmp_var_modifier.dup, '__riml_splat_idx'),
|
926
|
+
NumberNode.new('1')
|
927
|
+
])
|
928
|
+
])
|
929
|
+
),
|
930
|
+
ExplicitCallNode.new(nil, 'add', [
|
931
|
+
GetVariableNode.new(tmp_var_modifier.dup, '__riml_splat_str_vars'),
|
932
|
+
BinaryOperatorNode.new('.', [StringNode.new('__riml_splat_var_', :s), GetVariableNode.new(tmp_var_modifier.dup, '__riml_splat_idx')])
|
933
|
+
]),
|
934
|
+
AssignNode.new('+=', GetVariableNode.new(tmp_var_modifier.dup, '__riml_splat_idx'), NumberNode.new('1'))
|
935
|
+
])
|
936
|
+
)
|
937
|
+
nodes << splat_list_init << splat_size << splat_string_vars_init <<
|
938
|
+
splat_list_idx_init << while_loop
|
939
|
+
establish_parents(nodes)
|
940
|
+
nodes
|
941
|
+
end
|
942
|
+
|
943
|
+
def tmp_var_modifier
|
944
|
+
@tmp_var_modifier ||= begin
|
945
|
+
n = @splat_node
|
946
|
+
while n != nil && !(DefNode === n)
|
947
|
+
n = n.parent
|
948
|
+
end
|
949
|
+
# n is either `nil` or DefNode
|
950
|
+
if n.nil?
|
951
|
+
's:'
|
952
|
+
else
|
953
|
+
'n:'
|
954
|
+
end
|
955
|
+
end
|
956
|
+
end
|
957
|
+
|
958
|
+
def global_scope?
|
959
|
+
tmp_var_modifier == 's:'
|
960
|
+
end
|
961
|
+
|
962
|
+
def local_scope?
|
963
|
+
not global_scope?
|
964
|
+
end
|
965
|
+
end
|
966
|
+
|
967
|
+
|
928
968
|
class ObjectInstantiationToCall < AST_Rewriter
|
929
969
|
def match?(node)
|
930
970
|
ObjectInstantiationNode === node
|
@@ -1011,7 +1051,7 @@ module Riml
|
|
1011
1051
|
def replace(node)
|
1012
1052
|
orig_assign = node.dup
|
1013
1053
|
assigns = []
|
1014
|
-
while assign_node = (node
|
1054
|
+
while assign_node = (AssignNode === node && node.rhs)
|
1015
1055
|
assigns.unshift([node.lhs, node.rhs])
|
1016
1056
|
node = assign_node
|
1017
1057
|
end
|