home *** CD-ROM | disk | FTP | other *** search
/ rtsi.com / 2014.01.www.rtsi.com.tar / www.rtsi.com / OS9 / OSK / APPS / lout2.lzh / LOUT2 / z02.c < prev    next >
Text File  |  1994-01-23  |  22KB  |  574 lines

  1. /*@z02.c:Lexical Analyser:Declarations@***************************************/
  2. /*                                                                           */
  3. /*  LOUT: A HIGH-LEVEL LANGUAGE FOR DOCUMENT FORMATTING (VERSION 2.05)       */
  4. /*  COPYRIGHT (C) 1993 Jeffrey H. Kingston                                   */
  5. /*                                                                           */
  6. /*  Jeffrey H. Kingston (jeff@cs.su.oz.au)                                   */
  7. /*  Basser Department of Computer Science                                    */
  8. /*  The University of Sydney 2006                                            */
  9. /*  AUSTRALIA                                                                */
  10. /*                                                                           */
  11. /*  This program is free software; you can redistribute it and/or modify     */
  12. /*  it under the terms of the GNU General Public License as published by     */
  13. /*  the Free Software Foundation; either version 1, or (at your option)      */
  14. /*  any later version.                                                       */
  15. /*                                                                           */
  16. /*  This program is distributed in the hope that it will be useful,          */
  17. /*  but WITHOUT ANY WARRANTY; without even the implied warranty of           */
  18. /*  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the            */
  19. /*  GNU General Public License for more details.                             */
  20. /*                                                                           */
  21. /*  You should have received a copy of the GNU General Public License        */
  22. /*  along with this program; if not, write to the Free Software              */
  23. /*  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.                */
  24. /*                                                                           */
  25. /*  FILE:         z02.c                                                      */
  26. /*  MODULE:       Lexical Analyser                                           */
  27. /*  EXTERNS:      LexLegalName(), LexInit(), LexPush(), LexPop(),            */
  28. /*                LexNextTokenPos(), LexGetToken()                           */
  29. /*                                                                           */
  30. /*  Implementation note:  this fast and cryptic lexical analyser is adapted  */
  31. /*  from Waite, W. M.: The Cost of Lexical Analysis, in Software - Practice  */
  32. /*  and Experience, v16, pp473-488 (May 1986).                               */
  33. /*                                                                           */
  34. /*****************************************************************************/
  35. #include "externs"
  36. #define    BUFFER_SIZE    8192        /* size of buffer for block read     */
  37. #define    OTHER        0        /* punctuation or other character    */
  38. #define    LETTER        1        /* letter type                       */
  39. #define    QUOTE        2        /* quoted string delimiter type      */
  40. #define    ESCAPE        3        /* escape character inside strings   */
  41. #define    COMMENT        4        /* comment delimiter type            */
  42. #define    CSPACE        5        /* space character type              */
  43. #define    TAB        6        /* tab character type                */
  44. #define    NEWLINE        7        /* newline character type            */
  45. #define    ENDFILE        8        /* end of file character type        */
  46.  
  47. static    unsigned char    chtbl[256];    /* type table indexed by a FULL_CHAR */
  48. static    FULL_CHAR    *chpt;        /* pointer to current text character */
  49. static    FULL_CHAR    *frst;        /* address of first buffer character */
  50. static    FULL_CHAR    *limit;        /* just past last char in buffer     */
  51. static    FULL_CHAR    *buf;        /* the character buffer start pos    */
  52. static    int        blksize;    /* size of block read; others too    */
  53. static    FULL_CHAR    *startline;    /* position in buff of last newline  */
  54. static    FILE_NUM    this_file;    /* number of currently open file     */
  55. static    FILE        *fp;        /* current input file                */
  56. static    FILE_POS    file_pos;    /* current file position             */
  57. static    short        ftype;        /* the type of the current file      */
  58. static    OBJECT        next_token;    /* next token if already read         */
  59. static    int        offset;        /* where to start reading in file    */
  60. static    FULL_CHAR    *mem_block;    /* file buffer                       */
  61.  
  62. static int top_stack;        /* top of lexical analyser stack     */
  63. static struct {
  64.   FULL_CHAR    *chpt;        /* pointer to current text character */
  65.   FULL_CHAR    *frst;        /* address of first buffer character */
  66.   FULL_CHAR    *limit;        /* just past last char in buffer     */
  67.   FULL_CHAR    *buf;        /* the character buffer start pos    */
  68.   int        blksize;    /* size of block read; others too    */
  69.   FULL_CHAR    *startline;    /* position in buff of last newline  */
  70.   FILE_NUM    this_file;    /* number of currently open file     */
  71.   FILE        *fp;        /* current input file                */
  72.   FILE_POS    file_pos;    /* current file position             */
  73.   short        ftype;        /* the type of the current file      */
  74.   OBJECT    next_token;    /* next token if already read         */
  75.   int        offset;        /* where to start reading in file    */
  76.   FULL_CHAR    *mem_block;    /* file buffer                       */
  77. } lex_stack[MAX_LEX_STACK];
  78.  
  79. /*@::LexLegalName(), LexInit()@***********************************************/
  80. /*                                                                           */
  81. /*  BOOLEAN LexLegalName(str)                                                */
  82. /*                                                                           */
  83. /*  Check whether str is a valid name for a symbol table entry.              */
  84. /*  Valid names have the BNF form                                            */
  85. /*                                                                           */
  86. /*       <name> ::= <letter>  { <letter> }                                   */
  87. /*       <name> ::= <special> { <special> }                                  */
  88. /*       <name> ::= <escape>  { <letter> }                                   */
  89. /*                                                                           */
  90. /*  The third form is inaccessible to users and is for internal use only.    */
  91. /*                                                                           */
  92. /*****************************************************************************/
  93.  
  94. BOOLEAN LexLegalName(str)
  95. FULL_CHAR *str;
  96. { int i;  BOOLEAN res;
  97.   debug1(DLA, DDD, "LexLegalName( %s )", str);
  98.   switch( chtbl[str[0]] )
  99.   {
  100.     case ESCAPE:
  101.     case LETTER:
  102.     
  103.       for( i = 1;  chtbl[str[i]] == LETTER;  i++ );
  104.       res = str[i] == '\0';
  105.       break;
  106.  
  107.  
  108.     case OTHER:
  109.     
  110.       for( i = 1;  chtbl[str[i]] == OTHER;  i++ );
  111.       res = str[i] == '\0';
  112.       break;
  113.  
  114.  
  115.     default:
  116.     
  117.       res = FALSE;
  118.       break;
  119.  
  120.   }
  121.   debug1(DLA, DDD, "LexLegalName returning %s", bool(res));
  122.   return res;
  123. } /* end LexLegalName */
  124.  
  125.  
  126. /*****************************************************************************/
  127. /*                                                                           */
  128. /*  LexInit()                                                                */
  129. /*                                                                           */
  130. /*  Initialise character types.  Those not touched are 0 (OTHER).            */
  131. /*  The function initchtbl() assists in initializing the chtbl.              */
  132. /*                                                                           */
  133. /*****************************************************************************/
  134.  
  135. static initchtbl(val, str)
  136. int val;  FULL_CHAR *str;
  137. { int i;
  138.   for( i = 0;  str[i] != '\0';  i++ )
  139.     chtbl[ str[i] ] = val;
  140. } /* end initchtbl */
  141.  
  142. LexInit()
  143. { initchtbl(LETTER,  STR_LETTERS_LOWER);
  144.   initchtbl(LETTER,  STR_LETTERS_UPPER);
  145.   initchtbl(LETTER,  STR_LETTERS_SYMSTART);
  146.   initchtbl(LETTER,  STR_LETTERS_EXTRA0);
  147.   initchtbl(LETTER,  STR_LETTERS_EXTRA1);
  148.   initchtbl(LETTER,  STR_LETTERS_EXTRA2);
  149.   initchtbl(LETTER,  STR_LETTERS_EXTRA3);
  150.   initchtbl(LETTER,  STR_LETTERS_EXTRA4);
  151.   initchtbl(LETTER,  STR_LETTERS_EXTRA5);
  152.   initchtbl(LETTER,  STR_LETTERS_EXTRA6);
  153.   initchtbl(LETTER,  STR_LETTERS_EXTRA7);
  154.   initchtbl(QUOTE,   STR_QUOTE);
  155.   initchtbl(ESCAPE,  STR_ESCAPE);
  156.   initchtbl(COMMENT, STR_COMMENT);
  157.   initchtbl(CSPACE,  STR_SPACE);
  158.   initchtbl(TAB,     STR_TAB);
  159.   initchtbl(NEWLINE, STR_NEWLINE);
  160.   chtbl['\0'] = ENDFILE;
  161. } /* end LexInit */
  162.  
  163. /*@::LexPush(), LexPop()@*****************************************************/
  164. /*                                                                           */
  165. /*  LexPush(x, offs, ftype)                                                  */
  166. /*                                                                           */
  167. /*  Start reading from the file sequence whose first file is x (subsequent   */
  168. /*  files are obtained from NextFile).  The first file (x) is to be fseeked  */
  169. /*  to offs.  When the sequence is done, ftype determines how to continue:   */
  170. /*                                                                           */
  171. /*      ftype          action                                                */
  172. /*                                                                           */
  173. /*      SOURCE_FILE    last input file ends, return @End \Input              */
  174. /*      DATABASE_FILE  database file, return @End \Input                     */
  175. /*      INCLUDE_FILE   include file, must pop lexical analyser and continue  */
  176. /*                                                                           */
  177. /*****************************************************************************/
  178.  
  179. LexPush(x, offs, ftyp)
  180. FILE_NUM x;  int offs;  int ftyp;
  181. { char *malloc();
  182.   debug3(DLA, D, "LexPush(%s, %d, %s)", FileName(x), offs,
  183.     ftyp==SOURCE_FILE ? "source" : ftyp==INCLUDE_FILE ? "include" : "database");
  184.   if( top_stack >= MAX_LEX_STACK - 1 )
  185.     Error(FATAL, PosOfFile(x), "%s or %s file %s too deeply nested",
  186.       KW_INCLUDE, KW_DATABASE, FileName(x));
  187.   if( top_stack >= 0 )  /* save current state */
  188.   { lex_stack[top_stack].chpt        = chpt;
  189.     lex_stack[top_stack].frst        = frst;
  190.     lex_stack[top_stack].limit        = limit;
  191.     lex_stack[top_stack].buf        = buf;
  192.     lex_stack[top_stack].blksize    = blksize;
  193.     lex_stack[top_stack].startline    = startline;
  194.     lex_stack[top_stack].this_file    = this_file;
  195.     lex_stack[top_stack].fp        = fp;
  196.     lex_stack[top_stack].ftype        = ftype;
  197.     lex_stack[top_stack].next_token    = next_token;
  198.     lex_stack[top_stack].offset        = offset;
  199.     lex_stack[top_stack].mem_block    = mem_block;
  200.     FposCopy( lex_stack[top_stack].file_pos, file_pos );
  201.   }
  202.   top_stack += 1;
  203.   mem_block = (FULL_CHAR *) malloc((MAX_LINE+BUFFER_SIZE+2)*sizeof(FULL_CHAR));
  204.   if( mem_block == NULL )  Error(FATAL, PosOfFile(x),
  205.       "run out of memory when opening file %s", FileName(x));
  206.   buf = chpt = &mem_block[MAX_LINE];
  207.   this_file = x;  offset = offs;
  208.   ftype = ftyp;  next_token = nil;
  209.   *chpt = '\0';  fp = null;
  210. } /* end LexPush */
  211.  
  212.  
  213. /*****************************************************************************/
  214. /*                                                                           */
  215. /*  LexPop() - pop lexical analyser.                                         */
  216. /*                                                                           */
  217. /*****************************************************************************/
  218.  
  219. LexPop()
  220. { debug0(DLA, D, "LexPop()");
  221.   assert( top_stack > 0, "LexPop: top_stack <= 0!" );
  222.   if( fp != null )  fclose(fp);
  223.   top_stack--;
  224.   free( (char *) mem_block);
  225.   mem_block    = lex_stack[top_stack].mem_block;
  226.   chpt         = lex_stack[top_stack].chpt;
  227.   frst         = lex_stack[top_stack].frst;
  228.   limit        = lex_stack[top_stack].limit;
  229.   buf          = lex_stack[top_stack].buf;
  230.   blksize      = lex_stack[top_stack].blksize;
  231.   startline    = lex_stack[top_stack].startline;
  232.   this_file    = lex_stack[top_stack].this_file;
  233.   fp           = lex_stack[top_stack].fp;
  234.   ftype        = lex_stack[top_stack].ftype;
  235.   next_token   = lex_stack[top_stack].next_token;
  236.   offset       = lex_stack[top_stack].offset;
  237.   FposCopy( file_pos, lex_stack[top_stack].file_pos );
  238. } /* end LexPop */
  239.  
  240.  
  241. /*@::setword(), LexNextTokenPos(), srcnext()@*********************************/
  242. /*                                                                           */
  243. /*  setword(typ, res, file_pos, str, len)                                    */
  244. /*                                                                           */
  245. /*  Set variable res to a WORD or QWORD token containing string str, etc.    */
  246. /*                                                                           */
  247. /*****************************************************************************/
  248.  
  249. #define setword(typ, res, file_pos, str, len)                \
  250. { res = NewWord(typ, len, &file_pos);                    \
  251.   FposCopy(fpos(res), file_pos);                    \
  252.   for( c = 0;  c < len;  c++ ) string(res)[c] = str[c];            \
  253.   string(res)[c] = '\0';                        \
  254. }
  255.  
  256.  
  257. /*****************************************************************************/
  258. /*                                                                           */
  259. /*  long LexNextTokenPos()                                                   */
  260. /*                                                                           */
  261. /*  Equivalent to ftell() on the (buffered) current lex file.                */
  262. /*                                                                           */
  263. /*****************************************************************************/
  264.  
  265. long LexNextTokenPos()
  266. { long res;
  267.   if( next_token != nil )
  268.     Error(FATAL, &fpos(next_token), "illegal macro invokation in database");
  269.   res = ftell(fp) - (limit - chpt) - (buf - frst);
  270.   debug1(DLA, D, "LexNextTokenPos() returning %ld", res);
  271.   return res;
  272. }
  273.  
  274.  
  275. /*****************************************************************************/
  276. /*                                                                           */
  277. /*  static srcnext()                                                         */
  278. /*                                                                           */
  279. /*  Move to new line of input file.  May need to recharge buffer.            */
  280. /*                                                                           */
  281. /*****************************************************************************/
  282.  
  283. static srcnext()
  284. { register FULL_CHAR *col;
  285.   debug4(DLA, DDD, "srcnext();  buf: %d, chpt: %d, frst: %d, limit: %d",
  286.     buf - mem_block, chpt - mem_block, frst - mem_block, limit - mem_block);
  287.  
  288.   /* if time to transfer last line to area preceding buffer, do so */
  289.   if( blksize != 0 && chpt < limit )
  290.   { debug0(DLA, DDD, "srcnext: transferring.");
  291.     col = buf;
  292.     while( (*--col = *--limit) != CH_NEWLINE );
  293.     frst = col + 1;  limit++;  blksize = 0;
  294.   }
  295.  
  296.   /* if buffer is empty, read next block */
  297.   /*** changed by JK 9/92 from "if( chpt == limit )" to fix long lines bug */
  298.   if( chpt >= limit )
  299.   { if( chpt > limit )
  300.     { col_num(file_pos) = 1;
  301.       Error(FATAL, &file_pos, "line is too long (or final newline missing)");
  302.     }
  303.     chpt = frst;
  304.     blksize = fread( (char *) buf, sizeof(char), BUFFER_SIZE, fp);
  305.     debug4(DLA, D, "srcnext: %d = fread(0x%x, %d, %d, fp)",
  306.       blksize, buf, sizeof(char), BUFFER_SIZE);
  307.     frst = buf;  limit = buf + blksize;  *limit = CH_NEWLINE;
  308.   }
  309.  
  310.   /* if nothing more to read, make this clear */
  311.   if( chpt >= limit )
  312.   { debug0(DLA, DDD, "srcnext: nothing more to read");
  313.     chpt = limit = buf;  *limit = '\0';
  314.   }
  315.   debug4(DLA, DDD, "srcnext returning;  buf: %d, chpt: %d, frst: %d, limit: %d",
  316.     buf - mem_block, chpt - mem_block, frst - mem_block, limit - mem_block);
  317. } /* end srcnext */
  318.  
  319.  
  320. /*@::LexGetToken()@***********************************************************/
  321. /*                                                                           */
  322. /*  OBJECT LexGetToken()                                                     */
  323. /*                                                                           */
  324. /*  Get next token from input.  Look it up in symbol table.                  */
  325. /*                                                                           */
  326. /*****************************************************************************/
  327.  
  328. OBJECT LexGetToken()
  329. {
  330.        FULL_CHAR *startpos;        /* where the latest token started    */
  331.   register FULL_CHAR *p, *q;        /* pointer to current input char     */
  332.   register int      c;            /* temporary character (really char) */
  333.   OBJECT   res;                /* result token                      */
  334.   int vcount, hcount;            /* no. of newlines and spaces seen   */
  335.  
  336.   if( next_token != nil )
  337.   { next_token = Delete(res = next_token, PARENT);
  338.     debug2(DLA, DD, "LexGetToken%s (in macro) returning %s",
  339.       EchoFilePos(&file_pos), EchoToken(res));
  340.     return res;
  341.   }
  342.  
  343.   res = nil;  p = chpt;
  344.   vcount = hcount = 0;
  345.   do switch( chtbl[*p++] )
  346.   {
  347.       case ESCAPE:
  348.       
  349.     col_num(file_pos) = (startpos = p-1) - startline;
  350.     Error(WARN, &file_pos, "character %c outside quoted string", *startpos);
  351.     break;
  352.  
  353.  
  354.       case COMMENT:
  355.       
  356.     debug1(DLA, DDD, "LexGetToken%s: comment", EchoFilePos(&file_pos));
  357.     while( (c = *p++) != CH_NEWLINE && c != '\0' );
  358.     --p;
  359.     break;
  360.  
  361.  
  362.       case CSPACE:
  363.  
  364.     hcount++;
  365.     break;
  366.  
  367.  
  368.       case TAB:
  369.  
  370.     hcount += 8;
  371.     break;
  372.  
  373.  
  374.       case NEWLINE:
  375.       
  376.     chpt = p;  srcnext();
  377.     line_num(file_pos)++;
  378.     col_num(file_pos) = 0;
  379.     vcount++;  hcount = 0;
  380.     startline = (p = chpt) - 1;
  381.     break;
  382.  
  383.  
  384.       case ENDFILE:
  385.       
  386.     /* close current file, if any */
  387.     debug0(DLA, DDD, "LexGetToken: endfile");
  388.     if( fp != null )
  389.     { fclose(fp);  fp = null;
  390.       this_file = ftype == SOURCE_FILE ? NextFile(this_file) : NO_FILE;
  391.     }
  392.  
  393.     /* open next file */
  394.     while( this_file != NO_FILE )
  395.     { file_num(file_pos) = this_file;
  396.       line_num(file_pos) = 1;
  397.       col_num(file_pos) = 0;
  398.       fp = OpenFile(this_file, FALSE, TRUE);
  399.       if( fp != null )  break;
  400.       Error(WARN, &file_pos, "cannot open %s", FileName(this_file));
  401.       this_file = ftype == SOURCE_FILE ? NextFile(this_file) : NO_FILE;
  402.     }
  403.     if( fp != null )
  404.     { if( offset != 0 )
  405.       { fseek(fp, (long) offset, 0);
  406.         offset = 0L;
  407.       }
  408.       frst = limit = chpt = buf;
  409.       blksize = 0;  srcnext();
  410.       startline = (p = chpt) - 1;
  411.       hcount = 0;
  412.     }
  413.  
  414.     /* no next file, so take continuation */
  415.     else switch( ftype )
  416.     {
  417.       case SOURCE_FILE:
  418.       case DATABASE_FILE:
  419.       
  420.         /* input ends with "@End \Input" */
  421.         res = NewToken(END, &file_pos, 0, 0, END_PREC, nil);
  422.         next_token = NewToken(CLOSURE, &file_pos, 0,0, NO_PREC, StartSym);
  423.         --p;  startline = p;
  424.         break;
  425.  
  426.       case INCLUDE_FILE:
  427.  
  428.         LexPop();
  429.         (p = chpt) - 1;
  430.         hcount = 0;
  431.         break;
  432.  
  433.       default:  Error(INTERN, no_fpos, "ftype!");
  434.  
  435.     } /* end switch */
  436.     break;
  437.  
  438.  
  439.       case OTHER:
  440.       
  441.     col_num(file_pos) = (startpos = p-1) - startline;
  442.     while( chtbl[*p++] == OTHER );
  443.     c = p - startpos - 1;
  444.     do
  445.     { res = SearchSym(startpos, c);
  446.       --c; --p;
  447.     } while( c > 0 && res == nil );
  448.     goto MORE;  /* 7 lines down */
  449.     break;
  450.  
  451.  
  452.       case LETTER:
  453.       
  454.     col_num(file_pos) = (startpos = p-1) - startline;
  455.     while( chtbl[*p++] == LETTER );  --p;
  456.     res = SearchSym(startpos, p - startpos);
  457.  
  458.     MORE: if( res == nil )
  459.     { setword(WORD, res, file_pos, startpos, p-startpos);
  460.     }
  461.     else if( type(res) == MACRO )
  462.     { if( recursive(res) )
  463.       { Error(WARN, &file_pos, "recursion in macro");
  464.         setword(WORD, res, file_pos, startpos, p-startpos);
  465.       }
  466.       else
  467.       { res = CopyTokenList( sym_body(res), &file_pos );
  468.         if( res != nil ) next_token = Delete(res, PARENT);
  469.         else hcount = 0;
  470.       }
  471.     }
  472.     else if( predefined(res) == 0 )
  473.     { res = NewToken(CLOSURE, &file_pos, 0, 0, precedence(res), res);
  474.     }
  475.     else if( predefined(res) == INCLUDE || predefined(res) == SYS_INCLUDE )
  476.     { OBJECT t, fname;  FILE_NUM fnum;  int len;
  477.       chpt = p;
  478.       t = LexGetToken();
  479.       if( type(t) != LBR )
  480.       { Error(WARN, &fpos(t), "%s expected after %s", KW_LBR, SymName(res));
  481.         Dispose(t);
  482.         res = nil;
  483.         break;
  484.       }
  485.       fname = Parse(&t, nil, FALSE, FALSE);
  486.       fname = ReplaceWithTidy(fname);
  487.       if( !is_word(type(fname)) )
  488.       { Error(WARN, &fpos(fname), "name of %s file expected here",
  489.           SymName(res));
  490.         Dispose(fname);
  491.         res = nil;
  492.         break;
  493.       }
  494.       len = StringLength(string(fname)) - StringLength(SOURCE_SUFFIX);
  495.       if( len >= 0 && StringEqual(&string(fname)[len], SOURCE_SUFFIX) )
  496.         StringCopy(&string(fname)[len], STR_EMPTY);
  497.       fnum = DefineFile(string(fname), STR_EMPTY, &fpos(fname),
  498.           INCLUDE_FILE,
  499.           predefined(res)==INCLUDE ? INCLUDE_PATH : SYSINCLUDE_PATH);
  500.       Dispose(fname);
  501.       LexPush(fnum, 0, INCLUDE_FILE);
  502.       res = LexGetToken();
  503.       p = chpt;
  504.     }
  505.     else res = NewToken(predefined(res), &file_pos,0,0,precedence(res),res);
  506.     break;
  507.  
  508.  
  509.       case QUOTE:
  510.       
  511.     col_num(file_pos) = (startpos = q = p) - 1 - startline;
  512.     do switch( chtbl[*q++ = *p++] )
  513.     {
  514.       case OTHER:
  515.       case LETTER:
  516.       case COMMENT:
  517.       case CSPACE:
  518.       case TAB:    break;
  519.  
  520.       case NEWLINE:
  521.       case ENDFILE:    --p;
  522.             Error(WARN, &file_pos, "unterminated string");
  523.             setword(QWORD, res, file_pos, startpos, q-1-startpos);
  524.             break;
  525.  
  526.       case QUOTE:    setword(QWORD, res, file_pos, startpos, q-1-startpos);
  527.             break;
  528.  
  529.       case ESCAPE:    q--;
  530.             if( chtbl[*p] == NEWLINE || chtbl[*p] == ENDFILE )
  531.             { Error(WARN, &file_pos, "unterminated string");
  532.               setword(QWORD, res, file_pos, startpos, q-startpos);
  533.             }
  534.             else if( octaldigit(*p) )
  535.             { int count, ch;
  536.               count = ch = 0;
  537.               do
  538.               { ch = ch * 8 + digitchartonum(*p++);
  539.                 count++;
  540.               } while( octaldigit(*p) && count < 3 );
  541.               if( ch == '\0' )  Error(WARN, &file_pos,
  542.                 "skipping null character \0 in string");
  543.               else *q++ = ch;
  544.             }
  545.             else *q++ = *p++;
  546.             break;
  547.  
  548.       default:    Error(INTERN, &file_pos, "LexGetToken: quoted string");
  549.             break;
  550.  
  551.     } while( res == nil );
  552.     break;
  553.  
  554.  
  555.       default:
  556.       
  557.     Error(INTERN, &file_pos, "LexGetToken: bad chtbl[]");
  558.     break;
  559.  
  560.   } while( res == nil );
  561.  
  562.   if( p - startline >= MAX_LINE )
  563.   { col_num(file_pos) = 1;
  564.     Error(FATAL, &file_pos, "line is too long (or final newline missing)");
  565.   }
  566.  
  567.   chpt = p;
  568.   vspace(res) = vcount;
  569.   hspace(res) = hcount;
  570.   debug4(DLA, DD, "LexGetToken%s returning %s %s (@%d)",
  571.     EchoFilePos(&file_pos), Image(type(res)), EchoToken(res), (int) res);
  572.   return res;
  573. } /* end LexGetToken */
  574.