(*$S+*)
unit parser;

INTERFACE

uses {$U kermglob.code} kermglob;

{Change log:
13 May 89, V1.1: Fixed several bugs in parsing of HELP commands   RTC
13 May 89, V1.1: Added parsing for COMMENT command
30 Apr 89, V1.1: Added parsing for SET INTERFACE command   RTC
26 Apr 89, V1.1: minor cleanups   RTC
16 Apr 89, V1.1: Added BYE & FINISH command parsing       RTC
14 Apr 89, V1.1: Added parsing for GET, PUT & SHOW VERSION commands   RTC
13 Apr 89, V1.1: Added Version message       RTC
14 Aug 88: Added parsing for LOG, CLOSE, and SET SYSTEM commands   RTC
02 Jul 88: Added -NAMES, -TYPE, TAKE command parsing   RTC

}

   function parse: statustype;

   procedure initvocab;
 
   procedure par_version;


IMPLEMENTATION

uses
   {$U kermutil.code} kermutil;

const
  my_version = '   Parser Unit V1.1, 13 May 89';


procedure eatspaces(var s: string255);

var done: boolean;
    i: integer;

  begin
    done := (length(s) = 0);
    while not done do
      begin
        if s[1] = ' ' then
          begin
            i := length(s) - 1;
            s := copy(s,2,i);
            done := length(s) = 0
          end (* if *)
        else
            done := true
      end (* while *)
  end; (* eatspaces *)

procedure isolate_word(var line, s: string255);

var i: integer;
    done: boolean;

  begin
    done := false;
    i := 1;
    s := copy(' ',0,0);
    while (i <= length(line)) and not done do
      begin
        if line[i] = ' ' then
            done := true
        else
            s := concat(s,copy(line,i,1));
        i := i + 1;
      end; (* while *)
    line := copy(line,i,length(line)-i+1);
  end; (* isolate_word *)

function get_fn(var line, fn: string255): boolean;

var i, l: integer;

  begin
    get_fn := true;
    isolate_word(line, fn);
    l := length(fn);
    if (l < 1) then
        get_fn := false
  end; (* get_fn *)

function get_num( var line: string255; var n: integer ): boolean;

var
   numstr: string255;
   i, l: integer;
begin
   get_num := true;
   isolate_word( line, numstr );
   l := length(numstr);
   if (l>5) or (l<1) then begin
      n := 0;
      get_num := false
   end
   else begin
      n := 0; i := 1;
      numstr := concat( numstr, ' ' );
      while (numstr[i] in ['0'..'9']) do begin
         if n<(maxint div 10) then
            n := n*10 + ord( numstr[i] ) - ord( '0' );
         i := i + 1
      end
   end
end; { get_num }

function nextch(var ch: char): boolean;

var s: string255;

  begin
    isolate_word(line,s);
    if length(s) <> 1 then
        nextch := false
    else
      begin
        ch := s[1];
        nextch := true
      end (* else *)
  end; (* nextch *)

function parse(*: statustype*);

type states = (start, fin, get_filename, get_set_parm, get_parity,
               get_on_off, get_f_type, get_char, get_show_parm,
               get_help_show, get_int_type, get_naming, get_help_parm,
               exitstate, get_baud, get_line, get_log_parm, get_help_log);

var status: statustype;
    word: vocab;
    state: states;

function get_a_sym(var word: vocab): statustype;

var i: vocab;
    s: string255;
    stat: statustype;
    done: boolean;
    matches: integer;

  begin
    eat_spaces(line);
    if length(line) = 0 then
        get_a_sym := ateol
    else
      begin
        stat := null;
        done := false;
        isolate_word(line,s);
        i := allsym;
        matches := 0;
        repeat
            if (pos(s,vocablist[i]) = 1) and (i in expected) then
              begin
                matches := matches + 1;
                word := i
              end
            else if (s[1] < vocablist[i,1]) then
                done := true;
            if (i = versionsym) then
                done := true
            else
                i := succ(i)
        until (matches > 1) or done;
        if matches > 1 then
            stat := ambiguous
        else if (matches = 0) then
            stat := unrec;
        get_a_sym := stat
      end (* else *)
  end; (* get_a_sym *)

  begin
    state := start;
    parse := null;
    noun := nullsym;
    verb := nullsym;
    adj := nullsym;
    uppercase(line);
    repeat
        case state of
          start:
              begin
                expected := [comsym, consym, exitsym, helpsym, quitsym,
                             logsym, closesym, getsym, putsym, byesym, finsym,
                             recsym, sendsym, setsym, showsym, takesym];
                status := get_a_sym(verb);
                if status = ateol then
                  begin
                    parse := null;
                    exit(parse)
                  end (* if *)
                else if (status <> unrec) and (status <>  ambiguous) then
                    case verb of
                      comsym: state := get_line;
                      consym, exitsym, quitsym,
                      byesym, finsym, recsym: state := fin;
                      getsym, putsym,
                      sendsym, takesym: state := getfilename;
                      helpsym: state := get_help_parm;
                      logsym, closesym: state := get_log_param;
                      setsym: state := get_set_parm;
                      showsym: state := get_show_parm;
                    end (* case *)
              end; (* case start *)
          fin:
              begin
                expected := [];
                status := get_a_sym(verb);
                if status = ateol then
                  begin
                    parse := null;
                    exit(parse)
                  end (* if status *)
                else
                    status := unconfirmed
              end; (* case fin *)
          getfilename:
            begin
              expected := [];
              if getfn(line,xfilename) then
                begin
                  status := null;
                  state := fin
                end (* if *)
              else
                  status := fnexpected
            end; (* case get file name *)
          get_set_parm:
              begin
                expected := [paritysym, localsym, ibmsym, emulatesym,
                             escsym, debugsym, filenamsym, filetypesym,
                             intsym, filewarnsym, baudsym, systemsym];
                status := get_a_sym(noun);
                if status = ateol then
                    status := parm_expected
                else if (status <> unrec) and (status <>  ambiguous) then
                    case noun of
                      paritysym: state := get_parity;
                      localsym: state := get_on_off;
                      ibmsym: state := get_on_off;
                      emulatesym: state := get_on_off;
                      escsym: state := getchar;
                      debugsym: state := get_on_off;
                      filenamsym : state := get_naming;
                      filetypesym : state := get_f_type;
                      filewarnsym: state := get_on_off;
                      intsym: state := get_int_type;
                      baudsym: state := get_baud;
                      systemsym: state := get_line
                    end (* case *)
            end; (* case get_set_parm *)
          get_log_parm:
              begin
                expected := [debugsym];
                status := get_a_sym(adj);
                if status = ateol then
                    status := parm_expected
                else if (status <> unrec) and (status <> ambiguous) then
                    if verb = logsym
                      then state := getfilename
                      else state := fin
              end; (* case get_log_parm *)
          get_line:
              begin
                eat_spaces(line);
                parse := null;
                exit(parse)
              end; {case get_line}
          get_parity, get_naming, get_int_type, get_on_off, get_f_type:
              begin
                case state of
                  get_parity:   expected := [marksym, spacesym,
                                             nonesym, evensym, oddsym];
                  get_naming:   expected := [convsym, litsym];
                  get_int_type: expected := [kermitsym, ucsdsym];
                  get_on_off:   expected := [onsym, offsym];
                  get_f_type:   expected := [binsym, textsym];
                end {case state};
                status := get_a_sym(adj);
                if status = ateol then
                    status := parm_expected
                else if (status <> unrec) and (status <> ambiguous) then
                    state := fin
              end; (* case get_parity  *)
          get_baud:
             begin
               expected := [];
               if get_num( line, newbaud ) then begin
                  status := null; state := fin
               end
               else begin
                  newbaud := 0;
                  status := parm_expected
               end
             end; (* case get_baud *)
          get_char:
              if nextch(newescchar) then
                 state := fin
              else
                 status := ch_expected;
          get_show_parm:
              begin
                expected := [allsym, paritysym, localsym, ibmsym,
                             emulatesym, escsym, debugsym,
                             filenamsym, filetypesym, filewarnsym,
                             baudsym, systemsym, versionsym];
                status := get_a_sym(noun);
                if status = ateol then
                    status := parm_expected
                else if (status <> unrec) and (status <>  ambiguous) then
                    state := fin
              end; (* case get_show_parm *)
          get_help_show, get_help_log:
              begin
                case noun of
                  logsym, closesym:
                    expected := [debugsym];
                  setsym:
                    expected := [paritysym, localsym, ibmsym, escsym,
                                 intsym, debugsym, filenamsym, filetypesym,
                                 filewarnsym, baudsym, emulatesym, systemsym];
                  showsym:
                    expected := [paritysym, localsym, ibmsym, escsym,
                                 debugsym, filenamsym, filetypesym,
                                 filewarnsym, baudsym, emulatesym, systemsym,
                                 allsym, versionsym];
                end {case noun};
                status := get_a_sym(adj);
                if (status = at_eol) then
                  begin
                    status := null;
                    state := fin
                  end
                else if (status <> unrec) and (status <>  ambiguous) then
                    state := fin
              end; (* case get_help_show *)
          get_help_parm:
              begin
                expected := [consym, exitsym, helpsym, quitsym, recsym,
                             comsym, getsym, putsym, byesym, finsym, takesym,
                             logsym, closesym, sendsym, setsym, showsym];
                status := get_a_sym(noun);
                if status = ateol then
                  begin
                    parse := null;
                    exit(parse)
                  end;
                if (status <> unrec) and (status <>  ambiguous) then
                    case noun of
                      consym, comsym, getsym, putsym,
                      sendsym, finsym, byesym, takesym,
                      recsym: state := fin;
                      closesym, logsym: state := get_help_log;
                      showsym, setsym: state := get_help_show;
                      helpsym: state := fin;
                      exitsym, quitsym: state := fin;
                    end (* case *)
              end; (* case get_help_show *)
        end (* case *)
    until (status <> null);
    parse := status
  end; (* parse *)

procedure initvocab;

var i: integer;

  begin
    vocablist[allsym] :=        'ALL';
    vocablist[baudsym] :=       'BAUD';
    vocablist[binsym] :=        'BINARY';
    vocablist[byesym] :=        'BYE';
    vocablist[closesym] :=      'CLOSE';
    vocablist[comsym] :=        'COMMENT';
    vocablist[consym] :=        'CONNECT';
    vocablist[convsym] :=       'CONVERTED';
    vocablist[debugsym] :=      'DEBUG';
    vocablist[emulatesym] :=    'EMULATE';
    vocablist[escsym] :=        'ESCAPE';
    vocablist[evensym] :=       'EVEN';
    vocablist[exitsym] :=       'EXIT';
    vocablist[filenamsym] :=    'FILE-NAMES';
    vocablist[filetypesym] :=   'FILE-TYPE';
    vocablist[filewarnsym] :=   'FILE-WARNING';
    vocablist[finsym] :=        'FINISH';
    vocablist[getsym] :=        'GET';
    vocablist[helpsym] :=       'HELP';
    vocablist[ibmsym] :=        'IBM';
    vocablist[intsym] :=        'INTERFACE';
    vocablist[kermitsym] :=     'KERMIT';
    vocablist[litsym] :=        'LITERAL';
    vocablist[localsym] :=      'LOCAL-ECHO';
    vocablist[logsym] :=        'LOG';
    vocablist[marksym] :=       'MARK';
    vocablist[nonesym] :=       'NONE';
    vocablist[oddsym] :=        'ODD';
    vocablist[offsym] :=        'OFF';
    vocablist[onsym] :=         'ON';
    vocablist[paritysym] :=     'PARITY';
    vocablist[putsym] :=        'PUT';
    vocablist[quitsym] :=       'QUIT';
    vocablist[recsym] :=        'RECEIVE';
    vocablist[sendsym] :=       'SEND';
    vocablist[setsym] :=        'SET';
    vocablist[showsym] :=       'SHOW';
    vocablist[spacesym] :=      'SPACE';
    vocablist[systemsym] :=     'SYSTEM-ID';
    vocablist[takesym] :=       'TAKE';
    vocablist[textsym] :=       'TEXT';
    vocablist[ucsdsym] :=       'UCSD';
    vocablist[versionsym] :=    'VERSION';
  end; (* initvocab *)

procedure par_version;
 
  begin
    writeln(my_version)
  end {par_version};
 
end. (* end of unit *)

