$warnings = {}

def warn(msg, from=nil)
  warning = to_location(from) + msg

  # don't do the same warning more than once (since code is parsed multiple times to handle auto input cases in engine/hs code gen
  return if $warnings[warning]
  $warnings[warning] = true

  error_output "WARNING: ".red + warning + "\n"
end

def draw_fns(tokens, nodes)
  positions = []
  code = ""
  tokens.each.with_index{|token|
    positions << (code.size + token.str.size/2)
    code << token.str + " "
  }

  diagram = code.size.times.map{ " "*code.size }
  token_pos = -> t { positions[tokens.index{|token| token.equal?(t) }] }
  nodes.each{|node|
    next if !node.token
    op = lookup_op(node.token.str)
    pos = token_pos[node.token]
    if node.type == :UnmatchedEndBracket || op && op.block && !node.impl
      diagram[0][token_pos[node.token]] = "?"
    elsif op && op.block
      if node.impl
        end_pos = token_pos[nodes[node.impl].token]
        ((end_pos - pos) / 2).times{|i|
          diagram[i][pos+i] = "\\"
          diagram[i][end_pos-i-1] = "/"
        }
      end
    end
  }

  code + "\n" + diagram.take_while{|line|line.strip != ""} * "\n"
end

def to_location(from)
  token = case from
    when Token
      from
    when AST
      from.token
    when IR
      from.token
    when NilClass
      nil
    else
      raise "unknown location type %p " % from
    end

  if token
    "%s:%s (%s) " % [token.line_no||"?", token.char_no||"?", token.str]
  else
    ""
  end
end

class String
  def red
    "\e[31m#{self}\e[0m"
  end
end

class IogiiError < StandardError
  def initialize(message,from=nil)
    @message = message
    @from = from
  end
  def message
    "ERROR: ".red + to_location(@from) + @message
  end
end
