oppen 0.1.0 → 0.9.0

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: 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