oppen 0.1.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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 3566ebc351b729f845e192fb3687bfae4a874879b3741de2e1c43fb97946aa23
4
+ data.tar.gz: 9499a00567a1ad0d7f7ffc551573888513eb952945a83d90022a499d31248828
5
+ SHA512:
6
+ metadata.gz: 0f1348d6dac1afc87a495f2e395ed63a28ae42a722c235f1fe33fc317f1e13b0cbe5d7cae51c67a446755a7f26382c9b7555b80e394e9bdc161e620c2fbfde44
7
+ data.tar.gz: 430164e1995f18e4c75cddd59e6e39f6669c9312d01d29cb9670298b3459fb345ccd1a22742728b951859c248e628e0b1b968968ad00f025ea070173816fb476
data/LICENSE ADDED
@@ -0,0 +1,25 @@
1
+ The MIT License (MIT)
2
+ =====================
3
+
4
+ Copyright © `<2024>` `<Faveod>`
5
+
6
+ Permission is hereby granted, free of charge, to any person
7
+ obtaining a copy of this software and associated documentation
8
+ files (the “Software”), to deal in the Software without
9
+ restriction, including without limitation the rights to use,
10
+ copy, modify, merge, publish, distribute, sublicense, and/or sell
11
+ copies of the Software, and to permit persons to whom the
12
+ Software is furnished to do so, subject to the following
13
+ conditions:
14
+
15
+ The above copyright notice and this permission notice shall be
16
+ included in all copies or substantial portions of the Software.
17
+
18
+ THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND,
19
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
20
+ OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
21
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
22
+ HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
23
+ WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
24
+ FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
25
+ OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,30 @@
1
+ # Oppen's Pretty Printer
2
+ [![CI badge]][CI]
3
+ [![Docs latest badge]][Docs latest]
4
+ <!-- [![rubygems.org badge]][rubygems.org] -->
5
+
6
+ [CI badge]: https://github.com/Faveod/oppen-ruby/actions/workflows/test.yml/badge.svg
7
+ [CI]: https://github.com/Faveod/oppen-ruby/actions/workflows/test.yml
8
+ [Docs latest badge]: https://github.com/Faveod/oppen-ruby/actions/workflows/docs.yml/badge.svg
9
+ [Docs latest]: https://faveod.github.io/oppen-ruby/
10
+
11
+ An implementation of the pretty printing algorithm described by
12
+ [Derek C. Oppen](https://dl.acm.org/doi/pdf/10.1145/357114.357115).
13
+
14
+ > [!WARNING]
15
+ > This is still under development.
16
+
17
+ ## Difference with the original algorithm
18
+
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.
22
+
23
+ The only exceptions that we raise indicate a bug in the implementation. Please
24
+ report them.
25
+
26
+ ## Related projects
27
+
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)
data/bin/main.rb ADDED
@@ -0,0 +1,24 @@
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 ADDED
@@ -0,0 +1,19 @@
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
@@ -0,0 +1,209 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Oppen.
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.
7
+ class PrintStack
8
+ # Class that represents an item in the print stack.
9
+ class PrintStackEntry
10
+ # @return [Integer] Indentation level.
11
+ attr_reader :offset
12
+ # @return [Token::BreakType] (Called break in the original paper).
13
+ attr_reader :break_type
14
+
15
+ def initialize(offset, break_type)
16
+ @offset = offset
17
+ @break_type = break_type
18
+ end
19
+ end
20
+
21
+ # IO element that builds the output.
22
+ attr_reader :buffer
23
+
24
+ # Array representing the stack of PrintStackEntries.
25
+ attr_reader :items
26
+
27
+ # Delimiter between lines in output
28
+ attr_reader :new_line
29
+
30
+ # Page margin (Called length in the original paper).
31
+ attr_reader :margin
32
+
33
+ # Current available space (Called index in the original paper).
34
+ #
35
+ # @return [Integer] Current available space (Called index in the original paper).
36
+ attr_reader :space
37
+
38
+ def initialize(margin, new_line)
39
+ @buffer = StringIO.new
40
+ @items = []
41
+ @new_line = new_line
42
+ @margin = margin
43
+ @space = margin
44
+ end
45
+
46
+ # Returns the output of the print stack
47
+ #
48
+ # @return [StringIO]
49
+ def output
50
+ buffer.string
51
+ end
52
+
53
+ # Core method responsible for building the print stack and the output string.
54
+ #
55
+ # @note Called Print in the original paper.
56
+ #
57
+ # @param token [Token]
58
+ # @param token_length [Integer]
59
+ #
60
+ # @return [Nil]
61
+ def print(token, token_length)
62
+ case token
63
+ in Token::Begin
64
+ handle_begin token, token_length
65
+ in Token::End
66
+ handle_end
67
+ in Token::Break
68
+ handle_break token, token_length
69
+ in Token::String
70
+ handle_string token, token_length
71
+ end
72
+ end
73
+
74
+ # Handle Begin Token.
75
+ #
76
+ # @param token [Token]
77
+ # @param token_length [Integer]
78
+ #
79
+ # @return [Nil]
80
+ #
81
+ # @see Token::Begin
82
+ def handle_begin(token, token_length)
83
+ if token_length > space
84
+ type =
85
+ if token.break_type == Token::BreakType::CONSISTENT
86
+ Token::BreakType::CONSISTENT
87
+ else
88
+ Token::BreakType::INCONSISTENT
89
+ end
90
+ push PrintStackEntry.new space - token.offset, type
91
+ else
92
+ push PrintStackEntry.new 0, Token::BreakType::FITS
93
+ end
94
+ end
95
+
96
+ # Handle End Token.
97
+ #
98
+ # @return [Nil]
99
+ #
100
+ # @see Token::End
101
+ def handle_end
102
+ pop
103
+ end
104
+
105
+ # Handle Break Token.
106
+ #
107
+ # @param token [Token]
108
+ # @param token_length [Integer]
109
+ #
110
+ # @return [Nil]
111
+ #
112
+ # @see Token::Break
113
+ def handle_break(token, token_length)
114
+ block = top
115
+ case block.break_type
116
+ in Token::BreakType::FITS
117
+ @space -= token.blank_space
118
+ indent token.blank_space
119
+ in Token::BreakType::CONSISTENT
120
+ @space = block.offset - token.offset
121
+ print_new_line margin - space
122
+ in Token::BreakType::INCONSISTENT
123
+ if token_length > space
124
+ @space = block.offset - token.offset
125
+ print_new_line margin - space
126
+ else
127
+ @space -= token.blank_space
128
+ indent token.blank_space
129
+ end
130
+ end
131
+ end
132
+
133
+ # Handle String Token.
134
+ #
135
+ # @param token [Token]
136
+ # @param token_length [Integer]
137
+ #
138
+ # @return [Nil]
139
+ #
140
+ # @see Token::String
141
+ def handle_string(token, token_length)
142
+ @space = [0, space - token_length].max
143
+ write token.value
144
+ end
145
+
146
+ # Push a PrintStackEntry into the stack.
147
+ #
148
+ # @param print_stack_entry [PrintStackEntry]
149
+ #
150
+ # @return [Nil]
151
+ def push(print_stack_entry)
152
+ items.append(print_stack_entry)
153
+ end
154
+
155
+ # Pop a PrintStackEntry from the stack.
156
+ #
157
+ # @return [PrintStackEntry]
158
+ def pop
159
+ if items.empty?
160
+ raise 'Popping empty stack'
161
+ end
162
+
163
+ items.pop
164
+ end
165
+
166
+ # Get the element at the top of the stack.
167
+ #
168
+ # @return [PrintStackEntry]
169
+ def top
170
+ if items.empty?
171
+ raise 'Accessing empty stack'
172
+ end
173
+
174
+ items.last
175
+ end
176
+
177
+ # Add a new line to the output.
178
+ #
179
+ # @note Called PrintNewLine as well in the original paper.
180
+ #
181
+ # @param amount [Integer] indentation amount.
182
+ #
183
+ # @return [Nil]
184
+ def print_new_line(amount)
185
+ write new_line
186
+ indent amount
187
+ end
188
+
189
+ # Write a string to the output.
190
+ #
191
+ # @param string [String]
192
+ #
193
+ # @return [Nil]
194
+ def write(string)
195
+ buffer.write(string)
196
+ end
197
+
198
+ # Add indentation by `amount`.
199
+ #
200
+ # @note Called Indent as well in the original paper.
201
+ #
202
+ # @param amount [Integer]
203
+ #
204
+ # @return [Nil]
205
+ def indent(amount)
206
+ write ' ' * amount
207
+ end
208
+ end
209
+ end
@@ -0,0 +1,263 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'stringio'
4
+
5
+ require_relative 'scan_stack'
6
+ require_relative 'print_stack'
7
+
8
+ # Oppen.
9
+ module Oppen
10
+ # Oppen pretty-printer.
11
+ class Printer
12
+ # Ring buffer left index.
13
+ #
14
+ # @note Called left as well in the original paper.
15
+ attr_reader :left
16
+
17
+ # Total number of spaces needed to print from start of buffer to the left.
18
+ #
19
+ # @note Called leftTotal as well in the original paper.
20
+ attr_reader :left_total
21
+
22
+ # @note Called printStack as well in the original paper.
23
+ attr_reader :print_stack
24
+
25
+ # Ring buffer right index.
26
+ #
27
+ # @note Called right as well in the original paper.
28
+ attr_reader :right
29
+
30
+ # Total number of spaces needed to print from start of buffer to the right.
31
+ #
32
+ # @note Called leftTotal as well in the original paper.
33
+ attr_reader :right_total
34
+
35
+ # Potential breaking positions.
36
+ #
37
+ # @note Called scanStack as well in the original paper.
38
+ attr_reader :scan_stack
39
+
40
+ # Size buffer, initially filled with nil.
41
+ #
42
+ # @note Called size as well in the original paper.
43
+ attr_reader :size
44
+
45
+ # Token buffer, initially filled with nil.
46
+ #
47
+ # @note Called token in the original paper.
48
+ attr_reader :tokens
49
+
50
+ # @note Called PrettyPrintInit in the original paper.
51
+ #
52
+ # @param margin [Integer] maximum line width desired.
53
+ # @param new_line [String] the delimiter between lines.
54
+ def initialize(margin, new_line)
55
+ # Maximum size if the stacks
56
+ n = 3 * margin
57
+
58
+ @left = 0
59
+ @left_total = 1
60
+ @print_stack = PrintStack.new margin, new_line
61
+ @right = 0
62
+ @right_total = 1
63
+ @scan_stack = ScanStack.new n
64
+ @size = Array.new n
65
+ @tokens = Array.new n
66
+ end
67
+
68
+ # @return [StringIO]
69
+ def output
70
+ print_stack.output
71
+ end
72
+
73
+ # Core function of the algorithm responsible for populating the scan and print stack.
74
+ #
75
+ # @note Called PrettyPrint as well in the original paper.
76
+ #
77
+ # @param token [Token]
78
+ #
79
+ # @return [Nil]
80
+ def print(token)
81
+ case token
82
+ in Token::EOF
83
+ handle_eof
84
+ in Token::Begin
85
+ handle_begin token
86
+ in Token::End
87
+ handle_end token
88
+ in Token::Break
89
+ handle_break token
90
+ in Token::String
91
+ handle_string token
92
+ end
93
+ end
94
+
95
+ # Handle EOF Token.
96
+ #
97
+ # @return [Nil]
98
+ #
99
+ # @see Token::EOF
100
+ def handle_eof
101
+ if !scan_stack.empty?
102
+ check_stack 0
103
+ advance_left tokens[left], size[left]
104
+ end
105
+ print_stack.indent 0
106
+ end
107
+
108
+ # Handle Begin Token.
109
+ #
110
+ # @return [Nil]
111
+ #
112
+ # @see Token::Begin
113
+ def handle_begin(token)
114
+ if scan_stack.empty?
115
+ @left = 0
116
+ @left_total = 1
117
+ @right = 0
118
+ @right_total = 1
119
+ else
120
+ advance_right
121
+ end
122
+ tokens[right] = token
123
+ size[right] = -right_total
124
+ scan_stack.push right
125
+ end
126
+
127
+ # Handle End Token.
128
+ #
129
+ # @return [Nil]
130
+ #
131
+ # @see Token::End
132
+ def handle_end(token)
133
+ if scan_stack.empty?
134
+ print_stack.print token, 0
135
+ else
136
+ advance_right
137
+ tokens[right] = token
138
+ size[right] = -1
139
+ scan_stack.push right
140
+ end
141
+ end
142
+
143
+ # Handle Break Token.
144
+ #
145
+ # @return [Nil]
146
+ #
147
+ # @see Token::Break
148
+ def handle_break(token)
149
+ if scan_stack.empty?
150
+ @left = 0
151
+ @left_total = 1
152
+ @right = 0
153
+ @right_total = 1
154
+ else
155
+ advance_right
156
+ end
157
+ check_stack 0
158
+ scan_stack.push right
159
+ tokens[right] = token
160
+ size[right] = -right_total
161
+ @right_total += token.blank_space
162
+ end
163
+
164
+ # Handle String Token.
165
+ #
166
+ # @return [Nil]
167
+ #
168
+ # @see Token::String
169
+ def handle_string(token)
170
+ if scan_stack.empty?
171
+ print_stack.print token, token.length
172
+ else
173
+ advance_right
174
+ tokens[right] = token
175
+ size[right] = token.length
176
+ @right_total += token.length
177
+ check_stream
178
+ end
179
+ end
180
+
181
+ # Flushes the input if possible.
182
+ #
183
+ # @note Called CheckStream as well in the original paper.
184
+ #
185
+ # @return [Nil]
186
+ def check_stream
187
+ return if right_total - left_total <= print_stack.space
188
+
189
+ if !scan_stack.empty? && left == scan_stack.bottom
190
+ size[scan_stack.pop_bottom] = Float::INFINITY
191
+ end
192
+ advance_left tokens[left], size[left]
193
+ return if left == right
194
+
195
+ check_stream
196
+ end
197
+
198
+ # Advances the `right` pointer.
199
+ #
200
+ # @note Called AdvanceRight as well in the original paper.
201
+ #
202
+ # @return [Nil]
203
+ def advance_right
204
+ @right = (right + 1) % scan_stack.length
205
+ return if right != left
206
+
207
+ raise 'Token queue full'
208
+ end
209
+
210
+ # Advances the `left` pointer and lets the print stack
211
+ # print some of the tokens it contains.
212
+ #
213
+ # @note Called AdvanceLeft as well in the original paper.
214
+ #
215
+ # @return [Nil]
216
+ def advance_left(token, token_length)
217
+ return if token_length.negative?
218
+
219
+ print_stack.print token, token_length
220
+
221
+ case token
222
+ when Token::Break
223
+ @left_total += token.blank_space
224
+ when Token::String
225
+ @left_total += token_length
226
+ end
227
+
228
+ return if left == right
229
+
230
+ @left = (left + 1) % scan_stack.length
231
+ advance_left tokens[left], size[left]
232
+ end
233
+
234
+ # Updates the size buffer taking into
235
+ # account the length of the current group.
236
+ #
237
+ # @note Called CheckStack as well in the original paper.
238
+ #
239
+ # @param depth [Integer] depth of the group
240
+ #
241
+ # @return [Nil]
242
+ def check_stack(depth) # rubocop:disable Metrics/AbcSize
243
+ return if scan_stack.empty?
244
+
245
+ x = scan_stack.top
246
+ case tokens[x]
247
+ when Token::Begin
248
+ if depth.positive?
249
+ size[scan_stack.pop] = size[x] + right_total
250
+ check_stack depth - 1
251
+ end
252
+ when Token::End
253
+ size[scan_stack.pop] = 1
254
+ check_stack depth + 1
255
+ else
256
+ size[scan_stack.pop] = size[x] + right_total
257
+ if depth.positive?
258
+ check_stack depth
259
+ end
260
+ end
261
+ end
262
+ end
263
+ end
@@ -0,0 +1,111 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Oppen.
4
+ module Oppen
5
+ # A fixed-size stack that can be popped from top and bottom.
6
+ class ScanStack
7
+ def initialize(size)
8
+ @bottom = 0
9
+ @empty = true
10
+ @stack = Array.new(size)
11
+ @top = 0
12
+ end
13
+
14
+ # @return [Boolean]
15
+ def empty?
16
+ @empty
17
+ end
18
+
19
+ # @return [Integer]
20
+ def length
21
+ @stack.length
22
+ end
23
+
24
+ # @return [Object]
25
+ def top
26
+ if empty?
27
+ raise 'Accessing empty stack from top'
28
+ end
29
+
30
+ @stack[@top]
31
+ end
32
+
33
+ # @return [Object]
34
+ def bottom
35
+ if empty?
36
+ raise 'Accessing empty stack from bottom'
37
+ end
38
+
39
+ @stack[@bottom]
40
+ end
41
+
42
+ # Increment index (no overflow).
43
+ #
44
+ # @param index [Integer]
45
+ #
46
+ # @return [Integer]
47
+ def increment(index)
48
+ (index + 1) % length
49
+ end
50
+
51
+ # Decrement index (no overflow).
52
+ #
53
+ # @param index [Integer]
54
+ #
55
+ # @return [Integer]
56
+ def decrement(index)
57
+ (index - 1) % length
58
+ end
59
+
60
+ # Push a value to the top.
61
+ #
62
+ # @param value [Object]
63
+ #
64
+ # @return [Nil]
65
+ def push(value)
66
+ if empty?
67
+ @empty = false
68
+ else
69
+ @top = increment(@top)
70
+ if @top == @bottom
71
+ raise 'Stack full'
72
+ end
73
+ end
74
+ @stack[@top] = value
75
+ end
76
+
77
+ # Pop a value from the top.
78
+ #
79
+ # @return [Nil]
80
+ def pop
81
+ if empty?
82
+ raise 'Popping empty stack from top'
83
+ end
84
+
85
+ res = top
86
+ if @top == @bottom
87
+ @empty = true
88
+ else
89
+ @top = decrement(@top)
90
+ end
91
+ res
92
+ end
93
+
94
+ # Pop a value from the bottom.
95
+ #
96
+ # @return [Nil]
97
+ def pop_bottom
98
+ if empty?
99
+ raise 'Popping empty stack from bottom'
100
+ end
101
+
102
+ res = bottom
103
+ if @top == @bottom
104
+ @empty = true
105
+ else
106
+ @bottom = increment(@bottom)
107
+ end
108
+ res
109
+ end
110
+ end
111
+ end
@@ -0,0 +1,79 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Oppen.
4
+ module Oppen
5
+ # Token.
6
+ module Token
7
+ # BreakType.
8
+ #
9
+ # FITS => No break is needed (the block fits on the line).
10
+ # INCONSISTENT => New line will be forced only if necessary.
11
+ # CONSISTENT => Each subblock of the block will be placed on a new line.
12
+ module BreakType
13
+ # @return [Integer]
14
+ FITS = 0
15
+ # @return [Integer]
16
+ INCONSISTENT = 1
17
+ # @return [Integer]
18
+ CONSISTENT = 2
19
+ end
20
+
21
+ # String Token.
22
+ class String
23
+ # @return [String] String value.
24
+ attr_reader :value
25
+
26
+ def initialize(value)
27
+ @value = value
28
+ end
29
+
30
+ # @return [Integer]
31
+ def length
32
+ value.length
33
+ end
34
+ end
35
+
36
+ # Break Token.
37
+ class Break
38
+ # @return [Integer] Number of blank spaces.
39
+ attr_reader :blank_space
40
+ # @return [Integer] Indentation.
41
+ attr_reader :offset
42
+
43
+ def initialize(blank_space: 1, offset: 0)
44
+ @blank_space = blank_space
45
+ @offset = offset
46
+ end
47
+ end
48
+
49
+ # Distinguished instance of Break which forces a line break.
50
+ class LineBreak < Break
51
+ def initialize(offset: 0)
52
+ super(blank_space: 9999, offset:)
53
+ end
54
+ end
55
+
56
+ # Begin Token.
57
+ class Begin
58
+ # @return [BreakType]
59
+ attr_reader :break_type
60
+ # @return [Integer] Indentation.
61
+ attr_reader :offset
62
+
63
+ def initialize(break_type: BreakType::INCONSISTENT, offset: 2)
64
+ @offset = offset
65
+ @break_type = break_type
66
+ end
67
+ end
68
+
69
+ # End Token
70
+ class End
71
+ nil
72
+ end
73
+
74
+ # EOF Token
75
+ class EOF
76
+ nil
77
+ end
78
+ end
79
+ end
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Oppen.
4
+ module Oppen
5
+ # Oppen version
6
+ #
7
+ # @return [String] current version
8
+ VERSION = '0.1.0' # managed by release.sh
9
+ end
data/lib/oppen.rb ADDED
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'oppen/printer'
4
+ require_relative 'oppen/print_stack'
5
+ require_relative 'oppen/scan_stack'
6
+ require_relative 'oppen/token'
7
+ require_relative 'oppen/version'
8
+
9
+ # Oppen.
10
+ module Oppen
11
+ # Entry point of the pretty printer.
12
+ #
13
+ # @param tokens [Array[Token]] the list of tokens to be printed
14
+ # @param margin [Integer] maximum line width desired
15
+ # @param new_line [String] the delimiter between lines
16
+ #
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
20
+ tokens.each do |token|
21
+ printer.print token
22
+ end
23
+ printer.output
24
+ end
25
+ end
metadata ADDED
@@ -0,0 +1,54 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: oppen
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Amine Mike El Maalouf <amine.el-maalouf@epita.fr>
8
+ - Firas al-Khalil <firasalkhalil@gmail.com>
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2024-09-13 00:00:00.000000000 Z
13
+ dependencies: []
14
+ description: Implementation of the Oppen's pretty printing algorithm
15
+ email:
16
+ executables:
17
+ - main.rb
18
+ extensions: []
19
+ extra_rdoc_files: []
20
+ files:
21
+ - LICENSE
22
+ - README.md
23
+ - bin/main.rb
24
+ - bin/repl.rb
25
+ - lib/oppen.rb
26
+ - lib/oppen/print_stack.rb
27
+ - lib/oppen/printer.rb
28
+ - lib/oppen/scan_stack.rb
29
+ - lib/oppen/token.rb
30
+ - lib/oppen/version.rb
31
+ homepage: http://github.com/Faveod/oppen-ruby
32
+ licenses:
33
+ - MIT
34
+ metadata: {}
35
+ post_install_message:
36
+ rdoc_options: []
37
+ require_paths:
38
+ - lib
39
+ required_ruby_version: !ruby/object:Gem::Requirement
40
+ requirements:
41
+ - - "~>"
42
+ - !ruby/object:Gem::Version
43
+ version: '3.2'
44
+ required_rubygems_version: !ruby/object:Gem::Requirement
45
+ requirements:
46
+ - - ">="
47
+ - !ruby/object:Gem::Version
48
+ version: '0'
49
+ requirements: []
50
+ rubygems_version: 3.4.19
51
+ signing_key:
52
+ specification_version: 4
53
+ summary: Pretty-printing library
54
+ test_files: []