$title ('SCANNER MODULE')
scanm:
do;

/*

  modified  3/26/81  R. Silberstein
  modified  3/30/81  R. Silberstein
  modified  4/10/81  R. Silberstein
  modified  9/2/81   R. Silberstein

*/

$include (:f1:macro.lit)
$include (:f1:struc.lit)
$include (:f1:equals.lit)
$include (:f1:files.ext)
$include (:f1:predef.ext)
$include (:f1:subr2.ext)
$include (:f1:print.ext)
$include (:f1:global.ext)

        /* Variables : */

dcl
eoffound        byte,   /* true if end-of-file is found */
lowercase       byte,   /* false if stringinput, otherwise true */
crfound         byte,   /* true if previous input was CR */
printready      byte,   /* true if output line to be printed */
stacksave       addr;   /* save of stack pointer */


/* Routine to perform unnormal exit from module */

exit: proc;
  stackptr=stacksave;   /* restore input stack */
end exit;

/* Put printcharacter into printfile output buffer */

putprintchar: proc(ch);
  dcl ch byte;
  sourcebuf(sourceptr)=ch;
  if sourceptr < last(sourcebuf) then$do
    sourceptr=sourceptr+1;
  end$if;
end putprintchar;


/* Read single character from input file. Put characters
   except CR-LF to printbuffer. Convert to uppercase letters */

read$input: proc byte;
  dcl ch byte;
  if eoffound then call exit;  /* unnormal exit */
                               /* read byte from file */
  if include$on then ch=inincludebyte; else ch=insourcebyte;
  if ch=end$of$file then$do   /* test for end-of-file */
    eoffound=true;
  else$do
    if crfound and ch=lf then$do /* ignore LF after CR */
      ch=space;
    else$do;
      if ch=cr then$do    /* test for CR */
        crfound=true;
      else$do;
        crfound=false;
        call putprintchar(ch);
        if ch=lf then ch=space; /* interpret LF within line as space */
      end$if;
    end$if;
  end$if;
  if not lowercase then$do   /* convert to uppercase */
    ch=upper(ch);
  end$if;
  return ch;
end read$input;


/* skip blanks and tab's in input */

skip$blanks: proc;
  do while nextch=space or nextch=tab;
    nextch=read$input;
  end$while;
end skip$blanks;


/* Put character into accumulator */

putaccum: proc(ch);
  dcl ch byte;
  accum(acclen)=ch;
  if acclen < last(accum) then$do
    acclen=acclen+1;
  end$if;
end put$accum;


/* Routine to scan remainder of token until a non-
   alphanumeric character is found. Skip blanks
   behind token */

get$remainder: proc(numb);
  dcl (cont,numb) byte;
  cont=true;
  do while cont;
    do while alphanumeric(nextch:=read$input);
      call putaccum(nextch);
    end$while;
    cont=false;
    if nextch = '@' or nextch = '_' then$do
      cont=true;
      if numb then call putaccum(nextch);
    end$if;
  end$while;
  call skipblanks;
end get$remainder;

/* Routine to scan a text string. Called from SCAN */

stringr: proc;
  dcl cont byte;
  lowercase=true;
  acclen=0;
  cont=true;
  do while cont;
    nextch=readinput;
    do while nextch <> '''' and nextch <> cr;
      call putaccum(nextch);
      nextch=read$input;
    end$while;
    if nextch='''' then$do
      if (nextch:=readinput) = '''' then$do /* interpret '' as ' */
        call putaccum(nextch);
      else$do
        lowercase=false;
        call skipblanks;
        token.type=string;
        cont=false;
      end$if;
    else$do
      lowercase=false;
      token.type=error;
      cont=false;
    end$if;
  end$while;
end stringr;


/* Routine to scan a number. Called from SCAN. Test syntax
   of number, compute binary value. */

numbr: proc;
  dcl
  nobase        byte, /* number system, 2,8,10 or 16 */
  maxlgth       byte, /* max legal no of digits */
  (i,j)         byte, /* counters */
  ch            byte,
  value         addr, /* 16 bit binary value */
  errorflag     byte; /* syntax error flag */

  errorflag=false;
  call getremainder(true);   /* get rest of token */
  ch=accum(acclen-1);   /* pick up last character of token */
  j=acclen-2;

  /* B (binary) */
  IF CH = 'B' THEN
    do; nobase=2; maxlgth=16; end;

  /* O or Q (octal) */
  ELSE IF CH = 'O' OR CH = 'Q' THEN
    do; nobase=8; maxlgth=6; end;

  /* H (hexadecimal) */
  ELSE IF CH = 'H' THEN
    do; nobase=16; maxlgth=4; end;

  /* D (decimal) */
  ELSE IF CH = 'D' THEN
    do; nobase=10; maxlgth=5; end;

  /* no subscript, default=decimal */
  ELSE
    do; nobase=10; maxlgth=5; j=j+1; end;

  i=0ffh;      /* skip leading zeros */
  do while accum(i:=i+1) = '0'; end;
  if j < maxlgth+i then$do
    value=0;      /* syntax check number, compute binary value */
    do while i <= j;
      ch=accum(i);
      ch=ch-'0';
      if ch > 9 then ch=ch-7;
      if ch >= nobase then$do
        errorflag=true;
      end$if;
      value=value*nobase+ch;
      i=i+1;
    end$while;
  else$do
    errorflag=true;
  end$if;

  if errorflag then$do
    token.type=error;
  else$do
    token.type=number;
    token.descr=0;
    token.value=value;
  end$if;

end numbr;


/* Routine to scan an identifier. Lookup identifier in table
   for predefined symbols */

identr: proc;
  call get$remainder(false);  /* get rest of token into accumulator */
                       /* look up identifier */
  if not pfind(acclen,.accum(0),.token) then$do
    token.type=ident;
  end$if;
end identr;

        /* PUBLIC subroutines : */

scaninit: proc public;
  eofset,eoffound,crfound,lowercase,printready=false;
  CALL FILL (SPACE, SIZE (PREFIX), .PREFIX);
  CALL FILL (SPACE, LENGTH (ABSADDR), .ABSADDR);
  sourceptr,prefixptr=0;
  call printinit;   /* initiate print module */
  call rewindsource;
  nextch=space;
end scaninit;

scan: proc public;

  stacksave=stackptr;
  if printready then$do
    call print$source$line;
    print$ready=false;
  end$if;
  call skipblanks;
  if eoffound then$do
    token.type=spec;
    if crfound then$do
      eoffound=false;
      eofset=true;
    else$do
      printready=true;  /* terminate line before EOF */
      crfound=true;
      accum(0)=cr;
    end$if;
  else$do
    acclen=1;
    accum(0)=nextch;

    /* identifier */
    IF LETTER (NEXTCH) THEN call identr;

    /* number */
    ELSE IF DIGIT (NEXTCH) THEN call numbr;

    /* string */
    ELSE IF NEXTCH = '''' THEN call stringr;

    /* special letter */
    ELSE
      do;
      token.type=spec;
      if nextch='!' then accum(0) = cr;
      IF NEXTCH = ';' THEN$DO
        DO WHILE ACCUM (0) <> CR;
          ACCUM (0) = READINPUT;
        END$WHILE;
      END$IF;
      nextch=space;
      if crfound then$do
        print$ready=true;
      else$do
        call skipblanks;
      end$if;
      end;

  end$if;
end scan;

skip$rest$of$line: proc public;
  do while accum(0) <> cr;
    call scan;
  end$while;
end skip$rest$of$line;

specialtoken: proc(tok) byte public;
  dcl tok byte;
  if (token.type=spec) and (accum(0)=tok) then return true;
  return false;
end specialtoken;

skip$until: proc(tok) byte public;
  dcl tok byte;
  do forever;
    if token.type=spec then$do
      if accum(0)=tok then$do
        call scan;
        return true;
      end$if;
      if accum(0)=cr then return false;
    end$if;
    call scan;
  end$forever;
end skip$until;

emptyline: proc byte public;
  return specialtoken(cr);
end emptyline;

end$module scanm;
