# getsym --- get a symbol from the input

   subroutine getsym

   include "c1_com.r.i"

   untyped info (IDSIZE)
   integer lookup

   Symbol = Nsymbol
   Symptr = Nsymptr
   Symlen = Nsymlen
   Symline = Nsymline
   if (Nsymlen > 0)
      call scopy (Nsymtext, 1, Symtext, 1)
   else
      Symtext (1) = EOS

   repeat {
      call gettok

      if (Nsymbol == '#'c) {
         call c_preprocessor
         next
         }

      if (Nsymbol == IDSYM && lookup (Nsymtext, info, Keywd_tbl) == YES)
         if (IDTYPE (info) == DEFIDTYPE) {
            call invoke_macro (info)
            next
            }
         else if (IDTYPE (info) == KEYWDIDTYPE)
            Nsymbol = IDPTR (info)
         else
            FATAL ("Undefined IDTYPE in Nsymbol"p)

      break
      }

   DBG (1, call display_symbol (" Symbol='*s'"s, Symbol)
   DB call print (ERROUT, " ^*i '*s'(*i)*n"p, Symptr, Symtext, Symlen))
   return
   end



# gettok --- get next token from input stream

   subroutine gettok

   include "c1_com.r.i"

   integer i
   integer ctoi, gctoi, index, mapdn
   longint val
   longint gctol
   character c

   procedure analyze_number forward
   procedure check_becomes_op (c, sym) forward
   procedure check_double_op (c, sym1, sym2) forward

   Nsymptr = LAMBDA

   repeat {    # until a symbol is found
      Nsymlen = 0
      Nsymtext (1) = EOS
      Nsymline = Line_number (Level)

      repeat
         ngetch (c)
         until (c ~= ' 'c && c ~= HT)

      select (c)
      when (SET_OF_LETTERS, '_'c, '$'c) {

         while (IS_LETTER (c) || IS_DIGIT (c) || c == '$'c || c == '_'c) {
            Nsymlen += 1
            Nsymtext (Nsymlen) = c
            ngetch (c)
            }
         call putback (c)
         Nsymtext (Nsymlen + 1) = EOS

         if (ARG_PRESENT (m))
            call mapstr (Nsymtext, LOWER)

         Nsymbol = IDSYM
         break

         }  # end of identifier processing

      when (SET_OF_DIGITS) {                       # Number

         analyze_number
         break

         }  # end of number processing

      when ('.'c) {                                # Number  or  .

         ngetch (c)
         if (IS_DIGIT (c)) {
            call putback (c)
            c = '.'c
            analyze_number
            }
         else {
            call putback (c)
            Nsymbol = '.'c
            }
         break
         }

      when ('"'c, "'"c) {                          # quoted strings
         if (c == '"'c)
            Nsymbol = STRLITSYM
         else
            Nsymbol = CHARLITSYM

         call collect_quoted_string (c, Nsymtext, Nsymlen)

         break
         }

      when ('>'c) {              # >  ><  >=  >>  >>=
         ngetch (c)
         if (c == '<'c)
            Nsymbol = NESYM
         else if (c == '='c)
            Nsymbol = GESYM
         else if (c == '>'c) {
            ngetch (c)
            if (c == '='c)
               Nsymbol = RSHIFTAASYM
            else {
               Nsymbol = RSHIFTSYM
               call putback (c)
               }
            }
         else {
            Nsymbol = '>'c
            call putback (c)
            }
         break
         }

      when ('<'c) {              # <  <>  <=  <<  <<=
         ngetch (c)
         if (c == '>'c)
            Nsymbol = NESYM
         else if (c == '='c)
            Nsymbol = LESYM
         else if (c == '<'c) {
            ngetch (c)
            if (c == '='c)
               Nsymbol = LSHIFTAASYM
            else {
               Nsymbol = LSHIFTSYM
               call putback (c)
               }
            }
         else {
            Nsymbol = '<'c
            call putback (c)
            }
         break
         }

      when ('='c) {              # =  ==
         check_becomes_op ('='c, EQSYM)
         break
         }

      when ('!'c) {              # !  !=
         check_becomes_op ('!'c, NESYM)
         break
         }

      when ('/'c) {              # /* */  /  /=
         ngetch (c)
         if (c == '*'c) {
            ngetch (c)
            repeat {
               while (c ~= '*'c && c ~= EOF)
                  ngetch (c)
               if (c == EOF) {
                  SYNERR ("Missing trailing comment delimiter"p)
                  break
                  }
               ngetch (c)
               } until (c == '/'c)
            next
            }
         call putback (c)
         check_becomes_op ('/'c, DIVAASYM)
         break
         }

      when ('-'c) {              # ->  -  --  -=
         ngetch (c)
         if (c == '>'c)
            Nsymbol = POINTSTOSYM
         else {
            call putback (c)
            check_double_op ('-'c, DECSYM, SUBAASYM)
            }
         break
         }

      when ('+'c) {              # +  ++  +=
         check_double_op ('+'c, INCSYM, ADDAASYM)
         break
         }

      when ('&'c) {              # &  &&  &=
         check_double_op ('&'c, SANDSYM, ANDAASYM)
         break
         }

      when ('|'c) {              # |  ||  |=
         check_double_op ('|'c, SORSYM, ORAASYM)
         break
         }

      when ('*'c) {              # *  *=
         check_becomes_op ('*'c, MULAASYM)
         break
         }

      when ('%'c) {              # %  %=
         check_becomes_op ('%'c, REMAASYM)
         break
         }

      when ('^'c) {              # ^  ^=
         check_becomes_op ('^'c, XORAASYM)
         break
         }

      when (NEWLINE)
         next

      else {   # single_character symbol

#         if (c == '\'c) { # check if '\' at end of line
#            ngetch (c)
#            if (c == NEWLINE)
#               next
#            call putback (c)
#            c = '\'c
#            }

         Nsymbol = c
         Nsymtext (1) = c
         Nsymtext (2) = EOS
         Nsymlen = 1
         break
         }

      }  # repeat until a symbol is found

   DBG (2, call display_symbol ("Nsymbol='*s'"s, Nsymbol)
   DB call print (ERROUT, " ^*i '*s'(*i)*n"p, Nsymptr, Nsymtext, Nsymlen))

   return


   # collect_integer --- collect the digits of an integer

      procedure collect_integer (radix) {

      integer radix
      local x; integer x

      repeat {
         if (radix <= 10)
            x = c - '0'c + 1
         else
            x = index ("0123456789abcdef"s, mapdn (c))
         if (1 > x || x > radix)
            break
         Nsymtext (Nsymlen + 1) = c   # Strange order for efficiency
         Nsymlen += 1
         ngetch (c)
         }
      Nsymtext (Nsymlen + 1) = EOS
      }


   # convert_integer --- convert an integer symbol to the specified radix

      procedure convert_integer (radix) {

      integer radix
      local i; integer i

      i = 1
      val = gctol (Nsymtext, i, radix)
      if (Nsymtext (i) ~= EOS)
         SYNERR ("Illegal character in integer constant"p)
      }


   # return_integer --- convert 'val' back to characters and determine
   #                    data type

      procedure return_integer (radix) {

      integer radix

      if (c == 'l'c || c == 'L'c) {
         Nsymbol = LONGLITSYM
         ngetch (c)
         }
      else if (c == 's'c || c == 'L'c) {
         Nsymbol = SHORTLITSYM
         ngetch (c)
         }
      else if (radix == 10)
         if (val > MAXSHORT)
            Nsymbol = LONGLITSYM
         else
            Nsymbol = SHORTLITSYM
      else
         if (val > MAXUNSIGNED)
            Nsymbol = LONGLITSYM
         else
            Nsymbol = SHORTLITSYM

      call ltoc (val, Nsymtext, MAXTOK)

      }



   # analyze_number --- collect an integer/real number

      procedure analyze_number {

      collect_integer (10)

      select (c)
         when ('r'c, 'R'c) {                    # radix specified
            convert_integer (10)
            if (val < 2 | val > 16) {
               SYNERR ("Radix must be between 2 and 16"p)
               val = 16
               }
            ngetch (c)
            Nsymlen = 0
            collect_integer (val)
            convert_integer (val)
            return_integer (16)
            }
         when ('x'c, 'X'c) {
            if (Nsymtext (1) == '0'c && Nsymtext (2) == EOS) {
               ngetch (c)
               Nsymlen = 0
               collect_integer (16)
               convert_integer (16)
               }
            else {
               SYNERR ("Illegal hexadecimal constant"p)
               convert_integer (10)
               }
            return_integer (16)
            }
         when ('.'c, 'e'c, 'E'c) {
            if (c == '.'c) {
               Nsymtext (Nsymlen + 1) = '.'c
               Nsymlen += 1
               ngetch (c)
               collect_integer (10)
               }
            if (c == 'e'c || c == 'E'c) {
               Nsymtext (Nsymlen + 1) = 'e'c
               Nsymlen += 1
               ngetch (c)
               if (c == '-'c || c == '+'c) {
                  Nsymtext (Nsymlen + 1) = c
                  Nsymlen += 1
                  ngetch (c)
                  }
               collect_integer (10)
               }
            Nsymbol = DOUBLELITSYM
            }
         else
            if (Nsymtext (1) == '0'c) {
               convert_integer (8)
               return_integer (8)
               }
            else {
               convert_integer (10)
               return_integer (10)
               }

      call putback (c)
      }


   # check_becomes_op --- check for a single-character OP or OP=

      procedure check_becomes_op (c, sym) {

      character c
      integer sym
      local d; character d

      ngetch (d)
      if (d == '='c)
         Nsymbol = sym
      else {
         call putback (d)
         Nsymbol = c
         }
      }


   # check_double_op --- check for a single character OP, OPOP, or OP=

      procedure check_double_op (c, sym1, sym2) {

      character c
      integer sym1, sym2

      local d; character d

      ngetch (d)
      if (d == c)
         Nsymbol = sym1
      else if (d == '='c)
         Nsymbol = sym2
      else {
         Nsymbol = c
         call putback (d)
         }
      }


   end



# refill_buffer --- refill the input buffer and return first character

   subroutine refill_buffer (c)
   character c

   include "c1_com.r.i"

   integer getlin

   repeat {
      if (Level < 1) {
         c = EOF
         Inbuf (PBLIMIT) = EOS
         Ibp = PBLIMIT
         return
         }
      if (getlin (Inbuf (PBLIMIT), Infile (Level)) ~= EOF) {
         Line_number (Level) = Line_number (Level) + 1
         DBG (17, call print (ERROUT, "....*s"s, Inbuf (PBLIMIT)))
         break
         }
      call close (Infile (Level))
      Level = Level - 1
      if (Level >= 1) { # restore the name of the module that  #
                        # included the one we just finished    #

         call scopy (Mem, Fname_table (Level), Module_name, 1)
         call dsfree (Fname_table (Level))

                        # and reset the line number to prevent #
                        # screwy error message numbering       #
         Symline = Line_number (Level)
         }
      }
   c = Inbuf (PBLIMIT)
   Ibp = PBLIMIT + 1

   return
   end




# putback --- push character back onto input

   subroutine putback (c)
   character c

   include "c1_com.r.i"

   Ibp = Ibp - 1
   if (Ibp >= 1)
      Inbuf (Ibp) = c
   else
      FATAL ("too many characters pushed back"p)

   return
   end



# putback_str --- push string back onto input

   subroutine putback_str (str)
   character str (ARB)

   include "c1_com.r.i"

   integer i
   integer length

   for (i = length (str); i > 0; i = i - 1)
      call putback (str (i))

   return
   end



# putback_num --- push decimal number back onto input

   subroutine putback_num (n)
   integer n

   integer len
   integer itoc

   character chars (MAXLINE)

   len = itoc (n, chars, MAXLINE)
   chars (len + 1) = EOS
   call putback_str (chars)

   return
   end



# collect_quoted_string --- obtain a quoted string from input

   subroutine collect_quoted_string (quote, text, tl)
   character quote, text (ARB)
   integer tl

   include "c1_com.r.i"

   integer gctoi
   character c

   procedure analyze_escape forward

   ngetch (c)
   while (tl < MAXTOK && c ~= NEWLINE && c ~= quote) {
      if (c == '\'c)
         analyze_escape
      else {
         text (tl + 1) = c
         tl += 1
         }
      ngetch (c)
      }
   if (c == NEWLINE)
      SYNERR ("Missing closing quote"p)
   else if (tl >= MAXTOK)
      SYNERR ("Quoted literal too long"p)
   text (tl + 1) = EOS

   return


   # analyze_escape --- convert an escaped character in a quoted string

      procedure analyze_escape {

      local i; integer i
      local str; character str (5)

      ngetch (c)
      select (c)
         when (NEWLINE)
            c = EOF
         when ('n'c)
            c = NEWLINE
         when ('t'c)
            c = HT
         when ('b'c)
            c = BS
         when ('r'c)
            c = CR
         when ('f'c)
            c = FF
         when ('0'c, '1'c, '2'c, '3'c, '4'c, '5'c, '6'c, '7'c) {
            for (i = 1; i <= 3 && '0'c <= c && c <= '7'c; i += 1) {
               str (i) = c
               ngetch (c)
               }
            str (i) = EOS
            call putback (c)
            i = 1
            c = gctoi (str, i, 8)
            }

      if (c ~= EOF) {
         text (tl + 1) = c
         tl += 1
         }
      }

   end
