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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 209072194fbb082bd25f2583b7f930e38c79ca042c21e711e18dd7295ee00e86
4
- data.tar.gz: 6eeb620d7d3bd6599477a46a2a5b9df86db2bc8c6a0f07a619d065245393127f
3
+ metadata.gz: '008545ae6d86a7415e434ef51a19872fbf5b768db232d70bbac025667e002604'
4
+ data.tar.gz: a989a6e8d422c363e7ebca36fc10dac4ad030f145afc2b4bc152c9cff5bdfe49
5
5
  SHA512:
6
- metadata.gz: a2f8d7bd7b199b7ef0d5a97feebbe456fea9e557f6535760ad84b21a304d73c52ea954f392e7c07e0b682844fe86c0c8ccc4ef6786057785cba9f02f58c31c14
7
- data.tar.gz: 31a73892d3afff56d85c60a6756d6fedbd2bce259d7ca259b662a7f7002549fcb53146f2fb1a32136ae2c8cca03b20ded90d95a814363d18d0d80192db79c39b
6
+ metadata.gz: 9a3e32d3a05c638bd26a9e170d737186c9635eeb3c54844869b108c8252fdafa942a05a76a1e3d3d25d5e93a47d076c280f7346c1c313b5c7159a8f3d1166ef6
7
+ data.tar.gz: 1f3c076ea8b298f5a772d9c209c4bc64087f6093576e41e2ffb6289b5d0aba08c1b3ed9d3022ebb14b7d7fe4c083804e60219735bce5c00f96785e9dede2bb69
data/README.md CHANGED
@@ -31,8 +31,7 @@ monkeypatching.
31
31
 
32
32
  ## Usage
33
33
 
34
- > [!WARNING]
35
- > Lands when the APIs are stable.
34
+ A few examples of the API usage can be found in [examples/](examples/README.md).
36
35
 
37
36
  ## Oppen vs Wadler
38
37
 
data/lib/oppen/mixins.rb CHANGED
@@ -4,50 +4,73 @@ module Oppen
4
4
  # Mixins.
5
5
  module Mixins
6
6
  # Rotates circular array and triples its size.
7
- # This method is not for public use.
8
7
  #
9
- # @param arr [Array]
10
- # @param offset [Integer] Rotation amount
8
+ # @!visibility private
9
+ # @note This method is not for public use.
11
10
  #
12
- # @return [Array<Array, Integer, Integer>] upsized array, lhs, rhs
11
+ # @param arr [Array]
12
+ # the circular array.
13
+ # @param offset [Integer]
14
+ # rotation amount.
15
+ #
16
+ # @return [Array<Array, Integer, Integer>]
17
+ # upsized array, lhs, rhs.
13
18
  def upsize_circular_array(arr, offset)
14
19
  size = arr.size
15
- arr = arr.rotate(offset)
16
- arr.fill(nil, size, 2 * size)
20
+ arr = arr.rotate offset
21
+ arr.fill nil, size, 2 * size
17
22
  [arr, 0, size]
18
23
  end
19
24
 
20
- # Convert a list of tokens to its wadler representation.
21
- #
22
- # @param tokens [Array[Token]]
23
- # @param base_indent [Integer]
24
- #
25
25
  # @return [String]
26
- def tokens_to_wadler(tokens, base_indent = 4)
27
- out = StringIO.new
28
- write = ->(txt, nb_spaces) {
29
- out.write("#{' ' * nb_spaces}#{txt}\n")
26
+ def tokens_to_wadler(tokens, base_indent: 0, printer_name: 'out', width: tokens.length * 3)
27
+ printer = Oppen::Wadler.new(width: width)
28
+ printer.base_indent(base_indent)
29
+ indent = 2
30
+
31
+ handle_break_token = ->(token) {
32
+ if token.offset.positive?
33
+ printer.text "#{printer_name}.nest(#{token.offset}, '', '') {"
34
+ printer.nest_open indent
35
+ printer.break
36
+ end
37
+
38
+ printer.text(
39
+ case token
40
+ in Token::LineBreak
41
+ "#{printer_name}.break(line_continuation: #{token.line_continuation.inspect})"
42
+ in Token::Break
43
+ "#{printer_name}.breakable(#{token.str.inspect}, width: #{token.width}, " \
44
+ "line_continuation: #{token.line_continuation.inspect})"
45
+ end,
46
+ )
47
+
48
+ if token.offset.positive?
49
+ printer.nest_close indent
50
+ printer.break
51
+ printer.text '}'
52
+ end
30
53
  }
31
- nb_spaces = base_indent
32
- tokens.each do |token|
54
+
55
+ tokens.each_with_index do |token, idx|
33
56
  case token
34
57
  in Token::String
35
- write.call("out.text '#{token}'", nb_spaces)
36
- in Token::LineBreak
37
- write.call('out.break', nb_spaces)
58
+ printer.text "#{printer_name}.text(#{token.value.inspect}, width: #{token.width})"
38
59
  in Token::Break
39
- write.call('out.breakable', nb_spaces)
60
+ handle_break_token.(token)
40
61
  in Token::Begin
41
- write.call('out.group {', nb_spaces)
42
- nb_spaces += 2
62
+ printer.text "#{printer_name}.group(#{token.offset}, '', '', #{token.break_type.inspect}) {"
63
+ printer.nest_open indent
43
64
  in Token::End
44
- nb_spaces -= 2
45
- write.call('}', nb_spaces)
65
+ printer.nest_close indent
66
+ printer.break
67
+ printer.text '}'
46
68
  in Token::EOF
47
- write.call('', nb_spaces) # new line
69
+ nil
48
70
  end
71
+ printer.break if !tokens[idx + 1].is_a?(Token::End)
49
72
  end
50
- out.string
73
+ printer.output
51
74
  end
52
75
  end
53
76
  end
@@ -2,15 +2,16 @@
2
2
 
3
3
  # Oppen.
4
4
  module Oppen
5
- # Class that represents a stack that builds an output string
6
- # using the values of the tokens that were pushed into it.
5
+ # A stack of {Token}s.
7
6
  class PrintStack
8
- # Class that represents an item in the print stack.
7
+ # An item in the print stack.
9
8
  class PrintStackEntry
10
- # @return [Integer] Indentation level.
11
- attr_reader :offset
12
- # @return [Token::BreakType] (Called break in the original paper).
9
+ # @return [Token::BreakType]
10
+ # Called `break` in the original paper.
13
11
  attr_reader :break_type
12
+ # @return [Integer]
13
+ # Indentation level.
14
+ attr_reader :offset
14
15
 
15
16
  def initialize(offset, break_type)
16
17
  @offset = offset
@@ -18,34 +19,26 @@ module Oppen
18
19
  end
19
20
  end
20
21
 
21
- # IO element that builds the output.
22
+ # IO sink for the output.
22
23
  attr_reader :buffer
23
-
24
- # Config containing customization flags
24
+ # The printer's configuration, altering its behavior.
25
25
  attr_reader :config
26
-
27
- # Callable that generate spaces
26
+ # Space generator, a callable.
28
27
  attr_reader :genspace
29
-
30
- # Array representing the stack of PrintStackEntries.
28
+ # The stack of PrintStackEntries.
31
29
  attr_reader :items
32
-
33
- # Delimiter between lines in output
30
+ # Delimiter between lines.
34
31
  attr_reader :new_line
35
-
36
- # Maximum allowed width for printing (Called length in the original paper).
37
- attr_reader :width
38
-
39
- # Current available space (Called index in the original paper).
40
- #
41
- # @return [Integer] Current available space (Called index in the original paper).
32
+ # Current available space (`index` in the original paper).
42
33
  attr_reader :space
34
+ # Maximum allowed width for printing (`length` in the original paper).
35
+ attr_reader :width
43
36
 
44
37
  def initialize(width, new_line, config, space, out)
45
38
  @buffer = out
46
39
  @config = config
47
40
  @genspace =
48
- if space.respond_to?(:call)
41
+ if space.respond_to? :call
49
42
  raise ArgumentError, 'space argument must be a Proc of arity 1' \
50
43
  if space.to_proc.arity != 1
51
44
 
@@ -53,58 +46,62 @@ module Oppen
53
46
  else
54
47
  ->(n) { space * n }
55
48
  end
56
- @indent = 0
49
+ @indent = 0 # the amount of indentation to display on the next non empty new line.
57
50
  @items = []
58
51
  @new_line = new_line
59
52
  @width = width
60
53
  @space = width
61
54
  end
62
55
 
63
- # Returns the output of the print stack
56
+ # The final pretty-printed output.
64
57
  #
65
58
  # @return [String]
59
+ # The output of the print stack.
66
60
  def output
61
+ buffer.truncate buffer.pos
67
62
  buffer.string
68
63
  end
69
64
 
70
- # Core method responsible for building the print stack and the output string.
65
+ # Core method responsible for building the print stack and the output
66
+ # string.
71
67
  #
72
- # @note Called Print in the original paper.
68
+ # @note Called `Print` in the original paper.
73
69
  #
74
- # @param token [Token]
75
- # @param token_width [Integer]
70
+ # @param token [Token]
71
+ # @param token_width [Integer]
72
+ # @param trim_on_break [Integer]
73
+ # number of trailing whitespace characters to trim. If zero, no
74
+ # character will be trimmed.
76
75
  #
77
76
  # @return [Nil]
78
- def print(token, token_width)
77
+ def print(token, token_width, trim_on_break: 0)
79
78
  case token
80
79
  in Token::Begin
81
80
  handle_begin token, token_width
82
81
  in Token::End
83
82
  handle_end
84
83
  in Token::Break
85
- handle_break token, token_width
84
+ handle_break token, token_width, trim_on_break: trim_on_break
86
85
  in Token::String
87
86
  handle_string token, token_width
88
87
  end
89
88
  end
90
89
 
91
- # Handle Begin Token.
90
+ # Handle {Token::Begin}.
92
91
  #
93
- # @param token [Token]
92
+ # @param token [Token]
94
93
  # @param token_width [Integer]
95
94
  #
96
95
  # @return [Nil]
97
- #
98
- # @see Token::Begin
99
96
  def handle_begin(token, token_width)
100
97
  if token_width > space
101
98
  type =
102
- if token.break_type == Token::BreakType::CONSISTENT
103
- Token::BreakType::CONSISTENT
99
+ if token.break_type == :consistent
100
+ :consistent
104
101
  else
105
- Token::BreakType::INCONSISTENT
102
+ :inconsistent
106
103
  end
107
- if config&.indent_anchor == Config::IndentAnchor::ON_BEGIN
104
+ if config&.indent_anchor == :current_offset
108
105
  indent = token.offset
109
106
  if !items.empty?
110
107
  indent += top.offset
@@ -114,52 +111,54 @@ module Oppen
114
111
  end
115
112
  push PrintStackEntry.new indent, type
116
113
  else
117
- push PrintStackEntry.new 0, Token::BreakType::FITS
114
+ push PrintStackEntry.new 0, :fits
118
115
  end
119
116
  end
120
117
 
121
- # Handle End Token.
118
+ # Handle {Token::End}.
122
119
  #
123
120
  # @return [Nil]
124
- #
125
- # @see Token::End
126
121
  def handle_end
127
122
  pop
128
123
  end
129
124
 
130
- # Handle Break Token.
125
+ # Handle {Token::Break}.
131
126
  #
132
- # @param token [Token]
133
- # @param token_width [Integer]
127
+ # @param token [Token::Break]
128
+ # @param token_width [Integer]
129
+ # @param trim_on_break [Integer]
130
+ # number of trailing whitespace characters to trim.
131
+ # 0 = none.
134
132
  #
135
133
  # @return [Nil]
136
- #
137
- # @see Token::Break
138
- def handle_break(token, token_width)
134
+ def handle_break(token, token_width, trim_on_break: 0)
139
135
  block = top
140
136
  case block.break_type
141
- in Token::BreakType::FITS
137
+ in :fits
138
+ # No new line is needed (the block fits on the line).
142
139
  @space -= token.width
143
140
  write token
144
- in Token::BreakType::CONSISTENT
141
+ in :consistent
145
142
  @space = block.offset - token.offset
146
143
  indent =
147
- if config&.indent_anchor == Config::IndentAnchor::ON_BEGIN
144
+ if config&.indent_anchor == :current_offset
148
145
  token.offset
149
146
  else
150
147
  width - space
151
148
  end
149
+ erase trim_on_break
152
150
  write token.line_continuation
153
151
  print_new_line indent
154
- in Token::BreakType::INCONSISTENT
152
+ in :inconsistent
155
153
  if token_width > space
156
154
  @space = block.offset - token.offset
157
155
  indent =
158
- if config&.indent_anchor == Config::IndentAnchor::ON_BEGIN
156
+ if config&.indent_anchor == :current_offset
159
157
  token.offset
160
158
  else
161
159
  width - space
162
160
  end
161
+ erase trim_on_break
163
162
  write token.line_continuation
164
163
  print_new_line indent
165
164
  else
@@ -169,14 +168,12 @@ module Oppen
169
168
  end
170
169
  end
171
170
 
172
- # Handle String Token.
171
+ # Handle {Token::String}.
173
172
  #
174
- # @param token [Token]
173
+ # @param token [Token::String]
175
174
  # @param token_width [Integer]
176
175
  #
177
176
  # @return [Nil]
178
- #
179
- # @see Token::String
180
177
  def handle_string(token, token_width)
181
178
  return if token.value.empty?
182
179
 
@@ -188,16 +185,16 @@ module Oppen
188
185
  write token
189
186
  end
190
187
 
191
- # Push a PrintStackEntry into the stack.
188
+ # Push a {PrintStackEntry} into the stack.
192
189
  #
193
190
  # @param print_stack_entry [PrintStackEntry]
194
191
  #
195
192
  # @return [Nil]
196
193
  def push(print_stack_entry)
197
- items.append(print_stack_entry)
194
+ items.append print_stack_entry
198
195
  end
199
196
 
200
- # Pop a PrintStackEntry from the stack.
197
+ # Pop a {PrintStackEntry} from the stack.
201
198
  #
202
199
  # @return [PrintStackEntry]
203
200
  def pop
@@ -221,14 +218,15 @@ module Oppen
221
218
 
222
219
  # Add a new line to the output.
223
220
  #
224
- # @note Called PrintNewLine as well in the original paper.
221
+ # @note Called `PrintNewLine` as well in the original paper.
225
222
  #
226
- # @param amount [Integer] indentation amount.
223
+ # @param amount [Integer]
224
+ # indentation amount.
227
225
  #
228
226
  # @return [Nil]
229
227
  def print_new_line(amount)
230
228
  write new_line
231
- if config&.indent_anchor == Config::IndentAnchor::ON_BEGIN
229
+ if config&.indent_anchor == :current_offset
232
230
  @space = width - top.offset - amount
233
231
  @indent = width - space
234
232
  else
@@ -242,12 +240,24 @@ module Oppen
242
240
  #
243
241
  # @return [Nil]
244
242
  def write(obj)
245
- buffer.write(obj.to_s)
243
+ buffer.write obj.to_s
244
+ end
245
+
246
+ # Erase the last `count` characters.
247
+ #
248
+ # @param count [Integer]
249
+ #
250
+ # @return [Nil]
251
+ def erase(count = 0)
252
+ raise ArgumentError, "count = #{count} must be non-negative" if count.negative?
253
+
254
+ buffer.seek(-count, IO::SEEK_CUR)
255
+ @space += count
246
256
  end
247
257
 
248
258
  # Add indentation by `amount`.
249
259
  #
250
- # @note Called Indent as well in the original paper.
260
+ # @note Called `Indent` as well in the original paper.
251
261
  #
252
262
  # @param amount [Integer]
253
263
  #
@@ -255,7 +265,7 @@ module Oppen
255
265
  def indent(amount)
256
266
  raise ArgumentError 'Indenting using negative amount' if amount.negative?
257
267
 
258
- write genspace.call(amount) if amount.positive?
268
+ write genspace.(amount) if amount.positive?
259
269
  end
260
270
  end
261
271
  end