{ File: indicefi.pas }

{ Scopo: uso di array, procedure, funzioni e file di testo }

program IndiceTestoSuFile;
{ Genera e stampa l'indice analitico di un testo letto da file, cioe` la lista
  di parole distinte contenute nel testo.  Stampa per ogni parola anche il
  numero di occorrenze.
}

const
  MaxLungParola = 30;
  MaxNumParole  = 500;

type
  TipoIndiceCar        = 1..MaxLungParola;
  TipoParola           = packed array [TipoIndiceCar] of char;
  TipoIntervalloIndice = 1..MaxNumParole;
  TipoOccorrenza       = record
                           parola     : TipoParola;
                           occorrenza : 0..MAXINT
                         end;         
  TipoArrayParole      = array [TipoIntervalloIndice] of TipoOccorrenza;
  TipoIndice           = record
                           lista_parole    : TipoArrayParole;
                           numero_elementi : 0..MaxNumParole
                         end;              

  procedure LeggiParolaSuccessiva (var f: text; var parola: TipoParola;
                                   var fine: boolean);
  { Legge la parola successiva dal file f e la restituisce in parola.
    Se il file e` terminato pone fine a TRUE. }
  var
    ch      : char;
    i, lung : integer;

    function AlfaNumerico (ch: char): boolean;
    { Restituisce TRUE se ch e` una lettera alfabetica o una cifra. }
    begin { AlfaNumerico }
      AlfaNumerico := ((ch >= 'a') and (ch <= 'z')) or
                      ((ch >= 'A') and (ch <= 'Z')) or
                      ((ch >= '0') and (ch <= '9'))
    end; { AlfaNumerico }

    function Maiuscola (ch: char): char;
    { Se ch e` una lettera minuscola, lo converte in maiuscola, altrimenti lo
      lascia inalterato. }
    begin { Maiuscola }
      if (ch >= 'a') and (ch <= 'z') then
        Maiuscola := chr(ord(ch) - ord('a') + ord('A'))
      else
        Maiuscola := ch
    end; { Maiuscola }

  begin { LeggiParolaSuccessiva }
    if not eof(f) then
      repeat                        { salta i caratteri non alfanumerici fino
                                      all'inizio della prossima parola }
        read(f, ch)
      until eof(f) or AlfaNumerico(ch);

    if eof(f) then
      fine := TRUE
    else
    begin
      fine := FALSE;
      lung := 0;
      repeat                        { legge i caratteri della parola }
        lung := lung + 1;
        if lung <= MaxLungParola then
          parola[lung] := Maiuscola(ch);
        read(f, ch)
      until not AlfaNumerico(ch);

      for i := lung + 1 to MaxLungParola do  { completa la parola con blank }
        parola[i] := ' '
    end
  end; { LeggiParolaSuccessiva }


  procedure AggiungiIndice (parola: TipoParola; var indice: TipoIndice);
  { Aggiunge parola ad indice. }
  var
    i                 : integer;
    posizione_trovata : boolean;

    procedure InserisciParola(posizione: TipoIntervalloIndice);
    { Inserisce parola nell'indice nella posizione specificata. }
    var
      j : TipoIntervalloIndice;
    { variabili esterne:
      parola : TipoParola;
      indice : TipoIndice;
    }
    begin { InserisciParola }
      with indice do
      begin
        numero_elementi := numero_elementi + 1;
        for j := numero_elementi downto posizione+1 do
                                 { sposta le parole successive verso il basso }
          lista_parole[j] := lista_parole[j-1];
        lista_parole[posizione].parola := parola;
        lista_parole[posizione].occorrenza := 1
      end
    end; { InserisciParola }

  begin { AggiungiIndice }
    i := 1;
    posizione_trovata := FALSE;
    with indice do
    begin
      while (i <= numero_elementi) and (not posizione_trovata) do
                    { ricerca la posizione corretta in cui inserire la parola }
        if lista_parole[i].parola < parola then
          i := i + 1
        else
          posizione_trovata := TRUE;

      if posizione_trovata then               { se si e` trovata la posizione }
        if lista_parole[i].parola = parola then { se la parola e` presente
                                                    incrementa l'occorrenza   }
          lista_parole[i].occorrenza := lista_parole[i].occorrenza + 1
        else                                    { altrimenti                  }
          InserisciParola(i)                    {   inseriscila               }
      else                                    { altrimenti                    }
        InserisciParola(i)                    {   inseriscila alla fine       }
    end
  end; { AggiungiIndice }


  procedure StampaIndice(var indice: TipoIndice);
  { Stampa la lista di parole in indice.
    Il parametro indice e` passato per variabile per motivi di efficienza. }
  var
    i : TipoIntervalloIndice;

  begin { StampaIndice }
    writeln('Lista di parole nel testo:');
    with indice do
      for i := 1 to numero_elementi do
        writeln(lista_parole[i].parola, ' ', lista_parole[i].occorrenza:3)
  end; { StampaIndice }


{ N.B. Nel PASCAL standard le dichiarazioni di variabili devono precedere le
       dichiarazioni di procedure e funzioni. }

var
  nome   : string;      { nome del file contente il testo }
  f      : text;
  fine   : boolean;     { indica se si e` giunti alla fine del file }
  parola : TipoParola;  { la parola corrente }
  indice : TipoIndice;

begin { IndiceTestoSuFile }
  writeln('Nome del file da analizzare? ');
  readln(nome);
  Assign(f, nome);
  Reset(f);

  indice.numero_elementi := 0;
  LeggiParolaSuccessiva(f, parola, fine);
  while not fine do
  begin
    AggiungiIndice(parola, indice);
    LeggiParolaSuccessiva(f, parola, fine);
  end;

  Close(f);
  StampaIndice(indice)
end. { IndiceTestoSuFile }