flea 0.1.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.
Files changed (113) hide show
  1. data/.gitignore +1 -0
  2. data/MIT-LICENSE +20 -0
  3. data/README.rdoc +274 -0
  4. data/Rakefile +36 -0
  5. data/VERSION +1 -0
  6. data/bin/flea +39 -0
  7. data/examples/guess-the-number.scm +18 -0
  8. data/flea-language-spec/README.rdoc +3 -0
  9. data/flea-language-spec/flea-language-spec.yaml +2 -0
  10. data/flea-language-spec/test-cases/01-display-and-basic-literals/01-display-string-literal.scm +5 -0
  11. data/flea-language-spec/test-cases/01-display-and-basic-literals/02-display-integer-literal.scm +5 -0
  12. data/flea-language-spec/test-cases/01-display-and-basic-literals/03-display-boolean-true-literal.scm +5 -0
  13. data/flea-language-spec/test-cases/01-display-and-basic-literals/04-display-boolean-false-literal.scm +5 -0
  14. data/flea-language-spec/test-cases/01-display-and-basic-literals/05-display-list-literal-using-quote.scm +5 -0
  15. data/flea-language-spec/test-cases/01-display-and-basic-literals/06-display-identifier-quoted.scm +5 -0
  16. data/flea-language-spec/test-cases/02-variables/01-define-with-string.scm +6 -0
  17. data/flea-language-spec/test-cases/02-variables/02-define-with-integer.scm +6 -0
  18. data/flea-language-spec/test-cases/02-variables/03-define-with-boolean-true.scm +6 -0
  19. data/flea-language-spec/test-cases/02-variables/04-define-with-boolean-false.scm +6 -0
  20. data/flea-language-spec/test-cases/02-variables/05-define-with-list.scm +6 -0
  21. data/flea-language-spec/test-cases/03-basic-built-in-procedures/01-addition.scm +5 -0
  22. data/flea-language-spec/test-cases/03-basic-built-in-procedures/02-subtraction.scm +5 -0
  23. data/flea-language-spec/test-cases/03-basic-built-in-procedures/03-multiplication.scm +5 -0
  24. data/flea-language-spec/test-cases/03-basic-built-in-procedures/04-division.scm +5 -0
  25. data/flea-language-spec/test-cases/03-basic-built-in-procedures/05-equality-true-integer.scm +5 -0
  26. data/flea-language-spec/test-cases/03-basic-built-in-procedures/06-equality-false-integer.scm +5 -0
  27. data/flea-language-spec/test-cases/03-basic-built-in-procedures/07-equal?/01-equal?-true-integer.scm +5 -0
  28. data/flea-language-spec/test-cases/03-basic-built-in-procedures/07-equal?/02-equal?-false-integer.scm +5 -0
  29. data/flea-language-spec/test-cases/03-basic-built-in-procedures/07-equal?/03-equal?-true-string.scm +5 -0
  30. data/flea-language-spec/test-cases/03-basic-built-in-procedures/07-equal?/04-equal?-false-string.scm +5 -0
  31. data/flea-language-spec/test-cases/03-basic-built-in-procedures/07-equal?/05-equal?-true-boolean.scm +5 -0
  32. data/flea-language-spec/test-cases/03-basic-built-in-procedures/07-equal?/06-equal?-false-boolean.scm +5 -0
  33. data/flea-language-spec/test-cases/03-basic-built-in-procedures/07-equal?/07-equal?-true-list.scm +7 -0
  34. data/flea-language-spec/test-cases/03-basic-built-in-procedures/07-equal?/08-equal?-false-list.scm +7 -0
  35. data/flea-language-spec/test-cases/04-comma-quoting/01-comma-quoting.scm +5 -0
  36. data/flea-language-spec/test-cases/05-lambda/01-lambda.scm +5 -0
  37. data/flea-language-spec/test-cases/05-lambda/02-call-in-place-lambda.scm +5 -0
  38. data/flea-language-spec/test-cases/05-lambda/03-define-with-lambda.scm +6 -0
  39. data/flea-language-spec/test-cases/05-lambda/04-define-and-call-lambda.scm +8 -0
  40. data/flea-language-spec/test-cases/05-lambda/05-lambda-repeated-argument.scm +9 -0
  41. data/flea-language-spec/test-cases/05-lambda/06-lambda-list-argument.scm +8 -0
  42. data/flea-language-spec/test-cases/05-lambda/07-lambda-n-or-more-arguments.scm +12 -0
  43. data/flea-language-spec/test-cases/05-lambda/08-lambda-should-return-last-result.scm +16 -0
  44. data/flea-language-spec/test-cases/06-if/01-if-true-single-arg.scm +6 -0
  45. data/flea-language-spec/test-cases/06-if/02-if-false-single-arg.scm +6 -0
  46. data/flea-language-spec/test-cases/06-if/03-if-true-two-args.scm +7 -0
  47. data/flea-language-spec/test-cases/06-if/04-if-false-two-args.scm +7 -0
  48. data/flea-language-spec/test-cases/07-set/01-should-not-set-undefined-variable.scm +5 -0
  49. data/flea-language-spec/test-cases/07-set/02-set-previously-defined-variable.scm +8 -0
  50. data/flea-language-spec/test-cases/08-derived-expressions/01-begin/01-begin.scm +8 -0
  51. data/flea-language-spec/test-cases/09-list-manipulation/01-car.scm +7 -0
  52. data/flea-language-spec/test-cases/09-list-manipulation/02-cdr.scm +7 -0
  53. data/flea-language-spec/test-cases/09-list-manipulation/03-list-tail.scm +7 -0
  54. data/flea-language-spec/test-cases/09-list-manipulation/04-append.scm +10 -0
  55. data/flea-language-spec/test-cases/09-list-manipulation/05-list.scm +5 -0
  56. data/flea-language-spec/test-cases/10-functional-examples/countdown.scm +15 -0
  57. data/lib/flea.rb +5 -0
  58. data/lib/flea/environment.rb +38 -0
  59. data/lib/flea/interpreter.rb +67 -0
  60. data/lib/flea/standard_library/addition_operator.scm +7 -0
  61. data/lib/flea/standard_library/append.scm +7 -0
  62. data/lib/flea/standard_library/begin.scm +11 -0
  63. data/lib/flea/standard_library/car.scm +7 -0
  64. data/lib/flea/standard_library/cdr.scm +7 -0
  65. data/lib/flea/standard_library/cons.scm +13 -0
  66. data/lib/flea/standard_library/display.scm +8 -0
  67. data/lib/flea/standard_library/division_operator.scm +7 -0
  68. data/lib/flea/standard_library/equality_operator.scm +8 -0
  69. data/lib/flea/standard_library/gets.scm +6 -0
  70. data/lib/flea/standard_library/greater_than.scm +8 -0
  71. data/lib/flea/standard_library/if.scm +10 -0
  72. data/lib/flea/standard_library/lambda.scm +57 -0
  73. data/lib/flea/standard_library/less_than.scm +8 -0
  74. data/lib/flea/standard_library/list.scm +8 -0
  75. data/lib/flea/standard_library/list_predicate.scm +6 -0
  76. data/lib/flea/standard_library/list_tail.scm +5 -0
  77. data/lib/flea/standard_library/multiplication_operator.scm +7 -0
  78. data/lib/flea/standard_library/null.scm +3 -0
  79. data/lib/flea/standard_library/quote.scm +6 -0
  80. data/lib/flea/standard_library/rand.scm +6 -0
  81. data/lib/flea/standard_library/read.scm +6 -0
  82. data/lib/flea/standard_library/set.scm +9 -0
  83. data/lib/flea/standard_library/string_to_num.scm +6 -0
  84. data/lib/flea/standard_library/subtraction_operator.scm +7 -0
  85. data/spec/flea/environment_spec.rb +114 -0
  86. data/spec/flea/interpreter_spec.rb +85 -0
  87. data/spec/flea/standard_library/addition_operator_spec.rb +23 -0
  88. data/spec/flea/standard_library/append_spec.rb +17 -0
  89. data/spec/flea/standard_library/begin_spec.rb +20 -0
  90. data/spec/flea/standard_library/car_spec.rb +17 -0
  91. data/spec/flea/standard_library/cdr_spec.rb +17 -0
  92. data/spec/flea/standard_library/cons_spec.rb +25 -0
  93. data/spec/flea/standard_library/display_spec.rb +36 -0
  94. data/spec/flea/standard_library/division_operator_spec.rb +29 -0
  95. data/spec/flea/standard_library/equality_operator_spec.rb +45 -0
  96. data/spec/flea/standard_library/gets_spec.rb +23 -0
  97. data/spec/flea/standard_library/greater_than_spec.rb +29 -0
  98. data/spec/flea/standard_library/if_spec.rb +59 -0
  99. data/spec/flea/standard_library/lambda_spec.rb +70 -0
  100. data/spec/flea/standard_library/less_than_spec.rb +29 -0
  101. data/spec/flea/standard_library/list_predicate_spec.rb +25 -0
  102. data/spec/flea/standard_library/list_spec.rb +24 -0
  103. data/spec/flea/standard_library/list_tail_spec.rb +17 -0
  104. data/spec/flea/standard_library/mutiplication_operator_spec.rb +23 -0
  105. data/spec/flea/standard_library/null_spec.rb +26 -0
  106. data/spec/flea/standard_library/quote_spec.rb +20 -0
  107. data/spec/flea/standard_library/rand_spec.rb +25 -0
  108. data/spec/flea/standard_library/read_spec.rb +23 -0
  109. data/spec/flea/standard_library/set_spec.rb +23 -0
  110. data/spec/flea/standard_library/string_to_num_spec.rb +28 -0
  111. data/spec/flea/standard_library/subtraction_operator_spec.rb +24 -0
  112. data/spec/spec_helper.rb +10 -0
  113. metadata +231 -0
@@ -0,0 +1,7 @@
1
+ (define +
2
+ (native_function "
3
+ Proc.new() do |arguments, interpreter|
4
+ tmp = arguments.map {|item| interpreter.evaluate(item)}
5
+ tmp.inject {|sum, n| sum + n}
6
+ end
7
+ "))
@@ -0,0 +1,7 @@
1
+ (define append
2
+ (native_function "
3
+ Proc.new() do |arguments, interpreter|
4
+
5
+ arguments.map {|x| interpreter.evaluate(x) }.flatten
6
+ end
7
+ "))
@@ -0,0 +1,11 @@
1
+ (define begin
2
+ (native_function "
3
+ Proc.new() do |arguments, interpreter|
4
+ val = nil
5
+ arguments.each do |i|
6
+ val = interpreter.evaluate(i)
7
+ end
8
+
9
+ val
10
+ end
11
+ "))
@@ -0,0 +1,7 @@
1
+ (define car
2
+ (native_function "
3
+ Proc.new() do |argument, interpreter|
4
+ list = interpreter.evaluate(argument[0])
5
+ list[0]
6
+ end
7
+ "))
@@ -0,0 +1,7 @@
1
+ (define cdr
2
+ (native_function "
3
+ Proc.new() do |argument, interpreter|
4
+ list = interpreter.evaluate(argument[0])
5
+ list.slice(1, list.length)
6
+ end
7
+ "))
@@ -0,0 +1,13 @@
1
+ (define cons
2
+ (native_function "
3
+ Proc.new() do |arguments, interpreter|
4
+ arg_1 = interpreter.evaluate(arguments[0])
5
+ arg_2 = interpreter.evaluate(arguments[1]).dup
6
+
7
+ if arg_2.is_a? Array
8
+ arg_2.unshift arg_1
9
+ else
10
+ [arg_1, arg_2]
11
+ end
12
+ end
13
+ "))
@@ -0,0 +1,8 @@
1
+ (define display
2
+ (native_function "
3
+ Proc.new() do |arguments, interpreter|
4
+ output = interpreter.evaluate(arguments[0])
5
+ print interpreter.parser.to_sexp(output)
6
+ output
7
+ end
8
+ "))
@@ -0,0 +1,7 @@
1
+ (define /
2
+ (native_function "
3
+ Proc.new() do |arguments, interpreter|
4
+ tmp = arguments.map {|item| interpreter.evaluate(item)}
5
+ tmp.inject {|sum, n| sum / n}
6
+ end
7
+ "))
@@ -0,0 +1,8 @@
1
+ (define =
2
+ (native_function "
3
+ Proc.new() do |arguments, interpreter|
4
+ !(arguments.map{|x| interpreter.evaluate(x) == interpreter.evaluate(arguments[0])}).include?( false )
5
+ end
6
+ "))
7
+
8
+ (define equal? =)
@@ -0,0 +1,6 @@
1
+ (define gets
2
+ (native_function "
3
+ Proc.new() do |arguments, interpreter|
4
+ $stdin.gets
5
+ end
6
+ "))
@@ -0,0 +1,8 @@
1
+ (define greater-than?
2
+ (native_function "
3
+ Proc.new() do |arguments, interpreter|
4
+ arguments.slice(1, arguments.length).all? do |x|
5
+ interpreter.evaluate(arguments[0]) > interpreter.evaluate(x)
6
+ end
7
+ end
8
+ "))
@@ -0,0 +1,10 @@
1
+ (define if
2
+ (native_function "
3
+ Proc.new() do |arguments, interpreter|
4
+ if(interpreter.evaluate(arguments[0]))
5
+ interpreter.evaluate(arguments[1])
6
+ else
7
+ interpreter.evaluate(arguments[2]) unless(arguments[2].nil?)
8
+ end
9
+ end
10
+ "))
@@ -0,0 +1,57 @@
1
+ (define lambda
2
+ (native_function "
3
+ Proc.new() do |arguments, interpreter|
4
+ formals = arguments[0]
5
+ body = arguments.slice(1, arguments.length)
6
+
7
+ if formals.is_a? Array
8
+ # detect if any formal names have been used more than once
9
+ error_message = 'Formal {FORMAL} declared more than once'
10
+ formals.each_index do |x|
11
+ tmp = formals.dup
12
+ tmp.delete_at(x)
13
+ raise(error_message.gsub('{FORMAL}', formals[x])) if tmp.include? formals[x]
14
+ end
15
+ end
16
+
17
+ sub_env = Flea::Environment.new(interpreter.current_environment)
18
+
19
+ execute_body = Proc.new() do |body, environment, interpreter|
20
+ interpreter.current_environment = environment
21
+ result = nil
22
+ body.each do |expression|
23
+ result = interpreter.evaluate(expression)
24
+ end
25
+ interpreter.current_environment = environment.parent
26
+ result
27
+ end
28
+
29
+ if formals.is_a?(Array) && formals.include?(:'.')
30
+ Proc.new() do |arguments, interpreter|
31
+ args = arguments.dup
32
+ named_formals = formals.slice(0, formals.index(:'.'))
33
+ list_formal = formals[formals.index(:'.') + 1]
34
+ named_formals.each_index do |i|
35
+ sub_env.define(named_formals[i], interpreter.evaluate(args.shift))
36
+ end
37
+ sub_env.define(list_formal, args)
38
+ execute_body.call(body, sub_env, interpreter)
39
+ end
40
+
41
+ elsif formals.is_a? Array
42
+ Proc.new() do |arguments, interpreter|
43
+ formals.each_index do |i|
44
+ sub_env.define(formals[i], interpreter.evaluate(arguments[i]))
45
+ end
46
+ execute_body.call(body, sub_env, interpreter)
47
+ end
48
+
49
+ elsif formals.is_a? Symbol
50
+ Proc.new() do |arguments, interpreter|
51
+ arguments = arguments.map {|x| interpreter.evaluate(x) }
52
+ sub_env.define(formals, arguments)
53
+ execute_body.call(body, sub_env, interpreter)
54
+ end
55
+ end
56
+ end
57
+ "))
@@ -0,0 +1,8 @@
1
+ (define less-than?
2
+ (native_function "
3
+ Proc.new() do |arguments, interpreter|
4
+ arguments.slice(1, arguments.length).all? do |x|
5
+ interpreter.evaluate(arguments[0]) < interpreter.evaluate(x)
6
+ end
7
+ end
8
+ "))
@@ -0,0 +1,8 @@
1
+ (define list
2
+ (native_function "
3
+ Proc.new() do |arguments, interpreter|
4
+ arguments.map {|x|
5
+ interpreter.evaluate(x)
6
+ }
7
+ end
8
+ "))
@@ -0,0 +1,6 @@
1
+ (define list?
2
+ (native_function "
3
+ Proc.new() do |arguments, interpreter|
4
+ interpreter.evaluate(arguments[0]).is_a? Array
5
+ end
6
+ "))
@@ -0,0 +1,5 @@
1
+ (define list-tail
2
+ (lambda (x k)
3
+ (if (equal? k 0)
4
+ x
5
+ (list-tail (cdr x) (- k 1)))))
@@ -0,0 +1,7 @@
1
+ (define *
2
+ (native_function "
3
+ Proc.new() do |arguments, interpreter|
4
+ tmp = arguments.map {|item| interpreter.evaluate(item)}
5
+ tmp.inject {|sum, n| sum * n}
6
+ end
7
+ "))
@@ -0,0 +1,3 @@
1
+ (define null?
2
+ (lambda (arg)
3
+ (equal? arg '())))
@@ -0,0 +1,6 @@
1
+ (define quote
2
+ (native_function "
3
+ Proc.new() do |arguments, interpreter|
4
+ arguments[0]
5
+ end
6
+ "))
@@ -0,0 +1,6 @@
1
+ (define rand
2
+ (native_function "
3
+ Proc.new() do |arguments, interpreter|
4
+ rand(interpreter.evaluate(arguments[0]))
5
+ end
6
+ "))
@@ -0,0 +1,6 @@
1
+ (define read
2
+ (native_function "
3
+ Proc.new() do |arguments, interpreter|
4
+ interpreter.parser.parse_string($stdin.gets)
5
+ end
6
+ "))
@@ -0,0 +1,9 @@
1
+ (define set!
2
+ (native_function "
3
+ Proc.new() do |arguments, interpreter|
4
+ if( interpreter.current_environment.find(arguments[0]) == nil)
5
+ raise 'Cannot set unbound variable ' + arguments[0]
6
+ end
7
+ interpreter.current_environment.define(arguments[0], arguments[1])
8
+ end
9
+ "))
@@ -0,0 +1,6 @@
1
+ (define string-to-num
2
+ (native_function "
3
+ Proc.new() do |arguments, interpreter|
4
+ interpreter.evaluate(arguments[0]).to_i
5
+ end
6
+ "))
@@ -0,0 +1,7 @@
1
+ (define -
2
+ (native_function "
3
+ Proc.new() do |arguments, interpreter|
4
+ tmp = arguments.map {|item| interpreter.evaluate(item) }
5
+ tmp.inject {|sum, n| sum - n }
6
+ end
7
+ "))
@@ -0,0 +1,114 @@
1
+ require File.dirname(__FILE__) + '/../spec_helper'
2
+
3
+ describe "Flea" do
4
+ describe "::Environment" do
5
+
6
+ include Flea
7
+
8
+ describe ".new" do
9
+ it "should return an environment object" do
10
+ Environment.new.should be_an Environment
11
+ end
12
+
13
+ it "should return an environment object with no parent" do
14
+ environment = Environment.new
15
+ environment.parent.should be_nil
16
+ end
17
+
18
+ it "should return an environment object with the specified parent" do
19
+ parent_environment = mock("Environment")
20
+ environment = Environment.new(parent_environment)
21
+ environment.parent.should be parent_environment
22
+ end
23
+
24
+ it "should add base variables for #t and #f" do
25
+ environment = Environment.new
26
+ environment.should have_variable :"#t"
27
+ environment.should have_variable :"#f"
28
+ environment.find(:"#t").should be_true
29
+ environment.find(:"#f").should be_false
30
+ end
31
+ end
32
+
33
+ describe "#has_variable?" do
34
+ context "without a parent" do
35
+ before :each do
36
+ @environment = Environment.new
37
+ end
38
+
39
+ it "should return false if the variable is not set in the current environment" do
40
+ @environment.should_not have_variable :test
41
+ end
42
+
43
+ it "should return true if the variable is set in the current environment" do
44
+ @environment.define(:test, 1)
45
+ @environment.should have_variable :test
46
+ end
47
+ end
48
+
49
+ context "with a parent" do
50
+ before :each do
51
+ @parent_environment = Environment.new
52
+ @environment = Environment.new(@parent_environment)
53
+ end
54
+
55
+ it "should return false if the variable is not set in the parent environment" do
56
+ @environment.should_not have_variable :test
57
+ end
58
+
59
+ it "should return true if the variable is set in the parent environment" do
60
+ @parent_environment.define(:test, 1)
61
+ @environment.should have_variable :test
62
+ end
63
+ end
64
+ end
65
+
66
+ describe "#define" do
67
+ it "should set a variable to the supplied value" do
68
+ env = Environment.new
69
+ result = env.define(:test, 1)
70
+ env.should have_variable :test
71
+ env.find(:test).should == 1
72
+ result.should == 1
73
+ end
74
+ end
75
+
76
+ describe "#find" do
77
+ context "without a parent" do
78
+ before :each do
79
+ @environment = Environment.new
80
+ @environment.define(:test, 1)
81
+ end
82
+
83
+ it "should find a variable in the current environment" do
84
+ @environment.find(:test).should == 1
85
+ end
86
+
87
+ it "should return nil when variable is not set" do
88
+ @environment.find(:fake).should be_nil
89
+ end
90
+ end
91
+
92
+ context "with a parent" do
93
+ before :each do
94
+ @parent_environment = Environment.new
95
+ @environment = Environment.new(@parent_environment)
96
+ @parent_environment.define(:test, 1)
97
+ end
98
+
99
+ it "should find a variable in the parent environment" do
100
+ @environment.find(:test).should == 1
101
+ end
102
+
103
+ it "should return a variable from the current environment if it is set in both current and parent" do
104
+ @environment.define(:test, 5)
105
+ @environment.find(:test).should == 5
106
+ end
107
+
108
+ it "should return nil when variable is not set in the current or parent environment" do
109
+ @environment.find(:fake).should be_nil
110
+ end
111
+ end
112
+ end
113
+ end
114
+ end
@@ -0,0 +1,85 @@
1
+ require File.dirname(__FILE__) + '/../spec_helper'
2
+
3
+ describe "Flea" do
4
+ describe "::Interpreter" do
5
+
6
+ include Flea
7
+
8
+ describe "#new" do
9
+ it "should return an Interpreter" do
10
+ Interpreter.new.should be_an Interpreter
11
+ end
12
+
13
+ it "should allow setting the base environment to use" do
14
+ environment = mock("Environment")
15
+ interpreter = Interpreter.new(
16
+ :base_environment => environment,
17
+ :load_standard_library => false
18
+ )
19
+ interpreter.base_environment.should be environment
20
+ end
21
+ end
22
+
23
+ describe ".run" do
24
+ it "should run a program and return the last output from the program" do
25
+ interpreter = Interpreter.new
26
+ result = interpreter.run("(define test 1)")
27
+ result.should == 1
28
+ interpreter.base_environment.should have_variable :test
29
+ end
30
+ end
31
+
32
+ describe ".parse" do
33
+ it "should return an abstract syntax tree representing the supplied program" do
34
+ ast = Interpreter.new.parse("(define test 1)")
35
+ ast.should == [[:define, :test, 1]]
36
+ end
37
+ end
38
+
39
+ describe ".evaluate" do
40
+ before :each do
41
+ @environment = mock("Environment")
42
+ @interpreter = Interpreter.new(
43
+ :base_environment => @environment,
44
+ :load_standard_library => false
45
+ )
46
+ end
47
+
48
+ it "should return the value of a variable" do
49
+ @environment.should_receive(:find).with(:test).and_return(1)
50
+ result = @interpreter.evaluate(:test)
51
+ result.should == 1
52
+ end
53
+
54
+ it "should define a variable in the current environment" do
55
+ @environment.should_receive(:define).with(:test, 1).and_return(1)
56
+ result = @interpreter.evaluate([:define, :test, 1])
57
+ result.should == 1
58
+ end
59
+
60
+ it "should create a native function" do
61
+ result = @interpreter.evaluate([:native_function, "
62
+ Proc.new() do |arguments, interpreter|
63
+ 1
64
+ end
65
+ "])
66
+ result.should be_a Proc
67
+ result.call.should == 1
68
+ end
69
+
70
+ it "should call a native function" do
71
+ @environment.should_receive(:find).with(:foo).and_return(Proc.new {|a,b| "bar"})
72
+ result = @interpreter.evaluate([:foo, 1, 2, 3])
73
+ result.should == "bar"
74
+ end
75
+
76
+ [1, 1.0, "string"].each do |literal|
77
+ it "should return literal '#{literal}' as is" do
78
+ result = @interpreter.evaluate(literal)
79
+ result.should be literal
80
+ end
81
+ end
82
+ end
83
+
84
+ end
85
+ end