mirah 0.1.4-java → 0.2.0-java
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +0 -0
- data/CODE_OF_CONDUCT.md +74 -0
- data/History.txt +531 -0
- data/README.md +23 -10
- data/Rakefile +239 -156
- data/TODO.md +71 -10
- data/bin/mirah +1 -1
- data/bin/mirahc +1 -1
- data/dist/mirahc.jar +0 -0
- data/examples/bintrees.mirah +2 -2
- data/examples/construction.mirah +2 -2
- data/examples/fields.mirah +1 -1
- data/examples/fractal.mirah +1 -1
- data/examples/fractal.rb +70 -0
- data/examples/interfaces.mirah +1 -1
- data/examples/java_thing.mirah +1 -1
- data/examples/macros/square.mirah +3 -3
- data/examples/macros/square_int.mirah +3 -3
- data/examples/macros/string_each_char.mirah +6 -6
- data/examples/rosettacode/100-doors.mirah +0 -2
- data/examples/rosettacode/count-occurrences-of-a-substring.mirah +3 -3
- data/examples/rosettacode/empty-string.mirah +1 -1
- data/examples/rosettacode/fizz-buzz.mirah +4 -4
- data/examples/rosettacode/is-string-numeric.mirah +7 -7
- data/examples/rosettacode/palindrome.mirah +2 -2
- data/examples/rosettacode/reverse-a-string.mirah +1 -1
- data/examples/rosettacode/rot-13.mirah +1 -1
- data/examples/{edb.mirah → rosettacode/simple_character_math.mirah} +13 -4
- data/examples/rosettacode/string-case.mirah +2 -2
- data/examples/rosettacode/string-length.mirah +1 -1
- data/examples/swing.mirah +9 -14
- data/extensions_and_macros.md +117 -0
- data/lib/mirah.rb +1 -1
- data/lib/mirah/errors.rb +3 -1
- data/lib/mirah/transform/ast_ext.rb +3 -2
- data/lib/mirah/util/process_errors.rb +1 -2
- data/lib/mirah/version.rb +1 -1
- data/test/A.class +0 -0
- data/test/core/util/jvm_version_test.rb +10 -0
- data/test/core/util/mirah_arguments_test.rb +51 -4
- data/test/fixtures/cp1251_test.mirah +7 -0
- data/test/fixtures/org/foo/AbstractExecutorJava8.java +30 -0
- data/test/fixtures/org/foo/ClassWithSelfReferencingTypeParameter.java +24 -0
- data/test/fixtures/org/foo/InnerInterfaceClass.java +12 -0
- data/test/fixtures/org/foo/IntAnno.class +0 -0
- data/test/fixtures/org/foo/TypeFixtureJava8.java +10 -0
- data/test/fixtures/utf8_test.mirah +7 -0
- data/test/jvm/access_levels_test.rb +31 -0
- data/test/jvm/annotations_test.rb +3 -6
- data/test/jvm/blocks_test.rb +303 -120
- data/test/jvm/cast_test.rb +123 -50
- data/test/jvm/closure_test.rb +242 -0
- data/test/jvm/constructors_test.rb +1 -3
- data/test/jvm/example_test.rb +6 -2
- data/test/jvm/extensions/array_extensions_test.rb +181 -0
- data/test/jvm/extensions/collection_extensions_test.rb +195 -0
- data/test/jvm/{enumerable_test.rb → extensions/enumerable_test.rb} +81 -13
- data/test/jvm/extensions/hash_extensions_test.rb +56 -0
- data/test/jvm/extensions/list_extensions_test.rb +143 -0
- data/test/jvm/extensions/lock_extensions_test.rb +43 -0
- data/test/jvm/{numeric_extensions_test.rb → extensions/numeric_extensions_test.rb} +0 -0
- data/test/jvm/extensions/numeric_operators_test.rb +86 -0
- data/test/jvm/extensions/object_extensions_test.rb +122 -0
- data/test/jvm/{string_builder_extensions_test.rb → extensions/string_builder_extensions_test.rb} +0 -0
- data/test/jvm/{string_extensions_test.rb → extensions/string_extensions_test.rb} +57 -4
- data/test/jvm/generics_test.rb +14 -6
- data/test/jvm/import_test.rb +38 -1
- data/test/jvm/interface_test.rb +17 -0
- data/test/jvm/jvm_commands_test.rb +9 -0
- data/test/jvm/jvm_compiler_test.rb +568 -43
- data/test/jvm/macros_test.rb +343 -19
- data/test/jvm/main_method_test.rb +1 -3
- data/test/jvm/new_backend_test_helper.rb +54 -7
- data/test/jvm/rescue_test.rb +20 -5
- data/test/jvm/static_fields_test.rb +52 -10
- data/test/jvm/{mirror_compilation_test_helper.rb → string_test.rb} +10 -9
- data/test/jvm/varargs_test.rb +6 -16
- data/test/mirrors/base_type_test.rb +20 -7
- data/test/mirrors/bytecode_mirror_test.rb +8 -3
- data/test/mirrors/generics_test.rb +89 -10
- data/test/mirrors/member_test.rb +1 -1
- data/test/mirrors/method_lookup_test.rb +10 -3
- data/test/mirrors/mirrors_test.rb +20 -20
- data/test/mirrors/simple_async_mirror_loader_test.rb +1 -1
- data/test/mirrors/simple_mirror_loader_test.rb +1 -1
- data/test/newMirahClass$Closure2.class +0 -0
- data/test/newMirahClass.class +0 -0
- data/test/test_helper.rb +8 -1
- metadata +31 -16
- data/bin/bundler +0 -16
- data/bin/rake +0 -16
- data/examples/ant/example-build.xml~ +0 -7
- data/examples/test.edb +0 -9
- data/lib/mirah/compiler.rb +0 -67
- data/lib/mirah/parser.rb +0 -224
- data/lib/mirah/util/delegate.rb +0 -65
- data/test/jvm/list_extensions_test.rb +0 -23
data/test/jvm/macros_test.rb
CHANGED
@@ -12,6 +12,7 @@
|
|
12
12
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
13
13
|
# See the License for the specific language governing permissions and
|
14
14
|
# limitations under the License.
|
15
|
+
require 'test_helper'
|
15
16
|
|
16
17
|
class MacrosTest < Test::Unit::TestCase
|
17
18
|
#TODO perhaps one of these should be an error
|
@@ -29,7 +30,7 @@ class MacrosTest < Test::Unit::TestCase
|
|
29
30
|
foo [2,3] {|x| puts x }
|
30
31
|
EOF
|
31
32
|
|
32
|
-
|
33
|
+
assert_run_output("1\n2\n1\n3\n", cls)
|
33
34
|
end
|
34
35
|
|
35
36
|
def test_block_args_with_pipes_macro
|
@@ -46,7 +47,7 @@ class MacrosTest < Test::Unit::TestCase
|
|
46
47
|
foo [2,3] {|x| puts x }
|
47
48
|
EOF
|
48
49
|
|
49
|
-
|
50
|
+
assert_run_output("1\n2\n1\n3\n", cls)
|
50
51
|
end
|
51
52
|
|
52
53
|
|
@@ -59,7 +60,7 @@ class MacrosTest < Test::Unit::TestCase
|
|
59
60
|
puts(Object(foo))
|
60
61
|
EOF
|
61
62
|
|
62
|
-
|
63
|
+
assert_run_output("null\n", cls)
|
63
64
|
assert(!cls.respond_to?(:foo))
|
64
65
|
end
|
65
66
|
|
@@ -74,7 +75,7 @@ class MacrosTest < Test::Unit::TestCase
|
|
74
75
|
foo
|
75
76
|
EOF
|
76
77
|
|
77
|
-
|
78
|
+
assert_run_output("", cls)
|
78
79
|
assert(!cls.respond_to?(:foo))
|
79
80
|
end
|
80
81
|
|
@@ -87,7 +88,7 @@ class MacrosTest < Test::Unit::TestCase
|
|
87
88
|
puts(Object(foo()))
|
88
89
|
EOF
|
89
90
|
|
90
|
-
|
91
|
+
assert_run_output("null\n", cls)
|
91
92
|
assert(!cls.respond_to?(:foo))
|
92
93
|
end
|
93
94
|
|
@@ -100,7 +101,7 @@ class MacrosTest < Test::Unit::TestCase
|
|
100
101
|
puts(Object(foo))
|
101
102
|
EOF
|
102
103
|
|
103
|
-
|
104
|
+
assert_run_output("null\n", cls)
|
104
105
|
assert(!cls.respond_to?(:foo))
|
105
106
|
end
|
106
107
|
|
@@ -208,6 +209,38 @@ class MacrosTest < Test::Unit::TestCase
|
|
208
209
|
assert_equal("foobar", script.function)
|
209
210
|
end
|
210
211
|
|
212
|
+
def test_static_macro_instance_macro_coexistence_vcall
|
213
|
+
script, cls = compile(%q[
|
214
|
+
class StaticMacrosWithInstanceMacros
|
215
|
+
def foobar
|
216
|
+
"foobar"
|
217
|
+
end
|
218
|
+
|
219
|
+
macro def macro_foobar
|
220
|
+
quote {`@call.target`.foobar}
|
221
|
+
end
|
222
|
+
|
223
|
+
def call_foobar_instance
|
224
|
+
macro_foobar
|
225
|
+
end
|
226
|
+
|
227
|
+
macro def self.macro_foobar
|
228
|
+
quote {`@call.target`.new.foobar}
|
229
|
+
end
|
230
|
+
|
231
|
+
def self.call_foobar_static
|
232
|
+
macro_foobar
|
233
|
+
end
|
234
|
+
end
|
235
|
+
|
236
|
+
def function
|
237
|
+
"#{StaticMacrosWithInstanceMacros.call_foobar_static}\n#{StaticMacrosWithInstanceMacros.new.call_foobar_instance}"
|
238
|
+
end
|
239
|
+
])
|
240
|
+
|
241
|
+
assert_equal("foobar\nfoobar", script.function)
|
242
|
+
end
|
243
|
+
|
211
244
|
|
212
245
|
def test_unquote_method_definitions_with_main
|
213
246
|
script, cls = compile(<<-'EOF')
|
@@ -219,7 +252,7 @@ class MacrosTest < Test::Unit::TestCase
|
|
219
252
|
@`name`
|
220
253
|
end
|
221
254
|
def `"#{name}_set"`(`name`: `type`)
|
222
|
-
@`name` = `name`
|
255
|
+
@`name` = `name` # silly parsing `
|
223
256
|
end
|
224
257
|
end
|
225
258
|
end
|
@@ -233,7 +266,7 @@ class MacrosTest < Test::Unit::TestCase
|
|
233
266
|
puts x.foo
|
234
267
|
EOF
|
235
268
|
|
236
|
-
|
269
|
+
assert_run_output("0\n3\n", script)
|
237
270
|
end
|
238
271
|
|
239
272
|
def test_macro_hygene
|
@@ -282,6 +315,10 @@ class MacrosTest < Test::Unit::TestCase
|
|
282
315
|
end
|
283
316
|
|
284
317
|
def test_add_args_in_macro
|
318
|
+
pend "This is poor-man's splat-operator. It should be replaced by a proper splat-operator or abolished entirely."
|
319
|
+
# This unquote is intended to evaluate to more than just exactly one AST node (that is, 2 nodes in this case) and hence
|
320
|
+
# modifies the NodeList higher in the syntax tree, surprisingly.
|
321
|
+
# Hence, the intention of this unquote violates the Principle of Least Surprise.
|
285
322
|
cls, = compile(<<-EOF)
|
286
323
|
macro def foo(a:Array)
|
287
324
|
quote { bar "1", `a.values`, "2"}
|
@@ -294,9 +331,7 @@ class MacrosTest < Test::Unit::TestCase
|
|
294
331
|
foo(["a", "b"])
|
295
332
|
EOF
|
296
333
|
|
297
|
-
|
298
|
-
cls.main(nil)
|
299
|
-
end
|
334
|
+
assert_run_output("1 a b 2\n", cls)
|
300
335
|
end
|
301
336
|
|
302
337
|
def test_block_parameter_uses_outer_scope
|
@@ -310,9 +345,7 @@ class MacrosTest < Test::Unit::TestCase
|
|
310
345
|
end
|
311
346
|
EOF
|
312
347
|
|
313
|
-
|
314
|
-
cls.main(nil)
|
315
|
-
end
|
348
|
+
assert_run_output("3\n", cls)
|
316
349
|
end
|
317
350
|
|
318
351
|
|
@@ -326,9 +359,7 @@ class MacrosTest < Test::Unit::TestCase
|
|
326
359
|
end
|
327
360
|
EOF
|
328
361
|
|
329
|
-
|
330
|
-
cls.main(nil)
|
331
|
-
end
|
362
|
+
assert_run_output("hi\n", cls)
|
332
363
|
end
|
333
364
|
|
334
365
|
def test_method_def_after_macro_def_with_same_name_raises_error
|
@@ -371,7 +402,43 @@ class MacrosTest < Test::Unit::TestCase
|
|
371
402
|
x.foo = 3
|
372
403
|
puts x.foo
|
373
404
|
EOF
|
374
|
-
|
405
|
+
assert_run_output("0\n3\n", script)
|
406
|
+
end
|
407
|
+
|
408
|
+
def test_protected_attr_accessor
|
409
|
+
script, cls = compile(<<-EOF)
|
410
|
+
class ProtectedAttrAccessorTest
|
411
|
+
protected attr_accessor foo:int
|
412
|
+
|
413
|
+
def selfreflect
|
414
|
+
puts self.getClass.getDeclaredMethod("foo").getModifiers
|
415
|
+
puts self.getClass.getDeclaredMethod("foo_set",[int.class].toArray(Class[0])).getModifiers
|
416
|
+
end
|
417
|
+
end
|
418
|
+
|
419
|
+
ProtectedAttrAccessorTest.new.selfreflect
|
420
|
+
EOF
|
421
|
+
assert_run_output("4\n4\n", script)
|
422
|
+
end
|
423
|
+
|
424
|
+
def test_macro_in_abstract_class
|
425
|
+
pend
|
426
|
+
script, cls = compile(%q{
|
427
|
+
interface I1
|
428
|
+
end
|
429
|
+
|
430
|
+
abstract class C2 implements I1
|
431
|
+
macro def self.bar
|
432
|
+
quote do
|
433
|
+
puts "bar"
|
434
|
+
end
|
435
|
+
end
|
436
|
+
end
|
437
|
+
})
|
438
|
+
script, _ =compile(%q{
|
439
|
+
C2.bar
|
440
|
+
})
|
441
|
+
assert_run_output("bar\n", script)
|
375
442
|
end
|
376
443
|
|
377
444
|
def test_separate_compilation
|
@@ -387,6 +454,263 @@ class MacrosTest < Test::Unit::TestCase
|
|
387
454
|
script, _ =compile(<<-CODE)
|
388
455
|
InlineOneSayer.new.say_one
|
389
456
|
CODE
|
390
|
-
|
457
|
+
assert_run_output("one\n", script)
|
458
|
+
end
|
459
|
+
|
460
|
+
def test_separate_compilation_different_macro_dest
|
461
|
+
compile(<<-CODE, separate_macro_dest: true)
|
462
|
+
class InlineTwoSayer
|
463
|
+
macro def say_two
|
464
|
+
quote do
|
465
|
+
puts "two"
|
466
|
+
end
|
467
|
+
end
|
468
|
+
end
|
469
|
+
CODE
|
470
|
+
script, _ =compile(<<-CODE, separate_macro_dest: true)
|
471
|
+
InlineTwoSayer.new.say_two
|
472
|
+
CODE
|
473
|
+
assert_run_output("two\n", script)
|
474
|
+
end
|
475
|
+
|
476
|
+
def test_import_star_with_macro_def
|
477
|
+
cls1, cls2 = compile([<<-EOF1, <<-EOF2])
|
478
|
+
package org.bar.p1
|
479
|
+
import org.bar.p2.*
|
480
|
+
macro def foo1; end
|
481
|
+
puts MultiPackageImplicitRef.class
|
482
|
+
EOF1
|
483
|
+
package org.bar.p2
|
484
|
+
class MultiPackageImplicitRef; end
|
485
|
+
EOF2
|
486
|
+
|
487
|
+
assert_run_output "class org.bar.p2.MultiPackageImplicitRef\n", cls1
|
488
|
+
end
|
489
|
+
|
490
|
+
def test_explicit_import_of_as_yet_unresolved_type_in_file_with_macro
|
491
|
+
cls1, cls2 = compile([<<-EOF1, <<-EOF2])
|
492
|
+
package org.bar.p1
|
493
|
+
import org.bar.p2.MultiPackageExplicitRef
|
494
|
+
|
495
|
+
macro def foo1; end
|
496
|
+
puts MultiPackageExplicitRef.class
|
497
|
+
EOF1
|
498
|
+
package org.bar.p2
|
499
|
+
class MultiPackageExplicitRef; end
|
500
|
+
EOF2
|
501
|
+
|
502
|
+
assert_run_output "class org.bar.p2.MultiPackageExplicitRef\n", cls1
|
503
|
+
end
|
504
|
+
|
505
|
+
def test_macro_using_imported_unresolved_type_fails_to_compile
|
506
|
+
e = assert_raises Mirah::MirahError do
|
507
|
+
compile([<<-EOF1, <<-EOF2])
|
508
|
+
package org.bar.p1
|
509
|
+
import org.bar.p2.UsedInMacro
|
510
|
+
|
511
|
+
macro def foo1; puts UsedInMacro; quote {}; end
|
512
|
+
foo1
|
513
|
+
EOF1
|
514
|
+
package org.bar.p2
|
515
|
+
class MultiPackageExplicitRef2; end
|
516
|
+
EOF2
|
517
|
+
end
|
518
|
+
assert_equal "UsedInMacro;", e.position
|
519
|
+
assert_equal "Cannot find class org.bar.p1.UsedInMacro", e.message
|
520
|
+
end
|
521
|
+
|
522
|
+
def test_macro_changes_body_of_class_second_but_last_element
|
523
|
+
script, cls = compile(%q{
|
524
|
+
class ChangeableClass
|
525
|
+
macro def self.method_adding_macro
|
526
|
+
node = @call
|
527
|
+
node = node.parent until node.nil? || node.kind_of?(ClassDefinition) # cannot call enclosing_class(), currently
|
528
|
+
klass = ClassDefinition(node)
|
529
|
+
|
530
|
+
klass.body.add(quote do
|
531
|
+
def another_method
|
532
|
+
puts "called"
|
533
|
+
end
|
534
|
+
end)
|
535
|
+
nil
|
536
|
+
end
|
537
|
+
|
538
|
+
method_adding_macro
|
539
|
+
|
540
|
+
def last_body_element
|
541
|
+
1
|
542
|
+
end
|
543
|
+
end
|
544
|
+
|
545
|
+
ChangeableClass.new.another_method
|
546
|
+
})
|
547
|
+
assert_run_output("called\n", script)
|
548
|
+
end
|
549
|
+
|
550
|
+
def test_macro_changes_body_of_class_last_element
|
551
|
+
script, cls = compile(%q{
|
552
|
+
class ChangeableClass
|
553
|
+
macro def self.method_adding_macro
|
554
|
+
node = @call
|
555
|
+
node = node.parent until node.nil? || node.kind_of?(ClassDefinition) # cannot call enclosing_class(), currently
|
556
|
+
klass = ClassDefinition(node)
|
557
|
+
|
558
|
+
klass.body.add(quote do
|
559
|
+
def another_method
|
560
|
+
puts "called"
|
561
|
+
end
|
562
|
+
end)
|
563
|
+
nil
|
564
|
+
end
|
565
|
+
|
566
|
+
method_adding_macro
|
567
|
+
end
|
568
|
+
|
569
|
+
ChangeableClass.new.another_method
|
570
|
+
})
|
571
|
+
assert_run_output("called\n", script)
|
572
|
+
end
|
573
|
+
|
574
|
+
def test_macro_in_class_inheriting_from_previously_defined_class_inheriting_from_later_to_be_defined_class
|
575
|
+
script, cls = compile(%q{
|
576
|
+
interface Bar < Baz
|
577
|
+
end
|
578
|
+
|
579
|
+
class Foo
|
580
|
+
implements Bar
|
581
|
+
|
582
|
+
macro def self.generate_foo
|
583
|
+
quote do
|
584
|
+
def foo
|
585
|
+
puts "foo"
|
586
|
+
end
|
587
|
+
end
|
588
|
+
end
|
589
|
+
|
590
|
+
generate_foo
|
591
|
+
end
|
592
|
+
|
593
|
+
interface Baz
|
594
|
+
end
|
595
|
+
|
596
|
+
Foo.new.foo
|
597
|
+
})
|
598
|
+
assert_run_output("foo\n", script)
|
599
|
+
end
|
600
|
+
|
601
|
+
def test_macro_in_class_inheriting_from_previously_defined_class_inheriting_from_later_to_be_defined_class2
|
602
|
+
script, cls = compile(%q{
|
603
|
+
interface Bar < Baz
|
604
|
+
end
|
605
|
+
|
606
|
+
class Foo
|
607
|
+
implements Bar, Baz
|
608
|
+
|
609
|
+
macro def self.generate_foo
|
610
|
+
quote do
|
611
|
+
def foo
|
612
|
+
puts "foo"
|
613
|
+
end
|
614
|
+
end
|
615
|
+
end
|
616
|
+
|
617
|
+
generate_foo
|
618
|
+
end
|
619
|
+
|
620
|
+
interface Baz
|
621
|
+
end
|
622
|
+
|
623
|
+
Foo.new.foo
|
624
|
+
})
|
625
|
+
assert_run_output("foo\n", script)
|
626
|
+
end
|
627
|
+
|
628
|
+
def test_gensym_clash
|
629
|
+
script, cls = compile(%q{
|
630
|
+
result = []
|
631
|
+
c = lambda(Runnable) do
|
632
|
+
5.times do
|
633
|
+
end
|
634
|
+
end
|
635
|
+
result.each do |r:Runnable|
|
636
|
+
end
|
637
|
+
|
638
|
+
puts result
|
639
|
+
})
|
640
|
+
assert_run_output("[]\n", script)
|
641
|
+
end
|
642
|
+
|
643
|
+
def test_optional_args_macro
|
644
|
+
cls, = compile(<<-CODE)
|
645
|
+
class MacroWithBlock
|
646
|
+
macro def self._test(block:Block = nil)
|
647
|
+
if block
|
648
|
+
block.body
|
649
|
+
else
|
650
|
+
quote { puts "self nil" }
|
651
|
+
end
|
652
|
+
end
|
653
|
+
|
654
|
+
macro def test(block:Block = nil)
|
655
|
+
if block
|
656
|
+
block.body
|
657
|
+
else
|
658
|
+
quote { puts "nil" }
|
659
|
+
end
|
660
|
+
end
|
661
|
+
|
662
|
+
def self.main(args: String[]):void
|
663
|
+
mb = MacroWithBlock.new
|
664
|
+
mb.test
|
665
|
+
mb.test do
|
666
|
+
puts "test"
|
667
|
+
end
|
668
|
+
_test
|
669
|
+
_test do
|
670
|
+
puts "self test"
|
671
|
+
end
|
672
|
+
end
|
673
|
+
end
|
674
|
+
CODE
|
675
|
+
|
676
|
+
assert_run_output("nil\ntest\nself nil\nself test\n", cls)
|
677
|
+
end
|
678
|
+
|
679
|
+
def test_macro_varargs
|
680
|
+
cls, = compile(<<-CODE)
|
681
|
+
class MacroWithVarargs
|
682
|
+
macro def self.vararg(first:Node, *args:Node)
|
683
|
+
list = NodeList.new
|
684
|
+
list.add quote do
|
685
|
+
puts `first`
|
686
|
+
end
|
687
|
+
|
688
|
+
args.each do |arg:Node|
|
689
|
+
m = if arg.kind_of? Block
|
690
|
+
body = Block(arg).body
|
691
|
+
quote do
|
692
|
+
puts `body`
|
693
|
+
end
|
694
|
+
else
|
695
|
+
quote do
|
696
|
+
puts `arg`
|
697
|
+
end
|
698
|
+
end
|
699
|
+
list.add m
|
700
|
+
end
|
701
|
+
list
|
702
|
+
end
|
703
|
+
|
704
|
+
def self.main(*args:String):void
|
705
|
+
vararg 1
|
706
|
+
vararg 1, 2
|
707
|
+
vararg 1, 2, 3
|
708
|
+
vararg 1,2 {"test"}
|
709
|
+
end
|
710
|
+
end
|
711
|
+
CODE
|
712
|
+
|
713
|
+
assert_run_output("1\n1\n2\n1\n2\n3\n1\n2\ntest\n", cls)
|
391
714
|
end
|
392
715
|
end
|
716
|
+
|