riml 0.2.1 → 0.2.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.
- data/README.md +276 -20
- data/bin/riml +1 -1
- data/lib/ast_rewriter.rb +61 -2
- data/lib/compiler.rb +18 -7
- data/lib/errors.rb +1 -0
- data/lib/lexer.rb +7 -10
- data/lib/nodes.rb +34 -7
- data/lib/repl.rb +0 -5
- data/version.rb +2 -2
- metadata +2 -2
data/README.md
CHANGED
@@ -4,12 +4,13 @@ Riml, a relaxed version of Vimscript
|
|
4
4
|
====================================
|
5
5
|
|
6
6
|
Riml aims to be a superset of VimL that includes some nice features that I
|
7
|
-
enjoy in other scripting languages
|
8
|
-
heredocs, default case-sensitive string comparison
|
9
|
-
programmers take for granted. Also, Riml takes
|
10
|
-
some syntactic sugar for lots of VimL constructs.
|
11
|
-
are compiled
|
12
|
-
and the right side is the
|
7
|
+
enjoy in other scripting languages: classes, string interpolation,
|
8
|
+
heredocs, default case-sensitive string comparison, default parameters
|
9
|
+
in functions, and other things programmers tend to take for granted. Also, Riml takes
|
10
|
+
some liberties and provides some syntactic sugar for lots of VimL constructs.
|
11
|
+
To see how Riml constructs are compiled to VimL, just take a look at this README.
|
12
|
+
The left side is Riml, and the right side is the VimL after compilation.
|
13
|
+
|
13
14
|
Variables
|
14
15
|
---------
|
15
16
|
|
@@ -19,17 +20,30 @@ Variables
|
|
19
20
|
count += 1 let s:count += 1
|
20
21
|
end endwhile
|
21
22
|
|
22
|
-
If you don't specify a scope modifier, it's script local
|
23
|
-
global
|
23
|
+
If you don't specify a scope modifier (or namespace in Vimspeak), it's script local (s:)
|
24
|
+
by default in the global scope. Within a function, variables without scope modifiers are plain
|
24
25
|
old local variables.
|
25
26
|
|
26
27
|
###globally
|
27
28
|
|
28
29
|
a = 3 let s:a = 3
|
29
30
|
|
30
|
-
###locally
|
31
|
+
###locally (within function or for loop)
|
32
|
+
|
33
|
+
def exampleFunc(msg) function! s:exampleFunc(msg)
|
34
|
+
a = 3 let a = 3
|
35
|
+
echo msg echo a:msg
|
36
|
+
end endfunction
|
37
|
+
|
38
|
+
for i in expr() for i in s:expr()
|
39
|
+
echo i echo i
|
40
|
+
end endfor
|
31
41
|
|
32
|
-
|
42
|
+
Notice that within a function, it's unnecessary to prefix argument variables
|
43
|
+
with 'a:'. This is, of course, unless we shadow the argument variable by creating
|
44
|
+
our own local variable called 'msg'. In that case, we'd have to refer to the argument variable
|
45
|
+
as 'a:msg' explicitly. Shadowing variables in Riml is considered bad practice, as it's
|
46
|
+
much easier to just come up with unique variable names across a scope.
|
33
47
|
|
34
48
|
###Freeing memory
|
35
49
|
|
@@ -43,22 +57,82 @@ old local variables.
|
|
43
57
|
callcount += 1 let s:callcount += 1
|
44
58
|
echo "called #{callcount} times" echo "called " . s:callcount . " times"
|
45
59
|
|
46
|
-
|
47
|
-
|
60
|
+
Notice in the last line of Riml there's string interpolation. This works
|
61
|
+
in double-quoted strings and heredocs, which we'll encounter later.
|
62
|
+
|
63
|
+
In Riml, you can choose to end any block with 'end', or with whatever you used
|
64
|
+
to do in Vimscript ('endif', 'endfunction', etc...). Also, 'if' and 'unless' can
|
65
|
+
now be used as statement modifiers:
|
66
|
+
|
67
|
+
callcount = 0 unless s:callcount?
|
68
|
+
callcount += 1
|
69
|
+
echo "called #{callcount} times"
|
48
70
|
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
71
|
+
Here, the compiled output is the same as the previous example's. Both 'if' and
|
72
|
+
'unless' can be used this way.
|
73
|
+
|
74
|
+
Operators And Case Sensitivity
|
75
|
+
------------------------------
|
76
|
+
|
77
|
+
if "hi" == greeting if "hi" ==# s:greeting
|
78
|
+
echo greeting echo s:greeting
|
79
|
+
end end
|
80
|
+
|
81
|
+
Comparisons compile to case-sensitive by default. To get case-insensitive
|
82
|
+
comparisons, you have to explicitly use the form ending in '?' (ex: '==?').
|
83
|
+
The only operators that don't add a '#' even though the forms exist are the
|
84
|
+
'is' and 'isnot' operators. This is because 'is' is much different from its
|
85
|
+
cousin 'is#', and the same is true of 'isnot'.
|
54
86
|
|
55
87
|
Heredocs
|
56
88
|
--------
|
57
89
|
|
58
|
-
msg = <<EOS let s:msg = "a vim heredoc! " . s:cryForJoy() . "!\n"
|
90
|
+
msg = <<EOS let s:msg = "a vim heredoc! " . s:cryForJoy() . "!\nHooray!\n"
|
59
91
|
A vim heredoc! #{cryForJoy()}!
|
92
|
+
Hooray!
|
60
93
|
EOS
|
61
94
|
|
95
|
+
Riml heredocs must have the ending pattern start at the beginning
|
96
|
+
of the line. Interpolating expressions is allowed in heredocs. Compiled
|
97
|
+
heredocs always end with a newline.
|
98
|
+
|
99
|
+
Functions
|
100
|
+
---------
|
101
|
+
|
102
|
+
def fillSearchPat function! s:fillSearchPat()
|
103
|
+
@/ = getSearchPat() let @/ = s:getSearchPat()
|
104
|
+
return @/ return @/
|
105
|
+
end endfunction
|
106
|
+
|
107
|
+
When defining a function with no parameters, the parens after the function name are optional.
|
108
|
+
|
109
|
+
Functions are by default prepended by 's:' unless explicitly prepended with a
|
110
|
+
different scope modifier. Of course, you can use the old form ('function! Name()') for defining
|
111
|
+
functions if you want, as Riml aims to be a superset of VimL. There are a few exceptions
|
112
|
+
where Riml and VimL aren't compatible, and these differences are explained in
|
113
|
+
the section 'Incompatibilities with VimL'.
|
114
|
+
|
115
|
+
###Default Arguments
|
116
|
+
|
117
|
+
def fillSearchPat(pat = getDefaultSearchPat()) function! s:fillSearchPat(...)
|
118
|
+
@/ = pat if get(a:000, 0, 'rimldefault') !=# 'rimldefault'
|
119
|
+
return @/ let pat = remove(a:000, 0)
|
120
|
+
end else
|
121
|
+
let pat = s:getDefaultSearchPat()
|
122
|
+
endif
|
123
|
+
@/ = pat
|
124
|
+
return @/
|
125
|
+
endfunction
|
126
|
+
|
127
|
+
|
128
|
+
Default arguments must be the last arguments given to a function, but there can be more
|
129
|
+
than one default argument. Also, a splat argument (... or \*argName) can come after default argument(s).
|
130
|
+
Splats will be explained in the next section.
|
131
|
+
|
132
|
+
We can now call the function 'fillSearchPat' without any arguments and it will use the default
|
133
|
+
argument. Also, if we pass the string 'rimldefault', it will use the default argument as well. This
|
134
|
+
is useful if a function has many default arguments.
|
135
|
+
|
62
136
|
Classes
|
63
137
|
-------
|
64
138
|
|
@@ -82,7 +156,17 @@ Classes
|
|
82
156
|
return self.otherData
|
83
157
|
endfunction
|
84
158
|
|
85
|
-
|
159
|
+
Classes can only be defined once, and cannot be reopened. Public member
|
160
|
+
functions are defined with 'defm'. If you want to create a non-public function
|
161
|
+
inside a class, use 'def'. To create an instance of this class, simply:
|
162
|
+
|
163
|
+
obj = new MyClass('someData', 'someOtherData')
|
164
|
+
|
165
|
+
In this basic example of a class, we see a \*splat variable. This is just a
|
166
|
+
convenient way to refer to 'a:000' in a function. Splat variables are optional
|
167
|
+
parameters and get compiled to '...'.
|
168
|
+
|
169
|
+
###Class Inheritance
|
86
170
|
|
87
171
|
class Translation function! g:TranslationConstructor(input)
|
88
172
|
def initialize(input) let translationObj = {}
|
@@ -108,6 +192,178 @@ Classes
|
|
108
192
|
|
109
193
|
let s:translation = g:TranslationConstructor("Bonjour!")
|
110
194
|
call s:translation.translate()
|
195
|
+
=> "Hello!"
|
196
|
+
|
197
|
+
Classes that inherit must have their superclass defined before inheritance takes place. In
|
198
|
+
this example, 'Translation' is defined first, which is legal. Since 'Translation'
|
199
|
+
has an initialize function and 'FrenchToEnglishTranslation' (referred to now as FET)
|
200
|
+
doesn't, FET instances use the initialize function from 'Translation', and new
|
201
|
+
instances must be provided with an 'input' argument on creation. Basically, if
|
202
|
+
a class doesn't provide an initialize function, it uses its superclass's.
|
203
|
+
|
204
|
+
If you look at the last line of Riml in the previous example, you'll see that
|
205
|
+
it doesn't use Vimscript's builtin 'call' function for calling the 'translate'
|
206
|
+
method on the translation object. Riml can figure out when 'call' is necessary,
|
207
|
+
and will add it to the compiled Vimscript.
|
208
|
+
|
209
|
+
###Using 'super'
|
210
|
+
|
211
|
+
class Car function! g:CarConstructor(make, model, color)
|
212
|
+
def initialize(make, model, color) let carObj = {}
|
213
|
+
self.make = make let carObj.make = a:make
|
214
|
+
self.model = model let carObj.model = a:model
|
215
|
+
self.color = color let carObj.color = a:color
|
216
|
+
end endfunction
|
217
|
+
end
|
218
|
+
|
219
|
+
class HotRod < Car function! g:HotRodConstructor(make, model, color, topSpeed)
|
220
|
+
def initialize(make, model, color, topSpeed) let hotRodObj = {}
|
221
|
+
self.topSpeed = topSpeed let hotRodObj.topSpeed = a:topSpeed
|
222
|
+
super(make, model, color) let carObj = g:CarConstructor(a:make, a:model, a:color)
|
223
|
+
end call extend(hotRodObj, carObj)
|
224
|
+
let hotRodObj.drive = function('g:HotRod_drive')
|
225
|
+
defm drive return hotRodObj
|
226
|
+
if self.topSpeed > 140 endfunction
|
227
|
+
echo "Ahhhhhhh!"
|
228
|
+
else function! g:HotRod_drive() dict
|
229
|
+
echo "Nice" if self.topSpeed ># 140
|
230
|
+
end echo "Ahhhhhhh!"
|
231
|
+
end else
|
232
|
+
end echo "Nice"
|
233
|
+
endif
|
234
|
+
newCar = new HotRod("chevy", "mustang", "red", 160) endfunction
|
235
|
+
newCar.drive()
|
236
|
+
let s:newCar = g:HotRodConstructor("chevy", "mustang", "red", 160)
|
237
|
+
call s:newCar.drive()
|
238
|
+
|
239
|
+
Use of 'super' is legal only within subclasses. If arguments are given, these arguments are sent
|
240
|
+
to the superclass's function of the same name. If no arguments are given and parentheses are omitted,
|
241
|
+
('super' as opposed to 'super()'), every single argument is passed to the superclass's function.
|
242
|
+
This mirrors Ruby's approach.
|
243
|
+
|
244
|
+
Super can be called from an initialize (constructor) function, a public member function
|
245
|
+
('defm'), or a non-public function ('def'). An error is given during compilation if no
|
246
|
+
superclass function with that name is defined.
|
247
|
+
|
248
|
+
Compiling Riml
|
249
|
+
--------------
|
250
|
+
|
251
|
+
To compile a riml file named 'example.riml' that resides in the current
|
252
|
+
directory:
|
253
|
+
|
254
|
+
$ riml -c example.riml
|
255
|
+
|
256
|
+
This will create a new VimL file named 'example.vim'.
|
257
|
+
|
258
|
+
###riml\_source
|
259
|
+
|
260
|
+
It's useful to split a project into many files. For example, imagine we're creating a plugin
|
261
|
+
called 'awesome' that does something totally awesome, and it relies on another library
|
262
|
+
we wrote called 'my\_framework' that's also written in Riml.
|
263
|
+
|
264
|
+
Somewhere in 'awesome.riml', we have the line:
|
265
|
+
|
266
|
+
riml_source 'my_framework.riml'
|
267
|
+
|
268
|
+
This will compile the file 'my\_framework.riml' and create a VimL file named
|
269
|
+
'my\_framework.vim'.
|
270
|
+
|
271
|
+
In 'awesome.riml', that line will be compiled to:
|
272
|
+
|
273
|
+
source 'my_framework.vim'
|
274
|
+
|
275
|
+
This process is recursive, meaning that if 'my\_framework.riml' riml\_source's other
|
276
|
+
files, then those files will be compiled as well.
|
277
|
+
|
278
|
+
In 'awesome.riml', all the classes that were available by compiling 'my\_framework.riml'
|
279
|
+
are now available to it, so we can now subclass classes that were defined either
|
280
|
+
in 'my\_framework.riml' itself, or any files that it riml\_source'd either directly
|
281
|
+
or indirecty.
|
282
|
+
|
283
|
+
|
284
|
+
###riml\_include
|
285
|
+
|
286
|
+
Sometimes it's useful to have many files in development, but to include a file's contents
|
287
|
+
into another file during the build process. This is much like the C preprocessor's #include
|
288
|
+
directive.
|
289
|
+
|
290
|
+
To include a file named 'my\_lib.riml':
|
291
|
+
|
292
|
+
riml_include 'my_lib.riml'
|
293
|
+
|
294
|
+
This compiles the file and includes its content in place of the riml\_include directive itself.
|
295
|
+
Much like riml\_sourcing, the process is recursive. If 'my\_lib.riml' includes files, these files
|
296
|
+
are also compiled and will be part of the inclusion. Note that riml\_include does not create a
|
297
|
+
new file like riml\_source does.
|
298
|
+
|
299
|
+
Incompatibilities with VimL
|
300
|
+
---------------------------
|
301
|
+
|
302
|
+
Riml aims to be a superset of VimL, therefore any legal VimL should be legal
|
303
|
+
Riml as well. Unfortunately, this is not 100% possible as Vim is an old and
|
304
|
+
cryptic beast, and trying to create grammar for every possible Vim construct
|
305
|
+
would be a nightmare. In practice, however, when I've transformed plain old vim plugins
|
306
|
+
to be Riml-compatible, only a couple of ':' needed to be placed in strategic locations
|
307
|
+
for it to be valid Riml. This is explained below.
|
308
|
+
|
309
|
+
###Ex-literals
|
310
|
+
|
311
|
+
Fortunately, there are some pretty simple rules to follow to get valid Riml.
|
312
|
+
|
313
|
+
When doing anything with autocommands, normal, commands, set, ranges, etc... simply do:
|
314
|
+
|
315
|
+
:autocmd BufEnter * blahblah...
|
316
|
+
|
317
|
+
That is, prepend ':' to the line. When a line starts with ':', it passes directly
|
318
|
+
through the compiler and no transformations occur. This includes string interpolation,
|
319
|
+
which is ignored as well.
|
320
|
+
|
321
|
+
In Riml, when a line starts with ':' it's called an ex-literal.
|
322
|
+
|
323
|
+
Ex-literals are necessary for the following:
|
324
|
+
|
325
|
+
* autocommands
|
326
|
+
* command definitions
|
327
|
+
* set
|
328
|
+
* ranges (:h cmdline-ranges)
|
329
|
+
* normal (:h normal)
|
330
|
+
* mappings
|
331
|
+
* augroups
|
332
|
+
|
333
|
+
Basically anything that isn't a class, number, string, list, dict, function call,
|
334
|
+
function definition, loop or if construct, variable definition or unlet, etc...
|
335
|
+
needs to be an ex-literal in Riml.
|
336
|
+
|
337
|
+
Note that, like 'echo' which isn't a builtin function (:h functions) but is still legal
|
338
|
+
Riml, 'execute' is also allowed as it takes a string. This is extremely useful, as we can
|
339
|
+
now use execute with a string that allows interpolation.
|
340
|
+
|
341
|
+
Imagine having to write a grammar rule for the following:
|
342
|
+
|
343
|
+
set statusline+=[%{strlen(&fenc)?&fenc:'none'}, " File encoding
|
344
|
+
|
345
|
+
Since there's no string after the '+=', it makes it very hard.
|
346
|
+
So when the compiler can't parse a file correctly, prepend those lines with
|
347
|
+
':' and all should be well.
|
348
|
+
|
349
|
+
###Abbreviations
|
350
|
+
|
351
|
+
In VimL, there are abbreviations for everything; even "keywords" like 'function' can be
|
352
|
+
abbreviated. In Riml, abbreviations are not allowed. This makes Riml much easier to read
|
353
|
+
and understand.
|
354
|
+
|
355
|
+
Everything Else That Works
|
356
|
+
--------------------------
|
111
357
|
|
358
|
+
Everything not mentioned above as illegal is legal Riml. Here's a short (non-comprehensive) list
|
359
|
+
of constructs which are legal Riml but not mentioned in any of the examples:
|
112
360
|
|
113
|
-
|
361
|
+
* try/catch/finally blocks and throw
|
362
|
+
* curly-brace variable and function names
|
363
|
+
* while (and until) loops
|
364
|
+
* ternary operators
|
365
|
+
* exponents
|
366
|
+
* line continuations
|
367
|
+
* autoloadable variables and functions (:h autoload)
|
368
|
+
* let unpack (:h let-unpack)
|
369
|
+
* Much more!
|
data/bin/riml
CHANGED
@@ -38,7 +38,7 @@ module Riml
|
|
38
38
|
append_filenames_to_list_if_all_exist(options.check_syntax_files, *filenames)
|
39
39
|
end
|
40
40
|
|
41
|
-
opts.on("-t", "--source-path PATH", "Path riml uses for `riml_source` and `riml_include` to find files. Defaults to pwd.") do |path|
|
41
|
+
opts.on("-t", "--source-path PATH", "Colon-separated Path riml uses for `riml_source` and `riml_include` to find files. Defaults to pwd.") do |path|
|
42
42
|
if Dir.exists?(path)
|
43
43
|
Riml.source_path = path
|
44
44
|
else
|
data/lib/ast_rewriter.rb
CHANGED
@@ -150,9 +150,10 @@ module Riml
|
|
150
150
|
AssignNode.new('=', GetVariableNode.new(nil, dict_name), DictionaryNode.new({}))
|
151
151
|
)
|
152
152
|
|
153
|
-
|
153
|
+
InitializeSuperToObjectExtension.new(constructor, classes, node).rewrite_on_match
|
154
154
|
ExtendObjectWithMethods.new(node, classes).rewrite_on_match
|
155
155
|
SelfToDictName.new(dict_name).rewrite_on_match(constructor)
|
156
|
+
SuperToSuperclassFunction.new(node, classes).rewrite_on_match
|
156
157
|
|
157
158
|
constructor.expressions.push(
|
158
159
|
ReturnNode.new(GetVariableNode.new(nil, dict_name))
|
@@ -241,7 +242,7 @@ module Riml
|
|
241
242
|
end
|
242
243
|
end
|
243
244
|
|
244
|
-
class
|
245
|
+
class InitializeSuperToObjectExtension < AST_Rewriter
|
245
246
|
attr_reader :class_node
|
246
247
|
def initialize(constructor, classes, class_node)
|
247
248
|
super(constructor, classes)
|
@@ -291,6 +292,64 @@ module Riml
|
|
291
292
|
false
|
292
293
|
end
|
293
294
|
end
|
295
|
+
|
296
|
+
# rewrites calls to 'super' in non-initialize function
|
297
|
+
class SuperToSuperclassFunction < AST_Rewriter
|
298
|
+
def match?(node)
|
299
|
+
return false unless SuperNode === node
|
300
|
+
n = node
|
301
|
+
n = n.parent until DefNode === n || n.nil?
|
302
|
+
return false if n.nil? || ast.constructor == n
|
303
|
+
@function_node = n
|
304
|
+
end
|
305
|
+
|
306
|
+
def replace(node)
|
307
|
+
func_scope = @function_node.scope_modifier
|
308
|
+
superclass = classes[ast.superclass_name]
|
309
|
+
while superclass && !superclass.has_function?(func_scope, superclass_func_name(superclass)) && superclass.superclass?
|
310
|
+
superclass = classes[superclass.superclass_name]
|
311
|
+
end
|
312
|
+
if superclass.nil? || !superclass.has_function?(func_scope, superclass_func_name(superclass))
|
313
|
+
raise Riml::UserFunctionNotFoundError,
|
314
|
+
"super was called in class #{ast.name} in " \
|
315
|
+
"function #{@function_node.original_name}, but there are no " \
|
316
|
+
"functions with this name in that class's superclass hierarchy."
|
317
|
+
end
|
318
|
+
call_node = CallNode.new(
|
319
|
+
nil,
|
320
|
+
DictGetDotNode.new(
|
321
|
+
GetVariableNode.new(nil, 'self'),
|
322
|
+
[superclass_func_name(superclass)]
|
323
|
+
),
|
324
|
+
node.arguments
|
325
|
+
)
|
326
|
+
|
327
|
+
node.replace_with(call_node)
|
328
|
+
add_superclass_func_ref_to_constructor(superclass)
|
329
|
+
reestablish_parents(@function_node)
|
330
|
+
end
|
331
|
+
|
332
|
+
def superclass_func_name(superclass)
|
333
|
+
"#{superclass.name}_#{@function_node.original_name}"
|
334
|
+
end
|
335
|
+
|
336
|
+
def add_superclass_func_ref_to_constructor(superclass)
|
337
|
+
super_func_name = superclass_func_name(superclass)
|
338
|
+
assign_node = AssignNode.new('=',
|
339
|
+
DictGetDotNode.new(
|
340
|
+
GetVariableNode.new(nil, ast.constructor_obj_name),
|
341
|
+
[super_func_name]
|
342
|
+
),
|
343
|
+
CallNode.new(
|
344
|
+
nil,
|
345
|
+
'function',
|
346
|
+
[StringNode.new("g:#{super_func_name}", :s)]
|
347
|
+
)
|
348
|
+
)
|
349
|
+
ast.constructor.expressions << assign_node
|
350
|
+
reestablish_parents(ast.constructor)
|
351
|
+
end
|
352
|
+
end
|
294
353
|
end # ClassDefinitionToFunctions
|
295
354
|
|
296
355
|
class ObjectInstantiationToCall < AST_Rewriter
|
data/lib/compiler.rb
CHANGED
@@ -242,10 +242,15 @@ module Riml
|
|
242
242
|
private
|
243
243
|
def scope_modifier_for_node(node)
|
244
244
|
if node.scope
|
245
|
-
|
246
|
-
|
245
|
+
if node.scope.function && DefNode === node && !node.defined_on_dictionary?
|
246
|
+
return "s:"
|
247
|
+
elsif node.scope.argument_variable_names.include?(node.name)
|
248
|
+
return "a:"
|
249
|
+
elsif !node.is_a?(CallNode)
|
250
|
+
return ""
|
251
|
+
end
|
247
252
|
end
|
248
|
-
return "" if
|
253
|
+
return "" if node.respond_to?(:autoload?) && node.autoload?
|
249
254
|
"s:"
|
250
255
|
end
|
251
256
|
end
|
@@ -376,7 +381,11 @@ module Riml
|
|
376
381
|
|
377
382
|
class DefNodeVisitor < ScopedVisitor
|
378
383
|
def visit(node)
|
379
|
-
|
384
|
+
options = {}
|
385
|
+
if node.nested_function?
|
386
|
+
options[:nested_function] = true
|
387
|
+
end
|
388
|
+
setup_local_scope_for_descendants(node, options)
|
380
389
|
super
|
381
390
|
end
|
382
391
|
|
@@ -406,8 +415,9 @@ module Riml
|
|
406
415
|
end
|
407
416
|
|
408
417
|
private
|
409
|
-
def setup_local_scope_for_descendants(node)
|
410
|
-
|
418
|
+
def setup_local_scope_for_descendants(node, options)
|
419
|
+
options.merge!(:scope => node.to_scope)
|
420
|
+
node.expressions.accept(EstablishScopeVisitor.new(options))
|
411
421
|
end
|
412
422
|
|
413
423
|
def process_parameters!(node)
|
@@ -430,6 +440,7 @@ module Riml
|
|
430
440
|
class EstablishScopeVisitor < DrillDownVisitor
|
431
441
|
def initialize(options)
|
432
442
|
@scope = options[:scope]
|
443
|
+
@nested_function = options[:nested_function]
|
433
444
|
end
|
434
445
|
|
435
446
|
def visit(node)
|
@@ -437,7 +448,7 @@ module Riml
|
|
437
448
|
end
|
438
449
|
|
439
450
|
def establish_scope(node)
|
440
|
-
if node.scope
|
451
|
+
if node.scope && !@nested_function
|
441
452
|
node.scope = node.scope.merge @scope
|
442
453
|
else
|
443
454
|
node.scope = @scope
|
data/lib/errors.rb
CHANGED
@@ -10,6 +10,7 @@ module Riml
|
|
10
10
|
IncludeNotTopLevel = Class.new(RimlError)
|
11
11
|
# bad user arguments to Riml functions
|
12
12
|
UserArgumentError = Class.new(RimlError)
|
13
|
+
UserFunctionNotFoundError = Class.new(RimlError)
|
13
14
|
|
14
15
|
ClassNotFound = Class.new(RimlError)
|
15
16
|
ClassRedefinitionError = Class.new(RimlError)
|
data/lib/lexer.rb
CHANGED
@@ -33,7 +33,6 @@ module Riml
|
|
33
33
|
@indent_pending = false
|
34
34
|
@dedent_pending = false
|
35
35
|
@one_line_conditional_end_pending = false
|
36
|
-
@splat_allowed = false
|
37
36
|
@in_function_declaration = false
|
38
37
|
end
|
39
38
|
|
@@ -149,9 +148,7 @@ module Riml
|
|
149
148
|
@in_function_declaration = false unless DEFINE_KEYWORDS.include?(identifier) && @token_buf.size == 1
|
150
149
|
end
|
151
150
|
elsif splat = chunk[/\A(\.{3}|\*[a-zA-Z_]\w*)/]
|
152
|
-
raise SyntaxError, "unexpected splat, has to be enclosed in parentheses" unless @splat_allowed
|
153
151
|
@token_buf << [:SPLAT, splat]
|
154
|
-
@splat_allowed = false
|
155
152
|
@i += splat.size
|
156
153
|
# integer (octal)
|
157
154
|
elsif octal = chunk[/\A0[0-7]+/]
|
@@ -201,13 +198,13 @@ module Riml
|
|
201
198
|
pattern = $1
|
202
199
|
@i += heredoc_pattern.size
|
203
200
|
new_chunk = get_new_chunk
|
204
|
-
heredoc_string = new_chunk[%r|(.+?\r?\n)(#{Regexp.escape(pattern)})
|
201
|
+
heredoc_string = new_chunk[%r|(.+?\r?\n)(#{Regexp.escape(pattern)})|m, 1]
|
205
202
|
@i += heredoc_string.size + pattern.size
|
206
203
|
if ('"' + heredoc_string + '"') =~ INTERPOLATION_REGEX
|
207
204
|
parts = heredoc_string.split(INTERPOLATION_SPLIT_REGEX)
|
208
205
|
handle_interpolation(*parts)
|
209
206
|
else
|
210
|
-
@token_buf << [:STRING_D,
|
207
|
+
@token_buf << [:STRING_D, escape_chars!(heredoc_string)]
|
211
208
|
end
|
212
209
|
@lineno += heredoc_string.each_line.to_a.size
|
213
210
|
# operators of more than 1 char
|
@@ -227,8 +224,6 @@ module Riml
|
|
227
224
|
else
|
228
225
|
@token_buf << [value, value]
|
229
226
|
end
|
230
|
-
@splat_allowed = true if value == '('
|
231
|
-
@splat_allowed = false if value == ')'
|
232
227
|
@i += 1
|
233
228
|
if value == ']' || value == ')' && chunk[1, 1] == '.'
|
234
229
|
parse_dict_vals!
|
@@ -287,15 +282,17 @@ module Riml
|
|
287
282
|
if part[0..1] == '#{' && part[-1] == '}'
|
288
283
|
@token_buf.concat tokenize_without_moving_pos(part[2...-1])
|
289
284
|
else
|
290
|
-
@token_buf << [:STRING_D,
|
285
|
+
@token_buf << [:STRING_D, escape_chars!(part)]
|
291
286
|
end
|
292
287
|
# string-concatenate all the parts unless this is the last part
|
293
288
|
@token_buf << ['.', '.'] unless parts[i + 1].nil?
|
294
289
|
end
|
295
290
|
end
|
296
291
|
|
297
|
-
def
|
298
|
-
string.gsub(/"/, '\"')
|
292
|
+
def escape_chars!(string)
|
293
|
+
string.gsub!(/"/, '\"')
|
294
|
+
string.gsub!(/\n/, "\\n")
|
295
|
+
string
|
299
296
|
end
|
300
297
|
|
301
298
|
def tokenize_without_moving_pos(code)
|
data/lib/nodes.rb
CHANGED
@@ -323,7 +323,7 @@ class RimlCommandNode < CallNode
|
|
323
323
|
def initialize(*)
|
324
324
|
super
|
325
325
|
if arguments.empty? || !arguments.all? { |arg| arg.is_a?(StringNode) }
|
326
|
-
raise Riml::UserArgumentError, "#{name.inspect} error: must pass string (name of file)"
|
326
|
+
raise Riml::UserArgumentError, "#{name.inspect} error: must pass string(s) (name of file(s))"
|
327
327
|
end
|
328
328
|
end
|
329
329
|
|
@@ -500,24 +500,38 @@ class DefNode < Struct.new(:bang, :scope_modifier, :name, :parameters, :keyword,
|
|
500
500
|
include FullyNameable
|
501
501
|
include Walkable
|
502
502
|
|
503
|
-
attr_accessor :original_name
|
504
|
-
|
505
503
|
def initialize(*args)
|
506
504
|
super
|
507
505
|
# max number of arguments in viml
|
508
506
|
if parameters.reject(&DEFAULT_PARAMS).size > 20
|
509
507
|
raise Riml::UserArgumentError, "can't have more than 20 parameters for #{full_name}"
|
510
508
|
end
|
509
|
+
expressions.nodes.select { |node| DefNode === node}.each do |nested_func|
|
510
|
+
nested_func.nested_within.unshift(self)
|
511
|
+
end
|
511
512
|
end
|
512
513
|
|
513
514
|
SPLAT = lambda {|arg| arg == Riml::Constants::SPLAT_LITERAL || arg[0] == "*"}
|
514
515
|
DEFAULT_PARAMS = lambda {|p| DefaultParamNode === p}
|
515
516
|
|
517
|
+
def original_name
|
518
|
+
@original_name ||= name
|
519
|
+
end
|
520
|
+
attr_writer :original_name
|
521
|
+
|
516
522
|
# ["arg1", "arg2"}
|
517
523
|
def argument_variable_names
|
518
524
|
parameters.reject(&SPLAT)
|
519
525
|
end
|
520
526
|
|
527
|
+
def nested_within
|
528
|
+
@nested_within ||= []
|
529
|
+
end
|
530
|
+
|
531
|
+
def nested_function?
|
532
|
+
not nested_within.empty?
|
533
|
+
end
|
534
|
+
|
521
535
|
# returns the splat argument or nil
|
522
536
|
def splat
|
523
537
|
parameters.detect(&SPLAT)
|
@@ -531,12 +545,16 @@ class DefNode < Struct.new(:bang, :scope_modifier, :name, :parameters, :keyword,
|
|
531
545
|
end
|
532
546
|
end
|
533
547
|
|
548
|
+
def defined_on_dictionary?
|
549
|
+
keyword == 'dict'
|
550
|
+
end
|
551
|
+
|
534
552
|
def autoload?
|
535
553
|
name.include?('#')
|
536
554
|
end
|
537
555
|
|
538
556
|
def super_node
|
539
|
-
expressions.detect {|n| SuperNode === n}
|
557
|
+
expressions.nodes.detect {|n| SuperNode === n}
|
540
558
|
end
|
541
559
|
|
542
560
|
def to_scope
|
@@ -604,7 +622,7 @@ class ScopeNode
|
|
604
622
|
end
|
605
623
|
self.for_node_variable_names += other.for_node_variable_names
|
606
624
|
self.argument_variable_names -= for_node_variable_names
|
607
|
-
self.function = other.function
|
625
|
+
self.function = other.function
|
608
626
|
self
|
609
627
|
end
|
610
628
|
end
|
@@ -797,13 +815,15 @@ class ClassDefinitionNode < Struct.new(:name, :superclass_name, :expressions)
|
|
797
815
|
include Visitable
|
798
816
|
include Walkable
|
799
817
|
|
818
|
+
FUNCTIONS = lambda {|expr| DefNode === expr}
|
819
|
+
|
800
820
|
def superclass?
|
801
821
|
not superclass_name.nil?
|
802
822
|
end
|
803
823
|
|
804
824
|
def constructor
|
805
|
-
expressions.detect do |n|
|
806
|
-
next(false) unless DefNode === n && (n.name == 'initialize' || n.name
|
825
|
+
expressions.nodes.detect do |n|
|
826
|
+
next(false) unless DefNode === n && (n.name == 'initialize' || n.name == constructor_name)
|
807
827
|
if n.instance_of?(DefMethodNode)
|
808
828
|
Riml.warn("class #{name.inspect} has an initialize function declared with 'defm'. Please use 'def'.")
|
809
829
|
new_node = n.to_def_node
|
@@ -815,6 +835,13 @@ class ClassDefinitionNode < Struct.new(:name, :superclass_name, :expressions)
|
|
815
835
|
end
|
816
836
|
alias constructor? constructor
|
817
837
|
|
838
|
+
def find_function(scope_modifier, name)
|
839
|
+
expressions.nodes.select(&FUNCTIONS).detect do |def_node|
|
840
|
+
def_node.name == name && def_node.scope_modifier == scope_modifier
|
841
|
+
end
|
842
|
+
end
|
843
|
+
alias has_function? find_function
|
844
|
+
|
818
845
|
def constructor_name
|
819
846
|
"#{name}Constructor"
|
820
847
|
end
|
data/lib/repl.rb
CHANGED
@@ -69,7 +69,6 @@ module Riml
|
|
69
69
|
|
70
70
|
def compile_unit!
|
71
71
|
viml = Riml.compile(current_compilation_unit.join("\n"), parser, compiler).chomp
|
72
|
-
escape_newlines_in_strings!(viml)
|
73
72
|
puts viml, "\n"
|
74
73
|
rescue => e
|
75
74
|
raise unless e.kind_of?(RimlError)
|
@@ -92,10 +91,6 @@ module Riml
|
|
92
91
|
puts "#{e.class}: #{e}"
|
93
92
|
end
|
94
93
|
|
95
|
-
def escape_newlines_in_strings!(viml)
|
96
|
-
viml.gsub!(/("[^"]*?)\n+([^"]?")/, '\1\\n\2')
|
97
|
-
end
|
98
|
-
|
99
94
|
def exit_repl
|
100
95
|
exit
|
101
96
|
end
|
data/version.rb
CHANGED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: riml
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.2.
|
4
|
+
version: 0.2.2
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2013-04-
|
12
|
+
date: 2013-04-08 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: racc
|