oppen 0.9.7 → 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 +42 -39
- data/lib/oppen/print_stack.rb +62 -69
- data/lib/oppen/printer.rb +91 -78
- data/lib/oppen/scan_stack.rb +36 -16
- data/lib/oppen/token.rb +51 -36
- data/lib/oppen/version.rb +3 -2
- data/lib/oppen.rb +177 -77
- data/lib/wadler/print.rb +221 -46
- metadata +4 -4
data/lib/wadler/print.rb
CHANGED
@@ -4,30 +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.
|
13
27
|
attr_reader :whitespace
|
28
|
+
# @return [Integer]
|
29
|
+
# maximum line width.
|
14
30
|
attr_reader :width
|
15
31
|
|
16
|
-
# @param config
|
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
|
+
#
|
27
46
|
# @see Token::Whitespace
|
28
|
-
def initialize(config: Config.wadler,
|
29
|
-
|
30
|
-
|
47
|
+
def initialize(config: Config.wadler, new_line: "\n",
|
48
|
+
out: StringIO.new, space: ' ',
|
49
|
+
whitespace: ' ', width: 80)
|
31
50
|
@config = config
|
32
51
|
@current_indent = 0
|
33
52
|
@space = space
|
@@ -38,7 +57,8 @@ module Oppen
|
|
38
57
|
@whitespace = whitespace
|
39
58
|
end
|
40
59
|
|
41
|
-
# Add missing Begin, End or EOF
|
60
|
+
# Add missing {Token::Begin}, {Token::End} or {Token::EOF}.
|
61
|
+
#
|
42
62
|
# @return [Nil]
|
43
63
|
def add_missing_begin_and_end
|
44
64
|
if !tokens.first.is_a? Token::Begin
|
@@ -48,42 +68,124 @@ module Oppen
|
|
48
68
|
tokens << Oppen.eof if !tokens.last.is_a?(Oppen::Token::EOF)
|
49
69
|
end
|
50
70
|
|
51
|
-
#
|
52
|
-
# using Oppen's pretty printing algorithm.
|
71
|
+
# Call this to extract the final pretty-printed output.
|
53
72
|
#
|
54
73
|
# @return [String]
|
55
74
|
def output
|
56
75
|
add_missing_begin_and_end
|
57
|
-
Oppen.print(
|
76
|
+
Oppen.print(
|
77
|
+
tokens: tokens,
|
78
|
+
new_line: new_line,
|
79
|
+
config: config,
|
80
|
+
space: space,
|
81
|
+
out: out,
|
82
|
+
width: width,
|
83
|
+
)
|
58
84
|
end
|
59
85
|
|
60
|
-
#
|
61
|
-
#
|
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
|
+
# # }
|
62
108
|
#
|
63
109
|
# @return [String]
|
64
|
-
def show_print_commands(**)
|
110
|
+
def show_print_commands(**kwargs)
|
65
111
|
add_missing_begin_and_end
|
66
|
-
Oppen.tokens_to_wadler(tokens, **)
|
112
|
+
Oppen.tokens_to_wadler(tokens, **kwargs)
|
67
113
|
end
|
68
114
|
|
69
|
-
#
|
70
|
-
#
|
71
|
-
# @param
|
72
|
-
#
|
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.
|
125
|
+
#
|
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
|
73
170
|
#
|
74
|
-
#
|
171
|
+
# # =>
|
172
|
+
# # a
|
173
|
+
# # b c
|
75
174
|
#
|
76
175
|
# @return [Nil]
|
176
|
+
#
|
177
|
+
# @see Oppen.begin_consistent
|
178
|
+
# @see Oppen.begin_inconsistent
|
77
179
|
def group(indent = 0, open_obj = '', close_obj = '',
|
78
|
-
break_type =
|
180
|
+
break_type = :consistent)
|
79
181
|
raise ArgumentError, "#{open_obj.nil? ? 'open_obj' : 'close_obj'} cannot be nil" \
|
80
182
|
if open_obj.nil? || close_obj.nil?
|
81
183
|
|
82
184
|
tokens <<
|
83
185
|
case break_type
|
84
|
-
in
|
186
|
+
in :consistent
|
85
187
|
Oppen.begin_consistent(offset: indent)
|
86
|
-
in
|
188
|
+
in :inconsistent
|
87
189
|
Oppen.begin_inconsistent(offset: indent)
|
88
190
|
end
|
89
191
|
|
@@ -102,9 +204,44 @@ module Oppen
|
|
102
204
|
tokens << Oppen.end
|
103
205
|
end
|
104
206
|
|
105
|
-
#
|
106
|
-
#
|
107
|
-
#
|
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
|
+
# # }
|
108
245
|
#
|
109
246
|
# @return [Nil]
|
110
247
|
def nest(indent, open_obj = '', close_obj = '')
|
@@ -130,7 +267,10 @@ module Oppen
|
|
130
267
|
text(close_obj)
|
131
268
|
end
|
132
269
|
|
270
|
+
# Create a new text element.
|
271
|
+
#
|
133
272
|
# @param value [String]
|
273
|
+
# the value of the token.
|
134
274
|
#
|
135
275
|
# @return [Nil]
|
136
276
|
def text(value, width: value.length)
|
@@ -142,30 +282,56 @@ module Oppen
|
|
142
282
|
end
|
143
283
|
tokens << Oppen.whitespace(match)
|
144
284
|
else
|
145
|
-
tokens << Oppen.string(value, width:)
|
285
|
+
tokens << Oppen.string(value, width: width)
|
146
286
|
end
|
147
287
|
end
|
148
288
|
|
149
|
-
#
|
150
|
-
#
|
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.
|
151
297
|
#
|
152
298
|
# @return [Nil]
|
153
|
-
|
154
|
-
|
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)
|
155
303
|
end
|
156
304
|
|
157
|
-
#
|
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
|
158
323
|
#
|
159
324
|
# @return [Nil]
|
160
325
|
def break(line_continuation: '')
|
161
|
-
tokens << Oppen.line_break(line_continuation
|
326
|
+
tokens << Oppen.line_break(line_continuation: line_continuation, offset: current_indent)
|
162
327
|
end
|
163
328
|
|
164
329
|
# @!group Helpers
|
165
330
|
|
166
|
-
# Set a base indenetaion level
|
331
|
+
# Set a base indenetaion level for the printer.
|
167
332
|
#
|
168
333
|
# @param indent [Integer]
|
334
|
+
# the amount of indentation.
|
169
335
|
#
|
170
336
|
# @return [Nil]
|
171
337
|
def base_indent(indent = 0)
|
@@ -175,9 +341,14 @@ module Oppen
|
|
175
341
|
# Open a consistent group.
|
176
342
|
#
|
177
343
|
# @param inconsistent [Boolean]
|
344
|
+
# whether the break type of the group should be inconsistent.
|
178
345
|
# @param indent [Integer]
|
346
|
+
# the amount of indentation of the group.
|
179
347
|
#
|
180
348
|
# @return [Nil]
|
349
|
+
#
|
350
|
+
# @see Oppen.begin_consistent
|
351
|
+
# @see Oppen.begin_inconsistent
|
181
352
|
def group_open(inconsistent: false, indent: 0)
|
182
353
|
tokens <<
|
183
354
|
if inconsistent
|
@@ -194,9 +365,10 @@ module Oppen
|
|
194
365
|
tokens << Oppen.end
|
195
366
|
end
|
196
367
|
|
197
|
-
# Open a consistent group
|
368
|
+
# Open a consistent group and add indent amount.
|
198
369
|
#
|
199
370
|
# @param indent [Integer]
|
371
|
+
# the amount of indentation of the group.
|
200
372
|
#
|
201
373
|
# @return [Nil]
|
202
374
|
def indent_open(indent)
|
@@ -204,9 +376,10 @@ module Oppen
|
|
204
376
|
group_open
|
205
377
|
end
|
206
378
|
|
207
|
-
# Close a group
|
379
|
+
# Close a group and subtract indent.
|
208
380
|
#
|
209
381
|
# @param indent [Integer]
|
382
|
+
# the amount of indentation of the group.
|
210
383
|
#
|
211
384
|
# @return [Nil]
|
212
385
|
def indent_close(group, indent)
|
@@ -214,18 +387,20 @@ module Oppen
|
|
214
387
|
group_close(group)
|
215
388
|
end
|
216
389
|
|
217
|
-
# Open a nest by indent.
|
390
|
+
# Open a nest by adding indent.
|
218
391
|
#
|
219
392
|
# @param indent [Integer]
|
393
|
+
# the amount of indentation of the nest.
|
220
394
|
#
|
221
395
|
# @return [Nil]
|
222
396
|
def nest_open(indent)
|
223
397
|
@current_indent += indent
|
224
398
|
end
|
225
399
|
|
226
|
-
# Close a nest by indent.
|
400
|
+
# Close a nest by subtracting indent.
|
227
401
|
#
|
228
402
|
# @param indent [Integer]
|
403
|
+
# the amount of indentation of the nest.
|
229
404
|
#
|
230
405
|
# @return [Nil]
|
231
406
|
def nest_close(indent)
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
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>
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2024-12-
|
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
|
- - ">="
|