{
  Yacc closure and first set construction algorithms. See Aho/Sethi/Ullman,
  1986, Sections 4.4 and 4.7, for further explanation.


  Copyright (c) 1990-92  Albert Graef <ag@muwiinfa.geschichte.uni-mainz.de>
  Copyright (C) 1996     Berend de Boer <berend@pobox.com>

  This program is free software; you can redistribute it and/or modify
  it under the terms of the GNU General Public License as published by
  the Free Software Foundation; either version 2 of the License, or
  (at your option) any later version.

  This program is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  GNU General Public License for more details.

  You should have received a copy of the GNU General Public License
  along with this program; if not, write to the Free Software
  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.


$Revision: 1.2 $
$Modtime: 96-07-31 14:09 $

$History: YACCCLOS.PAS $
 *
 * *****************  Version 2  *****************
 * User: Berend       Date: 96-10-10   Time: 21:16
 * Updated in $/Lex and Yacc/tply
 * Updated for protected mode, windows and Delphi 1.X and 2.X.

}


unit YaccClos;

interface


procedure closures;
  (* compute the closure sets *)

procedure first_sets;
  (* compute first sets and nullable flags *)

implementation

uses YaccBase, YaccTabl;

procedure closures;

  (* The closure set of a nonterminal X is the set of all nonterminals Y
     s.t. Y appears as the first symbol in a rightmost derivation from the
     nonterminal X (i.e. X =>+ Y ... in a rightmost derivation). We can
     easily compute closure sets as follows:
     - Initialize the closure set for any nonterminal X to contain all
       nonterminals Y for which there is a rule X : Y ...
     - Now repeatedly pass over the already constructed sets, and for
       any nonterminal Y which has already been added to the closure set
       of some nonterminal X, also include the closure elements of Y in
       the closure set of X.
     The algorithm terminates as soon as no additional symbols have been
     added during the previous pass. *)

  var sym, i, count, prev_count : Integer;
      act_syms : IntSet;

  begin
    (* initialize closure sets: *)
    prev_count := 0;
    count := 0;
    for sym := 1 to n_nts do
      begin
        closure_table^[sym] := newEmptyIntSet;
        with rule_offs^[sym] do
          for i := rule_lo to rule_hi do
            with rule_table^[rule_no^[i]]^ do
              if (rhs_len>0) and (rhs_sym[1]<0) then
                include(closure_table^[sym]^, rhs_sym[1]);
        inc(count, size(closure_table^[sym]^));
      end;
    (* repeated passes until no more symbols have been added during the last
       pass: *)
    while prev_count<count do
      begin
        prev_count := count;
        count := 0;
        for sym := 1 to n_nts do
          begin
            act_syms := closure_table^[sym]^;
            for i := 1 to size(act_syms) do
              setunion(closure_table^[sym]^, closure_table^[-act_syms[i]]^);
            inc(count, size(closure_table^[sym]^));
          end;
      end;
  end(*closures*);

procedure first_sets;

  (* The first set of a nonterminal X is the set of all literal symbols
     y s.t. X =>+ y ... in some derivation of the nonterminal X. In
     addition, X is nullable if the empty string can be derived from X.
     Using the first set construction algorithm of Aho/Sethi/Ullman,
     Section 4.4, the first sets and nullable flags are computed as
     follows:

     For any production X -> y1 ... yn, where the yi are grammar symbols,
     add the symbols in the first set of y1 (y1 itself if it is a literal)
     to the first set of X; if y1 is a nullable nonterminal, then proceed
     with y2, etc., until either all yi have been considered or yi is non-
     nullable (or a literal symbol). If all of the yi are nullable (in
     particular, if n=0), then also set nullable[X] to true.

     This procedure is repeated until no more symbols have been added to any
     first set and none of the nullable flags have been changed during the
     previous pass. *)

  var i, j, l, sym : Integer;
      n, null, done : Boolean;

  begin
    (* initialize tables: *)
    for sym := 1 to n_nts do
      begin
        nullable^[sym] := false;
        first_set_table^[sym] := newEmptyIntSet;
      end;
    (* repeated passes until no more symbols added and no nullable flags
       modified: *)
    repeat
      done := true;
      for i := 1 to n_rules do
        with rule_table^[i]^ do
          begin
            l := size(first_set_table^[-lhs_sym]^);
            n := nullable^[-lhs_sym];
            null := true;
            j := 1;
            while (j<=rhs_len) and null do
              begin
                if rhs_sym[j]<0 then
                  begin
                    setunion( first_set_table^[-lhs_sym]^,
                              first_set_table^[-rhs_sym[j]]^ );
                    null := nullable^[-rhs_sym[j]];
                  end
                else
                  begin
                    include( first_set_table^[-lhs_sym]^,
                             rhs_sym[j] );
                    null := false;
                  end;
                inc(j);
              end;
            if null then nullable^[-lhs_sym] := true;
            if (l<size(first_set_table^[-lhs_sym]^)) or
               (n<>nullable^[-lhs_sym]) then
              done := false;
          end;
    until done;
  end(*first_sets*);

end(*YaccClosure*).