class IDL::Scanner

def next_token

def next_token
  sign = nil
  str = '' #initialize empty string
  while true
    ch = @in.getc
    if ch.nil?
      if @ifdef.size>0 and !in_expansion?
        parse_error 'mismatched #if/#endif'
      end
      if more_source?
        leave_source
        next
      else
        return [false, nil]
      end
    end
    if WHITESPACE.include? ch
      @in.skipwhile {|c| WHITESPACE.include?(c) }
      next
    end
    if str.empty? && ch == ?\#
      parse_directive
      next
    end
    unless do_parse?
      skipline
      next
    end
    str << ch
    case
    when BREAKCHARS.include?(ch)
      if SHIFTCHARS.include?(ch) && @in.lookc == ch
        # '<<' or '>>'
        str << @in.getc
      end
      return [str, str]
    when ch == ANNOTATION
      if @in_annotation
        return [str, str]
      else
        # return token returned by parse_annotation or parse next token recursively
        return parse_annotation || next_token
      end
    when ch == ?: #
      if @in.lookc == ?: #
        @in.skipc
        return %w(:: ::)
      else
        return %w(: :)
      end
    when ch == ?L
      _nxtc = @in.lookc
      if _nxtc == ?\'  #' #single quote, for a character literal.
        @in.skipc # skip 'L'
        _nxtc = @in.lookc
        ret = if _nxtc == ?\\
          @in.skipc
          next_escape_str
        elsif _nxtc == ?\' #'
          [ nil, nil ]
        else
          [ :char, '' << @in.getc ]
        end
        if @in.lookc != ?\' #'
          parse_error "wide character literal must be single wide character enclosed in \"'\""
        end
        @in.skipc
        return [ :wide_character_literal, ret ]
      elsif _nxtc == ?\" #" #double quote, for a string literal.
        ret = []
        chs = ''
        @in.skipc # skip 'L'
        while true
          _nxtc = @in.lookc
          if _nxtc == ?\\
            @in.skipc
            ret << [:char, chs] unless chs.empty?
            chs = ''
            ret << next_escape_str
          elsif _nxtc == ?\" #"
            @in.skipc
            ret << [:char, chs] unless chs.empty?
            return [ :wide_string_literal, ret ]
          else
            chs << @in.getc
          end
        end
      else
        return next_identifier(ch)
      end
    when IDCHARS.include?(ch)
      return next_identifier(ch)
    when ch == ?/ #
      _nxtc = @in.lookc
      if _nxtc == ?*
        # skip comment like a `/* ... */'
        @in.skipc # forward stream beyond `/*'
        ch1 = nil
        @in.skipuntil { |ch_|
          ch0 = ch1; ch1 = ch_
          ch0 == ?* and ch1 == ?/ #
        }
        if @in.lookc.nil?
          parse_error "cannot find comment closing brace (\'*/\'). "
        end
        @in.skipc
        str = '' # reset
        next
      elsif _nxtc == ?/
        # skip comment like a `// ...\n'
        @in.skipc
        unless @scan_comment  # scan_comment will be true when parsing commented annotations
          _nxtc = @in.lookc
          if _nxtc == ANNOTATION
            @in.skipc
            # return token returned by parse_annotation or parse next token recursively
            return parse_annotation(true) || next_token
          else
            @in.skipuntil {|c| LFCR.include?(c) }
          end
        end
        str = '' # reset
        next
      else
        return %w(/ /)
      end
    when SIGNS.include?(ch)
      _nxtc = @in.lookc
      if DIGITS.include? _nxtc
        sign = ch
        str = '' # reset
        next
      else
        return [str, str]
      end
    when (?1..?9).include?(ch)
      @in.mark(sign, ch)
      @in.skipwhile {|c| DIGITS.include?(c) }
      num_type = ([?., ?e, ?E, ?d, ?D].include?(@in.lookc)) ? skipfloat_or_fixed : :integer_literal
      r = @in.getregion
      if num_type == :floating_pt_literal
        return [:floating_pt_literal, r.to_f]
      elsif num_type == :fixed_pt_literal
        return [:fixed_pt_literal, r]
      else
        return [:integer_literal, r.to_i]
      end
    when ch == DOT #
      @in.mark(ch)
      @in.skipwhile {|c| DIGITS.include?(c) }
      num_type = (DOT != @in.lookc) ? skipfloat_or_fixed : nil
      s = @in.getregion
      if s == '.'
        parse_error 'token consisting of single dot (.) is invalid.'
      end
      if num_type == :floating_pt_literal
        return [:floating_pt_literal, s.to_f]
      elsif num_type == :fixed_pt_literal
        return [:fixed_pt_literal, s]
      else
        parse_error 'invalid floating point constant.'
      end
    when ch == ?0
      @in.mark(sign, ch)
      _nxtc = @in.lookc
      if _nxtc == ?x || _nxtc == ?X
        @in.skipc
        @in.skipwhile { |ch_| HEXCHARS.include? ch_ }
        s = @in.getregion
        return [:integer_literal, s.hex]
      else
        dec = false
        @in.skipwhile {|c| OCTALS.include?(c) }
        if (?8..?9).include? @in.lookc
          dec = TRUE
          @in.skipwhile {|c| DIGITS.include?(c) }
        end
        num_type = ([?., ?e, ?E, ?d, ?D].include?(@in.lookc)) ? skipfloat_or_fixed : :integer_literal
        s = @in.getregion
        ret = if num_type == :floating_pt_literal
          [:floating_pt_literal, s.to_f]
        elsif num_type == :fixed_pt_literal
          [:fixed_pt_literal, s]
        elsif dec
          parse_error "decimal literal starting with '0' should be octal ('0'..'7' only): #{s}"
        else
          [:integer_literal, s.oct]
        end
        return ret
      end
    when ch == ?\'  #' #single quote, for a character literal.
      _nxtc = @in.lookc
      ret = if _nxtc == ?\\
        @in.skipc
        next_escape
      elsif _nxtc == ?\' #'
        0
      elsif _nxtc
        ('' << @in.getc).unpack('C').first
      end
      if @in.lookc != ?\' #'
        parse_error "character literal must be single character enclosed in \"'\""
      end
      @in.skipc
      return [ :character_literal, ret ]
    when ch == ?\" #" #double quote, for a string literal.
      ret = ''
      while true
        _nxtc = @in.lookc
        if _nxtc == ?\\
          @in.skipc
          ret << next_escape
        elsif _nxtc == ?\" #"
          @in.skipc
          return [ :string_literal, ret ]
        elsif _nxtc
          ret << @in.getc
        else
          parse_error 'unterminated string literal'
        end
      end
    else
      parse_error 'illegal character [' << ch << ']'
    end #of case
  end #of while
  parse_error 'unexcepted error'
end #of method next_token