oppen 0.1.0

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