class IDL::Scanner

def next_identifier(first = nil)

def next_identifier(first = nil)
  @in.mark(first)
  while true
    if FULL_IDCHARS.include?(@in.lookc)
      @in.skipc
    else
      break
    end
  end
  s0 = @in.getregion        # raw IDL id
  s1 = s0.downcase.to_sym   # downcased symbolized
  s2 = s0.dup               # (to be) unescaped id
  # simple check
  if s2.length == 0
    parse_error 'identifier expected!'
  else
    if s2[0] == ?_
      s2.slice!(0) ## if starts with CORBA IDL escape => remove
    else
      parse_error "identifier must begin with alphabet character: #{s2}" unless ALPHA_LC.include?(s2[0]) || ALPHA_UC.include?(s2[0])
    end
  end
  # preprocessor check
  if @defined.has_key?(s2) and !is_expanded?(s2)
    # enter expansion as new source
    enter_expansion(@defined[s2], s2)
    # call next_token to parse expanded source
    next_token
  # keyword check
  elsif @in_annotation
    if BOOL_LITERALS.has_key?(s1)
      [ :boolean_literal, BOOL_LITERALS[s1] ]
    else
      [ :identifier, Identifier.new(s2, s2, s0) ]
    end
  elsif (a = KEYWORDS.assoc(s1)).nil?
    # check for language mapping keyword
    [ :identifier, Identifier.new(s2, chk_identifier(s2), s0) ]
  elsif s0 == a[1]
    [ a[1], nil ]
  else
    parse_error "'#{s0}' collides with IDL keyword '#{a[1]}'"
  end
end