oppen 0.9.7 → 1.0.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.
- checksums.yaml +4 -4
- data/README.md +99 -29
- data/lib/oppen/mixins.rb +39 -41
- data/lib/oppen/print_stack.rb +61 -68
- data/lib/oppen/printer.rb +90 -77
- data/lib/oppen/scan_stack.rb +36 -16
- data/lib/oppen/token.rb +50 -35
- data/lib/oppen/version.rb +3 -2
- data/lib/oppen.rb +174 -74
- data/lib/wadler/print.rb +719 -95
- metadata +8 -8
data/lib/oppen/printer.rb
CHANGED
@@ -2,9 +2,9 @@
|
|
2
2
|
|
3
3
|
require 'stringio'
|
4
4
|
|
5
|
-
require_relative 'scan_stack'
|
6
|
-
require_relative 'print_stack'
|
7
5
|
require_relative 'mixins'
|
6
|
+
require_relative 'print_stack'
|
7
|
+
require_relative 'scan_stack'
|
8
8
|
|
9
9
|
# Oppen.
|
10
10
|
module Oppen
|
@@ -12,73 +12,82 @@ module Oppen
|
|
12
12
|
class Printer
|
13
13
|
extend Mixins
|
14
14
|
|
15
|
+
# The printer's configuration, altering its behavior.
|
16
|
+
#
|
17
|
+
# @return [Config]
|
15
18
|
attr_reader :config
|
16
|
-
# Ring buffer left index.
|
19
|
+
# Ring buffer's left index.
|
17
20
|
#
|
18
|
-
# @note Called left as well in the original paper.
|
21
|
+
# @note Called `left` as well in the original paper.
|
22
|
+
#
|
23
|
+
# @return [Integer]
|
19
24
|
attr_reader :left
|
20
|
-
|
21
|
-
# Total number of spaces needed to print from start of buffer to the left.
|
25
|
+
# Number of spaces needed to print from start of buffer to left.
|
22
26
|
#
|
23
|
-
# @note Called leftTotal as well in the original paper.
|
27
|
+
# @note Called `leftTotal` as well in the original paper.
|
28
|
+
#
|
29
|
+
# @return [Integer]
|
24
30
|
attr_reader :left_total
|
25
|
-
|
26
|
-
#
|
31
|
+
# A stack of {Token}s; builds the the final output.
|
32
|
+
#
|
33
|
+
# @note Called `printStack` as well in the original paper.
|
34
|
+
#
|
35
|
+
# @return [PrintStack]
|
27
36
|
attr_reader :print_stack
|
28
|
-
|
29
|
-
# Ring buffer right index.
|
37
|
+
# Ring buffer's right index.
|
30
38
|
#
|
31
|
-
# @note Called right as well in the original paper.
|
39
|
+
# @note Called `right` as well in the original paper.
|
40
|
+
#
|
41
|
+
# @return [Integer]
|
32
42
|
attr_reader :right
|
33
|
-
|
34
|
-
# Total number of spaces needed to print from start of buffer to the right.
|
43
|
+
# Number of spaces needed to print from start of buffer to right.
|
35
44
|
#
|
36
|
-
# @note Called leftTotal as well in the original paper.
|
45
|
+
# @note Called `leftTotal` as well in the original paper.
|
46
|
+
#
|
47
|
+
# @return [Integer]
|
37
48
|
attr_reader :right_total
|
38
|
-
|
39
49
|
# Potential breaking positions.
|
40
50
|
#
|
41
|
-
# @note Called scanStack as well in the original paper.
|
51
|
+
# @note Called `scanStack` as well in the original paper.
|
52
|
+
#
|
53
|
+
# @return [ScanStack]
|
42
54
|
attr_reader :scan_stack
|
43
|
-
|
44
55
|
# Size buffer, initially filled with nil.
|
45
56
|
#
|
46
|
-
# @note Called size as well in the original paper.
|
57
|
+
# @note Called `size` as well in the original paper.
|
58
|
+
#
|
59
|
+
# @return [Integer]
|
47
60
|
attr_reader :size
|
48
|
-
|
49
61
|
# Token buffer, initially filled with nil.
|
50
62
|
#
|
51
|
-
# @note Called token in the original paper.
|
63
|
+
# @note Called `token` in the original paper.
|
64
|
+
#
|
65
|
+
# @return [Array<Tokens>]
|
52
66
|
attr_reader :tokens
|
53
67
|
|
54
|
-
#
|
55
|
-
#
|
56
|
-
# @
|
57
|
-
#
|
58
|
-
#
|
59
|
-
#
|
60
|
-
#
|
61
|
-
#
|
62
|
-
#
|
63
|
-
#
|
64
|
-
#
|
65
|
-
# @param width
|
66
|
-
#
|
67
|
-
# @param
|
68
|
-
#
|
69
|
-
#
|
70
|
-
# lambda `->(n){ n * space }`, where `n` is the number of columns
|
71
|
-
# to indent.
|
72
|
-
# If it's a callable, it will receive `n` and it needs to return
|
73
|
-
# a string.
|
74
|
-
# @param out [Object] should have a write and string method
|
68
|
+
# @note Called `PrettyPrintInit` in the original paper.
|
69
|
+
#
|
70
|
+
# @param config [Config]
|
71
|
+
# to customize the printer's behavior.
|
72
|
+
# @param new_line [String]
|
73
|
+
# the delimiter between lines.
|
74
|
+
# @param space [String, Proc]
|
75
|
+
# indentation string or a string generator.
|
76
|
+
# - If a `String`, spaces will be generated with the the lambda
|
77
|
+
# `->(n){ space * n }`, where `n` is the number of columns to indent.
|
78
|
+
# - If a `Proc`, it will receive `n` and it needs to return a `String`.
|
79
|
+
# @param width [Integer]
|
80
|
+
# maximum line width desired.
|
81
|
+
# @param out [Object]
|
82
|
+
# the output string buffer. It should have both `write` and `string`
|
83
|
+
# methods.
|
75
84
|
def initialize(width, new_line, config = Config.oppen,
|
76
85
|
space = ' ', out = StringIO.new)
|
77
86
|
# Maximum size if the stacks
|
78
87
|
n = 3 * width
|
79
88
|
|
80
89
|
@config = config
|
81
|
-
@last_whitespaces_width = 0
|
90
|
+
@last_whitespaces_width = 0 # Accumulates the width of the last Whitespace tokens encountered.
|
82
91
|
@left = 0
|
83
92
|
@left_total = 1
|
84
93
|
@print_stack = PrintStack.new width, new_line, config, space, out
|
@@ -89,14 +98,16 @@ module Oppen
|
|
89
98
|
@tokens = Array.new n
|
90
99
|
end
|
91
100
|
|
101
|
+
# The final pretty-printed output.
|
102
|
+
#
|
92
103
|
# @return [String]
|
93
|
-
|
94
|
-
|
95
|
-
end
|
104
|
+
# the output of the print stack.
|
105
|
+
def output = print_stack.output
|
96
106
|
|
97
|
-
# Core function of the algorithm responsible for populating the
|
107
|
+
# Core function of the algorithm responsible for populating the {ScanStack}
|
108
|
+
# and {PrintStack}.
|
98
109
|
#
|
99
|
-
# @note Called PrettyPrint as well in the original paper.
|
110
|
+
# @note Called `PrettyPrint` as well in the original paper.
|
100
111
|
#
|
101
112
|
# @param token [Token]
|
102
113
|
#
|
@@ -120,11 +131,9 @@ module Oppen
|
|
120
131
|
end
|
121
132
|
end
|
122
133
|
|
123
|
-
# Handle EOF
|
134
|
+
# Handle {Token::EOF}.
|
124
135
|
#
|
125
136
|
# @return [Nil]
|
126
|
-
#
|
127
|
-
# @see Token::EOF
|
128
137
|
def handle_eof
|
129
138
|
if !scan_stack.empty?
|
130
139
|
check_stack 0
|
@@ -133,11 +142,11 @@ module Oppen
|
|
133
142
|
print_stack.indent 0
|
134
143
|
end
|
135
144
|
|
136
|
-
# Handle Begin
|
145
|
+
# Handle {Token::Begin}.
|
137
146
|
#
|
138
|
-
# @
|
147
|
+
# @param token [Token::Begin]
|
139
148
|
#
|
140
|
-
# @
|
149
|
+
# @return [Nil]
|
141
150
|
def handle_begin(token)
|
142
151
|
if scan_stack.empty?
|
143
152
|
@left = 0
|
@@ -145,7 +154,7 @@ module Oppen
|
|
145
154
|
@right = 0
|
146
155
|
@right_total = 1
|
147
156
|
|
148
|
-
# config.trim_trailing_whitespaces
|
157
|
+
# config.trim_trailing_whitespaces.
|
149
158
|
@tokens[-1] = nil
|
150
159
|
else
|
151
160
|
advance_right
|
@@ -155,11 +164,11 @@ module Oppen
|
|
155
164
|
scan_stack.push right
|
156
165
|
end
|
157
166
|
|
158
|
-
# Handle End
|
167
|
+
# Handle {Token::End}.
|
159
168
|
#
|
160
|
-
# @
|
169
|
+
# @param token [Token::End]
|
161
170
|
#
|
162
|
-
# @
|
171
|
+
# @return [Nil]
|
163
172
|
def handle_end(token)
|
164
173
|
if scan_stack.empty?
|
165
174
|
print_stack.print token, 0
|
@@ -176,11 +185,11 @@ module Oppen
|
|
176
185
|
end
|
177
186
|
end
|
178
187
|
|
179
|
-
# Handle Break
|
188
|
+
# Handle {Token::Break}.
|
180
189
|
#
|
181
|
-
# @
|
190
|
+
# @param token [Token::Break]
|
182
191
|
#
|
183
|
-
# @
|
192
|
+
# @return [Nil]
|
184
193
|
def handle_break(token)
|
185
194
|
if scan_stack.empty?
|
186
195
|
@left = 0
|
@@ -188,7 +197,7 @@ module Oppen
|
|
188
197
|
@right = 0
|
189
198
|
@right_total = 1
|
190
199
|
|
191
|
-
# config.trim_trailing_whitespaces
|
200
|
+
# config.trim_trailing_whitespaces.
|
192
201
|
tokens[-1] = nil
|
193
202
|
print_stack.erase @last_whitespaces_width
|
194
203
|
@last_whitespaces_width = 0
|
@@ -202,11 +211,11 @@ module Oppen
|
|
202
211
|
@right_total += token.width
|
203
212
|
end
|
204
213
|
|
205
|
-
# Handle String
|
214
|
+
# Handle {Token::String}.
|
206
215
|
#
|
207
|
-
# @
|
216
|
+
# @param token [Token::String]
|
208
217
|
#
|
209
|
-
# @
|
218
|
+
# @return [Nil]
|
210
219
|
def handle_string(token)
|
211
220
|
if scan_stack.empty?
|
212
221
|
print_stack.print token, token.width
|
@@ -221,7 +230,7 @@ module Oppen
|
|
221
230
|
|
222
231
|
# Flushes the input if possible.
|
223
232
|
#
|
224
|
-
# @note Called CheckStream as well in the original paper.
|
233
|
+
# @note Called `CheckStream` as well in the original paper.
|
225
234
|
#
|
226
235
|
# @return [Nil]
|
227
236
|
def check_stream
|
@@ -236,9 +245,9 @@ module Oppen
|
|
236
245
|
check_stream
|
237
246
|
end
|
238
247
|
|
239
|
-
# Advances the
|
248
|
+
# Advances the {#right} pointer.
|
240
249
|
#
|
241
|
-
# @note Called AdvanceRight as well in the original paper.
|
250
|
+
# @note Called `AdvanceRight` as well in the original paper.
|
242
251
|
#
|
243
252
|
# @return [Nil]
|
244
253
|
def advance_right
|
@@ -253,10 +262,13 @@ module Oppen
|
|
253
262
|
@tokens, @left, @right = ScanStack.upsize_circular_array(@tokens, @left)
|
254
263
|
end
|
255
264
|
|
256
|
-
# Advances the
|
257
|
-
#
|
265
|
+
# Advances the {#left} pointer and lets the print stack print some of the
|
266
|
+
# tokens it contains.
|
267
|
+
#
|
268
|
+
# @note Called `AdvanceLeft` as well in the original paper.
|
258
269
|
#
|
259
|
-
# @
|
270
|
+
# @param token [Token]
|
271
|
+
# @param token_width [Integer]
|
260
272
|
#
|
261
273
|
# @return [Nil]
|
262
274
|
def advance_left(token, token_width)
|
@@ -296,12 +308,13 @@ module Oppen
|
|
296
308
|
advance_left tokens[left], size[left]
|
297
309
|
end
|
298
310
|
|
299
|
-
# Updates the size buffer taking into
|
300
|
-
#
|
311
|
+
# Updates the {#size} buffer taking into account the length of the current
|
312
|
+
# group.
|
301
313
|
#
|
302
|
-
# @note Called CheckStack as well in the original paper.
|
314
|
+
# @note Called `CheckStack` as well in the original paper.
|
303
315
|
#
|
304
|
-
# @param depth [Integer]
|
316
|
+
# @param depth [Integer]
|
317
|
+
# depth of the group.
|
305
318
|
#
|
306
319
|
# @return [Nil]
|
307
320
|
def check_stack(depth)
|
@@ -309,12 +322,12 @@ module Oppen
|
|
309
322
|
|
310
323
|
x = scan_stack.top
|
311
324
|
case tokens[x]
|
312
|
-
|
325
|
+
in Token::Begin
|
313
326
|
if depth.positive?
|
314
327
|
size[scan_stack.pop] = size[x] + right_total
|
315
328
|
check_stack depth - 1
|
316
329
|
end
|
317
|
-
|
330
|
+
in Token::End
|
318
331
|
size[scan_stack.pop] = 1
|
319
332
|
check_stack depth + 1
|
320
333
|
else
|
data/lib/oppen/scan_stack.rb
CHANGED
@@ -9,23 +9,28 @@ module Oppen
|
|
9
9
|
extend Mixins
|
10
10
|
|
11
11
|
def initialize(size, config)
|
12
|
-
@bottom = 0
|
13
|
-
@config = config
|
14
|
-
@empty = true
|
15
|
-
@stack = Array.new
|
16
|
-
@top = 0
|
12
|
+
@bottom = 0 # Points to the bottom of the stack.
|
13
|
+
@config = config # Printing config.
|
14
|
+
@empty = true # Emptiness flag.
|
15
|
+
@stack = Array.new size # The fixed sized stack.
|
16
|
+
@top = 0 # Points to the top of the stack.
|
17
17
|
end
|
18
18
|
|
19
|
+
# Whether the stack is empty.
|
20
|
+
#
|
19
21
|
# @return [Boolean]
|
20
|
-
def empty?
|
21
|
-
@empty
|
22
|
-
end
|
22
|
+
def empty? = @empty
|
23
23
|
|
24
|
+
# The current length of the stack.
|
25
|
+
#
|
24
26
|
# @return [Integer]
|
25
|
-
def length
|
26
|
-
@stack.length
|
27
|
-
end
|
27
|
+
def length = @stack.length
|
28
28
|
|
29
|
+
# The top element of the stack.
|
30
|
+
#
|
31
|
+
# @raise [RuntimeError]
|
32
|
+
# when accessing empty stack.
|
33
|
+
#
|
29
34
|
# @return [Object]
|
30
35
|
def top
|
31
36
|
if empty?
|
@@ -35,6 +40,11 @@ module Oppen
|
|
35
40
|
@stack[@top]
|
36
41
|
end
|
37
42
|
|
43
|
+
# The bottom element of the stack.
|
44
|
+
#
|
45
|
+
# @raise [RuntimeError]
|
46
|
+
# when accessing empty stack.
|
47
|
+
#
|
38
48
|
# @return [Object]
|
39
49
|
def bottom
|
40
50
|
if empty?
|
@@ -66,16 +76,20 @@ module Oppen
|
|
66
76
|
#
|
67
77
|
# @param value [Object]
|
68
78
|
#
|
79
|
+
# @raise [RuntimeError]
|
80
|
+
# when the stack is full and the `upsize_stack` flag is not activated in
|
81
|
+
# {Config}.
|
82
|
+
#
|
69
83
|
# @return [Nil]
|
70
84
|
def push(value)
|
71
85
|
if empty?
|
72
86
|
@empty = false
|
73
87
|
else
|
74
|
-
@top = increment
|
88
|
+
@top = increment @top
|
75
89
|
if @top == @bottom
|
76
90
|
raise 'Stack full' if !@config.upsize_stack?
|
77
91
|
|
78
|
-
@stack, @bottom, @top = ScanStack.upsize_circular_array
|
92
|
+
@stack, @bottom, @top = ScanStack.upsize_circular_array @stack, @bottom
|
79
93
|
end
|
80
94
|
end
|
81
95
|
@stack[@top] = value
|
@@ -83,6 +97,9 @@ module Oppen
|
|
83
97
|
|
84
98
|
# Pop a value from the top.
|
85
99
|
#
|
100
|
+
# @raise [RuntimeError]
|
101
|
+
# when accessing empty stack.
|
102
|
+
#
|
86
103
|
# @return [Nil]
|
87
104
|
def pop
|
88
105
|
if empty?
|
@@ -93,13 +110,16 @@ module Oppen
|
|
93
110
|
if @top == @bottom
|
94
111
|
@empty = true
|
95
112
|
else
|
96
|
-
@top = decrement
|
113
|
+
@top = decrement @top
|
97
114
|
end
|
98
115
|
res
|
99
116
|
end
|
100
117
|
|
101
118
|
# Pop a value from the bottom.
|
102
119
|
#
|
120
|
+
# @raise [RuntimeError]
|
121
|
+
# when accessing empty stack.
|
122
|
+
#
|
103
123
|
# @return [Nil]
|
104
124
|
def pop_bottom
|
105
125
|
if empty?
|
@@ -110,7 +130,7 @@ module Oppen
|
|
110
130
|
if @top == @bottom
|
111
131
|
@empty = true
|
112
132
|
else
|
113
|
-
@bottom = increment
|
133
|
+
@bottom = increment @bottom
|
114
134
|
end
|
115
135
|
res
|
116
136
|
end
|
@@ -119,7 +139,7 @@ module Oppen
|
|
119
139
|
#
|
120
140
|
# @param offset [Integer]
|
121
141
|
#
|
122
|
-
# @return [Array
|
142
|
+
# @return [Array<Integer>]
|
123
143
|
def update_indexes(offset)
|
124
144
|
@stack = @stack.map { |val|
|
125
145
|
(val + offset) % length if val
|
data/lib/oppen/token.rb
CHANGED
@@ -4,27 +4,14 @@
|
|
4
4
|
module Oppen
|
5
5
|
# Token.
|
6
6
|
class Token
|
7
|
-
#
|
7
|
+
# Default token width.
|
8
8
|
#
|
9
|
-
# FITS => No break is needed (the block fits on the line).
|
10
|
-
# INCONSISTENT => New line will be forced only if necessary.
|
11
|
-
# CONSISTENT => Each subblock of the block will be placed on a new line.
|
12
|
-
module BreakType
|
13
|
-
# @return [Integer]
|
14
|
-
FITS = 0
|
15
|
-
# @return [Integer]
|
16
|
-
INCONSISTENT = 1
|
17
|
-
# @return [Integer]
|
18
|
-
CONSISTENT = 2
|
19
|
-
end
|
20
|
-
|
21
|
-
# Default token width
|
22
9
|
# @return [Integer]
|
23
10
|
def width = 0
|
24
11
|
|
25
12
|
# String Token.
|
26
13
|
class String < Token
|
27
|
-
# @return [String]
|
14
|
+
# @return [String]
|
28
15
|
attr_reader :value
|
29
16
|
# @return [Integer]
|
30
17
|
attr_reader :width
|
@@ -39,15 +26,21 @@ module Oppen
|
|
39
26
|
def to_s = value
|
40
27
|
end
|
41
28
|
|
42
|
-
#
|
43
|
-
#
|
44
|
-
#
|
29
|
+
# This token is not part of Oppen's original work. We introduced it to
|
30
|
+
# handle trailing whitespaces.
|
31
|
+
#
|
32
|
+
# When the config flag `trim_trailing_whitespaces == true`, and a new line
|
33
|
+
# is needed, all the {Token::Whitespace} figuring after the last {Token::String}
|
34
|
+
# will be be skipped.
|
45
35
|
class Whitespace < ::Oppen::Token::String
|
46
36
|
end
|
47
37
|
|
48
38
|
# Break Token.
|
49
39
|
class Break < Token
|
50
|
-
# @return [String]
|
40
|
+
# @return [String]
|
41
|
+
# If a new line is needed, display this string before the new line.
|
42
|
+
#
|
43
|
+
# @see Wadler#break example on `line_continuation`.
|
51
44
|
attr_reader :line_continuation
|
52
45
|
# @return [Integer] Indentation.
|
53
46
|
attr_reader :offset
|
@@ -56,7 +49,7 @@ module Oppen
|
|
56
49
|
# @return [Integer]
|
57
50
|
attr_reader :width
|
58
51
|
|
59
|
-
def initialize(str = ' ',
|
52
|
+
def initialize(str = ' ', line_continuation: '', offset: 0, width: str.length)
|
60
53
|
raise ArgumentError, 'line_continuation cannot be nil' if line_continuation.nil?
|
61
54
|
|
62
55
|
@line_continuation = line_continuation
|
@@ -66,6 +59,8 @@ module Oppen
|
|
66
59
|
super()
|
67
60
|
end
|
68
61
|
|
62
|
+
# Convert token to String.
|
63
|
+
#
|
69
64
|
# @return [String]
|
70
65
|
def to_s = str
|
71
66
|
end
|
@@ -87,33 +82,53 @@ module Oppen
|
|
87
82
|
class Begin < Token
|
88
83
|
# @return [BreakType]
|
89
84
|
attr_reader :break_type
|
90
|
-
# @return [Integer]
|
85
|
+
# @return [Integer]
|
91
86
|
attr_reader :offset
|
92
87
|
|
93
|
-
def initialize(break_type:
|
88
|
+
def initialize(break_type: :inconsistent, offset: 2)
|
94
89
|
@offset = offset
|
95
90
|
@break_type = break_type
|
96
91
|
super()
|
97
92
|
end
|
98
|
-
|
99
|
-
# The break_type name as a String.
|
100
|
-
#
|
101
|
-
# @return [String]
|
102
|
-
def break_type_name
|
103
|
-
case @break_type
|
104
|
-
in BreakType::FITS then 'Oppen::Token::BreakType::FITS'
|
105
|
-
in BreakType::INCONSISTENT then 'Oppen::Token::BreakType::INCONSISTENT'
|
106
|
-
in BreakType::CONSISTENT then 'Oppen::Token::BreakType::CONSISTENT'
|
107
|
-
end
|
108
|
-
end
|
109
93
|
end
|
110
94
|
|
111
|
-
# End Token
|
95
|
+
# End Token.
|
112
96
|
class End < Token
|
113
97
|
nil
|
114
98
|
end
|
115
99
|
|
116
|
-
# EOF
|
100
|
+
# The EOF token can be interpreted as an output flush operation.
|
101
|
+
#
|
102
|
+
# @note Multiple {Token::EOF} tokens can be present in the same list of tokens.
|
103
|
+
#
|
104
|
+
# @example
|
105
|
+
# tokens = [
|
106
|
+
# Oppen::Token::Begin.new,
|
107
|
+
# Oppen::Token::String.new('XXXXXXXXXX'),
|
108
|
+
# Oppen::Token::End.new,
|
109
|
+
# Oppen::Token::EOF.new,
|
110
|
+
# Oppen::Token::Begin.new,
|
111
|
+
# Oppen::Token::String.new('YYYYYYYYYY'),
|
112
|
+
# Oppen::Token::End.new,
|
113
|
+
# ]
|
114
|
+
# Oppen.print tokens:
|
115
|
+
#
|
116
|
+
# # =>
|
117
|
+
# # XXXXXXXXXX
|
118
|
+
#
|
119
|
+
# tokens = [
|
120
|
+
# Oppen::Token::Begin.new,
|
121
|
+
# Oppen::Token::String.new('XXXXXXXXXX'),
|
122
|
+
# Oppen::Token::End.new,
|
123
|
+
# Oppen::Token::Begin.new,
|
124
|
+
# Oppen::Token::String.new('YYYYYYYYYY'),
|
125
|
+
# Oppen::Token::End.new,
|
126
|
+
# Oppen::Token::EOF.new,
|
127
|
+
# ]
|
128
|
+
# Oppen.print tokens:
|
129
|
+
#
|
130
|
+
# # =>
|
131
|
+
# # XXXXXXXXXXYYYYYYYYYY
|
117
132
|
class EOF < Token
|
118
133
|
nil
|
119
134
|
end
|