require 'stringio'
require_relative 'scan_stack'
require_relative 'print_stack'
require_relative 'mixins'
module Oppen
class Printer
extend Mixins
attr_reader :config
attr_reader :left
attr_reader :left_total
attr_reader :print_stack
attr_reader :right
attr_reader :right_total
attr_reader :scan_stack
attr_reader :size
attr_reader :tokens
def initialize(width, new_line, config = Config.oppen,
space = ' ', out = StringIO.new)
n = 3 * width
@config = config
@last_whitespaces_width = 0
@left = 0
@left_total = 1
@print_stack = PrintStack.new width, new_line, config, space, out
@right = 0
@right_total = 1
@scan_stack = ScanStack.new n, config
@size = Array.new n
@tokens = Array.new n
end
def output
print_stack.output
end
def print(token)
case token
in Token::EOF
handle_eof
in Token::Begin
handle_begin token
in Token::End
handle_end token
in Token::Break
handle_break token
in Token::Whitespace
@last_whitespaces_width += token.width
handle_string token
in Token::String
@last_whitespaces_width = 0
handle_string token
end
end
def handle_eof
if !scan_stack.empty?
check_stack 0
advance_left tokens[left], size[left]
end
print_stack.indent 0
end
def handle_begin(token)
if scan_stack.empty?
@left = 0
@left_total = 1
@right = 0
@right_total = 1
@tokens[-1] = nil
else
advance_right
end
tokens[right] = token
size[right] = -right_total
scan_stack.push right
end
def handle_end(token)
if scan_stack.empty?
print_stack.print token, 0
else
advance_right
tokens[right] = token
size[right] = -1
scan_stack.push right
if config&.eager_print? &&
(!scan_stack.empty? && right_total - left_total < print_stack.space)
check_stack 0
advance_left tokens[left], size[left]
end
end
end
def handle_break(token)
if scan_stack.empty?
@left = 0
@left_total = 1
@right = 0
@right_total = 1
tokens[-1] = nil
print_stack.erase @last_whitespaces_width
@last_whitespaces_width = 0
else
advance_right
end
check_stack 0
scan_stack.push right
tokens[right] = token
size[right] = -right_total
@right_total += token.width
end
def handle_string(token)
if scan_stack.empty?
print_stack.print token, token.width
else
advance_right
tokens[right] = token
size[right] = token.width
@right_total += token.width
check_stream if @last_whitespaces_width.zero?
end
end
def check_stream
return if right_total - left_total <= print_stack.space
if !scan_stack.empty? && left == scan_stack.bottom
size[scan_stack.pop_bottom] = Float::INFINITY
end
advance_left tokens[left], size[left]
return if left == right
check_stream
end
def advance_right
@right = (right + 1) % @size.length
return if right != left
raise 'Token queue full' if !config.upsize_stack?
@scan_stack.update_indexes(-@left)
@size, _left, _right = ScanStack.upsize_circular_array(@size, @left)
@tokens, @left, @right = ScanStack.upsize_circular_array(@tokens, @left)
end
def advance_left(token, token_width)
return if token_width.negative?
trim_on_break =
if token.is_a?(Token::Break)
idx = (left - 1) % tokens.length
while idx != right && tokens[idx] && !tokens[idx].is_a?(Token::String) \
&& !tokens[idx].is_a?(Token::Break)
idx = (idx - 1) % tokens.length
end
total = 0
while tokens[idx].is_a?(Token::Whitespace)
total += tokens[idx].width
idx = (idx - 1) % tokens.length
end
@last_whitespaces_width = 0
total
end
trim_on_break ||= 0
print_stack.print(token, token_width, trim_on_break:)
case token
when Token::Break
@left_total += token.width
when Token::String
@left_total += token_width
end
return if left == right
@left = (left + 1) % tokens.length
advance_left tokens[left], size[left]
end
def check_stack(depth)
return if scan_stack.empty?
x = scan_stack.top
case tokens[x]
when Token::Begin
if depth.positive?
size[scan_stack.pop] = size[x] + right_total
check_stack depth - 1
end
when Token::End
size[scan_stack.pop] = 1
check_stack depth + 1
else
size[scan_stack.pop] = size[x] + right_total
if depth.positive?
check_stack depth
end
end
end
end
end