riml 0.2.9 → 0.3.0
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/Gemfile.lock +0 -8
- data/README.md +47 -17
- data/lib/ast_rewriter.rb +221 -42
- data/lib/class_map.rb +7 -5
- data/lib/compiler.rb +6 -7
- data/lib/errors.rb +1 -0
- data/lib/get_sid_function.vim +3 -0
- data/lib/grammar.y +13 -12
- data/lib/nodes.rb +44 -11
- data/lib/parser.rb +737 -737
- data/lib/riml.rb +2 -1
- data/version.rb +2 -2
- metadata +3 -3
- data/Gemfile +0 -15
data/Gemfile.lock
CHANGED
@@ -1,13 +1,6 @@
|
|
1
1
|
GEM
|
2
2
|
remote: http://rubygems.org/
|
3
3
|
specs:
|
4
|
-
columnize (0.3.6)
|
5
|
-
debugger (1.6.1)
|
6
|
-
columnize (>= 0.3.1)
|
7
|
-
debugger-linecache (~> 1.2.0)
|
8
|
-
debugger-ruby_core_source (~> 1.2.3)
|
9
|
-
debugger-linecache (1.2.0)
|
10
|
-
debugger-ruby_core_source (1.2.3)
|
11
4
|
minitest (2.5.1)
|
12
5
|
racc (1.4.9)
|
13
6
|
rake (10.0.4)
|
@@ -16,7 +9,6 @@ PLATFORMS
|
|
16
9
|
ruby
|
17
10
|
|
18
11
|
DEPENDENCIES
|
19
|
-
debugger
|
20
12
|
minitest (~> 2.5.1)
|
21
13
|
racc
|
22
14
|
rake
|
data/README.md
CHANGED
@@ -171,21 +171,25 @@ Classes
|
|
171
171
|
|
172
172
|
###Basic Class
|
173
173
|
|
174
|
-
|
174
|
+
function! s:SID()
|
175
|
+
return matchstr(expand('<sfile>'), '<SNR>\zs\d\+\ze_SID$')
|
176
|
+
endfunction
|
177
|
+
|
178
|
+
class MyClass function! s:MyClassConstructor(data, otherData, ...)
|
175
179
|
def initialize(data, otherData, *options) let myClassObj = {}
|
176
180
|
self.data = data let myClassObj.data = a:data
|
177
181
|
self.otherData = otherData let myClassObj.otherData = a:otherData
|
178
182
|
self.options = options let myClassObj.options = a:000
|
179
|
-
end let myClassObj.getData = function('
|
180
|
-
let myClassObj.getOtherData = function('
|
183
|
+
end let myClassObj.getData = function('<SNR>' . s:SID() . '_s:MyClass_getData')
|
184
|
+
let myClassObj.getOtherData = function('<SNR>' . s:SID() . '_s:MyClass_getOtherData')
|
181
185
|
defm getData return myClassObj
|
182
186
|
return self.data endfunction
|
183
187
|
end
|
184
|
-
function!
|
188
|
+
function! <SID>s:MyClass_getdata() dict
|
185
189
|
defm getOtherData return self.data
|
186
190
|
return self.otherData endfunction
|
187
191
|
end
|
188
|
-
end function!
|
192
|
+
end function! <SID>s:MyClass_getOtherData() dict
|
189
193
|
return self.otherData
|
190
194
|
endfunction
|
191
195
|
|
@@ -195,27 +199,40 @@ inside a class, use 'def'. To create an instance of this class, simply:
|
|
195
199
|
|
196
200
|
obj = new MyClass('someData', 'someOtherData')
|
197
201
|
|
202
|
+
Classes have a default scope modifier of 's:'. That is, they cannot be instantiated
|
203
|
+
outside the script in which they are defined. In order to allow them to be instantiated
|
204
|
+
in any script file, you must declare the class with the explicit scope modifier of 'g:'.
|
205
|
+
For example:
|
206
|
+
|
207
|
+
class g:MyClass
|
208
|
+
...
|
209
|
+
end
|
210
|
+
|
198
211
|
In this basic example of a class, we see a \*splat variable. This is just a
|
199
212
|
convenient way to refer to 'a:000' in the body of a function. Splat variables
|
200
213
|
are optional parameters and get compiled to '...'.
|
201
214
|
|
202
215
|
###Class Inheritance
|
203
216
|
|
204
|
-
|
217
|
+
function! s:SID()
|
218
|
+
return matchstr(expand('<sfile>'), '<SNR>\zs\d\+\ze_SID$')
|
219
|
+
endfunction
|
220
|
+
|
221
|
+
class Translation function! s:TranslationConstructor(input)
|
205
222
|
def initialize(input) let translationObj = {}
|
206
223
|
self.input = input let translationObj.input = a:input
|
207
224
|
end return translationObj
|
208
225
|
end endfunction
|
209
226
|
|
210
|
-
class FrenchToEnglishTranslation < Translation function!
|
227
|
+
class FrenchToEnglishTranslation < Translation function! s:FrenchToEnglishTranslationConstructor(input)
|
211
228
|
defm translate let frenchToEnglishTranslationObj = {}
|
212
|
-
if (self.input == "Bonjour!") let translationObj =
|
229
|
+
if (self.input == "Bonjour!") let translationObj = s:TranslationConstructor(a:input)
|
213
230
|
echo "Hello!" call extend(frenchToEnglishTranslationObj, translationObj)
|
214
|
-
else let frenchToEnglishTranslationObj.translate = function('
|
231
|
+
else let frenchToEnglishTranslationObj.translate = function('<SNR>' . s:SID() . '_s:FrenchToEnglishTranslation_translate')
|
215
232
|
echo "Sorry, I don't know that word." return frenchToEnglishTranslationObj
|
216
233
|
end endfunction
|
217
234
|
end
|
218
|
-
end function!
|
235
|
+
end function! <SID>s:FrenchToEnglishTranslation_translate() dict
|
219
236
|
if (self.input ==# "Bonjour!")
|
220
237
|
translation = new echo "Hello!"
|
221
238
|
\ FrenchToEnglishTranslation("Bonjour!") else
|
@@ -223,7 +240,7 @@ are optional parameters and get compiled to '...'.
|
|
223
240
|
endif
|
224
241
|
endfunction
|
225
242
|
|
226
|
-
let s:translation =
|
243
|
+
let s:translation = s:TranslationConstructor("Bonjour!")
|
227
244
|
call s:translation.translate()
|
228
245
|
=> "Hello!"
|
229
246
|
|
@@ -234,6 +251,14 @@ instances use the initialize function from 'Translation', and new instances must
|
|
234
251
|
be provided with an 'input' argument on creation. Basically, if a class doesn't
|
235
252
|
provide an initialize function, it uses its superclass's.
|
236
253
|
|
254
|
+
A base class and inheriting class can have different scope modifiers. For example, if you
|
255
|
+
had a script-local base class and wanted to extend it but have the extending class be
|
256
|
+
global, this is not a problem. Simply:
|
257
|
+
|
258
|
+
class g:GlobalClass < ScriptLocalClass
|
259
|
+
...
|
260
|
+
end
|
261
|
+
|
237
262
|
If you look at the last line of Riml in the previous example, you'll see that
|
238
263
|
it doesn't use Vimscript's builtin 'call' function for calling the 'translate'
|
239
264
|
method on the translation object. Riml can figure out when 'call' is necessary,
|
@@ -241,7 +266,11 @@ and will add it to the compiled Vimscript.
|
|
241
266
|
|
242
267
|
###Using 'super'
|
243
268
|
|
244
|
-
|
269
|
+
function! s:SID()
|
270
|
+
return matchstr(expand('<sfile>'), '<SNR>\zs\d\+\ze_SID$')
|
271
|
+
endfunction
|
272
|
+
|
273
|
+
class Car function! s:CarConstructor(make, model, color)
|
245
274
|
def initialize(make, model, color) let carObj = {}
|
246
275
|
self.make = make let carObj.make = a:make
|
247
276
|
self.model = model let carObj.model = a:model
|
@@ -249,16 +278,16 @@ and will add it to the compiled Vimscript.
|
|
249
278
|
end endfunction
|
250
279
|
end
|
251
280
|
|
252
|
-
class HotRod < Car function!
|
281
|
+
class HotRod < Car function! s:HotRodConstructor(make, model, color, topSpeed)
|
253
282
|
def initialize(make, model, color, topSpeed) let hotRodObj = {}
|
254
283
|
self.topSpeed = topSpeed let hotRodObj.topSpeed = a:topSpeed
|
255
|
-
super(make, model, color) let carObj =
|
284
|
+
super(make, model, color) let carObj = s:CarConstructor(a:make, a:model, a:color)
|
256
285
|
end call extend(hotRodObj, carObj)
|
257
|
-
let hotRodObj.drive = function('
|
286
|
+
let hotRodObj.drive = function('<SNR>' . s:SID() . '_s:HotRod_drive')
|
258
287
|
defm drive return hotRodObj
|
259
288
|
if self.topSpeed > 140 endfunction
|
260
289
|
echo "Ahhhhhhh!"
|
261
|
-
else function!
|
290
|
+
else function! <SID>s:HotRod_drive() dict
|
262
291
|
echo "Nice" if self.topSpeed ># 140
|
263
292
|
end echo "Ahhhhhhh!"
|
264
293
|
end else
|
@@ -266,8 +295,9 @@ and will add it to the compiled Vimscript.
|
|
266
295
|
endif
|
267
296
|
newCar = new HotRod("chevy", "mustang", "red", 160) endfunction
|
268
297
|
newCar.drive()
|
269
|
-
let s:newCar =
|
298
|
+
let s:newCar = s:HotRodConstructor("chevy", "mustang", "red", 160)
|
270
299
|
call s:newCar.drive()
|
300
|
+
=> "Ahhhhhhh!"
|
271
301
|
|
272
302
|
Use of 'super' is legal only within subclasses. If arguments are given, these arguments are sent
|
273
303
|
to the superclass's function of the same name. If no arguments are given and parentheses are omitted,
|
data/lib/ast_rewriter.rb
CHANGED
@@ -7,24 +7,31 @@ module Riml
|
|
7
7
|
include Riml::Constants
|
8
8
|
|
9
9
|
attr_accessor :ast
|
10
|
-
attr_reader :classes, :
|
10
|
+
attr_reader :classes, :rewritten_included_and_sourced_files
|
11
11
|
|
12
12
|
def initialize(ast = nil, classes = nil)
|
13
13
|
@ast = ast
|
14
14
|
@classes = classes || ClassMap.new
|
15
|
-
|
16
|
-
#
|
17
|
-
@
|
15
|
+
# Keeps track of filenames with their rewritten ASTs, to prevent rewriting
|
16
|
+
# the same AST more than once.
|
17
|
+
@rewritten_included_and_sourced_files = {}
|
18
|
+
# Keeps track of which filenames included/sourced which.
|
19
|
+
# ex: { nil => ["main.riml"], "main.riml" => ["lib1.riml", "lib2.riml"],
|
20
|
+
# "lib1.riml" => [], "lib2.riml" => [] }
|
21
|
+
@included_and_sourced_file_refs = Hash.new { |h, k| h[k] = [] }
|
18
22
|
end
|
19
23
|
|
20
|
-
def rewrite(
|
21
|
-
if
|
22
|
-
|
23
|
-
return rewritten_ast
|
24
|
-
end
|
25
|
-
rewrite_included_files!(from_file)
|
24
|
+
def rewrite(filename = nil, included = false)
|
25
|
+
if filename && (rewritten_ast = rewritten_included_and_sourced_files[filename])
|
26
|
+
return rewritten_ast
|
26
27
|
end
|
27
28
|
establish_parents(ast)
|
29
|
+
class_registry = RegisterDefinedClasses.new(ast, classes)
|
30
|
+
class_registry.rewrite_on_match
|
31
|
+
rewrite_included_and_sourced_files!(filename)
|
32
|
+
if filename && !included && add_SID_function?(filename)
|
33
|
+
add_SID_function!
|
34
|
+
end
|
28
35
|
rewriters = [
|
29
36
|
StrictEqualsComparisonOperator.new(ast, classes),
|
30
37
|
VarEqualsComparisonOperator.new(ast, classes),
|
@@ -60,30 +67,33 @@ module Riml
|
|
60
67
|
replace node if match?(node)
|
61
68
|
end
|
62
69
|
|
63
|
-
# We need to rewrite the included files before anything else. This is in
|
64
|
-
# order to keep track of any classes defined in the included files (and
|
65
|
-
# files included in those, etc...). We keep a cache of rewritten asts
|
66
|
-
# because the
|
67
|
-
#
|
68
|
-
#
|
69
|
-
def
|
70
|
+
# We need to rewrite the included/sourced files before anything else. This is in
|
71
|
+
# order to keep track of any classes defined in the included and sourced files (and
|
72
|
+
# files included/sourced in those, etc...). We keep a cache of rewritten asts
|
73
|
+
# because the included/sourced files are parsed more than once. They're parsed
|
74
|
+
# first in this step, plus whenever the compiler visits a 'riml_include'/'riml_source'
|
75
|
+
# node in order to compile it on the spot.
|
76
|
+
def rewrite_included_and_sourced_files!(filename)
|
70
77
|
old_ast = ast
|
71
78
|
ast.children.each do |node|
|
72
|
-
next unless RimlCommandNode === node
|
79
|
+
next unless RimlCommandNode === node
|
80
|
+
action = node.name == 'riml_include' ? 'include' : 'source'
|
81
|
+
|
73
82
|
node.each_existing_file! do |file, fullpath|
|
74
|
-
if
|
75
|
-
msg = "#{
|
76
|
-
" #{file.inspect} already included #{
|
77
|
-
|
78
|
-
|
83
|
+
if filename && @included_and_sourced_file_refs[file].include?(filename)
|
84
|
+
msg = "#{filename.inspect} can't #{action} #{file.inspect}, as " \
|
85
|
+
" #{file.inspect} already included/sourced #{filename.inspect}"
|
86
|
+
# IncludeFileLoop/SourceFileLoop
|
87
|
+
raise Riml.const_get("#{action.capitalize}FileLoop"), msg
|
88
|
+
elsif filename == file
|
79
89
|
raise UserArgumentError, "#{file.inspect} can't include itself"
|
80
90
|
end
|
81
|
-
@
|
91
|
+
@included_and_sourced_file_refs[filename] << file
|
82
92
|
riml_src = File.read(fullpath)
|
83
93
|
# recursively parse included files with this ast_rewriter in order
|
84
94
|
# to pick up any classes that are defined there
|
85
|
-
rewritten_ast = Parser.new.parse(riml_src, self, file)
|
86
|
-
|
95
|
+
rewritten_ast = Parser.new.parse(riml_src, self, file, action == 'include')
|
96
|
+
rewritten_included_and_sourced_files[file] = rewritten_ast
|
87
97
|
end
|
88
98
|
end
|
89
99
|
ensure
|
@@ -94,6 +104,53 @@ module Riml
|
|
94
104
|
true
|
95
105
|
end
|
96
106
|
|
107
|
+
# Add SID function if this is the main file and it has defined classes, or
|
108
|
+
# if it included other files and any one of those other files defined classes.
|
109
|
+
def add_SID_function?(filename)
|
110
|
+
return true if ast.children.grep(ClassDefinitionNode).any?
|
111
|
+
included_files = @included_and_sourced_file_refs[filename]
|
112
|
+
while included_files.any?
|
113
|
+
incs = []
|
114
|
+
included_files.each do |included_file|
|
115
|
+
if (ast = rewritten_included_and_sourced_files[included_file])
|
116
|
+
return true if ast.children.grep(ClassDefinitionNode).any?
|
117
|
+
end
|
118
|
+
incs.concat @included_and_sourced_file_refs[included_file]
|
119
|
+
end
|
120
|
+
included_files = incs
|
121
|
+
end
|
122
|
+
false
|
123
|
+
end
|
124
|
+
|
125
|
+
# :h <SID>
|
126
|
+
def add_SID_function!
|
127
|
+
fchild = ast.nodes.first
|
128
|
+
return false if DefNode === fchild && fchild.name == 'SID' && fchild.scope_modifier == 's:'
|
129
|
+
fn = DefNode.new('!', nil, 's:', 'SID', [], nil, Nodes.new([
|
130
|
+
ReturnNode.new(CallNode.new(nil, 'matchstr', [
|
131
|
+
CallNode.new(nil, 'expand', [StringNode.new('<sfile>', :s)]),
|
132
|
+
StringNode.new('<SNR>\zs\d\+\ze_SID$', :s)
|
133
|
+
]
|
134
|
+
))
|
135
|
+
])
|
136
|
+
)
|
137
|
+
fn.parent = ast.nodes
|
138
|
+
establish_parents(fn)
|
139
|
+
ast.nodes.unshift fn
|
140
|
+
end
|
141
|
+
|
142
|
+
class RegisterDefinedClasses < AST_Rewriter
|
143
|
+
def match?(node)
|
144
|
+
ClassDefinitionNode === node
|
145
|
+
end
|
146
|
+
|
147
|
+
def replace(node)
|
148
|
+
n = node.dup
|
149
|
+
n.instance_variable_set("@registered_state", true)
|
150
|
+
classes[node.full_name] = n
|
151
|
+
end
|
152
|
+
end
|
153
|
+
|
97
154
|
class StrictEqualsComparisonOperator < AST_Rewriter
|
98
155
|
def match?(node)
|
99
156
|
BinaryOperatorNode === node && node.operator == '==='
|
@@ -139,12 +196,15 @@ module Riml
|
|
139
196
|
end
|
140
197
|
|
141
198
|
def replace(node)
|
142
|
-
classes[node.
|
199
|
+
classes[node.full_name] = node
|
143
200
|
|
201
|
+
RegisterPrivateFunctions.new(node, classes).rewrite_on_match
|
202
|
+
DefNodeToPrivateFunction.new(node, classes).rewrite_on_match
|
144
203
|
InsertInitializeMethod.new(node, classes).rewrite_on_match
|
145
204
|
constructor = node.constructor
|
146
|
-
constructor.scope_modifier = 'g:' unless constructor.scope_modifier
|
147
205
|
constructor.name = node.constructor_name
|
206
|
+
constructor.original_name = 'initialize'
|
207
|
+
constructor.scope_modifier = node.scope_modifier
|
148
208
|
# set up dictionary variable at top of function
|
149
209
|
dict_name = node.constructor_obj_name
|
150
210
|
constructor.expressions.unshift(
|
@@ -155,6 +215,7 @@ module Riml
|
|
155
215
|
ExtendObjectWithMethods.new(node, classes).rewrite_on_match
|
156
216
|
SelfToDictName.new(dict_name).rewrite_on_match(constructor)
|
157
217
|
SuperToSuperclassFunction.new(node, classes).rewrite_on_match
|
218
|
+
PrivateFunctionCallToPassObjExplicitly.new(node, classes).rewrite_on_match
|
158
219
|
|
159
220
|
constructor.expressions.push(
|
160
221
|
ReturnNode.new(GetVariableNode.new(nil, dict_name))
|
@@ -162,6 +223,87 @@ module Riml
|
|
162
223
|
reestablish_parents(constructor)
|
163
224
|
end
|
164
225
|
|
226
|
+
class RegisterPrivateFunctions < AST_Rewriter
|
227
|
+
def match?(node)
|
228
|
+
node.instance_of?(DefNode) && node.name != 'initialize'
|
229
|
+
end
|
230
|
+
|
231
|
+
def replace(node)
|
232
|
+
ast.private_function_names << node.name
|
233
|
+
end
|
234
|
+
end
|
235
|
+
|
236
|
+
class SelfToObjArgumentInPrivateFunction < AST_Rewriter
|
237
|
+
def initialize(ast, classes, class_node)
|
238
|
+
super(ast, classes)
|
239
|
+
@class_node = class_node
|
240
|
+
end
|
241
|
+
|
242
|
+
def match?(node)
|
243
|
+
return unless GetVariableNode === node && node.scope_modifier == nil && node.name == 'self'
|
244
|
+
return if node.parent.is_a?(DictGetDotNode) && node.parent.parent.is_a?(CallNode) &&
|
245
|
+
(@class_node.private_function_names & node.parent.keys).size == 1
|
246
|
+
# make sure we're not nested in a different function
|
247
|
+
n = node
|
248
|
+
until n.instance_of?(DefNode)
|
249
|
+
n = n.parent
|
250
|
+
end
|
251
|
+
n == ast
|
252
|
+
end
|
253
|
+
|
254
|
+
def replace(node)
|
255
|
+
node.name = @class_node.constructor_obj_name
|
256
|
+
node.scope_modifier = 'a:'
|
257
|
+
end
|
258
|
+
end
|
259
|
+
|
260
|
+
class DefNodeToPrivateFunction < AST_Rewriter
|
261
|
+
def match?(node)
|
262
|
+
return unless node.instance_of?(DefNode) && node.name != 'initialize'
|
263
|
+
node.private_function = true
|
264
|
+
end
|
265
|
+
|
266
|
+
def replace(node)
|
267
|
+
class_node = ast
|
268
|
+
class_name = class_node.name
|
269
|
+
node.scope_modifier = 's:'
|
270
|
+
node.name = "#{class_name}_#{node.name}"
|
271
|
+
node.sid = nil
|
272
|
+
node.keywords -= ['dict']
|
273
|
+
node.parameters.unshift(class_node.constructor_obj_name)
|
274
|
+
# rewrite `self` in function body to a:#{class_name}Obj
|
275
|
+
self_to_obj_argument = SelfToObjArgumentInPrivateFunction.new(node, classes, class_node)
|
276
|
+
self_to_obj_argument.rewrite_on_match
|
277
|
+
reestablish_parents(node)
|
278
|
+
end
|
279
|
+
end
|
280
|
+
|
281
|
+
class PrivateFunctionCallToPassObjExplicitly < AST_Rewriter
|
282
|
+
def match?(node)
|
283
|
+
CallNode === node && DictGetDotNode === node.name && node.name.dict.scope_modifier.nil? &&
|
284
|
+
node.name.dict.name == 'self' && (node.name.keys & ast.private_function_names).size == 1
|
285
|
+
end
|
286
|
+
|
287
|
+
def replace(node)
|
288
|
+
node.scope_modifier = 's:'
|
289
|
+
# find function that I'm in
|
290
|
+
n = node
|
291
|
+
until n.instance_of?(DefNode)
|
292
|
+
n = n.parent
|
293
|
+
end
|
294
|
+
if n.original_name == 'initialize'
|
295
|
+
node.arguments.unshift(GetVariableNode.new(nil, ast.constructor_obj_name))
|
296
|
+
elsif n.private_function
|
297
|
+
node.arguments.unshift(GetVariableNode.new('a:', ast.constructor_obj_name))
|
298
|
+
else
|
299
|
+
node.arguments.unshift(GetVariableNode.new(nil, 'self'))
|
300
|
+
end
|
301
|
+
func_name = node.name.keys.first
|
302
|
+
node.name = "#{ast.name}_#{func_name}"
|
303
|
+
reestablish_parents(node)
|
304
|
+
end
|
305
|
+
end
|
306
|
+
|
165
307
|
class ExtendObjectWithMethods < AST_Rewriter
|
166
308
|
def match?(node)
|
167
309
|
DefMethodNode === node
|
@@ -177,10 +319,12 @@ module Riml
|
|
177
319
|
node.remove
|
178
320
|
def_node.original_name = def_node.name.dup
|
179
321
|
def_node.name.insert(0, "#{ast.name}_")
|
322
|
+
def_node.sid = SIDNode.new
|
180
323
|
reestablish_parents(def_node)
|
181
324
|
extend_obj_with_methods(def_node)
|
182
325
|
end
|
183
326
|
|
327
|
+
# Ex: `let dogObj.bark = function('<SNR>' . s:SID() . '_s:Dog_bark')`
|
184
328
|
def extend_obj_with_methods(def_node)
|
185
329
|
constructor = ast.constructor
|
186
330
|
extension =
|
@@ -190,9 +334,23 @@ module Riml
|
|
190
334
|
[def_node.original_name]
|
191
335
|
),
|
192
336
|
CallNode.new(
|
193
|
-
nil, 'function', [
|
337
|
+
nil, 'function', [
|
338
|
+
BinaryOperatorNode.new(
|
339
|
+
'.',
|
340
|
+
[
|
341
|
+
BinaryOperatorNode.new(
|
342
|
+
'.',
|
343
|
+
[
|
344
|
+
StringNode.new('<SNR>', :s),
|
345
|
+
CallNode.new('s:', 'SID', []),
|
346
|
+
]
|
347
|
+
),
|
348
|
+
StringNode.new("_s:#{def_node.name}", :s)
|
349
|
+
],
|
350
|
+
)
|
351
|
+
],
|
194
352
|
)
|
195
|
-
|
353
|
+
)
|
196
354
|
constructor.expressions << extension
|
197
355
|
extension.parent = constructor.expressions
|
198
356
|
end
|
@@ -223,11 +381,11 @@ module Riml
|
|
223
381
|
def replace(class_node)
|
224
382
|
if class_node.superclass?
|
225
383
|
def_node = DefNode.new(
|
226
|
-
'!', nil, "initialize", superclass_params, nil, Nodes.new([SuperNode.new([], false)])
|
384
|
+
'!', nil, nil, "initialize", superclass_params, nil, Nodes.new([SuperNode.new([], false)])
|
227
385
|
)
|
228
386
|
else
|
229
387
|
def_node = DefNode.new(
|
230
|
-
'!', nil, "initialize", [], nil, Nodes.new([])
|
388
|
+
'!', nil, nil, "initialize", [], nil, Nodes.new([])
|
231
389
|
)
|
232
390
|
end
|
233
391
|
class_node.expressions.unshift(def_node)
|
@@ -235,7 +393,7 @@ module Riml
|
|
235
393
|
end
|
236
394
|
|
237
395
|
def superclass_params
|
238
|
-
classes.superclass(ast.
|
396
|
+
classes.superclass(ast.full_name).constructor.parameters
|
239
397
|
end
|
240
398
|
|
241
399
|
def recursive?
|
@@ -255,7 +413,13 @@ module Riml
|
|
255
413
|
end
|
256
414
|
|
257
415
|
def replace(constructor)
|
258
|
-
|
416
|
+
unless class_node.superclass?
|
417
|
+
# TODO: raise error instead of aborting
|
418
|
+
abort "class #{class_node.full_name.inspect} called super in its " \
|
419
|
+
" initialize function, but it has no superclass."
|
420
|
+
end
|
421
|
+
|
422
|
+
superclass = classes.superclass(class_node.full_name)
|
259
423
|
super_constructor = superclass.constructor
|
260
424
|
|
261
425
|
set_var_node = AssignNode.new('=', GetVariableNode.new(nil, superclass.constructor_obj_name),
|
@@ -305,14 +469,15 @@ module Riml
|
|
305
469
|
end
|
306
470
|
|
307
471
|
def replace(node)
|
308
|
-
|
309
|
-
|
472
|
+
# TODO: check if class even has superclass before all this
|
473
|
+
func_scope = 's:'
|
474
|
+
superclass = classes[ast.superclass_full_name]
|
310
475
|
while superclass && !superclass.has_function?(func_scope, superclass_func_name(superclass)) && superclass.superclass?
|
311
|
-
superclass = classes[superclass.
|
476
|
+
superclass = classes[superclass.superclass_full_name]
|
312
477
|
end
|
313
478
|
if superclass.nil? || !superclass.has_function?(func_scope, superclass_func_name(superclass))
|
314
479
|
raise Riml::UserFunctionNotFoundError,
|
315
|
-
"super was called in class #{ast.
|
480
|
+
"super was called in class #{ast.full_name} in " \
|
316
481
|
"function #{@function_node.original_name}, but there are no " \
|
317
482
|
"functions with this name in that class's superclass hierarchy."
|
318
483
|
end
|
@@ -342,9 +507,21 @@ module Riml
|
|
342
507
|
[super_func_name]
|
343
508
|
),
|
344
509
|
CallNode.new(
|
345
|
-
nil,
|
346
|
-
|
347
|
-
|
510
|
+
nil, 'function', [
|
511
|
+
BinaryOperatorNode.new(
|
512
|
+
'.',
|
513
|
+
[
|
514
|
+
BinaryOperatorNode.new(
|
515
|
+
'.',
|
516
|
+
[
|
517
|
+
StringNode.new('<SNR>', :s),
|
518
|
+
CallNode.new('s:', 'SID', []),
|
519
|
+
]
|
520
|
+
),
|
521
|
+
StringNode.new("_s:#{super_func_name}", :s)
|
522
|
+
],
|
523
|
+
)
|
524
|
+
],
|
348
525
|
)
|
349
526
|
)
|
350
527
|
ast.constructor.expressions << assign_node
|
@@ -359,7 +536,9 @@ module Riml
|
|
359
536
|
end
|
360
537
|
|
361
538
|
def replace(node)
|
362
|
-
constructor_name = node.call_node.
|
539
|
+
constructor_name = (node.call_node.scope_modifier ||
|
540
|
+
ClassDefinitionNode::DEFAULT_SCOPE_MODIFIER) +
|
541
|
+
node.call_node.name
|
363
542
|
class_node = classes[constructor_name]
|
364
543
|
call_node = node.call_node
|
365
544
|
call_node.name = class_node.constructor_name
|