oppen 0.9.6 → 0.9.8
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 +1 -2
- data/lib/oppen/mixins.rb +50 -27
- data/lib/oppen/print_stack.rb +77 -67
- data/lib/oppen/printer.rb +122 -77
- data/lib/oppen/scan_stack.rb +36 -16
- data/lib/oppen/token.rb +54 -22
- data/lib/oppen/version.rb +3 -2
- data/lib/oppen.rb +186 -75
- data/lib/wadler/print.rb +248 -45
- metadata +5 -5
data/lib/wadler/print.rb
CHANGED
@@ -4,26 +4,49 @@
|
|
4
4
|
module Oppen
|
5
5
|
# Wadler.
|
6
6
|
class Wadler
|
7
|
+
# @return [Config]
|
8
|
+
# The printer's configuration, altering its behavior.
|
7
9
|
attr_reader :config
|
10
|
+
# @return [Integer]
|
11
|
+
# the current indentation amount.
|
8
12
|
attr_reader :current_indent
|
9
|
-
|
13
|
+
# @return [String]
|
14
|
+
# the new line string, e.g. `\n`.
|
10
15
|
attr_reader :new_line
|
16
|
+
# @return [Object]
|
17
|
+
# the output string buffer. It should have a `write` and `string` methods.
|
11
18
|
attr_reader :out
|
19
|
+
# @return [Proc]
|
20
|
+
# space generator, a callable.
|
21
|
+
attr_reader :space
|
22
|
+
# @return [Array<Token>]
|
23
|
+
# the tokens list that is being built.
|
12
24
|
attr_reader :tokens
|
25
|
+
# @return [String]
|
26
|
+
# the whitespace character. Used to trim trailing whitespaces.
|
27
|
+
attr_reader :whitespace
|
28
|
+
# @return [Integer]
|
29
|
+
# maximum line width.
|
13
30
|
attr_reader :width
|
14
31
|
|
15
|
-
# @param config
|
16
|
-
#
|
17
|
-
#
|
18
|
-
#
|
19
|
-
#
|
20
|
-
#
|
21
|
-
#
|
22
|
-
#
|
23
|
-
#
|
24
|
-
#
|
25
|
-
|
26
|
-
|
32
|
+
# @param config [Config]
|
33
|
+
# to customize the printer's behavior.
|
34
|
+
# @param new_line [String]
|
35
|
+
# the new line String.
|
36
|
+
# @param out [Object]
|
37
|
+
# the output string buffer. It should have a `write` and `string` methods.
|
38
|
+
# @param space [String, Proc]
|
39
|
+
# indentation string or a string generator.
|
40
|
+
# - If a `String`, spaces will be generated with the the lambda
|
41
|
+
# `->(n){ space * n }`, where `n` is the number of columns to indent.
|
42
|
+
# - If a `Proc`, it will receive `n` and it needs to return a `String`.
|
43
|
+
# @param whitespace [String] the whitespace character. Used to trim trailing whitespaces.
|
44
|
+
# @param width [Integer] maximum line width desired.
|
45
|
+
#
|
46
|
+
# @see Token::Whitespace
|
47
|
+
def initialize(config: Config.wadler, new_line: "\n",
|
48
|
+
out: StringIO.new, space: ' ',
|
49
|
+
whitespace: ' ', width: 80)
|
27
50
|
@config = config
|
28
51
|
@current_indent = 0
|
29
52
|
@space = space
|
@@ -31,38 +54,138 @@ module Oppen
|
|
31
54
|
@new_line = new_line
|
32
55
|
@out = out
|
33
56
|
@tokens = []
|
57
|
+
@whitespace = whitespace
|
34
58
|
end
|
35
59
|
|
36
|
-
#
|
37
|
-
|
60
|
+
# Add missing {Token::Begin}, {Token::End} or {Token::EOF}.
|
61
|
+
#
|
62
|
+
# @return [Nil]
|
63
|
+
def add_missing_begin_and_end
|
38
64
|
if !tokens.first.is_a? Token::Begin
|
39
65
|
tokens.unshift Oppen.begin_consistent(offset: 0)
|
40
66
|
tokens << Oppen.end
|
41
67
|
end
|
42
|
-
if !tokens.last.is_a?
|
43
|
-
|
44
|
-
|
45
|
-
|
68
|
+
tokens << Oppen.eof if !tokens.last.is_a?(Oppen::Token::EOF)
|
69
|
+
end
|
70
|
+
|
71
|
+
# Call this to extract the final pretty-printed output.
|
72
|
+
#
|
73
|
+
# @return [String]
|
74
|
+
def output
|
75
|
+
add_missing_begin_and_end
|
76
|
+
Oppen.print(
|
77
|
+
tokens: tokens,
|
78
|
+
new_line: new_line,
|
79
|
+
config: config,
|
80
|
+
space: space,
|
81
|
+
out: out,
|
82
|
+
width: width,
|
83
|
+
)
|
84
|
+
end
|
85
|
+
|
86
|
+
# Convert a list of tokens to its wadler representation.
|
87
|
+
#
|
88
|
+
# This method reverse engineers a tokens list to transform it into Wadler
|
89
|
+
# printing commands. It can be particularly useful when debugging a black
|
90
|
+
# box program.
|
91
|
+
#
|
92
|
+
# @option kwargs [Integer] :base_indent
|
93
|
+
# the base indentation amount of the output.
|
94
|
+
# @option kwargs [String] :printer_name
|
95
|
+
# the name of the Wadler instance in the output.
|
96
|
+
#
|
97
|
+
# @example
|
98
|
+
# out = Oppen::Wadler.new
|
99
|
+
# out.group {
|
100
|
+
# out.text('Hello World!')
|
101
|
+
# }
|
102
|
+
# out.show_print_commands(out_name: 'out')
|
103
|
+
#
|
104
|
+
# # =>
|
105
|
+
# # out.group(0, "", "", :consistent) {
|
106
|
+
# # out.text("Hello World!", width: 12)
|
107
|
+
# # }
|
108
|
+
#
|
109
|
+
# @return [String]
|
110
|
+
def show_print_commands(**kwargs)
|
111
|
+
add_missing_begin_and_end
|
112
|
+
Oppen.tokens_to_wadler(tokens, **kwargs)
|
46
113
|
end
|
47
114
|
|
48
|
-
#
|
49
|
-
#
|
50
|
-
# @param
|
51
|
-
#
|
115
|
+
# Create a new group.
|
116
|
+
#
|
117
|
+
# @param indent [Integer]
|
118
|
+
# indentation.
|
119
|
+
# @param open_obj [String]
|
120
|
+
# opening delimiter.
|
121
|
+
# @param close_obj [String]
|
122
|
+
# closing delimiter.
|
123
|
+
# @param break_type [Token::BreakType]
|
124
|
+
# break type.
|
52
125
|
#
|
53
|
-
# @yield
|
126
|
+
# @yield
|
127
|
+
# the block of text in a group.
|
128
|
+
#
|
129
|
+
# @example
|
130
|
+
# out = Oppen::Wadler.new
|
131
|
+
# out.text 'a'
|
132
|
+
# out.group(2, '{', '}') {
|
133
|
+
# out.break
|
134
|
+
# out.text 'b'
|
135
|
+
# }
|
136
|
+
# out.output
|
137
|
+
#
|
138
|
+
# # =>
|
139
|
+
# # a
|
140
|
+
# # {
|
141
|
+
# # b
|
142
|
+
# # }
|
143
|
+
#
|
144
|
+
# @example Consistent Breaking
|
145
|
+
# out = Oppen::Wadler.new
|
146
|
+
# out.group(0, '', '', :consistent) {
|
147
|
+
# out.text 'a'
|
148
|
+
# out.break
|
149
|
+
# out.text 'b'
|
150
|
+
# out.breakable
|
151
|
+
# out.text 'c'
|
152
|
+
# }
|
153
|
+
# out.output
|
154
|
+
#
|
155
|
+
# # =>
|
156
|
+
# # a
|
157
|
+
# # b
|
158
|
+
# # c
|
159
|
+
#
|
160
|
+
# @example Inconsistent Breaking
|
161
|
+
# out = Oppen::Wadler.new
|
162
|
+
# out.group(0, '', '', :inconsistent) {
|
163
|
+
# out.text 'a'
|
164
|
+
# out.break
|
165
|
+
# out.text 'b'
|
166
|
+
# out.breakable
|
167
|
+
# out.text 'c'
|
168
|
+
# }
|
169
|
+
# out.output
|
170
|
+
#
|
171
|
+
# # =>
|
172
|
+
# # a
|
173
|
+
# # b c
|
54
174
|
#
|
55
175
|
# @return [Nil]
|
176
|
+
#
|
177
|
+
# @see Oppen.begin_consistent
|
178
|
+
# @see Oppen.begin_inconsistent
|
56
179
|
def group(indent = 0, open_obj = '', close_obj = '',
|
57
|
-
break_type =
|
180
|
+
break_type = :consistent)
|
58
181
|
raise ArgumentError, "#{open_obj.nil? ? 'open_obj' : 'close_obj'} cannot be nil" \
|
59
182
|
if open_obj.nil? || close_obj.nil?
|
60
183
|
|
61
184
|
tokens <<
|
62
185
|
case break_type
|
63
|
-
in
|
186
|
+
in :consistent
|
64
187
|
Oppen.begin_consistent(offset: indent)
|
65
|
-
in
|
188
|
+
in :inconsistent
|
66
189
|
Oppen.begin_inconsistent(offset: indent)
|
67
190
|
end
|
68
191
|
|
@@ -81,14 +204,47 @@ module Oppen
|
|
81
204
|
tokens << Oppen.end
|
82
205
|
end
|
83
206
|
|
84
|
-
#
|
85
|
-
#
|
86
|
-
#
|
87
|
-
#
|
207
|
+
# Create a new non-strict {group}.
|
208
|
+
#
|
209
|
+
# {group}s isolate breaking decisions, and in that sense they're considered
|
210
|
+
# strict; e.g. when a breakable is transformed into an actual break, its
|
211
|
+
# parent {group} might not get broken if the result could fit on the line.
|
212
|
+
#
|
213
|
+
# This is not the case with {nest}: if the same breakable was in a {nest}, the
|
214
|
+
# {group} containing the {nest} will also be broken.
|
215
|
+
#
|
216
|
+
# @note indentation cannot happen if there are no breaks in the {nest}.
|
217
|
+
#
|
218
|
+
# @note a {nest} will not forcibly indent its content if the break type of
|
219
|
+
# the enclosing {group} is `:inconsistent`.
|
220
|
+
#
|
221
|
+
# @param indent [Integer]
|
222
|
+
# indentation.
|
223
|
+
# @param open_obj [String]
|
224
|
+
# opening delimiter. A {break} is implicitly slipped after it if it's not empty.
|
225
|
+
# @param close_obj [String]
|
226
|
+
# closing delimiter. A {break} is implicitly slipped before it if it's not empty.
|
227
|
+
#
|
228
|
+
# @yield
|
229
|
+
# the block of text in a nest.
|
230
|
+
#
|
231
|
+
# @example
|
232
|
+
# out = Oppen::Wadler.new
|
233
|
+
# out.nest(2, '{', '}') {
|
234
|
+
# out.text 'a'
|
235
|
+
# out.break
|
236
|
+
# out.text 'b'
|
237
|
+
# }
|
238
|
+
# out.output
|
239
|
+
#
|
240
|
+
# # =>
|
241
|
+
# # {
|
242
|
+
# # a
|
243
|
+
# # b
|
244
|
+
# # }
|
88
245
|
#
|
89
246
|
# @return [Nil]
|
90
|
-
def nest(indent, open_obj = '', close_obj = ''
|
91
|
-
break_type = Oppen::Token::BreakType::CONSISTENT)
|
247
|
+
def nest(indent, open_obj = '', close_obj = '')
|
92
248
|
raise ArgumentError, "#{open_obj.nil? ? 'open_obj' : 'close_obj'} cannot be nil" \
|
93
249
|
if open_obj.nil? || close_obj.nil?
|
94
250
|
|
@@ -111,33 +267,71 @@ module Oppen
|
|
111
267
|
text(close_obj)
|
112
268
|
end
|
113
269
|
|
270
|
+
# Create a new text element.
|
271
|
+
#
|
114
272
|
# @param value [String]
|
273
|
+
# the value of the token.
|
115
274
|
#
|
116
275
|
# @return [Nil]
|
117
276
|
def text(value, width: value.length)
|
118
|
-
|
277
|
+
if config.trim_trailing_whitespaces? && value.match(/((?:#{Regexp.escape(whitespace)})+)\z/)
|
278
|
+
match = Regexp.last_match(1)
|
279
|
+
matched_length = match.length
|
280
|
+
if value.length != matched_length
|
281
|
+
tokens << Oppen.string(value[0...-matched_length], width: width - matched_length)
|
282
|
+
end
|
283
|
+
tokens << Oppen.whitespace(match)
|
284
|
+
else
|
285
|
+
tokens << Oppen.string(value, width: width)
|
286
|
+
end
|
119
287
|
end
|
120
288
|
|
121
|
-
#
|
122
|
-
#
|
289
|
+
# Create a new breakable element.
|
290
|
+
#
|
291
|
+
# @param str [String]
|
292
|
+
# the value of the token that will be displayed if no new line is needed.
|
293
|
+
# @param line_continuation [String]
|
294
|
+
# printed before the line break.
|
295
|
+
# @param width [Integer]
|
296
|
+
# the width of the token.
|
123
297
|
#
|
124
298
|
# @return [Nil]
|
125
|
-
|
126
|
-
|
299
|
+
#
|
300
|
+
# @see Wadler#break example on `line_continuation`.
|
301
|
+
def breakable(str = ' ', line_continuation: '', width: str.length)
|
302
|
+
tokens << Oppen.break(str, width: width, line_continuation: line_continuation, offset: current_indent)
|
127
303
|
end
|
128
304
|
|
129
|
-
#
|
305
|
+
# Create a new break element.
|
306
|
+
#
|
307
|
+
# @param line_continuation [String]
|
308
|
+
# printed before the line break.
|
309
|
+
#
|
310
|
+
# @example
|
311
|
+
# out = Oppen::Wadler.new
|
312
|
+
# out.text 'a'
|
313
|
+
# out.break
|
314
|
+
# out.text 'b'
|
315
|
+
# out.break line_continuation: '#'
|
316
|
+
# out.text 'c'
|
317
|
+
# out.output
|
318
|
+
#
|
319
|
+
# # =>
|
320
|
+
# # a
|
321
|
+
# # b#
|
322
|
+
# # c
|
130
323
|
#
|
131
324
|
# @return [Nil]
|
132
325
|
def break(line_continuation: '')
|
133
|
-
tokens << Oppen.line_break(line_continuation
|
326
|
+
tokens << Oppen.line_break(line_continuation: line_continuation, offset: current_indent)
|
134
327
|
end
|
135
328
|
|
136
329
|
# @!group Helpers
|
137
330
|
|
138
|
-
# Set a base indenetaion level
|
331
|
+
# Set a base indenetaion level for the printer.
|
139
332
|
#
|
140
333
|
# @param indent [Integer]
|
334
|
+
# the amount of indentation.
|
141
335
|
#
|
142
336
|
# @return [Nil]
|
143
337
|
def base_indent(indent = 0)
|
@@ -147,9 +341,14 @@ module Oppen
|
|
147
341
|
# Open a consistent group.
|
148
342
|
#
|
149
343
|
# @param inconsistent [Boolean]
|
344
|
+
# whether the break type of the group should be inconsistent.
|
150
345
|
# @param indent [Integer]
|
346
|
+
# the amount of indentation of the group.
|
151
347
|
#
|
152
348
|
# @return [Nil]
|
349
|
+
#
|
350
|
+
# @see Oppen.begin_consistent
|
351
|
+
# @see Oppen.begin_inconsistent
|
153
352
|
def group_open(inconsistent: false, indent: 0)
|
154
353
|
tokens <<
|
155
354
|
if inconsistent
|
@@ -166,9 +365,10 @@ module Oppen
|
|
166
365
|
tokens << Oppen.end
|
167
366
|
end
|
168
367
|
|
169
|
-
# Open a consistent group
|
368
|
+
# Open a consistent group and add indent amount.
|
170
369
|
#
|
171
370
|
# @param indent [Integer]
|
371
|
+
# the amount of indentation of the group.
|
172
372
|
#
|
173
373
|
# @return [Nil]
|
174
374
|
def indent_open(indent)
|
@@ -176,9 +376,10 @@ module Oppen
|
|
176
376
|
group_open
|
177
377
|
end
|
178
378
|
|
179
|
-
# Close a group
|
379
|
+
# Close a group and subtract indent.
|
180
380
|
#
|
181
381
|
# @param indent [Integer]
|
382
|
+
# the amount of indentation of the group.
|
182
383
|
#
|
183
384
|
# @return [Nil]
|
184
385
|
def indent_close(group, indent)
|
@@ -186,18 +387,20 @@ module Oppen
|
|
186
387
|
group_close(group)
|
187
388
|
end
|
188
389
|
|
189
|
-
# Open a nest by indent.
|
390
|
+
# Open a nest by adding indent.
|
190
391
|
#
|
191
392
|
# @param indent [Integer]
|
393
|
+
# the amount of indentation of the nest.
|
192
394
|
#
|
193
395
|
# @return [Nil]
|
194
396
|
def nest_open(indent)
|
195
397
|
@current_indent += indent
|
196
398
|
end
|
197
399
|
|
198
|
-
# Close a nest by indent.
|
400
|
+
# Close a nest by subtracting indent.
|
199
401
|
#
|
200
402
|
# @param indent [Integer]
|
403
|
+
# the amount of indentation of the nest.
|
201
404
|
#
|
202
405
|
# @return [Nil]
|
203
406
|
def nest_close(indent)
|
metadata
CHANGED
@@ -1,15 +1,15 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: oppen
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.9.
|
4
|
+
version: 0.9.8
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Amine Mike El Maalouf <amine.el-maalouf@epita.fr>
|
8
|
-
- Firas al-Khalil <
|
8
|
+
- Firas al-Khalil <firas.alkhalil@faveod.com>
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2024-
|
12
|
+
date: 2024-12-30 00:00:00.000000000 Z
|
13
13
|
dependencies: []
|
14
14
|
description: Implementation of the Oppen's pretty printing algorithm
|
15
15
|
email:
|
@@ -37,9 +37,9 @@ require_paths:
|
|
37
37
|
- lib
|
38
38
|
required_ruby_version: !ruby/object:Gem::Requirement
|
39
39
|
requirements:
|
40
|
-
- - "
|
40
|
+
- - ">="
|
41
41
|
- !ruby/object:Gem::Version
|
42
|
-
version: '3.
|
42
|
+
version: '3.0'
|
43
43
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
44
44
|
requirements:
|
45
45
|
- - ">="
|