require 'stringio'
require_relative 'scan_stack'
require_relative 'print_stack'
require_relative 'utils'
module Oppen
class Printer
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
@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::String
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
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
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
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) % scan_stack.length
return if right != left
raise 'Token queue full' if !config.upsize_stack?
@size, = Utils.upsize_circular_array(@size, @left)
@tokens, @left, @right = Utils.upsize_circular_array(@tokens, @left)
end
def advance_left(token, token_width)
return if token_width.negative?
print_stack.print token, token_width
case token
when Token::Break
@left_total += token.width
when Token::String
@left_total += token_width
end
return if left == right
@left = (left + 1) % scan_stack.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