oppen 0.9.6 → 0.9.8

Sign up to get free protection for your applications and to get access to all the features.
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