70
class TestRubyYieldGen < Test::Unit::TestCase
79
:opt_block_param => [[],
81
:block_param_def => [['|', '|'],
82
['|', :block_param, '|']],
83
:block_param => [[:f_arg, ",", :f_rest_arg, :opt_f_block_arg],
85
[:f_arg, ',', :f_rest_arg, ",", :f_arg, :opt_f_block_arg],
86
[:f_arg, :opt_f_block_arg],
87
[:f_rest_arg, :opt_f_block_arg],
88
[:f_rest_arg, ',', :f_arg, :opt_f_block_arg],
90
:f_arg => [[:f_arg_item],
91
[:f_arg, ',', :f_arg_item]],
92
:f_rest_arg => [['*', "var"],
94
:opt_f_block_arg => [[',', :f_block_arg],
96
:f_block_arg => [['&', 'var']],
97
:f_arg_item => [[:f_norm_arg],
98
['(', :f_margs, ')']],
99
:f_margs => [[:f_marg_list],
100
[:f_marg_list, ',', '*', :f_norm_arg],
101
[:f_marg_list, ',', '*', :f_norm_arg, ',', :f_marg_list],
102
[:f_marg_list, ',', '*'],
103
[:f_marg_list, ',', '*', ',', :f_marg_list],
105
[ '*', :f_norm_arg, ',', :f_marg_list],
107
[ '*', ',', :f_marg_list]],
108
:f_marg_list => [[:f_marg],
109
[:f_marg_list, ',', :f_marg]],
110
:f_marg => [[:f_norm_arg],
111
['(', :f_margs, ')']],
112
:f_norm_arg => [['var']],
114
:command_args => [[:open_args]],
115
:open_args => [[' ',:call_args],
117
['(', :call_args2, ')']],
118
:call_args => [[:command],
119
[ :args, :opt_block_arg],
120
[ :assocs, :opt_block_arg],
121
[ :args, ',', :assocs, :opt_block_arg],
123
:call_args2 => [[:arg, ',', :args, :opt_block_arg],
124
[:arg, ',', :block_arg],
125
[ :assocs, :opt_block_arg],
126
[:arg, ',', :assocs, :opt_block_arg],
127
[:arg, ',', :args, ',', :assocs, :opt_block_arg],
130
:command_args_noblock => [[:open_args_noblock]],
131
:open_args_noblock => [[' ',:call_args_noblock],
133
['(', :call_args2_noblock, ')']],
134
:call_args_noblock => [[:command],
137
[ :args, ',', :assocs]],
138
:call_args2_noblock => [[:arg, ',', :args],
140
[:arg, ',', :assocs],
141
[:arg, ',', :args, ',', :assocs]],
147
[:args,",","*",:arg]],
149
:assocs => [[:assoc],
150
[:assocs, ',', :assoc]],
151
:assoc => [[:arg, '=>', :arg],
152
['label', ':', :arg]],
153
:opt_block_arg => [[',', :block_arg],
155
:block_arg => [['&', :arg]],
156
#:test => [['def m() yield', :command_args_noblock, ' end; r = m {', :block_param_def, 'vars', '}; undef m; r']]
157
:test_proc => [['def m() yield', :command_args_noblock, ' end; r = m {', :block_param_def, 'vars', '}; undef m; r']],
158
:test_lambda => [['def m() yield', :command_args_noblock, ' end; r = m(&lambda {', :block_param_def, 'vars', '}); undef m; r']]
163
r = obj.subst('var') {
164
var = "v#{vars.length}"
171
def split_by_comma(ary)
172
return [] if ary.empty?
184
def emu_return_args(*vs)
188
def emu_eval_args(args)
192
code = "emu_return_args #{args.map {|a| a.join('') }.join(",")}"
196
def emu_bind_single(arg, param, result_binding)
197
#p [:emu_bind_single, arg, param]
198
if param.length == 1 && String === param[0] && /\A[a-z0-9]+\z/ =~ param[0]
199
result_binding[param[0]] = arg
200
elsif param.length == 1 && Array === param[0] && param[0][0] == '(' && param[0][-1] == ')'
201
arg = [arg] unless Array === arg
202
emu_bind_params(arg, split_by_comma(param[0][1...-1]), false, result_binding)
204
raise "unexpected param: #{param.inspect}"
209
def emu_bind_params(args, params, islambda, result_binding={})
210
#p [:emu_bind_params, args, params]
211
if params.last == [] # extra comma
216
params.each_with_index {|par, i|
217
star_index = i if par[0] == '*'
222
if args.length < params.length - 1
223
throw :emuerror, ArgumentError
226
if args.length != params.length
227
throw :emuerror, ArgumentError
232
# TRICK #2 : adjust mismatch on number of arguments
234
pre_params = params[0...star_index]
235
rest_param = params[star_index]
236
post_params = params[(star_index+1)..-1]
237
pre_params.each {|par| emu_bind_single(args.shift, par, result_binding) }
238
if post_params.length <= args.length
239
post_params.reverse_each {|par| emu_bind_single(args.pop, par, result_binding) }
241
post_params.each {|par| emu_bind_single(args.shift, par, result_binding) }
243
if rest_param != ['*']
244
emu_bind_single(args, rest_param[1..-1], result_binding)
247
params.each_with_index {|par, i|
248
emu_bind_single(args[i], par, result_binding)
252
#p [args, params, result_binding]
257
def emu_bind(t, islambda)
260
command_args_noblock = t[1]
261
block_param_def = t[3]
262
command_args_noblock = command_args_noblock.expand {|a| !(a[0] == '[' && a[-1] == ']') }
263
block_param_def = block_param_def.expand {|a| !(a[0] == '(' && a[-1] == ')') }
265
if command_args_noblock.to_a[0] == ' '
266
args = command_args_noblock.to_a[1..-1]
267
elsif command_args_noblock.to_a[0] == '(' && command_args_noblock.to_a[-1] == ')'
268
args = command_args_noblock.to_a[1...-1]
270
raise "unexpected command_args_noblock: #{command_args_noblock.inspect}"
272
args = emu_eval_args(split_by_comma(args))
274
params = block_param_def.to_a[1...-1]
275
params = split_by_comma(params)
277
#p [:emu0, args, params]
281
if params.last && params.last[0] == '&'
282
result_binding[params.last[1]] = nil
287
# TRICK #1 : single array argument is expanded if there are two or more params.
288
# * block parameter is not counted.
289
# * extra comma after single param forces the expansion.
290
if args.length == 1 && Array === args[0] && 1 < params.length
295
emu_bind_params(args, params, islambda, result_binding)
300
def emu(t, vars, islambda)
302
emu_binding = emu_bind(t, islambda)
303
vars.map {|var| emu_binding.fetch(var, "NOVAL") }
307
def check_nofork(t, islambda=false)
308
t, vars = rename_var(t)
309
t = t.subst('vars') { " [#{vars.join(",")}]" }
310
emu_values = emu(t, vars, islambda)
315
eval_values = eval(s)
317
eval_values = ArgumentError
319
#success = emu_values == eval_values ? 'succ' : 'fail'
320
#puts "eval:#{vs_ev.inspect[1...-1].delete(' ')}\temu:#{vs_emu.inspect[1...-1].delete(' ')}\t#{success}"
321
assert_equal(emu_values, eval_values, s)
325
syntax = Sentence.expand_syntax(Syntax)
326
Sentence.each(syntax, :test_proc, 4) {|t|
331
def test_yield_lambda
332
syntax = Sentence.expand_syntax(Syntax)
333
Sentence.each(syntax, :test_lambda, 4) {|t|
334
check_nofork(t, true)