oppen 0.1.0 → 0.9.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 3566ebc351b729f845e192fb3687bfae4a874879b3741de2e1c43fb97946aa23
4
- data.tar.gz: 9499a00567a1ad0d7f7ffc551573888513eb952945a83d90022a499d31248828
3
+ metadata.gz: 7a496d964de133aa177270080b9444d8e9ef865ce1b355bef4a6c4a43462d48c
4
+ data.tar.gz: d2955b0983ef133fd374c1c46fc3424d9cff05c4ec910a6ca5ad94ec3f2db78d
5
5
  SHA512:
6
- metadata.gz: 0f1348d6dac1afc87a495f2e395ed63a28ae42a722c235f1fe33fc317f1e13b0cbe5d7cae51c67a446755a7f26382c9b7555b80e394e9bdc161e620c2fbfde44
7
- data.tar.gz: 430164e1995f18e4c75cddd59e6e39f6669c9312d01d29cb9670298b3459fb345ccd1a22742728b951859c248e628e0b1b968968ad00f025ea070173816fb476
6
+ metadata.gz: d4319e6b5be2d8fa394cf8d6cd593c4e8ab0b5c1c0a313cc42e287a93b46e23a95d844b8bddd01e5547ac67faa4b04395d5edb5ae911380572a0df1b7acf0c95
7
+ data.tar.gz: 680e57e6f38f4cda980b57d04e787b4b33becc7b45de4c73810b243e82169411d62cd0d34b56a67df3e96debcc6d8a2ad777046aedec342429792fc91f3b3346
data/README.md CHANGED
@@ -1,30 +1,74 @@
1
1
  # Oppen's Pretty Printer
2
2
  [![CI badge]][CI]
3
3
  [![Docs latest badge]][Docs latest]
4
- <!-- [![rubygems.org badge]][rubygems.org] -->
4
+ [![rubygems.org badge]][rubygems.org]
5
5
 
6
6
  [CI badge]: https://github.com/Faveod/oppen-ruby/actions/workflows/test.yml/badge.svg
7
7
  [CI]: https://github.com/Faveod/oppen-ruby/actions/workflows/test.yml
8
8
  [Docs latest badge]: https://github.com/Faveod/oppen-ruby/actions/workflows/docs.yml/badge.svg
9
9
  [Docs latest]: https://faveod.github.io/oppen-ruby/
10
+ [rubygems.org badge]: https://img.shields.io/gem/v/oppen?label=rubygems.org
11
+ [rubygems.org]: https://rubygems.org/gems/oppen
10
12
 
11
13
  An implementation of the pretty printing algorithm described by
12
14
  [Derek C. Oppen](https://dl.acm.org/doi/pdf/10.1145/357114.357115).
13
15
 
14
- > [!WARNING]
16
+ We also provide an API similar to
17
+ [`ruby/prettyprint`](https://github.com/ruby/prettyprint), which we call
18
+ `Wadler`, in reference to Philip Wadler's paper, [_A prettier
19
+ printer_](https://homepages.inf.ed.ac.uk/wadler/papers/prettier/prettier.pdf),
20
+ the basis for `prettyprint`. This can be really helpful if you decide to
21
+ transition from `ruby/prettyprint` to this gem.
22
+
23
+ `Wadler` is implemented on top of `Oppen`, and it provides more options than
24
+ `ruby/prettyprint`, notably:
25
+ 1. Consistent and inconsistent breaking.
26
+ 1. Explicit breaking, which is achievable in `ruby/prettyprint` with some
27
+ monkeypatching.
28
+
29
+ > [!CAUTION]
15
30
  > This is still under development.
16
31
 
17
- ## Difference with the original algorithm
32
+ ## Usage
33
+
34
+ > [!WARNING]
35
+ > Lands when the APIs are stable.
36
+
37
+ ## Oppen vs Wadler
38
+
39
+ `Wadler` calls `Oppen` under the hood, so it's not a separate implementation,
40
+ and it's not calling ruby's `prettyprint`.
41
+
42
+ Both implementations have their use cases:
43
+ 1. Oppen gives more control over tokens sent to the printer.
44
+ 1. Wadler gives a more _"functional"_ API, which is far nicer to work with.
45
+
46
+ That being said, both APIs in this gem can achieve the same results, especially
47
+ on consistent and inconsistent breaking.
48
+
49
+ ## Noteworthy details
50
+
51
+ ### Difference with Oppen's original algorithm
52
+
53
+ 1. We took liberty to rename functions to make the API more modern and closer to
54
+ what we expect when writing Ruby code. All correspondences with the algorithm
55
+ as described in Oppen's paper are noted in the comments of classes and methods.
56
+ 1. We do not raise exceptions when we overflow the margin. The only exceptions
57
+ that we raise indicate a bug in the implementation. Please report them.
58
+
59
+ ### Difference with `ruby/prettyprint`
18
60
 
19
- We decided to diverge from Oppen's original algorithm to provide a more
20
- idiomatic Ruby experience, in one particular aspect: exceptions/errors: we do
21
- not raise exceptions when we overflow the margin.
61
+ Oppen's algorithm and `ruby/prettyprint` do not have the same starting positions
62
+ for a group's indentation. That's why you need to pay particular attention to
63
+ calls for `nest`; you might want to decrease them by `1` if you care about keeping
64
+ the same behavior.
22
65
 
23
- The only exceptions that we raise indicate a bug in the implementation. Please
24
- report them.
66
+ This is what we do in our test suite to verify the correspondence of the `Wadler`
67
+ API and the `ruby/prettyprint`. We decided to shift the burden to the user because
68
+ we think that the deicision taken by `ruby/prettyprint` does not suit us.
25
69
 
26
70
  ## Related projects
27
71
 
28
- 1. [Python implementation](https://github.com/stevej2608/oppen-pretty-printer)
29
- as a library.
30
- 1. [rustc implementation](https://doc.rust-lang.org/nightly/nightly-rustc/rustc_ast_pretty/pp/index.html)
72
+ 1. [`ruby/prettyprint`](https://github.com/ruby/prettyprint)
73
+ 1. [rustc implementation](https://doc.rust-lang.org/nightly/nightly-rustc/rustc_ast_pretty/pp/index.html)
74
+ 1. [`stevej2608/oppen-pretty-printer`](https://github.com/stevej2608/oppen-pretty-printer) as a library.
@@ -21,6 +21,12 @@ module Oppen
21
21
  # IO element that builds the output.
22
22
  attr_reader :buffer
23
23
 
24
+ # Config containing customization flags
25
+ attr_reader :config
26
+
27
+ # Callable that generate spaces
28
+ attr_reader :genspace
29
+
24
30
  # Array representing the stack of PrintStackEntries.
25
31
  attr_reader :items
26
32
 
@@ -35,8 +41,18 @@ module Oppen
35
41
  # @return [Integer] Current available space (Called index in the original paper).
36
42
  attr_reader :space
37
43
 
38
- def initialize(margin, new_line)
39
- @buffer = StringIO.new
44
+ def initialize(margin, new_line, config, space, out)
45
+ @buffer = out
46
+ @config = config
47
+ @genspace =
48
+ if space.respond_to?(:call)
49
+ raise ArgumentError, 'space argument must be a Proc of arity 1' \
50
+ if space.to_proc.arity != 1
51
+
52
+ space
53
+ else
54
+ ->(n) { space * n }
55
+ end
40
56
  @items = []
41
57
  @new_line = new_line
42
58
  @margin = margin
@@ -45,7 +61,7 @@ module Oppen
45
61
 
46
62
  # Returns the output of the print stack
47
63
  #
48
- # @return [StringIO]
64
+ # @return [String]
49
65
  def output
50
66
  buffer.string
51
67
  end
@@ -87,7 +103,15 @@ module Oppen
87
103
  else
88
104
  Token::BreakType::INCONSISTENT
89
105
  end
90
- push PrintStackEntry.new space - token.offset, type
106
+ if config&.indent_anchor == Config::IndentAnchor::ON_BEGIN
107
+ indent = token.offset
108
+ if !items.empty?
109
+ indent += top.offset
110
+ end
111
+ else
112
+ indent = space - token.offset
113
+ end
114
+ push PrintStackEntry.new indent, type
91
115
  else
92
116
  push PrintStackEntry.new 0, Token::BreakType::FITS
93
117
  end
@@ -114,18 +138,32 @@ module Oppen
114
138
  block = top
115
139
  case block.break_type
116
140
  in Token::BreakType::FITS
117
- @space -= token.blank_space
118
- indent token.blank_space
141
+ @space -= token.length
142
+ write token
119
143
  in Token::BreakType::CONSISTENT
120
144
  @space = block.offset - token.offset
121
- print_new_line margin - space
145
+ indent =
146
+ if config&.indent_anchor == Config::IndentAnchor::ON_BEGIN
147
+ token.offset
148
+ else
149
+ margin - space
150
+ end
151
+ write token.line_continuation
152
+ print_new_line indent
122
153
  in Token::BreakType::INCONSISTENT
123
154
  if token_length > space
124
155
  @space = block.offset - token.offset
125
- print_new_line margin - space
156
+ indent =
157
+ if config&.indent_anchor == Config::IndentAnchor::ON_BEGIN
158
+ token.offset
159
+ else
160
+ margin - space
161
+ end
162
+ write token.line_continuation
163
+ print_new_line indent
126
164
  else
127
- @space -= token.blank_space
128
- indent token.blank_space
165
+ @space -= token.length
166
+ write token
129
167
  end
130
168
  end
131
169
  end
@@ -140,7 +178,7 @@ module Oppen
140
178
  # @see Token::String
141
179
  def handle_string(token, token_length)
142
180
  @space = [0, space - token_length].max
143
- write token.value
181
+ write token
144
182
  end
145
183
 
146
184
  # Push a PrintStackEntry into the stack.
@@ -183,16 +221,21 @@ module Oppen
183
221
  # @return [Nil]
184
222
  def print_new_line(amount)
185
223
  write new_line
186
- indent amount
224
+ if config&.indent_anchor == Config::IndentAnchor::ON_BEGIN
225
+ @space = margin - top.offset - amount
226
+ indent margin - space
227
+ else
228
+ indent amount
229
+ end
187
230
  end
188
231
 
189
232
  # Write a string to the output.
190
233
  #
191
- # @param string [String]
234
+ # @param obj [Object]
192
235
  #
193
236
  # @return [Nil]
194
- def write(string)
195
- buffer.write(string)
237
+ def write(obj)
238
+ buffer.write(obj.to_s)
196
239
  end
197
240
 
198
241
  # Add indentation by `amount`.
@@ -203,7 +246,7 @@ module Oppen
203
246
  #
204
247
  # @return [Nil]
205
248
  def indent(amount)
206
- write ' ' * amount
249
+ write genspace.call(amount)
207
250
  end
208
251
  end
209
252
  end
data/lib/oppen/printer.rb CHANGED
@@ -9,6 +9,7 @@ require_relative 'print_stack'
9
9
  module Oppen
10
10
  # Oppen pretty-printer.
11
11
  class Printer
12
+ attr_reader :config
12
13
  # Ring buffer left index.
13
14
  #
14
15
  # @note Called left as well in the original paper.
@@ -51,13 +52,23 @@ module Oppen
51
52
  #
52
53
  # @param margin [Integer] maximum line width desired.
53
54
  # @param new_line [String] the delimiter between lines.
54
- def initialize(margin, new_line)
55
+ # @param config [Config]
56
+ # @param space [String, Proc] could be a String or a callable.
57
+ # If it's a string, spaces will be generated with the the
58
+ # lambda `->(n){ n * space }`, where `n` is the number of columns
59
+ # to indent.
60
+ # If it's a callable, it will receive `n` and it needs to return
61
+ # a string.
62
+ # @param out [Object] should have a write and string method
63
+ def initialize(margin, new_line, config = Config.oppen,
64
+ space = ' ', out = StringIO.new)
55
65
  # Maximum size if the stacks
56
66
  n = 3 * margin
57
67
 
68
+ @config = config
58
69
  @left = 0
59
70
  @left_total = 1
60
- @print_stack = PrintStack.new margin, new_line
71
+ @print_stack = PrintStack.new margin, new_line, config, space, out
61
72
  @right = 0
62
73
  @right_total = 1
63
74
  @scan_stack = ScanStack.new n
@@ -65,7 +76,7 @@ module Oppen
65
76
  @tokens = Array.new n
66
77
  end
67
78
 
68
- # @return [StringIO]
79
+ # @return [String]
69
80
  def output
70
81
  print_stack.output
71
82
  end
@@ -137,6 +148,11 @@ module Oppen
137
148
  tokens[right] = token
138
149
  size[right] = -1
139
150
  scan_stack.push right
151
+ if config&.eager_print &&
152
+ (!scan_stack.empty? && right_total - left_total < print_stack.space)
153
+ check_stack 0
154
+ advance_left tokens[left], size[left]
155
+ end
140
156
  end
141
157
  end
142
158
 
@@ -158,7 +174,7 @@ module Oppen
158
174
  scan_stack.push right
159
175
  tokens[right] = token
160
176
  size[right] = -right_total
161
- @right_total += token.blank_space
177
+ @right_total += token.length
162
178
  end
163
179
 
164
180
  # Handle String Token.
@@ -220,7 +236,7 @@ module Oppen
220
236
 
221
237
  case token
222
238
  when Token::Break
223
- @left_total += token.blank_space
239
+ @left_total += token.length
224
240
  when Token::String
225
241
  @left_total += token_length
226
242
  end
@@ -239,7 +255,7 @@ module Oppen
239
255
  # @param depth [Integer] depth of the group
240
256
  #
241
257
  # @return [Nil]
242
- def check_stack(depth) # rubocop:disable Metrics/AbcSize
258
+ def check_stack(depth)
243
259
  return if scan_stack.empty?
244
260
 
245
261
  x = scan_stack.top
data/lib/oppen/token.rb CHANGED
@@ -3,7 +3,7 @@
3
3
  # Oppen.
4
4
  module Oppen
5
5
  # Token.
6
- module Token
6
+ class Token
7
7
  # BreakType.
8
8
  #
9
9
  # FITS => No break is needed (the block fits on the line).
@@ -18,43 +18,79 @@ module Oppen
18
18
  CONSISTENT = 2
19
19
  end
20
20
 
21
+ # Default token length
22
+ # @return [Integer]
23
+ def length
24
+ 0
25
+ end
26
+
21
27
  # String Token.
22
- class String
28
+ class String < Token
23
29
  # @return [String] String value.
24
30
  attr_reader :value
25
31
 
26
32
  def initialize(value)
27
33
  @value = value
34
+ super()
28
35
  end
29
36
 
30
37
  # @return [Integer]
31
38
  def length
32
39
  value.length
33
40
  end
41
+
42
+ # @return [String]
43
+ def to_s
44
+ value
45
+ end
34
46
  end
35
47
 
36
48
  # Break Token.
37
- class Break
38
- # @return [Integer] Number of blank spaces.
39
- attr_reader :blank_space
49
+ class Break < Token
50
+ # @return [String] Break strings.
51
+ attr_reader :str
52
+ # @return [String] If a new line is needed display this string before the new line
53
+ attr_reader :line_continuation
40
54
  # @return [Integer] Indentation.
41
55
  attr_reader :offset
42
56
 
43
- def initialize(blank_space: 1, offset: 0)
44
- @blank_space = blank_space
57
+ def initialize(str = ' ', line_continuation: '', offset: 0)
58
+ raise ArgumentError, 'line_continuation cannot be nil' if line_continuation.nil?
59
+
60
+ @line_continuation = line_continuation
45
61
  @offset = offset
62
+ @str = str
63
+ super()
64
+ end
65
+
66
+ # @return [Integer]
67
+ def length
68
+ str.length
69
+ end
70
+
71
+ # @return [String]
72
+ def to_s
73
+ str
46
74
  end
47
75
  end
48
76
 
49
77
  # Distinguished instance of Break which forces a line break.
50
78
  class LineBreak < Break
51
- def initialize(offset: 0)
52
- super(blank_space: 9999, offset:)
79
+ # Mock string that represents an infinite string to force new line.
80
+ class LineBreakString
81
+ # @return [Integer]
82
+ def length
83
+ 999_999
84
+ end
85
+ end
86
+
87
+ def initialize(line_continuation: '', offset: 0)
88
+ super(LineBreakString.new, line_continuation:, offset:)
53
89
  end
54
90
  end
55
91
 
56
92
  # Begin Token.
57
- class Begin
93
+ class Begin < Token
58
94
  # @return [BreakType]
59
95
  attr_reader :break_type
60
96
  # @return [Integer] Indentation.
@@ -63,16 +99,17 @@ module Oppen
63
99
  def initialize(break_type: BreakType::INCONSISTENT, offset: 2)
64
100
  @offset = offset
65
101
  @break_type = break_type
102
+ super()
66
103
  end
67
104
  end
68
105
 
69
106
  # End Token
70
- class End
107
+ class End < Token
71
108
  nil
72
109
  end
73
110
 
74
111
  # EOF Token
75
- class EOF
112
+ class EOF < Token
76
113
  nil
77
114
  end
78
115
  end
data/lib/oppen/version.rb CHANGED
@@ -5,5 +5,5 @@ module Oppen
5
5
  # Oppen version
6
6
  #
7
7
  # @return [String] current version
8
- VERSION = '0.1.0' # managed by release.sh
8
+ VERSION = '0.9.0' # managed by release.sh
9
9
  end
data/lib/oppen.rb CHANGED
@@ -5,21 +5,142 @@ require_relative 'oppen/print_stack'
5
5
  require_relative 'oppen/scan_stack'
6
6
  require_relative 'oppen/token'
7
7
  require_relative 'oppen/version'
8
+ require_relative 'wadler/print'
8
9
 
9
10
  # Oppen.
10
11
  module Oppen
11
12
  # Entry point of the pretty printer.
12
13
  #
13
- # @param tokens [Array[Token]] the list of tokens to be printed
14
+ # @param config [Config]
15
+ # @param space [String, Proc] could be a String or a callable.
16
+ # If it's a string, spaces will be generated with the the
17
+ # lambda `->(n){ n * space }`, where `n` is the number of columns
18
+ # to indent.
19
+ # If it's a callable, it will receive `n` and it needs to return
20
+ # a string.
14
21
  # @param margin [Integer] maximum line width desired
15
22
  # @param new_line [String] the delimiter between lines
23
+ # @param out [Object] should have a write and string method
24
+ # @param tokens [Array[Token]] the list of tokens to be printed
16
25
  #
17
- # @return [StringIO] output of the pretty printer
18
- def self.print(tokens: [], margin: 80, new_line: "\n")
19
- printer = Printer.new margin, new_line
26
+ # @return [String] output of the pretty printer
27
+ def self.print(config: Config.oppen, space: ' ',
28
+ margin: 80, new_line: "\n", out: StringIO.new, tokens: [])
29
+ printer = Printer.new margin, new_line, config, space, out
20
30
  tokens.each do |token|
21
31
  printer.print token
22
32
  end
23
33
  printer.output
24
34
  end
35
+
36
+ # Config.
37
+ class Config
38
+ # IndentAnchor.
39
+ #
40
+ # ON_BREAK => anchor on break position (as in Oppen's original paper)
41
+ # ON_BEGIN => anchor on begin block position
42
+ module IndentAnchor
43
+ # @return [Integer]
44
+ ON_BREAK = 0
45
+ # @return [Integer]
46
+ ON_BEGIN = 1
47
+ end
48
+
49
+ attr_accessor :indent_anchor
50
+ # Print groups eagerly
51
+ #
52
+ # @example
53
+ # out = Oppen::Wadler.new (margin: 13)
54
+ # out.group {
55
+ # out.group {
56
+ # out.text 'abc'
57
+ # out.breakable
58
+ # out.text 'def'
59
+ # }
60
+ # out.group {
61
+ # out.text 'ghi'
62
+ # out.breakable
63
+ # out.text 'jkl'
64
+ # }
65
+ # }
66
+ # out.output
67
+ #
68
+ # # eager_print: false
69
+ # # =>
70
+ # # abc
71
+ # # defghi jkl
72
+ # #
73
+ # # eager_print: true
74
+ # # =>
75
+ # # abc defghi
76
+ # # jkl
77
+ #
78
+ # @return [Boolean]
79
+ attr_accessor :eager_print
80
+
81
+ def initialize(indent_anchor: IndentAnchor::ON_BREAK, eager_print: false)
82
+ @indent_anchor = indent_anchor
83
+ @eager_print = eager_print
84
+ end
85
+
86
+ # Default config for Oppen usage
87
+ # @return [Config]
88
+ def self.oppen
89
+ new
90
+ end
91
+
92
+ # Default config for Wadler usage
93
+ # @return [Config]
94
+ def self.wadler(eager_print: true)
95
+ new(indent_anchor: IndentAnchor::ON_BEGIN, eager_print:)
96
+ end
97
+ end
98
+
99
+ # @param value [String]
100
+ #
101
+ # @return [Oppen::Token::String] a new String token
102
+ def self.string(value)
103
+ Token::String.new(value)
104
+ end
105
+
106
+ # @param str [String]
107
+ # @param line_continuation [String] If a new line is needed display this string before the new line
108
+ # @param offset [Integer]
109
+ #
110
+ # @return [Oppen::Token::Break] a new Break token
111
+ def self.break(str = ' ', line_continuation: '', offset: 0)
112
+ Token::Break.new(str, line_continuation:, offset:)
113
+ end
114
+
115
+ # @param line_continuation [String] If a new line is needed display this string before the new line
116
+ # @param offset [Integer]
117
+ #
118
+ # @return [Oppen::Token::LineBreak] a new LineBreak token
119
+ def self.line_break(line_continuation: '', offset: 0)
120
+ Token::LineBreak.new(line_continuation:, offset:)
121
+ end
122
+
123
+ # @param offset [Integer]
124
+ #
125
+ # @return [Oppen::Token::Begin] a new consistent Begin token
126
+ def self.begin_consistent(offset: 2)
127
+ Token::Begin.new(break_type: Token::BreakType::CONSISTENT, offset:)
128
+ end
129
+
130
+ # @param offset [Integer]
131
+ #
132
+ # @return [Oppen::Token::Begin] a new inconsistent Begin token
133
+ def self.begin_inconsistent(offset: 2)
134
+ Token::Begin.new(break_type: Token::BreakType::INCONSISTENT, offset:)
135
+ end
136
+
137
+ # @return [Oppen::Token::End] a new End token
138
+ def self.end
139
+ Token::End.new
140
+ end
141
+
142
+ # @return [Oppen::Token::EOF] a new EOF token
143
+ def self.eof
144
+ Token::EOF.new
145
+ end
25
146
  end
@@ -0,0 +1,132 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Oppen.
4
+ module Oppen
5
+ # Wadler.
6
+ class Wadler
7
+ attr_reader :config
8
+ attr_reader :current_indent
9
+ attr_reader :space
10
+ attr_reader :margin
11
+ attr_reader :new_line
12
+ attr_reader :out
13
+ attr_reader :tokens
14
+
15
+ # @param config [Oppen::Config]
16
+ # @param space [String, Proc] could be a String or a callable.
17
+ # If it's a string, spaces will be generated with the the
18
+ # lambda `->(n){ n * space }`, where `n` is the number of columns
19
+ # to indent.
20
+ # If it's a callable, it will receive `n` and it needs to return
21
+ # a string.
22
+ # @param margin [Integer]
23
+ # @param new_line [String]
24
+ # @param out [Object] should have a write and string method
25
+ def initialize(config: Config.wadler, space: ' ',
26
+ margin: 80, new_line: "\n", out: StringIO.new)
27
+ @config = config
28
+ @current_indent = 0
29
+ @space = space
30
+ @margin = margin
31
+ @new_line = new_line
32
+ @out = out
33
+ @tokens = []
34
+ end
35
+
36
+ # @return [String]
37
+ def output
38
+ if !tokens.last.is_a? Oppen::Token::EOF
39
+ tokens << Oppen.eof
40
+ end
41
+ Oppen.print(tokens:, margin:, new_line:, config:, space:, out:)
42
+ end
43
+
44
+ # @param indent [Integer] group indentation
45
+ # @param open_obj [String] group opening delimiter
46
+ # @param close_obj [String] group closing delimiter
47
+ # @param break_type [Oppen::Token::BreakType] group breaking type
48
+ #
49
+ # @yield the block of text in a group
50
+ #
51
+ # @return [Nil]
52
+ def group(indent = 0, open_obj = '', close_obj = '',
53
+ break_type = Oppen::Token::BreakType::CONSISTENT)
54
+ raise ArgumentError, "#{open_obj.nil? ? 'open_obj' : 'close_obj'} cannot be nil" \
55
+ if open_obj.nil? || close_obj.nil?
56
+
57
+ tokens <<
58
+ case break_type
59
+ in Oppen::Token::BreakType::CONSISTENT
60
+ Oppen.begin_consistent(offset: indent)
61
+ in Oppen::Token::BreakType::INCONSISTENT
62
+ Oppen.begin_inconsistent(offset: indent)
63
+ end
64
+
65
+ if !open_obj.empty?
66
+ self.break
67
+ text(open_obj)
68
+ end
69
+
70
+ yield
71
+
72
+ if !close_obj.empty?
73
+ self.break
74
+ text(close_obj)
75
+ end
76
+
77
+ tokens << Oppen.end
78
+ end
79
+
80
+ # @param indent [Integer] nest indentation
81
+ # @param open_obj [String] nest opening delimiter
82
+ # @param close_obj [String] nest closing delimiter
83
+ # @param break_type [Oppen::Token::BreakType] nest breaking type
84
+ #
85
+ # @return [Nil]
86
+ def nest(indent, open_obj = '', close_obj = '',
87
+ break_type = Oppen::Token::BreakType::CONSISTENT)
88
+ raise ArgumentError, "#{open_obj.nil? ? 'open_obj' : 'close_obj'} cannot be nil" \
89
+ if open_obj.nil? || close_obj.nil?
90
+
91
+ @current_indent += indent
92
+
93
+ if !open_obj.empty?
94
+ text(open_obj)
95
+ self.break
96
+ end
97
+
98
+ begin
99
+ yield
100
+ ensure
101
+ @current_indent -= indent
102
+ end
103
+
104
+ return if close_obj.empty?
105
+
106
+ self.break
107
+ text(close_obj)
108
+ end
109
+
110
+ # @param value [String]
111
+ #
112
+ # @return [Nil]
113
+ def text(value)
114
+ tokens << Oppen.string(value)
115
+ end
116
+
117
+ # @param str [String]
118
+ # @param line_continuation [String] If a new line is needed display this string before the new line
119
+ #
120
+ # @return [Nil]
121
+ def breakable(str = ' ', line_continuation: '')
122
+ tokens << Oppen.break(str, line_continuation:, offset: current_indent)
123
+ end
124
+
125
+ # @param line_continuation [String] If a new line is needed display this string before the new line
126
+ #
127
+ # @return [Nil]
128
+ def break(line_continuation: '')
129
+ tokens << Oppen.line_break(line_continuation:, offset: current_indent)
130
+ end
131
+ end
132
+ end
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.1.0
4
+ version: 0.9.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Amine Mike El Maalouf <amine.el-maalouf@epita.fr>
@@ -9,25 +9,23 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2024-09-13 00:00:00.000000000 Z
12
+ date: 2024-09-27 00:00:00.000000000 Z
13
13
  dependencies: []
14
14
  description: Implementation of the Oppen's pretty printing algorithm
15
15
  email:
16
- executables:
17
- - main.rb
16
+ executables: []
18
17
  extensions: []
19
18
  extra_rdoc_files: []
20
19
  files:
21
20
  - LICENSE
22
21
  - README.md
23
- - bin/main.rb
24
- - bin/repl.rb
25
22
  - lib/oppen.rb
26
23
  - lib/oppen/print_stack.rb
27
24
  - lib/oppen/printer.rb
28
25
  - lib/oppen/scan_stack.rb
29
26
  - lib/oppen/token.rb
30
27
  - lib/oppen/version.rb
28
+ - lib/wadler/print.rb
31
29
  homepage: http://github.com/Faveod/oppen-ruby
32
30
  licenses:
33
31
  - MIT
data/bin/main.rb DELETED
@@ -1,24 +0,0 @@
1
- #!/usr/bin/env ruby
2
- # frozen_string_literal: true
3
-
4
- lib = File.expand_path('lib', __dir__)
5
- $LOAD_PATH.unshift(lib) if !$LOAD_PATH.include?(lib)
6
-
7
- require 'oppen'
8
-
9
- list = [
10
- Oppen::Token::Begin.new,
11
- Oppen::Token::String.new('XXXXXXXXXX'),
12
- Oppen::Token::Break.new,
13
- Oppen::Token::String.new('+'),
14
- Oppen::Token::Break.new,
15
- Oppen::Token::String.new('YYYYYYYYYY'),
16
- Oppen::Token::Break.new,
17
- Oppen::Token::String.new('+'),
18
- Oppen::Token::Break.new,
19
- Oppen::Token::String.new('ZZZZZZZZZZ'),
20
- Oppen::Token::End.new,
21
- Oppen::Token::EOF.new,
22
- ]
23
-
24
- puts Oppen.print tokens: list, line_width: 25
data/bin/repl.rb DELETED
@@ -1,19 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'colored'
4
-
5
- # Fancy
6
- WELCOME = <<-'YGM'
7
- __ __ ___ __ __ ____ ____ ___ ___ ___ ____ ____ ___ ___ ___
8
- | | | / \ | | || \ / | / _]| | || \ / || | | / _]
9
- | | || || | || D )| __| / [_ | _ _ || _ || o || _ _ | / [_
10
- | ~ || O || | || / | | || _]| \_/ || | || || \_/ || _]
11
- |___, || || : || \ | |_ || [_ | | || | || _ || | || [_
12
- | || || || . \| ______ ______ ______ __ || |
13
- |____/ \___/ \__,_||__|\_||___ /\ == \ /\ ___\ /\ == \ /\ \ ___||_____|
14
- \ \ __< \ \ __\ \ \ _-/ \ \ \____
15
- \ \_\ \_\ \ \_____\ \ \_\ \ \_____\
16
- \/_/ /_/ \/_____/ \/_/ \/_____/
17
- YGM
18
-
19
- puts WELCOME.green