code-ruby-parser 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 (96) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +1 -0
  3. data/.overcommit.yml +4 -0
  4. data/Gemfile +5 -0
  5. data/Gemfile.lock +32 -0
  6. data/bin/code-parser +27 -0
  7. data/bin/format +3 -0
  8. data/bin/template-parser +27 -0
  9. data/docs/class.code +9 -0
  10. data/docs/meetup.code +14 -0
  11. data/docs/rain.code +23 -0
  12. data/docs/slack.code +17 -0
  13. data/docs/stripe.code +7 -0
  14. data/docs/twitter.code +7 -0
  15. data/lib/code/parser/addition.rb +13 -0
  16. data/lib/code/parser/and_operator.rb +13 -0
  17. data/lib/code/parser/bitwise_and.rb +13 -0
  18. data/lib/code/parser/bitwise_or.rb +13 -0
  19. data/lib/code/parser/boolean.rb +13 -0
  20. data/lib/code/parser/call.rb +174 -0
  21. data/lib/code/parser/chained_call.rb +41 -0
  22. data/lib/code/parser/class.rb +56 -0
  23. data/lib/code/parser/code.rb +20 -0
  24. data/lib/code/parser/comments.rb +46 -0
  25. data/lib/code/parser/dictionnary.rb +48 -0
  26. data/lib/code/parser/equal.rb +39 -0
  27. data/lib/code/parser/equality.rb +20 -0
  28. data/lib/code/parser/error/syntax_error.rb +36 -0
  29. data/lib/code/parser/error.rb +6 -0
  30. data/lib/code/parser/function.rb +109 -0
  31. data/lib/code/parser/greater_than.rb +13 -0
  32. data/lib/code/parser/group.rb +15 -0
  33. data/lib/code/parser/identifier.rb +54 -0
  34. data/lib/code/parser/if.rb +81 -0
  35. data/lib/code/parser/if_modifier.rb +39 -0
  36. data/lib/code/parser/list.rb +20 -0
  37. data/lib/code/parser/multiplication.rb +13 -0
  38. data/lib/code/parser/negation.rb +18 -0
  39. data/lib/code/parser/not_keyword.rb +24 -0
  40. data/lib/code/parser/nothing.rb +13 -0
  41. data/lib/code/parser/number.rb +39 -0
  42. data/lib/code/parser/operation.rb +44 -0
  43. data/lib/code/parser/or_keyword.rb +13 -0
  44. data/lib/code/parser/or_operator.rb +13 -0
  45. data/lib/code/parser/power.rb +33 -0
  46. data/lib/code/parser/range.rb +13 -0
  47. data/lib/code/parser/rescue.rb +38 -0
  48. data/lib/code/parser/shift.rb +13 -0
  49. data/lib/code/parser/statement.rb +9 -0
  50. data/lib/code/parser/string.rb +61 -0
  51. data/lib/code/parser/ternary.rb +73 -0
  52. data/lib/code/parser/unary_minus.rb +23 -0
  53. data/lib/code/parser/while.rb +36 -0
  54. data/lib/code/parser.rb +237 -0
  55. data/lib/code-ruby-parser.rb +7 -0
  56. data/lib/code.rb +2 -0
  57. data/lib/template/parser.rb +32 -0
  58. data/lib/template-ruby-parser.rb +7 -0
  59. data/lib/template.rb +2 -0
  60. data/spec/code/parser/addition_spec.rb +26 -0
  61. data/spec/code/parser/and_operator_spec.rb +26 -0
  62. data/spec/code/parser/bitwise_and_spec.rb +26 -0
  63. data/spec/code/parser/bitwise_or_spec.rb +26 -0
  64. data/spec/code/parser/boolean_spec.rb +13 -0
  65. data/spec/code/parser/call_spec.rb +52 -0
  66. data/spec/code/parser/chained_call_spec.rb +33 -0
  67. data/spec/code/parser/class_spec.rb +32 -0
  68. data/spec/code/parser/code_spec.rb +13 -0
  69. data/spec/code/parser/dictionnary_spec.rb +40 -0
  70. data/spec/code/parser/equal_spec.rb +42 -0
  71. data/spec/code/parser/equality_spec.rb +26 -0
  72. data/spec/code/parser/function_spec.rb +43 -0
  73. data/spec/code/parser/greater_than_spec.rb +26 -0
  74. data/spec/code/parser/group_spec.rb +13 -0
  75. data/spec/code/parser/if_modifier_spec.rb +26 -0
  76. data/spec/code/parser/if_spec.rb +39 -0
  77. data/spec/code/parser/list_spec.rb +27 -0
  78. data/spec/code/parser/multiplication_spec.rb +26 -0
  79. data/spec/code/parser/negation_spec.rb +13 -0
  80. data/spec/code/parser/not_keyword_spec.rb +21 -0
  81. data/spec/code/parser/nothing_spec.rb +20 -0
  82. data/spec/code/parser/number_spec.rb +24 -0
  83. data/spec/code/parser/or_keyword_spec.rb +26 -0
  84. data/spec/code/parser/or_operator_spec.rb +26 -0
  85. data/spec/code/parser/power_spec.rb +21 -0
  86. data/spec/code/parser/range_spec.rb +21 -0
  87. data/spec/code/parser/rescue_spec.rb +26 -0
  88. data/spec/code/parser/shift_spec.rb +26 -0
  89. data/spec/code/parser/string_spec.rb +27 -0
  90. data/spec/code/parser/ternary_spec.rb +26 -0
  91. data/spec/code/parser/unary_minus_spec.rb +21 -0
  92. data/spec/code/parser/while_spec.rb +32 -0
  93. data/spec/spec_helper.rb +6 -0
  94. data/spec/template/parser_spec.rb +13 -0
  95. data/template-ruby-parser.gemspec +16 -0
  96. metadata +171 -0
@@ -0,0 +1,39 @@
1
+ class Code
2
+ class Parser
3
+ class Equal < ::Code::Parser
4
+ def parse
5
+ left = parse_subclass(::Code::Parser::Rescue)
6
+
7
+ previous_cursor = cursor
8
+
9
+ comments_before = parse_comments
10
+
11
+ if operator = match(EQUALS)
12
+ previous_cursor = cursor
13
+ comments_after = parse_comments
14
+
15
+ right = parse_subclass(::Code::Parser::Equal)
16
+
17
+ if right
18
+ {
19
+ equal: {
20
+ left: left,
21
+ right: right,
22
+ comments_before: comments_before,
23
+ comments_after: comments_after
24
+ }.compact
25
+ }
26
+ else
27
+ @cursor = previous_cursor
28
+ buffer!
29
+ { equal: { left: left, comments_before: comments_before } }
30
+ end
31
+ else
32
+ @cursor = previous_cursor
33
+ buffer!
34
+ left
35
+ end
36
+ end
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,20 @@
1
+ class Code
2
+ class Parser
3
+ class Equality < ::Code::Parser
4
+ def parse
5
+ parse_subclass(
6
+ ::Code::Parser::Operation,
7
+ operators: [
8
+ EQUAL + EQUAL + EQUAL,
9
+ LESSER + EQUAL + GREATER,
10
+ EQUAL + TILDE,
11
+ EXCLAMATION_POINT + TILDE,
12
+ EQUAL + EQUAL,
13
+ EXCLAMATION_POINT + EQUAL
14
+ ],
15
+ subclass: ::Code::Parser::While
16
+ )
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,36 @@
1
+ class Code
2
+ class Parser
3
+ class Error
4
+ class SyntaxError < ::Code::Parser::Error
5
+ def initialize(
6
+ message,
7
+ input:,
8
+ line:,
9
+ column:,
10
+ offset_lines:,
11
+ offset_columns:
12
+ )
13
+ @message = message
14
+ @input = input
15
+ @line = line
16
+ @column = column
17
+ @offset_lines = offset_lines
18
+ @offset_columns = offset_columns
19
+ end
20
+
21
+ def message
22
+ @message + "\n\n" + line_message + "\n" + " " * column +
23
+ "^" * offset_columns
24
+ end
25
+
26
+ private
27
+
28
+ def line_message
29
+ input.lines[line..(line + offset_lines)].join
30
+ end
31
+
32
+ attr_reader :input, :line, :column, :offset_lines, :offset_columns
33
+ end
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,6 @@
1
+ class Code
2
+ class Parser
3
+ class Error < ::StandardError
4
+ end
5
+ end
6
+ end
@@ -0,0 +1,109 @@
1
+ class Code
2
+ class Parser
3
+ class Function < ::Code::Parser
4
+ def parse
5
+ return parse_next unless next?(OPENING_PARENTHESIS)
6
+ previous_cursor = cursor
7
+ match(OPENING_PARENTHESIS)
8
+ parameters_comments = parse_comments
9
+ parameters = parse_parameters
10
+ match(CLOSING_PARENTHESIS)
11
+ comments_before = parse_comments
12
+ if match(EQUAL + GREATER)
13
+ comments_after = parse_comments
14
+ if match(OPENING_CURLY_BRACKET)
15
+ body = parse_code
16
+ match(CLOSING_CURLY_BRACKET)
17
+ {
18
+ function: {
19
+ parameters: parameters,
20
+ body: body,
21
+ parameters_comments: parameters_comments,
22
+ comments_before: comments_before,
23
+ comments_after: comments_after
24
+ }.compact
25
+ }
26
+ else
27
+ buffer!
28
+ @cursor = previous_cursor
29
+ parse_next
30
+ end
31
+ else
32
+ buffer!
33
+ @cursor = previous_cursor
34
+ parse_next
35
+ end
36
+ end
37
+
38
+ def parse_next
39
+ parse_subclass(::Code::Parser::ChainedCall)
40
+ end
41
+
42
+ def parse_parameters
43
+ parameters = []
44
+
45
+ parameters << (parse_keyword_parameter || parse_regular_parameter)
46
+
47
+ while match(COMMA) && !end_of_input?
48
+ parameters << (parse_keyword_parameter || parse_regular_parameter)
49
+ end
50
+
51
+ parameters.compact.empty? ? nil : parameters.compact
52
+ end
53
+
54
+ def parse_keyword_parameter
55
+ previous_cursor = cursor
56
+
57
+ comments_before = parse_comments
58
+
59
+ key = parse_subclass(::Code::Parser::Identifier)
60
+
61
+ comments_after = parse_comments
62
+
63
+ if key && (match(COLON) || match(EQUAL + GREATER))
64
+ default = parse_code
65
+
66
+ {
67
+ default: default,
68
+ keyword: true,
69
+ comments_before: comments_before,
70
+ comments_after: comments_after,
71
+ **key
72
+ }.compact
73
+ else
74
+ @cursor = previous_cursor
75
+ buffer!
76
+ return
77
+ end
78
+ end
79
+
80
+ def parse_regular_parameter
81
+ previous_cursor = cursor
82
+ comments_before = parse_comments
83
+
84
+ identifier = parse_subclass(::Code::Parser::Identifier)
85
+
86
+ comments_after = parse_comments
87
+
88
+ if identifier
89
+ if match(EQUAL)
90
+ default = parse_code
91
+ else
92
+ default = nil
93
+ end
94
+
95
+ {
96
+ default: default,
97
+ comments_before: comments_before,
98
+ comments_after: comments_after,
99
+ **identifier
100
+ }.compact
101
+ else
102
+ @cursor = previous_cursor
103
+ buffer!
104
+ return
105
+ end
106
+ end
107
+ end
108
+ end
109
+ end
@@ -0,0 +1,13 @@
1
+ class Code
2
+ class Parser
3
+ class GreaterThan < ::Code::Parser
4
+ def parse
5
+ parse_subclass(
6
+ ::Code::Parser::Operation,
7
+ operators: [GREATER + EQUAL, LESSER + EQUAL, GREATER, LESSER],
8
+ subclass: ::Code::Parser::BitwiseOr
9
+ )
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,15 @@
1
+ class Code
2
+ class Parser
3
+ class Group < ::Code::Parser
4
+ def parse
5
+ if match(OPENING_PARENTHESIS)
6
+ code = parse_subclass(::Code::Parser::Code)
7
+ match(CLOSING_PARENTHESIS)
8
+ { group: code }
9
+ else
10
+ parse_subclass(::Code::Parser::Call)
11
+ end
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,54 @@
1
+ class Code
2
+ class Parser
3
+ class Identifier < ::Code::Parser
4
+ def initialize(input, simple: false, **kargs)
5
+ super(input, **kargs)
6
+ @simple = simple
7
+ end
8
+
9
+ def parse
10
+ return if end_of_input?
11
+
12
+ previous_cursor = cursor
13
+
14
+ if !simple? && match(AMPERSAND) && !next?(SPECIAL)
15
+ kind = :block
16
+ elsif !simple && match(ASTERISK + ASTERISK) && !next?(SPECIAL)
17
+ kind = :keyword
18
+ elsif !simple? && match(ASTERISK) && !next?(SPECIAL)
19
+ kind = :regular
20
+ elsif !next?(SPECIAL)
21
+ kind = nil
22
+ else
23
+ @cursor = previous_cursor
24
+ buffer!
25
+ return
26
+ end
27
+
28
+ buffer!
29
+
30
+ consume while !next?(SPECIAL) && !end_of_input?
31
+
32
+ match(QUESTION_MARK) || match(EXCLAMATION_POINT) if !simple?
33
+
34
+ name = buffer!
35
+
36
+ if KEYWORDS.include?(name)
37
+ @cursor = previous_cursor
38
+ buffer!
39
+ return
40
+ else
41
+ { name: name, kind: kind }.compact
42
+ end
43
+ end
44
+
45
+ private
46
+
47
+ attr_reader :simple
48
+
49
+ def simple?
50
+ !!simple
51
+ end
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,81 @@
1
+ class Code
2
+ class Parser
3
+ class If < ::Code::Parser
4
+ def parse
5
+ if first_operator = match([IF_KEYWORD, UNLESS_KEYWORD])
6
+ previous_cursor = cursor
7
+ first_comments = parse_comments
8
+ first_condition = parse_subclass(::Code::Parser::If)
9
+
10
+ if first_condition
11
+ first_body = parse_code
12
+
13
+ others = []
14
+
15
+ while (other = parse_else) || (other = parse_elsif)
16
+ others << other
17
+ end
18
+
19
+ others = nil if others.empty?
20
+
21
+ match(END_KEYWORD)
22
+
23
+ {
24
+ if: {
25
+ first_operator: first_operator,
26
+ first_comments: first_comments,
27
+ first_condition: first_condition,
28
+ first_body: first_body,
29
+ others: others
30
+ }.compact
31
+ }
32
+ else
33
+ @cursor = previous_cursor
34
+ buffer!
35
+ parse_subclass(::Code::Parser::IfModifier)
36
+ end
37
+ else
38
+ parse_subclass(::Code::Parser::IfModifier)
39
+ end
40
+ end
41
+
42
+ private
43
+
44
+ def parse_else
45
+ return unless match(ELSE_KEYWORD)
46
+ comments_before = parse_comments
47
+
48
+ operator = match([IF_KEYWORD, UNLESS_KEYWORD]) || ELSE_KEYWORD
49
+
50
+ comments_after = parse_comments
51
+
52
+ condition = parse_subclass(::Code::Parser::If)
53
+
54
+ body = parse_code
55
+
56
+ {
57
+ operator: operator,
58
+ comments_before: comments_before,
59
+ comments_after: comments_after,
60
+ condition: condition,
61
+ body: body
62
+ }.compact
63
+ end
64
+
65
+ def parse_elsif
66
+ return unless operator = match([ELSIF_KEYWORD, ELSUNLESS_KEYWORD])
67
+
68
+ comments = parse_comments
69
+ condition = parse_subclass(::Code::Parser::If)
70
+ body = parse_code
71
+
72
+ {
73
+ operator: operator,
74
+ comments: comments,
75
+ condition: condition,
76
+ body: body
77
+ }.compact
78
+ end
79
+ end
80
+ end
81
+ end
@@ -0,0 +1,39 @@
1
+ class Code
2
+ class Parser
3
+ class IfModifier < ::Code::Parser
4
+ def parse
5
+ left = parse_subclass(::Code::Parser::OrKeyword)
6
+
7
+ previous_cursor = cursor
8
+
9
+ comments_before = parse_comments(whitespace: [SPACE])
10
+
11
+ if left && (operator = match(IF_KEYWORD)) ||
12
+ (operator = match(UNLESS_KEYWORD))
13
+ comments_after = parse_comments
14
+ right = parse_subclass(::Code::Parser::IfModifier)
15
+
16
+ if right
17
+ {
18
+ if_modifier: {
19
+ left: left,
20
+ right: right,
21
+ operator: operator,
22
+ comments_before: comments_before,
23
+ comments_after: comments_after
24
+ }.compact
25
+ }
26
+ else
27
+ @cursor = previous_cursor
28
+ buffer!
29
+ left
30
+ end
31
+ else
32
+ @cursor = previous_cursor
33
+ buffer!
34
+ left
35
+ end
36
+ end
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,20 @@
1
+ class Code
2
+ class Parser
3
+ class List < ::Code::Parser
4
+ def parse
5
+ if match(OPENING_SQUARE_BRACKET)
6
+ list = []
7
+
8
+ list << parse_code
9
+ list << parse_code while match(COMMA) && !end_of_input?
10
+
11
+ match(CLOSING_SQUARE_BRACKET)
12
+
13
+ { list: list.reject(&:empty?) }
14
+ else
15
+ parse_subclass(::Code::Parser::String)
16
+ end
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,13 @@
1
+ class Code
2
+ class Parser
3
+ class Multiplication < ::Code::Parser
4
+ def parse
5
+ parse_subclass(
6
+ ::Code::Parser::Operation,
7
+ operators: [ASTERISK, SLASH, PERCENT],
8
+ subclass: ::Code::Parser::UnaryMinus
9
+ )
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,18 @@
1
+ class Code
2
+ class Parser
3
+ class Negation < ::Code::Parser
4
+ def parse
5
+ if operator = match([EXCLAMATION_POINT, TILDE, PLUS])
6
+ {
7
+ negation: {
8
+ operator: operator,
9
+ statement: parse_subclass(::Code::Parser::Negation)
10
+ }
11
+ }
12
+ else
13
+ parse_subclass(::Code::Parser::Function)
14
+ end
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,24 @@
1
+ class Code
2
+ class Parser
3
+ class NotKeyword < ::Code::Parser
4
+ def parse
5
+ previous_cursor = cursor
6
+
7
+ if match(NOT_KEYWORD)
8
+ comments = parse_comments
9
+ right = parse_subclass(::Code::Parser::NotKeyword)
10
+
11
+ if right
12
+ { not: { right: right, comments: comments }.compact }
13
+ else
14
+ @cursor = previous_cursor
15
+ buffer!
16
+ parse_subclass(::Code::Parser::Equal)
17
+ end
18
+ else
19
+ parse_subclass(::Code::Parser::Equal)
20
+ end
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,13 @@
1
+ class Code
2
+ class Parser
3
+ class Nothing < ::Code::Parser
4
+ def parse
5
+ if match(NOTHING_KEYWORDS)
6
+ { nothing: buffer }
7
+ else
8
+ parse_subclass(::Code::Parser::Group)
9
+ end
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,39 @@
1
+ class Code
2
+ class Parser
3
+ class Number < ::Code::Parser
4
+ def parse
5
+ if match(DIGITS)
6
+ if match(X)
7
+ parse_base(16)
8
+ elsif match(O)
9
+ parse_base(8)
10
+ elsif match(B)
11
+ parse_base(2)
12
+ else
13
+ consume while (next?(DIGITS) || next?(UNDERSCORE)) && !end_of_input?
14
+
15
+ if next?(DOT) && next_next?(DIGITS)
16
+ consume
17
+ while (next?(DIGITS) || next?(UNDERSCORE)) && !end_of_input?
18
+ consume
19
+ end
20
+
21
+ { decimal: buffer.gsub(UNDERSCORE, EMPTY_STRING) }
22
+ else
23
+ { integer: buffer.gsub(UNDERSCORE, EMPTY_STRING).to_i }
24
+ end
25
+ end
26
+ else
27
+ parse_subclass(::Code::Parser::Boolean)
28
+ end
29
+ end
30
+
31
+ def parse_base(base)
32
+ buffer!
33
+ consume while (next?(DIGITS) || next?(UNDERSCORE)) && !end_of_input?
34
+
35
+ { integer: buffer.gsub(UNDERSCORE, EMPTY_STRING).to_i(base) }
36
+ end
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,44 @@
1
+ class Code
2
+ class Parser
3
+ class Operation < ::Code::Parser
4
+ def initialize(input, operators:, subclass:, **kargs)
5
+ super(input, **kargs)
6
+
7
+ @operators = operators
8
+ @subclass = subclass
9
+ end
10
+
11
+ def parse
12
+ previous_cursor = cursor
13
+ left = parse_subclass(subclass)
14
+ right = []
15
+ previous_cursor = cursor
16
+ comments = parse_comments
17
+
18
+ while operator = match(operators)
19
+ comments = parse_comments
20
+ statement = parse_subclass(subclass)
21
+ right << {
22
+ comments: comments,
23
+ statement: statement,
24
+ operator: operator
25
+ }.compact
26
+ end
27
+
28
+ if right.empty?
29
+ @cursor = previous_cursor
30
+ buffer!
31
+ left
32
+ else
33
+ {
34
+ operation: { left: left, comments: comments, right: right }.compact
35
+ }
36
+ end
37
+ end
38
+
39
+ private
40
+
41
+ attr_reader :operators, :subclass
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,13 @@
1
+ class Code
2
+ class Parser
3
+ class OrKeyword < ::Code::Parser
4
+ def parse
5
+ parse_subclass(
6
+ ::Code::Parser::Operation,
7
+ operators: [AND_KEYWORD, OR_KEYWORD],
8
+ subclass: ::Code::Parser::NotKeyword
9
+ )
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,13 @@
1
+ class Code
2
+ class Parser
3
+ class OrOperator < ::Code::Parser
4
+ def parse
5
+ parse_subclass(
6
+ ::Code::Parser::Operation,
7
+ operators: [PIPE + PIPE],
8
+ subclass: ::Code::Parser::GreaterThan
9
+ )
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,33 @@
1
+ class Code
2
+ class Parser
3
+ class Power < ::Code::Parser
4
+ def parse
5
+ previous_cursor = cursor
6
+ left = parse_subclass(::Code::Parser::Negation)
7
+ comments_before = parse_comments
8
+ if match(ASTERISK + ASTERISK)
9
+ comments_after = parse_comments
10
+ right = parse_subclass(::Code::Parser::Power)
11
+ if right
12
+ {
13
+ power: {
14
+ left: left,
15
+ right: right,
16
+ comments_before: comments_before,
17
+ comments_after: comments_after
18
+ }.compact
19
+ }
20
+ else
21
+ @cursor = previous_cursor
22
+ buffer!
23
+ parse_subclass(::Code::Parser::Negation)
24
+ end
25
+ else
26
+ @cursor = previous_cursor
27
+ buffer!
28
+ parse_subclass(::Code::Parser::Negation)
29
+ end
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,13 @@
1
+ class Code
2
+ class Parser
3
+ class Range < ::Code::Parser
4
+ def parse
5
+ parse_subclass(
6
+ ::Code::Parser::Operation,
7
+ operators: [DOT + DOT + DOT, DOT + DOT],
8
+ subclass: ::Code::Parser::OrOperator
9
+ )
10
+ end
11
+ end
12
+ end
13
+ end